diff --git a/README.md b/README.md index 0f9ed992352..b26fd56a7b1 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,8 @@ Check out [Github Issues](https://github.com/pagefaultgames/pokerogue/issues) to - Pokémon Sword/Shield - Pokémon Legends: Arceus - Pokémon Scarlet/Violet - - Firel (Custom Laboratory, Metropolis, Seabed, and Space biome music) - - Lmz (Custom Jungle biome music) + - Firel (Custom Ice Cave, Laboratory, Metropolis, Plains, Power Plant, Seabed, Space, and Volcano biome music) + - Lmz (Custom Ancient Ruins, Jungle, and Lake biome music) - Andr06 (Custom Slum and Sea biome music) ### 🎵 Sound Effects diff --git a/create-test-boilerplate.js b/create-test-boilerplate.js index d9cdbd4e7cf..6d9cde966d5 100644 --- a/create-test-boilerplate.js +++ b/create-test-boilerplate.js @@ -1,7 +1,3 @@ -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", @@ -10,63 +6,108 @@ import { fileURLToPath } from 'url'; * @example npm run create-test move tackle */ +import fs from "fs"; +import inquirer from "inquirer"; +import path from "path"; +import { fileURLToPath } from "url"; + // Get the directory name of the current module file const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); +const typeChoices = ["Move", "Ability", "Item", "Mystery Encounter"]; -// 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 +/** + * Prompts the user to select a type via list. + * @returns {Promise<{selectedOption: string}>} the selected type + */ +async function promptTestType() { + const typeAnswer = await inquirer.prompt([ + { + type: "list", + name: "selectedOption", + message: "What type of test would you like to create:", + choices: [...typeChoices, "EXIT"], + }, + ]); -if (!type || !fileName) { - console.error('Please provide a type ("move", "ability", or "item") and a file name.'); - process.exit(1); + if (typeAnswer.selectedOption === "EXIT") { + console.log("Exiting..."); + return process.exit(); + } else if (!typeChoices.includes(typeAnswer.selectedOption)) { + console.error('Please provide a valid type ("move", "ability", or "item")!'); + return await promptTestType(); + } + + return typeAnswer; } -// 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 +/** + * Prompts the user to provide a file name. + * @param {string} selectedType + * @returns {Promise<{userInput: string}>} the selected file name + */ +async function promptFileName(selectedType) { + const fileNameAnswer = await inquirer.prompt([ + { + type: "input", + name: "userInput", + message: `Please provide a file name for the ${selectedType} test:`, + }, + ]); -// Format the description for the test case -const formattedName = fileName - .replace(/_/g, ' ') - .replace(/\b\w/g, char => char.toUpperCase()); + if (!fileNameAnswer.userInput || fileNameAnswer.userInput.trim().length === 0) { + console.error("Please provide a valid file name!"); + return await promptFileName(selectedType); + } -// 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); + return fileNameAnswer; } -// Ensure the directory exists -if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); -} +/** + * Runs the interactive create-test "CLI" + * @returns {Promise} + */ +async function runInteractive() { + const typeAnswer = await promptTestType(); + const fileNameAnswer = await promptFileName(typeAnswer.selectedOption); -// Create the file with the given name -const filePath = path.join(dir, `${fileName}.test.ts`); + const type = typeAnswer.selectedOption.toLowerCase(); + // Convert fileName from kebab-case or camelCase to snake_case + const fileName = fileNameAnswer.userInput + .replace(/-+/g, "_") // Convert kebab-case (dashes) to underscores + .replace(/([a-z])([A-Z])/g, "$1_$2") // Convert camelCase to snake_case + .replace(/\s+/g, '_') // Replace spaces with underscores + .toLowerCase(); // Ensure all lowercase + // Format the description for the test case -if (fs.existsSync(filePath)) { - console.error(`File "${fileName}.test.ts" already exists.`); - process.exit(1); -} + const formattedName = fileName.replace(/_/g, " ").replace(/\b\w/g, (char) => char.toUpperCase()); + // Determine the directory based on the type + let dir; + let description; + switch (type) { + case "move": + dir = path.join(__dirname, "src", "test", "moves"); + description = `Moves - ${formattedName}`; + break; + case "ability": + dir = path.join(__dirname, "src", "test", "abilities"); + description = `Abilities - ${formattedName}`; + break; + case "item": + dir = path.join(__dirname, "src", "test", "items"); + description = `Items - ${formattedName}`; + break; + case "mystery encounter": + dir = path.join(__dirname, "src", "test", "mystery-encounter", "encounters"); + description = `Mystery Encounter - ${formattedName}`; + break; + default: + console.error('Invalid type. Please use "move", "ability", or "item".'); + process.exit(1); + } -// Define the content template -const content = `import { Abilities } from "#enums/abilities"; + // 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"; @@ -76,7 +117,6 @@ 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({ @@ -100,11 +140,27 @@ describe("${description}", () => { 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'); + // Ensure the directory exists + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } -console.log(`File created at: ${filePath}`); + // 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); + } + + // Write the template content to the file + fs.writeFileSync(filePath, content, "utf8"); + + console.log(`File created at: ${filePath}`); +} + +runInteractive(); diff --git a/index.css b/index.css index 1274f2fcead..2ec106516d2 100644 --- a/index.css +++ b/index.css @@ -26,10 +26,36 @@ body { #app { display: flex; justify-content: center; + align-items: center; } #app > div:first-child { - transform-origin: top !important; + transform-origin: center !important; +} + +/* + Supports automatic vertical centering as suggested in PR#1114, but only via CSS + + Condition factorized to deduce CSS rules: + true if (isLandscape && !isMobile() && !hasTouchscreen() || (hasTouchscreen() && !isTouchControlsEnabled)) +*/ + +/* isLandscape && !isMobile() && !hasTouchscreen() */ +@media (orientation: landscape) and (pointer: fine) { + #app { + align-items: center; + } +} + +@media (pointer: coarse) { + /* hasTouchscreen() && !isTouchControlsEnabled */ + body:has(> #touchControls[class=visible]) #app { + align-items: start; + } + + body:has(> #touchControls[class=visible]) #app > div:first-child { + transform-origin: top !important; + } } #layout:fullscreen #dpad, #layout:fullscreen { diff --git a/package-lock.json b/package-lock.json index 4a447554819..344100e4f6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "dependency-cruiser": "^16.3.10", "eslint": "^9.7.0", "eslint-plugin-import-x": "^4.2.1", + "inquirer": "^11.0.2", "jsdom": "^24.0.0", "lefthook": "^1.6.12", "phaser3spectorjs": "^0.0.8", @@ -1070,6 +1071,281 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@inquirer/checkbox": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-3.0.1.tgz", + "integrity": "sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/confirm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-4.0.1.tgz", + "integrity": "sha512-46yL28o2NJ9doViqOy0VDcoTzng7rAb6yPQKU7VDLqkmbCaH4JqK4yk4XqlzNWy9PVC5pG1ZUXPBQv+VqnYs2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", + "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.5.5", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/@types/node": { + "version": "22.5.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz", + "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@inquirer/core/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@inquirer/core/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/editor": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-3.0.1.tgz", + "integrity": "sha512-VA96GPFaSOVudjKFraokEEmUQg/Lub6OXvbIEZU1SDCmBzRkHGhxoFAVaF30nyiB4m5cEbDgiI2QRacXZ2hw9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/expand": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-3.0.1.tgz", + "integrity": "sha512-ToG8d6RIbnVpbdPdiN7BCxZGiHOTomOX94C2FaT5KOHupV40tKEDozp12res6cMIfRKrXLJyexAZhWVHgbALSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.6.tgz", + "integrity": "sha512-yfZzps3Cso2UbM7WlxKwZQh2Hs6plrbjs1QnzQDZhK2DgyCo6D8AaHps9olkNcUFlcYERMqU3uJSp1gmy3s/qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-3.0.1.tgz", + "integrity": "sha512-BDuPBmpvi8eMCxqC5iacloWqv+5tQSJlUafYWUe31ow1BVXjW2a5qe3dh4X/Z25Wp22RwvcaLCc2siHobEOfzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/number": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-2.0.1.tgz", + "integrity": "sha512-QpR8jPhRjSmlr/mD2cw3IR8HRO7lSVOnqUvQa8scv1Lsr3xoAMMworcYW3J13z3ppjBFBD2ef1Ci6AE5Qn8goQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/password": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-3.0.1.tgz", + "integrity": "sha512-haoeEPUisD1NeE2IanLOiFr4wcTXGWrBOyAyPZi1FfLJuXOzNmxCJPgUrGYKVh+Y8hfGJenIfz5Wb/DkE9KkMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/prompts": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-6.0.1.tgz", + "integrity": "sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^3.0.1", + "@inquirer/confirm": "^4.0.1", + "@inquirer/editor": "^3.0.1", + "@inquirer/expand": "^3.0.1", + "@inquirer/input": "^3.0.1", + "@inquirer/number": "^2.0.1", + "@inquirer/password": "^3.0.1", + "@inquirer/rawlist": "^3.0.1", + "@inquirer/search": "^2.0.1", + "@inquirer/select": "^3.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/rawlist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-3.0.1.tgz", + "integrity": "sha512-VgRtFIwZInUzTiPLSfDXK5jLrnpkuSOh1ctfaoygKAdPqjcjKYmGh6sCY1pb0aGnCGsmhUxoqLDUAU0ud+lGXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/search": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-2.0.1.tgz", + "integrity": "sha512-r5hBKZk3g5MkIzLVoSgE4evypGqtOannnB3PKTG9NRZxyFRKcfzrdxXXPcoJQsxJPzvdSU2Rn7pB7lw0GCmGAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/select": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-3.0.1.tgz", + "integrity": "sha512-lUDGUxPhdWMkN/fHy1Lk7pF3nK1fh/gqeyWXmctefhxLYxlDsc7vsPBEpxrfVGDsVdyYJsiJoD4bJ1b623cV1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", + "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", + "dev": true, + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1563,6 +1839,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "20.14.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", @@ -1585,6 +1871,13 @@ "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", "dev": true }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true, + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.0.0-alpha.58", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.58.tgz", @@ -1970,6 +2263,22 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -2192,6 +2501,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true, + "license": "MIT" + }, "node_modules/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -2202,6 +2518,16 @@ "node": ">= 16" } }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -3057,6 +3383,21 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3579,6 +3920,19 @@ "i18next": ">=8.4.0" } }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -3626,6 +3980,26 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/inquirer": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-11.0.2.tgz", + "integrity": "sha512-pnbn3nL+JFrTw/pLhzyE/IQ3+gA3n5JxTAZQDjB6qu4gbjOaiTnpZbxT6HY2DDCT7bzDjTTsd3snRP+B6N//Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/prompts": "^6.0.1", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "ansi-escapes": "^4.3.2", + "mute-stream": "^1.0.0", + "run-async": "^3.0.0", + "rxjs": "^7.8.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/interpret": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", @@ -4456,6 +4830,16 @@ "mustache": "bin/mustache" } }, + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -4605,6 +4989,16 @@ "node": ">= 0.8.0" } }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5092,6 +5486,16 @@ "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", "dev": true }, + "node_modules/run-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5116,6 +5520,16 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", @@ -5538,6 +5952,19 @@ "node": ">=14.0.0" } }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -5673,6 +6100,19 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typedoc": { "version": "0.26.5", "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.5.tgz", @@ -6346,6 +6786,19 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index dddf5aedebd..2109604c969 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "dependency-cruiser": "^16.3.10", "eslint": "^9.7.0", "eslint-plugin-import-x": "^4.2.1", + "inquirer": "^11.0.2", "jsdom": "^24.0.0", "lefthook": "^1.6.12", "phaser3spectorjs": "^0.0.8", diff --git a/public/audio/bgm/battle_star_admin.mp3 b/public/audio/bgm/battle_star_admin.mp3 new file mode 100644 index 00000000000..461a9a2b262 Binary files /dev/null and b/public/audio/bgm/battle_star_admin.mp3 differ diff --git a/public/audio/bgm/battle_star_boss.mp3 b/public/audio/bgm/battle_star_boss.mp3 new file mode 100644 index 00000000000..51cb33139c6 Binary files /dev/null and b/public/audio/bgm/battle_star_boss.mp3 differ diff --git a/public/audio/bgm/battle_star_grunt.mp3 b/public/audio/bgm/battle_star_grunt.mp3 new file mode 100644 index 00000000000..13da4900eed Binary files /dev/null and b/public/audio/bgm/battle_star_grunt.mp3 differ diff --git a/public/audio/bgm/ice_cave.mp3 b/public/audio/bgm/ice_cave.mp3 index 5d1b9e9e354..9d1c7c06bf0 100644 Binary files a/public/audio/bgm/ice_cave.mp3 and b/public/audio/bgm/ice_cave.mp3 differ diff --git a/public/audio/bgm/jungle.mp3 b/public/audio/bgm/jungle.mp3 index 3a21c9bdb41..fbb770b0ad3 100644 Binary files a/public/audio/bgm/jungle.mp3 and b/public/audio/bgm/jungle.mp3 differ diff --git a/public/audio/bgm/laboratory.mp3 b/public/audio/bgm/laboratory.mp3 index e2b617e590a..1eba66ef56f 100644 Binary files a/public/audio/bgm/laboratory.mp3 and b/public/audio/bgm/laboratory.mp3 differ diff --git a/public/audio/bgm/lake.mp3 b/public/audio/bgm/lake.mp3 index c61fef15e42..e6762935770 100644 Binary files a/public/audio/bgm/lake.mp3 and b/public/audio/bgm/lake.mp3 differ diff --git a/public/audio/bgm/metropolis.mp3 b/public/audio/bgm/metropolis.mp3 index 98c2eb396b6..514c9ae15b1 100644 Binary files a/public/audio/bgm/metropolis.mp3 and b/public/audio/bgm/metropolis.mp3 differ diff --git a/public/audio/bgm/mystery_encounter_delibirdy.mp3 b/public/audio/bgm/mystery_encounter_delibirdy.mp3 new file mode 100644 index 00000000000..515a429aaba Binary files /dev/null and b/public/audio/bgm/mystery_encounter_delibirdy.mp3 differ diff --git a/public/audio/bgm/mystery_encounter_fun_and_games.mp3 b/public/audio/bgm/mystery_encounter_fun_and_games.mp3 index a9660d75e90..7864bf7a73d 100644 Binary files a/public/audio/bgm/mystery_encounter_fun_and_games.mp3 and b/public/audio/bgm/mystery_encounter_fun_and_games.mp3 differ diff --git a/public/audio/bgm/mystery_encounter_gen_5_gts.mp3 b/public/audio/bgm/mystery_encounter_gen_5_gts.mp3 index 989a7f9c598..d03c8f8d4d5 100644 Binary files a/public/audio/bgm/mystery_encounter_gen_5_gts.mp3 and b/public/audio/bgm/mystery_encounter_gen_5_gts.mp3 differ diff --git a/public/audio/bgm/mystery_encounter_gen_6_gts.mp3 b/public/audio/bgm/mystery_encounter_gen_6_gts.mp3 index 2c574da66ae..c921a01c204 100644 Binary files a/public/audio/bgm/mystery_encounter_gen_6_gts.mp3 and b/public/audio/bgm/mystery_encounter_gen_6_gts.mp3 differ diff --git a/public/audio/bgm/mystery_encounter_weird_dream.mp3 b/public/audio/bgm/mystery_encounter_weird_dream.mp3 index a630fe549db..433e07bab08 100644 Binary files a/public/audio/bgm/mystery_encounter_weird_dream.mp3 and b/public/audio/bgm/mystery_encounter_weird_dream.mp3 differ diff --git a/public/audio/bgm/plains.mp3 b/public/audio/bgm/plains.mp3 index 6c7a008bce6..ff364600b4a 100644 Binary files a/public/audio/bgm/plains.mp3 and b/public/audio/bgm/plains.mp3 differ diff --git a/public/audio/bgm/power_plant.mp3 b/public/audio/bgm/power_plant.mp3 index 9813ad40a11..152667fcba6 100644 Binary files a/public/audio/bgm/power_plant.mp3 and b/public/audio/bgm/power_plant.mp3 differ diff --git a/public/audio/bgm/ruins.mp3 b/public/audio/bgm/ruins.mp3 index 62f31893423..3692c71562f 100644 Binary files a/public/audio/bgm/ruins.mp3 and b/public/audio/bgm/ruins.mp3 differ diff --git a/public/audio/bgm/volcano.mp3 b/public/audio/bgm/volcano.mp3 index 093bb86813b..a67bbd111de 100644 Binary files a/public/audio/bgm/volcano.mp3 and b/public/audio/bgm/volcano.mp3 differ diff --git a/public/battle-anims/clanging-scales.json b/public/battle-anims/clanging-scales.json index de1a3d5248f..e2135a1a9b4 100644 --- a/public/battle-anims/clanging-scales.json +++ b/public/battle-anims/clanging-scales.json @@ -27,7 +27,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 0, @@ -115,7 +115,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 0, @@ -215,7 +215,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 0, @@ -315,7 +315,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 0, @@ -414,7 +414,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 0, @@ -538,7 +538,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 23, @@ -685,7 +685,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": -19, @@ -784,7 +784,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 26, @@ -883,7 +883,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 23.5, @@ -994,7 +994,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 9, @@ -1069,7 +1069,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": -18.5, @@ -1157,7 +1157,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 37.5, @@ -1221,7 +1221,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 0, @@ -1284,7 +1284,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 0, @@ -1348,7 +1348,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 0, @@ -1448,7 +1448,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 0, @@ -1548,7 +1548,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 0, @@ -1647,7 +1647,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 0, @@ -1759,7 +1759,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": -25.5, @@ -1870,7 +1870,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 12, @@ -1957,7 +1957,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": -27, @@ -2044,7 +2044,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": -16, @@ -2143,7 +2143,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": -26.5, @@ -2230,7 +2230,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 23, @@ -2306,7 +2306,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": 24, @@ -2346,7 +2346,7 @@ "opacity": 255, "locked": true, "priority": 1, - "focus": 2 + "focus": 1 }, { "x": -27, diff --git a/public/fonts/pkmnems.ttf b/public/fonts/pkmnems.ttf index 0aa3a19b417..b0b50d0f10f 100644 Binary files a/public/fonts/pkmnems.ttf and b/public/fonts/pkmnems.ttf differ diff --git a/public/images/items.json b/public/images/items.json index dd0cf6837be..779823d1293 100644 --- a/public/images/items.json +++ b/public/images/items.json @@ -4,8 +4,8 @@ "image": "items.png", "format": "RGBA8888", "size": { - "w": 428, - "h": 428 + "w": 431, + "h": 431 }, "scale": 1, "frames": [ @@ -93,6 +93,27 @@ "h": 28 } }, + { + "filename": "leaders_crest", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 2, + "y": 3, + "w": 29, + "h": 27 + }, + "frame": { + "x": 61, + "y": 0, + "w": 29, + "h": 27 + } + }, { "filename": "ribbon_gen2", "rotated": false, @@ -171,8 +192,8 @@ "h": 26 }, "frame": { - "x": 61, - "y": 0, + "x": 59, + "y": 27, "w": 27, "h": 26 } @@ -339,7 +360,7 @@ "h": 26 }, "frame": { - "x": 88, + "x": 90, "y": 0, "w": 24, "h": 26 @@ -388,7 +409,7 @@ } }, { - "filename": "ability_capsule", + "filename": "black_glasses", "rotated": false, "trimmed": true, "sourceSize": { @@ -397,15 +418,15 @@ }, "spriteSourceSize": { "x": 4, - "y": 9, - "w": 24, - "h": 14 + "y": 8, + "w": 23, + "h": 17 }, "frame": { "x": 0, "y": 414, - "w": 24, - "h": 14 + "w": 23, + "h": 17 } }, { @@ -423,7 +444,7 @@ "h": 26 }, "frame": { - "x": 112, + "x": 114, "y": 0, "w": 23, "h": 26 @@ -444,7 +465,7 @@ "h": 22 }, "frame": { - "x": 135, + "x": 137, "y": 0, "w": 27, "h": 22 @@ -465,7 +486,7 @@ "h": 21 }, "frame": { - "x": 162, + "x": 164, "y": 0, "w": 28, "h": 21 @@ -486,7 +507,7 @@ "h": 21 }, "frame": { - "x": 190, + "x": 192, "y": 0, "w": 28, "h": 21 @@ -507,7 +528,7 @@ "h": 21 }, "frame": { - "x": 218, + "x": 220, "y": 0, "w": 28, "h": 21 @@ -528,7 +549,7 @@ "h": 21 }, "frame": { - "x": 246, + "x": 248, "y": 0, "w": 28, "h": 21 @@ -549,7 +570,7 @@ "h": 21 }, "frame": { - "x": 274, + "x": 276, "y": 0, "w": 28, "h": 21 @@ -570,7 +591,7 @@ "h": 21 }, "frame": { - "x": 302, + "x": 304, "y": 0, "w": 28, "h": 21 @@ -591,7 +612,7 @@ "h": 20 }, "frame": { - "x": 330, + "x": 332, "y": 0, "w": 26, "h": 20 @@ -612,7 +633,7 @@ "h": 20 }, "frame": { - "x": 356, + "x": 358, "y": 0, "w": 26, "h": 20 @@ -633,14 +654,14 @@ "h": 20 }, "frame": { - "x": 382, + "x": 384, "y": 0, "w": 25, "h": 20 } }, { - "filename": "catching_charm", + "filename": "ribbon_gen6", "rotated": false, "trimmed": true, "sourceSize": { @@ -649,15 +670,15 @@ }, "spriteSourceSize": { "x": 5, - "y": 4, - "w": 21, - "h": 24 + "y": 2, + "w": 22, + "h": 28 }, "frame": { - "x": 407, + "x": 409, "y": 0, - "w": 21, - "h": 24 + "w": 22, + "h": 28 } }, { @@ -745,7 +766,7 @@ } }, { - "filename": "ribbon_gen6", + "filename": "ribbon_gen8", "rotated": false, "trimmed": true, "sourceSize": { @@ -765,27 +786,6 @@ "h": 28 } }, - { - "filename": "ribbon_gen8", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 2, - "w": 22, - "h": 28 - }, - "frame": { - "x": 22, - "y": 237, - "w": 22, - "h": 28 - } - }, { "filename": "black_augurite", "rotated": false, @@ -802,7 +802,7 @@ }, "frame": { "x": 22, - "y": 265, + "y": 237, "w": 22, "h": 25 } @@ -823,7 +823,7 @@ }, "frame": { "x": 22, - "y": 290, + "y": 262, "w": 23, "h": 24 } @@ -844,7 +844,7 @@ }, "frame": { "x": 22, - "y": 314, + "y": 286, "w": 24, "h": 24 } @@ -865,7 +865,7 @@ }, "frame": { "x": 22, - "y": 338, + "y": 310, "w": 24, "h": 24 } @@ -886,7 +886,7 @@ }, "frame": { "x": 22, - "y": 362, + "y": 334, "w": 24, "h": 24 } @@ -907,13 +907,13 @@ }, "frame": { "x": 22, - "y": 386, + "y": 358, "w": 24, "h": 24 } }, { - "filename": "choice_specs", + "filename": "earth_plate", "rotated": false, "trimmed": true, "sourceSize": { @@ -922,15 +922,36 @@ }, "spriteSourceSize": { "x": 4, - "y": 8, + "y": 4, "w": 24, - "h": 18 + "h": 24 }, "frame": { - "x": 24, - "y": 410, + "x": 22, + "y": 382, "w": 24, - "h": 18 + "h": 24 + } + }, + { + "filename": "fist_plate", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 23, + "y": 406, + "w": 24, + "h": 24 } }, { @@ -975,6 +996,27 @@ "h": 16 } }, + { + "filename": "choice_specs", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 24, + "h": 18 + }, + "frame": { + "x": 59, + "y": 53, + "w": 24, + "h": 18 + } + }, { "filename": "calcium", "rotated": false, @@ -1018,7 +1060,7 @@ } }, { - "filename": "earth_plate", + "filename": "catching_charm", "rotated": false, "trimmed": true, "sourceSize": { @@ -1026,20 +1068,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 4, + "x": 5, "y": 4, - "w": 24, + "w": 21, "h": 24 }, "frame": { "x": 39, "y": 134, - "w": 24, + "w": 21, "h": 24 } }, { - "filename": "fist_plate", + "filename": "flame_plate", "rotated": false, "trimmed": true, "sourceSize": { @@ -1060,7 +1102,7 @@ } }, { - "filename": "flame_plate", + "filename": "focus_band", "rotated": false, "trimmed": true, "sourceSize": { @@ -1081,7 +1123,7 @@ } }, { - "filename": "focus_band", + "filename": "golden_punch", "rotated": false, "trimmed": true, "sourceSize": { @@ -1102,7 +1144,7 @@ } }, { - "filename": "golden_punch", + "filename": "gracidea", "rotated": false, "trimmed": true, "sourceSize": { @@ -1122,27 +1164,6 @@ "h": 24 } }, - { - "filename": "gracidea", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 44, - "y": 254, - "w": 24, - "h": 24 - } - }, { "filename": "grip_claw", "rotated": false, @@ -1159,7 +1180,7 @@ }, "frame": { "x": 45, - "y": 278, + "y": 254, "w": 24, "h": 24 } @@ -1180,7 +1201,7 @@ }, "frame": { "x": 46, - "y": 302, + "y": 278, "w": 24, "h": 24 } @@ -1201,7 +1222,7 @@ }, "frame": { "x": 46, - "y": 326, + "y": 302, "w": 24, "h": 24 } @@ -1222,7 +1243,7 @@ }, "frame": { "x": 46, - "y": 350, + "y": 326, "w": 24, "h": 24 } @@ -1243,13 +1264,13 @@ }, "frame": { "x": 46, - "y": 374, + "y": 350, "w": 24, "h": 24 } }, { - "filename": "abomasite", + "filename": "lucky_punch_great", "rotated": false, "trimmed": true, "sourceSize": { @@ -1257,16 +1278,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 + "x": 4, + "y": 4, + "w": 24, + "h": 24 }, "frame": { - "x": 48, - "y": 70, - "w": 16, - "h": 16 + "x": 46, + "y": 374, + "w": 24, + "h": 24 } }, { @@ -1284,12 +1305,33 @@ "h": 24 }, "frame": { - "x": 48, + "x": 47, "y": 398, "w": 23, "h": 24 } }, + { + "filename": "silver_powder", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 24, + "h": 15 + }, + "frame": { + "x": 48, + "y": 71, + "w": 24, + "h": 15 + } + }, { "filename": "elixir", "rotated": false, @@ -1347,14 +1389,14 @@ "h": 24 }, "frame": { - "x": 63, + "x": 60, "y": 134, "w": 18, "h": 24 } }, { - "filename": "lucky_punch_great", + "filename": "hp_up", "rotated": false, "trimmed": true, "sourceSize": { @@ -1362,20 +1404,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 4, + "x": 8, "y": 4, - "w": 24, + "w": 16, "h": 24 }, "frame": { "x": 63, "y": 158, - "w": 24, + "w": 16, "h": 24 } }, { - "filename": "lucky_punch_master", + "filename": "iron", "rotated": false, "trimmed": true, "sourceSize": { @@ -1383,20 +1425,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 4, + "x": 8, "y": 4, - "w": 24, + "w": 16, "h": 24 }, "frame": { "x": 63, "y": 182, - "w": 24, + "w": 16, "h": 24 } }, { - "filename": "lucky_punch_ultra", + "filename": "lucky_punch_master", "rotated": false, "trimmed": true, "sourceSize": { @@ -1417,7 +1459,7 @@ } }, { - "filename": "lustrous_globe", + "filename": "lucky_punch_ultra", "rotated": false, "trimmed": true, "sourceSize": { @@ -1437,6 +1479,27 @@ "h": 24 } }, + { + "filename": "lustrous_globe", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 69, + "y": 254, + "w": 24, + "h": 24 + } + }, { "filename": "meadow_plate", "rotated": false, @@ -1452,8 +1515,8 @@ "h": 24 }, "frame": { - "x": 68, - "y": 254, + "x": 70, + "y": 278, "w": 24, "h": 24 } @@ -1473,8 +1536,8 @@ "h": 24 }, "frame": { - "x": 69, - "y": 278, + "x": 70, + "y": 302, "w": 24, "h": 24 } @@ -1495,7 +1558,7 @@ }, "frame": { "x": 70, - "y": 302, + "y": 326, "w": 24, "h": 24 } @@ -1516,7 +1579,7 @@ }, "frame": { "x": 70, - "y": 326, + "y": 350, "w": 24, "h": 24 } @@ -1537,7 +1600,7 @@ }, "frame": { "x": 70, - "y": 350, + "y": 374, "w": 24, "h": 24 } @@ -1558,13 +1621,55 @@ }, "frame": { "x": 70, - "y": 374, + "y": 398, "w": 24, "h": 24 } }, { - "filename": "reveal_glass", + "filename": "ability_capsule", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 9, + "w": 24, + "h": 14 + }, + "frame": { + "x": 137, + "y": 22, + "w": 24, + "h": 14 + } + }, + { + "filename": "lure", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 17, + "h": 24 + }, + "frame": { + "x": 86, + "y": 27, + "w": 17, + "h": 24 + } + }, + { + "filename": "silk_scarf", "rotated": false, "trimmed": true, "sourceSize": { @@ -1574,13 +1679,13 @@ "spriteSourceSize": { "x": 4, "y": 4, - "w": 23, + "w": 24, "h": 24 }, "frame": { - "x": 71, - "y": 398, - "w": 23, + "x": 103, + "y": 26, + "w": 24, "h": 24 } }, @@ -1599,14 +1704,14 @@ "h": 23 }, "frame": { - "x": 135, - "y": 22, + "x": 127, + "y": 36, "w": 24, "h": 23 } }, { - "filename": "berry_pouch", + "filename": "coin_case", "rotated": false, "trimmed": true, "sourceSize": { @@ -1616,18 +1721,39 @@ "spriteSourceSize": { "x": 4, "y": 5, - "w": 23, + "w": 24, "h": 23 }, "frame": { - "x": 159, - "y": 22, - "w": 23, + "x": 103, + "y": 50, + "w": 24, "h": 23 } }, { - "filename": "silk_scarf", + "filename": "big_nugget", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 83, + "y": 53, + "w": 20, + "h": 20 + } + }, + { + "filename": "dragon_scale", "rotated": false, "trimmed": true, "sourceSize": { @@ -1636,17 +1762,59 @@ }, "spriteSourceSize": { "x": 4, - "y": 4, + "y": 8, "w": 24, + "h": 18 + }, + "frame": { + "x": 127, + "y": 59, + "w": 24, + "h": 18 + } + }, + { + "filename": "max_elixir", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 18, "h": 24 }, "frame": { - "x": 182, - "y": 21, - "w": 24, + "x": 151, + "y": 36, + "w": 18, "h": 24 } }, + { + "filename": "adamant_crystal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 6, + "w": 23, + "h": 21 + }, + "frame": { + "x": 151, + "y": 60, + "w": 23, + "h": 21 + } + }, { "filename": "sky_plate", "rotated": false, @@ -1662,7 +1830,7 @@ "h": 24 }, "frame": { - "x": 206, + "x": 169, "y": 21, "w": 24, "h": 24 @@ -1683,7 +1851,7 @@ "h": 24 }, "frame": { - "x": 230, + "x": 193, "y": 21, "w": 24, "h": 24 @@ -1704,7 +1872,7 @@ "h": 24 }, "frame": { - "x": 254, + "x": 217, "y": 21, "w": 24, "h": 24 @@ -1725,7 +1893,7 @@ "h": 24 }, "frame": { - "x": 278, + "x": 241, "y": 21, "w": 24, "h": 24 @@ -1746,33 +1914,12 @@ "h": 24 }, "frame": { - "x": 302, + "x": 265, "y": 21, "w": 24, "h": 24 } }, - { - "filename": "hp_up", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 326, - "y": 21, - "w": 16, - "h": 24 - } - }, { "filename": "toxic_plate", "rotated": false, @@ -1788,12 +1935,33 @@ "h": 24 }, "frame": { - "x": 342, - "y": 20, + "x": 289, + "y": 21, "w": 24, "h": 24 } }, + { + "filename": "max_revive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 22, + "h": 24 + }, + "frame": { + "x": 313, + "y": 21, + "w": 22, + "h": 24 + } + }, { "filename": "zap_plate", "rotated": false, @@ -1809,117 +1977,12 @@ "h": 24 }, "frame": { - "x": 366, + "x": 335, "y": 20, "w": 24, "h": 24 } }, - { - "filename": "lure", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 17, - "h": 24 - }, - "frame": { - "x": 390, - "y": 20, - "w": 17, - "h": 24 - } - }, - { - "filename": "oval_charm", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 21, - "h": 24 - }, - "frame": { - "x": 407, - "y": 24, - "w": 21, - "h": 24 - } - }, - { - "filename": "berry_pot", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 18, - "h": 22 - }, - "frame": { - "x": 59, - "y": 48, - "w": 18, - "h": 22 - } - }, - { - "filename": "adamant_crystal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 6, - "w": 23, - "h": 21 - }, - "frame": { - "x": 59, - "y": 27, - "w": 23, - "h": 21 - } - }, - { - "filename": "coin_case", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, - "h": 23 - }, - "frame": { - "x": 82, - "y": 26, - "w": 24, - "h": 23 - } - }, { "filename": "expert_belt", "rotated": false, @@ -1935,222 +1998,12 @@ "h": 23 }, "frame": { - "x": 106, - "y": 26, + "x": 359, + "y": 20, "w": 24, "h": 23 } }, - { - "filename": "exp_balance", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, - "h": 22 - }, - "frame": { - "x": 77, - "y": 49, - "w": 24, - "h": 22 - } - }, - { - "filename": "exp_share", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, - "h": 22 - }, - "frame": { - "x": 101, - "y": 49, - "w": 24, - "h": 22 - } - }, - { - "filename": "silver_powder", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 24, - "h": 15 - }, - "frame": { - "x": 64, - "y": 71, - "w": 24, - "h": 15 - } - }, - { - "filename": "dragon_scale", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 24, - "h": 18 - }, - "frame": { - "x": 88, - "y": 71, - "w": 24, - "h": 18 - } - }, - { - "filename": "full_heal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 9, - "y": 4, - "w": 15, - "h": 23 - }, - "frame": { - "x": 73, - "y": 86, - "w": 15, - "h": 23 - } - }, - { - "filename": "golden_net", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, - "h": 21 - }, - "frame": { - "x": 88, - "y": 89, - "w": 24, - "h": 21 - } - }, - { - "filename": "max_revive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 22, - "h": 24 - }, - "frame": { - "x": 73, - "y": 110, - "w": 22, - "h": 24 - } - }, - { - "filename": "iron", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 81, - "y": 134, - "w": 16, - "h": 24 - } - }, - { - "filename": "max_elixir", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 95, - "y": 110, - "w": 18, - "h": 24 - } - }, - { - "filename": "max_ether", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 97, - "y": 134, - "w": 18, - "h": 24 - } - }, { "filename": "hearthflame_mask", "rotated": false, @@ -2166,8 +2019,8 @@ "h": 23 }, "frame": { - "x": 87, - "y": 158, + "x": 383, + "y": 20, "w": 24, "h": 23 } @@ -2187,201 +2040,12 @@ "h": 23 }, "frame": { - "x": 87, - "y": 181, + "x": 407, + "y": 28, "w": 24, "h": 23 } }, - { - "filename": "red_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 20, - "h": 24 - }, - "frame": { - "x": 92, - "y": 204, - "w": 20, - "h": 24 - } - }, - { - "filename": "shiny_charm", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 21, - "h": 24 - }, - "frame": { - "x": 92, - "y": 228, - "w": 21, - "h": 24 - } - }, - { - "filename": "black_belt", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 92, - "y": 252, - "w": 22, - "h": 23 - } - }, - { - "filename": "bug_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 93, - "y": 275, - "w": 22, - "h": 23 - } - }, - { - "filename": "dark_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 94, - "y": 298, - "w": 22, - "h": 23 - } - }, - { - "filename": "dragon_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 94, - "y": 321, - "w": 22, - "h": 23 - } - }, - { - "filename": "dynamax_band", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 23, - "h": 23 - }, - "frame": { - "x": 94, - "y": 344, - "w": 23, - "h": 23 - } - }, - { - "filename": "griseous_core", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 94, - "y": 367, - "w": 23, - "h": 23 - } - }, - { - "filename": "leek", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 94, - "y": 390, - "w": 23, - "h": 23 - } - }, { "filename": "candy_overlay", "rotated": false, @@ -2397,14 +2061,14 @@ "h": 15 }, "frame": { - "x": 94, - "y": 413, + "x": 169, + "y": 45, "w": 16, "h": 15 } }, { - "filename": "eviolite", + "filename": "exp_balance", "rotated": false, "trimmed": true, "sourceSize": { @@ -2412,57 +2076,57 @@ "h": 32 }, "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 15, - "h": 15 - }, - "frame": { - "x": 110, - "y": 413, - "w": 15, - "h": 15 - } - }, - { - "filename": "max_lure", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 17, - "h": 24 - }, - "frame": { - "x": 112, - "y": 71, - "w": 17, - "h": 24 - } - }, - { - "filename": "bug_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, + "x": 4, "y": 5, - "w": 22, + "w": 24, "h": 22 }, "frame": { - "x": 125, - "y": 49, - "w": 22, + "x": 185, + "y": 45, + "w": 24, + "h": 22 + } + }, + { + "filename": "exp_share", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 24, + "h": 22 + }, + "frame": { + "x": 209, + "y": 45, + "w": 24, + "h": 22 + } + }, + { + "filename": "peat_block", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 24, + "h": 22 + }, + "frame": { + "x": 233, + "y": 45, + "w": 24, "h": 22 } }, @@ -2481,7 +2145,7 @@ "h": 23 }, "frame": { - "x": 147, + "x": 257, "y": 45, "w": 24, "h": 23 @@ -2502,14 +2166,14 @@ "h": 23 }, "frame": { - "x": 171, + "x": 281, "y": 45, "w": 24, "h": 23 } }, { - "filename": "macho_brace", + "filename": "berry_pouch", "rotated": false, "trimmed": true, "sourceSize": { @@ -2523,35 +2187,14 @@ "h": 23 }, "frame": { - "x": 195, + "x": 305, "y": 45, "w": 23, "h": 23 } }, { - "filename": "peat_block", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, - "h": 22 - }, - "frame": { - "x": 218, - "y": 45, - "w": 24, - "h": 22 - } - }, - { - "filename": "healing_charm", + "filename": "black_belt", "rotated": false, "trimmed": true, "sourceSize": { @@ -2560,82 +2203,19 @@ }, "spriteSourceSize": { "x": 5, - "y": 5, - "w": 23, - "h": 22 - }, - "frame": { - "x": 242, - "y": 45, - "w": 23, - "h": 22 - } - }, - { - "filename": "rare_candy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 265, - "y": 45, - "w": 23, - "h": 23 - } - }, - { - "filename": "rarer_candy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 288, - "y": 45, - "w": 23, - "h": 23 - } - }, - { - "filename": "electric_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, "y": 4, "w": 22, "h": 23 }, "frame": { - "x": 311, + "x": 328, "y": 45, "w": 22, "h": 23 } }, { - "filename": "max_potion", + "filename": "max_ether", "rotated": false, "trimmed": true, "sourceSize": { @@ -2649,140 +2229,14 @@ "h": 24 }, "frame": { - "x": 129, - "y": 71, + "x": 350, + "y": 44, "w": 18, "h": 24 } }, { - "filename": "fairy_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 147, - "y": 68, - "w": 22, - "h": 23 - } - }, - { - "filename": "fighting_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 169, - "y": 68, - "w": 22, - "h": 23 - } - }, - { - "filename": "fire_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 23 - }, - "frame": { - "x": 191, - "y": 68, - "w": 22, - "h": 23 - } - }, - { - "filename": "dragon_fang", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 21, - "h": 23 - }, - "frame": { - "x": 333, - "y": 45, - "w": 21, - "h": 23 - } - }, - { - "filename": "fire_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 354, - "y": 44, - "w": 22, - "h": 23 - } - }, - { - "filename": "flying_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 376, - "y": 44, - "w": 22, - "h": 23 - } - }, - { - "filename": "icy_reins_of_unity", + "filename": "reveal_glass", "rotated": false, "trimmed": true, "sourceSize": { @@ -2791,36 +2245,15 @@ }, "spriteSourceSize": { "x": 4, - "y": 7, - "w": 24, - "h": 20 + "y": 4, + "w": 23, + "h": 24 }, "frame": { - "x": 398, - "y": 48, - "w": 24, - "h": 20 - } - }, - { - "filename": "prism_scale", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 9, - "y": 8, - "w": 15, - "h": 15 - }, - "frame": { - "x": 112, - "y": 95, - "w": 15, - "h": 15 + "x": 368, + "y": 43, + "w": 23, + "h": 24 } }, { @@ -2838,266 +2271,14 @@ "h": 24 }, "frame": { - "x": 113, - "y": 110, + "x": 391, + "y": 43, "w": 16, "h": 24 } }, { - "filename": "pp_max", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 115, - "y": 134, - "w": 16, - "h": 24 - } - }, - { - "filename": "focus_sash", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 111, - "y": 158, - "w": 22, - "h": 23 - } - }, - { - "filename": "ghost_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 111, - "y": 181, - "w": 22, - "h": 23 - } - }, - { - "filename": "grass_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 112, - "y": 204, - "w": 22, - "h": 23 - } - }, - { - "filename": "ground_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 113, - "y": 227, - "w": 22, - "h": 23 - } - }, - { - "filename": "ice_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 114, - "y": 250, - "w": 22, - "h": 23 - } - }, - { - "filename": "lansat_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 21, - "h": 23 - }, - "frame": { - "x": 115, - "y": 273, - "w": 21, - "h": 23 - } - }, - { - "filename": "leaf_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 21, - "h": 23 - }, - "frame": { - "x": 116, - "y": 296, - "w": 21, - "h": 23 - } - }, - { - "filename": "never_melt_ice", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 23 - }, - "frame": { - "x": 116, - "y": 319, - "w": 22, - "h": 23 - } - }, - { - "filename": "normal_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 117, - "y": 342, - "w": 22, - "h": 23 - } - }, - { - "filename": "petaya_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 23 - }, - "frame": { - "x": 117, - "y": 365, - "w": 22, - "h": 23 - } - }, - { - "filename": "poison_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 117, - "y": 388, - "w": 22, - "h": 23 - } - }, - { - "filename": "black_glasses", + "filename": "golden_net", "rotated": false, "trimmed": true, "sourceSize": { @@ -3106,40 +2287,19 @@ }, "spriteSourceSize": { "x": 4, - "y": 8, - "w": 23, - "h": 17 - }, - "frame": { - "x": 125, - "y": 411, - "w": 23, - "h": 17 - } - }, - { - "filename": "hyper_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, "y": 5, - "w": 17, - "h": 23 + "w": 24, + "h": 21 }, "frame": { - "x": 213, - "y": 68, - "w": 17, - "h": 23 + "x": 407, + "y": 51, + "w": 24, + "h": 21 } }, { - "filename": "psychic_tera_shard", + "filename": "icy_reins_of_unity", "rotated": false, "trimmed": true, "sourceSize": { @@ -3147,20 +2307,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 + "x": 4, + "y": 7, + "w": 24, + "h": 20 }, "frame": { - "x": 230, + "x": 174, "y": 67, - "w": 22, - "h": 23 + "w": 24, + "h": 20 } }, { - "filename": "rusted_sword", + "filename": "metal_powder", "rotated": false, "trimmed": true, "sourceSize": { @@ -3169,15 +2329,120 @@ }, "spriteSourceSize": { "x": 4, - "y": 5, - "w": 23, - "h": 22 + "y": 6, + "w": 24, + "h": 20 }, "frame": { - "x": 252, + "x": 198, + "y": 67, + "w": 24, + "h": 20 + } + }, + { + "filename": "quick_powder", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 6, + "w": 24, + "h": 20 + }, + "frame": { + "x": 222, + "y": 67, + "w": 24, + "h": 20 + } + }, + { + "filename": "rusted_shield", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 6, + "w": 24, + "h": 20 + }, + "frame": { + "x": 246, "y": 68, - "w": 23, - "h": 22 + "w": 24, + "h": 20 + } + }, + { + "filename": "sacred_ash", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 24, + "h": 20 + }, + "frame": { + "x": 270, + "y": 68, + "w": 24, + "h": 20 + } + }, + { + "filename": "shadow_reins_of_unity", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 24, + "h": 20 + }, + "frame": { + "x": 294, + "y": 68, + "w": 24, + "h": 20 + } + }, + { + "filename": "soft_sand", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 24, + "h": 20 + }, + "frame": { + "x": 318, + "y": 68, + "w": 24, + "h": 20 } }, { @@ -3195,7 +2460,7 @@ "h": 21 }, "frame": { - "x": 275, + "x": 342, "y": 68, "w": 23, "h": 21 @@ -3216,77 +2481,14 @@ "h": 21 }, "frame": { - "x": 298, - "y": 68, - "w": 23, - "h": 21 - } - }, - { - "filename": "metal_powder", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 6, - "w": 24, - "h": 20 - }, - "frame": { - "x": 321, - "y": 68, - "w": 24, - "h": 20 - } - }, - { - "filename": "apicot_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 20 - }, - "frame": { - "x": 345, - "y": 68, - "w": 19, - "h": 20 - } - }, - { - "filename": "moon_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 6, - "w": 23, - "h": 21 - }, - "frame": { - "x": 364, + "x": 368, "y": 67, "w": 23, "h": 21 } }, { - "filename": "quick_powder", + "filename": "pp_max", "rotated": false, "trimmed": true, "sourceSize": { @@ -3294,20 +2496,41 @@ "h": 32 }, "spriteSourceSize": { - "x": 4, + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 391, + "y": 67, + "w": 16, + "h": 24 + } + }, + { + "filename": "binding_band", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, "y": 6, - "w": 24, + "w": 23, "h": 20 }, "frame": { - "x": 387, - "y": 68, - "w": 24, + "x": 407, + "y": 72, + "w": 23, "h": 20 } }, { - "filename": "super_lure", + "filename": "max_lure", "rotated": false, "trimmed": true, "sourceSize": { @@ -3321,77 +2544,14 @@ "h": 24 }, "frame": { - "x": 411, - "y": 68, + "x": 73, + "y": 87, "w": 17, "h": 24 } }, { - "filename": "rusted_shield", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 6, - "w": 24, - "h": 20 - }, - "frame": { - "x": 147, - "y": 91, - "w": 24, - "h": 20 - } - }, - { - "filename": "sacred_ash", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 24, - "h": 20 - }, - "frame": { - "x": 171, - "y": 91, - "w": 24, - "h": 20 - } - }, - { - "filename": "shadow_reins_of_unity", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 24, - "h": 20 - }, - "frame": { - "x": 195, - "y": 91, - "w": 24, - "h": 20 - } - }, - { - "filename": "sachet", + "filename": "bug_tera_shard", "rotated": false, "trimmed": true, "sourceSize": { @@ -3401,18 +2561,18 @@ "spriteSourceSize": { "x": 6, "y": 4, - "w": 18, + "w": 22, "h": 23 }, "frame": { - "x": 129, - "y": 95, - "w": 18, + "x": 73, + "y": 111, + "w": 22, "h": 23 } }, { - "filename": "relic_band", + "filename": "max_potion", "rotated": false, "trimmed": true, "sourceSize": { @@ -3421,15 +2581,141 @@ }, "spriteSourceSize": { "x": 7, - "y": 9, - "w": 17, - "h": 16 + "y": 4, + "w": 18, + "h": 24 }, "frame": { - "x": 129, - "y": 118, - "w": 17, - "h": 16 + "x": 78, + "y": 134, + "w": 18, + "h": 24 + } + }, + { + "filename": "oval_charm", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 21, + "h": 24 + }, + "frame": { + "x": 79, + "y": 158, + "w": 21, + "h": 24 + } + }, + { + "filename": "shiny_charm", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 21, + "h": 24 + }, + "frame": { + "x": 79, + "y": 182, + "w": 21, + "h": 24 + } + }, + { + "filename": "dynamax_band", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 23, + "h": 23 + }, + "frame": { + "x": 90, + "y": 73, + "w": 23, + "h": 23 + } + }, + { + "filename": "eviolite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 15, + "h": 15 + }, + "frame": { + "x": 90, + "y": 96, + "w": 15, + "h": 15 + } + }, + { + "filename": "dark_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 95, + "y": 111, + "w": 22, + "h": 23 + } + }, + { + "filename": "red_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 20, + "h": 24 + }, + "frame": { + "x": 96, + "y": 134, + "w": 20, + "h": 24 } }, { @@ -3447,8 +2733,8 @@ "h": 24 }, "frame": { - "x": 131, - "y": 134, + "x": 100, + "y": 158, "w": 16, "h": 24 } @@ -3468,14 +2754,14 @@ "h": 24 }, "frame": { - "x": 133, - "y": 158, + "x": 100, + "y": 182, "w": 16, "h": 24 } }, { - "filename": "charcoal", + "filename": "griseous_core", "rotated": false, "trimmed": true, "sourceSize": { @@ -3485,18 +2771,18 @@ "spriteSourceSize": { "x": 5, "y": 5, - "w": 22, - "h": 22 + "w": 23, + "h": 23 }, "frame": { - "x": 133, - "y": 182, - "w": 22, - "h": 22 + "x": 92, + "y": 206, + "w": 23, + "h": 23 } }, { - "filename": "reaper_cloth", + "filename": "leek", "rotated": false, "trimmed": true, "sourceSize": { @@ -3504,20 +2790,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, + "x": 4, "y": 5, - "w": 22, + "w": 23, "h": 23 }, "frame": { - "x": 134, - "y": 204, - "w": 22, + "x": 92, + "y": 229, + "w": 23, "h": 23 } }, { - "filename": "rock_tera_shard", + "filename": "dragon_tera_shard", "rotated": false, "trimmed": true, "sourceSize": { @@ -3531,14 +2817,14 @@ "h": 23 }, "frame": { - "x": 135, - "y": 227, + "x": 93, + "y": 252, "w": 22, "h": 23 } }, { - "filename": "sharp_beak", + "filename": "dragon_fang", "rotated": false, "trimmed": true, "sourceSize": { @@ -3551,15 +2837,204 @@ "w": 21, "h": 23 }, + "frame": { + "x": 94, + "y": 275, + "w": 21, + "h": 23 + } + }, + { + "filename": "electric_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 94, + "y": 298, + "w": 22, + "h": 23 + } + }, + { + "filename": "fairy_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 94, + "y": 321, + "w": 22, + "h": 23 + } + }, + { + "filename": "fighting_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 94, + "y": 344, + "w": 22, + "h": 23 + } + }, + { + "filename": "fire_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 23 + }, + "frame": { + "x": 94, + "y": 367, + "w": 22, + "h": 23 + } + }, + { + "filename": "fire_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 94, + "y": 390, + "w": 22, + "h": 23 + } + }, + { + "filename": "relic_crown", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 23, + "h": 18 + }, + "frame": { + "x": 94, + "y": 413, + "w": 23, + "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": 105, + "y": 96, + "w": 15, + "h": 15 + } + }, + { + "filename": "coupon", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 23, + "h": 19 + }, + "frame": { + "x": 113, + "y": 77, + "w": 23, + "h": 19 + } + }, + { + "filename": "full_heal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 9, + "y": 4, + "w": 15, + "h": 23 + }, "frame": { "x": 136, - "y": 250, - "w": 21, + "y": 77, + "w": 15, "h": 23 } }, { - "filename": "steel_tera_shard", + "filename": "golden_mystic_ticket", "rotated": false, "trimmed": true, "sourceSize": { @@ -3567,121 +3042,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 + "x": 4, + "y": 7, + "w": 23, + "h": 19 }, "frame": { - "x": 136, - "y": 273, - "w": 22, - "h": 23 - } - }, - { - "filename": "stellar_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 137, - "y": 296, - "w": 22, - "h": 23 - } - }, - { - "filename": "water_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 138, - "y": 319, - "w": 22, - "h": 23 - } - }, - { - "filename": "whipped_dream", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 21, - "h": 23 - }, - "frame": { - "x": 139, - "y": 342, - "w": 21, - "h": 23 - } - }, - { - "filename": "wide_lens", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 139, - "y": 365, - "w": 22, - "h": 23 - } - }, - { - "filename": "mystic_water", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 20, - "h": 23 - }, - "frame": { - "x": 139, - "y": 388, - "w": 20, - "h": 23 + "x": 151, + "y": 81, + "w": 23, + "h": 19 } }, { @@ -3699,33 +3069,12 @@ "h": 17 }, "frame": { - "x": 148, - "y": 411, + "x": 174, + "y": 87, "w": 23, "h": 17 } }, - { - "filename": "potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 23 - }, - "frame": { - "x": 159, - "y": 388, - "w": 17, - "h": 23 - } - }, { "filename": "chill_drive", "rotated": false, @@ -3741,12 +3090,264 @@ "h": 17 }, "frame": { - "x": 171, - "y": 411, + "x": 197, + "y": 87, "w": 23, "h": 17 } }, + { + "filename": "douse_drive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 23, + "h": 17 + }, + "frame": { + "x": 220, + "y": 87, + "w": 23, + "h": 17 + } + }, + { + "filename": "healing_charm", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 23, + "h": 22 + }, + "frame": { + "x": 243, + "y": 88, + "w": 23, + "h": 22 + } + }, + { + "filename": "macho_brace", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 23, + "h": 23 + }, + "frame": { + "x": 266, + "y": 88, + "w": 23, + "h": 23 + } + }, + { + "filename": "rare_candy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 23, + "h": 23 + }, + "frame": { + "x": 289, + "y": 88, + "w": 23, + "h": 23 + } + }, + { + "filename": "rarer_candy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 23, + "h": 23 + }, + "frame": { + "x": 312, + "y": 88, + "w": 23, + "h": 23 + } + }, + { + "filename": "rusted_sword", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 23, + "h": 22 + }, + "frame": { + "x": 335, + "y": 89, + "w": 23, + "h": 22 + } + }, + { + "filename": "abomasite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 120, + "y": 96, + "w": 16, + "h": 16 + } + }, + { + "filename": "bug_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 117, + "y": 112, + "w": 22, + "h": 22 + } + }, + { + "filename": "flying_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 116, + "y": 134, + "w": 22, + "h": 23 + } + }, + { + "filename": "focus_sash", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 116, + "y": 157, + "w": 22, + "h": 23 + } + }, + { + "filename": "ghost_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 116, + "y": 180, + "w": 22, + "h": 23 + } + }, + { + "filename": "grass_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 139, + "y": 100, + "w": 22, + "h": 23 + } + }, { "filename": "berry_juice", "rotated": false, @@ -3762,14 +3363,14 @@ "h": 21 }, "frame": { - "x": 219, - "y": 91, + "x": 139, + "y": 123, "w": 22, "h": 21 } }, { - "filename": "dark_memory", + "filename": "ground_tera_shard", "rotated": false, "trimmed": true, "sourceSize": { @@ -3777,20 +3378,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, + "x": 6, + "y": 4, "w": 22, - "h": 22 + "h": 23 }, "frame": { - "x": 241, - "y": 90, + "x": 138, + "y": 144, "w": 22, - "h": 22 + "h": 23 } }, { - "filename": "dire_hit", + "filename": "ice_tera_shard", "rotated": false, "trimmed": true, "sourceSize": { @@ -3798,41 +3399,41 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, + "x": 6, + "y": 4, "w": 22, - "h": 22 + "h": 23 }, "frame": { - "x": 263, - "y": 90, + "x": 138, + "y": 167, "w": 22, - "h": 22 + "h": 23 } }, { - "filename": "dna_splicers", + "filename": "black_sludge", "rotated": false, "trimmed": true, "sourceSize": { - "w": 32, - "h": 32 + "w": 24, + "h": 24 }, "spriteSourceSize": { - "x": 5, - "y": 5, + "x": 1, + "y": 2, "w": 22, - "h": 22 + "h": 19 }, "frame": { - "x": 285, - "y": 89, + "x": 138, + "y": 190, "w": 22, - "h": 22 + "h": 19 } }, { - "filename": "dragon_memory", + "filename": "never_melt_ice", "rotated": false, "trimmed": true, "sourceSize": { @@ -3843,17 +3444,17 @@ "x": 5, "y": 5, "w": 22, - "h": 22 + "h": 23 }, "frame": { - "x": 307, - "y": 89, + "x": 161, + "y": 104, "w": 22, - "h": 22 + "h": 23 } }, { - "filename": "electirizer", + "filename": "normal_tera_shard", "rotated": false, "trimmed": true, "sourceSize": { @@ -3861,20 +3462,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, + "x": 6, + "y": 4, "w": 22, - "h": 22 + "h": 23 }, "frame": { - "x": 329, - "y": 88, + "x": 183, + "y": 104, "w": 22, - "h": 22 + "h": 23 } }, { - "filename": "electric_memory", + "filename": "petaya_berry", "rotated": false, "trimmed": true, "sourceSize": { @@ -3885,34 +3486,13 @@ "x": 5, "y": 5, "w": 22, - "h": 22 + "h": 23 }, "frame": { - "x": 351, - "y": 88, + "x": 205, + "y": 104, "w": 22, - "h": 22 - } - }, - { - "filename": "enigma_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 373, - "y": 88, - "w": 22, - "h": 22 + "h": 23 } }, { @@ -3930,35 +3510,14 @@ "h": 24 }, "frame": { - "x": 395, - "y": 88, + "x": 227, + "y": 104, "w": 16, "h": 24 } }, { - "filename": "super_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 23 - }, - "frame": { - "x": 411, - "y": 92, - "w": 17, - "h": 23 - } - }, - { - "filename": "soft_sand", + "filename": "moon_stone", "rotated": false, "trimmed": true, "sourceSize": { @@ -3967,36 +3526,15 @@ }, "spriteSourceSize": { "x": 4, - "y": 7, - "w": 24, - "h": 20 - }, - "frame": { - "x": 147, - "y": 111, - "w": 24, - "h": 20 - } - }, - { - "filename": "binding_band", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, "y": 6, "w": 23, - "h": 20 + "h": 21 }, "frame": { - "x": 171, - "y": 111, + "x": 243, + "y": 110, "w": 23, - "h": 20 + "h": 21 } }, { @@ -4014,54 +3552,12 @@ "h": 21 }, "frame": { - "x": 194, + "x": 266, "y": 111, "w": 23, "h": 21 } }, - { - "filename": "fairy_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 147, - "y": 131, - "w": 22, - "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": 169, - "y": 131, - "w": 22, - "h": 22 - } - }, { "filename": "n_solarizer", "rotated": false, @@ -4077,8 +3573,8 @@ "h": 21 }, "frame": { - "x": 217, - "y": 112, + "x": 289, + "y": 111, "w": 23, "h": 21 } @@ -4098,35 +3594,14 @@ "h": 21 }, "frame": { - "x": 240, - "y": 112, + "x": 312, + "y": 111, "w": 23, "h": 21 } }, { - "filename": "deep_sea_tooth", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 263, - "y": 112, - "w": 22, - "h": 21 - } - }, - { - "filename": "fire_memory", + "filename": "charcoal", "rotated": false, "trimmed": true, "sourceSize": { @@ -4140,14 +3615,14 @@ "h": 22 }, "frame": { - "x": 285, + "x": 335, "y": 111, "w": 22, "h": 22 } }, { - "filename": "flying_memory", + "filename": "mystic_ticket", "rotated": false, "trimmed": true, "sourceSize": { @@ -4155,20 +3630,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 + "x": 4, + "y": 7, + "w": 23, + "h": 19 }, "frame": { - "x": 307, - "y": 111, - "w": 22, - "h": 22 + "x": 161, + "y": 127, + "w": 23, + "h": 19 } }, { - "filename": "ganlon_berry", + "filename": "poison_tera_shard", "rotated": false, "trimmed": true, "sourceSize": { @@ -4176,545 +3651,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 329, - "y": 110, - "w": 22, - "h": 22 - } - }, - { - "filename": "ghost_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 351, - "y": 110, - "w": 22, - "h": 22 - } - }, - { - "filename": "grass_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 373, - "y": 110, - "w": 22, - "h": 22 - } - }, - { - "filename": "ground_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 191, - "y": 132, - "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": 149, - "y": 153, - "w": 22, - "h": 22 - } - }, - { - "filename": "hard_meteorite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 20, - "h": 22 - }, - "frame": { - "x": 171, - "y": 153, - "w": 20, - "h": 22 - } - }, - { - "filename": "ice_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 191, - "y": 154, - "w": 22, - "h": 22 - } - }, - { - "filename": "ice_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 213, - "y": 133, - "w": 22, - "h": 22 - } - }, - { - "filename": "magmarizer", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 235, - "y": 133, - "w": 22, - "h": 22 - } - }, - { - "filename": "mini_black_hole", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 257, - "y": 133, - "w": 22, - "h": 22 - } - }, - { - "filename": "normal_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 279, - "y": 133, - "w": 22, - "h": 22 - } - }, - { - "filename": "poison_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 301, - "y": 133, - "w": 22, - "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": 213, - "y": 155, - "w": 22, - "h": 21 - } - }, - { - "filename": "protector", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 235, - "y": 155, - "w": 22, - "h": 22 - } - }, - { - "filename": "psychic_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 257, - "y": 155, - "w": 22, - "h": 22 - } - }, - { - "filename": "rock_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 279, - "y": 155, - "w": 22, - "h": 22 - } - }, - { - "filename": "scroll_of_darkness", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 301, - "y": 155, - "w": 22, - "h": 22 - } - }, - { - "filename": "super_repel", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, + "x": 6, "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 395, - "y": 112, - "w": 16, - "h": 24 - } - }, - { - "filename": "metronome", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 17, - "h": 22 - }, - "frame": { - "x": 411, - "y": 115, - "w": 17, - "h": 22 - } - }, - { - "filename": "scroll_of_waters", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, "w": 22, - "h": 22 - }, - "frame": { - "x": 155, - "y": 175, - "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": 156, - "y": 197, - "w": 22, - "h": 22 - } - }, - { - "filename": "starf_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 157, - "y": 219, - "w": 22, - "h": 22 - } - }, - { - "filename": "steel_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 157, - "y": 241, - "w": 22, - "h": 22 - } - }, - { - "filename": "thick_club", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 158, - "y": 263, - "w": 22, - "h": 22 - } - }, - { - "filename": "thunder_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 159, - "y": 285, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_bug", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 + "h": 23 }, "frame": { "x": 160, - "y": 307, + "y": 146, "w": 22, - "h": 22 + "h": 23 } }, { - "filename": "tm_dark", + "filename": "psychic_tera_shard", "rotated": false, "trimmed": true, "sourceSize": { @@ -4722,20 +3672,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, + "x": 6, + "y": 4, "w": 22, - "h": 22 + "h": 23 }, "frame": { "x": 160, - "y": 329, + "y": 169, "w": 22, - "h": 22 + "h": 23 } }, { - "filename": "sweet_apple", + "filename": "pair_of_tickets", "rotated": false, "trimmed": true, "sourceSize": { @@ -4743,20 +3693,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 + "x": 4, + "y": 7, + "w": 23, + "h": 19 }, "frame": { - "x": 177, - "y": 176, - "w": 22, - "h": 21 + "x": 184, + "y": 127, + "w": 23, + "h": 19 } }, { - "filename": "tm_dragon", + "filename": "reaper_cloth", "rotated": false, "trimmed": true, "sourceSize": { @@ -4767,227 +3717,17 @@ "x": 5, "y": 5, "w": 22, - "h": 22 - }, - "frame": { - "x": 178, - "y": 197, - "w": 22, - "h": 22 - } - }, - { - "filename": "syrupy_apple", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 199, - "y": 176, - "w": 22, - "h": 21 - } - }, - { - "filename": "tm_electric", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 179, - "y": 219, - "w": 22, - "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": 179, - "y": 241, - "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": 200, - "y": 197, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_fire", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 180, - "y": 263, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_flying", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 181, - "y": 285, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_ghost", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 201, - "y": 219, - "w": 22, - "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": 201, - "y": 241, - "w": 22, - "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": 202, - "y": 263, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_ice", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 + "h": 23 }, "frame": { "x": 182, - "y": 307, + "y": 146, "w": 22, - "h": 22 + "h": 23 } }, { - "filename": "tm_normal", + "filename": "rock_tera_shard", "rotated": false, "trimmed": true, "sourceSize": { @@ -4995,20 +3735,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, + "x": 6, + "y": 4, "w": 22, - "h": 22 + "h": 23 }, "frame": { "x": 182, - "y": 329, + "y": 169, "w": 22, - "h": 22 + "h": 23 } }, { - "filename": "tm_poison", + "filename": "blue_orb", "rotated": false, "trimmed": true, "sourceSize": { @@ -5016,20 +3756,41 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 + "x": 6, + "y": 6, + "w": 20, + "h": 20 }, "frame": { - "x": 203, - "y": 285, - "w": 22, - "h": 22 + "x": 207, + "y": 127, + "w": 20, + "h": 20 } }, { - "filename": "tm_psychic", + "filename": "steel_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 204, + "y": 147, + "w": 22, + "h": 23 + } + }, + { + "filename": "dark_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -5044,28 +3805,7 @@ }, "frame": { "x": 204, - "y": 307, - "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": 204, - "y": 329, + "y": 170, "w": 22, "h": 22 } @@ -5085,96 +3825,12 @@ "h": 20 }, "frame": { - "x": 221, - "y": 177, + "x": 160, + "y": 192, "w": 23, "h": 20 } }, - { - "filename": "tm_steel", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 222, - "y": 197, - "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": 244, - "y": 177, - "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": 266, - "y": 177, - "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": 288, - "y": 177, - "w": 22, - "h": 22 - } - }, { "filename": "shell_bell", "rotated": false, @@ -5190,14 +3846,161 @@ "h": 20 }, "frame": { - "x": 244, - "y": 199, + "x": 183, + "y": 192, "w": 23, "h": 20 } }, { - "filename": "x_accuracy", + "filename": "dawn_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 21 + }, + "frame": { + "x": 206, + "y": 192, + "w": 20, + "h": 21 + } + }, + { + "filename": "super_repel", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 227, + "y": 128, + "w": 16, + "h": 24 + } + }, + { + "filename": "deep_sea_tooth", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 243, + "y": 131, + "w": 22, + "h": 21 + } + }, + { + "filename": "stellar_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 226, + "y": 152, + "w": 22, + "h": 23 + } + }, + { + "filename": "water_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 226, + "y": 175, + "w": 22, + "h": 23 + } + }, + { + "filename": "deep_sea_scale", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 20 + }, + "frame": { + "x": 265, + "y": 132, + "w": 22, + "h": 20 + } + }, + { + "filename": "wide_lens", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 248, + "y": 152, + "w": 22, + "h": 23 + } + }, + { + "filename": "dire_hit", "rotated": false, "trimmed": true, "sourceSize": { @@ -5211,14 +4014,14 @@ "h": 22 }, "frame": { - "x": 223, - "y": 219, + "x": 248, + "y": 175, "w": 22, "h": 22 } }, { - "filename": "x_attack", + "filename": "dna_splicers", "rotated": false, "trimmed": true, "sourceSize": { @@ -5232,14 +4035,833 @@ "h": 22 }, "frame": { - "x": 223, - "y": 241, + "x": 287, + "y": 132, "w": 22, "h": 22 } }, { - "filename": "x_defense", + "filename": "dragon_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 309, + "y": 132, + "w": 22, + "h": 22 + } + }, + { + "filename": "super_lure", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 17, + "h": 24 + }, + "frame": { + "x": 270, + "y": 152, + "w": 17, + "h": 24 + } + }, + { + "filename": "electirizer", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 287, + "y": 154, + "w": 22, + "h": 22 + } + }, + { + "filename": "electric_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 309, + "y": 154, + "w": 22, + "h": 22 + } + }, + { + "filename": "enigma_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 270, + "y": 176, + "w": 22, + "h": 22 + } + }, + { + "filename": "fairy_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 292, + "y": 176, + "w": 22, + "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": 331, + "y": 133, + "w": 22, + "h": 22 + } + }, + { + "filename": "fire_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 331, + "y": 155, + "w": 22, + "h": 22 + } + }, + { + "filename": "hyper_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 17, + "h": 23 + }, + "frame": { + "x": 314, + "y": 176, + "w": 17, + "h": 23 + } + }, + { + "filename": "flying_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 331, + "y": 177, + "w": 22, + "h": 22 + } + }, + { + "filename": "blunder_policy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 19 + }, + "frame": { + "x": 226, + "y": 198, + "w": 22, + "h": 19 + } + }, + { + "filename": "fairy_feather", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 22, + "h": 20 + }, + "frame": { + "x": 248, + "y": 197, + "w": 22, + "h": 20 + } + }, + { + "filename": "dubious_disc", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 22, + "h": 19 + }, + "frame": { + "x": 270, + "y": 198, + "w": 22, + "h": 19 + } + }, + { + "filename": "ganlon_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 292, + "y": 198, + "w": 22, + "h": 22 + } + }, + { + "filename": "ghost_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 314, + "y": 199, + "w": 22, + "h": 22 + } + }, + { + "filename": "berry_pot", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 5, + "w": 18, + "h": 22 + }, + "frame": { + "x": 336, + "y": 199, + "w": 18, + "h": 22 + } + }, + { + "filename": "grass_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 116, + "y": 203, + "w": 22, + "h": 22 + } + }, + { + "filename": "ground_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 115, + "y": 225, + "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": 115, + "y": 247, + "w": 22, + "h": 22 + } + }, + { + "filename": "ice_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 115, + "y": 269, + "w": 22, + "h": 22 + } + }, + { + "filename": "ice_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 138, + "y": 209, + "w": 22, + "h": 22 + } + }, + { + "filename": "lansat_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 21, + "h": 23 + }, + "frame": { + "x": 137, + "y": 231, + "w": 21, + "h": 23 + } + }, + { + "filename": "leaf_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 21, + "h": 23 + }, + "frame": { + "x": 137, + "y": 254, + "w": 21, + "h": 23 + } + }, + { + "filename": "liechi_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 160, + "y": 212, + "w": 22, + "h": 21 + } + }, + { + "filename": "magmarizer", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 182, + "y": 212, + "w": 22, + "h": 22 + } + }, + { + "filename": "mini_black_hole", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 204, + "y": 213, + "w": 22, + "h": 22 + } + }, + { + "filename": "moon_flute", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 158, + "y": 233, + "w": 22, + "h": 22 + } + }, + { + "filename": "normal_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 158, + "y": 255, + "w": 22, + "h": 22 + } + }, + { + "filename": "poison_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 180, + "y": 234, + "w": 22, + "h": 22 + } + }, + { + "filename": "protector", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 180, + "y": 256, + "w": 22, + "h": 22 + } + }, + { + "filename": "psychic_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 202, + "y": 235, + "w": 22, + "h": 22 + } + }, + { + "filename": "rock_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 202, + "y": 257, + "w": 22, + "h": 22 + } + }, + { + "filename": "malicious_armor", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 20 + }, + "frame": { + "x": 226, + "y": 217, + "w": 22, + "h": 20 + } + }, + { + "filename": "scroll_of_darkness", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 248, + "y": 217, + "w": 22, + "h": 22 + } + }, + { + "filename": "scroll_of_waters", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 270, + "y": 217, + "w": 22, + "h": 22 + } + }, + { + "filename": "sharp_beak", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 21, + "h": 23 + }, + "frame": { + "x": 224, + "y": 237, + "w": 21, + "h": 23 + } + }, + { + "filename": "shed_shell", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 292, + "y": 220, + "w": 22, + "h": 22 + } + }, + { + "filename": "starf_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 314, + "y": 221, + "w": 22, + "h": 22 + } + }, + { + "filename": "dusk_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 21, + "h": 21 + }, + "frame": { + "x": 224, + "y": 260, + "w": 21, + "h": 21 + } + }, + { + "filename": "steel_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -5254,13 +4876,13 @@ }, "frame": { "x": 245, - "y": 219, + "y": 239, "w": 22, "h": 22 } }, { - "filename": "x_sp_atk", + "filename": "sun_flute", "rotated": false, "trimmed": true, "sourceSize": { @@ -5275,13 +4897,13 @@ }, "frame": { "x": 267, - "y": 199, + "y": 239, "w": 22, "h": 22 } }, { - "filename": "x_sp_def", + "filename": "sweet_apple", "rotated": false, "trimmed": true, "sourceSize": { @@ -5290,40 +4912,19 @@ }, "spriteSourceSize": { "x": 5, - "y": 5, + "y": 6, "w": 22, - "h": 22 - }, - "frame": { - "x": 224, - "y": 263, - "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 + "h": 21 }, "frame": { "x": 245, - "y": 241, + "y": 261, "w": 22, - "h": 22 + "h": 21 } }, { - "filename": "sitrus_berry", + "filename": "syrupy_apple", "rotated": false, "trimmed": true, "sourceSize": { @@ -5331,14 +4932,56 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 267, + "y": 261, + "w": 22, + "h": 21 + } + }, + { + "filename": "thick_club", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 289, + "y": 242, + "w": 22, + "h": 22 + } + }, + { + "filename": "hard_meteorite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, "y": 5, "w": 20, "h": 22 }, "frame": { - "x": 225, - "y": 285, + "x": 336, + "y": 221, "w": 20, "h": 22 } @@ -5357,99 +5000,15 @@ "w": 22, "h": 21 }, - "frame": { - "x": 267, - "y": 221, - "w": 22, - "h": 21 - } - }, - { - "filename": "deep_sea_scale", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 20 - }, - "frame": { - "x": 267, - "y": 242, - "w": 22, - "h": 20 - } - }, - { - "filename": "dusk_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 21, - "h": 21 - }, "frame": { "x": 289, - "y": 199, - "w": 21, - "h": 21 - } - }, - { - "filename": "poison_barb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 21, - "h": 21 - }, - "frame": { - "x": 246, - "y": 263, - "w": 21, - "h": 21 - } - }, - { - "filename": "fairy_feather", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, + "y": 264, "w": 22, - "h": 20 - }, - "frame": { - "x": 267, - "y": 262, - "w": 22, - "h": 20 + "h": 21 } }, { - "filename": "shiny_stone", + "filename": "thunder_stone", "rotated": false, "trimmed": true, "sourceSize": { @@ -5458,61 +5017,19 @@ }, "spriteSourceSize": { "x": 5, - "y": 6, - "w": 21, - "h": 21 - }, - "frame": { - "x": 289, - "y": 220, - "w": 21, - "h": 21 - } - }, - { - "filename": "zoom_lens", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 21, - "h": 21 - }, - "frame": { - "x": 289, - "y": 241, - "w": 21, - "h": 21 - } - }, - { - "filename": "lock_capsule", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, "y": 5, - "w": 19, + "w": 22, "h": 22 }, "frame": { - "x": 226, - "y": 307, - "w": 19, + "x": 311, + "y": 243, + "w": 22, "h": 22 } }, { - "filename": "malicious_armor", + "filename": "tm_bug", "rotated": false, "trimmed": true, "sourceSize": { @@ -5521,246 +5038,15 @@ }, "spriteSourceSize": { "x": 5, - "y": 6, - "w": 22, - "h": 20 - }, - "frame": { - "x": 289, - "y": 262, - "w": 22, - "h": 20 - } - }, - { - "filename": "metal_coat", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, "y": 5, - "w": 19, - "h": 22 - }, - "frame": { - "x": 226, - "y": 329, - "w": 19, - "h": 22 - } - }, - { - "filename": "unknown", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 310, - "y": 177, - "w": 16, - "h": 24 - } - }, - { - "filename": "zinc", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 310, - "y": 201, - "w": 16, - "h": 24 - } - }, - { - "filename": "soothe_bell", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 22 - }, - "frame": { - "x": 310, - "y": 225, - "w": 17, - "h": 22 - } - }, - { - "filename": "coupon", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 23, - "h": 19 - }, - "frame": { - "x": 161, - "y": 351, - "w": 23, - "h": 19 - } - }, - { - "filename": "relic_crown", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 23, - "h": 18 - }, - "frame": { - "x": 161, - "y": 370, - "w": 23, - "h": 18 - } - }, - { - "filename": "golden_mystic_ticket", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 23, - "h": 19 - }, - "frame": { - "x": 184, - "y": 351, - "w": 23, - "h": 19 - } - }, - { - "filename": "mystic_ticket", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 23, - "h": 19 - }, - "frame": { - "x": 207, - "y": 351, - "w": 23, - "h": 19 - } - }, - { - "filename": "pair_of_tickets", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 23, - "h": 19 - }, - "frame": { - "x": 184, - "y": 370, - "w": 23, - "h": 19 - } - }, - { - "filename": "leftovers", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 15, - "h": 22 - }, - "frame": { - "x": 176, - "y": 389, - "w": 15, - "h": 22 - } - }, - { - "filename": "black_sludge", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 24, - "h": 24 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, "w": 22, - "h": 19 + "h": 22 }, "frame": { - "x": 207, - "y": 370, + "x": 333, + "y": 243, "w": 22, - "h": 19 + "h": 22 } }, { @@ -5778,14 +5064,14 @@ "h": 20 }, "frame": { - "x": 191, - "y": 389, + "x": 311, + "y": 265, "w": 22, "h": 20 } }, { - "filename": "blunder_policy", + "filename": "tm_dark", "rotated": false, "trimmed": true, "sourceSize": { @@ -5794,225 +5080,15 @@ }, "spriteSourceSize": { "x": 5, - "y": 6, + "y": 5, "w": 22, - "h": 19 + "h": 22 }, "frame": { - "x": 194, - "y": 409, + "x": 333, + "y": 265, "w": 22, - "h": 19 - } - }, - { - "filename": "big_nugget", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 213, - "y": 389, - "w": 20, - "h": 20 - } - }, - { - "filename": "dubious_disc", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 22, - "h": 19 - }, - "frame": { - "x": 216, - "y": 409, - "w": 22, - "h": 19 - } - }, - { - "filename": "big_mushroom", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 19 - }, - "frame": { - "x": 230, - "y": 351, - "w": 19, - "h": 19 - } - }, - { - "filename": "lum_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 20, - "h": 19 - }, - "frame": { - "x": 229, - "y": 370, - "w": 20, - "h": 19 - } - }, - { - "filename": "blue_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 233, - "y": 389, - "w": 20, - "h": 20 - } - }, - { - "filename": "upgrade", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 22, - "h": 19 - }, - "frame": { - "x": 238, - "y": 409, - "w": 22, - "h": 19 - } - }, - { - "filename": "dawn_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 21 - }, - "frame": { - "x": 323, - "y": 133, - "w": 20, - "h": 21 - } - }, - { - "filename": "gb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 323, - "y": 154, - "w": 20, - "h": 20 - } - }, - { - "filename": "douse_drive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 23, - "h": 17 - }, - "frame": { - "x": 343, - "y": 132, - "w": 23, - "h": 17 - } - }, - { - "filename": "magnet", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 343, - "y": 149, - "w": 20, - "h": 20 + "h": 22 } }, { @@ -6030,14 +5106,140 @@ "h": 17 }, "frame": { - "x": 366, - "y": 132, + "x": 137, + "y": 277, "w": 23, "h": 17 } }, { - "filename": "mb", + "filename": "whipped_dream", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 21, + "h": 23 + }, + "frame": { + "x": 116, + "y": 291, + "w": 21, + "h": 23 + } + }, + { + "filename": "mystic_water", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 20, + "h": 23 + }, + "frame": { + "x": 116, + "y": 314, + "w": 20, + "h": 23 + } + }, + { + "filename": "sitrus_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 20, + "h": 22 + }, + "frame": { + "x": 116, + "y": 337, + "w": 20, + "h": 22 + } + }, + { + "filename": "tm_dragon", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 137, + "y": 294, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_electric", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 136, + "y": 316, + "w": 22, + "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": 136, + "y": 338, + "w": 22, + "h": 22 + } + }, + { + "filename": "gb", "rotated": false, "trimmed": true, "sourceSize": { @@ -6051,14 +5253,14 @@ "h": 20 }, "frame": { - "x": 363, - "y": 149, + "x": 116, + "y": 359, "w": 20, "h": 20 } }, { - "filename": "quick_claw", + "filename": "tm_fighting", "rotated": false, "trimmed": true, "sourceSize": { @@ -6066,20 +5268,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 21 + "x": 5, + "y": 5, + "w": 22, + "h": 22 }, "frame": { - "x": 326, - "y": 174, - "w": 19, - "h": 21 + "x": 116, + "y": 379, + "w": 22, + "h": 22 } }, { - "filename": "spell_tag", + "filename": "upgrade", "rotated": false, "trimmed": true, "sourceSize": { @@ -6087,41 +5289,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 19, - "h": 21 - }, - "frame": { - "x": 326, - "y": 195, - "w": 19, - "h": 21 - } - }, - { - "filename": "metal_alloy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, + "x": 5, "y": 7, - "w": 21, + "w": 22, "h": 19 }, "frame": { - "x": 345, - "y": 169, - "w": 21, + "x": 136, + "y": 360, + "w": 22, "h": 19 } }, { - "filename": "pb", + "filename": "tm_fire", "rotated": false, "trimmed": true, "sourceSize": { @@ -6129,58 +5310,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 + "x": 5, + "y": 5, + "w": 22, + "h": 22 }, "frame": { - "x": 345, - "y": 188, - "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": 366, - "y": 169, - "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": 365, - "y": 189, - "w": 20, - "h": 20 + "x": 138, + "y": 379, + "w": 22, + "h": 22 } }, { @@ -6198,12 +5337,348 @@ "h": 17 }, "frame": { - "x": 345, - "y": 208, + "x": 160, + "y": 277, "w": 20, "h": 17 } }, + { + "filename": "tm_flying", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 159, + "y": 294, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_ghost", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 158, + "y": 316, + "w": 22, + "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": 158, + "y": 338, + "w": 22, + "h": 22 + } + }, + { + "filename": "metal_alloy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 21, + "h": 19 + }, + "frame": { + "x": 158, + "y": 360, + "w": 21, + "h": 19 + } + }, + { + "filename": "lock_capsule", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 5, + "w": 19, + "h": 22 + }, + "frame": { + "x": 160, + "y": 379, + "w": 19, + "h": 22 + } + }, + { + "filename": "relic_band", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 9, + "w": 17, + "h": 16 + }, + "frame": { + "x": 180, + "y": 278, + "w": 17, + "h": 16 + } + }, + { + "filename": "metal_coat", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 19, + "h": 22 + }, + "frame": { + "x": 181, + "y": 294, + "w": 19, + "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": 180, + "y": 316, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_ice", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 180, + "y": 338, + "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": 179, + "y": 360, + "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": 179, + "y": 382, + "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": 117, + "y": 401, + "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": 139, + "y": 401, + "w": 22, + "h": 22 + } + }, + { + "filename": "sachet", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 18, + "h": 23 + }, + "frame": { + "x": 161, + "y": 401, + "w": 18, + "h": 23 + } + }, + { + "filename": "tm_steel", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 179, + "y": 404, + "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": 358, + "y": 89, + "w": 15, + "h": 22 + } + }, { "filename": "razor_fang", "rotated": false, @@ -6219,14 +5694,35 @@ "h": 20 }, "frame": { - "x": 327, - "y": 216, + "x": 373, + "y": 88, "w": 18, "h": 20 } }, { - "filename": "masterpiece_teacup", + "filename": "metronome", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 5, + "w": 17, + "h": 22 + }, + "frame": { + "x": 357, + "y": 111, + "w": 17, + "h": 22 + } + }, + { + "filename": "tm_water", "rotated": false, "trimmed": true, "sourceSize": { @@ -6235,19 +5731,19 @@ }, "spriteSourceSize": { "x": 5, - "y": 7, - "w": 21, - "h": 18 + "y": 5, + "w": 22, + "h": 22 }, "frame": { - "x": 365, - "y": 209, - "w": 21, - "h": 18 + "x": 353, + "y": 133, + "w": 22, + "h": 22 } }, { - "filename": "power_herb", + "filename": "water_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -6255,20 +5751,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 20, - "h": 19 + "x": 5, + "y": 5, + "w": 22, + "h": 22 }, "frame": { - "x": 345, - "y": 225, - "w": 20, - "h": 19 + "x": 353, + "y": 155, + "w": 22, + "h": 22 } }, { - "filename": "old_gateau", + "filename": "water_stone", "rotated": false, "trimmed": true, "sourceSize": { @@ -6276,20 +5772,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 21, - "h": 18 + "x": 5, + "y": 5, + "w": 22, + "h": 22 }, "frame": { - "x": 365, - "y": 227, - "w": 21, - "h": 18 + "x": 353, + "y": 177, + "w": 22, + "h": 22 } }, { - "filename": "baton", + "filename": "x_accuracy", "rotated": false, "trimmed": true, "sourceSize": { @@ -6297,20 +5793,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 + "x": 5, + "y": 5, + "w": 22, + "h": 22 }, "frame": { - "x": 327, - "y": 236, - "w": 18, - "h": 18 + "x": 354, + "y": 199, + "w": 22, + "h": 22 } }, { - "filename": "razor_claw", + "filename": "x_attack", "rotated": false, "trimmed": true, "sourceSize": { @@ -6318,20 +5814,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 20, - "h": 19 + "x": 5, + "y": 5, + "w": 22, + "h": 22 }, "frame": { - "x": 345, - "y": 244, - "w": 20, - "h": 19 + "x": 356, + "y": 221, + "w": 22, + "h": 22 } }, { - "filename": "sharp_meteorite", + "filename": "x_defense", "rotated": false, "trimmed": true, "sourceSize": { @@ -6339,20 +5835,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 21, - "h": 18 + "x": 5, + "y": 5, + "w": 22, + "h": 22 }, "frame": { - "x": 365, - "y": 245, - "w": 21, - "h": 18 + "x": 355, + "y": 243, + "w": 22, + "h": 22 } }, { - "filename": "golden_egg", + "filename": "x_sp_atk", "rotated": false, "trimmed": true, "sourceSize": { @@ -6360,20 +5856,41 @@ "h": 32 }, "spriteSourceSize": { - "x": 7, - "y": 6, + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 355, + "y": 265, + "w": 22, + "h": 22 + } + }, + { + "filename": "potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, "w": 17, - "h": 20 + "h": 23 }, "frame": { - "x": 383, - "y": 149, + "x": 374, + "y": 108, "w": 17, - "h": 20 + "h": 23 } }, { - "filename": "hard_stone", + "filename": "unknown", "rotated": false, "trimmed": true, "sourceSize": { @@ -6381,20 +5898,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 20 + "x": 8, + "y": 4, + "w": 16, + "h": 24 }, "frame": { - "x": 385, - "y": 169, - "w": 19, - "h": 20 + "x": 391, + "y": 91, + "w": 16, + "h": 24 } }, { - "filename": "rb", + "filename": "x_sp_def", "rotated": false, "trimmed": true, "sourceSize": { @@ -6402,20 +5919,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 + "x": 5, + "y": 5, + "w": 22, + "h": 22 }, "frame": { - "x": 385, - "y": 189, - "w": 20, - "h": 20 + "x": 407, + "y": 92, + "w": 22, + "h": 22 } }, { - "filename": "smooth_meteorite", + "filename": "zinc", "rotated": false, "trimmed": true, "sourceSize": { @@ -6423,20 +5940,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 20, - "h": 20 + "x": 8, + "y": 4, + "w": 16, + "h": 24 }, "frame": { - "x": 386, - "y": 209, - "w": 20, - "h": 20 + "x": 375, + "y": 131, + "w": 16, + "h": 24 } }, { - "filename": "strange_ball", + "filename": "super_potion", "rotated": false, "trimmed": true, "sourceSize": { @@ -6444,37 +5961,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 + "x": 8, + "y": 5, + "w": 17, + "h": 23 }, "frame": { - "x": 386, - "y": 229, - "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": 386, - "y": 249, - "w": 20, - "h": 20 + "x": 391, + "y": 115, + "w": 17, + "h": 23 } }, { @@ -6492,14 +5988,98 @@ "h": 17 }, "frame": { - "x": 405, - "y": 137, + "x": 408, + "y": 114, "w": 23, "h": 17 } }, { - "filename": "mystery_egg", + "filename": "soothe_bell", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 17, + "h": 22 + }, + "frame": { + "x": 375, + "y": 155, + "w": 17, + "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": 375, + "y": 177, + "w": 22, + "h": 22 + } + }, + { + "filename": "poison_barb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 21, + "h": 21 + }, + "frame": { + "x": 376, + "y": 199, + "w": 21, + "h": 21 + } + }, + { + "filename": "quick_claw", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 21 + }, + "frame": { + "x": 378, + "y": 220, + "w": 19, + "h": 21 + } + }, + { + "filename": "absolite", "rotated": false, "trimmed": true, "sourceSize": { @@ -6510,12 +6090,75 @@ "x": 8, "y": 8, "w": 16, + "h": 16 + }, + "frame": { + "x": 391, + "y": 138, + "w": 16, + "h": 16 + } + }, + { + "filename": "shiny_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 21, + "h": 21 + }, + "frame": { + "x": 392, + "y": 154, + "w": 21, + "h": 21 + } + }, + { + "filename": "oval_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 19 + }, + "frame": { + "x": 413, + "y": 131, + "w": 18, + "h": 19 + } + }, + { + "filename": "baton", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, "h": 18 }, "frame": { - "x": 311, - "y": 247, - "w": 16, + "x": 413, + "y": 150, + "w": 18, "h": 18 } }, @@ -6534,14 +6177,14 @@ "h": 18 }, "frame": { - "x": 327, - "y": 254, + "x": 413, + "y": 168, "w": 18, "h": 18 } }, { - "filename": "absolite", + "filename": "mystery_egg", "rotated": false, "trimmed": true, "sourceSize": { @@ -6552,55 +6195,13 @@ "x": 8, "y": 8, "w": 16, - "h": 16 + "h": 18 }, "frame": { - "x": 311, - "y": 265, + "x": 397, + "y": 175, "w": 16, - "h": 16 - } - }, - { - "filename": "unremarkable_teacup", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 21, "h": 18 - }, - "frame": { - "x": 345, - "y": 263, - "w": 21, - "h": 18 - } - }, - { - "filename": "white_herb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 20, - "h": 19 - }, - "frame": { - "x": 366, - "y": 263, - "w": 20, - "h": 19 } }, { @@ -6618,96 +6219,12 @@ "h": 18 }, "frame": { - "x": 327, - "y": 272, + "x": 413, + "y": 186, "w": 18, "h": 18 } }, - { - "filename": "wl_ability_urge", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 386, - "y": 269, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_antidote", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 345, - "y": 281, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_awakening", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 365, - "y": 282, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_burn_heal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 385, - "y": 287, - "w": 20, - "h": 18 - } - }, { "filename": "aerodactylite", "rotated": false, @@ -6723,8 +6240,8 @@ "h": 16 }, "frame": { - "x": 311, - "y": 281, + "x": 397, + "y": 193, "w": 16, "h": 16 } @@ -6744,96 +6261,12 @@ "h": 18 }, "frame": { - "x": 327, - "y": 290, + "x": 413, + "y": 204, "w": 18, "h": 18 } }, - { - "filename": "wl_custom_spliced", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 345, - "y": 299, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_custom_thief", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 365, - "y": 300, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_elixir", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 385, - "y": 305, - "w": 20, - "h": 18 - } - }, - { - "filename": "miracle_seed", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 19, - "h": 19 - }, - "frame": { - "x": 404, - "y": 154, - "w": 19, - "h": 19 - } - }, { "filename": "aggronite", "rotated": false, @@ -6849,54 +6282,12 @@ "h": 16 }, "frame": { - "x": 404, - "y": 173, + "x": 397, + "y": 209, "w": 16, "h": 16 } }, - { - "filename": "lucky_egg", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 17, - "h": 20 - }, - "frame": { - "x": 405, - "y": 189, - "w": 17, - "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": 406, - "y": 209, - "w": 18, - "h": 19 - } - }, { "filename": "light_ball", "rotated": false, @@ -6912,180 +6303,12 @@ "h": 18 }, "frame": { - "x": 406, - "y": 228, + "x": 413, + "y": 222, "w": 18, "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": 406, - "y": 246, - "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": 406, - "y": 264, - "w": 18, - "h": 18 - } - }, - { - "filename": "wl_ether", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 406, - "y": 282, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_full_heal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 405, - "y": 300, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_full_restore", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 405, - "y": 318, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_guard_spec", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 267, - "y": 282, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_hyper_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 287, - "y": 282, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_ice_heal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 307, - "y": 297, - "w": 20, - "h": 18 - } - }, { "filename": "alakazite", "rotated": false, @@ -7101,14 +6324,98 @@ "h": 16 }, "frame": { - "x": 327, - "y": 308, + "x": 397, + "y": 225, "w": 16, "h": 16 } }, { - "filename": "wl_item_drop", + "filename": "light_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 413, + "y": 240, + "w": 18, + "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": 200, + "y": 279, + "w": 21, + "h": 21 + } + }, + { + "filename": "lum_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 221, + "y": 281, + "w": 20, + "h": 19 + } + }, + { + "filename": "masterpiece_teacup", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 21, + "h": 18 + }, + "frame": { + "x": 241, + "y": 282, + "w": 21, + "h": 18 + } + }, + { + "filename": "old_gateau", "rotated": false, "trimmed": true, "sourceSize": { @@ -7118,268 +6425,16 @@ "spriteSourceSize": { "x": 6, "y": 8, - "w": 20, + "w": 21, "h": 18 }, "frame": { - "x": 343, - "y": 317, - "w": 20, + "x": 262, + "y": 282, + "w": 21, "h": 18 } }, - { - "filename": "wl_item_urge", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 363, - "y": 318, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_max_elixir", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 383, - "y": 323, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_max_ether", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 403, - "y": 336, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_max_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 246, - "y": 284, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_max_revive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 245, - "y": 302, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_paralyze_heal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 245, - "y": 320, - "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": 245, - "y": 338, - "w": 15, - "h": 11 - } - }, - { - "filename": "wl_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 249, - "y": 349, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_reset_urge", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 249, - "y": 367, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_revive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 253, - "y": 385, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_super_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 260, - "y": 403, - "w": 20, - "h": 18 - } - }, - { - "filename": "revive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 10, - "y": 8, - "w": 12, - "h": 17 - }, - "frame": { - "x": 265, - "y": 302, - "w": 12, - "h": 17 - } - }, { "filename": "altarianite", "rotated": false, @@ -7395,12 +6450,558 @@ "h": 16 }, "frame": { - "x": 277, + "x": 200, "y": 300, "w": 16, "h": 16 } }, + { + "filename": "sharp_meteorite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 21, + "h": 18 + }, + "frame": { + "x": 216, + "y": 300, + "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": 237, + "y": 300, + "w": 21, + "h": 18 + } + }, + { + "filename": "magnet", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 258, + "y": 300, + "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": 283, + "y": 285, + "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": 303, + "y": 285, + "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": 278, + "y": 305, + "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": 298, + "y": 305, + "w": 20, + "h": 20 + } + }, + { + "filename": "revive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 12, + "h": 17 + }, + "frame": { + "x": 202, + "y": 316, + "w": 12, + "h": 17 + } + }, + { + "filename": "power_herb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 214, + "y": 318, + "w": 20, + "h": 19 + } + }, + { + "filename": "razor_claw", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 234, + "y": 318, + "w": 20, + "h": 19 + } + }, + { + "filename": "smooth_meteorite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 254, + "y": 320, + "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": 274, + "y": 325, + "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": 294, + "y": 325, + "w": 20, + "h": 20 + } + }, + { + "filename": "spell_tag", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 19, + "h": 21 + }, + "frame": { + "x": 202, + "y": 337, + "w": 19, + "h": 21 + } + }, + { + "filename": "apicot_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 20 + }, + "frame": { + "x": 221, + "y": 337, + "w": 19, + "h": 20 + } + }, + { + "filename": "white_herb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 323, + "y": 287, + "w": 20, + "h": 19 + } + }, + { + "filename": "big_mushroom", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 19 + }, + "frame": { + "x": 343, + "y": 287, + "w": 19, + "h": 19 + } + }, + { + "filename": "candy_jar", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 20 + }, + "frame": { + "x": 362, + "y": 287, + "w": 19, + "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": 318, + "y": 306, + "w": 19, + "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": 337, + "y": 306, + "w": 19, + "h": 19 + } + }, + { + "filename": "wl_ability_urge", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 314, + "y": 326, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_antidote", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 356, + "y": 307, + "w": 20, + "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": 376, + "y": 307, + "w": 17, + "h": 20 + } + }, + { + "filename": "wl_awakening", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 393, + "y": 241, + "w": 20, + "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": 413, + "y": 258, + "w": 18, + "h": 18 + } + }, + { + "filename": "wl_burn_heal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 393, + "y": 259, + "w": 20, + "h": 18 + } + }, { "filename": "ampharosite", "rotated": false, @@ -7416,8 +7017,8 @@ "h": 16 }, "frame": { - "x": 265, - "y": 319, + "x": 377, + "y": 243, "w": 16, "h": 16 } @@ -7437,12 +7038,138 @@ "h": 16 }, "frame": { - "x": 281, - "y": 316, + "x": 377, + "y": 259, "w": 16, "h": 16 } }, + { + "filename": "relic_gold", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 9, + "y": 11, + "w": 15, + "h": 11 + }, + "frame": { + "x": 377, + "y": 275, + "w": 15, + "h": 11 + } + }, + { + "filename": "lucky_egg", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 17, + "h": 20 + }, + "frame": { + "x": 381, + "y": 286, + "w": 17, + "h": 20 + } + }, + { + "filename": "wl_custom_spliced", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 398, + "y": 277, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_custom_thief", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 398, + "y": 295, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_elixir", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 393, + "y": 313, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_ether", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 240, + "y": 340, + "w": 20, + "h": 18 + } + }, { "filename": "banettite", "rotated": false, @@ -7458,12 +7185,96 @@ "h": 16 }, "frame": { - "x": 297, - "y": 315, + "x": 413, + "y": 313, "w": 16, "h": 16 } }, + { + "filename": "wl_full_heal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 202, + "y": 358, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_full_restore", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 201, + "y": 376, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_guard_spec", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 201, + "y": 394, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_hyper_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 201, + "y": 412, + "w": 20, + "h": 18 + } + }, { "filename": "beedrillite", "rotated": false, @@ -7479,12 +7290,33 @@ "h": 16 }, "frame": { - "x": 281, - "y": 332, + "x": 222, + "y": 357, "w": 16, "h": 16 } }, + { + "filename": "wl_ice_heal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 238, + "y": 358, + "w": 20, + "h": 18 + } + }, { "filename": "blastoisinite", "rotated": false, @@ -7500,12 +7332,243 @@ "h": 16 }, "frame": { - "x": 297, - "y": 331, + "x": 222, + "y": 373, "w": 16, "h": 16 } }, + { + "filename": "wl_item_drop", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 221, + "y": 389, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_item_urge", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 221, + "y": 407, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_max_elixir", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 241, + "y": 376, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_max_ether", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 241, + "y": 394, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_max_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 241, + "y": 412, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_max_revive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 258, + "y": 358, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_paralyze_heal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 261, + "y": 376, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 261, + "y": 394, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_reset_urge", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 261, + "y": 412, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_revive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 278, + "y": 345, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_super_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 298, + "y": 345, + "w": 20, + "h": 18 + } + }, { "filename": "blazikenite", "rotated": false, @@ -7521,8 +7584,8 @@ "h": 16 }, "frame": { - "x": 269, - "y": 348, + "x": 318, + "y": 344, "w": 16, "h": 16 } @@ -7542,8 +7605,8 @@ "h": 16 }, "frame": { - "x": 269, - "y": 364, + "x": 334, + "y": 326, "w": 16, "h": 16 } @@ -7563,8 +7626,8 @@ "h": 16 }, "frame": { - "x": 285, - "y": 348, + "x": 334, + "y": 342, "w": 16, "h": 16 } @@ -7584,8 +7647,8 @@ "h": 16 }, "frame": { - "x": 285, - "y": 364, + "x": 350, + "y": 325, "w": 16, "h": 16 } @@ -7605,8 +7668,8 @@ "h": 16 }, "frame": { - "x": 273, - "y": 380, + "x": 350, + "y": 341, "w": 16, "h": 16 } @@ -7626,8 +7689,8 @@ "h": 16 }, "frame": { - "x": 289, - "y": 380, + "x": 366, + "y": 327, "w": 16, "h": 16 } @@ -7647,8 +7710,8 @@ "h": 16 }, "frame": { - "x": 301, - "y": 347, + "x": 366, + "y": 343, "w": 16, "h": 16 } @@ -7668,8 +7731,8 @@ "h": 16 }, "frame": { - "x": 301, - "y": 363, + "x": 382, + "y": 331, "w": 16, "h": 16 } @@ -7689,8 +7752,8 @@ "h": 16 }, "frame": { - "x": 305, - "y": 379, + "x": 398, + "y": 331, "w": 16, "h": 16 } @@ -7710,8 +7773,8 @@ "h": 16 }, "frame": { - "x": 280, - "y": 396, + "x": 414, + "y": 329, "w": 16, "h": 16 } @@ -7731,8 +7794,8 @@ "h": 16 }, "frame": { - "x": 280, - "y": 412, + "x": 382, + "y": 347, "w": 16, "h": 16 } @@ -7752,8 +7815,8 @@ "h": 16 }, "frame": { - "x": 296, - "y": 396, + "x": 398, + "y": 347, "w": 16, "h": 16 } @@ -7773,8 +7836,8 @@ "h": 16 }, "frame": { - "x": 296, - "y": 412, + "x": 414, + "y": 345, "w": 16, "h": 16 } @@ -7794,8 +7857,8 @@ "h": 16 }, "frame": { - "x": 312, - "y": 395, + "x": 281, + "y": 363, "w": 16, "h": 16 } @@ -7815,8 +7878,8 @@ "h": 16 }, "frame": { - "x": 312, - "y": 411, + "x": 281, + "y": 379, "w": 16, "h": 16 } @@ -7836,8 +7899,8 @@ "h": 16 }, "frame": { - "x": 313, - "y": 324, + "x": 297, + "y": 363, "w": 16, "h": 16 } @@ -7857,8 +7920,8 @@ "h": 16 }, "frame": { - "x": 317, - "y": 340, + "x": 281, + "y": 395, "w": 16, "h": 16 } @@ -7878,8 +7941,8 @@ "h": 16 }, "frame": { - "x": 317, - "y": 356, + "x": 297, + "y": 379, "w": 16, "h": 16 } @@ -7899,8 +7962,8 @@ "h": 16 }, "frame": { - "x": 321, - "y": 372, + "x": 281, + "y": 411, "w": 16, "h": 16 } @@ -7920,8 +7983,8 @@ "h": 16 }, "frame": { - "x": 328, - "y": 388, + "x": 297, + "y": 395, "w": 16, "h": 16 } @@ -7941,8 +8004,8 @@ "h": 16 }, "frame": { - "x": 328, - "y": 404, + "x": 297, + "y": 411, "w": 16, "h": 16 } @@ -7962,8 +8025,8 @@ "h": 16 }, "frame": { - "x": 333, - "y": 335, + "x": 313, + "y": 363, "w": 16, "h": 16 } @@ -7983,8 +8046,8 @@ "h": 16 }, "frame": { - "x": 333, - "y": 351, + "x": 313, + "y": 379, "w": 16, "h": 16 } @@ -8004,8 +8067,8 @@ "h": 16 }, "frame": { - "x": 337, - "y": 367, + "x": 313, + "y": 395, "w": 16, "h": 16 } @@ -8025,8 +8088,8 @@ "h": 16 }, "frame": { - "x": 344, - "y": 383, + "x": 313, + "y": 411, "w": 16, "h": 16 } @@ -8046,8 +8109,8 @@ "h": 16 }, "frame": { - "x": 344, - "y": 399, + "x": 350, + "y": 357, "w": 16, "h": 16 } @@ -8067,8 +8130,8 @@ "h": 16 }, "frame": { - "x": 349, - "y": 336, + "x": 366, + "y": 359, "w": 16, "h": 16 } @@ -8088,8 +8151,8 @@ "h": 16 }, "frame": { - "x": 365, - "y": 336, + "x": 334, + "y": 358, "w": 16, "h": 16 } @@ -8109,8 +8172,8 @@ "h": 16 }, "frame": { - "x": 381, - "y": 341, + "x": 382, + "y": 363, "w": 16, "h": 16 } @@ -8130,8 +8193,8 @@ "h": 16 }, "frame": { - "x": 353, - "y": 352, + "x": 398, + "y": 363, "w": 16, "h": 16 } @@ -8151,8 +8214,8 @@ "h": 16 }, "frame": { - "x": 369, - "y": 357, + "x": 414, + "y": 361, "w": 16, "h": 16 } @@ -8172,8 +8235,8 @@ "h": 16 }, "frame": { - "x": 385, - "y": 357, + "x": 329, + "y": 374, "w": 16, "h": 16 } @@ -8193,8 +8256,8 @@ "h": 16 }, "frame": { - "x": 401, - "y": 354, + "x": 329, + "y": 390, "w": 16, "h": 16 } @@ -8214,8 +8277,8 @@ "h": 16 }, "frame": { - "x": 401, - "y": 370, + "x": 329, + "y": 406, "w": 16, "h": 16 } @@ -8235,8 +8298,8 @@ "h": 16 }, "frame": { - "x": 360, - "y": 386, + "x": 345, + "y": 374, "w": 16, "h": 16 } @@ -8256,8 +8319,8 @@ "h": 16 }, "frame": { - "x": 360, - "y": 402, + "x": 345, + "y": 390, "w": 16, "h": 16 } @@ -8277,8 +8340,8 @@ "h": 16 }, "frame": { - "x": 376, - "y": 373, + "x": 345, + "y": 406, "w": 16, "h": 16 } @@ -8298,8 +8361,8 @@ "h": 16 }, "frame": { - "x": 376, - "y": 389, + "x": 361, + "y": 375, "w": 16, "h": 16 } @@ -8319,8 +8382,8 @@ "h": 16 }, "frame": { - "x": 376, - "y": 405, + "x": 361, + "y": 391, "w": 16, "h": 16 } @@ -8340,8 +8403,8 @@ "h": 16 }, "frame": { - "x": 392, - "y": 386, + "x": 361, + "y": 407, "w": 16, "h": 16 } @@ -8352,6 +8415,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:2fe5215a80083d35f525901078cb1f59:c17ac8d050238e3fca0ec935f6e8d37f:110e074689c9edd2c54833ce2e4d9270$" + "smartupdate": "$TexturePacker:SmartUpdate:934ea4080bad980d4fea720cc771f133:ed564bc47b79b15a763de57045178e88:110e074689c9edd2c54833ce2e4d9270$" } } diff --git a/public/images/items.png b/public/images/items.png index 4724f7b7b0e..5f032b30cfb 100644 Binary files a/public/images/items.png and b/public/images/items.png differ diff --git a/public/images/items/leaders_crest.png b/public/images/items/leaders_crest.png new file mode 100644 index 00000000000..45cf1656374 Binary files /dev/null and b/public/images/items/leaders_crest.png differ diff --git a/public/images/items/moon_flute.png b/public/images/items/moon_flute.png new file mode 100644 index 00000000000..893cb6a7579 Binary files /dev/null and b/public/images/items/moon_flute.png differ diff --git a/public/images/items/sun_flute.png b/public/images/items/sun_flute.png new file mode 100644 index 00000000000..7010c9fefbd Binary files /dev/null and b/public/images/items/sun_flute.png differ diff --git a/public/images/mystery-encounters/weird_dream_woman.json b/public/images/mystery-encounters/weird_dream_woman.json index 66a9b8d68db..49ebc001d18 100644 --- a/public/images/mystery-encounters/weird_dream_woman.json +++ b/public/images/mystery-encounters/weird_dream_woman.json @@ -5,29 +5,29 @@ "format": "RGBA8888", "size": { "w": 78, - "h": 87 + "h": 86 }, "scale": 1, "frames": [ { "filename": "0001.png", "rotated": false, - "trimmed": true, + "trimmed": false, "sourceSize": { - "w": 80, - "h": 87 + "w": 78, + "h": 86 }, "spriteSourceSize": { - "x": 1, + "x": 0, "y": 0, "w": 78, - "h": 87 + "h": 86 }, "frame": { "x": 0, "y": 0, "w": 78, - "h": 87 + "h": 86 } } ] @@ -36,6 +36,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:d3cce87ee0e3a880d840bffe9373d5d4:7c776d33b75abad1fe36b14a5e5734af:56468b7a2883e66dadcd2af13ebd8010$" + "smartupdate": "$TexturePacker:SmartUpdate:65266da62e9d2953511c0d68ae431345:c1ca63690bed8dd5af71bb443910c830:56468b7a2883e66dadcd2af13ebd8010$" } } diff --git a/public/images/mystery-encounters/weird_dream_woman.png b/public/images/mystery-encounters/weird_dream_woman.png index 1b8d142ed5b..50d04667152 100644 Binary files a/public/images/mystery-encounters/weird_dream_woman.png and b/public/images/mystery-encounters/weird_dream_woman.png differ diff --git a/public/images/pokemon/966-caph-starmobile.json b/public/images/pokemon/966-caph-starmobile.json new file mode 100644 index 00000000000..96c5aada282 --- /dev/null +++ b/public/images/pokemon/966-caph-starmobile.json @@ -0,0 +1,19 @@ +{ "frames": [ + { + "filename": "0001.png", + "frame": { "x": 0, "y": 0, "w": 94, "h": 94 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 26, "y": 0, "w": 94, "h": 94 }, + "sourceSize": { "w": 120, "h": 94 } + } + ], + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.8.1-x64", + "image": "966-caph-starmobile.png", + "format": "RGBA8888", + "size": { "w": 94, "h": 94 }, + "scale": "1" + } +} diff --git a/public/images/pokemon/966-caph-starmobile.png b/public/images/pokemon/966-caph-starmobile.png new file mode 100644 index 00000000000..987782e529e Binary files /dev/null and b/public/images/pokemon/966-caph-starmobile.png differ diff --git a/public/images/pokemon/966-navi-starmobile.json b/public/images/pokemon/966-navi-starmobile.json new file mode 100644 index 00000000000..6a39310af00 --- /dev/null +++ b/public/images/pokemon/966-navi-starmobile.json @@ -0,0 +1,19 @@ +{ "frames": [ + { + "filename": "0001.png", + "frame": { "x": 0, "y": 0, "w": 94, "h": 94 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 26, "y": 0, "w": 94, "h": 94 }, + "sourceSize": { "w": 120, "h": 94 } + } + ], + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.8.1-x64", + "image": "966-navi-starmobile.png", + "format": "RGBA8888", + "size": { "w": 94, "h": 94 }, + "scale": "1" + } +} diff --git a/public/images/pokemon/966-navi-starmobile.png b/public/images/pokemon/966-navi-starmobile.png new file mode 100644 index 00000000000..41d0fd4690c Binary files /dev/null and b/public/images/pokemon/966-navi-starmobile.png differ diff --git a/public/images/pokemon/966-ruchbah-starmobile.json b/public/images/pokemon/966-ruchbah-starmobile.json new file mode 100644 index 00000000000..c75a5630f45 --- /dev/null +++ b/public/images/pokemon/966-ruchbah-starmobile.json @@ -0,0 +1,19 @@ +{ "frames": [ + { + "filename": "0001.png", + "frame": { "x": 0, "y": 0, "w": 94, "h": 94 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 26, "y": 0, "w": 94, "h": 94 }, + "sourceSize": { "w": 120, "h": 94 } + } + ], + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.8.1-x64", + "image": "966-ruchbah-starmobile.png", + "format": "RGBA8888", + "size": { "w": 94, "h": 94 }, + "scale": "1" + } +} diff --git a/public/images/pokemon/966-ruchbah-starmobile.png b/public/images/pokemon/966-ruchbah-starmobile.png new file mode 100644 index 00000000000..765f1fe5eaa Binary files /dev/null and b/public/images/pokemon/966-ruchbah-starmobile.png differ diff --git a/public/images/pokemon/966-schedar-starmobile.json b/public/images/pokemon/966-schedar-starmobile.json new file mode 100644 index 00000000000..59f77f3c975 --- /dev/null +++ b/public/images/pokemon/966-schedar-starmobile.json @@ -0,0 +1,19 @@ +{ "frames": [ + { + "filename": "0001.png", + "frame": { "x": 0, "y": 0, "w": 94, "h": 94 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 26, "y": 0, "w": 94, "h": 94 }, + "sourceSize": { "w": 120, "h": 94 } + } + ], + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.8.1-x64", + "image": "966-schedar-starmobile.png", + "format": "RGBA8888", + "size": { "w": 94, "h": 94 }, + "scale": "1" + } +} diff --git a/public/images/pokemon/966-schedar-starmobile.png b/public/images/pokemon/966-schedar-starmobile.png new file mode 100644 index 00000000000..4cbc60f581f Binary files /dev/null and b/public/images/pokemon/966-schedar-starmobile.png differ diff --git a/public/images/pokemon/966-segin-starmobile.json b/public/images/pokemon/966-segin-starmobile.json new file mode 100644 index 00000000000..98b3938643b --- /dev/null +++ b/public/images/pokemon/966-segin-starmobile.json @@ -0,0 +1,19 @@ +{ "frames": [ + { + "filename": "0001.png", + "frame": { "x": 0, "y": 0, "w": 94, "h": 94 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 26, "y": 0, "w": 94, "h": 94 }, + "sourceSize": { "w": 120, "h": 94 } + } + ], + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.8.1-x64", + "image": "966-segin-starmobile.png", + "format": "RGBA8888", + "size": { "w": 94, "h": 94 }, + "scale": "1" + } +} diff --git a/public/images/pokemon/966-segin-starmobile.png b/public/images/pokemon/966-segin-starmobile.png new file mode 100644 index 00000000000..fab6b1f62ee Binary files /dev/null and b/public/images/pokemon/966-segin-starmobile.png differ diff --git a/public/images/pokemon/back/966-caph-starmobile.json b/public/images/pokemon/back/966-caph-starmobile.json new file mode 100644 index 00000000000..d71eccd11d7 --- /dev/null +++ b/public/images/pokemon/back/966-caph-starmobile.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "966-caph-starmobile.png", + "format": "RGBA8888", + "size": { + "w": 84, + "h": 84 + }, + "scale": 0.333, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 96, + "h": 96 + }, + "spriteSourceSize": { + "x": 6, + "y": 20, + "w": 84, + "h": 56 + }, + "frame": { + "x": 0, + "y": 0, + "w": 84, + "h": 56 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:0226ae22b7a4822d78e38df4af1f59a7:01ce69442faf54e54474cd349cad2f7d:f9a0366e304d666e4262fa0af369d1f4$" + } +} diff --git a/public/images/pokemon/back/966-caph-starmobile.png b/public/images/pokemon/back/966-caph-starmobile.png new file mode 100644 index 00000000000..d1e67365454 Binary files /dev/null and b/public/images/pokemon/back/966-caph-starmobile.png differ diff --git a/public/images/pokemon/back/966-navi-starmobile.json b/public/images/pokemon/back/966-navi-starmobile.json new file mode 100644 index 00000000000..99059aa6edb --- /dev/null +++ b/public/images/pokemon/back/966-navi-starmobile.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "966-navi-starmobile.png", + "format": "RGBA8888", + "size": { + "w": 84, + "h": 84 + }, + "scale": 0.333, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 96, + "h": 96 + }, + "spriteSourceSize": { + "x": 6, + "y": 20, + "w": 84, + "h": 56 + }, + "frame": { + "x": 0, + "y": 0, + "w": 84, + "h": 56 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:0226ae22b7a4822d78e38df4af1f59a7:01ce69442faf54e54474cd349cad2f7d:f9a0366e304d666e4262fa0af369d1f4$" + } +} diff --git a/public/images/pokemon/back/966-navi-starmobile.png b/public/images/pokemon/back/966-navi-starmobile.png new file mode 100644 index 00000000000..d1e67365454 Binary files /dev/null and b/public/images/pokemon/back/966-navi-starmobile.png differ diff --git a/public/images/pokemon/back/966-ruchbah-starmobile.json b/public/images/pokemon/back/966-ruchbah-starmobile.json new file mode 100644 index 00000000000..b3bb8463eac --- /dev/null +++ b/public/images/pokemon/back/966-ruchbah-starmobile.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "966-ruchbah-starmobile.png", + "format": "RGBA8888", + "size": { + "w": 84, + "h": 84 + }, + "scale": 0.333, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 96, + "h": 96 + }, + "spriteSourceSize": { + "x": 6, + "y": 20, + "w": 84, + "h": 56 + }, + "frame": { + "x": 0, + "y": 0, + "w": 84, + "h": 56 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:0226ae22b7a4822d78e38df4af1f59a7:01ce69442faf54e54474cd349cad2f7d:f9a0366e304d666e4262fa0af369d1f4$" + } +} diff --git a/public/images/pokemon/back/966-ruchbah-starmobile.png b/public/images/pokemon/back/966-ruchbah-starmobile.png new file mode 100644 index 00000000000..d1e67365454 Binary files /dev/null and b/public/images/pokemon/back/966-ruchbah-starmobile.png differ diff --git a/public/images/pokemon/back/966-schedar-starmobile.json b/public/images/pokemon/back/966-schedar-starmobile.json new file mode 100644 index 00000000000..9832835b3ce --- /dev/null +++ b/public/images/pokemon/back/966-schedar-starmobile.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "966-schedar-starmobile.png", + "format": "RGBA8888", + "size": { + "w": 84, + "h": 84 + }, + "scale": 0.333, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 96, + "h": 96 + }, + "spriteSourceSize": { + "x": 6, + "y": 20, + "w": 84, + "h": 56 + }, + "frame": { + "x": 0, + "y": 0, + "w": 84, + "h": 56 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:0226ae22b7a4822d78e38df4af1f59a7:01ce69442faf54e54474cd349cad2f7d:f9a0366e304d666e4262fa0af369d1f4$" + } +} diff --git a/public/images/pokemon/back/966-schedar-starmobile.png b/public/images/pokemon/back/966-schedar-starmobile.png new file mode 100644 index 00000000000..d1e67365454 Binary files /dev/null and b/public/images/pokemon/back/966-schedar-starmobile.png differ diff --git a/public/images/pokemon/back/966-segin-starmobile.json b/public/images/pokemon/back/966-segin-starmobile.json new file mode 100644 index 00000000000..75bd4d8f304 --- /dev/null +++ b/public/images/pokemon/back/966-segin-starmobile.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "966-segin-starmobile.png", + "format": "RGBA8888", + "size": { + "w": 84, + "h": 84 + }, + "scale": 0.333, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 96, + "h": 96 + }, + "spriteSourceSize": { + "x": 6, + "y": 20, + "w": 84, + "h": 56 + }, + "frame": { + "x": 0, + "y": 0, + "w": 84, + "h": 56 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:0226ae22b7a4822d78e38df4af1f59a7:01ce69442faf54e54474cd349cad2f7d:f9a0366e304d666e4262fa0af369d1f4$" + } +} diff --git a/public/images/pokemon/back/966-segin-starmobile.png b/public/images/pokemon/back/966-segin-starmobile.png new file mode 100644 index 00000000000..d1e67365454 Binary files /dev/null and b/public/images/pokemon/back/966-segin-starmobile.png differ diff --git a/public/images/pokemon/back/shiny/966-caph-starmobile.json b/public/images/pokemon/back/shiny/966-caph-starmobile.json new file mode 100644 index 00000000000..d71eccd11d7 --- /dev/null +++ b/public/images/pokemon/back/shiny/966-caph-starmobile.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "966-caph-starmobile.png", + "format": "RGBA8888", + "size": { + "w": 84, + "h": 84 + }, + "scale": 0.333, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 96, + "h": 96 + }, + "spriteSourceSize": { + "x": 6, + "y": 20, + "w": 84, + "h": 56 + }, + "frame": { + "x": 0, + "y": 0, + "w": 84, + "h": 56 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:0226ae22b7a4822d78e38df4af1f59a7:01ce69442faf54e54474cd349cad2f7d:f9a0366e304d666e4262fa0af369d1f4$" + } +} diff --git a/public/images/pokemon/back/shiny/966-caph-starmobile.png b/public/images/pokemon/back/shiny/966-caph-starmobile.png new file mode 100644 index 00000000000..64e72d6793f Binary files /dev/null and b/public/images/pokemon/back/shiny/966-caph-starmobile.png differ diff --git a/public/images/pokemon/back/shiny/966-navi-starmobile.json b/public/images/pokemon/back/shiny/966-navi-starmobile.json new file mode 100644 index 00000000000..99059aa6edb --- /dev/null +++ b/public/images/pokemon/back/shiny/966-navi-starmobile.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "966-navi-starmobile.png", + "format": "RGBA8888", + "size": { + "w": 84, + "h": 84 + }, + "scale": 0.333, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 96, + "h": 96 + }, + "spriteSourceSize": { + "x": 6, + "y": 20, + "w": 84, + "h": 56 + }, + "frame": { + "x": 0, + "y": 0, + "w": 84, + "h": 56 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:0226ae22b7a4822d78e38df4af1f59a7:01ce69442faf54e54474cd349cad2f7d:f9a0366e304d666e4262fa0af369d1f4$" + } +} diff --git a/public/images/pokemon/back/shiny/966-navi-starmobile.png b/public/images/pokemon/back/shiny/966-navi-starmobile.png new file mode 100644 index 00000000000..64e72d6793f Binary files /dev/null and b/public/images/pokemon/back/shiny/966-navi-starmobile.png differ diff --git a/public/images/pokemon/back/shiny/966-ruchbah-starmobile.json b/public/images/pokemon/back/shiny/966-ruchbah-starmobile.json new file mode 100644 index 00000000000..b3bb8463eac --- /dev/null +++ b/public/images/pokemon/back/shiny/966-ruchbah-starmobile.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "966-ruchbah-starmobile.png", + "format": "RGBA8888", + "size": { + "w": 84, + "h": 84 + }, + "scale": 0.333, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 96, + "h": 96 + }, + "spriteSourceSize": { + "x": 6, + "y": 20, + "w": 84, + "h": 56 + }, + "frame": { + "x": 0, + "y": 0, + "w": 84, + "h": 56 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:0226ae22b7a4822d78e38df4af1f59a7:01ce69442faf54e54474cd349cad2f7d:f9a0366e304d666e4262fa0af369d1f4$" + } +} diff --git a/public/images/pokemon/back/shiny/966-ruchbah-starmobile.png b/public/images/pokemon/back/shiny/966-ruchbah-starmobile.png new file mode 100644 index 00000000000..64e72d6793f Binary files /dev/null and b/public/images/pokemon/back/shiny/966-ruchbah-starmobile.png differ diff --git a/public/images/pokemon/back/shiny/966-schedar-starmobile.json b/public/images/pokemon/back/shiny/966-schedar-starmobile.json new file mode 100644 index 00000000000..9832835b3ce --- /dev/null +++ b/public/images/pokemon/back/shiny/966-schedar-starmobile.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "966-schedar-starmobile.png", + "format": "RGBA8888", + "size": { + "w": 84, + "h": 84 + }, + "scale": 0.333, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 96, + "h": 96 + }, + "spriteSourceSize": { + "x": 6, + "y": 20, + "w": 84, + "h": 56 + }, + "frame": { + "x": 0, + "y": 0, + "w": 84, + "h": 56 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:0226ae22b7a4822d78e38df4af1f59a7:01ce69442faf54e54474cd349cad2f7d:f9a0366e304d666e4262fa0af369d1f4$" + } +} diff --git a/public/images/pokemon/back/shiny/966-schedar-starmobile.png b/public/images/pokemon/back/shiny/966-schedar-starmobile.png new file mode 100644 index 00000000000..64e72d6793f Binary files /dev/null and b/public/images/pokemon/back/shiny/966-schedar-starmobile.png differ diff --git a/public/images/pokemon/back/shiny/966-segin-starmobile.json b/public/images/pokemon/back/shiny/966-segin-starmobile.json new file mode 100644 index 00000000000..75bd4d8f304 --- /dev/null +++ b/public/images/pokemon/back/shiny/966-segin-starmobile.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "966-segin-starmobile.png", + "format": "RGBA8888", + "size": { + "w": 84, + "h": 84 + }, + "scale": 0.333, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 96, + "h": 96 + }, + "spriteSourceSize": { + "x": 6, + "y": 20, + "w": 84, + "h": 56 + }, + "frame": { + "x": 0, + "y": 0, + "w": 84, + "h": 56 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:0226ae22b7a4822d78e38df4af1f59a7:01ce69442faf54e54474cd349cad2f7d:f9a0366e304d666e4262fa0af369d1f4$" + } +} diff --git a/public/images/pokemon/back/shiny/966-segin-starmobile.png b/public/images/pokemon/back/shiny/966-segin-starmobile.png new file mode 100644 index 00000000000..64e72d6793f Binary files /dev/null and b/public/images/pokemon/back/shiny/966-segin-starmobile.png differ diff --git a/public/images/pokemon/exp/back/745.png b/public/images/pokemon/exp/back/745.png index 46a354be8a4..f4949135164 100644 Binary files a/public/images/pokemon/exp/back/745.png and b/public/images/pokemon/exp/back/745.png differ diff --git a/public/images/pokemon/exp/back/shiny/745.json b/public/images/pokemon/exp/back/shiny/745.json index 8e83c592ce4..4867604448d 100644 --- a/public/images/pokemon/exp/back/shiny/745.json +++ b/public/images/pokemon/exp/back/shiny/745.json @@ -1,440 +1,230 @@ -{ - "textures": [ - { - "image": "745.png", - "format": "RGBA8888", - "size": { - "w": 300, - "h": 300 - }, - "scale": 1, - "frames": [ - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 1, - "y": 4, - "w": 60, - "h": 67 - }, - "frame": { - "x": 0, - "y": 0, - "w": 60, - "h": 67 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 1, - "y": 4, - "w": 60, - "h": 67 - }, - "frame": { - "x": 60, - "y": 0, - "w": 60, - "h": 67 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 1, - "y": 4, - "w": 60, - "h": 67 - }, - "frame": { - "x": 120, - "y": 0, - "w": 60, - "h": 67 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 1, - "y": 4, - "w": 60, - "h": 67 - }, - "frame": { - "x": 180, - "y": 0, - "w": 60, - "h": 67 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 1, - "y": 3, - "w": 60, - "h": 68 - }, - "frame": { - "x": 240, - "y": 0, - "w": 60, - "h": 68 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 0, - "y": 3, - "w": 61, - "h": 68 - }, - "frame": { - "x": 0, - "y": 67, - "w": 61, - "h": 68 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 1, - "y": 3, - "w": 60, - "h": 68 - }, - "frame": { - "x": 61, - "y": 67, - "w": 60, - "h": 68 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 0, - "y": 3, - "w": 61, - "h": 68 - }, - "frame": { - "x": 121, - "y": 67, - "w": 61, - "h": 68 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 60, - "h": 69 - }, - "frame": { - "x": 182, - "y": 68, - "w": 60, - "h": 69 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 60, - "h": 69 - }, - "frame": { - "x": 0, - "y": 135, - "w": 60, - "h": 69 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 61, - "h": 70 - }, - "frame": { - "x": 60, - "y": 135, - "w": 61, - "h": 70 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 61, - "h": 70 - }, - "frame": { - "x": 121, - "y": 135, - "w": 61, - "h": 70 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 61, - "h": 71 - }, - "frame": { - "x": 182, - "y": 137, - "w": 61, - "h": 71 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 61, - "h": 71 - }, - "frame": { - "x": 182, - "y": 137, - "w": 61, - "h": 71 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 61, - "h": 71 - }, - "frame": { - "x": 0, - "y": 205, - "w": 61, - "h": 71 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 61, - "h": 71 - }, - "frame": { - "x": 61, - "y": 205, - "w": 61, - "h": 71 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 61, - "h": 71 - }, - "frame": { - "x": 61, - "y": 205, - "w": 61, - "h": 71 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 61, - "h": 71 - }, - "frame": { - "x": 122, - "y": 208, - "w": 61, - "h": 71 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 61, - "h": 71 - }, - "frame": { - "x": 122, - "y": 208, - "w": 61, - "h": 71 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 61, - "h": 71 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 61, - "h": 71 - }, - "frame": { - "x": 183, - "y": 208, - "w": 61, - "h": 71 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:8d47c2cedd75d15c81c3aa0a0b14133c:28c19026319cfbbb59916e3d1b92f732:f9304907e03a5223c5bc78c934419106$" - } -} +{ + "textures": [ + { + "image": "745.png", + "format": "RGBA8888", + "size": { + "w": 181, + "h": 181 + }, + "scale": 1, + "frames": [ + { + "filename": "0004.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 72, + "h": 61 + }, + "spriteSourceSize": { + "x": 1, + "y": 0, + "w": 71, + "h": 61 + }, + "frame": { + "x": 0, + "y": 0, + "w": 71, + "h": 61 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 72, + "h": 61 + }, + "spriteSourceSize": { + "x": 1, + "y": 0, + "w": 71, + "h": 61 + }, + "frame": { + "x": 0, + "y": 0, + "w": 71, + "h": 61 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 72, + "h": 61 + }, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 71, + "h": 60 + }, + "frame": { + "x": 71, + "y": 0, + "w": 71, + "h": 60 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 72, + "h": 61 + }, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 71, + "h": 60 + }, + "frame": { + "x": 71, + "y": 0, + "w": 71, + "h": 60 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 72, + "h": 61 + }, + "spriteSourceSize": { + "x": 1, + "y": 2, + "w": 71, + "h": 59 + }, + "frame": { + "x": 71, + "y": 60, + "w": 71, + "h": 59 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 72, + "h": 61 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 70, + "h": 61 + }, + "frame": { + "x": 0, + "y": 61, + "w": 70, + "h": 61 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 72, + "h": 61 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 70, + "h": 61 + }, + "frame": { + "x": 0, + "y": 61, + "w": 70, + "h": 61 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 72, + "h": 61 + }, + "spriteSourceSize": { + "x": 1, + "y": 2, + "w": 68, + "h": 59 + }, + "frame": { + "x": 0, + "y": 122, + "w": 68, + "h": 59 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 72, + "h": 61 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 69, + "h": 61 + }, + "frame": { + "x": 70, + "y": 119, + "w": 69, + "h": 61 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 72, + "h": 61 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 69, + "h": 61 + }, + "frame": { + "x": 70, + "y": 119, + "w": 69, + "h": 61 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:9bdd7250af45db121574c90e718874a8:ca85d052f16849220d83acd876b20b8b:f9304907e03a5223c5bc78c934419106$" + } +} diff --git a/public/images/pokemon/exp/back/shiny/745.png b/public/images/pokemon/exp/back/shiny/745.png index 5eb15a8cf49..49f2d0569af 100644 Binary files a/public/images/pokemon/exp/back/shiny/745.png and b/public/images/pokemon/exp/back/shiny/745.png differ diff --git a/public/images/pokemon/exp/shiny/745.json b/public/images/pokemon/exp/shiny/745.json index 6cabccff28d..d0989a1ccd3 100644 --- a/public/images/pokemon/exp/shiny/745.json +++ b/public/images/pokemon/exp/shiny/745.json @@ -1,167 +1,524 @@ -{ - "textures": [ - { - "image": "745.png", - "format": "RGBA8888", - "size": { - "w": 189, - "h": 189 - }, - "scale": 1, - "frames": [ - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 58 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 65, - "h": 58 - }, - "frame": { - "x": 0, - "y": 0, - "w": 65, - "h": 58 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 66, - "h": 57 - }, - "frame": { - "x": 65, - "y": 0, - "w": 66, - "h": 57 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 58 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 64, - "h": 58 - }, - "frame": { - "x": 65, - "y": 57, - "w": 64, - "h": 58 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 58 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 64, - "h": 57 - }, - "frame": { - "x": 0, - "y": 58, - "w": 64, - "h": 57 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 58 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 65, - "h": 56 - }, - "frame": { - "x": 0, - "y": 115, - "w": 65, - "h": 56 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 58 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 62, - "h": 58 - }, - "frame": { - "x": 65, - "y": 115, - "w": 62, - "h": 58 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 58 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 62, - "h": 58 - }, - "frame": { - "x": 127, - "y": 115, - "w": 62, - "h": 58 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:1b95a218abc87c12576165b943d3cb77:4d796dc75302ca2e18ce15e67dcf3f0f:f9304907e03a5223c5bc78c934419106$" - } -} +{ + "textures": [ + { + "image": "745.png", + "format": "RGBA8888", + "size": { + "w": 286, + "h": 286 + }, + "scale": 1, + "frames": [ + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 3, + "w": 60, + "h": 55 + }, + "frame": { + "x": 0, + "y": 0, + "w": 60, + "h": 55 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 3, + "w": 60, + "h": 55 + }, + "frame": { + "x": 60, + "y": 0, + "w": 60, + "h": 55 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 3, + "w": 60, + "h": 55 + }, + "frame": { + "x": 120, + "y": 0, + "w": 60, + "h": 55 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 3, + "w": 60, + "h": 55 + }, + "frame": { + "x": 180, + "y": 0, + "w": 60, + "h": 55 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 1, + "w": 60, + "h": 57 + }, + "frame": { + "x": 0, + "y": 55, + "w": 60, + "h": 57 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 1, + "w": 60, + "h": 57 + }, + "frame": { + "x": 60, + "y": 55, + "w": 60, + "h": 57 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 1, + "w": 60, + "h": 57 + }, + "frame": { + "x": 120, + "y": 55, + "w": 60, + "h": 57 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 1, + "w": 60, + "h": 57 + }, + "frame": { + "x": 180, + "y": 55, + "w": 60, + "h": 57 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 1, + "w": 60, + "h": 57 + }, + "frame": { + "x": 180, + "y": 55, + "w": 60, + "h": 57 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 1, + "w": 60, + "h": 57 + }, + "frame": { + "x": 0, + "y": 112, + "w": 60, + "h": 57 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 1, + "w": 60, + "h": 57 + }, + "frame": { + "x": 60, + "y": 112, + "w": 60, + "h": 57 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 1, + "w": 60, + "h": 57 + }, + "frame": { + "x": 120, + "y": 112, + "w": 60, + "h": 57 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 60, + "h": 58 + }, + "frame": { + "x": 180, + "y": 112, + "w": 60, + "h": 58 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 60, + "h": 58 + }, + "frame": { + "x": 180, + "y": 112, + "w": 60, + "h": 58 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 60, + "h": 58 + }, + "frame": { + "x": 0, + "y": 169, + "w": 60, + "h": 58 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 60, + "h": 58 + }, + "frame": { + "x": 0, + "y": 169, + "w": 60, + "h": 58 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 60, + "h": 58 + }, + "frame": { + "x": 60, + "y": 169, + "w": 60, + "h": 58 + } + }, + { + "filename": "0004.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 60, + "h": 58 + }, + "frame": { + "x": 120, + "y": 169, + "w": 60, + "h": 58 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 60, + "h": 58 + }, + "frame": { + "x": 180, + "y": 170, + "w": 60, + "h": 58 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 60, + "h": 58 + }, + "frame": { + "x": 0, + "y": 227, + "w": 60, + "h": 58 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 60, + "h": 58 + }, + "frame": { + "x": 0, + "y": 227, + "w": 60, + "h": 58 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 60, + "h": 58 + }, + "frame": { + "x": 60, + "y": 227, + "w": 60, + "h": 58 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 60, + "h": 58 + }, + "frame": { + "x": 120, + "y": 227, + "w": 60, + "h": 58 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 60, + "h": 58 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 60, + "h": 58 + }, + "frame": { + "x": 180, + "y": 228, + "w": 60, + "h": 58 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:d67741bfb78b7ff0c920c5395dd91fc2:e78172ef76e3b6327173461a595a8a6b:f9304907e03a5223c5bc78c934419106$" + } +} diff --git a/public/images/pokemon/exp/shiny/745.png b/public/images/pokemon/exp/shiny/745.png index 7679c44ba13..c3256cf3f64 100644 Binary files a/public/images/pokemon/exp/shiny/745.png and b/public/images/pokemon/exp/shiny/745.png differ diff --git a/public/images/pokemon/icons/9/966-caph-starmobile.png b/public/images/pokemon/icons/9/966-caph-starmobile.png new file mode 100644 index 00000000000..fba351495bd Binary files /dev/null and b/public/images/pokemon/icons/9/966-caph-starmobile.png differ diff --git a/public/images/pokemon/icons/9/966-navi-starmobile.png b/public/images/pokemon/icons/9/966-navi-starmobile.png new file mode 100644 index 00000000000..fba351495bd Binary files /dev/null and b/public/images/pokemon/icons/9/966-navi-starmobile.png differ diff --git a/public/images/pokemon/icons/9/966-ruchbah-starmobile.png b/public/images/pokemon/icons/9/966-ruchbah-starmobile.png new file mode 100644 index 00000000000..fba351495bd Binary files /dev/null and b/public/images/pokemon/icons/9/966-ruchbah-starmobile.png differ diff --git a/public/images/pokemon/icons/9/966-schedar-starmobile.png b/public/images/pokemon/icons/9/966-schedar-starmobile.png new file mode 100644 index 00000000000..fba351495bd Binary files /dev/null and b/public/images/pokemon/icons/9/966-schedar-starmobile.png differ diff --git a/public/images/pokemon/icons/9/966-segin-starmobile.png b/public/images/pokemon/icons/9/966-segin-starmobile.png new file mode 100644 index 00000000000..fba351495bd Binary files /dev/null and b/public/images/pokemon/icons/9/966-segin-starmobile.png differ diff --git a/public/images/pokemon/icons/9/966s-caph-starmobile.png b/public/images/pokemon/icons/9/966s-caph-starmobile.png new file mode 100644 index 00000000000..e54e5c90e4a Binary files /dev/null and b/public/images/pokemon/icons/9/966s-caph-starmobile.png differ diff --git a/public/images/pokemon/icons/9/966s-navi-starmobile.png b/public/images/pokemon/icons/9/966s-navi-starmobile.png new file mode 100644 index 00000000000..e54e5c90e4a Binary files /dev/null and b/public/images/pokemon/icons/9/966s-navi-starmobile.png differ diff --git a/public/images/pokemon/icons/9/966s-ruchbah-starmobile.png b/public/images/pokemon/icons/9/966s-ruchbah-starmobile.png new file mode 100644 index 00000000000..e54e5c90e4a Binary files /dev/null and b/public/images/pokemon/icons/9/966s-ruchbah-starmobile.png differ diff --git a/public/images/pokemon/icons/9/966s-schedar-starmobile.png b/public/images/pokemon/icons/9/966s-schedar-starmobile.png new file mode 100644 index 00000000000..e54e5c90e4a Binary files /dev/null and b/public/images/pokemon/icons/9/966s-schedar-starmobile.png differ diff --git a/public/images/pokemon/icons/9/966s-segin-starmobile.png b/public/images/pokemon/icons/9/966s-segin-starmobile.png new file mode 100644 index 00000000000..e54e5c90e4a Binary files /dev/null and b/public/images/pokemon/icons/9/966s-segin-starmobile.png differ diff --git a/public/images/pokemon/shiny/966-caph-starmobile.json b/public/images/pokemon/shiny/966-caph-starmobile.json new file mode 100644 index 00000000000..96c5aada282 --- /dev/null +++ b/public/images/pokemon/shiny/966-caph-starmobile.json @@ -0,0 +1,19 @@ +{ "frames": [ + { + "filename": "0001.png", + "frame": { "x": 0, "y": 0, "w": 94, "h": 94 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 26, "y": 0, "w": 94, "h": 94 }, + "sourceSize": { "w": 120, "h": 94 } + } + ], + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.8.1-x64", + "image": "966-caph-starmobile.png", + "format": "RGBA8888", + "size": { "w": 94, "h": 94 }, + "scale": "1" + } +} diff --git a/public/images/pokemon/shiny/966-caph-starmobile.png b/public/images/pokemon/shiny/966-caph-starmobile.png new file mode 100644 index 00000000000..6107a426ff6 Binary files /dev/null and b/public/images/pokemon/shiny/966-caph-starmobile.png differ diff --git a/public/images/pokemon/shiny/966-navi-starmobile.json b/public/images/pokemon/shiny/966-navi-starmobile.json new file mode 100644 index 00000000000..6a39310af00 --- /dev/null +++ b/public/images/pokemon/shiny/966-navi-starmobile.json @@ -0,0 +1,19 @@ +{ "frames": [ + { + "filename": "0001.png", + "frame": { "x": 0, "y": 0, "w": 94, "h": 94 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 26, "y": 0, "w": 94, "h": 94 }, + "sourceSize": { "w": 120, "h": 94 } + } + ], + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.8.1-x64", + "image": "966-navi-starmobile.png", + "format": "RGBA8888", + "size": { "w": 94, "h": 94 }, + "scale": "1" + } +} diff --git a/public/images/pokemon/shiny/966-navi-starmobile.png b/public/images/pokemon/shiny/966-navi-starmobile.png new file mode 100644 index 00000000000..74999d4fa13 Binary files /dev/null and b/public/images/pokemon/shiny/966-navi-starmobile.png differ diff --git a/public/images/pokemon/shiny/966-ruchbah-starmobile.json b/public/images/pokemon/shiny/966-ruchbah-starmobile.json new file mode 100644 index 00000000000..c75a5630f45 --- /dev/null +++ b/public/images/pokemon/shiny/966-ruchbah-starmobile.json @@ -0,0 +1,19 @@ +{ "frames": [ + { + "filename": "0001.png", + "frame": { "x": 0, "y": 0, "w": 94, "h": 94 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 26, "y": 0, "w": 94, "h": 94 }, + "sourceSize": { "w": 120, "h": 94 } + } + ], + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.8.1-x64", + "image": "966-ruchbah-starmobile.png", + "format": "RGBA8888", + "size": { "w": 94, "h": 94 }, + "scale": "1" + } +} diff --git a/public/images/pokemon/shiny/966-ruchbah-starmobile.png b/public/images/pokemon/shiny/966-ruchbah-starmobile.png new file mode 100644 index 00000000000..9de01ac6a73 Binary files /dev/null and b/public/images/pokemon/shiny/966-ruchbah-starmobile.png differ diff --git a/public/images/pokemon/shiny/966-schedar-starmobile.json b/public/images/pokemon/shiny/966-schedar-starmobile.json new file mode 100644 index 00000000000..59f77f3c975 --- /dev/null +++ b/public/images/pokemon/shiny/966-schedar-starmobile.json @@ -0,0 +1,19 @@ +{ "frames": [ + { + "filename": "0001.png", + "frame": { "x": 0, "y": 0, "w": 94, "h": 94 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 26, "y": 0, "w": 94, "h": 94 }, + "sourceSize": { "w": 120, "h": 94 } + } + ], + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.8.1-x64", + "image": "966-schedar-starmobile.png", + "format": "RGBA8888", + "size": { "w": 94, "h": 94 }, + "scale": "1" + } +} diff --git a/public/images/pokemon/shiny/966-schedar-starmobile.png b/public/images/pokemon/shiny/966-schedar-starmobile.png new file mode 100644 index 00000000000..79033bb123c Binary files /dev/null and b/public/images/pokemon/shiny/966-schedar-starmobile.png differ diff --git a/public/images/pokemon/shiny/966-segin-starmobile.json b/public/images/pokemon/shiny/966-segin-starmobile.json new file mode 100644 index 00000000000..98b3938643b --- /dev/null +++ b/public/images/pokemon/shiny/966-segin-starmobile.json @@ -0,0 +1,19 @@ +{ "frames": [ + { + "filename": "0001.png", + "frame": { "x": 0, "y": 0, "w": 94, "h": 94 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 26, "y": 0, "w": 94, "h": 94 }, + "sourceSize": { "w": 120, "h": 94 } + } + ], + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.8.1-x64", + "image": "966-segin-starmobile.png", + "format": "RGBA8888", + "size": { "w": 94, "h": 94 }, + "scale": "1" + } +} diff --git a/public/images/pokemon/shiny/966-segin-starmobile.png b/public/images/pokemon/shiny/966-segin-starmobile.png new file mode 100644 index 00000000000..f4cab89a203 Binary files /dev/null and b/public/images/pokemon/shiny/966-segin-starmobile.png differ diff --git a/public/images/pokemon_icons_9.json b/public/images/pokemon_icons_9.json index 26e28eedae0..01994a41a02 100644 --- a/public/images/pokemon_icons_9.json +++ b/public/images/pokemon_icons_9.json @@ -4,8 +4,8 @@ "image": "pokemon_icons_9.png", "format": "RGBA8888", "size": { - "w": 252, - "h": 591 + "w": 255, + "h": 646 }, "scale": 1, "frames": [ @@ -382,7 +382,7 @@ }, "frame": { "x": 0, - "y": 270, + "y": 300, "w": 30, "h": 30 } @@ -429,27 +429,6 @@ "h": 27 } }, - { - "filename": "8901", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 5, - "y": 1, - "w": 30, - "h": 28 - }, - "frame": { - "x": 222, - "y": 0, - "w": 30, - "h": 28 - } - }, { "filename": "1020", "rotated": false, @@ -528,12 +507,33 @@ "h": 26 }, "frame": { - "x": 126, - "y": 28, + "x": 222, + "y": 0, "w": 32, "h": 26 } }, + { + "filename": "8901", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 5, + "y": 1, + "w": 30, + "h": 28 + }, + "frame": { + "x": 0, + "y": 330, + "w": 30, + "h": 28 + } + }, { "filename": "8901s", "rotated": false, @@ -550,7 +550,7 @@ }, "frame": { "x": 0, - "y": 300, + "y": 358, "w": 30, "h": 28 } @@ -571,7 +571,7 @@ }, "frame": { "x": 0, - "y": 328, + "y": 386, "w": 27, "h": 30 } @@ -592,7 +592,7 @@ }, "frame": { "x": 0, - "y": 358, + "y": 416, "w": 27, "h": 30 } @@ -611,6 +611,27 @@ "w": 32, "h": 25 }, + "frame": { + "x": 126, + "y": 28, + "w": 32, + "h": 25 + } + }, + { + "filename": "984s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 4, + "y": 3, + "w": 32, + "h": 25 + }, "frame": { "x": 158, "y": 27, @@ -619,7 +640,7 @@ } }, { - "filename": "984s", + "filename": "1014", "rotated": false, "trimmed": true, "sourceSize": { @@ -640,7 +661,7 @@ } }, { - "filename": "992", + "filename": "1014s", "rotated": false, "trimmed": true, "sourceSize": { @@ -648,16 +669,16 @@ "h": 30 }, "spriteSourceSize": { - "x": 6, - "y": 2, - "w": 30, - "h": 26 + "x": 4, + "y": 3, + "w": 32, + "h": 25 }, "frame": { "x": 222, - "y": 28, - "w": 30, - "h": 26 + "y": 26, + "w": 32, + "h": 25 } }, { @@ -676,7 +697,7 @@ }, "frame": { "x": 0, - "y": 388, + "y": 446, "w": 27, "h": 29 } @@ -697,7 +718,7 @@ }, "frame": { "x": 0, - "y": 417, + "y": 475, "w": 27, "h": 29 } @@ -718,7 +739,7 @@ }, "frame": { "x": 0, - "y": 446, + "y": 504, "w": 29, "h": 28 } @@ -739,7 +760,7 @@ }, "frame": { "x": 0, - "y": 474, + "y": 532, "w": 29, "h": 28 } @@ -760,7 +781,7 @@ }, "frame": { "x": 0, - "y": 502, + "y": 560, "w": 29, "h": 27 } @@ -781,7 +802,7 @@ }, "frame": { "x": 0, - "y": 529, + "y": 587, "w": 29, "h": 27 } @@ -802,7 +823,7 @@ }, "frame": { "x": 0, - "y": 556, + "y": 614, "w": 28, "h": 28 } @@ -828,90 +849,6 @@ "h": 21 } }, - { - "filename": "975s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 32, - "h": 21 - }, - "frame": { - "x": 126, - "y": 54, - "w": 32, - "h": 21 - } - }, - { - "filename": "1014", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 4, - "y": 3, - "w": 32, - "h": 25 - }, - "frame": { - "x": 158, - "y": 52, - "w": 32, - "h": 25 - } - }, - { - "filename": "1014s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 4, - "y": 3, - "w": 32, - "h": 25 - }, - "frame": { - "x": 190, - "y": 52, - "w": 32, - "h": 25 - } - }, - { - "filename": "992s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 2, - "w": 30, - "h": 26 - }, - "frame": { - "x": 222, - "y": 54, - "w": 30, - "h": 26 - } - }, { "filename": "1024-terastal", "rotated": false, @@ -926,11 +863,95 @@ "w": 32, "h": 22 }, + "frame": { + "x": 126, + "y": 53, + "w": 32, + "h": 22 + } + }, + { + "filename": "1025", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 32, + "h": 24 + }, + "frame": { + "x": 158, + "y": 52, + "w": 32, + "h": 24 + } + }, + { + "filename": "1025s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 32, + "h": 24 + }, + "frame": { + "x": 190, + "y": 52, + "w": 32, + "h": 24 + } + }, + { + "filename": "992", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 6, + "y": 2, + "w": 30, + "h": 26 + }, + "frame": { + "x": 222, + "y": 51, + "w": 30, + "h": 26 + } + }, + { + "filename": "975s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 32, + "h": 21 + }, "frame": { "x": 93, "y": 75, "w": 32, - "h": 22 + "h": 21 } }, { @@ -955,7 +976,7 @@ } }, { - "filename": "1025", + "filename": "992s", "rotated": false, "trimmed": true, "sourceSize": { @@ -963,37 +984,16 @@ "h": 30 }, "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 32, - "h": 24 + "x": 6, + "y": 2, + "w": 30, + "h": 26 }, "frame": { "x": 157, - "y": 77, - "w": 32, - "h": 24 - } - }, - { - "filename": "1025s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 32, - "h": 24 - }, - "frame": { - "x": 189, - "y": 77, - "w": 32, - "h": 24 + "y": 76, + "w": 30, + "h": 26 } }, { @@ -1011,8 +1011,29 @@ "h": 25 }, "frame": { - "x": 221, - "y": 80, + "x": 187, + "y": 76, + "w": 30, + "h": 25 + } + }, + { + "filename": "993s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 6, + "y": 2, + "w": 30, + "h": 25 + }, + "frame": { + "x": 217, + "y": 77, "w": 30, "h": 25 } @@ -1033,7 +1054,7 @@ }, "frame": { "x": 27, - "y": 328, + "y": 386, "w": 23, "h": 30 } @@ -1054,7 +1075,7 @@ }, "frame": { "x": 27, - "y": 358, + "y": 416, "w": 23, "h": 30 } @@ -1075,7 +1096,7 @@ }, "frame": { "x": 27, - "y": 388, + "y": 446, "w": 25, "h": 30 } @@ -1096,7 +1117,7 @@ }, "frame": { "x": 27, - "y": 418, + "y": 476, "w": 25, "h": 28 } @@ -1117,7 +1138,7 @@ }, "frame": { "x": 29, - "y": 446, + "y": 504, "w": 25, "h": 30 } @@ -1138,7 +1159,7 @@ }, "frame": { "x": 29, - "y": 476, + "y": 534, "w": 25, "h": 28 } @@ -1159,7 +1180,7 @@ }, "frame": { "x": 29, - "y": 504, + "y": 562, "w": 26, "h": 28 } @@ -1180,7 +1201,7 @@ }, "frame": { "x": 29, - "y": 532, + "y": 590, "w": 27, "h": 26 } @@ -1201,32 +1222,11 @@ }, "frame": { "x": 28, - "y": 558, + "y": 616, "w": 28, "h": 28 } }, - { - "filename": "993s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 2, - "w": 30, - "h": 25 - }, - "frame": { - "x": 62, - "y": 86, - "w": 30, - "h": 25 - } - }, { "filename": "924", "rotated": false, @@ -1242,8 +1242,8 @@ "h": 20 }, "frame": { - "x": 92, - "y": 97, + "x": 62, + "y": 86, "w": 29, "h": 20 } @@ -1263,8 +1263,8 @@ "h": 20 }, "frame": { - "x": 121, - "y": 97, + "x": 61, + "y": 106, "w": 29, "h": 20 } @@ -1284,8 +1284,8 @@ "h": 22 }, "frame": { - "x": 150, - "y": 101, + "x": 91, + "y": 96, "w": 29, "h": 22 } @@ -1305,33 +1305,12 @@ "h": 22 }, "frame": { - "x": 179, - "y": 101, + "x": 120, + "y": 97, "w": 29, "h": 22 } }, - { - "filename": "935", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 7, - "w": 13, - "h": 21 - }, - "frame": { - "x": 208, - "y": 101, - "w": 13, - "h": 21 - } - }, { "filename": "925-three", "rotated": false, @@ -1347,8 +1326,8 @@ "h": 20 }, "frame": { - "x": 221, - "y": 105, + "x": 149, + "y": 102, "w": 29, "h": 20 } @@ -1368,54 +1347,12 @@ "h": 20 }, "frame": { - "x": 61, - "y": 111, + "x": 90, + "y": 118, "w": 29, "h": 20 } }, - { - "filename": "976", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 5, - "y": 10, - "w": 29, - "h": 18 - }, - "frame": { - "x": 90, - "y": 117, - "w": 29, - "h": 18 - } - }, - { - "filename": "976s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 5, - "y": 10, - "w": 29, - "h": 18 - }, - "frame": { - "x": 119, - "y": 117, - "w": 29, - "h": 18 - } - }, { "filename": "1022", "rotated": false, @@ -1431,8 +1368,8 @@ "h": 25 }, "frame": { - "x": 148, - "y": 123, + "x": 119, + "y": 119, "w": 29, "h": 25 } @@ -1452,12 +1389,33 @@ "h": 25 }, "frame": { - "x": 177, - "y": 123, + "x": 148, + "y": 122, "w": 29, "h": 25 } }, + { + "filename": "976", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 5, + "y": 10, + "w": 29, + "h": 18 + }, + "frame": { + "x": 31, + "y": 118, + "w": 29, + "h": 18 + } + }, { "filename": "8128-blaze", "rotated": false, @@ -1473,54 +1431,12 @@ "h": 27 }, "frame": { - "x": 206, - "y": 125, + "x": 30, + "y": 136, "w": 29, "h": 27 } }, - { - "filename": "913", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 5, - "w": 17, - "h": 23 - }, - "frame": { - "x": 235, - "y": 125, - "w": 17, - "h": 23 - } - }, - { - "filename": "913s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 5, - "w": 17, - "h": 23 - }, - "frame": { - "x": 235, - "y": 148, - "w": 17, - "h": 23 - } - }, { "filename": "8128s-blaze", "rotated": false, @@ -1536,8 +1452,8 @@ "h": 27 }, "frame": { - "x": 31, - "y": 118, + "x": 30, + "y": 163, "w": 29, "h": 27 } @@ -1558,7 +1474,7 @@ }, "frame": { "x": 30, - "y": 145, + "y": 190, "w": 27, "h": 28 } @@ -1579,7 +1495,7 @@ }, "frame": { "x": 30, - "y": 173, + "y": 218, "w": 27, "h": 28 } @@ -1600,7 +1516,7 @@ }, "frame": { "x": 30, - "y": 201, + "y": 246, "w": 26, "h": 28 } @@ -1621,7 +1537,7 @@ }, "frame": { "x": 30, - "y": 229, + "y": 274, "w": 27, "h": 26 } @@ -1642,7 +1558,7 @@ }, "frame": { "x": 30, - "y": 255, + "y": 300, "w": 25, "h": 27 } @@ -1663,13 +1579,13 @@ }, "frame": { "x": 30, - "y": 282, + "y": 327, "w": 25, "h": 27 } }, { - "filename": "916", + "filename": "964-hero", "rotated": false, "trimmed": true, "sourceSize": { @@ -1677,20 +1593,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 7, - "y": 9, - "w": 25, - "h": 19 + "x": 9, + "y": 0, + "w": 22, + "h": 29 }, "frame": { "x": 30, - "y": 309, - "w": 25, - "h": 19 + "y": 354, + "w": 22, + "h": 29 } }, { - "filename": "911", + "filename": "976s", "rotated": false, "trimmed": true, "sourceSize": { @@ -1698,16 +1614,16 @@ "h": 30 }, "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 28, - "h": 23 + "x": 5, + "y": 10, + "w": 29, + "h": 18 }, "frame": { "x": 60, - "y": 131, - "w": 28, - "h": 23 + "y": 126, + "w": 29, + "h": 18 } }, { @@ -1724,9 +1640,51 @@ "w": 28, "h": 24 }, + "frame": { + "x": 59, + "y": 144, + "w": 28, + "h": 24 + } + }, + { + "filename": "911", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 28, + "h": 23 + }, + "frame": { + "x": 59, + "y": 168, + "w": 28, + "h": 23 + } + }, + { + "filename": "8128s-aqua", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 5, + "w": 28, + "h": 24 + }, "frame": { "x": 57, - "y": 154, + "y": 191, "w": 28, "h": 24 } @@ -1747,13 +1705,13 @@ }, "frame": { "x": 57, - "y": 178, + "y": 215, "w": 28, "h": 23 } }, { - "filename": "968", + "filename": "950", "rotated": false, "trimmed": true, "sourceSize": { @@ -1761,37 +1719,58 @@ "h": 30 }, "spriteSourceSize": { - "x": 8, - "y": 0, - "w": 23, - "h": 28 - }, - "frame": { - "x": 56, - "y": 201, - "w": 23, - "h": 28 - } - }, - { - "filename": "964-hero", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 0, - "w": 22, - "h": 29 + "x": 6, + "y": 11, + "w": 28, + "h": 17 }, "frame": { "x": 57, - "y": 229, - "w": 22, - "h": 29 + "y": 238, + "w": 28, + "h": 17 + } + }, + { + "filename": "916", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 9, + "w": 25, + "h": 19 + }, + "frame": { + "x": 56, + "y": 255, + "w": 25, + "h": 19 + } + }, + { + "filename": "998", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 2, + "w": 25, + "h": 26 + }, + "frame": { + "x": 57, + "y": 274, + "w": 25, + "h": 26 } }, { @@ -1810,13 +1789,55 @@ }, "frame": { "x": 55, - "y": 258, + "y": 300, "w": 22, "h": 29 } }, { - "filename": "999", + "filename": "914", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 3, + "w": 24, + "h": 25 + }, + "frame": { + "x": 55, + "y": 329, + "w": 24, + "h": 25 + } + }, + { + "filename": "968", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 0, + "w": 23, + "h": 28 + }, + "frame": { + "x": 52, + "y": 354, + "w": 23, + "h": 28 + } + }, + { + "filename": "954", "rotated": false, "trimmed": true, "sourceSize": { @@ -1826,16 +1847,100 @@ "spriteSourceSize": { "x": 9, "y": 0, - "w": 22, + "w": 21, "h": 29 }, "frame": { - "x": 55, - "y": 287, - "w": 22, + "x": 77, + "y": 300, + "w": 21, "h": 29 } }, + { + "filename": "908", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 2, + "w": 20, + "h": 26 + }, + "frame": { + "x": 79, + "y": 329, + "w": 20, + "h": 26 + } + }, + { + "filename": "1016", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 1, + "w": 24, + "h": 27 + }, + "frame": { + "x": 75, + "y": 355, + "w": 24, + "h": 27 + } + }, + { + "filename": "950s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 6, + "y": 11, + "w": 28, + "h": 17 + }, + "frame": { + "x": 89, + "y": 138, + "w": 28, + "h": 17 + } + }, + { + "filename": "968s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 0, + "w": 23, + "h": 28 + }, + "frame": { + "x": 87, + "y": 155, + "w": 23, + "h": 28 + } + }, { "filename": "990", "rotated": false, @@ -1851,8 +1956,8 @@ "h": 23 }, "frame": { - "x": 88, - "y": 135, + "x": 117, + "y": 144, "w": 28, "h": 23 } @@ -1872,33 +1977,12 @@ "h": 23 }, "frame": { - "x": 116, - "y": 135, + "x": 145, + "y": 147, "w": 28, "h": 23 } }, - { - "filename": "8128s-aqua", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 5, - "w": 28, - "h": 24 - }, - "frame": { - "x": 85, - "y": 158, - "w": 28, - "h": 24 - } - }, { "filename": "972", "rotated": false, @@ -1914,8 +1998,8 @@ "h": 22 }, "frame": { - "x": 113, - "y": 158, + "x": 110, + "y": 167, "w": 27, "h": 22 } @@ -1935,14 +2019,14 @@ "h": 22 }, "frame": { - "x": 85, - "y": 182, + "x": 137, + "y": 170, "w": 27, "h": 22 } }, { - "filename": "968s", + "filename": "947", "rotated": false, "trimmed": true, "sourceSize": { @@ -1951,36 +2035,15 @@ }, "spriteSourceSize": { "x": 8, - "y": 0, + "y": 6, "w": 23, - "h": 28 + "h": 22 }, "frame": { - "x": 79, - "y": 204, + "x": 87, + "y": 183, "w": 23, - "h": 28 - } - }, - { - "filename": "998", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 25, - "h": 26 - }, - "frame": { - "x": 79, - "y": 232, - "w": 25, - "h": 26 + "h": 22 } }, { @@ -1998,14 +2061,14 @@ "h": 26 }, "frame": { - "x": 77, - "y": 258, + "x": 85, + "y": 205, "w": 25, "h": 26 } }, { - "filename": "999s", + "filename": "985", "rotated": false, "trimmed": true, "sourceSize": { @@ -2014,120 +2077,15 @@ }, "spriteSourceSize": { "x": 9, - "y": 0, - "w": 22, - "h": 29 + "y": 7, + "w": 27, + "h": 21 }, "frame": { - "x": 77, - "y": 284, - "w": 22, - "h": 29 - } - }, - { - "filename": "936", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 20, - "h": 28 - }, - "frame": { - "x": 102, - "y": 204, - "w": 20, - "h": 28 - } - }, - { - "filename": "908", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 2, - "w": 20, - "h": 26 - }, - "frame": { - "x": 104, - "y": 232, - "w": 20, - "h": 26 - } - }, - { - "filename": "982", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 2, - "w": 22, - "h": 26 - }, - "frame": { - "x": 102, - "y": 258, - "w": 22, - "h": 26 - } - }, - { - "filename": "1016", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 1, - "w": 24, - "h": 27 - }, - "frame": { - "x": 99, - "y": 284, - "w": 24, - "h": 27 - } - }, - { - "filename": "1017-hearthflame-mask", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 25, - "h": 24 - }, - "frame": { - "x": 113, - "y": 180, - "w": 25, - "h": 24 + "x": 110, + "y": 189, + "w": 27, + "h": 21 } }, { @@ -2145,77 +2103,14 @@ "h": 24 }, "frame": { - "x": 113, - "y": 180, + "x": 85, + "y": 231, "w": 25, "h": 24 } }, { - "filename": "1017s-hearthflame-mask", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 25, - "h": 24 - }, - "frame": { - "x": 113, - "y": 180, - "w": 25, - "h": 24 - } - }, - { - "filename": "1017s-hearthflame-mask-tera", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 25, - "h": 24 - }, - "frame": { - "x": 113, - "y": 180, - "w": 25, - "h": 24 - } - }, - { - "filename": "936s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 20, - "h": 28 - }, - "frame": { - "x": 122, - "y": 204, - "w": 20, - "h": 28 - } - }, - { - "filename": "954", + "filename": "999", "rotated": false, "trimmed": true, "sourceSize": { @@ -2225,18 +2120,18 @@ "spriteSourceSize": { "x": 9, "y": 0, - "w": 21, + "w": 22, "h": 29 }, "frame": { - "x": 124, - "y": 232, - "w": 21, + "x": 110, + "y": 210, + "w": 22, "h": 29 } }, { - "filename": "914", + "filename": "916s", "rotated": false, "trimmed": true, "sourceSize": { @@ -2244,57 +2139,57 @@ "h": 30 }, "spriteSourceSize": { - "x": 8, - "y": 3, - "w": 24, - "h": 25 - }, - "frame": { - "x": 124, - "y": 261, - "w": 24, - "h": 25 - } - }, - { - "filename": "914s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 3, - "w": 24, - "h": 25 - }, - "frame": { - "x": 123, - "y": 286, - "w": 24, - "h": 25 - } - }, - { - "filename": "916-female", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, + "x": 7, + "y": 9, "w": 25, + "h": 19 + }, + "frame": { + "x": 81, + "y": 255, + "w": 25, + "h": 19 + } + }, + { + "filename": "982", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 2, + "w": 22, + "h": 26 + }, + "frame": { + "x": 82, + "y": 274, + "w": 22, + "h": 26 + } + }, + { + "filename": "985s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 27, "h": 21 }, "frame": { - "x": 55, - "y": 316, - "w": 25, + "x": 137, + "y": 192, + "w": 27, "h": 21 } }, @@ -2313,14 +2208,182 @@ "h": 27 }, "frame": { - "x": 50, - "y": 337, + "x": 132, + "y": 213, "w": 24, "h": 27 } }, { - "filename": "987", + "filename": "917", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 6, + "w": 22, + "h": 22 + }, + "frame": { + "x": 110, + "y": 239, + "w": 22, + "h": 22 + } + }, + { + "filename": "914s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 3, + "w": 24, + "h": 25 + }, + "frame": { + "x": 132, + "y": 240, + "w": 24, + "h": 25 + } + }, + { + "filename": "956", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 26, + "h": 22 + }, + "frame": { + "x": 106, + "y": 261, + "w": 26, + "h": 22 + } + }, + { + "filename": "916-female", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 25, + "h": 21 + }, + "frame": { + "x": 132, + "y": 265, + "w": 25, + "h": 21 + } + }, + { + "filename": "1002", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 27, + "h": 21 + }, + "frame": { + "x": 104, + "y": 283, + "w": 27, + "h": 21 + } + }, + { + "filename": "956s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 26, + "h": 22 + }, + "frame": { + "x": 131, + "y": 286, + "w": 26, + "h": 22 + } + }, + { + "filename": "1017-hearthflame-mask", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 25, + "h": 24 + }, + "frame": { + "x": 98, + "y": 304, + "w": 25, + "h": 24 + } + }, + { + "filename": "999s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 0, + "w": 22, + "h": 29 + }, + "frame": { + "x": 99, + "y": 328, + "w": 22, + "h": 29 + } + }, + { + "filename": "982s", "rotated": false, "trimmed": true, "sourceSize": { @@ -2329,15 +2392,36 @@ }, "spriteSourceSize": { "x": 10, - "y": 4, - "w": 24, - "h": 24 + "y": 2, + "w": 22, + "h": 26 }, "frame": { - "x": 50, - "y": 364, - "w": 24, - "h": 24 + "x": 99, + "y": 357, + "w": 22, + "h": 26 + } + }, + { + "filename": "1002s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 27, + "h": 21 + }, + "frame": { + "x": 123, + "y": 308, + "w": 27, + "h": 21 } }, { @@ -2355,8 +2439,8 @@ "h": 29 }, "frame": { - "x": 52, - "y": 388, + "x": 121, + "y": 329, "w": 21, "h": 29 } @@ -2376,14 +2460,14 @@ "h": 27 }, "frame": { - "x": 52, - "y": 417, + "x": 121, + "y": 358, "w": 22, "h": 27 } }, { - "filename": "1023", + "filename": "936", "rotated": false, "trimmed": true, "sourceSize": { @@ -2392,19 +2476,19 @@ }, "spriteSourceSize": { "x": 10, - "y": 1, + "y": 0, "w": 20, "h": 28 }, "frame": { - "x": 54, - "y": 444, + "x": 164, + "y": 170, "w": 20, "h": 28 } }, { - "filename": "1023s", + "filename": "913", "rotated": false, "trimmed": true, "sourceSize": { @@ -2412,583 +2496,16 @@ "h": 30 }, "spriteSourceSize": { - "x": 10, - "y": 1, - "w": 20, - "h": 28 + "x": 11, + "y": 5, + "w": 17, + "h": 23 }, "frame": { - "x": 54, - "y": 472, - "w": 20, - "h": 28 - } - }, - { - "filename": "1000s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 1, - "w": 22, - "h": 27 - }, - "frame": { - "x": 55, - "y": 500, - "w": 22, - "h": 27 - } - }, - { - "filename": "1006", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 1, - "w": 22, - "h": 27 - }, - "frame": { - "x": 56, - "y": 527, - "w": 22, - "h": 27 - } - }, - { - "filename": "1006s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 1, - "w": 22, - "h": 27 - }, - "frame": { - "x": 56, - "y": 554, - "w": 22, - "h": 27 - } - }, - { - "filename": "908s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 2, - "w": 20, - "h": 26 - }, - "frame": { - "x": 80, - "y": 313, - "w": 20, - "h": 26 - } - }, - { - "filename": "956", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 26, - "h": 22 - }, - "frame": { - "x": 100, - "y": 311, - "w": 26, - "h": 22 - } - }, - { - "filename": "917", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 6, - "w": 22, - "h": 22 - }, - "frame": { - "x": 126, - "y": 311, - "w": 22, - "h": 22 - } - }, - { - "filename": "956s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 26, - "h": 22 - }, - "frame": { - "x": 74, - "y": 339, - "w": 26, - "h": 22 - } - }, - { - "filename": "982s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 2, - "w": 22, - "h": 26 - }, - "frame": { - "x": 100, - "y": 333, - "w": 22, - "h": 26 - } - }, - { - "filename": "987s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 74, - "y": 361, - "w": 24, - "h": 24 - } - }, - { - "filename": "1012-artisan", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 26, - "h": 22 - }, - "frame": { - "x": 122, - "y": 333, - "w": 26, - "h": 22 - } - }, - { - "filename": "1012-counterfeit", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 26, - "h": 22 - }, - "frame": { - "x": 122, - "y": 333, - "w": 26, - "h": 22 - } - }, - { - "filename": "950", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 28, - "h": 17 - }, - "frame": { - "x": 144, - "y": 148, - "w": 28, - "h": 17 - } - }, - { - "filename": "950s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 28, - "h": 17 - }, - "frame": { - "x": 172, - "y": 148, - "w": 28, - "h": 17 - } - }, - { - "filename": "985", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 7, - "w": 27, - "h": 21 - }, - "frame": { - "x": 140, - "y": 165, - "w": 27, - "h": 21 - } - }, - { - "filename": "985s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 7, - "w": 27, - "h": 21 - }, - "frame": { - "x": 167, - "y": 165, - "w": 27, - "h": 21 - } - }, - { - "filename": "953", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 12, - "w": 24, - "h": 16 - }, - "frame": { - "x": 138, - "y": 186, - "w": 24, - "h": 16 - } - }, - { - "filename": "1010", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 2, - "w": 21, - "h": 26 - }, - "frame": { - "x": 142, - "y": 202, - "w": 21, - "h": 26 - } - }, - { - "filename": "953s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 12, - "w": 24, - "h": 16 - }, - "frame": { - "x": 162, - "y": 186, - "w": 24, - "h": 16 - } - }, - { - "filename": "1010s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 2, - "w": 21, - "h": 26 - }, - "frame": { - "x": 163, - "y": 202, - "w": 21, - "h": 26 - } - }, - { - "filename": "1002", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 27, - "h": 21 - }, - "frame": { - "x": 145, - "y": 228, - "w": 27, - "h": 21 - } - }, - { - "filename": "1017-cornerstone-mask", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 148, - "y": 249, - "w": 24, - "h": 24 - } - }, - { - "filename": "1017-cornerstone-mask-tera", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 148, - "y": 249, - "w": 24, - "h": 24 - } - }, - { - "filename": "1017s-cornerstone-mask", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 148, - "y": 249, - "w": 24, - "h": 24 - } - }, - { - "filename": "1017s-cornerstone-mask-tera", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 148, - "y": 249, - "w": 24, - "h": 24 - } - }, - { - "filename": "973", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 15, - "h": 26 - }, - "frame": { - "x": 172, - "y": 228, - "w": 15, - "h": 26 - } - }, - { - "filename": "916s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 9, - "w": 25, - "h": 19 - }, - "frame": { - "x": 148, - "y": 273, - "w": 25, - "h": 19 - } - }, - { - "filename": "997", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 9, - "w": 21, - "h": 19 - }, - "frame": { - "x": 147, - "y": 292, - "w": 21, - "h": 19 + "x": 173, + "y": 147, + "w": 17, + "h": 23 } }, { @@ -3006,428 +2523,8 @@ "h": 25 }, "frame": { - "x": 148, - "y": 311, - "w": 20, - "h": 25 - } - }, - { - "filename": "910", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 7, - "w": 21, - "h": 21 - }, - "frame": { - "x": 148, - "y": 336, - "w": 21, - "h": 21 - } - }, - { - "filename": "906", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 9, - "w": 18, - "h": 19 - }, - "frame": { - "x": 172, - "y": 254, - "w": 18, - "h": 19 - } - }, - { - "filename": "906s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 9, - "w": 18, - "h": 19 - }, - "frame": { - "x": 173, - "y": 273, - "w": 18, - "h": 19 - } - }, - { - "filename": "1017-teal-mask", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 168, - "y": 292, - "w": 24, - "h": 24 - } - }, - { - "filename": "1017-teal-mask-tera", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 168, - "y": 292, - "w": 24, - "h": 24 - } - }, - { - "filename": "1017s-teal-mask", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 168, - "y": 292, - "w": 24, - "h": 24 - } - }, - { - "filename": "1017s-teal-mask-tera", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 168, - "y": 292, - "w": 24, - "h": 24 - } - }, - { - "filename": "943", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 24, - "h": 20 - }, - "frame": { - "x": 168, - "y": 316, - "w": 24, - "h": 20 - } - }, - { - "filename": "916s-female", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 25, - "h": 21 - }, - "frame": { - "x": 169, - "y": 336, - "w": 25, - "h": 21 - } - }, - { - "filename": "1002s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 27, - "h": 21 - }, - "frame": { - "x": 200, - "y": 152, - "w": 27, - "h": 21 - } - }, - { - "filename": "945", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 25, - "h": 22 - }, - "frame": { - "x": 227, - "y": 171, - "w": 25, - "h": 22 - } - }, - { - "filename": "1012s-artisan", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 26, - "h": 22 - }, - "frame": { - "x": 194, - "y": 173, - "w": 26, - "h": 22 - } - }, - { - "filename": "1012s-counterfeit", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 26, - "h": 22 - }, - "frame": { - "x": 194, - "y": 173, - "w": 26, - "h": 22 - } - }, - { - "filename": "973s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 2, - "w": 15, - "h": 26 - }, - "frame": { - "x": 184, - "y": 202, - "w": 15, - "h": 26 - } - }, - { - "filename": "918", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 4, - "w": 22, - "h": 24 - }, - "frame": { - "x": 199, - "y": 195, - "w": 22, - "h": 24 - } - }, - { - "filename": "1017-wellspring-mask", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 221, - "y": 193, - "w": 24, - "h": 24 - } - }, - { - "filename": "1017-wellspring-mask-tera", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 221, - "y": 193, - "w": 24, - "h": 24 - } - }, - { - "filename": "1017s-wellspring-mask", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 221, - "y": 193, - "w": 24, - "h": 24 - } - }, - { - "filename": "1017s-wellspring-mask-tera", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 221, - "y": 193, - "w": 24, - "h": 24 - } - }, - { - "filename": "949s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 3, - "w": 20, - "h": 25 - }, - "frame": { - "x": 187, - "y": 228, + "x": 177, + "y": 122, "w": 20, "h": 25 } @@ -3447,138 +2544,12 @@ "h": 20 }, "frame": { - "x": 190, - "y": 253, + "x": 178, + "y": 102, "w": 20, "h": 20 } }, - { - "filename": "967", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 9, - "w": 20, - "h": 19 - }, - "frame": { - "x": 191, - "y": 273, - "w": 20, - "h": 19 - } - }, - { - "filename": "962", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 3, - "w": 19, - "h": 25 - }, - "frame": { - "x": 192, - "y": 292, - "w": 19, - "h": 25 - } - }, - { - "filename": "967s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 9, - "w": 20, - "h": 19 - }, - "frame": { - "x": 192, - "y": 317, - "w": 20, - "h": 19 - } - }, - { - "filename": "910s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 7, - "w": 21, - "h": 21 - }, - "frame": { - "x": 194, - "y": 336, - "w": 21, - "h": 21 - } - }, - { - "filename": "962s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 3, - "w": 19, - "h": 25 - }, - "frame": { - "x": 207, - "y": 219, - "w": 19, - "h": 25 - } - }, - { - "filename": "918s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 4, - "w": 22, - "h": 24 - }, - "frame": { - "x": 226, - "y": 217, - "w": 22, - "h": 24 - } - }, { "filename": "923", "rotated": false, @@ -3594,77 +2565,14 @@ "h": 24 }, "frame": { - "x": 210, - "y": 244, + "x": 198, + "y": 101, "w": 19, "h": 24 } }, { - "filename": "961", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 229, - "y": 241, - "w": 23, - "h": 23 - } - }, - { - "filename": "961s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 229, - "y": 264, - "w": 23, - "h": 23 - } - }, - { - "filename": "909", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 9, - "w": 18, - "h": 19 - }, - "frame": { - "x": 211, - "y": 268, - "w": 18, - "h": 19 - } - }, - { - "filename": "945s", + "filename": "1012-artisan", "rotated": false, "trimmed": true, "sourceSize": { @@ -3674,16 +2582,58 @@ "spriteSourceSize": { "x": 7, "y": 6, - "w": 25, + "w": 26, "h": 22 }, "frame": { - "x": 211, - "y": 287, - "w": 25, + "x": 217, + "y": 102, + "w": 26, "h": 22 } }, + { + "filename": "908s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 2, + "w": 20, + "h": 26 + }, + "frame": { + "x": 197, + "y": 125, + "w": 20, + "h": 26 + } + }, + { + "filename": "1000s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 1, + "w": 22, + "h": 27 + }, + "frame": { + "x": 217, + "y": 124, + "w": 22, + "h": 27 + } + }, { "filename": "948", "rotated": false, @@ -3699,8 +2649,8 @@ "h": 21 }, "frame": { - "x": 236, - "y": 287, + "x": 239, + "y": 124, "w": 16, "h": 21 } @@ -3720,14 +2670,35 @@ "h": 21 }, "frame": { - "x": 236, - "y": 308, + "x": 239, + "y": 145, "w": 16, "h": 21 } }, { - "filename": "927", + "filename": "1012-counterfeit", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 26, + "h": 22 + }, + "frame": { + "x": 190, + "y": 151, + "w": 26, + "h": 22 + } + }, + { + "filename": "947s", "rotated": false, "trimmed": true, "sourceSize": { @@ -3736,19 +2707,82 @@ }, "spriteSourceSize": { "x": 8, - "y": 7, - "w": 24, - "h": 21 + "y": 6, + "w": 23, + "h": 22 }, "frame": { - "x": 212, - "y": 309, - "w": 24, - "h": 21 + "x": 216, + "y": 151, + "w": 23, + "h": 22 } }, { - "filename": "920", + "filename": "951", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 8, + "w": 16, + "h": 20 + }, + "frame": { + "x": 239, + "y": 166, + "w": 16, + "h": 20 + } + }, + { + "filename": "1017s-hearthflame-mask-tera", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 25, + "h": 24 + }, + "frame": { + "x": 184, + "y": 173, + "w": 25, + "h": 24 + } + }, + { + "filename": "1017s-hearthflame-mask", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 25, + "h": 24 + }, + "frame": { + "x": 209, + "y": 173, + "w": 25, + "h": 24 + } + }, + { + "filename": "1010", "rotated": false, "trimmed": true, "sourceSize": { @@ -3757,19 +2791,19 @@ }, "spriteSourceSize": { "x": 9, - "y": 5, - "w": 22, - "h": 23 + "y": 2, + "w": 21, + "h": 26 }, "frame": { - "x": 215, - "y": 330, - "w": 22, - "h": 23 + "x": 234, + "y": 186, + "w": 21, + "h": 26 } }, { - "filename": "912", + "filename": "936s", "rotated": false, "trimmed": true, "sourceSize": { @@ -3777,20 +2811,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 13, - "y": 9, - "w": 15, - "h": 19 + "x": 10, + "y": 0, + "w": 20, + "h": 28 }, "frame": { - "x": 237, - "y": 329, - "w": 15, - "h": 19 + "x": 156, + "y": 213, + "w": 20, + "h": 28 } }, { - "filename": "912s", + "filename": "963", "rotated": false, "trimmed": true, "sourceSize": { @@ -3798,16 +2832,79 @@ "h": 30 }, "spriteSourceSize": { - "x": 13, - "y": 9, - "w": 15, - "h": 19 + "x": 9, + "y": 13, + "w": 22, + "h": 15 }, "frame": { - "x": 237, - "y": 348, - "w": 15, - "h": 19 + "x": 164, + "y": 198, + "w": 22, + "h": 15 + } + }, + { + "filename": "918", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 4, + "w": 22, + "h": 24 + }, + "frame": { + "x": 156, + "y": 241, + "w": 22, + "h": 24 + } + }, + { + "filename": "1006", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 1, + "w": 22, + "h": 27 + }, + "frame": { + "x": 157, + "y": 265, + "w": 22, + "h": 27 + } + }, + { + "filename": "1012s-artisan", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 26, + "h": 22 + }, + "frame": { + "x": 186, + "y": 197, + "w": 26, + "h": 22 } }, { @@ -3825,14 +2922,14 @@ "h": 22 }, "frame": { - "x": 215, - "y": 353, + "x": 212, + "y": 197, "w": 22, "h": 22 } }, { - "filename": "960", + "filename": "1010s", "rotated": false, "trimmed": true, "sourceSize": { @@ -3840,16 +2937,268 @@ "h": 30 }, "spriteSourceSize": { - "x": 12, - "y": 10, - "w": 15, - "h": 18 + "x": 9, + "y": 2, + "w": 21, + "h": 26 }, "frame": { - "x": 237, - "y": 367, - "w": 15, - "h": 18 + "x": 234, + "y": 212, + "w": 21, + "h": 26 + } + }, + { + "filename": "1012s-counterfeit", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 26, + "h": 22 + }, + "frame": { + "x": 176, + "y": 219, + "w": 26, + "h": 22 + } + }, + { + "filename": "987", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 178, + "y": 241, + "w": 24, + "h": 24 + } + }, + { + "filename": "1006s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 1, + "w": 22, + "h": 27 + }, + "frame": { + "x": 202, + "y": 219, + "w": 22, + "h": 27 + } + }, + { + "filename": "987s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 179, + "y": 265, + "w": 24, + "h": 24 + } + }, + { + "filename": "997", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 9, + "w": 21, + "h": 19 + }, + "frame": { + "x": 202, + "y": 246, + "w": 21, + "h": 19 + } + }, + { + "filename": "918s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 4, + "w": 22, + "h": 24 + }, + "frame": { + "x": 203, + "y": 265, + "w": 22, + "h": 24 + } + }, + { + "filename": "916s-female", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 25, + "h": 21 + }, + "frame": { + "x": 157, + "y": 292, + "w": 25, + "h": 21 + } + }, + { + "filename": "1017-cornerstone-mask-tera", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 182, + "y": 289, + "w": 24, + "h": 24 + } + }, + { + "filename": "923s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 4, + "w": 19, + "h": 24 + }, + "frame": { + "x": 206, + "y": 289, + "w": 19, + "h": 24 + } + }, + { + "filename": "1023", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 1, + "w": 20, + "h": 28 + }, + "frame": { + "x": 142, + "y": 329, + "w": 20, + "h": 28 + } + }, + { + "filename": "1023s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 1, + "w": 20, + "h": 28 + }, + "frame": { + "x": 143, + "y": 357, + "w": 20, + "h": 28 + } + }, + { + "filename": "953", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 12, + "w": 24, + "h": 16 + }, + "frame": { + "x": 150, + "y": 313, + "w": 24, + "h": 16 } }, { @@ -3867,8 +3216,8 @@ "h": 21 }, "frame": { - "x": 122, - "y": 355, + "x": 174, + "y": 313, "w": 25, "h": 21 } @@ -3888,12 +3237,264 @@ "h": 21 }, "frame": { - "x": 147, - "y": 357, + "x": 199, + "y": 313, "w": 25, "h": 21 } }, + { + "filename": "945", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 25, + "h": 22 + }, + "frame": { + "x": 162, + "y": 334, + "w": 25, + "h": 22 + } + }, + { + "filename": "945s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 25, + "h": 22 + }, + "frame": { + "x": 187, + "y": 334, + "w": 25, + "h": 22 + } + }, + { + "filename": "1017-cornerstone-mask", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 163, + "y": 356, + "w": 24, + "h": 24 + } + }, + { + "filename": "1017-teal-mask-tera", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 187, + "y": 356, + "w": 24, + "h": 24 + } + }, + { + "filename": "907", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 6, + "w": 17, + "h": 22 + }, + "frame": { + "x": 212, + "y": 334, + "w": 17, + "h": 22 + } + }, + { + "filename": "949s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 3, + "w": 20, + "h": 25 + }, + "frame": { + "x": 211, + "y": 356, + "w": 20, + "h": 25 + } + }, + { + "filename": "1017-teal-mask", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 231, + "y": 238, + "w": 24, + "h": 24 + } + }, + { + "filename": "1017-wellspring-mask-tera", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 231, + "y": 262, + "w": 24, + "h": 24 + } + }, + { + "filename": "1017-wellspring-mask", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 231, + "y": 286, + "w": 24, + "h": 24 + } + }, + { + "filename": "1017s-cornerstone-mask-tera", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 231, + "y": 310, + "w": 24, + "h": 24 + } + }, + { + "filename": "1017s-cornerstone-mask", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 231, + "y": 334, + "w": 24, + "h": 24 + } + }, + { + "filename": "1017s-teal-mask-tera", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 231, + "y": 358, + "w": 24, + "h": 24 + } + }, { "filename": "952", "rotated": false, @@ -3909,182 +3510,14 @@ "h": 22 }, "frame": { - "x": 172, - "y": 357, + "x": 163, + "y": 380, "w": 25, "h": 22 } }, { - "filename": "909s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 9, - "w": 18, - "h": 19 - }, - "frame": { - "x": 197, - "y": 357, - "w": 18, - "h": 19 - } - }, - { - "filename": "920s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 5, - "w": 22, - "h": 23 - }, - "frame": { - "x": 100, - "y": 359, - "w": 22, - "h": 23 - } - }, - { - "filename": "952s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 25, - "h": 22 - }, - "frame": { - "x": 122, - "y": 376, - "w": 25, - "h": 22 - } - }, - { - "filename": "966", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 25, - "h": 21 - }, - "frame": { - "x": 147, - "y": 378, - "w": 25, - "h": 21 - } - }, - { - "filename": "966s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 25, - "h": 21 - }, - "frame": { - "x": 172, - "y": 379, - "w": 25, - "h": 21 - } - }, - { - "filename": "923s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 4, - "w": 19, - "h": 24 - }, - "frame": { - "x": 197, - "y": 376, - "w": 19, - "h": 24 - } - }, - { - "filename": "941", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 7, - "w": 21, - "h": 21 - }, - "frame": { - "x": 216, - "y": 375, - "w": 21, - "h": 21 - } - }, - { - "filename": "960s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 10, - "w": 15, - "h": 18 - }, - "frame": { - "x": 237, - "y": 385, - "w": 15, - "h": 18 - } - }, - { - "filename": "927s", + "filename": "961", "rotated": false, "trimmed": true, "sourceSize": { @@ -4093,122 +3526,17 @@ }, "spriteSourceSize": { "x": 8, - "y": 7, - "w": 24, - "h": 21 - }, - "frame": { - "x": 98, - "y": 382, - "w": 24, - "h": 21 - } - }, - { - "filename": "943s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 24, - "h": 20 - }, - "frame": { - "x": 74, - "y": 385, - "w": 24, - "h": 20 - } - }, - { - "filename": "986", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, "y": 5, "w": 23, "h": 23 }, "frame": { - "x": 74, - "y": 405, + "x": 188, + "y": 380, "w": 23, "h": 23 } }, - { - "filename": "986s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 74, - "y": 428, - "w": 23, - "h": 23 - } - }, - { - "filename": "947", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 6, - "w": 23, - "h": 22 - }, - "frame": { - "x": 74, - "y": 451, - "w": 23, - "h": 22 - } - }, - { - "filename": "947s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 6, - "w": 23, - "h": 22 - }, - "frame": { - "x": 74, - "y": 473, - "w": 23, - "h": 22 - } - }, { "filename": "991", "rotated": false, @@ -4224,12 +3552,453 @@ "h": 24 }, "frame": { - "x": 77, - "y": 495, + "x": 211, + "y": 381, "w": 20, "h": 24 } }, + { + "filename": "1017s-teal-mask", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 231, + "y": 382, + "w": 24, + "h": 24 + } + }, + { + "filename": "952s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 25, + "h": 22 + }, + "frame": { + "x": 52, + "y": 382, + "w": 25, + "h": 22 + } + }, + { + "filename": "1017s-wellspring-mask-tera", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 50, + "y": 404, + "w": 24, + "h": 24 + } + }, + { + "filename": "920", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 5, + "w": 22, + "h": 23 + }, + "frame": { + "x": 77, + "y": 382, + "w": 22, + "h": 23 + } + }, + { + "filename": "920s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 5, + "w": 22, + "h": 23 + }, + "frame": { + "x": 99, + "y": 383, + "w": 22, + "h": 23 + } + }, + { + "filename": "966-caph-starmobile", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 25, + "h": 21 + }, + "frame": { + "x": 121, + "y": 385, + "w": 25, + "h": 21 + } + }, + { + "filename": "907s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 6, + "w": 17, + "h": 22 + }, + "frame": { + "x": 146, + "y": 385, + "w": 17, + "h": 22 + } + }, + { + "filename": "966-navi-starmobile", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 25, + "h": 21 + }, + "frame": { + "x": 74, + "y": 405, + "w": 25, + "h": 21 + } + }, + { + "filename": "966-ruchbah-starmobile", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 25, + "h": 21 + }, + "frame": { + "x": 99, + "y": 406, + "w": 25, + "h": 21 + } + }, + { + "filename": "910", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 21, + "h": 21 + }, + "frame": { + "x": 124, + "y": 406, + "w": 21, + "h": 21 + } + }, + { + "filename": "953s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 12, + "w": 24, + "h": 16 + }, + "frame": { + "x": 50, + "y": 428, + "w": 24, + "h": 16 + } + }, + { + "filename": "966-schedar-starmobile", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 25, + "h": 21 + }, + "frame": { + "x": 74, + "y": 426, + "w": 25, + "h": 21 + } + }, + { + "filename": "966-segin-starmobile", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 25, + "h": 21 + }, + "frame": { + "x": 99, + "y": 427, + "w": 25, + "h": 21 + } + }, + { + "filename": "910s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 21, + "h": 21 + }, + "frame": { + "x": 124, + "y": 427, + "w": 21, + "h": 21 + } + }, + { + "filename": "962", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 3, + "w": 19, + "h": 25 + }, + "frame": { + "x": 145, + "y": 407, + "w": 19, + "h": 25 + } + }, + { + "filename": "1017s-wellspring-mask", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 164, + "y": 402, + "w": 24, + "h": 24 + } + }, + { + "filename": "961s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 23, + "h": 23 + }, + "frame": { + "x": 188, + "y": 403, + "w": 23, + "h": 23 + } + }, + { + "filename": "939", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 20, + "h": 21 + }, + "frame": { + "x": 211, + "y": 405, + "w": 20, + "h": 21 + } + }, + { + "filename": "927", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 7, + "w": 24, + "h": 21 + }, + "frame": { + "x": 231, + "y": 406, + "w": 24, + "h": 21 + } + }, + { + "filename": "962s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 3, + "w": 19, + "h": 25 + }, + "frame": { + "x": 52, + "y": 444, + "w": 19, + "h": 25 + } + }, + { + "filename": "966", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 25, + "h": 21 + }, + "frame": { + "x": 71, + "y": 447, + "w": 25, + "h": 21 + } + }, { "filename": "991s", "rotated": false, @@ -4245,12 +4014,117 @@ "h": 24 }, "frame": { - "x": 78, - "y": 519, + "x": 52, + "y": 469, "w": 20, "h": 24 } }, + { + "filename": "986", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 5, + "w": 23, + "h": 23 + }, + "frame": { + "x": 72, + "y": 468, + "w": 23, + "h": 23 + } + }, + { + "filename": "966s-caph-starmobile", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 25, + "h": 21 + }, + "frame": { + "x": 96, + "y": 448, + "w": 25, + "h": 21 + } + }, + { + "filename": "927s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 7, + "w": 24, + "h": 21 + }, + "frame": { + "x": 121, + "y": 448, + "w": 24, + "h": 21 + } + }, + { + "filename": "966s-navi-starmobile", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 25, + "h": 21 + }, + "frame": { + "x": 95, + "y": 469, + "w": 25, + "h": 21 + } + }, + { + "filename": "966s-ruchbah-starmobile", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 25, + "h": 21 + }, + "frame": { + "x": 120, + "y": 469, + "w": 25, + "h": 21 + } + }, { "filename": "1013-masterpiece", "rotated": false, @@ -4266,8 +4140,8 @@ "h": 24 }, "frame": { - "x": 78, - "y": 543, + "x": 145, + "y": 432, "w": 20, "h": 24 } @@ -4287,14 +4161,14 @@ "h": 24 }, "frame": { - "x": 78, - "y": 543, + "x": 145, + "y": 456, "w": 20, "h": 24 } }, { - "filename": "1013s-masterpiece", + "filename": "966s-schedar-starmobile", "rotated": false, "trimmed": true, "sourceSize": { @@ -4302,62 +4176,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 10, - "y": 4, - "w": 20, - "h": 24 - }, - "frame": { - "x": 78, - "y": 567, - "w": 20, - "h": 24 - } - }, - { - "filename": "1013s-unremarkable", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 4, - "w": 20, - "h": 24 - }, - "frame": { - "x": 78, - "y": 567, - "w": 20, - "h": 24 - } - }, - { - "filename": "941s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, + "x": 7, "y": 7, - "w": 21, + "w": 25, "h": 21 }, "frame": { - "x": 216, - "y": 396, - "w": 21, + "x": 165, + "y": 426, + "w": 25, "h": 21 } }, { - "filename": "996", + "filename": "966s-segin-starmobile", "rotated": false, "trimmed": true, "sourceSize": { @@ -4365,16 +4197,37 @@ "h": 30 }, "spriteSourceSize": { - "x": 12, - "y": 12, - "w": 15, - "h": 16 + "x": 7, + "y": 7, + "w": 25, + "h": 21 }, "frame": { - "x": 237, - "y": 403, - "w": 15, - "h": 16 + "x": 165, + "y": 447, + "w": 25, + "h": 21 + } + }, + { + "filename": "966s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 25, + "h": 21 + }, + "frame": { + "x": 190, + "y": 426, + "w": 25, + "h": 21 } }, { @@ -4392,12 +4245,33 @@ "h": 21 }, "frame": { - "x": 122, - "y": 398, + "x": 190, + "y": 447, "w": 25, "h": 21 } }, + { + "filename": "973", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 2, + "w": 15, + "h": 26 + }, + "frame": { + "x": 215, + "y": 426, + "w": 15, + "h": 26 + } + }, { "filename": "8128s", "rotated": false, @@ -4413,14 +4287,14 @@ "h": 21 }, "frame": { - "x": 147, - "y": 399, + "x": 230, + "y": 427, "w": 25, "h": 21 } }, { - "filename": "1024", + "filename": "943", "rotated": false, "trimmed": true, "sourceSize": { @@ -4434,8 +4308,29 @@ "h": 20 }, "frame": { - "x": 172, - "y": 400, + "x": 165, + "y": 468, + "w": 24, + "h": 20 + } + }, + { + "filename": "943s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 24, + "h": 20 + }, + "frame": { + "x": 189, + "y": 468, "w": 24, "h": 20 } @@ -4455,12 +4350,33 @@ "h": 20 }, "frame": { - "x": 196, - "y": 400, + "x": 145, + "y": 480, "w": 20, "h": 20 } }, + { + "filename": "1024", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 24, + "h": 20 + }, + "frame": { + "x": 165, + "y": 488, + "w": 24, + "h": 20 + } + }, { "filename": "1024s", "rotated": false, @@ -4476,476 +4392,14 @@ "h": 20 }, "frame": { - "x": 98, - "y": 403, + "x": 189, + "y": 488, "w": 24, "h": 20 } }, { - "filename": "939", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 7, - "w": 20, - "h": 21 - }, - "frame": { - "x": 97, - "y": 423, - "w": 20, - "h": 21 - } - }, - { - "filename": "939s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 7, - "w": 20, - "h": 21 - }, - "frame": { - "x": 97, - "y": 444, - "w": 20, - "h": 21 - } - }, - { - "filename": "971", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 7, - "w": 20, - "h": 21 - }, - "frame": { - "x": 97, - "y": 465, - "w": 20, - "h": 21 - } - }, - { - "filename": "971s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 7, - "w": 20, - "h": 21 - }, - "frame": { - "x": 97, - "y": 486, - "w": 20, - "h": 21 - } - }, - { - "filename": "969", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 16, - "w": 18, - "h": 12 - }, - "frame": { - "x": 97, - "y": 507, - "w": 18, - "h": 12 - } - }, - { - "filename": "907", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 6, - "w": 17, - "h": 22 - }, - "frame": { - "x": 98, - "y": 519, - "w": 17, - "h": 22 - } - }, - { - "filename": "907s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 6, - "w": 17, - "h": 22 - }, - "frame": { - "x": 98, - "y": 541, - "w": 17, - "h": 22 - } - }, - { - "filename": "929", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 6, - "w": 17, - "h": 22 - }, - "frame": { - "x": 98, - "y": 563, - "w": 17, - "h": 22 - } - }, - { - "filename": "929s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 6, - "w": 17, - "h": 22 - }, - "frame": { - "x": 117, - "y": 423, - "w": 17, - "h": 22 - } - }, - { - "filename": "1011", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 5, - "w": 19, - "h": 22 - }, - "frame": { - "x": 117, - "y": 445, - "w": 19, - "h": 22 - } - }, - { - "filename": "1011s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 5, - "w": 19, - "h": 22 - }, - "frame": { - "x": 117, - "y": 467, - "w": 19, - "h": 22 - } - }, - { - "filename": "931-white-plumage", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 8, - "w": 20, - "h": 20 - }, - "frame": { - "x": 117, - "y": 489, - "w": 20, - "h": 20 - } - }, - { - "filename": "1004", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 8, - "w": 21, - "h": 20 - }, - "frame": { - "x": 115, - "y": 509, - "w": 21, - "h": 20 - } - }, - { - "filename": "1004s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 8, - "w": 21, - "h": 20 - }, - "frame": { - "x": 115, - "y": 529, - "w": 21, - "h": 20 - } - }, - { - "filename": "931-yellow-plumage", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 8, - "w": 20, - "h": 20 - }, - "frame": { - "x": 115, - "y": 549, - "w": 20, - "h": 20 - } - }, - { - "filename": "931s-blue-plumage", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 8, - "w": 20, - "h": 20 - }, - "frame": { - "x": 115, - "y": 569, - "w": 20, - "h": 20 - } - }, - { - "filename": "935s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 7, - "w": 13, - "h": 21 - }, - "frame": { - "x": 134, - "y": 419, - "w": 13, - "h": 21 - } - }, - { - "filename": "931s-green-plumage", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 8, - "w": 20, - "h": 20 - }, - "frame": { - "x": 147, - "y": 420, - "w": 20, - "h": 20 - } - }, - { - "filename": "931s-white-plumage", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 8, - "w": 20, - "h": 20 - }, - "frame": { - "x": 167, - "y": 420, - "w": 20, - "h": 20 - } - }, - { - "filename": "931s-yellow-plumage", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 8, - "w": 20, - "h": 20 - }, - "frame": { - "x": 187, - "y": 420, - "w": 20, - "h": 20 - } - }, - { - "filename": "997s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 9, - "w": 21, - "h": 19 - }, - "frame": { - "x": 136, - "y": 440, - "w": 21, - "h": 19 - } - }, - { - "filename": "1015", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 7, - "w": 17, - "h": 21 - }, - "frame": { - "x": 136, - "y": 459, - "w": 17, - "h": 21 - } - }, - { - "filename": "932", + "filename": "906", "rotated": false, "trimmed": true, "sourceSize": { @@ -4959,222 +4413,12 @@ "h": 19 }, "frame": { - "x": 157, - "y": 440, + "x": 215, + "y": 452, "w": 18, "h": 19 } }, - { - "filename": "1015s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 7, - "w": 17, - "h": 21 - }, - "frame": { - "x": 153, - "y": 459, - "w": 17, - "h": 21 - } - }, - { - "filename": "932s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 9, - "w": 18, - "h": 19 - }, - "frame": { - "x": 175, - "y": 440, - "w": 18, - "h": 19 - } - }, - { - "filename": "958", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 8, - "w": 17, - "h": 20 - }, - "frame": { - "x": 170, - "y": 459, - "w": 17, - "h": 20 - } - }, - { - "filename": "922", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 9, - "w": 17, - "h": 19 - }, - "frame": { - "x": 193, - "y": 440, - "w": 17, - "h": 19 - } - }, - { - "filename": "958s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 8, - "w": 17, - "h": 20 - }, - "frame": { - "x": 187, - "y": 459, - "w": 17, - "h": 20 - } - }, - { - "filename": "951", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 8, - "w": 16, - "h": 20 - }, - "frame": { - "x": 207, - "y": 420, - "w": 16, - "h": 20 - } - }, - { - "filename": "922s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 9, - "w": 17, - "h": 19 - }, - "frame": { - "x": 210, - "y": 440, - "w": 17, - "h": 19 - } - }, - { - "filename": "963", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 13, - "w": 22, - "h": 15 - }, - "frame": { - "x": 204, - "y": 459, - "w": 22, - "h": 15 - } - }, - { - "filename": "999-roaming", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 10, - "w": 14, - "h": 18 - }, - "frame": { - "x": 223, - "y": 417, - "w": 14, - "h": 18 - } - }, - { - "filename": "996s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 12, - "w": 15, - "h": 16 - }, - "frame": { - "x": 237, - "y": 419, - "w": 15, - "h": 16 - } - }, { "filename": "963s", "rotated": false, @@ -5190,8 +4434,8 @@ "h": 15 }, "frame": { - "x": 227, - "y": 435, + "x": 233, + "y": 448, "w": 22, "h": 15 } @@ -5211,12 +4455,33 @@ "h": 15 }, "frame": { - "x": 227, - "y": 450, + "x": 233, + "y": 463, "w": 22, "h": 15 } }, + { + "filename": "1013s-masterpiece", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 4, + "w": 20, + "h": 24 + }, + "frame": { + "x": 213, + "y": 471, + "w": 20, + "h": 24 + } + }, { "filename": "964s-zero", "rotated": false, @@ -5232,8 +4497,8 @@ "h": 15 }, "frame": { - "x": 226, - "y": 465, + "x": 233, + "y": 478, "w": 22, "h": 15 } @@ -5253,12 +4518,33 @@ "h": 15 }, "frame": { - "x": 204, - "y": 474, + "x": 233, + "y": 493, "w": 22, "h": 15 } }, + { + "filename": "931-white-plumage", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 20, + "h": 20 + }, + "frame": { + "x": 213, + "y": 495, + "w": 20, + "h": 20 + } + }, { "filename": "974s", "rotated": false, @@ -5274,12 +4560,390 @@ "h": 15 }, "frame": { - "x": 226, - "y": 480, + "x": 233, + "y": 508, "w": 22, "h": 15 } }, + { + "filename": "1013s-unremarkable", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 4, + "w": 20, + "h": 24 + }, + "frame": { + "x": 54, + "y": 493, + "w": 20, + "h": 24 + } + }, + { + "filename": "986s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 5, + "w": 23, + "h": 23 + }, + "frame": { + "x": 54, + "y": 517, + "w": 23, + "h": 23 + } + }, + { + "filename": "941", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 21, + "h": 21 + }, + "frame": { + "x": 54, + "y": 540, + "w": 21, + "h": 21 + } + }, + { + "filename": "973s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 2, + "w": 15, + "h": 26 + }, + "frame": { + "x": 74, + "y": 491, + "w": 15, + "h": 26 + } + }, + { + "filename": "939s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 20, + "h": 21 + }, + "frame": { + "x": 55, + "y": 561, + "w": 20, + "h": 21 + } + }, + { + "filename": "913s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 5, + "w": 17, + "h": 23 + }, + "frame": { + "x": 77, + "y": 517, + "w": 17, + "h": 23 + } + }, + { + "filename": "1011", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 5, + "w": 19, + "h": 22 + }, + "frame": { + "x": 75, + "y": 540, + "w": 19, + "h": 22 + } + }, + { + "filename": "931-yellow-plumage", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 20, + "h": 20 + }, + "frame": { + "x": 75, + "y": 562, + "w": 20, + "h": 20 + } + }, + { + "filename": "941s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 21, + "h": 21 + }, + "frame": { + "x": 56, + "y": 582, + "w": 21, + "h": 21 + } + }, + { + "filename": "971", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 20, + "h": 21 + }, + "frame": { + "x": 56, + "y": 603, + "w": 20, + "h": 21 + } + }, + { + "filename": "1011s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 5, + "w": 19, + "h": 22 + }, + "frame": { + "x": 56, + "y": 624, + "w": 19, + "h": 22 + } + }, + { + "filename": "929", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 6, + "w": 17, + "h": 22 + }, + "frame": { + "x": 75, + "y": 624, + "w": 17, + "h": 22 + } + }, + { + "filename": "971s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 20, + "h": 21 + }, + "frame": { + "x": 77, + "y": 582, + "w": 20, + "h": 21 + } + }, + { + "filename": "1015", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 17, + "h": 21 + }, + "frame": { + "x": 76, + "y": 603, + "w": 17, + "h": 21 + } + }, + { + "filename": "929s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 6, + "w": 17, + "h": 22 + }, + "frame": { + "x": 92, + "y": 624, + "w": 17, + "h": 22 + } + }, + { + "filename": "1015s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 17, + "h": 21 + }, + "frame": { + "x": 93, + "y": 603, + "w": 17, + "h": 21 + } + }, + { + "filename": "935", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 13, + "h": 21 + }, + "frame": { + "x": 89, + "y": 491, + "w": 13, + "h": 21 + } + }, + { + "filename": "1004", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 8, + "w": 21, + "h": 20 + }, + "frame": { + "x": 102, + "y": 490, + "w": 21, + "h": 20 + } + }, { "filename": "980", "rotated": false, @@ -5295,12 +4959,33 @@ "h": 15 }, "frame": { - "x": 137, - "y": 480, + "x": 123, + "y": 490, "w": 22, "h": 15 } }, + { + "filename": "931s-blue-plumage", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 20, + "h": 20 + }, + "frame": { + "x": 145, + "y": 500, + "w": 20, + "h": 20 + } + }, { "filename": "980s", "rotated": false, @@ -5316,12 +5001,285 @@ "h": 15 }, "frame": { - "x": 137, - "y": 495, + "x": 123, + "y": 505, "w": 22, "h": 15 } }, + { + "filename": "997s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 9, + "w": 21, + "h": 19 + }, + "frame": { + "x": 165, + "y": 508, + "w": 21, + "h": 19 + } + }, + { + "filename": "1004s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 8, + "w": 21, + "h": 20 + }, + "frame": { + "x": 186, + "y": 508, + "w": 21, + "h": 20 + } + }, + { + "filename": "931s-green-plumage", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 20, + "h": 20 + }, + "frame": { + "x": 207, + "y": 515, + "w": 20, + "h": 20 + } + }, + { + "filename": "931s-white-plumage", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 20, + "h": 20 + }, + "frame": { + "x": 227, + "y": 523, + "w": 20, + "h": 20 + } + }, + { + "filename": "935s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 13, + "h": 21 + }, + "frame": { + "x": 94, + "y": 512, + "w": 13, + "h": 21 + } + }, + { + "filename": "951s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 8, + "w": 16, + "h": 20 + }, + "frame": { + "x": 107, + "y": 510, + "w": 16, + "h": 20 + } + }, + { + "filename": "931s-yellow-plumage", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 20, + "h": 20 + }, + "frame": { + "x": 94, + "y": 533, + "w": 20, + "h": 20 + } + }, + { + "filename": "967", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 9, + "w": 20, + "h": 19 + }, + "frame": { + "x": 123, + "y": 520, + "w": 20, + "h": 19 + } + }, + { + "filename": "967s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 9, + "w": 20, + "h": 19 + }, + "frame": { + "x": 143, + "y": 520, + "w": 20, + "h": 19 + } + }, + { + "filename": "970", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 12, + "w": 20, + "h": 16 + }, + "frame": { + "x": 163, + "y": 527, + "w": 20, + "h": 16 + } + }, + { + "filename": "970s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 12, + "w": 20, + "h": 16 + }, + "frame": { + "x": 183, + "y": 528, + "w": 20, + "h": 16 + } + }, + { + "filename": "906s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 9, + "w": 18, + "h": 19 + }, + "frame": { + "x": 95, + "y": 553, + "w": 18, + "h": 19 + } + }, + { + "filename": "958", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 8, + "w": 17, + "h": 20 + }, + "frame": { + "x": 97, + "y": 572, + "w": 17, + "h": 20 + } + }, { "filename": "915", "rotated": false, @@ -5337,8 +5295,8 @@ "h": 17 }, "frame": { - "x": 136, - "y": 510, + "x": 114, + "y": 539, "w": 19, "h": 17 } @@ -5358,96 +5316,12 @@ "h": 17 }, "frame": { - "x": 136, - "y": 527, + "x": 133, + "y": 539, "w": 19, "h": 17 } }, - { - "filename": "951s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 8, - "w": 16, - "h": 20 - }, - "frame": { - "x": 159, - "y": 480, - "w": 16, - "h": 20 - } - }, - { - "filename": "970", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 12, - "w": 20, - "h": 16 - }, - "frame": { - "x": 175, - "y": 479, - "w": 20, - "h": 16 - } - }, - { - "filename": "919", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 13, - "w": 16, - "h": 15 - }, - "frame": { - "x": 159, - "y": 500, - "w": 16, - "h": 15 - } - }, - { - "filename": "970s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 12, - "w": 20, - "h": 16 - }, - "frame": { - "x": 175, - "y": 495, - "w": 20, - "h": 16 - } - }, { "filename": "946", "rotated": false, @@ -5463,12 +5337,96 @@ "h": 16 }, "frame": { - "x": 155, - "y": 515, + "x": 113, + "y": 556, "w": 19, "h": 16 } }, + { + "filename": "909", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 9, + "w": 18, + "h": 19 + }, + "frame": { + "x": 114, + "y": 572, + "w": 18, + "h": 19 + } + }, + { + "filename": "909s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 9, + "w": 18, + "h": 19 + }, + "frame": { + "x": 132, + "y": 556, + "w": 18, + "h": 19 + } + }, + { + "filename": "932", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 9, + "w": 18, + "h": 19 + }, + "frame": { + "x": 132, + "y": 575, + "w": 18, + "h": 19 + } + }, + { + "filename": "938", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 14, + "y": 12, + "w": 11, + "h": 16 + }, + "frame": { + "x": 152, + "y": 539, + "w": 11, + "h": 16 + } + }, { "filename": "942", "rotated": false, @@ -5484,8 +5442,8 @@ "h": 15 }, "frame": { - "x": 155, - "y": 531, + "x": 163, + "y": 543, "w": 19, "h": 15 } @@ -5505,14 +5463,14 @@ "h": 15 }, "frame": { - "x": 136, + "x": 182, "y": 544, "w": 19, "h": 15 } }, { - "filename": "946s", + "filename": "932s", "rotated": false, "trimmed": true, "sourceSize": { @@ -5520,58 +5478,16 @@ "h": 30 }, "spriteSourceSize": { - "x": 10, - "y": 12, - "w": 19, - "h": 16 + "x": 11, + "y": 9, + "w": 18, + "h": 19 }, "frame": { - "x": 135, - "y": 559, - "w": 19, - "h": 16 - } - }, - { - "filename": "965", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 12, - "w": 19, - "h": 16 - }, - "frame": { - "x": 135, - "y": 575, - "w": 19, - "h": 16 - } - }, - { - "filename": "965s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 12, - "w": 19, - "h": 16 - }, - "frame": { - "x": 155, - "y": 546, - "w": 19, - "h": 16 + "x": 150, + "y": 558, + "w": 18, + "h": 19 } }, { @@ -5589,12 +5505,327 @@ "h": 17 }, "frame": { - "x": 154, - "y": 562, + "x": 150, + "y": 577, "w": 18, "h": 17 } }, + { + "filename": "999-roaming", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 10, + "w": 14, + "h": 18 + }, + "frame": { + "x": 168, + "y": 558, + "w": 14, + "h": 18 + } + }, + { + "filename": "946s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 12, + "w": 19, + "h": 16 + }, + "frame": { + "x": 182, + "y": 559, + "w": 19, + "h": 16 + } + }, + { + "filename": "912", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 9, + "w": 15, + "h": 19 + }, + "frame": { + "x": 168, + "y": 576, + "w": 15, + "h": 19 + } + }, + { + "filename": "958s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 8, + "w": 17, + "h": 20 + }, + "frame": { + "x": 183, + "y": 575, + "w": 17, + "h": 20 + } + }, + { + "filename": "965", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 12, + "w": 19, + "h": 16 + }, + "frame": { + "x": 203, + "y": 535, + "w": 19, + "h": 16 + } + }, + { + "filename": "922", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 9, + "w": 17, + "h": 19 + }, + "frame": { + "x": 201, + "y": 551, + "w": 17, + "h": 19 + } + }, + { + "filename": "912s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 9, + "w": 15, + "h": 19 + }, + "frame": { + "x": 218, + "y": 551, + "w": 15, + "h": 19 + } + }, + { + "filename": "922s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 9, + "w": 17, + "h": 19 + }, + "frame": { + "x": 233, + "y": 543, + "w": 17, + "h": 19 + } + }, + { + "filename": "926", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 13, + "w": 17, + "h": 15 + }, + "frame": { + "x": 233, + "y": 562, + "w": 17, + "h": 15 + } + }, + { + "filename": "965s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 12, + "w": 19, + "h": 16 + }, + "frame": { + "x": 201, + "y": 570, + "w": 19, + "h": 16 + } + }, + { + "filename": "928", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 10, + "w": 13, + "h": 18 + }, + "frame": { + "x": 220, + "y": 570, + "w": 13, + "h": 18 + } + }, + { + "filename": "926s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 13, + "w": 17, + "h": 15 + }, + "frame": { + "x": 233, + "y": 577, + "w": 17, + "h": 15 + } + }, + { + "filename": "969", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 16, + "w": 18, + "h": 12 + }, + "frame": { + "x": 200, + "y": 586, + "w": 18, + "h": 12 + } + }, + { + "filename": "960", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 10, + "w": 15, + "h": 18 + }, + "frame": { + "x": 218, + "y": 588, + "w": 15, + "h": 18 + } + }, + { + "filename": "940", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 14, + "w": 17, + "h": 14 + }, + "frame": { + "x": 233, + "y": 592, + "w": 17, + "h": 14 + } + }, { "filename": "969s", "rotated": false, @@ -5610,8 +5841,8 @@ "h": 12 }, "frame": { - "x": 154, - "y": 579, + "x": 114, + "y": 591, "w": 18, "h": 12 } @@ -5631,35 +5862,14 @@ "h": 17 }, "frame": { - "x": 195, - "y": 489, + "x": 110, + "y": 603, "w": 18, "h": 17 } }, { - "filename": "928", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 10, - "w": 13, - "h": 18 - }, - "frame": { - "x": 213, - "y": 489, - "w": 13, - "h": 18 - } - }, - { - "filename": "926", + "filename": "940s", "rotated": false, "trimmed": true, "sourceSize": { @@ -5668,57 +5878,15 @@ }, "spriteSourceSize": { "x": 11, - "y": 13, + "y": 14, "w": 17, - "h": 15 + "h": 14 }, "frame": { - "x": 226, - "y": 495, + "x": 132, + "y": 594, "w": 17, - "h": 15 - } - }, - { - "filename": "926s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 13, - "w": 17, - "h": 15 - }, - "frame": { - "x": 195, - "y": 506, - "w": 17, - "h": 15 - } - }, - { - "filename": "999s-roaming", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 10, - "w": 14, - "h": 18 - }, - "frame": { - "x": 212, - "y": 507, - "w": 14, - "h": 18 + "h": 14 } }, { @@ -5736,14 +5904,14 @@ "h": 16 }, "frame": { - "x": 226, - "y": 510, + "x": 149, + "y": 594, "w": 17, "h": 16 } }, { - "filename": "940", + "filename": "955s", "rotated": false, "trimmed": true, "sourceSize": { @@ -5752,15 +5920,15 @@ }, "spriteSourceSize": { "x": 11, - "y": 14, + "y": 12, "w": 17, - "h": 14 + "h": 16 }, "frame": { - "x": 175, - "y": 511, + "x": 166, + "y": 595, "w": 17, - "h": 14 + "h": 16 } }, { @@ -5778,12 +5946,54 @@ "h": 18 }, "frame": { - "x": 174, - "y": 525, + "x": 183, + "y": 595, "w": 16, "h": 18 } }, + { + "filename": "919", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 13, + "w": 16, + "h": 15 + }, + "frame": { + "x": 199, + "y": 598, + "w": 16, + "h": 15 + } + }, + { + "filename": "919s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 13, + "w": 16, + "h": 15 + }, + "frame": { + "x": 128, + "y": 608, + "w": 16, + "h": 15 + } + }, { "filename": "921s", "rotated": false, @@ -5799,12 +6009,54 @@ "h": 18 }, "frame": { - "x": 174, - "y": 543, + "x": 144, + "y": 610, "w": 16, "h": 18 } }, + { + "filename": "960s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 10, + "w": 15, + "h": 18 + }, + "frame": { + "x": 109, + "y": 628, + "w": 15, + "h": 18 + } + }, + { + "filename": "999s-roaming", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 10, + "w": 14, + "h": 18 + }, + "frame": { + "x": 124, + "y": 628, + "w": 14, + "h": 18 + } + }, { "filename": "928s", "rotated": false, @@ -5820,8 +6072,8 @@ "h": 18 }, "frame": { - "x": 172, - "y": 562, + "x": 138, + "y": 628, "w": 13, "h": 18 } @@ -5841,8 +6093,8 @@ "h": 17 }, "frame": { - "x": 185, - "y": 561, + "x": 160, + "y": 611, "w": 16, "h": 17 } @@ -5862,138 +6114,12 @@ "h": 17 }, "frame": { - "x": 190, - "y": 525, + "x": 151, + "y": 628, "w": 16, "h": 17 } }, - { - "filename": "955s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 12, - "w": 17, - "h": 16 - }, - "frame": { - "x": 190, - "y": 542, - "w": 17, - "h": 16 - } - }, - { - "filename": "919s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 13, - "w": 16, - "h": 15 - }, - "frame": { - "x": 206, - "y": 525, - "w": 16, - "h": 15 - } - }, - { - "filename": "944", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 13, - "w": 16, - "h": 15 - }, - "frame": { - "x": 207, - "y": 540, - "w": 16, - "h": 15 - } - }, - { - "filename": "940s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 14, - "w": 17, - "h": 14 - }, - "frame": { - "x": 222, - "y": 526, - "w": 17, - "h": 14 - } - }, - { - "filename": "944s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 13, - "w": 16, - "h": 15 - }, - "frame": { - "x": 223, - "y": 540, - "w": 16, - "h": 15 - } - }, - { - "filename": "938", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 14, - "y": 12, - "w": 11, - "h": 16 - }, - "frame": { - "x": 239, - "y": 526, - "w": 11, - "h": 16 - } - }, { "filename": "938s", "rotated": false, @@ -6009,12 +6135,96 @@ "h": 16 }, "frame": { - "x": 239, - "y": 542, + "x": 167, + "y": 628, "w": 11, "h": 16 } }, + { + "filename": "944", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 13, + "w": 16, + "h": 15 + }, + "frame": { + "x": 176, + "y": 613, + "w": 16, + "h": 15 + } + }, + { + "filename": "944s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 13, + "w": 16, + "h": 15 + }, + "frame": { + "x": 192, + "y": 613, + "w": 16, + "h": 15 + } + }, + { + "filename": "996", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 12, + "w": 15, + "h": 16 + }, + "frame": { + "x": 178, + "y": 628, + "w": 15, + "h": 16 + } + }, + { + "filename": "996s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 12, + "w": 15, + "h": 16 + }, + "frame": { + "x": 193, + "y": 628, + "w": 15, + "h": 16 + } + }, { "filename": "978-curly", "rotated": false, @@ -6030,8 +6240,8 @@ "h": 14 }, "frame": { - "x": 207, - "y": 555, + "x": 208, + "y": 613, "w": 15, "h": 14 } @@ -6051,8 +6261,8 @@ "h": 14 }, "frame": { - "x": 222, - "y": 555, + "x": 208, + "y": 627, "w": 15, "h": 14 } @@ -6072,8 +6282,8 @@ "h": 14 }, "frame": { - "x": 237, - "y": 558, + "x": 223, + "y": 606, "w": 15, "h": 14 } @@ -6093,8 +6303,8 @@ "h": 14 }, "frame": { - "x": 201, - "y": 569, + "x": 223, + "y": 620, "w": 15, "h": 14 } @@ -6114,8 +6324,8 @@ "h": 14 }, "frame": { - "x": 216, - "y": 569, + "x": 238, + "y": 606, "w": 15, "h": 14 } @@ -6135,8 +6345,8 @@ "h": 14 }, "frame": { - "x": 231, - "y": 572, + "x": 238, + "y": 620, "w": 15, "h": 14 } @@ -6147,6 +6357,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:d412b44b05c0ac7988fc321b8a4eb571:51dd93a83920102d7a1b879808f62790:6fb417eff82c0971c86b4818772ba292$" + "smartupdate": "$TexturePacker:SmartUpdate:b180859bc4c006d56ee5322ca73fa54e:212d282258f5086ad99b3b2b95f7ec1a:6fb417eff82c0971c86b4818772ba292$" } } diff --git a/public/images/pokemon_icons_9.png b/public/images/pokemon_icons_9.png index 6123a15cbe9..2985fb800d6 100644 Binary files a/public/images/pokemon_icons_9.png and b/public/images/pokemon_icons_9.png differ diff --git a/public/images/statuses_ca_ES.json b/public/images/statuses_ca_ES.json new file mode 100644 index 00000000000..be1b78e0e41 --- /dev/null +++ b/public/images/statuses_ca_ES.json @@ -0,0 +1,188 @@ +{ + "textures": [ + { + "image": "statuses_ca_ES.png", + "format": "RGBA8888", + "size": { + "w": 22, + "h": 64 + }, + "scale": 1, + "frames": [ + { + "filename": "pokerus", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 22, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + }, + "frame": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + } + }, + { + "filename": "burn", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 8, + "w": 20, + "h": 8 + } + }, + { + "filename": "faint", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 16, + "w": 20, + "h": 8 + } + }, + { + "filename": "freeze", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 24, + "w": 20, + "h": 8 + } + }, + { + "filename": "paralysis", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 32, + "w": 20, + "h": 8 + } + }, + { + "filename": "poison", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 40, + "w": 20, + "h": 8 + } + }, + { + "filename": "sleep", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 48, + "w": 20, + "h": 8 + } + }, + { + "filename": "toxic", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 56, + "w": 20, + "h": 8 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$" + } +} diff --git a/public/images/statuses_ca_ES.png b/public/images/statuses_ca_ES.png new file mode 100644 index 00000000000..d372b989be9 Binary files /dev/null and b/public/images/statuses_ca_ES.png differ diff --git a/public/images/statuses_de.json b/public/images/statuses_de.json new file mode 100644 index 00000000000..90840b8eeb1 --- /dev/null +++ b/public/images/statuses_de.json @@ -0,0 +1,188 @@ +{ + "textures": [ + { + "image": "statuses_de.png", + "format": "RGBA8888", + "size": { + "w": 22, + "h": 64 + }, + "scale": 1, + "frames": [ + { + "filename": "pokerus", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 22, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + }, + "frame": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + } + }, + { + "filename": "burn", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 8, + "w": 20, + "h": 8 + } + }, + { + "filename": "faint", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 16, + "w": 20, + "h": 8 + } + }, + { + "filename": "freeze", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 24, + "w": 20, + "h": 8 + } + }, + { + "filename": "paralysis", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 32, + "w": 20, + "h": 8 + } + }, + { + "filename": "poison", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 40, + "w": 20, + "h": 8 + } + }, + { + "filename": "sleep", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 48, + "w": 20, + "h": 8 + } + }, + { + "filename": "toxic", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 56, + "w": 20, + "h": 8 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$" + } +} diff --git a/public/images/statuses_de.png b/public/images/statuses_de.png new file mode 100644 index 00000000000..ab85384d591 Binary files /dev/null and b/public/images/statuses_de.png differ diff --git a/public/images/statuses_es.json b/public/images/statuses_es.json new file mode 100644 index 00000000000..4b44aa117e4 --- /dev/null +++ b/public/images/statuses_es.json @@ -0,0 +1,188 @@ +{ + "textures": [ + { + "image": "statuses_es.png", + "format": "RGBA8888", + "size": { + "w": 22, + "h": 64 + }, + "scale": 1, + "frames": [ + { + "filename": "pokerus", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 22, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + }, + "frame": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + } + }, + { + "filename": "burn", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 8, + "w": 20, + "h": 8 + } + }, + { + "filename": "faint", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 16, + "w": 20, + "h": 8 + } + }, + { + "filename": "freeze", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 24, + "w": 20, + "h": 8 + } + }, + { + "filename": "paralysis", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 32, + "w": 20, + "h": 8 + } + }, + { + "filename": "poison", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 40, + "w": 20, + "h": 8 + } + }, + { + "filename": "sleep", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 48, + "w": 20, + "h": 8 + } + }, + { + "filename": "toxic", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 56, + "w": 20, + "h": 8 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$" + } +} diff --git a/public/images/statuses_es.png b/public/images/statuses_es.png new file mode 100644 index 00000000000..d372b989be9 Binary files /dev/null and b/public/images/statuses_es.png differ diff --git a/public/images/statuses_fr.json b/public/images/statuses_fr.json new file mode 100644 index 00000000000..78f78a0856c --- /dev/null +++ b/public/images/statuses_fr.json @@ -0,0 +1,188 @@ +{ + "textures": [ + { + "image": "statuses_fr.png", + "format": "RGBA8888", + "size": { + "w": 22, + "h": 64 + }, + "scale": 1, + "frames": [ + { + "filename": "pokerus", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 22, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + }, + "frame": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + } + }, + { + "filename": "burn", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 8, + "w": 20, + "h": 8 + } + }, + { + "filename": "faint", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 16, + "w": 20, + "h": 8 + } + }, + { + "filename": "freeze", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 24, + "w": 20, + "h": 8 + } + }, + { + "filename": "paralysis", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 32, + "w": 20, + "h": 8 + } + }, + { + "filename": "poison", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 40, + "w": 20, + "h": 8 + } + }, + { + "filename": "sleep", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 48, + "w": 20, + "h": 8 + } + }, + { + "filename": "toxic", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 56, + "w": 20, + "h": 8 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$" + } +} diff --git a/public/images/statuses_fr.png b/public/images/statuses_fr.png new file mode 100644 index 00000000000..95989cd5d97 Binary files /dev/null and b/public/images/statuses_fr.png differ diff --git a/public/images/statuses_it.json b/public/images/statuses_it.json new file mode 100644 index 00000000000..76fc9ae8b4b --- /dev/null +++ b/public/images/statuses_it.json @@ -0,0 +1,188 @@ +{ + "textures": [ + { + "image": "statuses_it.png", + "format": "RGBA8888", + "size": { + "w": 22, + "h": 64 + }, + "scale": 1, + "frames": [ + { + "filename": "pokerus", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 22, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + }, + "frame": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + } + }, + { + "filename": "burn", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 8, + "w": 20, + "h": 8 + } + }, + { + "filename": "faint", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 16, + "w": 20, + "h": 8 + } + }, + { + "filename": "freeze", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 24, + "w": 20, + "h": 8 + } + }, + { + "filename": "paralysis", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 32, + "w": 20, + "h": 8 + } + }, + { + "filename": "poison", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 40, + "w": 20, + "h": 8 + } + }, + { + "filename": "sleep", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 48, + "w": 20, + "h": 8 + } + }, + { + "filename": "toxic", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 56, + "w": 20, + "h": 8 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$" + } +} diff --git a/public/images/statuses_it.png b/public/images/statuses_it.png new file mode 100644 index 00000000000..d372b989be9 Binary files /dev/null and b/public/images/statuses_it.png differ diff --git a/public/images/statuses_ja.json b/public/images/statuses_ja.json new file mode 100644 index 00000000000..8de633e8e43 --- /dev/null +++ b/public/images/statuses_ja.json @@ -0,0 +1,188 @@ +{ + "textures": [ + { + "image": "statuses_ja.png", + "format": "RGBA8888", + "size": { + "w": 22, + "h": 64 + }, + "scale": 1, + "frames": [ + { + "filename": "pokerus", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 22, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + }, + "frame": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + } + }, + { + "filename": "burn", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 8, + "w": 20, + "h": 8 + } + }, + { + "filename": "faint", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 16, + "w": 20, + "h": 8 + } + }, + { + "filename": "freeze", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 24, + "w": 20, + "h": 8 + } + }, + { + "filename": "paralysis", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 32, + "w": 20, + "h": 8 + } + }, + { + "filename": "poison", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 40, + "w": 20, + "h": 8 + } + }, + { + "filename": "sleep", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 48, + "w": 20, + "h": 8 + } + }, + { + "filename": "toxic", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 56, + "w": 20, + "h": 8 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$" + } +} diff --git a/public/images/statuses_ja.png b/public/images/statuses_ja.png new file mode 100644 index 00000000000..305fbe9168c Binary files /dev/null and b/public/images/statuses_ja.png differ diff --git a/public/images/statuses_ko.json b/public/images/statuses_ko.json new file mode 100644 index 00000000000..7e1e2dd6cda --- /dev/null +++ b/public/images/statuses_ko.json @@ -0,0 +1,188 @@ +{ + "textures": [ + { + "image": "statuses_ko.png", + "format": "RGBA8888", + "size": { + "w": 22, + "h": 64 + }, + "scale": 1, + "frames": [ + { + "filename": "pokerus", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 22, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + }, + "frame": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + } + }, + { + "filename": "burn", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 8, + "w": 20, + "h": 8 + } + }, + { + "filename": "faint", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 16, + "w": 20, + "h": 8 + } + }, + { + "filename": "freeze", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 24, + "w": 20, + "h": 8 + } + }, + { + "filename": "paralysis", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 32, + "w": 20, + "h": 8 + } + }, + { + "filename": "poison", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 40, + "w": 20, + "h": 8 + } + }, + { + "filename": "sleep", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 48, + "w": 20, + "h": 8 + } + }, + { + "filename": "toxic", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 56, + "w": 20, + "h": 8 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$" + } +} diff --git a/public/images/statuses_ko.png b/public/images/statuses_ko.png new file mode 100644 index 00000000000..d372b989be9 Binary files /dev/null and b/public/images/statuses_ko.png differ diff --git a/public/images/statuses_pt_BR.json b/public/images/statuses_pt_BR.json new file mode 100644 index 00000000000..b607991af8f --- /dev/null +++ b/public/images/statuses_pt_BR.json @@ -0,0 +1,188 @@ +{ + "textures": [ + { + "image": "statuses_pt_BR.png", + "format": "RGBA8888", + "size": { + "w": 22, + "h": 64 + }, + "scale": 1, + "frames": [ + { + "filename": "pokerus", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 22, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + }, + "frame": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + } + }, + { + "filename": "burn", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 8, + "w": 20, + "h": 8 + } + }, + { + "filename": "faint", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 16, + "w": 20, + "h": 8 + } + }, + { + "filename": "freeze", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 24, + "w": 20, + "h": 8 + } + }, + { + "filename": "paralysis", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 32, + "w": 20, + "h": 8 + } + }, + { + "filename": "poison", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 40, + "w": 20, + "h": 8 + } + }, + { + "filename": "sleep", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 48, + "w": 20, + "h": 8 + } + }, + { + "filename": "toxic", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 56, + "w": 20, + "h": 8 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$" + } +} diff --git a/public/images/statuses_pt_BR.png b/public/images/statuses_pt_BR.png new file mode 100644 index 00000000000..3073540e8a2 Binary files /dev/null and b/public/images/statuses_pt_BR.png differ diff --git a/public/images/statuses_zh_CN.json b/public/images/statuses_zh_CN.json new file mode 100644 index 00000000000..28760650ecd --- /dev/null +++ b/public/images/statuses_zh_CN.json @@ -0,0 +1,188 @@ +{ + "textures": [ + { + "image": "statuses_zh_CN.png", + "format": "RGBA8888", + "size": { + "w": 22, + "h": 64 + }, + "scale": 1, + "frames": [ + { + "filename": "pokerus", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 22, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + }, + "frame": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + } + }, + { + "filename": "burn", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 8, + "w": 20, + "h": 8 + } + }, + { + "filename": "faint", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 16, + "w": 20, + "h": 8 + } + }, + { + "filename": "freeze", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 24, + "w": 20, + "h": 8 + } + }, + { + "filename": "paralysis", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 32, + "w": 20, + "h": 8 + } + }, + { + "filename": "poison", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 40, + "w": 20, + "h": 8 + } + }, + { + "filename": "sleep", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 48, + "w": 20, + "h": 8 + } + }, + { + "filename": "toxic", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 56, + "w": 20, + "h": 8 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$" + } +} diff --git a/public/images/statuses_zh_CN.png b/public/images/statuses_zh_CN.png new file mode 100644 index 00000000000..d372b989be9 Binary files /dev/null and b/public/images/statuses_zh_CN.png differ diff --git a/public/images/statuses_zh_TW.json b/public/images/statuses_zh_TW.json new file mode 100644 index 00000000000..bf05b2ab0d5 --- /dev/null +++ b/public/images/statuses_zh_TW.json @@ -0,0 +1,188 @@ +{ + "textures": [ + { + "image": "statuses.png", + "format": "RGBA8888", + "size": { + "w": 22, + "h": 64 + }, + "scale": 1, + "frames": [ + { + "filename": "pokerus", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 22, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + }, + "frame": { + "x": 0, + "y": 0, + "w": 22, + "h": 8 + } + }, + { + "filename": "burn", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 8, + "w": 20, + "h": 8 + } + }, + { + "filename": "faint", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 16, + "w": 20, + "h": 8 + } + }, + { + "filename": "freeze", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 24, + "w": 20, + "h": 8 + } + }, + { + "filename": "paralysis", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 32, + "w": 20, + "h": 8 + } + }, + { + "filename": "poison", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 40, + "w": 20, + "h": 8 + } + }, + { + "filename": "sleep", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 48, + "w": 20, + "h": 8 + } + }, + { + "filename": "toxic", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 56, + "w": 20, + "h": 8 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$" + } +} diff --git a/public/images/statuses_zh_TW.png b/public/images/statuses_zh_TW.png new file mode 100644 index 00000000000..d372b989be9 Binary files /dev/null and b/public/images/statuses_zh_TW.png differ diff --git a/public/images/trainer/atticus.json b/public/images/trainer/atticus.json new file mode 100644 index 00000000000..95621998bf2 --- /dev/null +++ b/public/images/trainer/atticus.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "atticus.png", + "format": "RGBA8888", + "size": { + "w": 46, + "h": 46 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 21, + "y": 33, + "w": 43, + "h": 46 + }, + "frame": { + "x": 0, + "y": 0, + "w": 43, + "h": 46 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:6dcd7c3d3982793cbca0d6fcd1f9260e:19c44634629fadd9d039d23dc71ec987:d26ede35f15aa571d5a7a2dd2fb868e1$" + } +} diff --git a/public/images/trainer/atticus.png b/public/images/trainer/atticus.png new file mode 100644 index 00000000000..e3e7e870f2b Binary files /dev/null and b/public/images/trainer/atticus.png differ diff --git a/public/images/trainer/eri.json b/public/images/trainer/eri.json new file mode 100644 index 00000000000..fd4daf60437 --- /dev/null +++ b/public/images/trainer/eri.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "eri.png", + "format": "RGBA8888", + "size": { + "w": 74, + "h": 74 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 15, + "y": 5, + "w": 45, + "h": 74 + }, + "frame": { + "x": 0, + "y": 0, + "w": 45, + "h": 74 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:59594ac27e74ec85e2949d12ff680dc2:d65b6b00858ac47b26ef8393a8fa6795:d7f4cd3ff755f8074c14d3006b0c8301$" + } +} diff --git a/public/images/trainer/eri.png b/public/images/trainer/eri.png new file mode 100644 index 00000000000..0c9bdf7b47b Binary files /dev/null and b/public/images/trainer/eri.png differ diff --git a/public/images/trainer/expert_pokemon_breeder.json b/public/images/trainer/expert_pokemon_breeder.json new file mode 100644 index 00000000000..cd6ecffb267 --- /dev/null +++ b/public/images/trainer/expert_pokemon_breeder.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "expert_pokemon_breeder.png", + "format": "RGBA8888", + "size": { + "w": 39, + "h": 75 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 21, + "y": 3, + "w": 39, + "h": 75 + }, + "frame": { + "x": 0, + "y": 0, + "w": 39, + "h": 75 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:cb681265d8dca038a518ab14076fd140:18ff41b1ef6967682643a11695926e58:c59ea3971195f5a395b75223a77d9068$" + } +} diff --git a/public/images/trainer/expert_pokemon_breeder.png b/public/images/trainer/expert_pokemon_breeder.png new file mode 100644 index 00000000000..0625f5255c3 Binary files /dev/null and b/public/images/trainer/expert_pokemon_breeder.png differ diff --git a/public/images/trainer/giacomo.json b/public/images/trainer/giacomo.json new file mode 100644 index 00000000000..5eeb2cd685b --- /dev/null +++ b/public/images/trainer/giacomo.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "giacomo.png", + "format": "RGBA8888", + "size": { + "w": 75, + "h": 75 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 23, + "y": 4, + "w": 37, + "h": 75 + }, + "frame": { + "x": 0, + "y": 0, + "w": 37, + "h": 75 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:8c4e7da48e5667abc6d364330268c092:0fa43e58d8a746d3b86cb2dd763719f4:8603cc19e888c8c8de62177f4011577c$" + } +} diff --git a/public/images/trainer/giacomo.png b/public/images/trainer/giacomo.png new file mode 100644 index 00000000000..275f47fad3c Binary files /dev/null and b/public/images/trainer/giacomo.png differ diff --git a/public/images/trainer/mela.json b/public/images/trainer/mela.json new file mode 100644 index 00000000000..c9db18acc5a --- /dev/null +++ b/public/images/trainer/mela.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "mela.png", + "format": "RGBA8888", + "size": { + "w": 78, + "h": 78 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 18, + "y": 1, + "w": 46, + "h": 78 + }, + "frame": { + "x": 0, + "y": 0, + "w": 46, + "h": 78 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:e26d8c926c54c848cef673b3f59f35e7:ff040c2cebb1a92d2ef61dc91c018390:68668cf06383ff459cccaafb6bf56215$" + } +} diff --git a/public/images/trainer/mela.png b/public/images/trainer/mela.png new file mode 100644 index 00000000000..fbb08ed69cf Binary files /dev/null and b/public/images/trainer/mela.png differ diff --git a/public/images/trainer/ortega.json b/public/images/trainer/ortega.json new file mode 100644 index 00000000000..53bab5dba40 --- /dev/null +++ b/public/images/trainer/ortega.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "ortega.png", + "format": "RGBA8888", + "size": { + "w": 69, + "h": 69 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 8, + "y": 10, + "w": 53, + "h": 69 + }, + "frame": { + "x": 0, + "y": 0, + "w": 53, + "h": 69 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:c6ff92d90ed884222095de81d1db9166:a91cf3c83a063f549c52afb42f7ba3b0:c3f9fcec121c8bc93f2b230b20b79c57$" + } +} diff --git a/public/images/trainer/ortega.png b/public/images/trainer/ortega.png new file mode 100644 index 00000000000..7f694c6ded6 Binary files /dev/null and b/public/images/trainer/ortega.png differ diff --git a/public/images/trainer/penny.json b/public/images/trainer/penny.json new file mode 100644 index 00000000000..da64efffa3b --- /dev/null +++ b/public/images/trainer/penny.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "penny.png", + "format": "RGBA8888", + "size": { + "w": 75, + "h": 75 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 24, + "y": 4, + "w": 34, + "h": 75 + }, + "frame": { + "x": 0, + "y": 0, + "w": 34, + "h": 75 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:54f184bf1995a94a78aff33c9a851e6b:a6c9b3fe428b0cd0344b5cf14b999f36:cf221da9747cb8cb356053d3042d8d22$" + } +} diff --git a/public/images/trainer/penny.png b/public/images/trainer/penny.png new file mode 100644 index 00000000000..0e36760e21b Binary files /dev/null and b/public/images/trainer/penny.png differ diff --git a/public/images/trainer/star_grunt_f.json b/public/images/trainer/star_grunt_f.json new file mode 100644 index 00000000000..e26477e3512 --- /dev/null +++ b/public/images/trainer/star_grunt_f.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "star_grunt_f.png", + "format": "RGBA8888", + "size": { + "w": 68, + "h": 68 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 24, + "y": 11, + "w": 30, + "h": 68 + }, + "frame": { + "x": 0, + "y": 0, + "w": 30, + "h": 68 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:b542a1bdd6995584fc776f75d578b434:f03fddece4494ab59698002fe6671972:c6f0e54e24ec5ffaa711700431b1955e$" + } +} diff --git a/public/images/trainer/star_grunt_f.png b/public/images/trainer/star_grunt_f.png new file mode 100644 index 00000000000..6eb63ae1e03 Binary files /dev/null and b/public/images/trainer/star_grunt_f.png differ diff --git a/public/images/trainer/star_grunt_m.json b/public/images/trainer/star_grunt_m.json new file mode 100644 index 00000000000..bf49e3027e6 --- /dev/null +++ b/public/images/trainer/star_grunt_m.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "star_grunt_m.png", + "format": "RGBA8888", + "size": { + "w": 70, + "h": 70 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 24, + "y": 9, + "w": 31, + "h": 70 + }, + "frame": { + "x": 0, + "y": 0, + "w": 31, + "h": 70 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:abc4b0424c37fd55a2bf2e9f5142adce:41a140aa68a1eda61d9a00cab4e07721:a0796711f9e0333796b6629cd43ff8e8$" + } +} diff --git a/public/images/trainer/star_grunt_m.png b/public/images/trainer/star_grunt_m.png new file mode 100644 index 00000000000..a69359eda8e Binary files /dev/null and b/public/images/trainer/star_grunt_m.png differ diff --git a/src/battle-scene.ts b/src/battle-scene.ts index f4df595e988..08fdd0531c2 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -105,6 +105,7 @@ import HeldModifierConfig from "#app/interfaces/held-modifier-config"; import { ExpPhase } from "#app/phases/exp-phase"; import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +import { ExpGainsSpeed } from "./enums/exp-gains-speed"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; @@ -180,7 +181,7 @@ export default class BattleScene extends SceneBase { public experimentalSprites: boolean = false; public musicPreference: integer = 0; public moveAnimations: boolean = true; - public expGainsSpeed: integer = 0; + public expGainsSpeed: ExpGainsSpeed = ExpGainsSpeed.DEFAULT; public skipSeenDialogues: boolean = false; /** * Determines if the egg hatching animation should be skipped @@ -781,6 +782,14 @@ export default class BattleScene extends SceneBase { return this.getPlayerField().find(p => p.isActive()); } + /** + * Finds the first {@linkcode Pokemon.isActive() | active PlayerPokemon} that isn't also currently switching out + * @returns Either the first {@linkcode PlayerPokemon} satisfying, or undefined if no player pokemon on the field satisfy + */ + getNonSwitchedPlayerPokemon(): PlayerPokemon | undefined { + return this.getPlayerField().find(p => p.isActive() && p.switchOutStatus === false); + } + /** * Returns an array of PlayerPokemon of length 1 or 2 depending on if double battles or not * @returns array of {@linkcode PlayerPokemon} @@ -798,6 +807,14 @@ export default class BattleScene extends SceneBase { return this.getEnemyField().find(p => p.isActive()); } + /** + * Finds the first {@linkcode Pokemon.isActive() | active EnemyPokemon} pokemon from the enemy that isn't also currently switching out + * @returns Either the first {@linkcode EnemyPokemon} satisfying, or undefined if no player pokemon on the field satisfy + */ + getNonSwitchedEnemyPokemon(): EnemyPokemon | undefined { + return this.getEnemyField().find(p => p.isActive() && p.switchOutStatus === false); + } + /** * Returns an array of EnemyPokemon of length 1 or 2 depending on if double battles or not * @returns array of {@linkcode EnemyPokemon} @@ -1914,6 +1931,19 @@ export default class BattleScene extends SceneBase { return false; } + /** + * Fades out current track for `delay` ms, then fades in new track. + * @param newBgmKey + * @param destroy + * @param delay + */ + fadeAndSwitchBgm(newBgmKey: string, destroy: boolean = false, delay: number = 2000) { + this.fadeOutBgm(delay, destroy); + this.time.delayedCall(delay, () => { + this.playBgm(newBgmKey); + }); + } + playSound(sound: string | AnySound, config?: object): AnySound { const key = typeof sound === "string" ? sound : sound.key; config = config ?? {}; @@ -2138,12 +2168,16 @@ export default class BattleScene extends SceneBase { return 20.87; case "battle_macro_grunt": // SWSH Trainer Battle return 11.56; + case "battle_star_grunt": //SV Team Star Battle + return 133.362; case "battle_galactic_admin": //BDSP Team Galactic Admin Battle return 11.997; case "battle_skull_admin": //SM Team Skull Admin Battle return 15.463; case "battle_oleana": //SWSH Oleana Battle return 14.110; + case "battle_star_admin": //SV Team Star Boss Battle + return 9.493; case "battle_rocket_boss": //USUM Giovanni Battle return 9.115; case "battle_aqua_magma_boss": //ORAS Archie & Maxie Battle @@ -2160,6 +2194,18 @@ export default class BattleScene extends SceneBase { return 13.13; case "battle_macro_boss": //SWSH Rose Battle return 11.42; + case "battle_star_boss": //SV Cassiopeia Battle + return 25.764; + case "mystery_encounter_gen_5_gts": // BW GTS + return 8.52; + case "mystery_encounter_gen_6_gts": // XY GTS + return 9.24; + case "mystery_encounter_fun_and_games": // EoS Guildmaster Wigglytuff + return 4.78; + case "mystery_encounter_weird_dream": // EoS Temporal Spire + return 41.42; + case "mystery_encounter_delibirdy": // Firel Delibirdy + return 82.28; } return 0; @@ -2606,7 +2652,7 @@ export default class BattleScene extends SceneBase { } party.forEach((enemyPokemon: EnemyPokemon, i: integer) => { - if (heldModifiersConfigs && i < heldModifiersConfigs.length && heldModifiersConfigs[i] && heldModifiersConfigs[i].length > 0) { + if (heldModifiersConfigs && i < heldModifiersConfigs.length && heldModifiersConfigs[i]) { heldModifiersConfigs[i].forEach(mt => { let modifier: PokemonHeldItemModifier; if (mt.modifier instanceof PokemonHeldItemModifierType) { @@ -2617,8 +2663,7 @@ export default class BattleScene extends SceneBase { } const stackCount = mt.stackCount ?? 1; modifier.stackCount = stackCount; - // TODO: set isTransferable - // modifier.isTransferrable = mt.isTransferable ?? true; + modifier.isTransferable = mt.isTransferable ?? modifier.isTransferable; this.addEnemyModifier(modifier, true); }); } else { diff --git a/src/battle.ts b/src/battle.ts index a886a0eb771..d99e1a91c15 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -14,8 +14,16 @@ import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; import i18next from "#app/plugins/i18n"; -import MysteryEncounter from "./data/mystery-encounters/mystery-encounter"; +import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +import { CustomModifierSettings } from "#app/modifier/modifier-type"; +import { ModifierTier } from "#app/modifier/modifier-tier"; + +export enum ClassicFixedBossWaves { + // TODO: other fixed wave battles should be added here + EVIL_BOSS_1 = 115, + EVIL_BOSS_2 = 165, +} export enum BattleType { WILD, @@ -157,7 +165,7 @@ export default class Battle { } addPostBattleLoot(enemyPokemon: EnemyPokemon): void { - this.postBattleLoot.push(...enemyPokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferrable, false).map(i => { + this.postBattleLoot.push(...enemyPokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferable, false).map(i => { const ret = i as PokemonHeldItemModifier; //@ts-ignore - this is awful to fix/change ret.pokemonId = null; @@ -419,6 +427,7 @@ export class FixedBattleConfig { public getTrainer: GetTrainerFunc; public getEnemyParty: GetEnemyPartyFunc; public seedOffsetWaveIndex: number; + public customModifierRewardSettings?: CustomModifierSettings; setBattleType(battleType: BattleType): FixedBattleConfig { this.battleType = battleType; @@ -444,6 +453,11 @@ export class FixedBattleConfig { this.seedOffsetWaveIndex = seedOffsetWaveIndex; return this; } + + setCustomModifierRewards(customModifierRewardSettings: CustomModifierSettings) { + this.customModifierRewardSettings = customModifierRewardSettings; + return this; + } } @@ -503,29 +517,35 @@ export const classicFixedBattles: FixedBattleConfigs = { [8]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), [25]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) - .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_2, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), + .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_2, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) + .setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], allowLuckUpgrades: false }), [35]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) - .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT ], true)), + .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)), [55]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) - .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_3, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), + .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_3, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) + .setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], allowLuckUpgrades: false }), [62]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) - .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT ], true)), + .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)), [64]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) - .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT ], true)), + .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)), [66]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) - .setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA ], true)), + .setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA, [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ] ], true)), [95]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) - .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_4, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), + .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_4, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) + .setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }), [112]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) - .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT ], true)), + .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)), [114]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) - .setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA ], true, 1)), - [115]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) - .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.MAXIE, TrainerType.ARCHIE, TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE, TrainerType.LUSAMINE, TrainerType.GUZMA, TrainerType.ROSE ])), + .setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA, [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ] ], true, 1)), + [ClassicFixedBossWaves.EVIL_BOSS_1]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) + .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.MAXIE, TrainerType.ARCHIE, TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE, TrainerType.LUSAMINE, TrainerType.GUZMA, TrainerType.ROSE, TrainerType.PENNY ])) + .setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }), [145]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) - .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_5, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), - [165]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) - .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_2, TrainerType.MAXIE_2, TrainerType.ARCHIE_2, TrainerType.CYRUS_2, TrainerType.GHETSIS_2, TrainerType.LYSANDRE_2, TrainerType.LUSAMINE_2, TrainerType.GUZMA_2, TrainerType.ROSE_2 ])), + .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_5, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) + .setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }), + [ClassicFixedBossWaves.EVIL_BOSS_2]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) + .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_2, TrainerType.MAXIE_2, TrainerType.ARCHIE_2, TrainerType.CYRUS_2, TrainerType.GHETSIS_2, TrainerType.LYSANDRE_2, TrainerType.LUSAMINE_2, TrainerType.GUZMA_2, TrainerType.ROSE_2, TrainerType.PENNY_2 ])) + .setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }), [182]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.LORELEI, TrainerType.WILL, TrainerType.SIDNEY, TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, [ TrainerType.HALA, TrainerType.MOLAYNE ], TrainerType.MARNIE_ELITE, TrainerType.RIKA, TrainerType.CRISPIN ])), [184]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(182) @@ -538,4 +558,5 @@ export const classicFixedBattles: FixedBattleConfigs = { .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.BLUE, [ TrainerType.RED, TrainerType.LANCE_CHAMPION ], [ TrainerType.STEVEN, TrainerType.WALLACE ], TrainerType.CYNTHIA, [ TrainerType.ALDER, TrainerType.IRIS ], TrainerType.DIANTHA, TrainerType.HAU, TrainerType.LEON, [ TrainerType.GEETA, TrainerType.NEMONA ], TrainerType.KIERAN ])), [195]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_6, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) + .setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], allowLuckUpgrades: false }) }; diff --git a/src/constants.ts b/src/constants.ts index a2f7e47b996..0b1261ad814 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1 +1,5 @@ -export const PLAYER_PARTY_MAX_SIZE = 6; +/** The maximum size of the player's party */ +export const PLAYER_PARTY_MAX_SIZE: number = 6; + +/** Whether to use seasonal splash messages in general */ +export const USE_SEASONAL_SPLASH_MESSAGES: boolean = false; diff --git a/src/data/ability.ts b/src/data/ability.ts index fbec5a1e177..28784c07134 100755 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1099,7 +1099,7 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { 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 (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) { if (simulated) { return true; } @@ -1683,7 +1683,7 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { return new Promise(resolve => { if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.stealCondition || this.stealCondition(pokemon, defender, move))) { - const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferrable); + const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferable); if (heldItems.length) { const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false).then(success => { @@ -1776,7 +1776,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { return new Promise(resolve => { if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) { - const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferrable); + const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferable); if (heldItems.length) { const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false).then(success => { @@ -2638,7 +2638,11 @@ export class PreStatStageChangeAbAttr extends AbAttr { } } +/** + * Protect one or all {@linkcode BattleStat} from reductions caused by other Pokémon's moves and Abilities + */ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr { + /** {@linkcode BattleStat} to protect or `undefined` if **all** {@linkcode BattleStat} are protected */ private protectedStat?: BattleStat; constructor(protectedStat?: BattleStat) { @@ -2647,7 +2651,17 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr { this.protectedStat = protectedStat; } - applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, _args: any[]): boolean { + /** + * Apply the {@linkcode ProtectedStatAbAttr} to an interaction + * @param _pokemon + * @param _passive + * @param simulated + * @param stat the {@linkcode BattleStat} being affected + * @param cancelled The {@linkcode Utils.BooleanHolder} that will be set to true if the stat is protected + * @param _args + * @returns true if the stat is protected, false otherwise + */ + 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; @@ -3770,7 +3784,7 @@ export class StatStageChangeMultiplierAbAttr extends AbAttr { this.multiplier = multiplier; } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.IntegerHolder).value *= this.multiplier; return true; @@ -4830,11 +4844,9 @@ export function initAbilities() { .bypassFaint(), new Ability(Abilities.VOLT_ABSORB, 3) .attr(TypeImmunityHealAbAttr, Type.ELECTRIC) - .partial() // Healing not blocked by Heal Block .ignorable(), new Ability(Abilities.WATER_ABSORB, 3) .attr(TypeImmunityHealAbAttr, Type.WATER) - .partial() // Healing not blocked by Heal Block .ignorable(), new Ability(Abilities.OBLIVIOUS, 3) .attr(BattlerTagImmunityAbAttr, BattlerTagType.INFATUATED) @@ -4947,8 +4959,7 @@ export function initAbilities() { .attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.SOUND_BASED)) .ignorable(), new Ability(Abilities.RAIN_DISH, 3) - .attr(PostWeatherLapseHealAbAttr, 1, WeatherType.RAIN, WeatherType.HEAVY_RAIN) - .partial(), // Healing not blocked by Heal Block + .attr(PostWeatherLapseHealAbAttr, 1, WeatherType.RAIN, WeatherType.HEAVY_RAIN), new Ability(Abilities.SAND_STREAM, 3) .attr(PostSummonWeatherChangeAbAttr, WeatherType.SANDSTORM) .attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.SANDSTORM), @@ -5079,7 +5090,6 @@ export function initAbilities() { .attr(PostWeatherLapseHealAbAttr, 2, WeatherType.RAIN, WeatherType.HEAVY_RAIN) .attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 1.25) .attr(TypeImmunityHealAbAttr, Type.WATER) - .partial() // Healing not blocked by Heal Block .ignorable(), new Ability(Abilities.DOWNLOAD, 4) .attr(DownloadAbAttr), @@ -5160,8 +5170,7 @@ export function initAbilities() { .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 + .attr(PostWeatherLapseHealAbAttr, 1, WeatherType.HAIL, WeatherType.SNOW), new Ability(Abilities.SOLID_ROCK, 4) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75) .ignorable(), @@ -5331,8 +5340,7 @@ export function initAbilities() { .ignorable() .unimplemented(), new Ability(Abilities.CHEEK_POUCH, 6) - .attr(HealFromBerryUseAbAttr, 1/3) - .partial(), // Healing not blocked by Heal Block + .attr(HealFromBerryUseAbAttr, 1/3), new Ability(Abilities.PROTEAN, 6) .attr(PokemonTypeChangeAbAttr), //.condition((p) => !p.summonData?.abilitiesApplied.includes(Abilities.PROTEAN)), //Gen 9 Implementation diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 64f7d85cae3..d7b995f748f 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -428,7 +428,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent { moveAnim.bgSprite.setScale(1.25); moveAnim.bgSprite.setAlpha(this.opacity / 255); scene.field.add(moveAnim.bgSprite); - const fieldPokemon = scene.getEnemyPokemon() || scene.getPlayerPokemon(); + const fieldPokemon = scene.getNonSwitchedEnemyPokemon() || scene.getNonSwitchedPlayerPokemon(); if (!isNullOrUndefined(priority)) { scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority!); } else if (fieldPokemon?.isOnField()) { @@ -488,14 +488,14 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise { } else { moveAnims.set(move, null); const defaultMoveAnim = allMoves[move] instanceof AttackMove ? Moves.TACKLE : allMoves[move] instanceof SelfStatusMove ? Moves.FOCUS_ENERGY : Moves.TAIL_WHIP; - const moveName = Moves[move].toLowerCase().replace(/\_/g, "-"); + const fetchAnimAndResolve = (move: Moves) => { - scene.cachedFetch(`./battle-anims/${moveName}.json`) + scene.cachedFetch(`./battle-anims/${Utils.animationFileName(move)}.json`) .then(response => { const contentType = response.headers.get("content-type"); if (!response.ok || contentType?.indexOf("application/json") === -1) { - console.error(`Could not load animation file for move '${moveName}'`, response.status, response.statusText); - populateMoveAnim(move, moveAnims.get(defaultMoveAnim)); + useDefaultAnim(move, defaultMoveAnim); + logMissingMoveAnim(move, response.status, response.statusText); return resolve(); } return response.json(); @@ -515,6 +515,11 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise { } else { resolve(); } + }) + .catch(error => { + useDefaultAnim(move, defaultMoveAnim); + logMissingMoveAnim(move, error); + return resolve(); }); }; fetchAnimAndResolve(move); @@ -522,6 +527,29 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise { }); } +/** + * Populates the default animation for the given move. + * + * @param move the move to populate an animation for + * @param defaultMoveAnim the move to use as the default animation + */ +function useDefaultAnim(move: Moves, defaultMoveAnim: Moves) { + populateMoveAnim(move, moveAnims.get(defaultMoveAnim)); +} + +/** + * Helper method for printing a warning to the console when a move animation is missing. + * + * @param move the move to populate an animation for + * @param optionalParams parameters to add to the error logging + * + * @remarks use {@linkcode useDefaultAnim} to use a default animation + */ +function logMissingMoveAnim(move: Moves, ...optionalParams: any[]) { + const moveName = Utils.animationFileName(move); + console.warn(`Could not load animation file for move '${moveName}'`, ...optionalParams); +} + /** * Fetches animation configs to be used in a Mystery Encounter * @param scene @@ -910,7 +938,7 @@ export abstract class BattleAnim { const sprites = spriteCache[isUser ? AnimFrameTarget.USER : AnimFrameTarget.TARGET]; const spriteSource = isUser ? userSprite : targetSprite; if ((isUser ? u : t) === sprites.length) { - if (!isUser && !!targetSubstitute) { + if (isUser || !targetSubstitute) { const sprite = scene.addPokemonSprite(isUser ? user! : target, 0, 0, spriteSource!.texture, spriteSource!.frame.name, true); // TODO: are those bangs correct? [ "spriteColors", "fusionSpriteColors" ].map(k => sprite.pipelineData[k] = (isUser ? user! : target).getSprite().pipelineData[k]); // TODO: are those bangs correct? sprite.setPipelineData("spriteKey", (isUser ? user! : target).getBattleSpriteKey()); @@ -961,7 +989,7 @@ export abstract class BattleAnim { const setSpritePriority = (priority: integer) => { switch (priority) { case 0: - scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, scene.getEnemyPokemon() || scene.getPlayerPokemon()!); // TODO: is this bang correct? + scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, scene.getNonSwitchedEnemyPokemon() || scene.getNonSwitchedPlayerPokemon()!); // This bang assumes that if (the EnemyPokemon is undefined, then the PlayerPokemon function must return an object), correct assumption? break; case 1: scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 965da844121..3be6562307b 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -3,7 +3,7 @@ import { getPokemonNameWithAffix } from "../messages"; import Pokemon, { MoveResult, HitResult } from "../field/pokemon"; import { StatusEffect } from "./status-effect"; import * as Utils from "../utils"; -import { ChargeAttr, MoveFlags, allMoves } from "./move"; +import { ChargeAttr, MoveFlags, allMoves, MoveCategory, applyMoveAttrs, StatusCategoryOnAllyAttr, HealOnAllyAttr } from "./move"; import { Type } from "./type"; import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ReverseDrainAbAttr, applyAbAttrs, ProtectStatAbAttr } from "./ability"; import { TerrainType } from "./terrain"; @@ -141,6 +141,18 @@ export abstract class MoveRestrictionBattlerTag extends BattlerTag { */ abstract isMoveRestricted(move: Moves): boolean; + /** + * Checks if this tag is restricting a move based on a user's decisions during the target selection phase + * + * @param {Moves} move {@linkcode Moves} move ID to check restriction for + * @param {Pokemon} user {@linkcode Pokemon} the user of the above move + * @param {Pokemon} target {@linkcode Pokemon} the target of the above move + * @returns {boolean} `false` unless overridden by the child tag + */ + isMoveTargetRestricted(move: Moves, user: Pokemon, target: Pokemon): boolean { + return false; + } + /** * Gets the text to display when the player attempts to select a move that is restricted by this tag. * @@ -524,10 +536,6 @@ export class FlinchedTag extends BattlerTag { applyAbAttrs(FlinchEffectAbAttr, pokemon, null); } - canAdd(pokemon: Pokemon): boolean { - return !pokemon.isMax(); - } - /** * Cancels the Pokemon's next Move on the turn this tag is applied * @param pokemon The {@linkcode Pokemon} with this tag @@ -878,10 +886,6 @@ export class EncoreTag extends BattlerTag { } canAdd(pokemon: Pokemon): boolean { - if (pokemon.isMax()) { - return false; - } - const lastMoves = pokemon.getLastXMoves(1); if (!lastMoves.length) { return false; @@ -1060,19 +1064,11 @@ export class MinimizeTag extends BattlerTag { super(BattlerTagType.MINIMIZED, BattlerTagLapseType.TURN_END, 1, Moves.MINIMIZE); } - canAdd(pokemon: Pokemon): boolean { - return !pokemon.isMax(); - } - onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { - //If a pokemon dynamaxes they lose minimized status - if (pokemon.isMax()) { - return false; - } return lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); } @@ -2194,6 +2190,74 @@ export class ExposedTag extends BattlerTag { } } +/** + * Tag that prevents HP recovery from held items and move effects. It also blocks the usage of recovery moves. + * Applied by moves: {@linkcode Moves.HEAL_BLOCK | Heal Block (5 turns)}, {@linkcode Moves.PSYCHIC_NOISE | Psychic Noise (2 turns)} + * + * @extends MoveRestrictionBattlerTag + */ +export class HealBlockTag extends MoveRestrictionBattlerTag { + constructor(turnCount: number, sourceMove: Moves) { + super(BattlerTagType.HEAL_BLOCK, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.TURN_END ], turnCount, sourceMove); + } + + onActivation(pokemon: Pokemon): string { + return i18next.t("battle:battlerTagsHealBlock", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }); + } + + /** + * Checks if a move is disabled under Heal Block + * @param {Moves} move {@linkcode Moves} the move ID + * @returns `true` if the move has a TRIAGE_MOVE flag and is a status move + */ + override isMoveRestricted(move: Moves): boolean { + if (allMoves[move].hasFlag(MoveFlags.TRIAGE_MOVE) && allMoves[move].category === MoveCategory.STATUS) { + return true; + } + return false; + } + + /** + * Checks if a move is disabled under Heal Block because of its choice of target + * Implemented b/c of Pollen Puff + * @param {Moves} move {@linkcode Moves} the move ID + * @param {Pokemon} user {@linkcode Pokemon} the move user + * @param {Pokemon} target {@linkcode Pokemon} the target of the move + * @returns `true` if the move cannot be used because the target is an ally + */ + override isMoveTargetRestricted(move: Moves, user: Pokemon, target: Pokemon) { + const moveCategory = new Utils.IntegerHolder(allMoves[move].category); + applyMoveAttrs(StatusCategoryOnAllyAttr, user, target, allMoves[move], moveCategory); + if (allMoves[move].hasAttr(HealOnAllyAttr) && moveCategory.value === MoveCategory.STATUS ) { + return true; + } + return false; + } + + /** + * Uses DisabledTag's selectionDeniedText() message + */ + 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 onRemove(pokemon: Pokemon): void { + super.onRemove(pokemon); + + pokemon.scene.queueMessage(i18next.t("battle:battlerTagsHealBlockOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), null, false, null); + } +} + /** * Tag that doubles the type effectiveness of Fire-type moves. * @extends BattlerTag @@ -2238,8 +2302,8 @@ export class SubstituteTag extends BattlerTag { pokemon.scene.triggerPokemonBattleAnim(pokemon, PokemonAnimType.SUBSTITUTE_ADD); pokemon.scene.queueMessage(i18next.t("battlerTags:substituteOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), 1500); - // Remove any trapping effects from the user - pokemon.findAndRemoveTags(tag => tag instanceof TrappedTag); + // Remove any binding effects from the user + pokemon.findAndRemoveTags(tag => tag instanceof DamagingTrapTag); } /** Queues an on-remove battle animation that removes the Substitute's sprite. */ @@ -2506,6 +2570,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new SubstituteTag(sourceMove, sourceId); case BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON: return new MysteryEncounterPostSummonTag(); + case BattlerTagType.HEAL_BLOCK: + return new HealBlockTag(turnCount, sourceMove); case BattlerTagType.NONE: default: return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 2205519c532..46b56a30835 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -451,6 +451,30 @@ export class SingleGenerationChallenge extends Challenge { applyFixedBattle(waveIndex: Number, battleConfig: FixedBattleConfig): boolean { let trainerTypes: TrainerType[] = []; switch (waveIndex) { + case 35: + trainerTypes = [ TrainerType.ROCKET_GRUNT, TrainerType.ROCKET_GRUNT, Utils.randSeedItem([ TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT ]), TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, Utils.randSeedItem([ TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT ]), TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ]; + break; + case 62: + trainerTypes = [ TrainerType.ROCKET_GRUNT, TrainerType.ROCKET_GRUNT, Utils.randSeedItem([ TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT ]), TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, Utils.randSeedItem([ TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT ]), TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ]; + break; + case 64: + trainerTypes = [ TrainerType.ROCKET_GRUNT, TrainerType.ROCKET_GRUNT, Utils.randSeedItem([ TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT ]), TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, Utils.randSeedItem([ TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT ]), TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ]; + break; + case 66: + trainerTypes = [ Utils.randSeedItem([ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ]), Utils.randSeedItem([ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ]), Utils.randSeedItem([ TrainerType.TABITHA, TrainerType.COURTNEY, TrainerType.MATT, TrainerType.SHELLY ]), Utils.randSeedItem([ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ]), Utils.randSeedItem([ TrainerType.ZINZOLIN, TrainerType.ROOD ]), Utils.randSeedItem([ TrainerType.XEROSIC, TrainerType.BRYONY ]), Utils.randSeedItem([ TrainerType.FABA, TrainerType.PLUMERIA ]), TrainerType.OLEANA, Utils.randSeedItem([ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ]) ]; + break; + case 112: + trainerTypes = [ TrainerType.ROCKET_GRUNT, TrainerType.ROCKET_GRUNT, Utils.randSeedItem([ TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT ]), TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, Utils.randSeedItem([ TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT ]), TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ]; + break; + case 114: + trainerTypes = [ Utils.randSeedItem([ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ]), Utils.randSeedItem([ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ]), Utils.randSeedItem([ TrainerType.TABITHA, TrainerType.COURTNEY, TrainerType.MATT, TrainerType.SHELLY ]), Utils.randSeedItem([ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ]), Utils.randSeedItem([ TrainerType.ZINZOLIN, TrainerType.ROOD ]), Utils.randSeedItem([ TrainerType.XEROSIC, TrainerType.BRYONY ]), Utils.randSeedItem([ TrainerType.FABA, TrainerType.PLUMERIA ]), TrainerType.OLEANA, Utils.randSeedItem([ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ]) ]; + break; + case 115: + trainerTypes = [ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.ROCKET_BOSS_GIOVANNI_1, Utils.randSeedItem([ TrainerType.MAXIE, TrainerType.ARCHIE ]), TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE, Utils.randSeedItem([ TrainerType.LUSAMINE, TrainerType.GUZMA ]), TrainerType.ROSE, TrainerType.PENNY ]; + break; + case 165: + trainerTypes = [ TrainerType.ROCKET_BOSS_GIOVANNI_2, TrainerType.ROCKET_BOSS_GIOVANNI_2, Utils.randSeedItem([ TrainerType.MAXIE_2, TrainerType.ARCHIE_2 ]), TrainerType.CYRUS_2, TrainerType.GHETSIS_2, TrainerType.LYSANDRE_2, Utils.randSeedItem([ TrainerType.LUSAMINE_2, TrainerType.GUZMA_2 ]), TrainerType.ROSE_2, TrainerType.PENNY_2 ]; + break; case 182: trainerTypes = [ TrainerType.LORELEI, TrainerType.WILL, TrainerType.SIDNEY, TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, Utils.randSeedItem([ TrainerType.HALA, TrainerType.MOLAYNE ]), TrainerType.MARNIE_ELITE, TrainerType.RIKA ]; break; diff --git a/src/data/dialogue.ts b/src/data/dialogue.ts index b01242d083a..499cd106cf9 100644 --- a/src/data/dialogue.ts +++ b/src/data/dialogue.ts @@ -837,11 +837,15 @@ export const trainerTypeDialogue: TrainerTypeDialogue = { "dialogue:macro_grunt.encounter.1", "dialogue:macro_grunt.encounter.2", "dialogue:macro_grunt.encounter.3", + "dialogue:macro_grunt.encounter.4", + "dialogue:macro_grunt.encounter.5", ], victory: [ "dialogue:macro_grunt.victory.1", "dialogue:macro_grunt.victory.2", "dialogue:macro_grunt.victory.3", + "dialogue:macro_grunt.victory.4", + "dialogue:macro_grunt.victory.5", ] } ], @@ -859,6 +863,84 @@ export const trainerTypeDialogue: TrainerTypeDialogue = { ] } ], + [TrainerType.STAR_GRUNT]: [ + { + encounter: [ + "dialogue:star_grunt.encounter.1", + "dialogue:star_grunt.encounter.2", + "dialogue:star_grunt.encounter.3", + "dialogue:star_grunt.encounter.4", + "dialogue:star_grunt.encounter.5", + ], + victory: [ + "dialogue:star_grunt.victory.1", + "dialogue:star_grunt.victory.2", + "dialogue:star_grunt.victory.3", + "dialogue:star_grunt.victory.4", + "dialogue:star_grunt.victory.5", + ] + } + ], + [TrainerType.GIACOMO]: [ + { + encounter: [ + "dialogue:giacomo.encounter.1", + "dialogue:giacomo.encounter.2", + ], + victory: [ + "dialogue:giacomo.victory.1", + "dialogue:giacomo.victory.2", + ] + } + ], + [TrainerType.MELA]: [ + { + encounter: [ + "dialogue:mela.encounter.1", + "dialogue:mela.encounter.2", + ], + victory: [ + "dialogue:mela.victory.1", + "dialogue:mela.victory.2", + ] + } + ], + [TrainerType.ATTICUS]: [ + { + encounter: [ + "dialogue:atticus.encounter.1", + "dialogue:atticus.encounter.2", + ], + victory: [ + "dialogue:atticus.victory.1", + "dialogue:atticus.victory.2", + ] + } + ], + [TrainerType.ORTEGA]: [ + { + encounter: [ + "dialogue:ortega.encounter.1", + "dialogue:ortega.encounter.2", + ], + victory: [ + "dialogue:ortega.victory.1", + "dialogue:ortega.victory.2", + ] + } + ], + [TrainerType.ERI]: [ + { + encounter: [ + "dialogue:eri.encounter.1", + "dialogue:eri.encounter.2", + ], + victory: [ + "dialogue:eri.victory.1", + "dialogue:eri.victory.2", + ] + } + ], [TrainerType.ROCKET_BOSS_GIOVANNI_1]: [ { encounter: [ @@ -1093,6 +1175,32 @@ export const trainerTypeDialogue: TrainerTypeDialogue = { ] } ], + [TrainerType.PENNY]: [ + { + encounter: [ + "dialogue:star_boss_penny_1.encounter.1" + ], + victory: [ + "dialogue:star_boss_penny_1.victory.1" + ], + defeat: [ + "dialogue:star_boss_penny_1.defeat.1" + ] + } + ], + [TrainerType.PENNY_2]: [ + { + encounter: [ + "dialogue:star_boss_penny_2.encounter.1" + ], + victory: [ + "dialogue:star_boss_penny_2.victory.1" + ], + defeat: [ + "dialogue:star_boss_penny_2.defeat.1" + ] + } + ], [TrainerType.BUCK]: [ { encounter: [ diff --git a/src/data/egg-moves.ts b/src/data/egg-moves.ts index b516238c46e..3e58f993df2 100644 --- a/src/data/egg-moves.ts +++ b/src/data/egg-moves.ts @@ -264,7 +264,7 @@ export const speciesEggMoves = { [Species.PANPOUR]: [ Moves.NASTY_PLOT, Moves.ENERGY_BALL, Moves.EARTH_POWER, Moves.STEAM_ERUPTION ], [Species.MUNNA]: [ Moves.COSMIC_POWER, Moves.AURA_SPHERE, Moves.EARTH_POWER, Moves.MYSTICAL_POWER ], [Species.PIDOVE]: [ Moves.GUNK_SHOT, Moves.TIDY_UP, Moves.FLOATY_FALL, Moves.TRIPLE_ARROWS ], - [Species.BLITZLE]: [ Moves.HIGH_HORSEPOWER, Moves.THUNDEROUS_KICK, Moves.FLARE_BLITZ, Moves.VOLT_TACKLE ], + [Species.BLITZLE]: [ Moves.HORN_LEECH, Moves.SWORDS_DANCE, Moves.FLARE_BLITZ, Moves.BOLT_STRIKE ], [Species.ROGGENROLA]: [ Moves.BODY_PRESS, Moves.CURSE, Moves.SHORE_UP, Moves.DIAMOND_STORM ], [Species.WOOBAT]: [ Moves.ESPER_WING, Moves.STORED_POWER, Moves.MYSTICAL_FIRE, Moves.OBLIVION_WING ], [Species.DRILBUR]: [ Moves.IRON_HEAD, Moves.MOUNTAIN_GALE, Moves.SHIFT_GEAR, Moves.THOUSAND_ARROWS ], diff --git a/src/data/egg.ts b/src/data/egg.ts index 0219f4f5b47..b37240a2028 100644 --- a/src/data/egg.ts +++ b/src/data/egg.ts @@ -1,6 +1,6 @@ import BattleScene from "../battle-scene"; import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./pokemon-species"; -import { VariantTier } from "../enums/variant-tiers"; +import { VariantTier } from "../enums/variant-tier"; import * as Utils from "../utils"; import Overrides from "#app/overrides"; import { pokemonPrevolutions } from "./pokemon-evolutions"; @@ -178,7 +178,7 @@ export class Egg { // 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; + this._variantTier = VariantTier.STANDARD; } // Needs this._tier so it needs to be generated afer the tier override if bought from same species this._eggMoveIndex = eggOptions?.eggMoveIndex ?? this.rollEggMoveIndex(); @@ -494,12 +494,12 @@ export class Egg { // place but I don't want to touch the pokemon class. private rollVariant(): VariantTier { if (!this.isShiny) { - return VariantTier.COMMON; + return VariantTier.STANDARD; } const rand = Utils.randSeedInt(10); if (rand >= 4) { - return VariantTier.COMMON; // 6/10 + return VariantTier.STANDARD; // 6/10 } else if (rand >= 1) { return VariantTier.RARE; // 3/10 } else { diff --git a/src/data/move.ts b/src/data/move.ts index 1d1a788e768..a84bf38c5ea 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1,16 +1,15 @@ -import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims"; -import { EncoreTag, GulpMissileTag, HelpingHandTag, SemiInvulnerableTag, ShellTrapTag, StockpilingTag, TrappedTag, SubstituteTag, TypeBoostTag } from "./battler-tags"; +import { ChargeAnim, initMoveAnim, loadMoveAnimAssets, MoveChargeAnim } from "./battle-anims"; +import { EncoreTag, GulpMissileTag, HelpingHandTag, SemiInvulnerableTag, ShellTrapTag, StockpilingTag, SubstituteTag, TrappedTag, TypeBoostTag } from "./battler-tags"; import { getPokemonNameWithAffix } from "../messages"; import Pokemon, { AttackMoveResult, EnemyPokemon, HitResult, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../field/pokemon"; -import { StatusEffect, getStatusEffectHealText, isNonVolatileStatusEffect, getNonVolatileStatusEffects } from "./status-effect"; +import { getNonVolatileStatusEffects, getStatusEffectHealText, isNonVolatileStatusEffect, StatusEffect } from "./status-effect"; import { getTypeDamageMultiplier, Type } from "./type"; -import { Constructor } from "#app/utils"; +import { Constructor, NumberHolder } from "#app/utils"; import * as Utils from "../utils"; import { WeatherType } from "./weather"; import { ArenaTagSide, ArenaTrapTag, WeakenMoveTypeTag } from "./arena-tag"; -import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, BlockItemTheftAbAttr, applyPostAttackAbAttrs, ConfusionOnStatusEffectAbAttr, HealFromBerryUseAbAttr, IgnoreProtectOnContactAbAttr, IgnoreMoveEffectsAbAttr, applyPreDefendAbAttrs, MoveEffectChanceMultiplierAbAttr, WonderSkinAbAttr, applyPreAttackAbAttrs, MoveTypeChangeAbAttr, UserFieldMoveTypePowerBoostAbAttr, FieldMoveTypePowerBoostAbAttr, AllyMoveCategoryPowerBoostAbAttr, VariableMovePowerAbAttr } from "./ability"; -import { allAbilities } from "./ability"; -import { PokemonHeldItemModifier, BerryModifier, PreserveBerryModifier, PokemonMoveAccuracyBoosterModifier, AttackTypeBoosterModifier, PokemonMultiHitModifier } from "../modifier/modifier"; +import { allAbilities, AllyMoveCategoryPowerBoostAbAttr, applyAbAttrs, applyPostAttackAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, BlockItemTheftAbAttr, BlockNonDirectDamageAbAttr, BlockOneHitKOAbAttr, BlockRecoilDamageAttr, ConfusionOnStatusEffectAbAttr, FieldMoveTypePowerBoostAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, HealFromBerryUseAbAttr, IgnoreContactAbAttr, IgnoreMoveEffectsAbAttr, IgnoreProtectOnContactAbAttr, MaxMultiHitAbAttr, MoveAbilityBypassAbAttr, MoveEffectChanceMultiplierAbAttr, MoveTypeChangeAbAttr, ReverseDrainAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, UnswappableAbilityAbAttr, UserFieldMoveTypePowerBoostAbAttr, VariableMovePowerAbAttr, WonderSkinAbAttr } from "./ability"; +import { AttackTypeBoosterModifier, BerryModifier, PokemonHeldItemModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, PreserveBerryModifier } from "../modifier/modifier"; import { BattlerIndex, BattleType } from "../battle"; import { TerrainType } from "./terrain"; import { ModifierPoolType } from "#app/modifier/modifier-type"; @@ -25,7 +24,7 @@ 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 { BATTLE_STATS, type BattleStat, EFFECTIVE_STATS, type EffectiveStat, getStatKey, Stat } 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"; @@ -36,7 +35,6 @@ 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"; -import { NumberHolder } from "#app/utils"; import { GameMode } from "#app/game-mode"; import { applyChallenges, ChallengeType } from "./challenge"; @@ -813,10 +811,6 @@ export default class Move implements Localizable { power.value *= typeBoost.boostValue; } - if (source.scene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() && this.type === Type.GROUND && this.moveTarget === MoveTarget.ALL_NEAR_OTHERS) { - power.value /= 2; - } - applyMoveAttrs(VariablePowerAttr, source, target, this, power); source.scene.applyModifiers(PokemonMultiHitModifier, source.isPlayer(), source, new Utils.IntegerHolder(0), power); @@ -2140,7 +2134,7 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr { if (rand >= this.chance) { return resolve(false); } - const heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferrable); + const heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferable); if (heldItems.length) { const poolType = target.isPlayer() ? ModifierPoolType.PLAYER : target.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD; const highestItemTier = heldItems.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is the bang after tier correct? @@ -2217,7 +2211,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { } // Considers entire transferrable item pool by default (Knock Off). Otherwise berries only if specified (Incinerate). - let heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferrable); + let heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferable); if (this.berriesOnly) { heldItems = heldItems.filter(m => m instanceof BerryModifier && m.pokemonId === target.id, target.isPlayer()); @@ -2421,6 +2415,16 @@ export class BypassSleepAttr extends MoveAttr { return false; } + + /** + * Returns arbitrarily high score when Pokemon is asleep, otherwise shouldn't be used + * @param user + * @param target + * @param move + */ + getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { + return user.status && user.status.effect === StatusEffect.SLEEP ? 200 : -10; + } } /** @@ -3843,7 +3847,7 @@ export class StormAccuracyAttr extends VariableAccuracyAttr { * @extends VariableAccuracyAttr * @see {@linkcode apply} */ -export class MinimizeAccuracyAttr extends VariableAccuracyAttr { +export class AlwaysHitMinimizeAttr extends VariableAccuracyAttr { /** * @see {@linkcode apply} * @param user N/A @@ -3977,19 +3981,18 @@ 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.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); + const category = (args[0] as Utils.NumberHolder); - // 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) { + const predictedPhysDmg = target.getBaseDamage(user, move, MoveCategory.PHYSICAL, true, true); + const predictedSpecDmg = target.getBaseDamage(user, move, MoveCategory.SPECIAL, true, true); + + if (predictedPhysDmg > predictedSpecDmg) { category.value = MoveCategory.PHYSICAL; return true; - } else if (atkRatio === specialRatio && user.randSeedInt(2) === 0) { + } else if (predictedPhysDmg === predictedSpecDmg && user.randSeedInt(2) === 0) { category.value = MoveCategory.PHYSICAL; return true; } - return false; } } @@ -4536,6 +4539,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr { case BattlerTagType.NIGHTMARE: case BattlerTagType.DROWSY: case BattlerTagType.DISABLED: + case BattlerTagType.HEAL_BLOCK: return -5; case BattlerTagType.SEEDED: case BattlerTagType.SALT_CURED: @@ -4856,7 +4860,9 @@ export class RemoveAllSubstitutesAttr extends MoveEffectAttr { } /** - * Attribute used when a move hits a {@linkcode BattlerTagType} for double damage + * Attribute used when a move can deal damage to {@linkcode BattlerTagType} + * Moves that always hit but do not deal double damage: Thunder, Fissure, Sky Uppercut, + * Smack Down, Hurricane, Thousand Arrows * @extends MoveAttr */ export class HitsTagAttr extends MoveAttr { @@ -4865,7 +4871,7 @@ export class HitsTagAttr extends MoveAttr { /** Should this move deal double damage against {@linkcode HitsTagAttr.tagType}? */ public doubleDamage: boolean; - constructor(tagType: BattlerTagType, doubleDamage?: boolean) { + constructor(tagType: BattlerTagType, doubleDamage: boolean = false) { super(); this.tagType = tagType; @@ -4877,6 +4883,17 @@ export class HitsTagAttr extends MoveAttr { } } +/** + * Used for moves that will always hit for a given tag but also doubles damage. + * Moves include: Gust, Stomp, Body Slam, Surf, Earthquake, Magnitude, Twister, + * Whirlpool, Dragon Rush, Heat Crash, Steam Roller, Flying Press + */ +export class HitsTagForDoubleDamageAttr extends HitsTagAttr { + constructor(tagType: BattlerTagType) { + super(tagType, true); + } +} + export class AddArenaTagAttr extends MoveEffectAttr { public tagType: ArenaTagType; public turnCount: integer; @@ -5205,7 +5222,6 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { switchOutTarget.leaveField(false); if (switchOutTarget.hp) { - switchOutTarget.setWildFlee(true); user.scene.queueMessage(i18next.t("moveTriggers:fled", {pokemonName: getPokemonNameWithAffix(switchOutTarget)}), null, true, 500); // in double battles redirect potential moves off fled pokemon @@ -5248,7 +5264,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { return false; } - if (!this.user && move.category === MoveCategory.STATUS && (target.hasAbilityWithAttr(ForceSwitchOutImmunityAbAttr) || target.isMax())) { + if (!this.user && move.category === MoveCategory.STATUS && (target.hasAbilityWithAttr(ForceSwitchOutImmunityAbAttr))) { return false; } @@ -6397,7 +6413,7 @@ export class AttackedByItemAttr extends MoveAttr { */ getCondition(): MoveConditionFunc { return (user: Pokemon, target: Pokemon, move: Move) => { - const heldItems = target.getHeldItems().filter(i => i.isTransferrable); + const heldItems = target.getHeldItems().filter(i => i.isTransferable); if (heldItems.length === 0) { return false; } @@ -6458,8 +6474,6 @@ const failOnGravityCondition: MoveConditionFunc = (user, target, move) => !user. 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) => { @@ -6758,12 +6772,11 @@ export function initMoves() { new AttackMove(Moves.CUT, Type.NORMAL, MoveCategory.PHYSICAL, 50, 95, 30, -1, 0, 1) .slicingMove(), new AttackMove(Moves.GUST, Type.FLYING, MoveCategory.SPECIAL, 40, 100, 35, -1, 0, 1) - .attr(HitsTagAttr, BattlerTagType.FLYING, true) + .attr(HitsTagForDoubleDamageAttr, BattlerTagType.FLYING) .windMove(), new AttackMove(Moves.WING_ATTACK, Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 35, -1, 0, 1), new StatusMove(Moves.WHIRLWIND, Type.NORMAL, -1, 20, -1, -6, 1) .attr(ForceSwitchOutAttr) - .attr(HitsTagAttr, BattlerTagType.FLYING, false) .ignoresSubstitute() .hidesTarget() .windMove(), @@ -6776,8 +6789,8 @@ export function initMoves() { new AttackMove(Moves.SLAM, Type.NORMAL, MoveCategory.PHYSICAL, 80, 75, 20, -1, 0, 1), new AttackMove(Moves.VINE_WHIP, Type.GRASS, MoveCategory.PHYSICAL, 45, 100, 25, -1, 0, 1), new AttackMove(Moves.STOMP, Type.NORMAL, MoveCategory.PHYSICAL, 65, 100, 20, 30, 0, 1) - .attr(MinimizeAccuracyAttr) - .attr(HitsTagAttr, BattlerTagType.MINIMIZED, true) + .attr(AlwaysHitMinimizeAttr) + .attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED) .attr(FlinchAttr), new AttackMove(Moves.DOUBLE_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, 30, 100, 30, -1, 0, 1) .attr(MultiHitAttr, MultiHitType._2), @@ -6801,8 +6814,8 @@ export function initMoves() { .attr(OneHitKOAccuracyAttr), new AttackMove(Moves.TACKLE, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 35, -1, 0, 1), new AttackMove(Moves.BODY_SLAM, Type.NORMAL, MoveCategory.PHYSICAL, 85, 100, 15, 30, 0, 1) - .attr(MinimizeAccuracyAttr) - .attr(HitsTagAttr, BattlerTagType.MINIMIZED, true) + .attr(AlwaysHitMinimizeAttr) + .attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED) .attr(StatusEffectAttr, StatusEffect.PARALYSIS), new AttackMove(Moves.WRAP, Type.NORMAL, MoveCategory.PHYSICAL, 15, 90, 20, -1, 0, 1) .attr(TrapAttr, BattlerTagType.WRAP), @@ -6855,8 +6868,7 @@ export function initMoves() { new StatusMove(Moves.DISABLE, Type.NORMAL, 100, 20, -1, 0, 1) .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) - .ignoresSubstitute() - .condition(failOnMaxCondition), + .ignoresSubstitute(), new AttackMove(Moves.ACID, Type.POISON, MoveCategory.SPECIAL, 40, 100, 30, 10, 0, 1) .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), @@ -6871,7 +6883,7 @@ export function initMoves() { new AttackMove(Moves.HYDRO_PUMP, Type.WATER, MoveCategory.SPECIAL, 110, 80, 5, -1, 0, 1), new AttackMove(Moves.SURF, Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, -1, 0, 1) .target(MoveTarget.ALL_NEAR_OTHERS) - .attr(HitsTagAttr, BattlerTagType.UNDERWATER, true) + .attr(HitsTagForDoubleDamageAttr, BattlerTagType.UNDERWATER) .attr(GulpMissileTagAttr), new AttackMove(Moves.ICE_BEAM, Type.ICE, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 1) .attr(StatusEffectAttr, StatusEffect.FREEZE), @@ -6894,8 +6906,7 @@ export function initMoves() { .attr(RecoilAttr) .recklessMove(), new AttackMove(Moves.LOW_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, -1, 0, 1) - .attr(WeightPowerAttr) - .condition(failOnMaxCondition), + .attr(WeightPowerAttr), new AttackMove(Moves.COUNTER, Type.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, -1, -5, 1) .attr(CounterDamageAttr, (move: Move) => move.category === MoveCategory.PHYSICAL, 2) .target(MoveTarget.ATTACKER), @@ -6955,17 +6966,18 @@ export function initMoves() { new AttackMove(Moves.THUNDER, Type.ELECTRIC, MoveCategory.SPECIAL, 110, 70, 10, 30, 0, 1) .attr(StatusEffectAttr, StatusEffect.PARALYSIS) .attr(ThunderAccuracyAttr) - .attr(HitsTagAttr, BattlerTagType.FLYING, false), + .attr(HitsTagAttr, BattlerTagType.FLYING), new AttackMove(Moves.ROCK_THROW, Type.ROCK, MoveCategory.PHYSICAL, 50, 90, 15, -1, 0, 1) .makesContact(false), new AttackMove(Moves.EARTHQUAKE, Type.GROUND, MoveCategory.PHYSICAL, 100, 100, 10, -1, 0, 1) - .attr(HitsTagAttr, BattlerTagType.UNDERGROUND, true) + .attr(HitsTagForDoubleDamageAttr, BattlerTagType.UNDERGROUND) + .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() ? 0.5 : 1) .makesContact(false) .target(MoveTarget.ALL_NEAR_OTHERS), new AttackMove(Moves.FISSURE, Type.GROUND, MoveCategory.PHYSICAL, 200, 30, 5, -1, 0, 1) .attr(OneHitKOAttr) .attr(OneHitKOAccuracyAttr) - .attr(HitsTagAttr, BattlerTagType.UNDERGROUND, false) + .attr(HitsTagAttr, BattlerTagType.UNDERGROUND) .makesContact(false), new AttackMove(Moves.DIG, Type.GROUND, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 1) .attr(ChargeAttr, ChargeAnim.DIG_CHARGING, i18next.t("moveTriggers:dugAHole", {pokemonName: "{USER}"}), BattlerTagType.UNDERGROUND) @@ -7353,7 +7365,8 @@ export function initMoves() { new AttackMove(Moves.MAGNITUDE, Type.GROUND, MoveCategory.PHYSICAL, -1, 100, 30, -1, 0, 2) .attr(PreMoveMessageAttr, magnitudeMessageFunc) .attr(MagnitudePowerAttr) - .attr(HitsTagAttr, BattlerTagType.UNDERGROUND, true) + .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() ? 0.5 : 1) + .attr(HitsTagForDoubleDamageAttr, BattlerTagType.UNDERGROUND) .makesContact(false) .target(MoveTarget.ALL_NEAR_OTHERS), new AttackMove(Moves.DYNAMIC_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 50, 5, 100, 0, 2) @@ -7409,7 +7422,7 @@ export function initMoves() { new AttackMove(Moves.CROSS_CHOP, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 80, 5, -1, 0, 2) .attr(HighCritAttr), new AttackMove(Moves.TWISTER, Type.DRAGON, MoveCategory.SPECIAL, 40, 100, 20, 20, 0, 2) - .attr(HitsTagAttr, BattlerTagType.FLYING, true) + .attr(HitsTagForDoubleDamageAttr, BattlerTagType.FLYING) .attr(FlinchAttr) .windMove() .target(MoveTarget.ALL_NEAR_ENEMIES), @@ -7441,7 +7454,7 @@ export function initMoves() { .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), + .attr(HitsTagForDoubleDamageAttr, BattlerTagType.UNDERWATER), new AttackMove(Moves.BEAT_UP, Type.DARK, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 2) .attr(MultiHitAttr, MultiHitType.BEAT_UP) .attr(BeatUpAttr) @@ -7539,7 +7552,7 @@ export function initMoves() { .attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true) .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(MovePowerMultiplierAttr, (user, target, move) => target.getHeldItems().filter(i => i.isTransferable).length > 0 ? 1.5 : 1) .attr(RemoveHeldItemAttr, false), new AttackMove(Moves.ENDEAVOR, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 3) .attr(MatchHpAttr) @@ -7814,8 +7827,8 @@ export function initMoves() { .makesContact() .attr(LessPPMorePowerAttr), new StatusMove(Moves.HEAL_BLOCK, Type.PSYCHIC, 100, 15, -1, 0, 4) - .target(MoveTarget.ALL_NEAR_ENEMIES) - .unimplemented(), + .attr(AddBattlerTagAttr, BattlerTagType.HEAL_BLOCK, false, true, 5) + .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.WRING_OUT, Type.NORMAL, MoveCategory.SPECIAL, -1, 100, 5, -1, 0, 4) .attr(OpponentHighHpPowerAttr, 120) .makesContact(), @@ -7895,8 +7908,8 @@ export function initMoves() { new AttackMove(Moves.DRAGON_PULSE, Type.DRAGON, MoveCategory.SPECIAL, 85, 100, 10, -1, 0, 4) .pulseMove(), new AttackMove(Moves.DRAGON_RUSH, Type.DRAGON, MoveCategory.PHYSICAL, 100, 75, 10, 20, 0, 4) - .attr(MinimizeAccuracyAttr) - .attr(HitsTagAttr, BattlerTagType.MINIMIZED, true) + .attr(AlwaysHitMinimizeAttr) + .attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED) .attr(FlinchAttr), new AttackMove(Moves.POWER_GEM, Type.ROCK, MoveCategory.SPECIAL, 80, 100, 20, -1, 0, 4), new AttackMove(Moves.DRAIN_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 4) @@ -8006,8 +8019,7 @@ export function initMoves() { .target(MoveTarget.ENEMY_SIDE), new AttackMove(Moves.GRASS_KNOT, Type.GRASS, MoveCategory.SPECIAL, -1, 100, 20, -1, 0, 4) .attr(WeightPowerAttr) - .makesContact() - .condition(failOnMaxCondition), + .makesContact(), new AttackMove(Moves.CHATTER, Type.FLYING, MoveCategory.SPECIAL, 65, 100, 20, 100, 0, 4) .attr(ConfuseAttr) .soundBased(), @@ -8094,7 +8106,7 @@ export function initMoves() { .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true) .attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED) .attr(RemoveBattlerTagAttr, [BattlerTagType.FLYING, BattlerTagType.MAGNET_RISEN]) - .attr(HitsTagAttr, BattlerTagType.FLYING, false) + .attr(HitsTagAttr, BattlerTagType.FLYING) .makesContact(false), new AttackMove(Moves.STORM_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 5) .attr(CritOnlyAttr), @@ -8107,10 +8119,9 @@ export function initMoves() { .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) + .attr(AlwaysHitMinimizeAttr) .attr(CompareWeightPowerAttr) - .attr(HitsTagAttr, BattlerTagType.MINIMIZED, true) - .condition(failOnMaxCondition), + .attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED), new AttackMove(Moves.SYNCHRONOISE, Type.PSYCHIC, MoveCategory.SPECIAL, 120, 100, 10, -1, 0, 5) .target(MoveTarget.ALL_NEAR_OTHERS) .condition(unknownTypeCondition) @@ -8193,7 +8204,7 @@ export function initMoves() { new StatusMove(Moves.QUASH, Type.DARK, 100, 15, -1, 0, 5) .unimplemented(), new AttackMove(Moves.ACROBATICS, Type.FLYING, MoveCategory.PHYSICAL, 55, 100, 15, -1, 0, 5) - .attr(MovePowerMultiplierAttr, (user, target, move) => Math.max(1, 2 - 0.2 * user.getHeldItems().filter(i => i.isTransferrable).reduce((v, m) => v + m.stackCount, 0))), + .attr(MovePowerMultiplierAttr, (user, target, move) => Math.max(1, 2 - 0.2 * user.getHeldItems().filter(i => i.isTransferable).reduce((v, m) => v + m.stackCount, 0))), new StatusMove(Moves.REFLECT_TYPE, Type.NORMAL, -1, 15, -1, 0, 5) .ignoresSubstitute() .attr(CopyTypeAttr), @@ -8229,6 +8240,7 @@ export function initMoves() { .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.BULLDOZE, Type.GROUND, MoveCategory.PHYSICAL, 60, 100, 20, 100, 0, 5) .attr(StatStageChangeAttr, [ Stat.SPD ], -1) + .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() ? 0.5 : 1) .makesContact(false) .target(MoveTarget.ALL_NEAR_OTHERS), new AttackMove(Moves.FROST_BREATH, Type.ICE, MoveCategory.SPECIAL, 60, 90, 10, 100, 0, 5) @@ -8260,13 +8272,14 @@ export function initMoves() { .attr(StatStageChangeAttr, [ Stat.DEF ], -1) .slicingMove(), new AttackMove(Moves.HEAT_CRASH, Type.FIRE, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 5) - .attr(MinimizeAccuracyAttr) + .attr(AlwaysHitMinimizeAttr) .attr(CompareWeightPowerAttr) - .attr(HitsTagAttr, BattlerTagType.MINIMIZED, true) - .condition(failOnMaxCondition), + .attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED), new AttackMove(Moves.LEAF_TORNADO, Type.GRASS, MoveCategory.SPECIAL, 65, 90, 10, 50, 0, 5) .attr(StatStageChangeAttr, [ Stat.ACC ], -1), new AttackMove(Moves.STEAMROLLER, Type.BUG, MoveCategory.PHYSICAL, 65, 100, 20, 30, 0, 5) + .attr(AlwaysHitMinimizeAttr) + .attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED) .attr(FlinchAttr), new SelfStatusMove(Moves.COTTON_GUARD, Type.GRASS, -1, 10, -1, 0, 5) .attr(StatStageChangeAttr, [ Stat.DEF ], 3, true), @@ -8279,7 +8292,7 @@ export function initMoves() { new AttackMove(Moves.HURRICANE, Type.FLYING, MoveCategory.SPECIAL, 110, 70, 10, 30, 0, 5) .attr(ThunderAccuracyAttr) .attr(ConfuseAttr) - .attr(HitsTagAttr, BattlerTagType.FLYING, false) + .attr(HitsTagAttr, BattlerTagType.FLYING) .windMove(), new AttackMove(Moves.HEAD_CHARGE, Type.NORMAL, MoveCategory.PHYSICAL, 120, 100, 15, -1, 0, 5) .attr(RecoilAttr) @@ -8333,9 +8346,9 @@ export function initMoves() { .attr(LastMoveDoublePowerAttr, Moves.FUSION_FLARE) .makesContact(false), new AttackMove(Moves.FLYING_PRESS, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 95, 10, -1, 0, 6) - .attr(MinimizeAccuracyAttr) + .attr(AlwaysHitMinimizeAttr) .attr(FlyingTypeMultiplierAttr) - .attr(HitsTagAttr, BattlerTagType.MINIMIZED, true) + .attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED) .condition(failOnGravityCondition), new StatusMove(Moves.MAT_BLOCK, Type.FIGHTING, -1, 10, -1, 0, 6) .target(MoveTarget.USER_SIDE) @@ -8506,8 +8519,8 @@ export function initMoves() { new AttackMove(Moves.THOUSAND_ARROWS, Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 6) .attr(NeutralDamageAgainstFlyingTypeMultiplierAttr) .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true) - .attr(HitsTagAttr, BattlerTagType.FLYING, false) - .attr(HitsTagAttr, BattlerTagType.MAGNET_RISEN, false) + .attr(HitsTagAttr, BattlerTagType.FLYING) + .attr(HitsTagAttr, BattlerTagType.MAGNET_RISEN) .attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED) .attr(RemoveBattlerTagAttr, [BattlerTagType.FLYING, BattlerTagType.MAGNET_RISEN]) .makesContact(false) @@ -8764,6 +8777,8 @@ export function initMoves() { .partial() .ignoresVirtual(), new AttackMove(Moves.MALICIOUS_MOONSAULT, Type.DARK, MoveCategory.PHYSICAL, 180, -1, 1, -1, 0, 7) + .attr(AlwaysHitMinimizeAttr) + .attr(HitsTagAttr, BattlerTagType.MINIMIZED, true) .partial() .ignoresVirtual(), new AttackMove(Moves.OCEANIC_OPERETTA, Type.WATER, MoveCategory.SPECIAL, 195, -1, 1, -1, 0, 7) @@ -9111,7 +9126,7 @@ export function initMoves() { new AttackMove(Moves.SHELL_SIDE_ARM, Type.POISON, MoveCategory.SPECIAL, 90, 100, 10, 20, 0, 8) .attr(ShellSideArmCategoryAttr) .attr(StatusEffectAttr, StatusEffect.POISON) - .partial(), + .partial(), // Physical version of the move does not make contact new AttackMove(Moves.MISTY_EXPLOSION, Type.FAIRY, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 8) .attr(SacrificialAttr) .target(MoveTarget.ALL_NEAR_OTHERS) @@ -9119,7 +9134,7 @@ export function initMoves() { .condition(failIfDampCondition) .makesContact(false), new AttackMove(Moves.GRASSY_GLIDE, Type.GRASS, MoveCategory.PHYSICAL, 55, 100, 20, -1, 0, 8) - .attr(IncrementMovePriorityAttr, (user, target, move) =>user.scene.arena.getTerrainType()===TerrainType.GRASSY&&user.isGrounded()), + .attr(IncrementMovePriorityAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.GRASSY && user.isGrounded()), new AttackMove(Moves.RISING_VOLTAGE, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 20, -1, 0, 8) .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.ELECTRIC && target.isGrounded() ? 2 : 1), new AttackMove(Moves.TERRAIN_PULSE, Type.NORMAL, MoveCategory.SPECIAL, 50, 100, 10, -1, 0, 8) @@ -9595,7 +9610,7 @@ export function initMoves() { .recklessMove(), new AttackMove(Moves.PSYCHIC_NOISE, Type.PSYCHIC, MoveCategory.SPECIAL, 75, 100, 10, -1, 0, 9) .soundBased() - .partial(), + .attr(AddBattlerTagAttr, BattlerTagType.HEAL_BLOCK, false, false, 2), new AttackMove(Moves.UPPER_HAND, Type.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, 100, 3, 9) .attr(FlinchAttr) .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 && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].priority > 0 ) // TODO: is this bang correct? diff --git a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts index b66ca10c9f5..f7666fa1b37 100644 --- a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts +++ b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts @@ -2,7 +2,7 @@ import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattl import { trainerConfigs, } from "#app/data/trainer-config"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { TrainerType } from "#enums/trainer-type"; import { Species } from "#enums/species"; @@ -99,7 +99,7 @@ export const ATrainersTestEncounter: MysteryEncounter = const trainerConfig = trainerConfigs[trainerType].clone(); const trainerSpriteKey = trainerConfig.getSpriteKey(); encounter.enemyPartyConfigs.push({ - levelAdditiveMultiplier: 1, + levelAdditiveModifier: 1, trainerConfig: trainerConfig }); @@ -152,7 +152,6 @@ export const ATrainersTestEncounter: MysteryEncounter = }; encounter.setDialogueToken("eggType", i18next.t(`${namespace}.eggTypes.epic`)); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SACRED_ASH], guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA], fillRemaining: true }, [eggOptions]); - return initBattleWithEnemyConfig(scene, config); } ) @@ -180,7 +179,7 @@ export const ATrainersTestEncounter: MysteryEncounter = ) .withOutroDialogue([ { - text: `${namespace}.outro`, - }, + text: `${namespace}.outro` + } ]) .build(); diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index a9a273c6ec4..18f998192ce 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -4,9 +4,9 @@ import { BerryModifierType, modifierTypes, PokemonHeldItemModifierType } from "# import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -import { PersistentModifierRequirement } from "../mystery-encounter-requirements"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { PersistentModifierRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; @@ -208,7 +208,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = // Calculate boss mon const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 1, + levelAdditiveModifier: 1, pokemonConfigs: [ { species: getPokemonSpecies(Species.GREEDENT), @@ -291,7 +291,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter!; const berryMap = encounter.misc.berryItemsMap; - // Returns 2/5 of the berries stolen from each Pokemon + // Returns 2/5 of the berries stolen to each Pokemon const party = scene.getParty(); party.forEach(pokemon => { const stolenBerries: BerryModifier[] = berryMap.get(pokemon.id); @@ -310,6 +310,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = } } }); + await scene.updateModifiers(true); transitionMysteryEncounterIntroVisuals(scene, true, true, 500); leaveEncounterWithoutBattle(scene, true); diff --git a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts index 9f38b5a4dea..eb43424a8ff 100644 --- a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts +++ b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts @@ -3,9 +3,9 @@ import { modifierTypes } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -import { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "../mystery-encounter-requirements"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { EXTORTION_ABILITIES, EXTORTION_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import { getPokemonSpecies } from "#app/data/pokemon-species"; diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index 7e6914cabdd..523b8598f95 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -17,13 +17,13 @@ import { randSeedInt } from "#app/utils"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getPokemonNameWithAffix } from "#app/messages"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { TrainerSlot } from "#app/data/trainer-config"; -import { applyModifierTypeToPlayerPokemon, getHighestStatPlayerPokemon, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; +import { applyModifierTypeToPlayerPokemon, getEncounterPokemonLevelForWave, getHighestStatPlayerPokemon, getSpriteKeysFromPokemon, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import PokemonData from "#app/system/pokemon-data"; import { BerryModifier } from "#app/modifier/modifier"; import i18next from "#app/plugins/i18n"; @@ -56,12 +56,11 @@ export const BerriesAboundEncounter: MysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter!; // Calculate boss mon - const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0); + const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true); const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true); encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon)); const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 1, pokemonConfigs: [{ level: level, species: bossSpecies, @@ -163,10 +162,10 @@ export const BerriesAboundEncounter: MysteryEncounter = .withOptionPhase(async (scene: BattleScene) => { // Pick race for berries const encounter = scene.currentBattle.mysteryEncounter!; - const fastestPokemon = encounter.misc.fastestPokemon; - const enemySpeed = encounter.misc.enemySpeed; + const fastestPokemon: PlayerPokemon = encounter.misc.fastestPokemon; + const enemySpeed: number = encounter.misc.enemySpeed; const speedDiff = fastestPokemon.getStat(Stat.SPD) / (enemySpeed * 1.1); - const numBerries = encounter.misc.numBerries; + const numBerries: number = encounter.misc.numBerries; const shopOptions: ModifierTypeOption[] = []; for (let i = 0; i < 5; i++) { diff --git a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts index 7fdaec35dc3..202488030ee 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -9,6 +9,7 @@ import { transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { + getRandomPartyMemberFunc, trainerConfigs, TrainerPartyCompoundTemplate, TrainerPartyTemplate, @@ -17,14 +18,12 @@ import { import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PartyMemberStrength } from "#enums/party-member-strength"; import BattleScene from "#app/battle-scene"; -import * as Utils from "#app/utils"; import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { TrainerType } from "#enums/trainer-type"; import { Species } from "#enums/species"; -import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon"; -import { getPokemonSpecies } from "#app/data/pokemon-species"; +import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { LearnMovePhase } from "#app/phases/learn-move-phase"; import { Moves } from "#enums/moves"; @@ -192,6 +191,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = new AttackTypeBoosterHeldItemTypeRequirement(Type.BUG, 1), new TypeRequirement(Type.BUG, false, 1) )) + .withMaxAllowedEncounters(1) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withIntroSpriteConfigs([]) // These are set in onInit() .withAutoHideIntroVisuals(false) @@ -286,7 +286,8 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = // Player gets different rewards depending on the number of bug types they have const numBugTypes = scene.getParty().filter(p => p.isOfType(Type.BUG, true)).length; - encounter.setDialogueToken("numBugTypes", numBugTypes.toString()); + const numBugTypesText = i18next.t(`${namespace}.numBugTypes`, { count: numBugTypes }); + encounter.setDialogueToken("numBugTypes", numBugTypesText); if (numBugTypes < 2) { setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SUPER_LURE, modifierTypes.GREAT_BALL], fillRemaining: false }); @@ -582,16 +583,6 @@ function getTrainerConfigForWave(waveIndex: number) { return config; } -function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void) { - return (scene: BattleScene, level: number, strength: PartyMemberStrength) => { - let species = Utils.randSeedItem(speciesPool); - if (!ignoreEvolution) { - species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength); - } - return scene.addEnemyPokemon(getPokemonSpecies(species), level, trainerSlot, undefined, undefined, postProcess); - }; -} - function doBugTypeMoveTutor(scene: BattleScene): Promise { return new Promise(async resolve => { const moveOptions = scene.currentBattle.mysteryEncounter!.misc.moveTutorOptions; diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index 061d2a33e8a..d930e43c45f 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -5,7 +5,7 @@ import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifi import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PartyMemberStrength } from "#enums/party-member-strength"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; @@ -246,12 +246,12 @@ export const ClowningAroundEncounter: MysteryEncounter = const party = scene.getParty(); let mostHeldItemsPokemon = party[0]; let count = mostHeldItemsPokemon.getHeldItems() - .filter(m => m.isTransferrable && !(m instanceof BerryModifier)) + .filter(m => m.isTransferable && !(m instanceof BerryModifier)) .reduce((v, m) => v + m.stackCount, 0); party.forEach(pokemon => { const nextCount = pokemon.getHeldItems() - .filter(m => m.isTransferrable && !(m instanceof BerryModifier)) + .filter(m => m.isTransferable && !(m instanceof BerryModifier)) .reduce((v, m) => v + m.stackCount, 0); if (nextCount > count) { mostHeldItemsPokemon = pokemon; @@ -276,7 +276,7 @@ export const ClowningAroundEncounter: MysteryEncounter = // Shuffle Transferable held items in the same tier (only shuffles Ultra and Rogue atm) let numUltra = 0; let numRogue = 0; - items.filter(m => m.isTransferrable && !(m instanceof BerryModifier)) + items.filter(m => m.isTransferable && !(m instanceof BerryModifier)) .forEach(m => { const type = m.type.withTierFromPool(); const tier = type.tier ?? ModifierTier.ULTRA; diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index 046e2b2f876..8a0a18d48ea 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -3,8 +3,8 @@ import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/po import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { getPokemonSpecies } from "#app/data/pokemon-species"; @@ -19,7 +19,7 @@ import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter- import { DANCING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { BattlerIndex } from "#app/battle"; -import { catchPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; +import { catchPokemon, getEncounterPokemonLevelForWave, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { PokeballType } from "#enums/pokeball"; import { modifierTypes } from "#app/modifier/modifier-type"; import { LearnMovePhase } from "#app/phases/learn-move-phase"; @@ -107,7 +107,7 @@ export const DancingLessonsEncounter: MysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter!; const species = getPokemonSpecies(Species.ORICORIO); - const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0); + const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); const enemyPokemon = new EnemyPokemon(scene, species, level, TrainerSlot.NONE, false); if (!enemyPokemon.moveset.some(m => m && m.getMove().id === Moves.REVELATION_DANCE)) { if (enemyPokemon.moveset.length < 4) { @@ -133,7 +133,7 @@ export const DancingLessonsEncounter: MysteryEncounter = } const oricorioData = new PokemonData(enemyPokemon); - const oricorio = scene.addEnemyPokemon(species, scene.currentBattle.enemyLevels![0], TrainerSlot.NONE, false, oricorioData); + const oricorio = scene.addEnemyPokemon(species, level, TrainerSlot.NONE, false, oricorioData); // Adds a real Pokemon sprite to the field (required for the animation) scene.getEnemyParty().forEach(enemyPokemon => { @@ -146,7 +146,6 @@ export const DancingLessonsEncounter: MysteryEncounter = encounter.loadAssets.push(oricorio.loadAssets()); const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 1, pokemonConfigs: [{ species: species, dataSource: oricorioData, diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index 212ff6ed1bb..09b058ab7c9 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -5,8 +5,8 @@ import { Species } from "#enums/species"; import BattleScene from "#app/battle-scene"; import { modifierTypes } from "#app/modifier/modifier-type"; import { getPokemonSpecies } from "#app/data/pokemon-species"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, } from "../utils/encounter-phase-utils"; import { getRandomPlayerPokemon, getRandomSpeciesByStarterTier } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; @@ -18,7 +18,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; /** i18n namespace for encounter */ const namespace = "mysteryEncounter:darkDeal"; -/** Exclude Ultra Beasts (inludes Cosmog/Solgaleo/Lunala/Necrozma), Paradox (includes Miraidon/Koraidon), Eternatus, and egg-locked mythicals */ +/** Exclude Ultra Beasts (inludes Cosmog/Solgaleo/Lunala/Necrozma), Paradox (includes Miraidon/Koraidon), Eternatus, and Mythicals */ const excludedBosses = [ Species.NECROZMA, Species.COSMOG, @@ -63,11 +63,24 @@ const excludedBosses = [ Species.CELEBI, Species.DEOXYS, Species.JIRACHI, + Species.DARKRAI, Species.PHIONE, Species.MANAPHY, Species.ARCEUS, + Species.SHAYMIN, Species.VICTINI, + Species.MELOETTA, + Species.KELDEO, + Species.GENESECT, + Species.DIANCIE, + Species.HOOPA, + Species.VOLCANION, + Species.MAGEARNA, + Species.MARSHADOW, + Species.ZERAORA, + Species.ZARUDE, Species.MELTAN, + Species.MELMETAL, Species.PECHARUNT, ]; @@ -151,7 +164,7 @@ export const DarkDealEncounter: MysteryEncounter = // Starter egg tier, 35/50/10/5 %odds for tiers 6/7/8/9+ const roll = randSeedInt(100); const starterTier: number | [number, number] = - roll > 65 ? 6 : roll > 15 ? 7 : roll > 5 ? 8 : [9, 10]; + roll >= 65 ? 6 : roll >= 15 ? 7 : roll >= 5 ? 8 : [9, 10]; const bossSpecies = getPokemonSpecies(getRandomSpeciesByStarterTier(starterTier, excludedBosses, bossTypes)); const pokemonConfig: EnemyPokemonConfig = { species: bossSpecies, diff --git a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts index ed9344d3c95..25959abe19e 100644 --- a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts +++ b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts @@ -4,9 +4,9 @@ import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifi import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement } from "../mystery-encounter-requirements"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; @@ -33,6 +33,8 @@ const OPTION_3_DISALLOWED_MODIFIERS = [ "PokemonBaseStatTotalModifier" ]; +const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 1.5; + /** * Delibird-y encounter. * @see {@link https://github.com/pagefaultgames/pokerogue/issues/3804 | GitHub Issue #3804} @@ -42,7 +44,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DELIBIRDY) .withEncounterTier(MysteryEncounterTier.GREAT) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) - .withSceneRequirement(new MoneyRequirement(0, 2)) // Must have enough money for it to spawn at the very least + .withSceneRequirement(new MoneyRequirement(0, DELIBIRDY_MONEY_PRICE_MULTIPLIER)) // Must have enough money for it to spawn at the very least .withPrimaryPokemonRequirement(new CombinationPokemonRequirement( // Must also have either option 2 or 3 available to spawn new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS), new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true) @@ -93,12 +95,18 @@ export const DelibirdyEncounter: MysteryEncounter = .withOnInit((scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter!; encounter.setDialogueToken("delibirdName", getPokemonSpecies(Species.DELIBIRD).getName()); + + scene.loadBgm("mystery_encounter_delibirdy", "mystery_encounter_delibirdy.mp3"); + return true; + }) + .withOnVisualsStart((scene: BattleScene) => { + scene.fadeAndSwitchBgm("mystery_encounter_delibirdy"); return true; }) .withOption( MysteryEncounterOptionBuilder .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) - .withSceneMoneyRequirement(0, 2) // Must have money to spawn + .withSceneMoneyRequirement(0, DELIBIRDY_MONEY_PRICE_MULTIPLIER) // Must have money to spawn .withDialogue({ buttonLabel: `${namespace}.option.1.label`, buttonTooltip: `${namespace}.option.1.tooltip`, diff --git a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts index e35ca08b6a0..104b46bce8a 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -9,7 +9,7 @@ import { Species } from "#enums/species"; import BattleScene from "#app/battle-scene"; import MysteryEncounter, { MysteryEncounterBuilder, -} from "../mystery-encounter"; +} from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index e0101d60a2a..d03a3c1fcca 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -6,7 +6,7 @@ import { modifierTypes } from "#app/modifier/modifier-type"; import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { Stat } from "#enums/stat"; diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 1861abcc7a4..470c4b96c82 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -3,8 +3,8 @@ import { EnemyPartyConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounte import { AttackTypeBoosterModifierType, modifierTypes, } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { TypeRequirement } from "../mystery-encounter-requirements"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { TypeRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { Species } from "#enums/species"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Gender } from "#app/data/gender"; diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index c163a2fc194..aa11a07f218 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -17,12 +17,12 @@ import { } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MoveRequirement } from "../mystery-encounter-requirements"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { TrainerSlot } from "#app/data/trainer-config"; -import { getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; +import { getEncounterPokemonLevelForWave, getSpriteKeysFromPokemon, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import PokemonData from "#app/system/pokemon-data"; import { BattlerTagType } from "#enums/battler-tag-type"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; @@ -54,12 +54,11 @@ export const FightOrFlightEncounter: MysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter!; // Calculate boss mon - const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0); + const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true); const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true); encounter.setDialogueToken("enemyPokemon", bossPokemon.getNameToRender()); const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 1, pokemonConfigs: [{ level: level, species: bossSpecies, @@ -69,7 +68,7 @@ export const FightOrFlightEncounter: MysteryEncounter = mysteryEncounterBattleEffects: (pokemon: Pokemon) => { queueEncounterMessage(pokemon.scene, `${namespace}.option.1.stat_boost`); // Randomly boost 1 stat 2 stages - pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [randSeedInt(5)], 2)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [randSeedInt(4, 1)], 2)); } }], }; diff --git a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts index a544657e47c..9ca7c7c2865 100644 --- a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts +++ b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts @@ -1,7 +1,7 @@ import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { TrainerSlot } from "#app/data/trainer-config"; import Pokemon, { FieldPosition, PlayerPokemon } from "#app/field/pokemon"; @@ -84,12 +84,7 @@ export const FunAndGamesEncounter: MysteryEncounter = return true; }) .withOnVisualsStart((scene: BattleScene) => { - // Change the bgm - scene.fadeOutBgm(2000, false); - scene.time.delayedCall(2000, () => { - scene.playBgm("mystery_encounter_fun_and_games"); - }); - + scene.fadeAndSwitchBgm("mystery_encounter_fun_and_games"); return true; }) .withOption(MysteryEncounterOptionBuilder @@ -175,7 +170,9 @@ async function summonPlayerPokemon(scene: BattleScene) { const party = scene.getParty(); const chosenIndex = party.indexOf(playerPokemon); if (chosenIndex !== 0) { - [party[chosenIndex], party[0]] = [party[chosenIndex], party[chosenIndex]]; + const leadPokemon = party[0]; + party[0] = playerPokemon; + party[chosenIndex] = leadPokemon; } // Do trainer summon animation diff --git a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts index 1c5a1f009d9..55d4953d438 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -4,7 +4,7 @@ import { ModifierTier } from "#app/modifier/modifier-tier"; import { getPlayerModifierTypeOptions, ModifierPoolType, ModifierTypeOption, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { Species } from "#enums/species"; import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species"; @@ -23,6 +23,7 @@ import { getPokeballAtlasKey, getPokeballTintColor, PokeballType } from "#app/da import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { trainerNamePools } from "#app/data/trainer-names"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { addPokemonDataToDexAndValidateAchievements } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:globalTradeSystem"; @@ -118,17 +119,13 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = return true; }) .withOnVisualsStart((scene: BattleScene) => { - // Change the bgm - scene.fadeOutBgm(1500, false); - scene.time.delayedCall(1500, () => { - scene.playBgm(scene.currentBattle.mysteryEncounter!.misc.bgmKey); - }); - + scene.fadeAndSwitchBgm(scene.currentBattle.mysteryEncounter!.misc.bgmKey); return true; }) .withOption( MysteryEncounterOptionBuilder .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) + .withHasDexProgress(true) .withDialogue({ buttonLabel: `${namespace}.option.1.label`, buttonTooltip: `${namespace}.option.1.tooltip`, @@ -200,6 +197,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon); await showEncounterText(scene, `${namespace}.trade_received`, null, 0, true, 4000); scene.playBgm(encounter.misc.bgmKey); + await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon); await hideTradeBackground(scene); tradedPokemon.destroy(); @@ -210,6 +208,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = .withOption( MysteryEncounterOptionBuilder .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) + .withHasDexProgress(true) .withDialogue({ buttonLabel: `${namespace}.option.2.label`, buttonTooltip: `${namespace}.option.2.tooltip`, @@ -218,8 +217,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter!; const onPokemonSelected = (pokemon: PlayerPokemon) => { // Randomly generate a Wonder Trade pokemon - // const randomTradeOption = generateTradeOption(scene.getParty().map(p => p.species)); - const randomTradeOption = getPokemonSpecies(Species.BURMY); + const randomTradeOption = generateTradeOption(scene.getParty().map(p => p.species)); const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false); // Extra shiny roll at 1/128 odds (boosted by events and charms) if (!tradePokemon.shiny) { @@ -280,7 +278,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = await showTradeBackground(scene); await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon); await showEncounterText(scene, `${namespace}.trade_received`, null, 0, true, 4000); - scene.playBgm(scene.currentBattle.mysteryEncounter!.misc.bgmKey); + scene.playBgm(encounter.misc.bgmKey); + await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon); await hideTradeBackground(scene); tradedPokemon.destroy(); @@ -301,7 +300,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = const onPokemonSelected = (pokemon: PlayerPokemon) => { // Get Pokemon held items and filter for valid ones const validItems = pokemon.getHeldItems().filter((it) => { - return it.isTransferrable; + return it.isTransferable; }); return validItems.map((modifier: PokemonHeldItemModifier) => { @@ -322,7 +321,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = const selectableFilter = (pokemon: Pokemon) => { // If pokemon has items to trade const meetsReqs = pokemon.getHeldItems().filter((it) => { - return it.isTransferrable; + return it.isTransferable; }).length > 0; if (!meetsReqs) { return getEncounterText(scene, `${namespace}.option.3.invalid_selection`) ?? null; diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 0f0538d7542..509ffb11b26 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -3,13 +3,14 @@ import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter-phase-utils"; import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import {PokemonMove} from "#app/field/pokemon"; const OPTION_1_REQUIRED_MOVE = Moves.SURF; const OPTION_2_REQUIRED_MOVE = Moves.FLY; @@ -44,8 +45,8 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with const encounter = scene.currentBattle.mysteryEncounter!; encounter.setDialogueToken("damagePercentage", String(DAMAGE_PERCENTAGE)); - encounter.setDialogueToken("option1RequiredMove", Moves[OPTION_1_REQUIRED_MOVE]); - encounter.setDialogueToken("option2RequiredMove", Moves[OPTION_2_REQUIRED_MOVE]); + encounter.setDialogueToken("option1RequiredMove", new PokemonMove(OPTION_1_REQUIRED_MOVE).getName()); + encounter.setDialogueToken("option2RequiredMove", new PokemonMove(OPTION_2_REQUIRED_MOVE).getName()); return true; }) diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index 230f5242a36..ac257a8975f 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -15,7 +15,7 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PartyMemberStrength } from "#enums/party-member-strength"; import BattleScene from "#app/battle-scene"; import * as Utils from "#app/utils"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; @@ -75,7 +75,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = const hardSpriteKey = hardConfig.getSpriteKey(female, hardConfig.doubleOnly); encounter.enemyPartyConfigs.push({ trainerConfig: hardConfig, - levelAdditiveMultiplier: 1, + levelAdditiveModifier: 1, female: female, }); @@ -87,6 +87,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = ); const e4Template = trainerPartyTemplates.ELITE_FOUR; const brutalConfig = trainerConfigs[brutalTrainerType].clone(); + brutalConfig.title = trainerConfigs[brutalTrainerType].title; brutalConfig.setPartyTemplates(e4Template); // @ts-ignore brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func @@ -97,7 +98,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = const brutalSpriteKey = brutalConfig.getSpriteKey(female, brutalConfig.doubleOnly); encounter.enemyPartyConfigs.push({ trainerConfig: brutalConfig, - levelAdditiveMultiplier: 1.5, + levelAdditiveModifier: 1.5, female: female, }); diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 26e846ed874..18b2db53ba2 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -5,8 +5,8 @@ import { ModifierTier } from "#app/modifier/modifier-tier"; import { randSeedInt } from "#app/utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { getPokemonSpecies } from "#app/data/pokemon-species"; @@ -68,7 +68,7 @@ export const MysteriousChestEncounter: MysteryEncounter = // Calculate boss mon const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 0.5, + levelAdditiveModifier: 0.5, disableSwitch: true, pokemonConfigs: [ { @@ -179,6 +179,7 @@ export const MysteriousChestEncounter: MysteryEncounter = encounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender()); await showEncounterText(scene, `${namespace}.option.1.bad`); transitionMysteryEncounterIntroVisuals(scene, true, true, 500); + setEncounterRewards(scene, { fillRemaining: true }); await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); } } diff --git a/src/data/mystery-encounters/encounters/part-timer-encounter.ts b/src/data/mystery-encounters/encounters/part-timer-encounter.ts index 2abbb53c333..f5486d34ea9 100644 --- a/src/data/mystery-encounters/encounters/part-timer-encounter.ts +++ b/src/data/mystery-encounters/encounters/part-timer-encounter.ts @@ -2,8 +2,8 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/myst import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MoveRequirement } from "../mystery-encounter-requirements"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { Stat } from "#enums/stat"; diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 49abf98cf49..2690460757f 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -1,7 +1,7 @@ import { initSubsequentOptionSelect, leaveEncounterWithoutBattle, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import MysteryEncounterOption, { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { TrainerSlot } from "#app/data/trainer-config"; import { HiddenAbilityRateBoosterModifier, IvScannerModifier } from "#app/modifier/modifier"; @@ -25,7 +25,7 @@ const namespace = "mysteryEncounter:safariZone"; const TRAINER_THROW_ANIMATION_TIMES = [512, 184, 768]; -const SAFARI_MONEY_MULTIPLIER = 2.75; +const SAFARI_MONEY_MULTIPLIER = 2; /** * Safari Zone encounter. diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts index 8ee4782def5..933f184351a 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -5,9 +5,9 @@ import { randSeedInt } from "#app/utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -import { MoneyRequirement } from "../mystery-encounter-requirements"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; @@ -19,6 +19,9 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounter:shadyVitaminDealer"; +const VITAMIN_DEALER_CHEAP_PRICE_MULTIPLIER = 1.5; +const VITAMIN_DEALER_EXPENSIVE_PRICE_MULTIPLIER = 3.5; + /** * Shady Vitamin Dealer encounter. * @see {@link https://github.com/pagefaultgames/pokerogue/issues/3798 | GitHub Issue #3798} @@ -28,7 +31,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.SHADY_VITAMIN_DEALER) .withEncounterTier(MysteryEncounterTier.COMMON) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) - .withSceneRequirement(new MoneyRequirement(0, 1.5)) // Must have the money for at least the cheap deal + .withSceneRequirement(new MoneyRequirement(0, VITAMIN_DEALER_CHEAP_PRICE_MULTIPLIER)) // Must have the money for at least the cheap deal .withPrimaryPokemonHealthRatioRequirement([0.5, 1]) // At least 1 Pokemon must have above half HP .withIntroSpriteConfigs([ { @@ -64,7 +67,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = .withOption( MysteryEncounterOptionBuilder .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) - .withSceneMoneyRequirement(0, 1.5) + .withSceneMoneyRequirement(0, VITAMIN_DEALER_CHEAP_PRICE_MULTIPLIER) .withDialogue({ buttonLabel: `${namespace}.option.1.label`, buttonTooltip: `${namespace}.option.1.tooltip`, @@ -115,7 +118,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = await applyModifierTypeToPlayerPokemon(scene, chosenPokemon, modType); } - leaveEncounterWithoutBattle(scene); + leaveEncounterWithoutBattle(scene, true); }) .withPostOptionPhase(async (scene: BattleScene) => { // Damage and status applied after dealer leaves (to make thematic sense) @@ -142,7 +145,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = .withOption( MysteryEncounterOptionBuilder .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) - .withSceneMoneyRequirement(0, 3.5) + .withSceneMoneyRequirement(0, VITAMIN_DEALER_EXPENSIVE_PRICE_MULTIPLIER) .withDialogue({ buttonLabel: `${namespace}.option.2.label`, buttonTooltip: `${namespace}.option.2.tooltip`, @@ -193,7 +196,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = await applyModifierTypeToPlayerPokemon(scene, chosenPokemon, modType); } - leaveEncounterWithoutBattle(scene); + leaveEncounterWithoutBattle(scene, true); }) .withPostOptionPhase(async (scene: BattleScene) => { // Status applied after dealer leaves (to make thematic sense) diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index b9f08b12ffd..bfccc46ee0f 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -1,22 +1,24 @@ import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; -import { modifierTypes } from "#app/modifier/modifier-type"; +import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import BattleScene from "#app/battle-scene"; import { StatusEffect } from "#app/data/status-effect"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -import { MoveRequirement } from "../mystery-encounter-requirements"; -import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, } from "../utils/encounter-phase-utils"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; +import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterExp, setEncounterRewards, } from "../utils/encounter-phase-utils"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { Moves } from "#enums/moves"; import { BattlerIndex } from "#app/battle"; -import { PokemonMove } from "#app/field/pokemon"; +import { AiType, PokemonMove } from "#app/field/pokemon"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { PartyHealPhase } from "#app/phases/party-heal-phase"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { BerryType } from "#enums/berry-type"; +import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data"; /** i18n namespace for the encounter */ const namespace = "mysteryEncounter:slumberingSnorlax"; @@ -38,7 +40,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = fileRoot: "pokemon", hasShadow: true, tint: 0.25, - scale: 1.5, + scale: 1.25, repeat: true, y: 5, }, @@ -58,10 +60,22 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = species: bossSpecies, isBoss: true, status: [StatusEffect.SLEEP, 5], // Extra turns on timer for Snorlax's start of fight moves - moveSet: [Moves.REST, Moves.SLEEP_TALK, Moves.CRUNCH, Moves.GIGA_IMPACT] + moveSet: [Moves.REST, Moves.SLEEP_TALK, Moves.CRUNCH, Moves.GIGA_IMPACT], + modifierConfigs: [ + { + modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType, + stackCount: 2 + }, + { + modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.ENIGMA]) as PokemonHeldItemModifierType, + stackCount: 2 + }, + ], + mysteryEncounterPokemonData: new MysteryEncounterPokemonData({ spriteScale: 1.25 }), + aiType: AiType.SMART // Required to ensure Snorlax uses Sleep Talk while it is asleep }; const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 0.5, + levelAdditiveModifier: 0.5, pokemonConfigs: [pokemonConfig], }; encounter.enemyPartyConfigs = [config]; diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts index bf976458fdd..10b0e5222b3 100644 --- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts +++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts @@ -2,8 +2,8 @@ import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig import { randSeedInt } from "#app/utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MoneyRequirement, WaveModulusRequirement } from "../mystery-encounter-requirements"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MoneyRequirement, WaveModulusRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import Pokemon, { EnemyPokemon } from "#app/field/pokemon"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; @@ -20,12 +20,13 @@ import { getPokemonNameWithAffix } from "#app/messages"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { Stat } from "#enums/stat"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { getEncounterPokemonLevelForWave, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounter:teleportingHijinks"; -const MONEY_COST_MULTIPLIER = 2.5; -const BIOME_CANDIDATES = [Biome.SPACE, Biome.FAIRY_CAVE, Biome.LABORATORY, Biome.ISLAND]; +const MONEY_COST_MULTIPLIER = 1.75; +const BIOME_CANDIDATES = [Biome.SPACE, Biome.FAIRY_CAVE, Biome.LABORATORY, Biome.ISLAND, Biome.WASTELAND, Biome.DOJO]; const MACHINE_INTERFACING_TYPES = [Type.ELECTRIC, Type.STEEL]; /** @@ -130,7 +131,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter!; // Init enemy - const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0); + const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true); const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true); encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon)); @@ -166,7 +167,7 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) { await showEncounterText(scene, `${namespace}.attacked`); // Init enemy - const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0); + const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true); const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true); encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon)); diff --git a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts new file mode 100644 index 00000000000..d4795c90453 --- /dev/null +++ b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts @@ -0,0 +1,549 @@ +import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { trainerConfigs } from "#app/data/trainer-config"; +import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import BattleScene from "#app/battle-scene"; +import { randSeedShuffle } from "#app/utils"; +import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { Biome } from "#enums/biome"; +import { TrainerType } from "#enums/trainer-type"; +import i18next from "i18next"; +import { Species } from "#enums/species"; +import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species"; +import { Nature } from "#enums/nature"; +import { Moves } from "#enums/moves"; +import { Type } from "#app/data/type"; +import { Stat } from "#enums/stat"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +import { IEggOptions } from "#app/data/egg"; +import { EggSourceType } from "#enums/egg-source-types"; +import { EggTier } from "#enums/egg-type"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { achvs } from "#app/system/achv"; + +/** the i18n namespace for the encounter */ +const namespace = "mysteryEncounter:expertPokemonBreeder"; + +const trainerNameKey = "trainerNames:expert_pokemon_breeder"; + +const FIRST_STAGE_EVOLUTION_WAVE = 30; +const SECOND_STAGE_EVOLUTION_WAVE = 45; +const FINAL_STAGE_EVOLUTION_WAVE = 60; + +const FRIENDSHIP_ADDED = 20; + +class BreederSpeciesEvolution { + species: Species; + evolution: number; + + constructor(species: Species, evolution: number) { + this.species = species; + this.evolution = evolution; + } +} + +const POOL_1_POKEMON: (Species | BreederSpeciesEvolution)[][] = [ + [Species.MUNCHLAX, new BreederSpeciesEvolution(Species.SNORLAX, SECOND_STAGE_EVOLUTION_WAVE)], + [Species.HAPPINY, new BreederSpeciesEvolution(Species.CHANSEY, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.BLISSEY, FINAL_STAGE_EVOLUTION_WAVE)], + [Species.MAGBY, new BreederSpeciesEvolution(Species.MAGMAR, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.MAGMORTAR, FINAL_STAGE_EVOLUTION_WAVE)], + [Species.ELEKID, new BreederSpeciesEvolution(Species.ELECTABUZZ, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ELECTIVIRE, FINAL_STAGE_EVOLUTION_WAVE)], + [Species.RIOLU, new BreederSpeciesEvolution(Species.LUCARIO, SECOND_STAGE_EVOLUTION_WAVE)], + [Species.BUDEW, new BreederSpeciesEvolution(Species.ROSELIA, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ROSERADE, FINAL_STAGE_EVOLUTION_WAVE)], + [Species.TOXEL, new BreederSpeciesEvolution(Species.TOXTRICITY, SECOND_STAGE_EVOLUTION_WAVE)], + [Species.MIME_JR, new BreederSpeciesEvolution(Species.GALAR_MR_MIME, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.MR_RIME, FINAL_STAGE_EVOLUTION_WAVE)] +]; + +const POOL_2_POKEMON: (Species | BreederSpeciesEvolution)[][] = [ + [Species.PICHU, new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.RAICHU, FINAL_STAGE_EVOLUTION_WAVE)], + [Species.PICHU, new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ALOLA_RAICHU, FINAL_STAGE_EVOLUTION_WAVE)], + [Species.JYNX], + [Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONLEE, SECOND_STAGE_EVOLUTION_WAVE)], + [Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONCHAN, SECOND_STAGE_EVOLUTION_WAVE)], + [Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONTOP, SECOND_STAGE_EVOLUTION_WAVE)], + [Species.IGGLYBUFF, new BreederSpeciesEvolution(Species.JIGGLYPUFF, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.WIGGLYTUFF, FINAL_STAGE_EVOLUTION_WAVE)], + [Species.AZURILL, new BreederSpeciesEvolution(Species.MARILL, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.AZUMARILL, FINAL_STAGE_EVOLUTION_WAVE)], + [Species.WYNAUT, new BreederSpeciesEvolution(Species.WOBBUFFET, SECOND_STAGE_EVOLUTION_WAVE)], + [Species.CHINGLING, new BreederSpeciesEvolution(Species.CHIMECHO, SECOND_STAGE_EVOLUTION_WAVE)], + [Species.BONSLY, new BreederSpeciesEvolution(Species.SUDOWOODO, SECOND_STAGE_EVOLUTION_WAVE)], + [Species.MANTYKE, new BreederSpeciesEvolution(Species.MANTINE, SECOND_STAGE_EVOLUTION_WAVE)] +]; + +/** + * The Expert Pokémon Breeder encounter. + * @see {@link https://github.com/pagefaultgames/pokerogue/issues/3818 | GitHub Issue #3818} + * @see For biome requirements check {@linkcode mysteryEncountersByBiome} + */ +export const TheExpertPokemonBreederEncounter: MysteryEncounter = + MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER) + .withEncounterTier(MysteryEncounterTier.ULTRA) + .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) + .withScenePartySizeRequirement(4, 6, true) // Must have at least 4 legal pokemon in party + .withIntroSpriteConfigs([]) // These are set in onInit() + .withIntroDialogue([ + { + text: `${namespace}.intro`, + }, + { + speaker: trainerNameKey, + text: `${namespace}.intro_dialogue`, + }, + ]) + .withOnInit((scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter!; + const waveIndex = scene.currentBattle.waveIndex; + // Calculates what trainers are available for battle in the encounter + + // If player is in space biome, uses special "Space" version of the trainer + encounter.enemyPartyConfigs = [ + getPartyConfig(scene) + ]; + + const cleffaSpecies = waveIndex < FIRST_STAGE_EVOLUTION_WAVE ? Species.CLEFFA : waveIndex < FINAL_STAGE_EVOLUTION_WAVE ? Species.CLEFAIRY : Species.CLEFABLE; + encounter.spriteConfigs = [ + { + spriteKey: cleffaSpecies.toString(), + fileRoot: "pokemon", + hasShadow: true, + repeat: true, + x: 14, + y: -2, + yShadow: -2 + }, + { + spriteKey: "expert_pokemon_breeder", + fileRoot: "trainer", + hasShadow: true, + x: -14, + y: 4, + yShadow: 2 + }, + ]; + + // Determine the 3 pokemon the player can battle with + let partyCopy = scene.getParty().slice(0); + partyCopy = partyCopy + .filter(p => p.isAllowedInBattle()) + .sort((a, b) => a.friendship - b.friendship); + + const pokemon1 = partyCopy[0]; + const pokemon2 = partyCopy[1]; + const pokemon3 = partyCopy[2]; + encounter.setDialogueToken("pokemon1Name", pokemon1.getNameToRender()); + encounter.setDialogueToken("pokemon2Name", pokemon2.getNameToRender()); + encounter.setDialogueToken("pokemon3Name", pokemon3.getNameToRender()); + + // Dialogue and egg calcs for Pokemon 1 + const [pokemon1CommonEggs, pokemon1RareEggs] = calculateEggRewardsForPokemon(pokemon1); + let pokemon1Tooltip = getEncounterText(scene, `${namespace}.option.1.tooltip_base`)!; + if (pokemon1RareEggs > 0) { + const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon1RareEggs, rarity: i18next.t("egg:greatTier") }); + pokemon1Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText }); + encounter.setDialogueToken("pokemon1RareEggs", eggsText); + } + if (pokemon1CommonEggs > 0) { + const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon1CommonEggs, rarity: i18next.t("egg:defaultTier") }); + pokemon1Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText }); + encounter.setDialogueToken("pokemon1CommonEggs", eggsText); + } + encounter.options[0].dialogue!.buttonTooltip = pokemon1Tooltip; + + // Dialogue and egg calcs for Pokemon 2 + const [pokemon2CommonEggs, pokemon2RareEggs] = calculateEggRewardsForPokemon(pokemon2); + let pokemon2Tooltip = getEncounterText(scene, `${namespace}.option.2.tooltip_base`)!; + if (pokemon2RareEggs > 0) { + const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon2RareEggs, rarity: i18next.t("egg:greatTier") }); + pokemon2Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText }); + encounter.setDialogueToken("pokemon2RareEggs", eggsText); + } + if (pokemon2CommonEggs > 0) { + const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon2CommonEggs, rarity: i18next.t("egg:defaultTier") }); + pokemon2Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText }); + encounter.setDialogueToken("pokemon1CommonEggs", eggsText); + } + encounter.options[1].dialogue!.buttonTooltip = pokemon2Tooltip; + + // Dialogue and egg calcs for Pokemon 3 + const [pokemon3CommonEggs, pokemon3RareEggs] = calculateEggRewardsForPokemon(pokemon3); + let pokemon3Tooltip = getEncounterText(scene, `${namespace}.option.3.tooltip_base`)!; + if (pokemon3RareEggs > 0) { + const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon3RareEggs, rarity: i18next.t("egg:greatTier") }); + pokemon3Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText }); + encounter.setDialogueToken("pokemon3RareEggs", eggsText); + } + if (pokemon3CommonEggs > 0) { + const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon3CommonEggs, rarity: i18next.t("egg:defaultTier") }); + pokemon3Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText }); + encounter.setDialogueToken("pokemon3CommonEggs", eggsText); + } + encounter.options[2].dialogue!.buttonTooltip = pokemon3Tooltip; + + encounter.misc = { + pokemon1, + pokemon1CommonEggs, + pokemon1RareEggs, + pokemon2, + pokemon2CommonEggs, + pokemon2RareEggs, + pokemon3, + pokemon3CommonEggs, + pokemon3RareEggs + }; + + return true; + }) + .withTitle(`${namespace}.title`) + .withDescription(`${namespace}.description`) + .withQuery(`${namespace}.query`) + .withOption( + MysteryEncounterOptionBuilder + .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) + .withDialogue({ + buttonLabel: `${namespace}.option.1.label`, + selected: [ + { + speaker: trainerNameKey, + text: `${namespace}.option.selected`, + }, + ], + }) + .withOptionPhase(async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter!; + // Spawn battle with first pokemon + const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; + + const { pokemon1, pokemon1CommonEggs, pokemon1RareEggs } = encounter.misc; + encounter.setDialogueToken("chosenPokemon", pokemon1.getNameToRender()); + const eggOptions = getEggOptions(scene, pokemon1CommonEggs, pokemon1RareEggs); + setEncounterRewards(scene, { fillRemaining: true }, eggOptions); + + // Remove all Pokemon from the party except the chosen Pokemon + removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon1); + + // Configure outro dialogue for egg rewards + encounter.dialogue.outro = [ + { + speaker: trainerNameKey, + text: `${namespace}.outro`, + }, + ]; + if (encounter.dialogueTokens.hasOwnProperty("pokemon1CommonEggs")) { + encounter.dialogue.outro.push({ + text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon1CommonEggs"] }), + }); + } + if (encounter.dialogueTokens.hasOwnProperty("pokemon1RareEggs")) { + encounter.dialogue.outro.push({ + text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon1RareEggs"] }), + }); + } + + initBattleWithEnemyConfig(scene, config); + }) + .withPostOptionPhase(async (scene: BattleScene) => { + // Give achievement if in Space biome + checkAchievement(scene); + // Give 20 friendship to the chosen pokemon + scene.currentBattle.mysteryEncounter!.misc.pokemon1.addFriendship(FRIENDSHIP_ADDED); + await restorePartyAndHeldItems(scene); + }) + .build() + ) + .withOption( + MysteryEncounterOptionBuilder + .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) + .withDialogue({ + buttonLabel: `${namespace}.option.2.label`, + selected: [ + { + speaker: trainerNameKey, + text: `${namespace}.option.selected`, + }, + ], + }) + .withOptionPhase(async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter!; + // Spawn battle with second pokemon + const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; + + const { pokemon2, pokemon2CommonEggs, pokemon2RareEggs } = encounter.misc; + encounter.setDialogueToken("chosenPokemon", pokemon2.getNameToRender()); + const eggOptions = getEggOptions(scene, pokemon2CommonEggs, pokemon2RareEggs); + setEncounterRewards(scene, { fillRemaining: true }, eggOptions); + + // Remove all Pokemon from the party except the chosen Pokemon + removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon2); + + // Configure outro dialogue for egg rewards + encounter.dialogue.outro = [ + { + speaker: trainerNameKey, + text: `${namespace}.outro`, + }, + ]; + if (encounter.dialogueTokens.hasOwnProperty("pokemon2CommonEggs")) { + encounter.dialogue.outro.push({ + text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon2CommonEggs"] }), + }); + } + if (encounter.dialogueTokens.hasOwnProperty("pokemon2RareEggs")) { + encounter.dialogue.outro.push({ + text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon2RareEggs"] }), + }); + } + + initBattleWithEnemyConfig(scene, config); + }) + .withPostOptionPhase(async (scene: BattleScene) => { + // Give achievement if in Space biome + checkAchievement(scene); + // Give 20 friendship to the chosen pokemon + scene.currentBattle.mysteryEncounter!.misc.pokemon2.addFriendship(FRIENDSHIP_ADDED); + await restorePartyAndHeldItems(scene); + }) + .build() + ) + .withOption( + MysteryEncounterOptionBuilder + .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) + .withDialogue({ + buttonLabel: `${namespace}.option.3.label`, + selected: [ + { + speaker: trainerNameKey, + text: `${namespace}.option.selected`, + }, + ], + }) + .withOptionPhase(async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter!; + // Spawn battle with third pokemon + const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; + + const { pokemon3, pokemon3CommonEggs, pokemon3RareEggs } = encounter.misc; + encounter.setDialogueToken("chosenPokemon", pokemon3.getNameToRender()); + const eggOptions = getEggOptions(scene, pokemon3CommonEggs, pokemon3RareEggs); + setEncounterRewards(scene, { fillRemaining: true }, eggOptions); + + // Remove all Pokemon from the party except the chosen Pokemon + removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon3); + + // Configure outro dialogue for egg rewards + encounter.dialogue.outro = [ + { + speaker: trainerNameKey, + text: `${namespace}.outro`, + }, + ]; + if (encounter.dialogueTokens.hasOwnProperty("pokemon3CommonEggs")) { + encounter.dialogue.outro.push({ + text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon3CommonEggs"] }), + }); + } + if (encounter.dialogueTokens.hasOwnProperty("pokemon3RareEggs")) { + encounter.dialogue.outro.push({ + text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon3RareEggs"] }), + }); + } + + initBattleWithEnemyConfig(scene, config); + }) + .withPostOptionPhase(async (scene: BattleScene) => { + // Give achievement if in Space biome + checkAchievement(scene); + // Give 20 friendship to the chosen pokemon + scene.currentBattle.mysteryEncounter!.misc.pokemon3.addFriendship(FRIENDSHIP_ADDED); + await restorePartyAndHeldItems(scene); + }) + .build() + ) + .withOutroDialogue([ + { + text: `${namespace}.outro`, + }, + ]) + .build(); + +function getPartyConfig(scene: BattleScene): EnemyPartyConfig { + // Bug type superfan trainer config + const waveIndex = scene.currentBattle.waveIndex; + const breederConfig = trainerConfigs[TrainerType.EXPERT_POKEMON_BREEDER].clone(); + breederConfig.name = i18next.t(trainerNameKey); + + // First mon is *always* this special cleffa + const cleffaSpecies = waveIndex < FIRST_STAGE_EVOLUTION_WAVE ? Species.CLEFFA : waveIndex < FINAL_STAGE_EVOLUTION_WAVE ? Species.CLEFAIRY : Species.CLEFABLE; + const baseConfig: EnemyPartyConfig = { + trainerType: TrainerType.EXPERT_POKEMON_BREEDER, + pokemonConfigs: [ + { + nickname: i18next.t(`${namespace}.cleffa_1_nickname`, { speciesName: getPokemonSpecies(cleffaSpecies).getName() }), + species: getPokemonSpecies(cleffaSpecies), + isBoss: false, + abilityIndex: 1, // Magic Guard + shiny: false, + nature: Nature.ADAMANT, + moveSet: [Moves.METEOR_MASH, Moves.FIRE_PUNCH, Moves.ICE_PUNCH, Moves.THUNDER_PUNCH], + ivs: [31, 31, 31, 31, 31, 31], + modifierConfigs: [ + { + modifier: generateModifierType(scene, modifierTypes.TERA_SHARD, [Type.STEEL]) as PokemonHeldItemModifierType, + }, + { + modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [Stat.ATK]) as PokemonHeldItemModifierType, + stackCount: 1 + Math.floor(waveIndex / 20), // +1 Protein every 20 waves + }, + { + modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [Stat.SPD]) as PokemonHeldItemModifierType, + stackCount: 1 + Math.floor(waveIndex / 40), // +1 Carbos every 40 waves + }, + ] + } + ] + }; + + if (scene.arena.biomeType === Biome.SPACE) { + // All 3 members always Cleffa line, but different configs + baseConfig.pokemonConfigs!.push({ + nickname: i18next.t(`${namespace}.cleffa_2_nickname`, { speciesName: getPokemonSpecies(cleffaSpecies).getName() }), + species: getPokemonSpecies(cleffaSpecies), + isBoss: false, + abilityIndex: 1, // Magic Guard + shiny: true, + variant: 1, + nature: Nature.MODEST, + moveSet: [Moves.MOONBLAST, Moves.MYSTICAL_FIRE, Moves.ICE_BEAM, Moves.THUNDERBOLT], + ivs: [31, 31, 31, 31, 31, 31] + }, + { + nickname: i18next.t(`${namespace}.cleffa_3_nickname`, { speciesName: getPokemonSpecies(cleffaSpecies).getName() }), + species: getPokemonSpecies(cleffaSpecies), + isBoss: false, + abilityIndex: 2, // Friend Guard / Unaware + shiny: true, + variant: 2, + nature: Nature.BOLD, + moveSet: [Moves.TRI_ATTACK, Moves.STORED_POWER, Moves.TAKE_HEART, Moves.MOONLIGHT], + ivs: [31, 31, 31, 31, 31, 31] + }); + } else { + // Second member from pool 1 + const pool1Species = getSpeciesFromPool(POOL_1_POKEMON, waveIndex); + // Third member from pool 2 + const pool2Species = getSpeciesFromPool(POOL_2_POKEMON, waveIndex); + + baseConfig.pokemonConfigs!.push({ + species: getPokemonSpecies(pool1Species), + isBoss: false, + ivs: [31, 31, 31, 31, 31, 31] + }, + { + species: getPokemonSpecies(pool2Species), + isBoss: false, + ivs: [31, 31, 31, 31, 31, 31] + }); + } + + return baseConfig; +} + +function getSpeciesFromPool(speciesPool: (Species | BreederSpeciesEvolution)[][], waveIndex: number): Species { + const poolCopy = speciesPool.slice(0); + randSeedShuffle(poolCopy); + const speciesEvolutions = poolCopy.pop()!.slice(0); + let speciesObject = speciesEvolutions.pop()!; + while (speciesObject instanceof BreederSpeciesEvolution && speciesObject.evolution > waveIndex) { + speciesObject = speciesEvolutions.pop()!; + } + return speciesObject instanceof BreederSpeciesEvolution ? speciesObject.species : speciesObject; +} + +function calculateEggRewardsForPokemon(pokemon: PlayerPokemon): [number, number] { + const bst = pokemon.calculateBaseStats().reduce((a, b) => a + b, 0); + // 1 point for every 20 points below 680 BST the pokemon is, (max 18, min 1) + const pointsFromBst = Math.min(Math.max(Math.floor((680 - bst) / 20), 1), 18); + + const rootSpecies = pokemon.species.getRootSpeciesId(true); + let pointsFromStarterTier = 0; + // 2 points for every 1 below 7 that the pokemon's starter tier is (max 12, min 0) + if (speciesStarters.hasOwnProperty(rootSpecies)) { + const starterTier = speciesStarters[rootSpecies]; + pointsFromStarterTier = Math.min(Math.max(Math.floor(7 - starterTier) * 2, 0), 12); + } + + // Maximum of 30 points + const totalPoints = Math.min(pointsFromStarterTier + pointsFromBst, 30); + + // 1 Rare egg for every 6 points + const numRares = Math.floor(totalPoints / 6); + // 1 Common egg for every point leftover + const numCommons = totalPoints % 6; + + return [numCommons, numRares]; +} + +function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number) { + const eggDescription = i18next.t(`${namespace}.title`) + ":\n" + i18next.t(trainerNameKey); + const eggOptions: IEggOptions[] = []; + + if (commonEggs > 0) { + for (let i = 0; i < commonEggs; i++) { + eggOptions.push({ + scene, + pulled: false, + sourceType: EggSourceType.EVENT, + eggDescriptor: eggDescription, + tier: EggTier.COMMON + }); + } + } + if (rareEggs > 0) { + for (let i = 0; i < rareEggs; i++) { + eggOptions.push({ + scene, + pulled: false, + sourceType: EggSourceType.EVENT, + eggDescriptor: eggDescription, + tier: EggTier.GREAT + }); + } + } + + return eggOptions; +} + +function removePokemonFromPartyAndStoreHeldItems(scene: BattleScene, encounter: MysteryEncounter, chosenPokemon: PlayerPokemon) { + const party = scene.getParty(); + const chosenIndex = party.indexOf(chosenPokemon); + party[chosenIndex] = party[0]; + party[0] = chosenPokemon; + encounter.misc.originalParty = scene.getParty().slice(1); + encounter.misc.originalPartyHeldItems = encounter.misc.originalParty + .map(p => p.getHeldItems()); + scene["party"] = [ + chosenPokemon + ]; +} + +function checkAchievement(scene: BattleScene) { + if (scene.arena.biomeType === Biome.SPACE) { + scene.validateAchv(achvs.BREEDERS_IN_SPACE); + } +} + +async function restorePartyAndHeldItems(scene: BattleScene) { + const encounter = scene.currentBattle.mysteryEncounter!; + // Restore original party + scene.getParty().push(...encounter.misc.originalParty); + + // Restore held items + const originalHeldItems = encounter.misc.originalPartyHeldItems; + originalHeldItems.forEach(pokemonHeldItemsList => { + pokemonHeldItemsList.forEach(heldItem => { + scene.addModifier(heldItem, true, false, false, true); + }); + }); + await scene.updateModifiers(true); +} diff --git a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts index 16b0c421bd4..c26c6aa3b7f 100644 --- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts @@ -2,8 +2,8 @@ import { leaveEncounterWithoutBattle, transitionMysteryEncounterIntroVisuals, up import { isNullOrUndefined, randSeedInt } from "#app/utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MoneyRequirement } from "../mystery-encounter-requirements"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { catchPokemon, getRandomSpeciesByStarterTier, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species"; import { Species } from "#enums/species"; @@ -15,11 +15,15 @@ import PokemonData from "#app/system/pokemon-data"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { Abilities } from "#enums/abilities"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounter:pokemonSalesman"; -const MAX_POKEMON_PRICE_MULTIPLIER = 6; +const MAX_POKEMON_PRICE_MULTIPLIER = 4; + +/** Odds of shiny magikarp will be 1/value */ +const SHINY_MAGIKARP_WEIGHT = 100; /** * Pokemon Salesman encounter. @@ -58,12 +62,12 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = const tries = 0; // Reroll any species that don't have HAs - while (isNullOrUndefined(species.abilityHidden) && tries < 5) { + while ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) && tries < 5) { species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5])); } let pokemon: PlayerPokemon; - if (isNullOrUndefined(species.abilityHidden) || randSeedInt(100) === 0) { + if (randSeedInt(SHINY_MAGIKARP_WEIGHT) === 0 || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) { // If no HA mon found or you roll 1%, give shiny Magikarp species = getPokemonSpecies(Species.MAGIKARP); const hiddenIndex = species.ability2 ? 2 : 1; diff --git a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts index 047aa0d83f6..55cb10644e8 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -2,7 +2,7 @@ import { EnemyPartyConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounte import { modifierTypes, PokemonHeldItemModifierType, } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Species } from "#enums/species"; import { Nature } from "#app/data/nature"; @@ -36,6 +36,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = .withEncounterTier(MysteryEncounterTier.GREAT) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withScenePartySizeRequirement(3, 6) // Must have at least 3 pokemon in party + .withMaxAllowedEncounters(1) .withHideWildIntroMessage(true) .withAutoHideIntroVisuals(false) .withIntroSpriteConfigs([ @@ -70,7 +71,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = // Calculate boss mon const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 1, + levelAdditiveModifier: 1, disableSwitch: true, pokemonConfigs: [ { @@ -159,6 +160,11 @@ export const TheStrongStuffEncounter: MysteryEncounter = encounter.setDialogueToken("increaseValue", BST_INCREASE_VALUE.toString()); await showEncounterText(scene, `${namespace}.option.1.selected_2`, null, undefined, true); + encounter.dialogue.outro = [ + { + text: `${namespace}.outro`, + } + ]; setEncounterRewards(scene, { fillRemaining: true }); leaveEncounterWithoutBattle(scene, true); return true; @@ -192,6 +198,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = ignorePp: true }); + encounter.dialogue.outro = []; transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); } diff --git a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts index 902aefcb490..60061efbc7a 100644 --- a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts @@ -2,7 +2,7 @@ import { EnemyPartyConfig, generateModifierType, generateModifierTypeOption, ini import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { TrainerType } from "#enums/trainer-type"; import { Species } from "#enums/species"; @@ -146,7 +146,7 @@ async function spawnNextTrainerOrEndEncounter(scene: BattleScene) { // Give 10x Voucher const newModifier = modifierTypes.VOUCHER_PREMIUM().newModifier(); - scene.addModifier(newModifier); + await scene.addModifier(newModifier); scene.playSound("item_fanfare"); await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name })); diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index 6c0f1706fa5..33d841b7f02 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -3,7 +3,7 @@ import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattl import { getNatureName, Nature } from "#app/data/nature"; import { speciesStarters } from "#app/data/pokemon-species"; import Pokemon, { PlayerPokemon } from "#app/field/pokemon"; -import { PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/modifier/modifier"; +import { PokemonHeldItemModifier } from "#app/modifier/modifier"; import { AbilityAttr } from "#app/system/game-data"; import PokemonData from "#app/system/pokemon-data"; import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; @@ -11,8 +11,8 @@ import { isNullOrUndefined, randSeedShuffle } from "#app/utils"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { getEncounterText, queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; @@ -34,6 +34,7 @@ export const TrainingSessionEncounter: MysteryEncounter = .withEncounterTier(MysteryEncounterTier.ULTRA) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withScenePartySizeRequirement(2, 6, true) // Must have at least 2 unfainted pokemon in party + .withFleeAllowed(false) .withHideWildIntroMessage(true) .withIntroSpriteConfigs([ { @@ -97,12 +98,7 @@ export const TrainingSessionEncounter: MysteryEncounter = 5 ); const modifiers = new ModifiersHolder(); - const config = getEnemyConfig( - scene, - playerPokemon, - segments, - modifiers - ); + const config = getEnemyConfig(scene, playerPokemon, segments, modifiers); scene.removePokemonFromPlayerParty(playerPokemon, false); const onBeforeRewardsPhase = () => { @@ -163,6 +159,7 @@ export const TrainingSessionEncounter: MysteryEncounter = // Add pokemon and mods back scene.getParty().push(playerPokemon); for (const mod of modifiers.value) { + mod.pokemonId = playerPokemon.id; scene.addModifier(mod, true, false, false, true); } scene.updateModifiers(true); @@ -230,17 +227,9 @@ export const TrainingSessionEncounter: MysteryEncounter = // Spawn medium training session with chosen pokemon // Every 40 waves, add +1 boss segment, capping at 6 - const segments = Math.min( - 2 + Math.floor(scene.currentBattle.waveIndex / 40), - 6 - ); + const segments = Math.min(2 + Math.floor(scene.currentBattle.waveIndex / 40), 6); const modifiers = new ModifiersHolder(); - const config = getEnemyConfig( - scene, - playerPokemon, - segments, - modifiers - ); + const config = getEnemyConfig(scene, playerPokemon, segments, modifiers); scene.removePokemonFromPlayerParty(playerPokemon, false); const onBeforeRewardsPhase = () => { @@ -377,6 +366,7 @@ export const TrainingSessionEncounter: MysteryEncounter = // Add pokemon and mods back scene.getParty().push(playerPokemon); for (const mod of modifiers.value) { + mod.pokemonId = playerPokemon.id; scene.addModifier(mod, true, false, false, true); } scene.updateModifiers(true); @@ -410,10 +400,12 @@ function getEnemyConfig(scene: BattleScene, playerPokemon: PlayerPokemon, segmen playerPokemon.resetSummonData(); // Passes modifiers by reference - modifiers.value = playerPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier)); + modifiers.value = playerPokemon.getHeldItems(); const modifierConfigs = modifiers.value.map((mod) => { return { - modifier: mod + modifier: mod.clone(), + isTransferable: false, + stackCount: mod.stackCount }; }) as HeldModifierConfig[]; diff --git a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts index ec6291f2a8c..d295c8ab548 100644 --- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts +++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts @@ -2,8 +2,8 @@ import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierType, initBattleW import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { Species } from "#enums/species"; @@ -67,7 +67,7 @@ export const TrashToTreasureEncounter: MysteryEncounter = moveSet: [Moves.PAYBACK, Moves.GUNK_SHOT, Moves.STOMPING_TANTRUM, Moves.DRAIN_PUNCH] }; const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 1, + levelAdditiveModifier: 1, pokemonConfigs: [pokemonConfig], disableSwitch: true }; diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index f9148b87f9b..0816c9cd2a6 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -5,8 +5,8 @@ import Pokemon, { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; import { getPartyLuckValue } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MoveRequirement, PersistentModifierRequirement } from "../mystery-encounter-requirements"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MoveRequirement, PersistentModifierRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { TrainerSlot } from "#app/data/trainer-config"; diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index 476cc98f503..ed8986d99bb 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -2,8 +2,8 @@ import { Type } from "#app/data/type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import BattleScene from "#app/battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { leaveEncounterWithoutBattle, setEncounterRewards, } from "../utils/encounter-phase-utils"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; @@ -14,7 +14,7 @@ import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, Pokemo import { achvs } from "#app/system/achv"; import { speciesEggMoves } from "#app/data/egg-moves"; import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data"; -import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; import { doPokemonTransformationSequence, TransformationScreenPosition } from "#app/data/mystery-encounters/utils/encounter-transformation-sequence"; @@ -130,12 +130,7 @@ export const WeirdDreamEncounter: MysteryEncounter = return true; }) .withOnVisualsStart((scene: BattleScene) => { - // Change the bgm - scene.fadeOutBgm(3000, false); - scene.time.delayedCall(3000, () => { - scene.playBgm("mystery_encounter_weird_dream"); - }); - + scene.fadeAndSwitchBgm("mystery_encounter_weird_dream"); return true; }) .withOption( @@ -340,7 +335,7 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon const newStarterUnlocked = await scene.gameData.setPokemonCaught(newPokemon, true, false, false); if (newStarterUnlocked) { atLeastOneNewStarter = true; - queueEncounterMessage(scene, i18next.t("battle:addedAsAStarter", { pokemonName: getPokemonSpecies(speciesRootForm).getName() })); + await showEncounterText(scene, i18next.t("battle:addedAsAStarter", { pokemonName: getPokemonSpecies(speciesRootForm).getName() })); } } diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index fb3daf53a8b..865877445c1 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -3,7 +3,7 @@ import { Moves } from "#app/enums/moves"; import Pokemon, { PlayerPokemon } from "#app/field/pokemon"; import BattleScene from "#app/battle-scene"; import { Type } from "../type"; -import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "./mystery-encounter-requirements"; +import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { CanLearnMoveRequirement, CanLearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement"; import { isNullOrUndefined, randSeedInt } from "#app/utils"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index a204ea848da..2a5f6fda7e1 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -25,6 +25,9 @@ export interface EncounterStartOfBattleEffect { followUp?: boolean; } +const DEFAULT_MAX_ALLOWED_ENCOUNTERS = 2; +const DEFAULT_MAX_ALLOWED_ROGUE_ENCOUNTERS = 1; + /** * Used by {@linkcode MysteryEncounterBuilder} class to define required/optional properties on the {@linkcode MysteryEncounter} class when building. * @@ -42,6 +45,7 @@ export interface IMysteryEncounter { autoHideIntroVisuals: boolean; enterIntroVisualsFromRight: boolean; catchAllowed: boolean; + fleeAllowed: boolean; continuousEncounter: boolean; maxAllowedEncounters: number; hasBattleAnimationsWithoutTargets: boolean; @@ -110,6 +114,11 @@ export default class MysteryEncounter implements IMysteryEncounter { * Default false */ catchAllowed: boolean; + /** + * If true, allows fleeing from a wild encounter (trainer battle MEs auto-disable fleeing) + * Default true + */ + fleeAllowed: boolean; /** * If true, encounter will continuously run through multiple battles/puzzles/etc. instead of going to next wave * MUST EVENTUALLY BE DISABLED TO CONTINUE TO NEXT WAVE @@ -246,8 +255,8 @@ export default class MysteryEncounter implements IMysteryEncounter { this.encounterTier = this.encounterTier ?? MysteryEncounterTier.COMMON; this.dialogue = this.dialogue ?? {}; this.spriteConfigs = this.spriteConfigs ? [...this.spriteConfigs] : []; - // Default max is 1 for ROGUE encounters, 3 for others - this.maxAllowedEncounters = this.maxAllowedEncounters ?? this.encounterTier === MysteryEncounterTier.ROGUE ? 1 : 3; + // Default max is 1 for ROGUE encounters, 2 for others + this.maxAllowedEncounters = this.maxAllowedEncounters ?? this.encounterTier === MysteryEncounterTier.ROGUE ? DEFAULT_MAX_ALLOWED_ROGUE_ENCOUNTERS : DEFAULT_MAX_ALLOWED_ENCOUNTERS; this.encounterMode = MysteryEncounterMode.DEFAULT; this.requirements = this.requirements ? this.requirements : []; this.hideBattleIntroMessage = this.hideBattleIntroMessage ?? false; @@ -520,6 +529,7 @@ export class MysteryEncounterBuilder implements Partial { enterIntroVisualsFromRight: boolean = false; continuousEncounter: boolean = false; catchAllowed: boolean = false; + fleeAllowed: boolean = true; lockEncounterRewardTiers: boolean = false; startOfBattleEffectsComplete: boolean = false; hasBattleAnimationsWithoutTargets: boolean = false; @@ -580,8 +590,8 @@ export class MysteryEncounterBuilder implements Partial { * There should be at least 2 options defined and no more than 4. * If complex use {@linkcode MysteryEncounterBuilder.withOption} * - * @param dialogue - {@linkcode OptionTextDisplay} - * @param callback - {@linkcode OptionPhaseCallback} + * @param dialogue {@linkcode OptionTextDisplay} + * @param callback {@linkcode OptionPhaseCallback} * @returns */ withSimpleDexProgressOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback): this & Pick { @@ -732,7 +742,7 @@ export class MysteryEncounterBuilder implements Partial { * * @param min min wave (or exact size if only min is given) * @param max optional max size. If not given, defaults to min => exact wave - * @param excludeFainted - if true, only counts unfainted mons + * @param excludeFainted if true, only counts unfainted mons * @returns */ withScenePartySizeRequirement(min: number, max?: number, excludeFainted: boolean = false): this & Required> { @@ -798,7 +808,7 @@ export class MysteryEncounterBuilder implements Partial { * NOTE: If rewards are dependent on options selected, runtime data, etc., * It may be better to programmatically set doEncounterRewards elsewhere. * There is a helper function in mystery-encounter utils, setEncounterRewards(), which can be called programmatically to set rewards - * @param doEncounterRewards - synchronous callback function to perform during rewards phase of the encounter + * @param doEncounterRewards Synchronous callback function to perform during rewards phase of the encounter * @returns */ withRewards(doEncounterRewards: (scene: BattleScene) => boolean): this & Required> { @@ -812,7 +822,7 @@ export class MysteryEncounterBuilder implements Partial { * NOTE: If rewards are dependent on options selected, runtime data, etc., * It may be better to programmatically set doEncounterExp elsewhere. * There is a helper function in mystery-encounter utils, setEncounterExp(), which can be called programmatically to set rewards - * @param doEncounterExp - synchronous callback function to perform during rewards phase of the encounter + * @param doEncounterExp Synchronous callback function to perform during rewards phase of the encounter * @returns */ withExp(doEncounterExp: (scene: BattleScene) => boolean): this & Required> { @@ -823,7 +833,7 @@ export class MysteryEncounterBuilder implements Partial { * Can be used to perform init logic before intro visuals are shown and before the MysteryEncounterPhase begins * Useful for performing things like procedural generation of intro sprites, etc. * - * @param onInit - synchronous callback function to perform as soon as the encounter is selected for the next phase + * @param onInit Synchronous callback function to perform as soon as the encounter is selected for the next phase * @returns */ withOnInit(onInit: (scene: BattleScene) => boolean): this & Required> { @@ -833,7 +843,7 @@ export class MysteryEncounterBuilder implements Partial { /** * Can be used to perform some extra logic (usually animations) when the enemy field is finished sliding in * - * @param onVisualsStart - synchronous callback function to perform as soon as the enemy field finishes sliding in + * @param onVisualsStart Synchronous callback function to perform as soon as the enemy field finishes sliding in * @returns */ withOnVisualsStart(onVisualsStart: (scene: BattleScene) => boolean): this & Required> { @@ -843,7 +853,7 @@ export class MysteryEncounterBuilder implements Partial { /** * Can set whether catching is allowed or not on the encounter * This flag can also be programmatically set inside option event functions or elsewhere - * @param catchAllowed - if true, allows enemy pokemon to be caught during the encounter + * @param catchAllowed If `true`, allows enemy pokemon to be caught during the encounter * @returns */ withCatchAllowed(catchAllowed: boolean): this & Required> { @@ -851,7 +861,16 @@ export class MysteryEncounterBuilder implements Partial { } /** - * @param hideBattleIntroMessage - if true, will not show the trainerAppeared/wildAppeared/bossAppeared message for an encounter + * Can set whether fleeing is allowed or not on the encounter + * @param fleeAllowed If `false`, prevents fleeing from a wild battle (trainer battle MEs already have flee disabled) + * @returns + */ + withFleeAllowed(fleeAllowed: boolean): this & Required> { + return Object.assign(this, { fleeAllowed }); + } + + /** + * @param hideBattleIntroMessage If `true`, will not show the trainerAppeared/wildAppeared/bossAppeared message for an encounter * @returns */ withHideWildIntroMessage(hideBattleIntroMessage: boolean): this & Required> { @@ -859,7 +878,7 @@ export class MysteryEncounterBuilder implements Partial { } /** - * @param autoHideIntroVisuals - if false, will not hide the intro visuals that are displayed at the beginning of encounter + * @param autoHideIntroVisuals If `false`, will not hide the intro visuals that are displayed at the beginning of encounter * @returns */ withAutoHideIntroVisuals(autoHideIntroVisuals: boolean): this & Required> { @@ -867,7 +886,7 @@ export class MysteryEncounterBuilder implements Partial { } /** - * @param enterIntroVisualsFromRight - If true, will slide in intro visuals from the right side of the screen. If false, slides in from left, as normal + * @param enterIntroVisualsFromRight If `true`, will slide in intro visuals from the right side of the screen. If false, slides in from left, as normal * Default false * @returns */ @@ -878,7 +897,7 @@ export class MysteryEncounterBuilder implements Partial { /** * Add a title for the encounter * - * @param title - title of the encounter + * @param title Title of the encounter * @returns */ withTitle(title: string): this { @@ -898,7 +917,7 @@ export class MysteryEncounterBuilder implements Partial { /** * Add a description of the encounter * - * @param description - description of the encounter + * @param description Description of the encounter * @returns */ withDescription(description: string): this { @@ -918,7 +937,7 @@ export class MysteryEncounterBuilder implements Partial { /** * Add a query for the encounter * - * @param query - query to use for the encounter + * @param query Query to use for the encounter * @returns */ withQuery(query: string): this { @@ -938,7 +957,7 @@ export class MysteryEncounterBuilder implements Partial { /** * Add outro dialogue/s for the encounter * - * @param dialogue - outro dialogue/s + * @param dialogue Outro dialogue(s) * @returns */ withOutroDialogue(dialogue: MysteryEncounterDialogue["outro"] = []): this { diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index d235ff86861..cc2eaf234c4 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -31,6 +31,7 @@ import { BugTypeSuperfanEncounter } from "#app/data/mystery-encounters/encounter import { FunAndGamesEncounter } from "#app/data/mystery-encounters/encounters/fun-and-games-encounter"; import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/uncommon-breed-encounter"; import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encounters/global-trade-system-encounter"; +import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter"; /** * Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * ) / MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT @@ -184,7 +185,8 @@ const humanTransitableBiomeEncounters: MysteryEncounterType[] = [ MysteryEncounterType.SHADY_VITAMIN_DEALER, MysteryEncounterType.THE_POKEMON_SALESMAN, MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE, - MysteryEncounterType.THE_WINSTRATE_CHALLENGE + MysteryEncounterType.THE_WINSTRATE_CHALLENGE, + MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER ]; const civilizationBiomeEncounters: MysteryEncounterType[] = [ @@ -238,7 +240,6 @@ export const mysteryEncountersByBiome = new Map([ MysteryEncounterType.SAFARI_ZONE, MysteryEncounterType.ABSOLUTE_AVARICE ]], - [Biome.SEA, [ MysteryEncounterType.LOST_AT_SEA ]], @@ -275,7 +276,9 @@ export const mysteryEncountersByBiome = new Map([ [Biome.ABYSS, [ MysteryEncounterType.DANCING_LESSONS ]], - [Biome.SPACE, []], + [Biome.SPACE, [ + MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER + ]], [Biome.CONSTRUCTION_SITE, []], [Biome.JUNGLE, [ MysteryEncounterType.SAFARI_ZONE @@ -319,6 +322,7 @@ export function initMysteryEncounters() { allMysteryEncounters[MysteryEncounterType.FUN_AND_GAMES] = FunAndGamesEncounter; allMysteryEncounters[MysteryEncounterType.UNCOMMON_BREED] = UncommonBreedEncounter; allMysteryEncounters[MysteryEncounterType.GLOBAL_TRADE_SYSTEM] = GlobalTradeSystemEncounter; + allMysteryEncounters[MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER] = TheExpertPokemonBreederEncounter; // Add extreme encounters to biome map extremeBiomeEncounters.forEach(encounter => { diff --git a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts index bbdf2ee5a4d..a0b4edd4a36 100644 --- a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts +++ b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts @@ -2,7 +2,7 @@ import BattleScene from "#app/battle-scene"; import { Moves } from "#app/enums/moves"; import { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import { isNullOrUndefined } from "#app/utils"; -import { EncounterPokemonRequirement } from "../mystery-encounter-requirements"; +import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; /** * {@linkcode CanLearnMoveRequirement} options diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 2cd369fbaad..187de3c93c4 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -3,7 +3,7 @@ import { biomeLinks, BiomePoolTier } from "#app/data/biomes"; import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; import { AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters"; import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import Pokemon, { FieldPosition, PlayerPokemon, PokemonMove, PokemonSummonData } from "#app/field/pokemon"; +import Pokemon, { AiType, FieldPosition, PlayerPokemon, PokemonMove, PokemonSummonData } from "#app/field/pokemon"; import { CustomModifierSettings, ModifierPoolType, ModifierType, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; import { MysteryEncounterBattlePhase, MysteryEncounterBattleStartCleanupPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases"; import PokemonData from "#app/system/pokemon-data"; @@ -36,6 +36,7 @@ import { BattleEndPhase } from "#app/phases/battle-end-phase"; import { GameOverPhase } from "#app/phases/game-over-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import { PartyExpPhase } from "#app/phases/party-exp-phase"; +import { Variant } from "#app/data/variant"; /** * Animates exclamation sprite over trainer's head at start of encounter @@ -67,6 +68,7 @@ export function doTrainerExclamation(scene: BattleScene) { export interface EnemyPokemonConfig { species: PokemonSpecies; isBoss: boolean; + nickname?: string; bossSegments?: number; bossSegmentModifier?: number; // Additive to the determined segment number mysteryEncounterPokemonData?: MysteryEncounterPokemonData; @@ -79,27 +81,32 @@ export interface EnemyPokemonConfig { nature?: Nature; ivs?: [number, number, number, number, number, number]; shiny?: boolean; + /** Is only checked if Pokemon is shiny */ + variant?: Variant; /** Can set just the status, or pass a timer on the status turns */ status?: StatusEffect | [StatusEffect, number]; mysteryEncounterBattleEffects?: (pokemon: Pokemon) => void; modifierConfigs?: HeldModifierConfig[]; tags?: BattlerTagType[]; dataSource?: PokemonData; + aiType?: AiType; } export interface EnemyPartyConfig { - /** Formula for enemy: level += waveIndex / 10 * levelAdditive */ - levelAdditiveMultiplier?: number; + /** Formula for enemy level: level += waveIndex / 10 * levelAdditiveModifier */ + levelAdditiveModifier?: number; doubleBattle?: boolean; /** Generates trainer battle solely off trainer type */ trainerType?: TrainerType; /** More customizable option for configuring trainer battle */ trainerConfig?: TrainerConfig; pokemonConfigs?: EnemyPokemonConfig[]; - /** True for female trainer, false for male */ + /** `true` for female trainer, false for male */ female?: boolean; - /** True will prevent player from switching */ + /** `true` will prevent player from switching */ disableSwitch?: boolean; + /** `true` or leaving undefined will increment dex seen count for the encounter battle, `false` will not */ + countAsSeen?: boolean; } /** @@ -156,10 +163,10 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: // ME levels are modified by an additive value that scales with wave index // Base scaling: Every 10 waves, modifier gets +1 level - // This can be amplified or counteracted by setting levelAdditiveMultiplier in config - // levelAdditiveMultiplier value of 0.5 will halve the modifier scaling, 2 will double it, etc. + // This can be amplified or counteracted by setting levelAdditiveModifier in config + // levelAdditiveModifier value of 0.5 will halve the modifier scaling, 2 will double it, etc. // Leaving null/undefined will disable level scaling - const mult: number = !isNullOrUndefined(partyConfig.levelAdditiveMultiplier) ? partyConfig.levelAdditiveMultiplier! : 0; + const mult: number = !isNullOrUndefined(partyConfig.levelAdditiveModifier) ? partyConfig.levelAdditiveModifier! : 0; const additive = Math.max(Math.round((scene.currentBattle.waveIndex / 10) * mult), 0); battle.enemyLevels = battle.enemyLevels.map(level => level + additive); @@ -210,13 +217,18 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: enemyPokemon.resetSummonData(); } - if (!loaded) { + if (!loaded && isNullOrUndefined(partyConfig.countAsSeen) || partyConfig.countAsSeen) { scene.gameData.setPokemonSeen(enemyPokemon, true, !!(trainerType || trainerConfig)); } if (partyConfig?.pokemonConfigs && e < partyConfig.pokemonConfigs.length) { const config = partyConfig.pokemonConfigs[e]; + // Set form + if (!isNullOrUndefined(config.nickname)) { + enemyPokemon.nickname = btoa(unescape(encodeURIComponent(config.nickname!))); + } + // Generate new id, reset status and HP in case using data source if (config.dataSource) { enemyPokemon.id = Utils.randSeedInt(4294967296); @@ -232,6 +244,11 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: enemyPokemon.shiny = config.shiny!; } + // Set Variant + if (enemyPokemon.shiny && !isNullOrUndefined(config.variant)) { + enemyPokemon.variant = config.variant!; + } + // Set custom mystery encounter data fields (such as sprite scale, custom abilities, types, etc.) if (!isNullOrUndefined(config.mysteryEncounterPokemonData)) { enemyPokemon.mysteryEncounterPokemonData = config.mysteryEncounterPokemonData!; @@ -286,6 +303,11 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: enemyPokemon.summonData.gender = config.gender!; } + // Set AI type + if (!isNullOrUndefined(config.aiType)) { + enemyPokemon.aiType = config.aiType!; + } + // Set moves if (config?.moveSet && config.moveSet.length > 0) { const moves = config.moveSet.map(m => new PokemonMove(m)); @@ -307,6 +329,9 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: // Requires re-priming summon data to update everything properly enemyPokemon.primeSummonData(enemyPokemon.summonData); + if (enemyPokemon.isShiny() && !enemyPokemon["shinySparkle"]) { + enemyPokemon.initShinySparkle(); + } enemyPokemon.initBattleInfo(); enemyPokemon.getBattleInfo().initInfo(enemyPokemon); enemyPokemon.generateName(); @@ -702,19 +727,19 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase: if (encounter.continuousEncounter || doNotContinue) { return; } else if (encounter.encounterMode === MysteryEncounterMode.NO_BATTLE) { - scene.pushPhase(new EggLapsePhase(scene)); scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase)); + scene.pushPhase(new EggLapsePhase(scene)); } else if (!scene.getEnemyParty().find(p => encounter.encounterMode !== MysteryEncounterMode.TRAINER_BATTLE ? p.isOnField() : !p?.isFainted(true))) { scene.pushPhase(new BattleEndPhase(scene)); if (encounter.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) { scene.pushPhase(new TrainerVictoryPhase(scene)); } if (scene.gameMode.isEndless || !scene.gameMode.isWaveFinal(scene.currentBattle.waveIndex)) { + scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase)); if (!encounter.doContinueEncounter) { // Only lapse eggs once for multi-battle encounters scene.pushPhase(new EggLapsePhase(scene)); } - scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase)); } } } diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index bac8bded9ba..86c86010c29 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -19,6 +19,10 @@ import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifi import { Gender } from "#app/data/gender"; import { PermanentStat } from "#enums/stat"; import { VictoryPhase } from "#app/phases/victory-phase"; +import { SummaryUiMode } from "#app/ui/summary-ui-handler"; + +/** Will give +1 level every 10 waves */ +export const STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER = 1; /** * Gets the sprite key and file root for a given PokemonSpecies (accounts for gender, shiny, variants, forms, and experimental) @@ -289,10 +293,12 @@ export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: numb */ export async function applyModifierTypeToPlayerPokemon(scene: BattleScene, pokemon: PlayerPokemon, modType: PokemonHeldItemModifierType, fallbackModifierType?: PokemonHeldItemModifierType) { // Check if the Pokemon has max stacks of that item already + const modifier = modType.newModifier(pokemon); const existing = scene.findModifier(m => ( m instanceof PokemonHeldItemModifier && m.type.id === modType.id && - m.pokemonId === pokemon.id + m.pokemonId === pokemon.id && + m.matchType(modifier) )) as PokemonHeldItemModifier; // At max stacks @@ -305,7 +311,6 @@ export async function applyModifierTypeToPlayerPokemon(scene: BattleScene, pokem return applyModifierTypeToPlayerPokemon(scene, pokemon, fallbackModifierType); } - const modifier = modType.newModifier(pokemon); await scene.addModifier(modifier, false, false, false, true); } @@ -327,7 +332,7 @@ export function trainerThrowPokeball(scene: BattleScene, pokemon: EnemyPokemon, const _3m = 3 * pokemon.getMaxHp(); const _2h = 2 * pokemon.hp; const catchRate = pokemon.species.catchRate; - const pokeballMultiplier = getPokeballCatchMultiplier(this.pokeballType); + const pokeballMultiplier = getPokeballCatchMultiplier(pokeballType); const statusMultiplier = pokemon.status ? getStatusEffectCatchRateMultiplier(pokemon.status.effect) : 1; const x = Math.round((((_3m - _2h) * catchRate * pokeballMultiplier) / _3m) * statusMultiplier); ballTwitchRate = Math.round(65536 / Math.sqrt(Math.sqrt(255 / x))); @@ -501,8 +506,6 @@ function failCatch(scene: BattleScene, pokemon: EnemyPokemon, originalY: number, * @param isObtain */ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, pokeball: Phaser.GameObjects.Sprite | null, pokeballType: PokeballType, showCatchObtainMessage: boolean = true, isObtain: boolean = false): Promise { - scene.unshiftPhase(new VictoryPhase(scene, pokemon.id, true)); - const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm(); if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1) { @@ -528,6 +531,11 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po return new Promise(resolve => { const doPokemonCatchMenu = () => { const end = () => { + // Ensure the pokemon is in the enemy party in all situations + if (!scene.getEnemyParty().some(p => p.id === pokemon.id)) { + scene.getEnemyParty().push(pokemon); + } + scene.unshiftPhase(new VictoryPhase(scene, pokemon.id, true)); scene.pokemonInfoContainer.hide(); if (pokeball) { removePb(scene, pokeball); @@ -539,8 +547,8 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po scene.field.remove(pokemon, true); } }; - const addToParty = () => { - const newPokemon = pokemon.addToParty(pokeballType); + const addToParty = (slotIndex?: number) => { + const newPokemon = pokemon.addToParty(pokeballType, slotIndex); const modifiers = scene.findModifiers(m => m instanceof PokemonHeldItemModifier, false); if (scene.getParty().filter(p => p.isShiny()).length === 6) { scene.validateAchv(achvs.SHINY_PARTY); @@ -559,12 +567,19 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po if (scene.getParty().length === 6) { const promptRelease = () => { scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => { - scene.pokemonInfoContainer.makeRoomForConfirmUi(); + scene.pokemonInfoContainer.makeRoomForConfirmUi(1, true); scene.ui.setMode(Mode.CONFIRM, () => { - scene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, 0, (slotIndex: number, _option: PartyOption) => { + const newPokemon = scene.addPlayerPokemon(pokemon.species, pokemon.level, pokemon.abilityIndex, pokemon.formIndex, pokemon.gender, pokemon.shiny, pokemon.variant, pokemon.ivs, pokemon.nature, pokemon); + scene.ui.setMode(Mode.SUMMARY, newPokemon, 0, SummaryUiMode.DEFAULT, () => { + scene.ui.setMode(Mode.MESSAGE).then(() => { + promptRelease(); + }); + }, false); + }, () => { + scene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, 0, (slotIndex: integer, _option: PartyOption) => { scene.ui.setMode(Mode.MESSAGE).then(() => { if (slotIndex < 6) { - addToParty(); + addToParty(slotIndex); } else { promptRelease(); } @@ -575,7 +590,7 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po removePokemon(); end(); }); - }); + }, "fullParty"); }); }; promptRelease(); @@ -711,13 +726,50 @@ export function getGoldenBugNetSpecies(): PokemonSpecies { const roll = randSeedInt(totalWeight); let w = 0; - for (const species of GOLDEN_BUG_NET_SPECIES_POOL) { - w += species[1]; + for (const speciesWeightPair of GOLDEN_BUG_NET_SPECIES_POOL) { + w += speciesWeightPair[1]; if (roll < w) { - return getPokemonSpecies(species); + return getPokemonSpecies(speciesWeightPair[0]); } } // Defaults to Scyther return getPokemonSpecies(Species.SCYTHER); } + +/** + * Generates a Pokemon level for a given wave, with an option to increase/decrease by a scaling modifier + * @param scene + * @param levelAdditiveModifier Default 0. will add +(1 level / 10 waves * levelAdditiveModifier) to the level calculation + */ +export function getEncounterPokemonLevelForWave(scene: BattleScene, levelAdditiveModifier: number = 0) { + const currentBattle = scene.currentBattle; + // Default to use the first generated level from enemyLevels, or generate a new one if it DNE + const baseLevel = currentBattle.enemyLevels && currentBattle.enemyLevels?.length > 0 ? currentBattle.enemyLevels[0] : currentBattle.getLevelForWave(); + + // Add a level scaling modifier that is (+1 level per 10 waves) * levelAdditiveModifier + return baseLevel + Math.max(Math.round((currentBattle.waveIndex / 10) * levelAdditiveModifier), 0); +} + +export async function addPokemonDataToDexAndValidateAchievements(scene: BattleScene, pokemon: PlayerPokemon) { + const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm(); + + if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1) { + scene.validateAchv(achvs.HIDDEN_ABILITY); + } + + if (pokemon.species.subLegendary) { + scene.validateAchv(achvs.CATCH_SUB_LEGENDARY); + } + + if (pokemon.species.legendary) { + scene.validateAchv(achvs.CATCH_LEGENDARY); + } + + if (pokemon.species.mythical) { + scene.validateAchv(achvs.CATCH_MYTHICAL); + } + + scene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs); + return scene.gameData.setPokemonCaught(pokemon, true, false, false); +} diff --git a/src/data/mystery-encounters/utils/encounter-transformation-sequence.ts b/src/data/mystery-encounters/utils/encounter-transformation-sequence.ts index fd9d43829e5..fcadb101817 100644 --- a/src/data/mystery-encounters/utils/encounter-transformation-sequence.ts +++ b/src/data/mystery-encounters/utils/encounter-transformation-sequence.ts @@ -103,7 +103,7 @@ export function doPokemonTransformationSequence(scene: BattleScene, previousPoke scene.time.delayedCall(1000, () => { pokemonEvoTintSprite.setScale(0.25); pokemonEvoTintSprite.setVisible(true); - doCycle(scene, 2, 6, pokemonTintSprite, pokemonEvoTintSprite).then(() => { + doCycle(scene, 1.5, 6, pokemonTintSprite, pokemonEvoTintSprite).then(() => { pokemonEvoSprite.setVisible(true); doCircleInward(scene, transformationBaseBg, transformationContainer, xOffset, yOffset); @@ -115,7 +115,7 @@ export function doPokemonTransformationSequence(scene: BattleScene, previousPoke delay: 150, easing: "Sine.easeIn", onComplete: () => { - scene.time.delayedCall(2500, () => { + scene.time.delayedCall(3000, () => { resolve(); scene.tweens.add({ targets: pokemonEvoSprite, diff --git a/src/data/pokemon-evolutions.ts b/src/data/pokemon-evolutions.ts index 6479d620182..f9602d1386a 100644 --- a/src/data/pokemon-evolutions.ts +++ b/src/data/pokemon-evolutions.ts @@ -17,7 +17,8 @@ export enum SpeciesWildEvolutionDelay { SHORT, MEDIUM, LONG, - VERY_LONG + VERY_LONG, + NEVER } export enum EvolutionItem { @@ -39,19 +40,34 @@ export enum EvolutionItem { TART_APPLE, STRAWBERRY_SWEET, UNREMARKABLE_TEACUP, - - CHIPPED_POT = 51, - BLACK_AUGURITE, + UPGRADE, + DUBIOUS_DISC, + DRAGON_SCALE, + PRISM_SCALE, + RAZOR_CLAW, + RAZOR_FANG, + REAPER_CLOTH, + ELECTIRIZER, + MAGMARIZER, + PROTECTOR, + SACHET, + WHIPPED_DREAM, + SYRUPY_APPLE, + CHIPPED_POT, GALARICA_CUFF, GALARICA_WREATH, - PEAT_BLOCK, AUSPICIOUS_ARMOR, MALICIOUS_ARMOR, MASTERPIECE_TEACUP, + SUN_FLUTE, + MOON_FLUTE, + + BLACK_AUGURITE = 51, + PEAT_BLOCK, METAL_ALLOY, SCROLL_OF_DARKNESS, SCROLL_OF_WATERS, - SYRUPY_APPLE + LEADERS_CREST } /** @@ -222,7 +238,7 @@ export const pokemonEvolutions: PokemonEvolutions = { ], [Species.SLOWPOKE]: [ new SpeciesEvolution(Species.SLOWBRO, 37, null, null), - new SpeciesEvolution(Species.SLOWKING, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* King's Rock */), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.SLOWKING, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.MAGNEMITE]: [ new SpeciesEvolution(Species.MAGNETON, 30, null, null) @@ -249,8 +265,8 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.ELECTRODE, 30, null, null) ], [Species.CUBONE]: [ - new SpeciesEvolution(Species.ALOLA_MAROWAK, 28, null, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.ISLAND || p.scene.arena.biomeType === Biome.BEACH), SpeciesWildEvolutionDelay.MEDIUM), - new SpeciesEvolution(Species.MAROWAK, 28, null, null) + new SpeciesEvolution(Species.ALOLA_MAROWAK, 28, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), + new SpeciesEvolution(Species.MAROWAK, 28, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) ], [Species.TYROGUE]: [ new SpeciesEvolution(Species.HITMONLEE, 20, null, new SpeciesEvolutionCondition(p => p.stats[Stat.ATK] > p.stats[Stat.DEF])), @@ -258,8 +274,8 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.HITMONTOP, 20, null, new SpeciesEvolutionCondition(p => p.stats[Stat.ATK] === p.stats[Stat.DEF])) ], [Species.KOFFING]: [ - new SpeciesEvolution(Species.GALAR_WEEZING, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.METROPOLIS || p.scene.arena.biomeType === Biome.SLUM), SpeciesWildEvolutionDelay.MEDIUM), - new SpeciesEvolution(Species.WEEZING, 35, null, null) + new SpeciesEvolution(Species.GALAR_WEEZING, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), + new SpeciesEvolution(Species.WEEZING, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) ], [Species.RHYHORN]: [ new SpeciesEvolution(Species.RHYDON, 42, null, null) @@ -304,7 +320,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.QUILAVA, 14, null, null) ], [Species.QUILAVA]: [ - new SpeciesEvolution(Species.HISUI_TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), + new SpeciesEvolution(Species.HISUI_TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) ], [Species.TOTODILE]: [ @@ -652,7 +668,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.DEWOTT, 17, null, null) ], [Species.DEWOTT]: [ - new SpeciesEvolution(Species.HISUI_SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), + new SpeciesEvolution(Species.HISUI_SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) ], [Species.PATRAT]: [ @@ -800,10 +816,10 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.BISHARP, 52, null, null) ], [Species.BISHARP]: [ - new SpeciesEvolution(Species.KINGAMBIT, 64, null, null) + new SpeciesEvolution(Species.KINGAMBIT, 1, EvolutionItem.LEADERS_CREST, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.RUFFLET]: [ - new SpeciesEvolution(Species.HISUI_BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), + new SpeciesEvolution(Species.HISUI_BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) ], [Species.VULLABY]: [ @@ -883,20 +899,20 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.CLAWITZER, 37, null, null) ], [Species.TYRUNT]: [ - new SpeciesEvolution(Species.TYRANTRUM, 39, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) + new SpeciesEvolution(Species.TYRANTRUM, 39, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) ], [Species.AMAURA]: [ - new SpeciesEvolution(Species.AURORUS, 39, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) + new SpeciesEvolution(Species.AURORUS, 39, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) ], [Species.GOOMY]: [ - new SpeciesEvolution(Species.HISUI_SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), + new SpeciesEvolution(Species.HISUI_SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) ], [Species.SLIGGOO]: [ new SpeciesEvolution(Species.GOODRA, 50, null, new SpeciesEvolutionCondition(p => [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ].indexOf(p.scene.arena.weather?.weatherType || WeatherType.NONE) > -1), SpeciesWildEvolutionDelay.LONG) ], [Species.BERGMITE]: [ - new SpeciesEvolution(Species.HISUI_AVALUGG, 37, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), + new SpeciesEvolution(Species.HISUI_AVALUGG, 37, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.AVALUGG, 37, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) ], [Species.NOIBAT]: [ @@ -906,7 +922,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.DARTRIX, 17, null, null) ], [Species.DARTRIX]: [ - new SpeciesEvolution(Species.HISUI_DECIDUEYE, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), + new SpeciesEvolution(Species.HISUI_DECIDUEYE, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.DECIDUEYE, 34, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) ], [Species.LITTEN]: [ @@ -928,7 +944,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.TOUCANNON, 28, null, null) ], [Species.YUNGOOS]: [ - new SpeciesEvolution(Species.GUMSHOOS, 20, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) + new SpeciesEvolution(Species.GUMSHOOS, 20, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) ], [Species.GRUBBIN]: [ new SpeciesEvolution(Species.CHARJABUG, 20, null, null) @@ -946,7 +962,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.ARAQUANID, 22, null, null) ], [Species.FOMANTIS]: [ - new SpeciesEvolution(Species.LURANTIS, 34, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) + new SpeciesEvolution(Species.LURANTIS, 34, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) ], [Species.MORELULL]: [ new SpeciesEvolution(Species.SHIINOTIC, 24, null, null) @@ -973,17 +989,17 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.KOMMO_O, 45, null, null) ], [Species.COSMOG]: [ - new SpeciesEvolution(Species.COSMOEM, 43, null, null) + new SpeciesEvolution(Species.COSMOEM, 23, null, null) ], [Species.COSMOEM]: [ - new SpeciesEvolution(Species.SOLGALEO, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)), - new SpeciesEvolution(Species.LUNALA, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) + new SpeciesEvolution(Species.SOLGALEO, 53, EvolutionItem.SUN_FLUTE, null, SpeciesWildEvolutionDelay.VERY_LONG), + new SpeciesEvolution(Species.LUNALA, 53, EvolutionItem.MOON_FLUTE, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.MELTAN]: [ new SpeciesEvolution(Species.MELMETAL, 48, null, null) ], [Species.ALOLA_RATTATA]: [ - new SpeciesEvolution(Species.ALOLA_RATICATE, 20, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) + new SpeciesEvolution(Species.ALOLA_RATICATE, 20, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) ], [Species.ALOLA_DIGLETT]: [ new SpeciesEvolution(Species.ALOLA_DUGTRIO, 26, null, null) @@ -1090,7 +1106,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.GALAR_RAPIDASH, 40, null, null) ], [Species.GALAR_FARFETCHD]: [ - new SpeciesEvolution(Species.SIRFETCHD, 30, null, null) + new SpeciesEvolution(Species.SIRFETCHD, 30, null, null, SpeciesWildEvolutionDelay.LONG) ], [Species.GALAR_SLOWPOKE]: [ new SpeciesEvolution(Species.GALAR_SLOWBRO, 1, EvolutionItem.GALARICA_CUFF, null, SpeciesWildEvolutionDelay.VERY_LONG), @@ -1106,7 +1122,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.GALAR_LINOONE, 20, null, null) ], [Species.GALAR_LINOONE]: [ - new SpeciesEvolution(Species.OBSTAGOON, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) + new SpeciesEvolution(Species.OBSTAGOON, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) ], [Species.GALAR_YAMASK]: [ new SpeciesEvolution(Species.RUNERIGUS, 34, null, null) @@ -1214,7 +1230,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.GLIMMORA, 35, null, null) ], [Species.GREAVARD]: [ - new SpeciesEvolution(Species.HOUNDSTONE, 30, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) + new SpeciesEvolution(Species.HOUNDSTONE, 30, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) ], [Species.FRIGIBAX]: [ new SpeciesEvolution(Species.ARCTIBAX, 35, null, null) @@ -1226,8 +1242,8 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.CLODSIRE, 20, null, null) ], [Species.PIKACHU]: [ - new SpeciesFormEvolution(Species.ALOLA_RAICHU, "", "", 1, EvolutionItem.THUNDER_STONE, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.ISLAND || p.scene.arena.biomeType === Biome.BEACH), SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(Species.ALOLA_RAICHU, "partner", "", 1, EvolutionItem.THUNDER_STONE, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.ISLAND || p.scene.arena.biomeType === Biome.BEACH), SpeciesWildEvolutionDelay.LONG), + new SpeciesFormEvolution(Species.ALOLA_RAICHU, "", "", 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.LONG), + new SpeciesFormEvolution(Species.ALOLA_RAICHU, "partner", "", 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.RAICHU, "", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.RAICHU, "partner", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG) ], @@ -1255,7 +1271,7 @@ export const pokemonEvolutions: PokemonEvolutions = { ], [Species.POLIWHIRL]: [ new SpeciesEvolution(Species.POLIWRATH, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesEvolution(Species.POLITOED, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* King's Rock */), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.POLITOED, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.WEEPINBELL]: [ new SpeciesEvolution(Species.VICTREEBEL, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) @@ -1267,7 +1283,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.CLOYSTER, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.EXEGGCUTE]: [ - new SpeciesEvolution(Species.ALOLA_EXEGGUTOR, 1, EvolutionItem.LEAF_STONE, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.ISLAND || p.scene.arena.biomeType === Biome.BEACH), SpeciesWildEvolutionDelay.LONG), + new SpeciesEvolution(Species.ALOLA_EXEGGUTOR, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG), new SpeciesEvolution(Species.EXEGGUTOR, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.TANGELA]: [ @@ -1280,12 +1296,12 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.STARMIE, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.EEVEE]: [ - new SpeciesFormEvolution(Species.SYLVEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(Species.SYLVEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(Species.ESPEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(Species.ESPEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(Species.UMBREON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(Species.UMBREON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), + new SpeciesFormEvolution(Species.SYLVEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), + new SpeciesFormEvolution(Species.SYLVEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), + new SpeciesFormEvolution(Species.ESPEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG), + new SpeciesFormEvolution(Species.ESPEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG), + new SpeciesFormEvolution(Species.UMBREON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), + new SpeciesFormEvolution(Species.UMBREON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.VAPOREON, "", "", 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.VAPOREON, "partner", "", 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.JOLTEON, "", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG), @@ -1329,10 +1345,10 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.DUDUNSPARCE, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.GLIGAR]: [ - new SpeciesEvolution(Species.GLISCOR, 1, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor fang at night*/), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.GLISCOR, 1, EvolutionItem.RAZOR_FANG, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor fang at night*/), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.SNEASEL]: [ - new SpeciesEvolution(Species.WEAVILE, 1, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor claw at night*/), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.WEAVILE, 1, EvolutionItem.RAZOR_CLAW, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor claw at night*/), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.URSARING]: [ new SpeciesEvolution(Species.URSALUNA, 1, EvolutionItem.PEAT_BLOCK, null, SpeciesWildEvolutionDelay.VERY_LONG) //Ursaring does not evolve into Bloodmoon Ursaluna @@ -1362,8 +1378,8 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.SUDOWOODO, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM) ], [Species.MIME_JR]: [ - new SpeciesEvolution(Species.GALAR_MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (p.scene.arena.biomeType === Biome.ICE_CAVE || p.scene.arena.biomeType === Biome.SNOWY_FOREST)), SpeciesWildEvolutionDelay.MEDIUM), - new SpeciesEvolution(Species.MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(Species.GALAR_MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), SpeciesWildEvolutionDelay.MEDIUM), + new SpeciesEvolution(Species.MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)), SpeciesWildEvolutionDelay.MEDIUM) ], [Species.PANSAGE]: [ new SpeciesEvolution(Species.SIMISAGE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) @@ -1381,8 +1397,8 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.WHIMSICOTT, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.PETILIL]: [ - new SpeciesEvolution(Species.HISUI_LILLIGANT, 1, EvolutionItem.SUN_STONE, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.VERY_LONG), - new SpeciesEvolution(Species.LILLIGANT, 1, EvolutionItem.SUN_STONE, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.HISUI_LILLIGANT, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.LONG), + new SpeciesEvolution(Species.LILLIGANT, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.BASCULIN]: [ new SpeciesFormEvolution(Species.BASCULEGION, "white-striped", "female", 40, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE), SpeciesWildEvolutionDelay.VERY_LONG), @@ -1435,7 +1451,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.APPLETUN, 1, EvolutionItem.SWEET_APPLE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.CLOBBOPUS]: [ - new SpeciesEvolution(Species.GRAPPLOCT, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.TAUNT).length > 0), SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(Species.GRAPPLOCT, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.TAUNT).length > 0)/*Once Taunt is implemented, change evo level to 1 and delay to LONG*/) ], [Species.SINISTEA]: [ new SpeciesFormEvolution(Species.POLTEAGEIST, "phony", "phony", 1, EvolutionItem.CRACKED_POT, null, SpeciesWildEvolutionDelay.LONG), @@ -1472,7 +1488,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.OVERQWIL, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.BARB_BARRAGE).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.HISUI_SNEASEL]: [ - new SpeciesEvolution(Species.SNEASLER, 1, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY /* Razor claw at day*/), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.SNEASLER, 1, EvolutionItem.RAZOR_CLAW, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY /* Razor claw at day*/), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.CHARCADET]: [ new SpeciesEvolution(Species.ARMAROUGE, 1, EvolutionItem.AUSPICIOUS_ARMOR, null, SpeciesWildEvolutionDelay.LONG), @@ -1512,10 +1528,10 @@ export const pokemonEvolutions: PokemonEvolutions = { SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.RHYDON]: [ - new SpeciesEvolution(Species.RHYPERIOR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* Protector */), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.RHYPERIOR, 1, EvolutionItem.PROTECTOR, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.SEADRA]: [ - new SpeciesEvolution(Species.KINGDRA, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* Dragon scale*/), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.KINGDRA, 1, EvolutionItem.DRAGON_SCALE, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.SCYTHER]: [ new SpeciesEvolution(Species.SCIZOR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition( @@ -1524,22 +1540,22 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.KLEAVOR, 1, EvolutionItem.BLACK_AUGURITE, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.ELECTABUZZ]: [ - new SpeciesEvolution(Species.ELECTIVIRE, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* Electirizer*/), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.ELECTIVIRE, 1, EvolutionItem.ELECTIRIZER, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.MAGMAR]: [ - new SpeciesEvolution(Species.MAGMORTAR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* Magmarizer*/), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.MAGMORTAR, 1, EvolutionItem.MAGMARIZER, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.PORYGON]: [ - new SpeciesEvolution(Species.PORYGON2, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /*Upgrade*/), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.PORYGON2, 1, EvolutionItem.UPGRADE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.PORYGON2]: [ - new SpeciesEvolution(Species.PORYGON_Z, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* Dubious disc*/), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.PORYGON_Z, 1, EvolutionItem.DUBIOUS_DISC, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.FEEBAS]: [ - new SpeciesEvolution(Species.MILOTIC, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* Prism scale*/), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.MILOTIC, 1, EvolutionItem.PRISM_SCALE, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.DUSCLOPS]: [ - new SpeciesEvolution(Species.DUSKNOIR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* Reaper cloth*/), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.DUSKNOIR, 1, EvolutionItem.REAPER_CLOTH, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.CLAMPERL]: [ new SpeciesEvolution(Species.HUNTAIL, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => p.gender === Gender.MALE, p => p.gender = Gender.MALE /* Deep Sea Tooth */), SpeciesWildEvolutionDelay.VERY_LONG), @@ -1558,10 +1574,10 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.ACCELGOR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => !!p.scene.gameData.dexData[Species.KARRABLAST].caughtAttr), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.SPRITZEE]: [ - new SpeciesEvolution(Species.AROMATISSE, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /*Sachet*/), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.AROMATISSE, 1, EvolutionItem.SACHET, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.SWIRLIX]: [ - new SpeciesEvolution(Species.SLURPUFF, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /*Whipped Dream*/), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.SLURPUFF, 1, EvolutionItem.WHIPPED_DREAM, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.PHANTUMP]: [ new SpeciesEvolution(Species.TREVENANT, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) @@ -1576,7 +1592,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.ANNIHILAPE, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.RAGE_FIST).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.GOLBAT]: [ - new SpeciesEvolution(Species.CROBAT, 1, null, new SpeciesFriendshipEvolutionCondition(110), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.CROBAT, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.CHANSEY]: [ new SpeciesEvolution(Species.BLISSEY, 1, null, new SpeciesFriendshipEvolutionCondition(200), SpeciesWildEvolutionDelay.LONG) @@ -1610,29 +1626,29 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.CHANSEY, 1, null, new SpeciesFriendshipEvolutionCondition(160), SpeciesWildEvolutionDelay.SHORT) ], [Species.MUNCHLAX]: [ - new SpeciesEvolution(Species.SNORLAX, 1, null, new SpeciesFriendshipEvolutionCondition(90), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.SNORLAX, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG) ], [Species.RIOLU]: [ - new SpeciesEvolution(Species.LUCARIO, 1, null, new SpeciesFriendshipEvolutionCondition(90, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.LUCARIO, 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG) ], [Species.WOOBAT]: [ - new SpeciesEvolution(Species.SWOOBAT, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(Species.SWOOBAT, 1, null, new SpeciesFriendshipEvolutionCondition(90), SpeciesWildEvolutionDelay.MEDIUM) ], [Species.SWADLOON]: [ - new SpeciesEvolution(Species.LEAVANNY, 1, null, new SpeciesFriendshipEvolutionCondition(110), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.LEAVANNY, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG) ], [Species.TYPE_NULL]: [ - new SpeciesEvolution(Species.SILVALLY, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.SILVALLY, 1, null, new SpeciesFriendshipEvolutionCondition(100), SpeciesWildEvolutionDelay.LONG) ], [Species.ALOLA_MEOWTH]: [ - new SpeciesEvolution(Species.ALOLA_PERSIAN, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.ALOLA_PERSIAN, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG) ], [Species.SNOM]: [ new SpeciesEvolution(Species.FROSMOTH, 1, null, new SpeciesFriendshipEvolutionCondition(90, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.MEDIUM) ], [Species.GIMMIGHOUL]: [ - new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.VERY_LONG), - new SpeciesFormEvolution(Species.GHOLDENGO, "roaming", "", 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 1, null, new SpeciesEvolutionCondition( p => p.evoCounter > 9 ), SpeciesWildEvolutionDelay.VERY_LONG), + new SpeciesFormEvolution(Species.GHOLDENGO, "roaming", "", 1, null, new SpeciesEvolutionCondition( p => p.evoCounter > 9 ), SpeciesWildEvolutionDelay.VERY_LONG) ] }; diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index 4fc833939e4..a904f497b0f 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -684,7 +684,7 @@ export const pokemonFormChanges: PokemonFormChanges = { new SpeciesFormChange(Species.GROUDON, "", SpeciesFormKey.PRIMAL, new SpeciesFormChangeItemTrigger(FormChangeItem.RED_ORB)) ], [Species.RAYQUAZA]: [ - new SpeciesFormChange(Species.RAYQUAZA, "", SpeciesFormKey.MEGA, new SpeciesFormChangeCompoundTrigger(new SpeciesFormChangeItemTrigger(FormChangeItem.RAYQUAZITE), new SpeciesFormChangeMoveLearnedTrigger(Moves.DRAGON_ASCENT))) + new SpeciesFormChange(Species.RAYQUAZA, "", SpeciesFormKey.MEGA, new SpeciesFormChangeItemTrigger(FormChangeItem.RAYQUAZITE)) ], [Species.DEOXYS]: [ new SpeciesFormChange(Species.DEOXYS, "normal", "attack", new SpeciesFormChangeItemTrigger(FormChangeItem.SHARP_METEORITE)), diff --git a/src/data/pokemon-level-moves.ts b/src/data/pokemon-level-moves.ts index 93bd57ae32c..b5608093df2 100644 --- a/src/data/pokemon-level-moves.ts +++ b/src/data/pokemon-level-moves.ts @@ -1609,6 +1609,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = { [ 12, Moves.DRAGON_BREATH ], [ 16, Moves.CURSE ], [ 20, Moves.ROCK_SLIDE ], + [ 22, Moves.GYRO_BALL ], //Custom, from USUM [ 24, Moves.SCREECH ], [ 28, Moves.SAND_TOMB ], [ 32, Moves.STEALTH_ROCK ], @@ -2121,7 +2122,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = { [ 20, Moves.DOUBLE_HIT ], [ 24, Moves.SLASH ], [ 28, Moves.FOCUS_ENERGY ], - [ 30, Moves.STEEL_WING ], + [ 30, Moves.STEEL_WING ], //Custom [ 32, Moves.AGILITY ], [ 36, Moves.AIR_SLASH ], [ 40, Moves.X_SCISSOR ], @@ -7549,14 +7550,15 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = { [ 1, Moves.POUND ], [ 1, Moves.COPYCAT ], [ 1, Moves.BARRIER ], + [ 1, Moves.TICKLE ], //USUM [ 4, Moves.BATON_PASS ], [ 8, Moves.ENCORE ], [ 12, Moves.CONFUSION ], - [ 16, Moves.ROLE_PLAY ], + [ 16, Moves.MIMIC ], //Custom, swapped with Role Play to be closer to USUM [ 20, Moves.PROTECT ], [ 24, Moves.RECYCLE ], [ 28, Moves.PSYBEAM ], - [ 32, Moves.MIMIC ], + [ 32, Moves.ROLE_PLAY ], //Custom, swapped with Mimic [ 36, Moves.LIGHT_SCREEN ], [ 36, Moves.REFLECT ], [ 36, Moves.SAFEGUARD ], @@ -19496,6 +19498,108 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = { [ 51, Moves.BELCH ], ], }, + [Species.REVAVROOM]: { + 1: [ + [ EVOLVE_MOVE, Moves.WICKED_TORQUE ], + [ EVOLVE_MOVE, Moves.SHIFT_GEAR ], + [ 1, Moves.LICK ], + [ 1, Moves.POISON_GAS ], + [ 1, Moves.MAGNET_RISE ], + [ 4, Moves.SMOG ], + [ 7, Moves.TAUNT ], + [ 10, Moves.ASSURANCE ], + [ 13, Moves.SLUDGE ], + [ 17, Moves.GYRO_BALL ], + [ 21, Moves.HEADBUTT ], + [ 25, Moves.SCREECH ], + [ 28, Moves.IRON_HEAD ], + [ 32, Moves.SWAGGER ], + [ 36, Moves.POISON_JAB ], + [ 46, Moves.UPROAR ], + [ 52, Moves.SPIN_OUT ], + [ 58, Moves.GUNK_SHOT ], + ], + 2: [ + [ EVOLVE_MOVE, Moves.BLAZING_TORQUE ], + [ EVOLVE_MOVE, Moves.SHIFT_GEAR ], + [ 1, Moves.LICK ], + [ 1, Moves.POISON_GAS ], + [ 1, Moves.MAGNET_RISE ], + [ 4, Moves.SMOG ], + [ 7, Moves.TAUNT ], + [ 10, Moves.ASSURANCE ], + [ 13, Moves.SLUDGE ], + [ 17, Moves.GYRO_BALL ], + [ 21, Moves.HEADBUTT ], + [ 25, Moves.SCREECH ], + [ 28, Moves.IRON_HEAD ], + [ 32, Moves.SWAGGER ], + [ 36, Moves.POISON_JAB ], + [ 46, Moves.UPROAR ], + [ 52, Moves.SPIN_OUT ], + [ 58, Moves.GUNK_SHOT ], + ], + 3: [ + [ EVOLVE_MOVE, Moves.NOXIOUS_TORQUE ], + [ EVOLVE_MOVE, Moves.SHIFT_GEAR ], + [ 1, Moves.LICK ], + [ 1, Moves.POISON_GAS ], + [ 1, Moves.MAGNET_RISE ], + [ 4, Moves.SMOG ], + [ 7, Moves.TAUNT ], + [ 10, Moves.ASSURANCE ], + [ 13, Moves.SLUDGE ], + [ 17, Moves.GYRO_BALL ], + [ 21, Moves.HEADBUTT ], + [ 25, Moves.SCREECH ], + [ 28, Moves.IRON_HEAD ], + [ 32, Moves.SWAGGER ], + [ 36, Moves.POISON_JAB ], + [ 46, Moves.UPROAR ], + [ 52, Moves.SPIN_OUT ], + [ 58, Moves.GUNK_SHOT ], + ], + 4: [ + [ EVOLVE_MOVE, Moves.MAGICAL_TORQUE ], + [ EVOLVE_MOVE, Moves.SHIFT_GEAR ], + [ 1, Moves.LICK ], + [ 1, Moves.POISON_GAS ], + [ 1, Moves.MAGNET_RISE ], + [ 4, Moves.SMOG ], + [ 7, Moves.TAUNT ], + [ 10, Moves.ASSURANCE ], + [ 13, Moves.SLUDGE ], + [ 17, Moves.GYRO_BALL ], + [ 21, Moves.HEADBUTT ], + [ 25, Moves.SCREECH ], + [ 28, Moves.IRON_HEAD ], + [ 32, Moves.SWAGGER ], + [ 36, Moves.POISON_JAB ], + [ 46, Moves.UPROAR ], + [ 52, Moves.SPIN_OUT ], + [ 58, Moves.GUNK_SHOT ], + ], + 5: [ + [ EVOLVE_MOVE, Moves.COMBAT_TORQUE ], + [ EVOLVE_MOVE, Moves.SHIFT_GEAR ], + [ 1, Moves.LICK ], + [ 1, Moves.POISON_GAS ], + [ 1, Moves.MAGNET_RISE ], + [ 4, Moves.SMOG ], + [ 7, Moves.TAUNT ], + [ 10, Moves.ASSURANCE ], + [ 13, Moves.SLUDGE ], + [ 17, Moves.GYRO_BALL ], + [ 21, Moves.HEADBUTT ], + [ 25, Moves.SCREECH ], + [ 28, Moves.IRON_HEAD ], + [ 32, Moves.SWAGGER ], + [ 36, Moves.POISON_JAB ], + [ 46, Moves.UPROAR ], + [ 52, Moves.SPIN_OUT ], + [ 58, Moves.GUNK_SHOT ], + ], + }, [Species.PALDEA_TAUROS]: { 1: [ [ 1, Moves.TACKLE ], diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index d2f63275a5e..b8ddd826035 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -944,7 +944,7 @@ export function initSpecies() { new PokemonSpecies(Species.VENUSAUR, 1, false, false, false, "Seed Pokémon", Type.GRASS, Type.POISON, 2, 100, Abilities.OVERGROW, Abilities.NONE, Abilities.CHLOROPHYLL, 525, 80, 82, 83, 100, 100, 80, 45, 50, 263, GrowthRate.MEDIUM_SLOW, 87.5, true, true, new PokemonForm("Normal", "", Type.GRASS, Type.POISON, 2, 100, Abilities.OVERGROW, Abilities.NONE, Abilities.CHLOROPHYLL, 525, 80, 82, 83, 100, 100, 80, 45, 50, 263, true, null, true), new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.GRASS, Type.POISON, 2.4, 155.5, Abilities.THICK_FAT, Abilities.THICK_FAT, Abilities.THICK_FAT, 625, 80, 100, 123, 122, 120, 80, 45, 50, 263, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.POISON, 24, 100, Abilities.CHLOROPHYLL, Abilities.CHLOROPHYLL, Abilities.CHLOROPHYLL, 625, 120, 82, 98, 130, 115, 80, 45, 50, 263, true), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.POISON, 24, 999.9, Abilities.CHLOROPHYLL, Abilities.CHLOROPHYLL, Abilities.CHLOROPHYLL, 625, 120, 82, 98, 130, 115, 80, 45, 50, 263, true), ), new PokemonSpecies(Species.CHARMANDER, 1, false, false, false, "Lizard Pokémon", Type.FIRE, null, 0.6, 8.5, Abilities.BLAZE, Abilities.NONE, Abilities.SOLAR_POWER, 309, 39, 52, 43, 60, 50, 65, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.CHARMELEON, 1, false, false, false, "Flame Pokémon", Type.FIRE, null, 1.1, 19, Abilities.BLAZE, Abilities.NONE, Abilities.SOLAR_POWER, 405, 58, 64, 58, 80, 65, 80, 45, 50, 142, GrowthRate.MEDIUM_SLOW, 87.5, false), @@ -952,20 +952,20 @@ export function initSpecies() { new PokemonForm("Normal", "", Type.FIRE, Type.FLYING, 1.7, 90.5, Abilities.BLAZE, Abilities.NONE, Abilities.SOLAR_POWER, 534, 78, 84, 78, 109, 85, 100, 45, 50, 267, false, null, true), new PokemonForm("Mega X", SpeciesFormKey.MEGA_X, Type.FIRE, Type.DRAGON, 1.7, 110.5, Abilities.TOUGH_CLAWS, Abilities.NONE, Abilities.TOUGH_CLAWS, 634, 78, 130, 111, 130, 85, 100, 45, 50, 267), new PokemonForm("Mega Y", SpeciesFormKey.MEGA_Y, Type.FIRE, Type.FLYING, 1.7, 100.5, Abilities.DROUGHT, Abilities.NONE, Abilities.DROUGHT, 634, 78, 104, 78, 159, 115, 100, 45, 50, 267), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, Type.FLYING, 28, 90.5, Abilities.SOLAR_POWER, Abilities.SOLAR_POWER, Abilities.SOLAR_POWER, 634, 118, 84, 93, 139, 110, 100, 45, 50, 267), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, Type.FLYING, 28, 999.9, Abilities.BERSERK, Abilities.BERSERK, Abilities.BERSERK, 634, 118, 84, 93, 139, 110, 100, 45, 50, 267), ), new PokemonSpecies(Species.SQUIRTLE, 1, false, false, false, "Tiny Turtle Pokémon", Type.WATER, null, 0.5, 9, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 314, 44, 48, 65, 50, 64, 43, 45, 50, 63, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.WARTORTLE, 1, false, false, false, "Turtle Pokémon", Type.WATER, null, 1, 22.5, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 405, 59, 63, 80, 65, 80, 58, 45, 50, 142, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.BLASTOISE, 1, false, false, false, "Shellfish Pokémon", Type.WATER, null, 1.6, 85.5, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 530, 79, 83, 100, 85, 105, 78, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true, new PokemonForm("Normal", "", Type.WATER, null, 1.6, 85.5, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 530, 79, 83, 100, 85, 105, 78, 45, 50, 265, false, null, true), new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.WATER, null, 1.6, 101.1, Abilities.MEGA_LAUNCHER, Abilities.NONE, Abilities.MEGA_LAUNCHER, 630, 79, 103, 120, 135, 115, 78, 45, 50, 265), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.STEEL, 25, 85.5, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, 630, 119, 83, 130, 115, 115, 68, 45, 50, 265), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.STEEL, 25, 999.9, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, 630, 119, 83, 130, 115, 115, 68, 45, 50, 265), ), new PokemonSpecies(Species.CATERPIE, 1, false, false, false, "Worm Pokémon", Type.BUG, null, 0.3, 2.9, Abilities.SHIELD_DUST, Abilities.NONE, Abilities.RUN_AWAY, 195, 45, 30, 35, 20, 20, 45, 255, 50, 39, GrowthRate.MEDIUM_FAST, 50, false), 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.COMPOUND_EYES, Abilities.COMPOUND_EYES, Abilities.COMPOUND_EYES, 495, 85, 35, 80, 120, 90, 85, 45, 50, 198, true), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.FLYING, 17, 999.9, 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), @@ -994,7 +994,7 @@ export function initSpecies() { new PokemonForm("Cute Cosplay", "cute-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom new PokemonForm("Smart Cosplay", "smart-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom new PokemonForm("Tough Cosplay", "tough-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ELECTRIC, null, 21, 6, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, 530, 125, 95, 60, 90, 70, 90, 190, 50, 112), //+100 BST from Partner Form + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ELECTRIC, null, 21, 999.9, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, 530, 125, 95, 60, 90, 70, 90, 190, 50, 112), //+100 BST from Partner Form ), new PokemonSpecies(Species.RAICHU, 1, false, false, false, "Mouse Pokémon", Type.ELECTRIC, null, 0.8, 30, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 485, 60, 90, 55, 90, 80, 110, 75, 50, 243, GrowthRate.MEDIUM_FAST, 50, true), new PokemonSpecies(Species.SANDSHREW, 1, false, false, false, "Mouse Pokémon", Type.GROUND, null, 0.6, 12, Abilities.SAND_VEIL, Abilities.NONE, Abilities.SAND_RUSH, 300, 50, 75, 85, 20, 30, 40, 255, 50, 60, GrowthRate.MEDIUM_FAST, 50, false), @@ -1024,7 +1024,7 @@ export function initSpecies() { new PokemonSpecies(Species.DUGTRIO, 1, false, false, false, "Mole Pokémon", Type.GROUND, null, 0.7, 33.3, Abilities.SAND_VEIL, Abilities.ARENA_TRAP, Abilities.SAND_FORCE, 425, 35, 100, 50, 50, 70, 120, 50, 50, 149, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.MEOWTH, 1, false, false, false, "Scratch Cat Pokémon", Type.NORMAL, null, 0.4, 4.2, Abilities.PICKUP, Abilities.TECHNICIAN, Abilities.UNNERVE, 290, 40, 45, 35, 40, 40, 90, 255, 50, 58, GrowthRate.MEDIUM_FAST, 50, false, true, new PokemonForm("Normal", "", Type.NORMAL, null, 0.4, 4.2, Abilities.PICKUP, Abilities.TECHNICIAN, Abilities.UNNERVE, 290, 40, 45, 35, 40, 40, 90, 255, 50, 58, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, null, 33, 4.2, Abilities.TECHNICIAN, Abilities.TECHNICIAN, Abilities.TECHNICIAN, 540, 115, 110, 65, 65, 70, 115, 255, 50, 58), //+100 BST from Persian + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, null, 33, 999.9, Abilities.TECHNICIAN, Abilities.TECHNICIAN, Abilities.TECHNICIAN, 540, 115, 110, 65, 65, 70, 115, 255, 50, 58), //+100 BST from Persian ), new PokemonSpecies(Species.PERSIAN, 1, false, false, false, "Classy Cat Pokémon", Type.NORMAL, null, 1, 32, Abilities.LIMBER, Abilities.TECHNICIAN, Abilities.UNNERVE, 440, 65, 70, 60, 65, 65, 115, 90, 50, 154, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.PSYDUCK, 1, false, false, false, "Duck Pokémon", Type.WATER, null, 0.8, 19.6, Abilities.DAMP, Abilities.CLOUD_NINE, Abilities.SWIFT_SWIM, 320, 50, 52, 48, 65, 50, 55, 190, 50, 64, GrowthRate.MEDIUM_FAST, 50, false), @@ -1046,7 +1046,7 @@ export function initSpecies() { new PokemonSpecies(Species.MACHOKE, 1, false, false, false, "Superpower Pokémon", Type.FIGHTING, null, 1.5, 70.5, Abilities.GUTS, Abilities.NO_GUARD, Abilities.STEADFAST, 405, 80, 100, 70, 50, 60, 45, 90, 50, 142, GrowthRate.MEDIUM_SLOW, 75, false), new PokemonSpecies(Species.MACHAMP, 1, false, false, false, "Superpower Pokémon", Type.FIGHTING, null, 1.6, 130, Abilities.GUTS, Abilities.NO_GUARD, Abilities.STEADFAST, 505, 90, 130, 80, 65, 85, 55, 45, 50, 253, GrowthRate.MEDIUM_SLOW, 75, false, true, new PokemonForm("Normal", "", Type.FIGHTING, null, 1.6, 130, Abilities.GUTS, Abilities.NO_GUARD, Abilities.STEADFAST, 505, 90, 130, 80, 65, 85, 55, 45, 50, 253, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIGHTING, null, 25, 130, Abilities.GUTS, Abilities.GUTS, Abilities.GUTS, 605, 115, 170, 95, 65, 95, 65, 45, 50, 253), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIGHTING, null, 25, 999.9, Abilities.GUTS, Abilities.GUTS, Abilities.GUTS, 605, 115, 170, 95, 65, 95, 65, 45, 50, 253), ), new PokemonSpecies(Species.BELLSPROUT, 1, false, false, false, "Flower Pokémon", Type.GRASS, Type.POISON, 0.7, 4, Abilities.CHLOROPHYLL, Abilities.NONE, Abilities.GLUTTONY, 300, 50, 75, 35, 70, 30, 40, 255, 70, 60, GrowthRate.MEDIUM_SLOW, 50, false), new PokemonSpecies(Species.WEEPINBELL, 1, false, false, false, "Flycatcher Pokémon", Type.GRASS, Type.POISON, 1, 6.4, Abilities.CHLOROPHYLL, Abilities.NONE, Abilities.GLUTTONY, 390, 65, 90, 50, 85, 45, 55, 120, 70, 137, GrowthRate.MEDIUM_SLOW, 50, false), @@ -1079,7 +1079,7 @@ export function initSpecies() { new PokemonSpecies(Species.GENGAR, 1, false, false, false, "Shadow Pokémon", Type.GHOST, Type.POISON, 1.5, 40.5, Abilities.CURSED_BODY, Abilities.NONE, Abilities.NONE, 500, 60, 65, 60, 130, 75, 110, 45, 50, 250, GrowthRate.MEDIUM_SLOW, 50, false, true, new PokemonForm("Normal", "", Type.GHOST, Type.POISON, 1.5, 40.5, Abilities.CURSED_BODY, Abilities.NONE, Abilities.NONE, 500, 60, 65, 60, 130, 75, 110, 45, 50, 250, false, null, true), new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.GHOST, Type.POISON, 1.4, 40.5, Abilities.SHADOW_TAG, Abilities.NONE, Abilities.NONE, 600, 60, 65, 80, 170, 95, 130, 45, 50, 250), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GHOST, Type.POISON, 20, 40.5, Abilities.CURSED_BODY, Abilities.CURSED_BODY, Abilities.CURSED_BODY, 600, 140, 65, 70, 140, 85, 100, 45, 50, 250), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GHOST, Type.POISON, 20, 999.9, Abilities.CURSED_BODY, Abilities.CURSED_BODY, Abilities.CURSED_BODY, 600, 140, 65, 70, 140, 85, 100, 45, 50, 250), ), new PokemonSpecies(Species.ONIX, 1, false, false, false, "Rock Snake Pokémon", Type.ROCK, Type.GROUND, 8.8, 210, Abilities.ROCK_HEAD, Abilities.STURDY, Abilities.WEAK_ARMOR, 385, 35, 45, 160, 30, 45, 70, 45, 50, 77, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.DROWZEE, 1, false, false, false, "Hypnosis Pokémon", Type.PSYCHIC, null, 1, 32.4, Abilities.INSOMNIA, Abilities.FOREWARN, Abilities.INNER_FOCUS, 328, 60, 48, 45, 43, 90, 42, 190, 70, 66, GrowthRate.MEDIUM_FAST, 50, false), @@ -1087,7 +1087,7 @@ export function initSpecies() { new PokemonSpecies(Species.KRABBY, 1, false, false, false, "River Crab Pokémon", Type.WATER, null, 0.4, 6.5, Abilities.HYPER_CUTTER, Abilities.SHELL_ARMOR, Abilities.SHEER_FORCE, 325, 30, 105, 90, 25, 25, 50, 225, 50, 65, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.KINGLER, 1, false, false, false, "Pincer Pokémon", Type.WATER, null, 1.3, 60, Abilities.HYPER_CUTTER, Abilities.SHELL_ARMOR, Abilities.SHEER_FORCE, 475, 55, 130, 115, 50, 50, 75, 60, 50, 166, GrowthRate.MEDIUM_FAST, 50, false, true, new PokemonForm("Normal", "", Type.WATER, null, 1.3, 60, Abilities.HYPER_CUTTER, Abilities.SHELL_ARMOR, Abilities.SHEER_FORCE, 475, 55, 130, 115, 50, 50, 75, 60, 50, 166, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 19, 60, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, 575, 90, 155, 140, 50, 80, 70, 60, 50, 166), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 19, 999.9, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, 575, 90, 155, 140, 50, 80, 70, 60, 50, 166), ), new PokemonSpecies(Species.VOLTORB, 1, false, false, false, "Ball Pokémon", Type.ELECTRIC, null, 0.5, 10.4, Abilities.SOUNDPROOF, Abilities.STATIC, Abilities.AFTERMATH, 330, 40, 30, 50, 55, 55, 100, 190, 70, 66, GrowthRate.MEDIUM_FAST, null, false), new PokemonSpecies(Species.ELECTRODE, 1, false, false, false, "Ball Pokémon", Type.ELECTRIC, null, 1.2, 66.6, Abilities.SOUNDPROOF, Abilities.STATIC, Abilities.AFTERMATH, 490, 60, 50, 70, 80, 80, 150, 60, 70, 172, GrowthRate.MEDIUM_FAST, null, false), @@ -1131,13 +1131,13 @@ export function initSpecies() { ), new PokemonSpecies(Species.LAPRAS, 1, false, false, false, "Transport Pokémon", Type.WATER, Type.ICE, 2.5, 220, Abilities.WATER_ABSORB, Abilities.SHELL_ARMOR, Abilities.HYDRATION, 535, 130, 85, 80, 85, 95, 60, 45, 50, 187, GrowthRate.SLOW, 50, false, true, new PokemonForm("Normal", "", Type.WATER, Type.ICE, 2.5, 220, Abilities.WATER_ABSORB, Abilities.SHELL_ARMOR, Abilities.HYDRATION, 535, 130, 85, 80, 85, 95, 60, 45, 50, 187, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.ICE, 24, 220, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, 635, 170, 85, 95, 115, 110, 60, 45, 50, 187), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.ICE, 24, 999.9, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, 635, 170, 85, 95, 115, 110, 60, 45, 50, 187), ), new PokemonSpecies(Species.DITTO, 1, false, false, false, "Transform Pokémon", Type.NORMAL, null, 0.3, 4, Abilities.LIMBER, Abilities.NONE, Abilities.IMPOSTER, 288, 48, 48, 48, 48, 48, 48, 35, 50, 101, GrowthRate.MEDIUM_FAST, null, false), new PokemonSpecies(Species.EEVEE, 1, false, false, false, "Evolution Pokémon", Type.NORMAL, null, 0.3, 6.5, Abilities.RUN_AWAY, Abilities.ADAPTABILITY, Abilities.ANTICIPATION, 325, 55, 55, 50, 45, 65, 55, 45, 50, 65, GrowthRate.MEDIUM_FAST, 87.5, false, true, new PokemonForm("Normal", "", Type.NORMAL, null, 0.3, 6.5, Abilities.RUN_AWAY, Abilities.ADAPTABILITY, Abilities.ANTICIPATION, 325, 55, 55, 50, 45, 65, 55, 45, 50, 65, false, null, true), new PokemonForm("Partner", "partner", Type.NORMAL, null, 0.3, 6.5, Abilities.RUN_AWAY, Abilities.ADAPTABILITY, Abilities.ANTICIPATION, 435, 65, 75, 70, 65, 85, 75, 45, 50, 65, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, null, 18, 6.5, Abilities.PROTEAN, Abilities.PROTEAN, Abilities.PROTEAN, 535, 105, 95, 70, 95, 85, 85, 45, 50, 65), //+100 BST from Partner Form + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, null, 18, 999.9, Abilities.PROTEAN, Abilities.PROTEAN, Abilities.PROTEAN, 535, 110, 90, 70, 95, 85, 85, 45, 50, 65), //+100 BST from Partner Form ), new PokemonSpecies(Species.VAPOREON, 1, false, false, false, "Bubble Jet Pokémon", Type.WATER, null, 1, 29, Abilities.WATER_ABSORB, Abilities.NONE, Abilities.HYDRATION, 525, 130, 65, 60, 110, 95, 65, 45, 50, 184, GrowthRate.MEDIUM_FAST, 87.5, false), new PokemonSpecies(Species.JOLTEON, 1, false, false, false, "Lightning Pokémon", Type.ELECTRIC, null, 0.8, 24.5, Abilities.VOLT_ABSORB, Abilities.NONE, Abilities.QUICK_FEET, 525, 65, 65, 60, 110, 95, 130, 45, 50, 184, GrowthRate.MEDIUM_FAST, 87.5, false), @@ -1153,7 +1153,7 @@ export function initSpecies() { ), new PokemonSpecies(Species.SNORLAX, 1, false, false, false, "Sleeping Pokémon", Type.NORMAL, null, 2.1, 460, Abilities.IMMUNITY, Abilities.THICK_FAT, Abilities.GLUTTONY, 540, 160, 110, 65, 65, 110, 30, 25, 50, 189, GrowthRate.SLOW, 87.5, false, true, new PokemonForm("Normal", "", Type.NORMAL, null, 2.1, 460, Abilities.IMMUNITY, Abilities.THICK_FAT, Abilities.GLUTTONY, 540, 160, 110, 65, 65, 110, 30, 25, 50, 189, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, null, 35, 460, Abilities.HARVEST, Abilities.HARVEST, Abilities.HARVEST, 640, 200, 135, 80, 80, 125, 20, 25, 50, 189), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, null, 35, 999.9, Abilities.HARVEST, Abilities.HARVEST, Abilities.HARVEST, 640, 200, 135, 80, 80, 125, 20, 25, 50, 189), ), new PokemonSpecies(Species.ARTICUNO, 1, true, false, false, "Freeze Pokémon", Type.ICE, Type.FLYING, 1.7, 55.4, Abilities.PRESSURE, Abilities.NONE, Abilities.SNOW_CLOAK, 580, 90, 85, 100, 95, 125, 85, 3, 35, 290, GrowthRate.SLOW, null, false), new PokemonSpecies(Species.ZAPDOS, 1, true, false, false, "Electric Pokémon", Type.ELECTRIC, Type.FLYING, 1.6, 52.6, Abilities.PRESSURE, Abilities.NONE, Abilities.STATIC, 580, 90, 90, 85, 125, 90, 100, 3, 35, 290, GrowthRate.SLOW, null, false), @@ -1793,7 +1793,7 @@ export function initSpecies() { new PokemonSpecies(Species.TRUBBISH, 5, false, false, false, "Trash Bag Pokémon", Type.POISON, null, 0.6, 31, Abilities.STENCH, Abilities.STICKY_HOLD, Abilities.AFTERMATH, 329, 50, 50, 62, 40, 62, 65, 190, 50, 66, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.GARBODOR, 5, false, false, false, "Trash Heap Pokémon", Type.POISON, null, 1.9, 107.3, Abilities.STENCH, Abilities.WEAK_ARMOR, Abilities.AFTERMATH, 474, 80, 95, 82, 60, 82, 75, 60, 50, 166, GrowthRate.MEDIUM_FAST, 50, false, true, new PokemonForm("Normal", "", Type.POISON, null, 1.9, 107.3, Abilities.STENCH, Abilities.WEAK_ARMOR, Abilities.AFTERMATH, 474, 80, 95, 82, 60, 82, 75, 60, 50, 166, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.POISON, Type.STEEL, 21, 107.3, Abilities.TOXIC_DEBRIS, Abilities.TOXIC_DEBRIS, Abilities.TOXIC_DEBRIS, 574, 135, 125, 102, 57, 102, 53, 60, 50, 166), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.POISON, Type.STEEL, 21, 999.9, Abilities.TOXIC_DEBRIS, Abilities.TOXIC_DEBRIS, Abilities.TOXIC_DEBRIS, 574, 135, 125, 102, 57, 102, 53, 60, 50, 166), ), new PokemonSpecies(Species.ZORUA, 5, false, false, false, "Tricky Fox Pokémon", Type.DARK, null, 0.7, 12.5, Abilities.ILLUSION, Abilities.NONE, Abilities.NONE, 330, 40, 65, 40, 80, 40, 65, 75, 50, 66, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.ZOROARK, 5, false, false, false, "Illusion Fox Pokémon", Type.DARK, null, 1.6, 81.1, Abilities.ILLUSION, Abilities.NONE, Abilities.NONE, 510, 60, 105, 60, 120, 60, 105, 45, 50, 179, GrowthRate.MEDIUM_SLOW, 87.5, false), @@ -2267,25 +2267,25 @@ export function initSpecies() { new PokemonSpecies(Species.MELTAN, 7, false, false, true, "Hex Nut Pokémon", Type.STEEL, null, 0.2, 8, Abilities.MAGNET_PULL, Abilities.NONE, Abilities.NONE, 300, 46, 65, 65, 55, 35, 34, 3, 0, 150, GrowthRate.SLOW, null, false), new PokemonSpecies(Species.MELMETAL, 7, false, false, true, "Hex Nut Pokémon", Type.STEEL, null, 2.5, 800, Abilities.IRON_FIST, Abilities.NONE, Abilities.NONE, 600, 135, 143, 143, 80, 65, 34, 3, 0, 300, GrowthRate.SLOW, null, false, true, new PokemonForm("Normal", "", Type.STEEL, null, 2.5, 800, Abilities.IRON_FIST, Abilities.NONE, Abilities.NONE, 600, 135, 143, 143, 80, 65, 34, 3, 0, 300, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, null, 25, 800, Abilities.IRON_FIST, Abilities.IRON_FIST, Abilities.IRON_FIST, 700, 175, 165, 155, 85, 75, 45, 3, 0, 300), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, null, 25, 999.9, Abilities.IRON_FIST, Abilities.IRON_FIST, Abilities.IRON_FIST, 700, 175, 165, 155, 85, 75, 45, 3, 0, 300), ), new PokemonSpecies(Species.GROOKEY, 8, false, false, false, "Chimp Pokémon", Type.GRASS, null, 0.3, 5, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 310, 50, 65, 50, 40, 40, 65, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.THWACKEY, 8, false, false, false, "Beat Pokémon", Type.GRASS, null, 0.7, 14, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 420, 70, 85, 70, 55, 60, 80, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.RILLABOOM, 8, false, false, false, "Drummer Pokémon", Type.GRASS, null, 2.1, 90, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 530, 100, 125, 90, 60, 70, 85, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true, new PokemonForm("Normal", "", Type.GRASS, null, 2.1, 90, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 530, 100, 125, 90, 60, 70, 85, 45, 50, 265, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, null, 28, 90, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, 630, 125, 150, 105, 85, 85, 80, 45, 50, 265), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, null, 28, 999.9, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, 630, 125, 150, 105, 85, 85, 80, 45, 50, 265), ), new PokemonSpecies(Species.SCORBUNNY, 8, false, false, false, "Rabbit Pokémon", Type.FIRE, null, 0.3, 4.5, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 310, 50, 71, 40, 40, 40, 69, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.RABOOT, 8, false, false, false, "Rabbit Pokémon", Type.FIRE, null, 0.6, 9, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 420, 65, 86, 60, 55, 60, 94, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.CINDERACE, 8, false, false, false, "Striker Pokémon", Type.FIRE, null, 1.4, 33, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 530, 80, 116, 75, 65, 75, 119, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true, new PokemonForm("Normal", "", Type.FIRE, null, 1.4, 33, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 530, 80, 116, 75, 65, 75, 119, 45, 50, 265, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, null, 27, 33, Abilities.LIBERO, Abilities.LIBERO, Abilities.LIBERO, 630, 100, 146, 80, 90, 80, 134, 45, 50, 265), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, null, 27, 999.9, Abilities.LIBERO, Abilities.LIBERO, Abilities.LIBERO, 630, 100, 146, 80, 90, 80, 134, 45, 50, 265), ), new PokemonSpecies(Species.SOBBLE, 8, false, false, false, "Water Lizard Pokémon", Type.WATER, null, 0.3, 4, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 310, 50, 40, 40, 70, 40, 70, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.DRIZZILE, 8, false, false, false, "Water Lizard Pokémon", Type.WATER, null, 0.7, 11.5, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 420, 65, 60, 55, 95, 55, 90, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.INTELEON, 8, false, false, false, "Secret Agent Pokémon", Type.WATER, null, 1.9, 45.2, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 530, 70, 85, 65, 125, 65, 120, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true, new PokemonForm("Normal", "", Type.WATER, null, 1.9, 45.2, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 530, 70, 85, 65, 125, 65, 120, 45, 50, 265, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 40, 45.2, Abilities.SNIPER, Abilities.SNIPER, Abilities.SNIPER, 630, 95, 97, 77, 147, 77, 137, 45, 50, 265), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 40, 999.9, Abilities.SNIPER, Abilities.SNIPER, Abilities.SNIPER, 630, 95, 97, 77, 147, 77, 137, 45, 50, 265), ), new PokemonSpecies(Species.SKWOVET, 8, false, false, false, "Cheeky Pokémon", Type.NORMAL, null, 0.3, 2.5, Abilities.CHEEK_POUCH, Abilities.NONE, Abilities.GLUTTONY, 275, 70, 55, 55, 35, 35, 25, 255, 50, 55, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.GREEDENT, 8, false, false, false, "Greedy Pokémon", Type.NORMAL, null, 0.6, 6, Abilities.CHEEK_POUCH, Abilities.NONE, Abilities.GLUTTONY, 460, 120, 95, 95, 55, 75, 20, 90, 50, 161, GrowthRate.MEDIUM_FAST, 50, false), @@ -2293,13 +2293,13 @@ export function initSpecies() { new PokemonSpecies(Species.CORVISQUIRE, 8, false, false, false, "Raven Pokémon", Type.FLYING, null, 0.8, 16, Abilities.KEEN_EYE, Abilities.UNNERVE, Abilities.BIG_PECKS, 365, 68, 67, 55, 43, 55, 77, 120, 50, 128, GrowthRate.MEDIUM_SLOW, 50, false), new PokemonSpecies(Species.CORVIKNIGHT, 8, false, false, false, "Raven Pokémon", Type.FLYING, Type.STEEL, 2.2, 75, Abilities.PRESSURE, Abilities.UNNERVE, Abilities.MIRROR_ARMOR, 495, 98, 87, 105, 53, 85, 67, 45, 50, 248, GrowthRate.MEDIUM_SLOW, 50, false, true, new PokemonForm("Normal", "", Type.FLYING, Type.STEEL, 2.2, 75, Abilities.PRESSURE, Abilities.UNNERVE, Abilities.MIRROR_ARMOR, 495, 98, 87, 105, 53, 85, 67, 45, 50, 248, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FLYING, Type.STEEL, 14, 75, Abilities.MIRROR_ARMOR, Abilities.MIRROR_ARMOR, Abilities.MIRROR_ARMOR, 595, 128, 102, 140, 53, 95, 77, 45, 50, 248), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FLYING, Type.STEEL, 14, 999.9, Abilities.MIRROR_ARMOR, Abilities.MIRROR_ARMOR, Abilities.MIRROR_ARMOR, 595, 128, 102, 140, 53, 95, 77, 45, 50, 248), ), new PokemonSpecies(Species.BLIPBUG, 8, false, false, false, "Larva Pokémon", Type.BUG, null, 0.4, 8, Abilities.SWARM, Abilities.COMPOUND_EYES, Abilities.TELEPATHY, 180, 25, 20, 20, 25, 45, 45, 255, 50, 36, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.DOTTLER, 8, false, false, false, "Radome Pokémon", Type.BUG, Type.PSYCHIC, 0.4, 19.5, Abilities.SWARM, Abilities.COMPOUND_EYES, Abilities.TELEPATHY, 335, 50, 35, 80, 50, 90, 30, 120, 50, 117, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.ORBEETLE, 8, false, false, false, "Seven Spot Pokémon", Type.BUG, Type.PSYCHIC, 0.4, 40.8, Abilities.SWARM, Abilities.FRISK, Abilities.TELEPATHY, 505, 60, 45, 110, 80, 120, 90, 45, 50, 253, GrowthRate.MEDIUM_FAST, 50, false, true, new PokemonForm("Normal", "", Type.BUG, Type.PSYCHIC, 0.4, 40.8, Abilities.SWARM, Abilities.FRISK, Abilities.TELEPATHY, 505, 60, 45, 110, 80, 120, 90, 45, 50, 253, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.PSYCHIC, 14, 40.8, Abilities.TRACE, Abilities.TRACE, Abilities.TRACE, 605, 90, 45, 130, 110, 140, 90, 45, 50, 253), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.PSYCHIC, 14, 999.9, Abilities.TRACE, Abilities.TRACE, Abilities.TRACE, 605, 90, 45, 130, 110, 140, 90, 45, 50, 253), ), new PokemonSpecies(Species.NICKIT, 8, false, false, false, "Fox Pokémon", Type.DARK, null, 0.6, 8.9, Abilities.RUN_AWAY, Abilities.UNBURDEN, Abilities.STAKEOUT, 245, 40, 28, 28, 47, 52, 50, 255, 50, 49, GrowthRate.FAST, 50, false), new PokemonSpecies(Species.THIEVUL, 8, false, false, false, "Fox Pokémon", Type.DARK, null, 1.2, 19.9, Abilities.RUN_AWAY, Abilities.UNBURDEN, Abilities.STAKEOUT, 455, 70, 58, 58, 87, 92, 90, 127, 50, 159, GrowthRate.FAST, 50, false), @@ -2310,7 +2310,7 @@ export function initSpecies() { new PokemonSpecies(Species.CHEWTLE, 8, false, false, false, "Snapping Pokémon", Type.WATER, null, 0.3, 8.5, Abilities.STRONG_JAW, Abilities.SHELL_ARMOR, Abilities.SWIFT_SWIM, 284, 50, 64, 50, 38, 38, 44, 255, 50, 57, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.DREDNAW, 8, false, false, false, "Bite Pokémon", Type.WATER, Type.ROCK, 1, 115.5, Abilities.STRONG_JAW, Abilities.SHELL_ARMOR, Abilities.SWIFT_SWIM, 485, 90, 115, 90, 48, 68, 74, 75, 50, 170, GrowthRate.MEDIUM_FAST, 50, false, true, new PokemonForm("Normal", "", Type.WATER, Type.ROCK, 1, 115.5, Abilities.STRONG_JAW, Abilities.SHELL_ARMOR, Abilities.SWIFT_SWIM, 485, 90, 115, 90, 48, 68, 74, 75, 50, 170, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.ROCK, 24, 115.5, Abilities.STRONG_JAW, Abilities.STRONG_JAW, Abilities.STRONG_JAW, 585, 115, 145, 115, 43, 83, 84, 75, 50, 170), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.ROCK, 24, 999.9, Abilities.STRONG_JAW, Abilities.STRONG_JAW, Abilities.STRONG_JAW, 585, 115, 145, 115, 43, 83, 84, 75, 50, 170), ), new PokemonSpecies(Species.YAMPER, 8, false, false, false, "Puppy Pokémon", Type.ELECTRIC, null, 0.3, 13.5, Abilities.BALL_FETCH, Abilities.NONE, Abilities.RATTLED, 270, 59, 45, 50, 40, 50, 26, 255, 50, 54, GrowthRate.FAST, 50, false), new PokemonSpecies(Species.BOLTUND, 8, false, false, false, "Dog Pokémon", Type.ELECTRIC, null, 1, 34, Abilities.STRONG_JAW, Abilities.NONE, Abilities.COMPETITIVE, 490, 69, 90, 60, 90, 60, 121, 45, 50, 172, GrowthRate.FAST, 50, false), @@ -2318,21 +2318,21 @@ export function initSpecies() { new PokemonSpecies(Species.CARKOL, 8, false, false, false, "Coal Pokémon", Type.ROCK, Type.FIRE, 1.1, 78, Abilities.STEAM_ENGINE, Abilities.FLAME_BODY, Abilities.FLASH_FIRE, 410, 80, 60, 90, 60, 70, 50, 120, 50, 144, GrowthRate.MEDIUM_SLOW, 50, false), new PokemonSpecies(Species.COALOSSAL, 8, false, false, false, "Coal Pokémon", Type.ROCK, Type.FIRE, 2.8, 310.5, Abilities.STEAM_ENGINE, Abilities.FLAME_BODY, Abilities.FLASH_FIRE, 510, 110, 80, 120, 80, 90, 30, 45, 50, 255, GrowthRate.MEDIUM_SLOW, 50, false, true, new PokemonForm("Normal", "", Type.ROCK, Type.FIRE, 2.8, 310.5, Abilities.STEAM_ENGINE, Abilities.FLAME_BODY, Abilities.FLASH_FIRE, 510, 110, 80, 120, 80, 90, 30, 45, 50, 255, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ROCK, Type.FIRE, 42, 310.5, Abilities.STEAM_ENGINE, Abilities.STEAM_ENGINE, Abilities.STEAM_ENGINE, 610, 140, 95, 130, 95, 110, 40, 45, 50, 255), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ROCK, Type.FIRE, 42, 999.9, Abilities.STEAM_ENGINE, Abilities.STEAM_ENGINE, Abilities.STEAM_ENGINE, 610, 140, 95, 130, 95, 110, 40, 45, 50, 255), ), new PokemonSpecies(Species.APPLIN, 8, false, false, false, "Apple Core Pokémon", Type.GRASS, Type.DRAGON, 0.2, 0.5, Abilities.RIPEN, Abilities.GLUTTONY, Abilities.BULLETPROOF, 260, 40, 40, 80, 40, 40, 20, 255, 50, 52, GrowthRate.ERRATIC, 50, false), new PokemonSpecies(Species.FLAPPLE, 8, false, false, false, "Apple Wing Pokémon", Type.GRASS, Type.DRAGON, 0.3, 1, Abilities.RIPEN, Abilities.GLUTTONY, Abilities.HUSTLE, 485, 70, 110, 80, 95, 60, 70, 45, 50, 170, GrowthRate.ERRATIC, 50, false, true, new PokemonForm("Normal", "", Type.GRASS, Type.DRAGON, 0.3, 1, Abilities.RIPEN, Abilities.GLUTTONY, Abilities.HUSTLE, 485, 70, 110, 80, 95, 60, 70, 45, 50, 170, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.DRAGON, 24, 1, Abilities.HUSTLE, Abilities.HUSTLE, Abilities.HUSTLE, 585, 90, 130, 100, 85, 80, 100, 45, 50, 170), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.DRAGON, 24, 999.9, Abilities.HUSTLE, Abilities.HUSTLE, Abilities.HUSTLE, 585, 90, 130, 100, 85, 80, 100, 45, 50, 170), ), new PokemonSpecies(Species.APPLETUN, 8, false, false, false, "Apple Nectar Pokémon", Type.GRASS, Type.DRAGON, 0.4, 13, Abilities.RIPEN, Abilities.GLUTTONY, Abilities.THICK_FAT, 485, 110, 85, 80, 100, 80, 30, 45, 50, 170, GrowthRate.ERRATIC, 50, false, true, new PokemonForm("Normal", "", Type.GRASS, Type.DRAGON, 0.4, 13, Abilities.RIPEN, Abilities.GLUTTONY, Abilities.THICK_FAT, 485, 110, 85, 80, 100, 80, 30, 45, 50, 170, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.DRAGON, 24, 13, Abilities.THICK_FAT, Abilities.THICK_FAT, Abilities.THICK_FAT, 585, 130, 75, 115, 125, 115, 25, 45, 50, 170), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.DRAGON, 24, 999.9, Abilities.THICK_FAT, Abilities.THICK_FAT, Abilities.THICK_FAT, 585, 130, 75, 115, 125, 115, 25, 45, 50, 170), ), new PokemonSpecies(Species.SILICOBRA, 8, false, false, false, "Sand Snake Pokémon", Type.GROUND, null, 2.2, 7.6, Abilities.SAND_SPIT, Abilities.SHED_SKIN, Abilities.SAND_VEIL, 315, 52, 57, 75, 35, 50, 46, 255, 50, 63, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.SANDACONDA, 8, false, false, false, "Sand Snake Pokémon", Type.GROUND, null, 3.8, 65.5, Abilities.SAND_SPIT, Abilities.SHED_SKIN, Abilities.SAND_VEIL, 510, 72, 107, 125, 65, 70, 71, 120, 50, 179, GrowthRate.MEDIUM_FAST, 50, false, true, new PokemonForm("Normal", "", Type.GROUND, null, 3.8, 65.5, Abilities.SAND_SPIT, Abilities.SHED_SKIN, Abilities.SAND_VEIL, 510, 72, 107, 125, 65, 70, 71, 120, 50, 179, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GROUND, null, 22, 65.5, Abilities.SAND_SPIT, Abilities.SAND_SPIT, Abilities.SAND_SPIT, 610, 117, 137, 140, 55, 80, 81, 120, 50, 179), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GROUND, null, 22, 999.9, Abilities.SAND_SPIT, Abilities.SAND_SPIT, Abilities.SAND_SPIT, 610, 117, 137, 140, 55, 80, 81, 120, 50, 179), ), new PokemonSpecies(Species.CRAMORANT, 8, false, false, false, "Gulp Pokémon", Type.FLYING, Type.WATER, 0.8, 18, Abilities.GULP_MISSILE, Abilities.NONE, Abilities.NONE, 475, 70, 85, 55, 85, 95, 85, 45, 50, 166, GrowthRate.MEDIUM_FAST, 50, false, false, new PokemonForm("Normal", "", Type.FLYING, Type.WATER, 0.8, 18, Abilities.GULP_MISSILE, Abilities.NONE, Abilities.NONE, 475, 70, 85, 55, 85, 95, 85, 45, 50, 166, false, null, true), @@ -2345,12 +2345,12 @@ export function initSpecies() { new PokemonSpecies(Species.TOXTRICITY, 8, false, false, false, "Punk Pokémon", Type.ELECTRIC, Type.POISON, 1.6, 40, Abilities.PUNK_ROCK, Abilities.PLUS, Abilities.TECHNICIAN, 502, 75, 98, 70, 114, 70, 75, 45, 50, 176, GrowthRate.MEDIUM_SLOW, 50, false, true, new PokemonForm("Amped Form", "amped", Type.ELECTRIC, Type.POISON, 1.6, 40, Abilities.PUNK_ROCK, Abilities.PLUS, Abilities.TECHNICIAN, 502, 75, 98, 70, 114, 70, 75, 45, 50, 176, false, "", true), new PokemonForm("Low-Key Form", "lowkey", Type.ELECTRIC, Type.POISON, 1.6, 40, Abilities.PUNK_ROCK, Abilities.MINUS, Abilities.TECHNICIAN, 502, 75, 98, 70, 114, 70, 75, 45, 50, 176, false, "lowkey", true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ELECTRIC, Type.POISON, 24, 40, Abilities.PUNK_ROCK, Abilities.PUNK_ROCK, Abilities.PUNK_ROCK, 602, 114, 98, 82, 144, 82, 82, 45, 50, 176), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ELECTRIC, Type.POISON, 24, 999.9, Abilities.PUNK_ROCK, Abilities.PUNK_ROCK, Abilities.PUNK_ROCK, 602, 114, 98, 82, 144, 82, 82, 45, 50, 176), ), new PokemonSpecies(Species.SIZZLIPEDE, 8, false, false, false, "Radiator Pokémon", Type.FIRE, Type.BUG, 0.7, 1, Abilities.FLASH_FIRE, Abilities.WHITE_SMOKE, Abilities.FLAME_BODY, 305, 50, 65, 45, 50, 50, 45, 190, 50, 61, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.CENTISKORCH, 8, false, false, false, "Radiator Pokémon", Type.FIRE, Type.BUG, 3, 120, Abilities.FLASH_FIRE, Abilities.WHITE_SMOKE, Abilities.FLAME_BODY, 525, 100, 115, 65, 90, 90, 65, 75, 50, 184, GrowthRate.MEDIUM_FAST, 50, false, true, new PokemonForm("Normal", "", Type.FIRE, Type.BUG, 3, 120, Abilities.FLASH_FIRE, Abilities.WHITE_SMOKE, Abilities.FLAME_BODY, 525, 100, 115, 65, 90, 90, 65, 75, 50, 184, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, Type.BUG, 75, 120, Abilities.FLASH_FIRE, Abilities.FLASH_FIRE, Abilities.FLASH_FIRE, 625, 140, 145, 75, 90, 100, 75, 75, 50, 184), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, Type.BUG, 75, 999.9, Abilities.FLASH_FIRE, Abilities.FLASH_FIRE, Abilities.FLASH_FIRE, 625, 140, 145, 75, 90, 100, 75, 75, 50, 184), ), new PokemonSpecies(Species.CLOBBOPUS, 8, false, false, false, "Tantrum Pokémon", Type.FIGHTING, null, 0.6, 4, Abilities.LIMBER, Abilities.NONE, Abilities.TECHNICIAN, 310, 50, 68, 60, 50, 50, 32, 180, 50, 62, GrowthRate.MEDIUM_SLOW, 50, false), new PokemonSpecies(Species.GRAPPLOCT, 8, false, false, false, "Jujitsu Pokémon", Type.FIGHTING, null, 1.6, 39, Abilities.LIMBER, Abilities.NONE, Abilities.TECHNICIAN, 480, 80, 118, 90, 70, 80, 42, 45, 50, 168, GrowthRate.MEDIUM_SLOW, 50, false), @@ -2366,13 +2366,13 @@ export function initSpecies() { new PokemonSpecies(Species.HATTREM, 8, false, false, false, "Serene Pokémon", Type.PSYCHIC, null, 0.6, 4.8, Abilities.HEALER, Abilities.ANTICIPATION, Abilities.MAGIC_BOUNCE, 370, 57, 40, 65, 86, 73, 49, 120, 50, 130, GrowthRate.SLOW, 0, false), new PokemonSpecies(Species.HATTERENE, 8, false, false, false, "Silent Pokémon", Type.PSYCHIC, Type.FAIRY, 2.1, 5.1, Abilities.HEALER, Abilities.ANTICIPATION, Abilities.MAGIC_BOUNCE, 510, 57, 90, 95, 136, 103, 29, 45, 50, 255, GrowthRate.SLOW, 0, false, true, new PokemonForm("Normal", "", Type.PSYCHIC, Type.FAIRY, 2.1, 5.1, Abilities.HEALER, Abilities.ANTICIPATION, Abilities.MAGIC_BOUNCE, 510, 57, 90, 95, 136, 103, 29, 45, 50, 255, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.PSYCHIC, Type.FAIRY, 26, 5.1, Abilities.MAGIC_BOUNCE, Abilities.MAGIC_BOUNCE, Abilities.MAGIC_BOUNCE, 610, 97, 90, 105, 146, 122, 50, 45, 50, 255), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.PSYCHIC, Type.FAIRY, 26, 999.9, Abilities.MAGIC_BOUNCE, Abilities.MAGIC_BOUNCE, Abilities.MAGIC_BOUNCE, 610, 97, 90, 105, 146, 122, 50, 45, 50, 255), ), new PokemonSpecies(Species.IMPIDIMP, 8, false, false, false, "Wily Pokémon", Type.DARK, Type.FAIRY, 0.4, 5.5, Abilities.PRANKSTER, Abilities.FRISK, Abilities.PICKPOCKET, 265, 45, 45, 30, 55, 40, 50, 255, 50, 53, GrowthRate.MEDIUM_FAST, 100, false), new PokemonSpecies(Species.MORGREM, 8, false, false, false, "Devious Pokémon", Type.DARK, Type.FAIRY, 0.8, 12.5, Abilities.PRANKSTER, Abilities.FRISK, Abilities.PICKPOCKET, 370, 65, 60, 45, 75, 55, 70, 120, 50, 130, GrowthRate.MEDIUM_FAST, 100, false), new PokemonSpecies(Species.GRIMMSNARL, 8, false, false, false, "Bulk Up Pokémon", Type.DARK, Type.FAIRY, 1.5, 61, Abilities.PRANKSTER, Abilities.FRISK, Abilities.PICKPOCKET, 510, 95, 120, 65, 95, 75, 60, 45, 50, 255, GrowthRate.MEDIUM_FAST, 100, false, true, new PokemonForm("Normal", "", Type.DARK, Type.FAIRY, 1.5, 61, Abilities.PRANKSTER, Abilities.FRISK, Abilities.PICKPOCKET, 510, 95, 120, 65, 95, 75, 60, 45, 50, 255, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.DARK, Type.FAIRY, 32, 61, Abilities.PRANKSTER, Abilities.PRANKSTER, Abilities.PRANKSTER, 610, 135, 138, 77, 110, 85, 65, 45, 50, 255), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.DARK, Type.FAIRY, 32, 999.9, Abilities.PRANKSTER, Abilities.PRANKSTER, Abilities.PRANKSTER, 610, 135, 138, 77, 110, 85, 65, 45, 50, 255), ), new PokemonSpecies(Species.OBSTAGOON, 8, false, false, false, "Blocking Pokémon", Type.DARK, Type.NORMAL, 1.6, 46, Abilities.RECKLESS, Abilities.GUTS, Abilities.DEFIANT, 520, 93, 90, 101, 60, 81, 95, 45, 50, 260, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.PERRSERKER, 8, false, false, false, "Viking Pokémon", Type.STEEL, null, 0.8, 28, Abilities.BATTLE_ARMOR, Abilities.TOUGH_CLAWS, Abilities.STEELY_SPIRIT, 440, 70, 110, 100, 50, 60, 50, 90, 50, 154, GrowthRate.MEDIUM_FAST, 50, false), @@ -2391,7 +2391,7 @@ export function initSpecies() { new PokemonForm("Ruby Swirl", "ruby-swirl", Type.FAIRY, null, 0.3, 0.5, Abilities.SWEET_VEIL, Abilities.NONE, Abilities.AROMA_VEIL, 495, 65, 60, 75, 110, 121, 64, 100, 50, 173, false, null, true), new PokemonForm("Caramel Swirl", "caramel-swirl", Type.FAIRY, null, 0.3, 0.5, Abilities.SWEET_VEIL, Abilities.NONE, Abilities.AROMA_VEIL, 495, 65, 60, 75, 110, 121, 64, 100, 50, 173, false, null, true), new PokemonForm("Rainbow Swirl", "rainbow-swirl", Type.FAIRY, null, 0.3, 0.5, Abilities.SWEET_VEIL, Abilities.NONE, Abilities.AROMA_VEIL, 495, 65, 60, 75, 110, 121, 64, 100, 50, 173, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FAIRY, null, 30, 0.5, Abilities.MISTY_SURGE, Abilities.MISTY_SURGE, Abilities.MISTY_SURGE, 595, 135, 60, 75, 130, 131, 64, 100, 50, 173), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FAIRY, null, 30, 999.9, Abilities.MISTY_SURGE, Abilities.MISTY_SURGE, Abilities.MISTY_SURGE, 595, 135, 60, 75, 130, 131, 64, 100, 50, 173), ), new PokemonSpecies(Species.FALINKS, 8, false, false, false, "Formation Pokémon", Type.FIGHTING, null, 3, 62, Abilities.BATTLE_ARMOR, Abilities.NONE, Abilities.DEFIANT, 470, 65, 100, 100, 70, 60, 75, 45, 50, 165, GrowthRate.MEDIUM_FAST, null, false), new PokemonSpecies(Species.PINCURCHIN, 8, false, false, false, "Sea Urchin Pokémon", Type.ELECTRIC, null, 0.3, 1, Abilities.LIGHTNING_ROD, Abilities.NONE, Abilities.ELECTRIC_SURGE, 435, 48, 101, 95, 91, 85, 15, 75, 50, 152, GrowthRate.MEDIUM_FAST, 50, false), @@ -2413,7 +2413,7 @@ export function initSpecies() { new PokemonSpecies(Species.CUFANT, 8, false, false, false, "Copperderm Pokémon", Type.STEEL, null, 1.2, 100, Abilities.SHEER_FORCE, Abilities.NONE, Abilities.HEAVY_METAL, 330, 72, 80, 49, 40, 49, 40, 190, 50, 66, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.COPPERAJAH, 8, false, false, false, "Copperderm Pokémon", Type.STEEL, null, 3, 650, Abilities.SHEER_FORCE, Abilities.NONE, Abilities.HEAVY_METAL, 500, 122, 130, 69, 80, 69, 30, 90, 50, 175, GrowthRate.MEDIUM_FAST, 50, false, true, new PokemonForm("Normal", "", Type.STEEL, null, 3, 650, Abilities.SHEER_FORCE, Abilities.NONE, Abilities.HEAVY_METAL, 500, 122, 130, 69, 80, 69, 30, 90, 50, 175, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, Type.GROUND, 23, 650, Abilities.MOLD_BREAKER, Abilities.MOLD_BREAKER, Abilities.MOLD_BREAKER, 600, 167, 155, 89, 80, 89, 20, 90, 50, 175), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, Type.GROUND, 23, 999.9, Abilities.MOLD_BREAKER, Abilities.MOLD_BREAKER, Abilities.MOLD_BREAKER, 600, 167, 155, 89, 80, 89, 20, 90, 50, 175), ), new PokemonSpecies(Species.DRACOZOLT, 8, false, false, false, "Fossil Pokémon", Type.ELECTRIC, Type.DRAGON, 1.8, 190, Abilities.VOLT_ABSORB, Abilities.HUSTLE, Abilities.SAND_RUSH, 505, 90, 100, 90, 80, 70, 75, 45, 50, 177, GrowthRate.SLOW, null, false), new PokemonSpecies(Species.ARCTOZOLT, 8, false, false, false, "Fossil Pokémon", Type.ELECTRIC, Type.ICE, 2.3, 150, Abilities.VOLT_ABSORB, Abilities.STATIC, Abilities.SLUSH_RUSH, 505, 90, 100, 90, 90, 80, 55, 45, 50, 177, GrowthRate.SLOW, null, false), @@ -2421,7 +2421,7 @@ export function initSpecies() { new PokemonSpecies(Species.ARCTOVISH, 8, false, false, false, "Fossil Pokémon", Type.WATER, Type.ICE, 2, 175, Abilities.WATER_ABSORB, Abilities.ICE_BODY, Abilities.SLUSH_RUSH, 505, 90, 90, 100, 80, 90, 55, 45, 50, 177, GrowthRate.SLOW, null, false), new PokemonSpecies(Species.DURALUDON, 8, false, false, false, "Alloy Pokémon", Type.STEEL, Type.DRAGON, 1.8, 40, Abilities.LIGHT_METAL, Abilities.HEAVY_METAL, Abilities.STALWART, 535, 70, 95, 115, 120, 50, 85, 45, 50, 187, GrowthRate.MEDIUM_FAST, 50, false, true, new PokemonForm("Normal", "", Type.STEEL, Type.DRAGON, 1.8, 40, Abilities.LIGHT_METAL, Abilities.HEAVY_METAL, Abilities.STALWART, 535, 70, 95, 115, 120, 50, 85, 45, 50, 187, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, Type.DRAGON, 43, 40, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, 635, 100, 105, 119, 166, 57, 88, 45, 50, 187), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, Type.DRAGON, 43, 999.9, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, 635, 100, 110, 120, 175, 60, 70, 45, 50, 187), ), new PokemonSpecies(Species.DREEPY, 8, false, false, false, "Lingering Pokémon", Type.DRAGON, Type.GHOST, 0.5, 2, Abilities.CLEAR_BODY, Abilities.INFILTRATOR, Abilities.CURSED_BODY, 270, 28, 60, 30, 40, 30, 82, 45, 50, 54, GrowthRate.SLOW, 50, false), new PokemonSpecies(Species.DRAKLOAK, 8, false, false, false, "Caretaker Pokémon", Type.DRAGON, Type.GHOST, 1.4, 11, Abilities.CLEAR_BODY, Abilities.INFILTRATOR, Abilities.CURSED_BODY, 410, 68, 80, 50, 60, 50, 102, 45, 50, 144, GrowthRate.SLOW, 50, false), @@ -2436,14 +2436,14 @@ export function initSpecies() { ), new PokemonSpecies(Species.ETERNATUS, 8, false, true, false, "Gigantic Pokémon", Type.POISON, Type.DRAGON, 20, 950, Abilities.PRESSURE, Abilities.NONE, Abilities.NONE, 690, 140, 85, 95, 145, 95, 130, 255, 0, 345, GrowthRate.SLOW, null, false, true, new PokemonForm("Normal", "", Type.POISON, Type.DRAGON, 20, 950, Abilities.PRESSURE, Abilities.NONE, Abilities.NONE, 690, 140, 85, 95, 145, 95, 130, 255, 0, 345, false, null, true), - new PokemonForm("E-Max", "eternamax", Type.POISON, Type.DRAGON, 100, 0, Abilities.PRESSURE, Abilities.NONE, Abilities.NONE, 1125, 255, 115, 250, 125, 250, 130, 255, 0, 345), + new PokemonForm("E-Max", "eternamax", Type.POISON, Type.DRAGON, 100, 999.9, Abilities.PRESSURE, Abilities.NONE, Abilities.NONE, 1125, 255, 115, 250, 125, 250, 130, 255, 0, 345), ), new PokemonSpecies(Species.KUBFU, 8, true, false, false, "Wushu Pokémon", Type.FIGHTING, null, 0.6, 12, Abilities.INNER_FOCUS, Abilities.NONE, Abilities.NONE, 385, 60, 90, 60, 53, 50, 72, 3, 50, 77, GrowthRate.SLOW, 87.5, false), new PokemonSpecies(Species.URSHIFU, 8, true, false, false, "Wushu Pokémon", Type.FIGHTING, Type.DARK, 1.9, 105, Abilities.UNSEEN_FIST, Abilities.NONE, Abilities.NONE, 550, 100, 130, 100, 63, 60, 97, 3, 50, 275, GrowthRate.SLOW, 87.5, false, true, new PokemonForm("Single Strike Style", "single-strike", Type.FIGHTING, Type.DARK, 1.9, 105, Abilities.UNSEEN_FIST, Abilities.NONE, Abilities.NONE, 550, 100, 130, 100, 63, 60, 97, 3, 50, 275, false, "", true), new PokemonForm("Rapid Strike Style", "rapid-strike", Type.FIGHTING, Type.WATER, 1.9, 105, Abilities.UNSEEN_FIST, Abilities.NONE, Abilities.NONE, 550, 100, 130, 100, 63, 60, 97, 3, 50, 275, false, null, true), - new PokemonForm("G-Max Single Strike Style", SpeciesFormKey.GIGANTAMAX_SINGLE, Type.FIGHTING, Type.DARK, 29, 105, Abilities.UNSEEN_FIST, Abilities.NONE, Abilities.NONE, 650, 125, 150, 115, 73, 70, 117, 3, 50, 275), - new PokemonForm("G-Max Rapid Strike Style", SpeciesFormKey.GIGANTAMAX_RAPID, Type.FIGHTING, Type.WATER, 26, 105, Abilities.UNSEEN_FIST, Abilities.NONE, Abilities.NONE, 650, 125, 150, 115, 73, 70, 117, 3, 50, 275), + new PokemonForm("G-Max Single Strike Style", SpeciesFormKey.GIGANTAMAX_SINGLE, Type.FIGHTING, Type.DARK, 29, 999.9, Abilities.UNSEEN_FIST, Abilities.NONE, Abilities.NONE, 650, 125, 150, 115, 73, 70, 117, 3, 50, 275), + new PokemonForm("G-Max Rapid Strike Style", SpeciesFormKey.GIGANTAMAX_RAPID, Type.FIGHTING, Type.WATER, 26, 999.9, Abilities.UNSEEN_FIST, Abilities.NONE, Abilities.NONE, 650, 125, 150, 115, 73, 70, 117, 3, 50, 275), ), new PokemonSpecies(Species.ZARUDE, 8, false, false, true, "Rogue Monkey Pokémon", Type.DARK, Type.GRASS, 1.8, 70, Abilities.LEAF_GUARD, Abilities.NONE, Abilities.NONE, 600, 105, 120, 105, 70, 95, 105, 3, 0, 300, GrowthRate.SLOW, null, false, false, new PokemonForm("Normal", "", Type.DARK, Type.GRASS, 1.8, 70, Abilities.LEAF_GUARD, Abilities.NONE, Abilities.NONE, 600, 105, 120, 105, 70, 95, 105, 3, 0, 300, false, null, true), @@ -2545,7 +2545,14 @@ export function initSpecies() { new PokemonForm("Hero Form", "hero", Type.WATER, null, 1.8, 97.4, Abilities.ZERO_TO_HERO, Abilities.NONE, Abilities.ZERO_TO_HERO, 650, 100, 160, 97, 106, 87, 100, 45, 50, 160), ), new PokemonSpecies(Species.VAROOM, 9, false, false, false, "Single-Cyl Pokémon", Type.STEEL, Type.POISON, 1, 35, Abilities.OVERCOAT, Abilities.NONE, Abilities.SLOW_START, 300, 45, 70, 63, 30, 45, 47, 190, 50, 60, GrowthRate.MEDIUM_FAST, 50, false), - new PokemonSpecies(Species.REVAVROOM, 9, false, false, false, "Multi-Cyl Pokémon", Type.STEEL, Type.POISON, 1.8, 120, Abilities.OVERCOAT, Abilities.NONE, Abilities.FILTER, 500, 80, 119, 90, 54, 67, 90, 75, 50, 175, GrowthRate.MEDIUM_FAST, 50, false), + new PokemonSpecies(Species.REVAVROOM, 9, false, false, false, "Multi-Cyl Pokémon", Type.STEEL, Type.POISON, 1.8, 120, Abilities.OVERCOAT, Abilities.NONE, Abilities.FILTER, 500, 80, 119, 90, 54, 67, 90, 75, 50, 175, GrowthRate.MEDIUM_FAST, 50, false, false, + new PokemonForm("Normal", "", Type.STEEL, Type.POISON, 1.8, 120, Abilities.OVERCOAT, Abilities.NONE, Abilities.FILTER, 500, 80, 119, 90, 54, 67, 90, 75, 50, 175, false, null, true), + new PokemonForm("Segin Starmobile", "segin-starmobile", Type.STEEL, Type.DARK, 1.8, 240, Abilities.INTIMIDATE, Abilities.NONE, Abilities.INTIMIDATE, 600, 120, 129, 100, 59, 77, 115, 75, 50, 175), + new PokemonForm("Schedar Starmobile", "schedar-starmobile", Type.STEEL, Type.FIRE, 1.8, 240, Abilities.SPEED_BOOST, Abilities.NONE, Abilities.SPEED_BOOST, 600, 120, 129, 100, 59, 77, 115, 75, 50, 175), + new PokemonForm("Navi Starmobile", "navi-starmobile", Type.STEEL, Type.POISON, 1.8, 240, Abilities.TOXIC_DEBRIS, Abilities.NONE, Abilities.TOXIC_DEBRIS, 600, 120, 129, 100, 59, 77, 115, 75, 50, 175), + new PokemonForm("Ruchbah Starmobile", "ruchbah-starmobile", Type.STEEL, Type.FAIRY, 1.8, 240, Abilities.MISTY_SURGE, Abilities.NONE, Abilities.MISTY_SURGE, 600, 120, 129, 100, 59, 77, 115, 75, 50, 175), + new PokemonForm("Caph Starmobile", "caph-starmobile", Type.STEEL, Type.FIGHTING, 1.8, 240, Abilities.STAMINA, Abilities.NONE, Abilities.STAMINA, 600, 120, 129, 100, 59, 77, 115, 75, 50, 175), + ), new PokemonSpecies(Species.CYCLIZAR, 9, false, false, false, "Mount Pokémon", Type.DRAGON, Type.NORMAL, 1.6, 63, Abilities.SHED_SKIN, Abilities.NONE, Abilities.REGENERATOR, 501, 70, 95, 65, 85, 65, 121, 190, 50, 175, GrowthRate.MEDIUM_SLOW, 50, false), new PokemonSpecies(Species.ORTHWORM, 9, false, false, false, "Earthworm Pokémon", Type.STEEL, null, 2.5, 310, Abilities.EARTH_EATER, Abilities.NONE, Abilities.SAND_VEIL, 480, 70, 85, 145, 60, 55, 65, 25, 50, 240, GrowthRate.SLOW, 50, false), new PokemonSpecies(Species.GLIMMET, 9, false, false, false, "Ore Pokémon", Type.ROCK, Type.POISON, 0.7, 8, Abilities.TOXIC_DEBRIS, Abilities.NONE, Abilities.CORROSION, 350, 48, 35, 42, 105, 60, 60, 70, 50, 70, GrowthRate.MEDIUM_SLOW, 50, false), @@ -3348,6 +3355,7 @@ export function getStarterValueFriendshipCap(value: integer): integer { } } +export const POKERUS_STARTER_COUNT = 5; //adjust here! /** * Method to get the daily list of starters with Pokerus. * @param scene {@linkcode BattleScene} used as part of RNG @@ -3356,10 +3364,9 @@ export function getStarterValueFriendshipCap(value: integer): integer { export function getPokerusStarters(scene: BattleScene): PokemonSpecies[] { const pokerusStarters: PokemonSpecies[] = []; const date = new Date(); - const starterCount = 3; //for easy future adjustment! date.setUTCHours(0, 0, 0, 0); scene.executeWithSeedOffset(() => { - while (pokerusStarters.length < starterCount) { + while (pokerusStarters.length < POKERUS_STARTER_COUNT) { const randomSpeciesId = parseInt(Utils.randSeedItem(Object.keys(speciesStarters)), 10); const species = getPokemonSpecies(randomSpeciesId); if (!pokerusStarters.includes(species)) { @@ -3396,7 +3403,7 @@ export const starterPassiveAbilities = { [Species.POLIWAG]: Abilities.NO_GUARD, [Species.ABRA]: Abilities.PSYCHIC_SURGE, [Species.MACHOP]: Abilities.QUICK_FEET, - [Species.BELLSPROUT]: Abilities.PROTOSYNTHESIS, + [Species.BELLSPROUT]: Abilities.FLOWER_GIFT, [Species.TENTACOOL]: Abilities.TOXIC_CHAIN, [Species.GEODUDE]: Abilities.DRY_SKIN, [Species.PONYTA]: Abilities.MAGIC_GUARD, @@ -3424,7 +3431,7 @@ export const starterPassiveAbilities = { [Species.STARYU]: Abilities.REGENERATOR, [Species.SCYTHER]: Abilities.TINTED_LENS, [Species.PINSIR]: Abilities.TINTED_LENS, - [Species.TAUROS]: Abilities.SCRAPPY, + [Species.TAUROS]: Abilities.STAMINA, [Species.MAGIKARP]: Abilities.MULTISCALE, [Species.LAPRAS]: Abilities.LIGHTNING_ROD, [Species.DITTO]: Abilities.ADAPTABILITY, @@ -3492,7 +3499,7 @@ export const starterPassiveAbilities = { [Species.LARVITAR]: Abilities.SAND_RUSH, [Species.LUGIA]: Abilities.DELTA_STREAM, [Species.HO_OH]: Abilities.MAGIC_GUARD, - [Species.CELEBI]: Abilities.GRASSY_SURGE, + [Species.CELEBI]: Abilities.PSYCHIC_SURGE, [Species.TREECKO]: Abilities.TINTED_LENS, [Species.TORCHIC]: Abilities.RECKLESS, [Species.MUDKIP]: Abilities.DRIZZLE, @@ -3630,7 +3637,7 @@ export const starterPassiveAbilities = { [Species.PANPOUR]: Abilities.SAP_SIPPER, [Species.MUNNA]: Abilities.NEUTRALIZING_GAS, [Species.PIDOVE]: Abilities.SNIPER, - [Species.BLITZLE]: Abilities.RECKLESS, + [Species.BLITZLE]: Abilities.ELECTRIC_SURGE, [Species.ROGGENROLA]: Abilities.SOLID_ROCK, [Species.WOOBAT]: Abilities.OPPORTUNIST, [Species.DRILBUR]: Abilities.SAND_STREAM, @@ -3830,7 +3837,7 @@ export const starterPassiveAbilities = { [Species.DURALUDON]: Abilities.STEELWORKER, [Species.DREEPY]: Abilities.PARENTAL_BOND, [Species.ZACIAN]: Abilities.UNNERVE, - [Species.ZAMAZENTA]: Abilities.STAMINA, + [Species.ZAMAZENTA]: Abilities.UNNERVE, [Species.ETERNATUS]: Abilities.NEUTRALIZING_GAS, [Species.KUBFU]: Abilities.IRON_FIST, [Species.ZARUDE]: Abilities.TOUGH_CLAWS, @@ -3862,7 +3869,7 @@ export const starterPassiveAbilities = { [Species.KLAWF]: Abilities.WATER_ABSORB, [Species.CAPSAKID]: Abilities.PARENTAL_BOND, [Species.RELLOR]: Abilities.PRANKSTER, - [Species.FLITTLE]: Abilities.MAGIC_BOUNCE, + [Species.FLITTLE]: Abilities.DAZZLING, [Species.TINKATINK]: Abilities.STEELWORKER, [Species.WIGLETT]: Abilities.STURDY, [Species.BOMBIRDIER]: Abilities.UNBURDEN, @@ -3913,7 +3920,7 @@ export const starterPassiveAbilities = { [Species.TERAPAGOS]: Abilities.SOUL_HEART, [Species.PECHARUNT]: Abilities.TOXIC_CHAIN, [Species.ALOLA_RATTATA]: Abilities.ADAPTABILITY, - [Species.ALOLA_SANDSHREW]: Abilities.TOUGH_CLAWS, + [Species.ALOLA_SANDSHREW]: Abilities.ICE_SCALES, [Species.ALOLA_VULPIX]: Abilities.SHEER_FORCE, [Species.ALOLA_DIGLETT]: Abilities.STURDY, [Species.ALOLA_MEOWTH]: Abilities.DARK_AURA, diff --git a/src/data/splash-messages.ts b/src/data/splash-messages.ts index 8e95bba0591..b8069f77737 100644 --- a/src/data/splash-messages.ts +++ b/src/data/splash-messages.ts @@ -1,46 +1,136 @@ -import i18next from "i18next"; +import { USE_SEASONAL_SPLASH_MESSAGES } from "#app/constants"; -export function getBattleCountSplashMessage(): string { - return `{COUNT} ${i18next.t("splashMessages:battlesWon")}`; +//#region Interfaces/Types + +type Month = "01" | "02" | "03" | "04" | "05" | "06" | "07" | "08" | "09" | "10" | "11" | "12"; +type Day = + | Month + | "13" + | "14" + | "15" + | "16" + | "17" + | "18" + | "19" + | "20" + | "21" + | "22" + | "23" + | "24" + | "25" + | "26" + | "27" + | "28" + | "29" + | "30" + | "31"; + +/** + * Represents a season with its {@linkcode name}, + * {@linkcode start} day+month, {@linkcode end} day+month + * and {@linkcode messages}. + */ +interface Season { + /** The name of the season (internal use only) */ + name: string; + /** The start day and month of the season. Format `MM-DD` */ + start: `${Month}-${Day}`; + /** The end day and month of the season. Format `MM-DD` */ + end: `${Month}-${Day}`; + /** Collection of the messages to display (without the `i18next.t()` call!) */ + messages: string[]; } +//#region Constants + +/** The weight multiplier for the battles-won splash message */ +const BATTLES_WON_WEIGHT_MULTIPLIER = 10; +/** The weight multiplier for the seasonal splash messages */ +const SEASONAL_WEIGHT_MULTIPLIER = 10; + +//#region Common Messages + +const commonSplashMessages = [ + ...Array(BATTLES_WON_WEIGHT_MULTIPLIER).fill("battlesWon"), + "joinTheDiscord", + "infiniteLevels", + "everythingStacks", + "optionalSaveScumming", + "biomes", + "openSource", + "playWithSpeed", + "liveBugTesting", + "heavyInfluence", + "pokemonRiskAndPokemonRain", + "nowWithMoreSalt", + "infiniteFusionAtHome", + "brokenEggMoves", + "magnificent", + "mubstitute", + "thatsCrazy", + "oranceJuice", + "questionableBalancing", + "coolShaders", + "aiFree", + "suddenDifficultySpikes", + "basedOnAnUnfinishedFlashGame", + "moreAddictiveThanIntended", + "mostlyConsistentSeeds", + "achievementPointsDontDoAnything", + "youDoNotStartAtLevel", + "dontTalkAboutTheManaphyEggIncident", + "alsoTryPokengine", + "alsoTryEmeraldRogue", + "alsoTryRadicalRed", + "eeveeExpo", + "ynoproject", + "breedersInSpace", +]; + +//#region Seasonal Messages + +const seasonalSplashMessages: Season[] = [ + { + name: "Halloween", + start: "09-15", + end: "10-31", + messages: ["halloween.pumpkaboosAbout", "halloween.mayContainSpiders", "halloween.spookyScaryDuskulls"], + }, + { + name: "XMAS", + start: "12-01", + end: "12-26", + messages: ["xmas.happyHolidays", "xmas.delibirdSeason"], + }, + { + name: "New Year's", + start: "01-01", + end: "01-31", + messages: ["newYears.happyNewYear"], + }, +]; + +//#endregion + export function getSplashMessages(): string[] { - const splashMessages = Array(10).fill(getBattleCountSplashMessage()); - splashMessages.push( - i18next.t("splashMessages:joinTheDiscord"), - i18next.t("splashMessages:infiniteLevels"), - i18next.t("splashMessages:everythingStacks"), - i18next.t("splashMessages:optionalSaveScumming"), - i18next.t("splashMessages:biomes"), - i18next.t("splashMessages:openSource"), - i18next.t("splashMessages:playWithSpeed"), - i18next.t("splashMessages:liveBugTesting"), - i18next.t("splashMessages:heavyInfluence"), - i18next.t("splashMessages:pokemonRiskAndPokemonRain"), - i18next.t("splashMessages:nowWithMoreSalt"), - i18next.t("splashMessages:infiniteFusionAtHome"), - i18next.t("splashMessages:brokenEggMoves"), - i18next.t("splashMessages:magnificent"), - i18next.t("splashMessages:mubstitute"), - i18next.t("splashMessages:thatsCrazy"), - i18next.t("splashMessages:oranceJuice"), - i18next.t("splashMessages:questionableBalancing"), - i18next.t("splashMessages:coolShaders"), - i18next.t("splashMessages:aiFree"), - i18next.t("splashMessages:suddenDifficultySpikes"), - i18next.t("splashMessages:basedOnAnUnfinishedFlashGame"), - i18next.t("splashMessages:moreAddictiveThanIntended"), - i18next.t("splashMessages:mostlyConsistentSeeds"), - i18next.t("splashMessages:achievementPointsDontDoAnything"), - i18next.t("splashMessages:youDoNotStartAtLevel"), - i18next.t("splashMessages:dontTalkAboutTheManaphyEggIncident"), - i18next.t("splashMessages:alsoTryPokengine"), - i18next.t("splashMessages:alsoTryEmeraldRogue"), - i18next.t("splashMessages:alsoTryRadicalRed"), - i18next.t("splashMessages:eeveeExpo"), - i18next.t("splashMessages:ynoproject"), - i18next.t("splashMessages:breedersInSpace"), - ); + const splashMessages: string[] = [...commonSplashMessages]; + console.log("use seasonal splash messages", USE_SEASONAL_SPLASH_MESSAGES); + if (USE_SEASONAL_SPLASH_MESSAGES) { + // add seasonal splash messages if the season is active + for (const { name, start, end, messages } of seasonalSplashMessages) { + const now = new Date(); + const startDate = new Date(`${start}-${now.getFullYear()}`); + const endDate = new Date(`${end}-${now.getFullYear()}`); - return splashMessages; + if (now >= startDate && now <= endDate) { + console.log(`Adding ${messages.length} ${name} splash messages (weight: x${SEASONAL_WEIGHT_MULTIPLIER})`); + messages.forEach((message) => { + const weightedMessage = Array(SEASONAL_WEIGHT_MULTIPLIER).fill(message); + splashMessages.push(...weightedMessage); + }); + } + } + } + + return splashMessages.map((message) => `splashMessages:${message}`); } diff --git a/src/data/trainer-config.ts b/src/data/trainer-config.ts index 0323c6d43c4..62f9589b7a3 100644 --- a/src/data/trainer-config.ts +++ b/src/data/trainer-config.ts @@ -1,6 +1,6 @@ import BattleScene, { startingWave } from "../battle-scene"; import { ModifierTypeFunc, modifierTypes } from "../modifier/modifier-type"; -import { EnemyPokemon } from "../field/pokemon"; +import { EnemyPokemon, PokemonMove } from "../field/pokemon"; import * as Utils from "../utils"; import { PokeballType } from "./pokeball"; import { pokemonEvolutions, pokemonPrevolutions } from "./pokemon-evolutions"; @@ -255,7 +255,9 @@ export class TrainerConfig { name = i18next.t("trainerNames:rival"); } } + this.name = name; + return this; } @@ -333,6 +335,9 @@ export class TrainerConfig { case TrainerType.ROSE_2: trainerType = TrainerType.ROSE; break; + case TrainerType.PENNY_2: + trainerType = TrainerType.PENNY; + break; case TrainerType.MARNIE_ELITE: trainerType = TrainerType.MARNIE; break; @@ -617,6 +622,41 @@ export class TrainerConfig { [TrainerPoolTier.RARE]: [Species.TINKATINK, Species.HISUI_LILLIGANT] }; } + case "star_1": { + return { + [TrainerPoolTier.COMMON]: [ Species.MURKROW, Species.SEEDOT, Species.CACNEA, Species.STUNKY, Species.SANDILE, Species.NYMBLE, Species.MASCHIFF, Species.GALAR_ZIGZAGOON ], + [TrainerPoolTier.UNCOMMON]: [ Species.UMBREON, Species.SNEASEL, Species.CORPHISH, Species.ZORUA, Species.INKAY, Species.BOMBIRDIER ], + [TrainerPoolTier.RARE]: [ Species.DEINO, Species.SPRIGATITO ] + }; + } + case "star_2": { + return { + [TrainerPoolTier.COMMON]: [ Species.GROWLITHE, Species.HOUNDOUR, Species.NUMEL, Species.LITWICK, Species.FLETCHLING, Species.LITLEO, Species.ROLYCOLY, Species.CAPSAKID ], + [TrainerPoolTier.UNCOMMON]: [ Species.PONYTA, Species.FLAREON, Species.MAGBY, Species.TORKOAL, Species.SALANDIT, Species.TURTONATOR ], + [TrainerPoolTier.RARE]: [ Species.LARVESTA, Species.FUECOCO ] + }; + } + case "star_3": { + return { + [TrainerPoolTier.COMMON]: [ Species.ZUBAT, Species.GRIMER, Species.STUNKY, Species.FOONGUS, Species.MAREANIE, Species.TOXEL, Species.SHROODLE, Species.PALDEA_WOOPER ], + [TrainerPoolTier.UNCOMMON]: [ Species.GASTLY, Species.SEVIPER, Species.SKRELP, Species.ALOLA_GRIMER, Species.GALAR_SLOWPOKE, Species.HISUI_QWILFISH ], + [TrainerPoolTier.RARE]: [ Species.BULBASAUR, Species.GLIMMET ] + }; + } + case "star_4": { + return { + [TrainerPoolTier.COMMON]: [ Species.CLEFFA, Species.IGGLYBUFF, Species.AZURILL, Species.COTTONEE, Species.FLABEBE, Species.HATENNA, Species.IMPIDIMP, Species.TINKATINK ], + [TrainerPoolTier.UNCOMMON]: [ Species.TOGEPI, Species.GARDEVOIR, Species.SYLVEON, Species.KLEFKI, Species.MIMIKYU, Species.ALOLA_VULPIX ], + [TrainerPoolTier.RARE]: [ Species.POPPLIO, Species.GALAR_PONYTA ] + }; + } + case "star_5": { + return { + [TrainerPoolTier.COMMON]: [ Species.SHROOMISH, Species.MAKUHITA, Species.MEDITITE, Species.CROAGUNK, Species.SCRAGGY, Species.MIENFOO, Species.PAWMI, Species.PALDEA_TAUROS ], + [TrainerPoolTier.UNCOMMON]: [ Species.RIOLU, Species.TIMBURR, Species.HAWLUCHA, Species.PASSIMIAN, Species.FALINKS, Species.FLAMIGO ], + [TrainerPoolTier.RARE]: [ Species.JANGMO_O, Species.QUAXLY ] + }; + } } console.warn(`Evil team admin for ${team} not found. Returning empty species pools.`); @@ -899,6 +939,20 @@ export class TrainerConfig { return this; } + /** + * Sets a localized name for the trainer. This should only be used for trainers that dont use a "initFor" function and are considered "named" trainers + * @param name - The name of the trainer. + * @returns {TrainerConfig} The updated TrainerConfig instance. + */ + setLocalizedName(name: string): TrainerConfig { + // Check if the internationalization (i18n) system is initialized. + if (!getIsInitialized()) { + initI18n(); + } + this.name = i18next.t(`trainerNames:${name.toLowerCase().replace(/\s/g, "_")}`); + return this; + } + /** * Retrieves the title for the trainer based on the provided trainer slot and variant. * @param {TrainerSlot} trainerSlot - The slot to determine which title to use. Defaults to TrainerSlot.NONE. @@ -1089,8 +1143,16 @@ function getGymLeaderPartyTemplate(scene: BattleScene) { return getWavePartyTemplate(scene, trainerPartyTemplates.GYM_LEADER_1, trainerPartyTemplates.GYM_LEADER_2, trainerPartyTemplates.GYM_LEADER_3, trainerPartyTemplates.GYM_LEADER_4, trainerPartyTemplates.GYM_LEADER_5); } -function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void): PartyMemberFunc { - return (scene: BattleScene, level: integer, strength: PartyMemberStrength) => { +/** + * Randomly selects one of the `Species` from `speciesPool`, determines its evolution, level, and strength. + * Then adds Pokemon to scene. + * @param speciesPool + * @param trainerSlot + * @param ignoreEvolution + * @param postProcess + */ +export function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void) { + return (scene: BattleScene, level: number, strength: PartyMemberStrength) => { let species = Utils.randSeedItem(speciesPool); if (!ignoreEvolution) { species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength, scene.currentBattle.waveIndex); @@ -1254,7 +1316,7 @@ export const signatureSpecies: SignatureSpecies = { IRIS: [Species.HAXORUS, Species.RESHIRAM, Species.ARCHEOPS], // Druddigon lead, Gmax Lapras DIANTHA: [Species.HAWLUCHA, Species.XERNEAS, Species.GOODRA], // Gourgeist lead, Mega Gardevoir HAU: [[Species.SOLGALEO, Species.LUNALA], Species.NOIVERN, [Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA], [Species.TAPU_BULU, Species.TAPU_FINI, Species.TAPU_KOKO, Species.TAPU_LELE]], // Alola Raichu lead - LEON: [Species.DRAGAPULT, [Species.ZACIAN, Species.ZAMAZENTA], Species.AEGISLASH], // Rillaboom/Cinderace/Inteleon lead, GMax Charizard + LEON: [Species.DRAGAPULT, Species.ZACIAN, Species.AEGISLASH], // Rillaboom/Cinderace/Inteleon lead, GMax Charizard GEETA: [Species.MIRAIDON, [Species.ESPATHRA, Species.VELUZA], [Species.AVALUGG, Species.HISUI_AVALUGG], Species.KINGAMBIT], // Glimmora lead NEMONA: [Species.KORAIDON, Species.PAWMOT, [Species.DUDUNSPARCE, Species.ORTHWORM], [Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL]], // Lycanroc lead KIERAN: [[Species.GRIMMSNARL, Species.INCINEROAR, Species.PORYGON_Z], Species.OGERPON, Species.TERAPAGOS, Species.HYDRAPPLE], // Poliwrath/Politoed lead @@ -1504,6 +1566,38 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.SUPER_RARE]: [Species.DURALUDON, Species.DREEPY] }), [TrainerType.OLEANA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("macro_admin", "macro", [Species.GARBODOR]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_oleana").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), + [TrainerType.STAR_GRUNT]: new TrainerConfig(++t).setHasGenders("Star Grunt Female").setHasDouble("Star Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) + .setSpeciesPools({ + [TrainerPoolTier.COMMON]: [ Species.DUNSPARCE, Species.HOUNDOUR, Species.AZURILL, Species.GULPIN, Species.FOONGUS, Species.FLETCHLING, Species.LITLEO, Species.FLABEBE, Species.CRABRAWLER, Species.NYMBLE, Species.PAWMI, Species.FIDOUGH, Species.SQUAWKABILLY, Species.MASCHIFF, Species.SHROODLE, Species.KLAWF, Species.WIGLETT, Species.PALDEA_WOOPER ], + [TrainerPoolTier.UNCOMMON]: [ Species.KOFFING, Species.EEVEE, Species.GIRAFARIG, Species.RALTS, Species.TORKOAL, Species.SEVIPER, Species.SCRAGGY, Species.ZORUA, Species.MIMIKYU, Species.IMPIDIMP, Species.FALINKS, Species.CAPSAKID, Species.TINKATINK, Species.BOMBIRDIER, Species.CYCLIZAR, Species.FLAMIGO, Species.PALDEA_TAUROS ], + [TrainerPoolTier.RARE]: [ Species.MANKEY, Species.PAWNIARD, Species.CHARCADET, Species.FLITTLE, Species.VAROOM, Species.ORTHWORM], + [TrainerPoolTier.SUPER_RARE]: [ Species.DONDOZO, Species.GIMMIGHOUL ] + }), + [TrainerType.GIACOMO]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_1", [Species.KINGAMBIT]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.REVAVROOM], TrainerSlot.TRAINER, true, p => { + p.formIndex = 1; // Segin Starmobile + p.moveset = [ new PokemonMove(Moves.WICKED_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; + })), + [TrainerType.MELA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_2", [Species.ARMAROUGE]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.REVAVROOM], TrainerSlot.TRAINER, true, p => { + p.formIndex = 2; // Schedar Starmobile + p.moveset = [ new PokemonMove(Moves.BLAZING_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; + })), + [TrainerType.ATTICUS]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_3", [Species.REVAVROOM]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.REVAVROOM], TrainerSlot.TRAINER, true, p => { + p.formIndex = 3; // Navi Starmobile + p.moveset = [ new PokemonMove(Moves.NOXIOUS_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; + })), + [TrainerType.ORTEGA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_4", [Species.DACHSBUN]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.REVAVROOM], TrainerSlot.TRAINER, true, p => { + p.formIndex = 4; // Ruchbah Starmobile + p.moveset = [ new PokemonMove(Moves.MAGICAL_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; + })), + [TrainerType.ERI]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_5", [Species.ANNIHILAPE]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.REVAVROOM], TrainerSlot.TRAINER, true, p => { + p.formIndex = 5; // Caph Starmobile + p.moveset = [ new PokemonMove(Moves.COMBAT_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; + })), [TrainerType.BROCK]: new TrainerConfig((t = TrainerType.BROCK)).initForGymLeader(signatureSpecies["BROCK"], true, Type.ROCK).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), [TrainerType.MISTY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MISTY"], false, Type.WATER).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), @@ -2138,6 +2232,64 @@ export const trainerConfigs: TrainerConfigs = { p.generateName(); p.pokeball = PokeballType.ULTRA_BALL; })), + [TrainerType.PENNY]: new TrainerConfig(++t).setName("Cassiopeia").initForEvilTeamLeader("Star Boss", []).setMixedBattleBgm("battle_star_boss").setVictoryBgm("victory_team_plasma") + .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VAPOREON, Species.JOLTEON, Species.FLAREON ])) + .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.ESPEON, Species.UMBREON ], TrainerSlot.TRAINER, true, p => { + p.abilityIndex = 2; // Magic Bounce Espeon, Inner Focus Umbreon + p.generateAndPopulateMoveset(); + })) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.LEAFEON, Species.GLACEON ])) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.ROTOM ], TrainerSlot.TRAINER, true, p => { + p.generateAndPopulateMoveset(); + p.formIndex = Utils.randSeedInt(5, 1); // Heat, Wash, Frost, Fan, or Mow + })) + .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.SYLVEON ], TrainerSlot.TRAINER, true, p => { + p.generateAndPopulateMoveset(); + p.abilityIndex = 2; // Pixilate + })) + .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.EEVEE ], TrainerSlot.TRAINER, true, p => { + p.setBoss(true, 2); + p.generateAndPopulateMoveset(); + p.formIndex = 2; // G-Max Eevee + p.pokeball = PokeballType.ULTRA_BALL; + p.generateName(); + })) + .setGenModifiersFunc(party => { + const teraPokemon = party[4]; + return [modifierTypes.TERA_SHARD().generateType([], [teraPokemon.species.type1])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier]; //TODO: is the bang correct? + }), + [TrainerType.PENNY_2]: new TrainerConfig(++t).setName("Cassiopeia").initForEvilTeamLeader("Star Boss", [], true).setMixedBattleBgm("battle_star_boss").setVictoryBgm("victory_team_plasma") + .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => { + p.setBoss(true, 2); + p.formIndex = Utils.randSeedInt(5, 1); //Random Starmobile form + p.generateAndPopulateMoveset(); + p.pokeball = PokeballType.ULTRA_BALL; + })) + .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.ENTEI, Species.RAIKOU, Species.SUICUNE ], TrainerSlot.TRAINER, true, p => { + p.generateAndPopulateMoveset(); + p.pokeball = PokeballType.ULTRA_BALL; + })) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.WALKING_WAKE, Species.GOUGING_FIRE, Species.RAGING_BOLT ])) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.SYLVEON ], TrainerSlot.TRAINER, true, p => { + p.generateAndPopulateMoveset(); + p.abilityIndex = 2; // Pixilate + })) + .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.EEVEE ], TrainerSlot.TRAINER, true, p => { + p.setBoss(true, 2); + p.generateAndPopulateMoveset(); + p.formIndex = 2; + p.generateName(); + p.pokeball = PokeballType.ULTRA_BALL; + })) + .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.ZAMAZENTA ], TrainerSlot.TRAINER, true, p => { + p.setBoss(true, 2); + p.generateAndPopulateMoveset(); + p.pokeball = PokeballType.MASTER_BALL; + })) + .setGenModifiersFunc(party => { + const teraPokemon = party[3]; + return [modifierTypes.TERA_SHARD().generateType([], [teraPokemon.species.type1])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier]; //TODO: is the bang correct? + }), [TrainerType.BUCK]: new TrainerConfig(++t).setName("Buck").initForStatTrainer([], true) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.CLAYDOL ], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 3); @@ -2270,21 +2422,24 @@ export const trainerConfigs: TrainerConfigs = { } p.pokeball = PokeballType.MASTER_BALL; })), - [TrainerType.VICTOR]: new TrainerConfig(++t).setName("Victor").setTitle("The Winstrates") + [TrainerType.VICTOR]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Victor") .setMoneyMultiplier(1) // The Winstrate trainers have total money multiplier of 6 .setPartyTemplates(trainerPartyTemplates.ONE_AVG_ONE_STRONG), - [TrainerType.VICTORIA]: new TrainerConfig(++t).setName("Victoria").setTitle("The Winstrates") + [TrainerType.VICTORIA]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Victoria") .setMoneyMultiplier(1) .setPartyTemplates(trainerPartyTemplates.ONE_AVG_ONE_STRONG), - [TrainerType.VIVI]: new TrainerConfig(++t).setName("Vivi").setTitle("The Winstrates") + [TrainerType.VIVI]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Vivi") .setMoneyMultiplier(1) .setPartyTemplates(trainerPartyTemplates.TWO_AVG_ONE_STRONG), - [TrainerType.VICKY]: new TrainerConfig(++t).setName("Vicky").setTitle("The Winstrates") + [TrainerType.VICKY]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Vicky") .setMoneyMultiplier(1) .setPartyTemplates(trainerPartyTemplates.ONE_AVG), - [TrainerType.VITO]: new TrainerConfig(++t).setName("Vito").setTitle("The Winstrates") + [TrainerType.VITO]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Vito") .setMoneyMultiplier(2) .setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(2, PartyMemberStrength.STRONG))), [TrainerType.BUG_TYPE_SUPERFAN]: new TrainerConfig(++t).setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.ACE_TRAINER) - .setPartyTemplates(new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE)) + .setPartyTemplates(new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE)), + [TrainerType.EXPERT_POKEMON_BREEDER]: new TrainerConfig(++t).setMoneyMultiplier(3).setEncounterBgm(TrainerType.ACE_TRAINER).setLocalizedName("Expert Pokemon Breeder") + .setPartyTemplates(new TrainerPartyTemplate(3, PartyMemberStrength.STRONG)) }; + diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index f367b1b56c0..6cf2d260dcb 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -80,4 +80,5 @@ export enum BattlerTagType { BURNED_UP = "BURNED_UP", DOUBLE_SHOCKED = "DOUBLE_SHOCKED", MYSTERY_ENCOUNTER_POST_SUMMON = "MYSTERY_ENCOUNTER_POST_SUMMON", + HEAL_BLOCK = "HEAL_BLOCK", } diff --git a/src/enums/exp-gains-speed.ts b/src/enums/exp-gains-speed.ts new file mode 100644 index 00000000000..964c4f99c70 --- /dev/null +++ b/src/enums/exp-gains-speed.ts @@ -0,0 +1,22 @@ +/** + * Defines the speed of gaining experience. + * + * @remarks + * The `expGainSpeed` can have several modes: + * - `0` - Default: The normal speed. + * - `1` - Fast: Fast speed. + * - `2` - Faster: Faster speed. + * - `3` - Skip: Skip gaining exp animation. + * + * @default 0 - Uses the default normal speed. + */ +export enum ExpGainsSpeed { + /** The normal speed. */ + DEFAULT, + /** Fast speed. */ + FAST, + /** Faster speed. */ + FASTER, + /** Skip gaining exp animation. */ + SKIP +} diff --git a/src/enums/mystery-encounter-type.ts b/src/enums/mystery-encounter-type.ts index 39a8087599c..b973652b113 100644 --- a/src/enums/mystery-encounter-type.ts +++ b/src/enums/mystery-encounter-type.ts @@ -28,5 +28,6 @@ export enum MysteryEncounterType { BUG_TYPE_SUPERFAN, FUN_AND_GAMES, UNCOMMON_BREED, - GLOBAL_TRADE_SYSTEM + GLOBAL_TRADE_SYSTEM, + THE_EXPERT_POKEMON_BREEDER } diff --git a/src/enums/trainer-type.ts b/src/enums/trainer-type.ts index cfc52b70eb0..cb7509067b5 100644 --- a/src/enums/trainer-type.ts +++ b/src/enums/trainer-type.ts @@ -78,6 +78,12 @@ export enum TrainerType { PLUMERIA, MACRO_GRUNT, OLEANA, + STAR_GRUNT, + GIACOMO, + MELA, + ATTICUS, + ORTEGA, + ERI, ROCKET_BOSS_GIOVANNI_1, ROCKET_BOSS_GIOVANNI_2, MAXIE, @@ -96,6 +102,8 @@ export enum TrainerType { GUZMA_2, ROSE, ROSE_2, + PENNY, + PENNY_2, BUCK, CHERYL, MARLEY, @@ -107,6 +115,7 @@ export enum TrainerType { VICKY, VITO, BUG_TYPE_SUPERFAN, + EXPERT_POKEMON_BREEDER, BROCK = 200, MISTY, diff --git a/src/enums/variant-tiers.ts b/src/enums/variant-tiers.ts deleted file mode 100644 index 20a0e8ec4e4..00000000000 --- a/src/enums/variant-tiers.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum VariantTier { - COMMON, - RARE, - EPIC -} diff --git a/src/field/arena.ts b/src/field/arena.ts index 0466c01c82b..9897da7cfd7 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -1,19 +1,19 @@ import BattleScene from "../battle-scene"; -import { BiomePoolTier, PokemonPools, BiomeTierTrainerPools, biomePokemonPools, biomeTrainerPools } from "../data/biomes"; +import { biomePokemonPools, BiomePoolTier, BiomeTierTrainerPools, biomeTrainerPools, PokemonPools } from "../data/biomes"; import { Constructor } from "#app/utils"; import * as Utils from "../utils"; import PokemonSpecies, { getPokemonSpecies } from "../data/pokemon-species"; -import { Weather, WeatherType, getTerrainClearMessage, getTerrainStartMessage, getWeatherClearMessage, getWeatherStartMessage } from "../data/weather"; +import { getTerrainClearMessage, getTerrainStartMessage, getWeatherClearMessage, getWeatherStartMessage, Weather, WeatherType } from "../data/weather"; import { CommonAnim } from "../data/battle-anims"; import { Type } from "../data/type"; import Move from "../data/move"; import { ArenaTag, ArenaTagSide, ArenaTrapTag, getArenaTag } from "../data/arena-tag"; import { BattlerIndex } from "../battle"; import { Terrain, TerrainType } from "../data/terrain"; -import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability"; +import { applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs, PostTerrainChangeAbAttr, PostWeatherChangeAbAttr } from "../data/ability"; import Pokemon from "./pokemon"; import Overrides from "#app/overrides"; -import { WeatherChangedEvent, TerrainChangedEvent, TagAddedEvent, TagRemovedEvent } from "../events/arena"; +import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "../events/arena"; import { ArenaTagType } from "#enums/arena-tag-type"; import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; @@ -746,7 +746,7 @@ export class Arena { case Biome.TOWN: return 7.288; case Biome.PLAINS: - return 7.693; + return 17.485; case Biome.GRASS: return 1.995; case Biome.TALL_GRASS: @@ -762,7 +762,7 @@ export class Arena { case Biome.BEACH: return 3.462; case Biome.LAKE: - return 5.350; + return 7.215; case Biome.SEABED: return 2.600; case Biome.MOUNTAIN: @@ -774,13 +774,13 @@ export class Arena { case Biome.DESERT: return 1.143; case Biome.ICE_CAVE: - return 15.010; + return 0.000; case Biome.MEADOW: return 3.891; case Biome.POWER_PLANT: - return 2.810; + return 9.447; case Biome.VOLCANO: - return 5.116; + return 17.637; case Biome.GRAVEYARD: return 3.232; case Biome.DOJO: @@ -788,7 +788,7 @@ export class Arena { case Biome.FACTORY: return 4.985; case Biome.RUINS: - return 2.270; + return 0.000; case Biome.WASTELAND: return 6.336; case Biome.ABYSS: diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index ea1b7fba372..d2db40d0e98 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -3,14 +3,14 @@ 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, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, 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, MoveTarget } from "../data/move"; import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species"; import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils"; import * as Utils from "../utils"; import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type"; import { getLevelTotalExp } from "../data/exp"; 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, PokemonBaseStatFlatModifier, PokemonBaseStatTotalModifier, PokemonIncrementingStatModifier } from "../modifier/modifier"; +import { DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, HiddenAbilityRateBoosterModifier, BaseStatModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempStatStageBoosterModifier, TempCritBoosterModifier, StatBoosterModifier, CritBoosterModifier, TerastallizeModifier, PokemonBaseStatFlatModifier, PokemonBaseStatTotalModifier, PokemonIncrementingStatModifier, EvoTrackerModifier } from "../modifier/modifier"; import { PokeballType } from "../data/pokeball"; import { Gender } from "../data/gender"; import { initMoveAnim, loadMoveAnimAssets } from "../data/battle-anims"; @@ -95,10 +95,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public metLevel: integer; public metBiome: Biome | -1; public metSpecies: Species; + public metWave: number; public luck: integer; public pauseEvolutions: boolean; public pokerus: boolean; - public wildFlee: boolean; + public switchOutStatus: boolean; + public evoCounter: integer; public fusionSpecies: PokemonSpecies | null; public fusionFormIndex: integer; @@ -143,7 +145,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.species = species; this.pokeball = dataSource?.pokeball || PokeballType.POKEBALL; this.level = level; - this.wildFlee = false; + this.switchOutStatus = false; // Determine the ability index if (abilityIndex !== undefined) { @@ -193,8 +195,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.luck = dataSource.luck; this.metBiome = dataSource.metBiome; this.metSpecies = dataSource.metSpecies ?? (this.metBiome !== -1 ? this.species.speciesId : this.species.getRootSpeciesId(true)); + this.metWave = dataSource.metWave ?? (this.metBiome === -1 ? -1 : 0); this.pauseEvolutions = dataSource.pauseEvolutions; this.pokerus = !!dataSource.pokerus; + this.evoCounter = dataSource.evoCounter ?? 0; this.fusionSpecies = dataSource.fusionSpecies instanceof PokemonSpecies ? dataSource.fusionSpecies : dataSource.fusionSpecies ? getPokemonSpecies(dataSource.fusionSpecies) : null; this.fusionFormIndex = dataSource.fusionFormIndex; this.fusionAbilityIndex = dataSource.fusionAbilityIndex; @@ -238,6 +242,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.metLevel = level; this.metBiome = scene.currentBattle ? scene.arena.biomeType : -1; this.metSpecies = species.speciesId; + this.metWave = scene.currentBattle ? scene.currentBattle.waveIndex : -1; this.pokerus = false; if (level > 1) { @@ -338,7 +343,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { isAllowed(): boolean { const challengeAllowed = new Utils.BooleanHolder(true); applyChallenges(this.scene.gameMode, ChallengeType.POKEMON_IN_BATTLE, this, challengeAllowed); - return !this.wildFlee && challengeAllowed.value; + return !this.isFainted() && challengeAllowed.value; } isActive(onField?: boolean): boolean { @@ -579,7 +584,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getSpriteScale(): number { const formKey = this.getFormKey(); - if (formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1 || formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1) { + if (this.isMax() === true || formKey === "segin-starmobile" || formKey === "schedar-starmobile" || formKey === "navi-starmobile" || formKey === "ruchbah-starmobile" || formKey === "caph-starmobile") { return 1.5; } else if (this.mysteryEncounterPokemonData.spriteScale > 0) { return this.mysteryEncounterPokemonData.spriteScale; @@ -592,8 +597,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Resetting properties should not be shown on the field this.setVisible(false); - // Reset field position - this.setFieldPosition(FieldPosition.CENTER); + // Remove the offset from having a Substitute active if (this.isOffsetBySubstitute()) { this.x -= this.getSubstituteOffset()[0]; this.y -= this.getSubstituteOffset()[1]; @@ -870,22 +874,29 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param stat the desired {@linkcode EffectiveStat} * @param opponent the target {@linkcode Pokemon} * @param move the {@linkcode Move} being used + * @param ignoreAbility determines whether this Pokemon's abilities should be ignored during the stat calculation + * @param ignoreOppAbility during an attack, determines whether the opposing Pokemon's abilities should be ignored during the stat calculation. * @param isCritical determines whether a critical hit has occurred or not (`false` by default) + * @param simulated if `true`, nullifies any effects that produce any changes to game state from triggering * @returns the final in-battle value of a stat */ - getEffectiveStat(stat: EffectiveStat, opponent?: Pokemon, move?: Move, isCritical: boolean = false): integer { + getEffectiveStat(stat: EffectiveStat, opponent?: Pokemon, move?: Move, ignoreAbility: boolean = false, ignoreOppAbility: boolean = false, isCritical: boolean = false, simulated: boolean = true): integer { const statValue = new Utils.NumberHolder(this.getStat(stat, false)); this.scene.applyModifiers(StatBoosterModifier, this.isPlayer(), this, stat, statValue); + // The Ruin abilities here are never ignored, but they reveal themselves on summon anyway const fieldApplied = new Utils.BooleanHolder(false); for (const pokemon of this.scene.getField(true)) { - applyFieldStatMultiplierAbAttrs(FieldMultiplyStatAbAttr, pokemon, stat, statValue, this, fieldApplied); + applyFieldStatMultiplierAbAttrs(FieldMultiplyStatAbAttr, pokemon, stat, statValue, this, fieldApplied, simulated); if (fieldApplied.value) { break; } } - applyStatMultiplierAbAttrs(StatMultiplierAbAttr, this, stat, statValue); - let ret = statValue.value * this.getStatStageMultiplier(stat, opponent, move, isCritical); + if (!ignoreAbility) { + applyStatMultiplierAbAttrs(StatMultiplierAbAttr, this, stat, statValue, simulated); + } + + let ret = statValue.value * this.getStatStageMultiplier(stat, opponent, move, ignoreOppAbility, isCritical, simulated); switch (stat) { case Stat.ATK: @@ -1262,13 +1273,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param attrType {@linkcode AbAttr} The ability attribute to check for. * @param canApply {@linkcode Boolean} If false, it doesn't check whether the ability is currently active * @param ignoreOverride {@linkcode Boolean} If true, it ignores ability changing effects - * @returns {AbAttr[]} A list of all the ability attributes on this ability. + * @returns A list of all the ability attributes on this ability. */ - getAbilityAttrs(attrType: { new(...args: any[]): AbAttr }, canApply: boolean = true, ignoreOverride?: boolean): AbAttr[] { - const abilityAttrs: AbAttr[] = []; + getAbilityAttrs(attrType: { new(...args: any[]): T }, canApply: boolean = true, ignoreOverride?: boolean): T[] { + const abilityAttrs: T[] = []; if (!canApply || this.canApplyAbility()) { - abilityAttrs.push(...this.getAbility(ignoreOverride).getAttrs(attrType)); + abilityAttrs.push(...this.getAbility(ignoreOverride).getAttrs(attrType)); } if (!canApply || this.canApplyAbility(true)) { @@ -2141,11 +2152,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * sets if the pokemon has fled (implies it's a wild pokemon) + * sets if the pokemon is switching out (if it's a enemy wild implies it's going to flee) * @param status - boolean */ - setWildFlee(status: boolean): void { - this.wildFlee = status; + setSwitchOutStatus(status: boolean): void { + this.switchOutStatus = status; } updateInfo(instant?: boolean): Promise { @@ -2223,10 +2234,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param stat the desired {@linkcode EffectiveStat} * @param opponent the target {@linkcode Pokemon} * @param move the {@linkcode Move} being used + * @param ignoreOppAbility determines whether the effects of the opponent's abilities (i.e. Unaware) should be ignored (`false` by default) * @param isCritical determines whether a critical hit has occurred or not (`false` by default) + * @param simulated determines whether effects are applied without altering game state (`true` by default) * @return the stat stage multiplier to be used for effective stat calculation */ - getStatStageMultiplier(stat: EffectiveStat, opponent?: Pokemon, move?: Move, isCritical: boolean = false): number { + getStatStageMultiplier(stat: EffectiveStat, opponent?: Pokemon, move?: Move, ignoreOppAbility: boolean = false, isCritical: boolean = false, simulated: boolean = true): number { const statStage = new Utils.IntegerHolder(this.getStatStage(stat)); const ignoreStatStage = new Utils.BooleanHolder(false); @@ -2243,7 +2256,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { break; } } - applyAbAttrs(IgnoreOpponentStatStagesAbAttr, opponent, null, false, stat, ignoreStatStage); + if (!ignoreOppAbility) { + applyAbAttrs(IgnoreOpponentStatStagesAbAttr, opponent, null, simulated, stat, ignoreStatStage); + } if (move) { applyMoveAttrs(IgnoreOpponentStatStagesAttr, this, opponent, move, ignoreStatStage); } @@ -2308,13 +2323,68 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Apply the results of a move to this pokemon - * @param {Pokemon} source The pokemon using the move - * @param {PokemonMove} battlerMove The move being used - * @returns {HitResult} The result of the attack - */ - apply(source: Pokemon, move: Move): HitResult { - let result: HitResult; + * Calculates the base damage of the given move against this Pokemon when attacked by the given source. + * Used during damage calculation and for Shell Side Arm's forecasting effect. + * @param source the attacking {@linkcode Pokemon}. + * @param move the {@linkcode Move} used in the attack. + * @param moveCategory the move's {@linkcode MoveCategory} after variable-category effects are applied. + * @param ignoreAbility if `true`, ignores this Pokemon's defensive ability effects (defaults to `false`). + * @param ignoreSourceAbility if `true`, ignore's the attacking Pokemon's ability effects (defaults to `false`). + * @param isCritical if `true`, calculates effective stats as if the hit were critical (defaults to `false`). + * @param simulated if `true`, suppresses changes to game state during calculation (defaults to `true`). + * @returns The move's base damage against this Pokemon when used by the source Pokemon. + */ + getBaseDamage(source: Pokemon, move: Move, moveCategory: MoveCategory, ignoreAbility: boolean = false, ignoreSourceAbility: boolean = false, isCritical: boolean = false, simulated: boolean = true): number { + const isPhysical = moveCategory === MoveCategory.PHYSICAL; + + /** A base damage multiplier based on the source's level */ + const levelMultiplier = (2 * source.level / 5 + 2); + + /** The power of the move after power boosts from abilities, etc. have applied */ + const power = move.calculateBattlePower(source, this, simulated); + + /** + * The attacker's offensive stat for the given move's category. + * Critical hits cause negative stat stages to be ignored. + */ + const sourceAtk = new Utils.NumberHolder(source.getEffectiveStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, ignoreSourceAbility, ignoreAbility, isCritical, simulated)); + applyMoveAttrs(VariableAtkAttr, source, this, move, sourceAtk); + + /** + * This Pokemon's defensive stat for the given move's category. + * Critical hits cause positive stat stages to be ignored. + */ + const targetDef = new Utils.NumberHolder(this.getEffectiveStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, ignoreAbility, ignoreSourceAbility, isCritical, simulated)); + applyMoveAttrs(VariableDefAttr, source, this, move, targetDef); + + /** + * The attack's base damage, as determined by the source's level, move power + * and Attack stat as well as this Pokemon's Defense stat + */ + const baseDamage = ((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2; + + /** Debug message for non-simulated calls (i.e. when damage is actually dealt) */ + if (!simulated) { + console.log("base damage", baseDamage, move.name, power, sourceAtk.value, targetDef.value); + } + + return baseDamage; + } + + /** + * Calculates the damage of an attack made by another Pokemon against this Pokemon + * @param source {@linkcode Pokemon} the attacking Pokemon + * @param move {@linkcode Pokemon} the move used in the attack + * @param ignoreAbility If `true`, ignores this Pokemon's defensive ability effects + * @param ignoreSourceAbility If `true`, ignores the attacking Pokemon's ability effects + * @param isCritical If `true`, calculates damage for a critical hit. + * @param simulated If `true`, suppresses changes to game state during the calculation. + * @returns a {@linkcode DamageCalculationResult} object with three fields: + * - `cancelled`: `true` if the move was cancelled by another effect. + * - `result`: {@linkcode HitResult} indicates the attack's type effectiveness. + * - `damage`: `number` the attack's final damage output. + */ + getAttackDamage(source: Pokemon, move: Move, ignoreAbility: boolean = false, ignoreSourceAbility: boolean = false, isCritical: boolean = false, simulated: boolean = true): DamageCalculationResult { const damage = new Utils.NumberHolder(0); const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; @@ -2332,291 +2402,330 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * The effectiveness of the move being used. Along with type matchups, this * accounts for changes in effectiveness from the move's attributes and the * abilities of both the source and this Pokemon. + * + * Note that the source's abilities are not ignored here */ - const typeMultiplier = this.getMoveEffectiveness(source, move, false, false, cancelled); + const typeMultiplier = this.getMoveEffectiveness(source, move, ignoreAbility, simulated, cancelled); - switch (moveCategory) { - case MoveCategory.PHYSICAL: - case MoveCategory.SPECIAL: - const isPhysical = moveCategory === MoveCategory.PHYSICAL; - const sourceTeraType = source.getTeraType(); + const isPhysical = moveCategory === MoveCategory.PHYSICAL; - const power = move.calculateBattlePower(source, this); + /** Combined damage multiplier from field effects such as weather, terrain, etc. */ + const arenaAttackTypeMultiplier = new Utils.NumberHolder(this.scene.arena.getAttackTypeMultiplier(moveType, source.isGrounded())); + applyMoveAttrs(IgnoreWeatherTypeDebuffAttr, source, this, move, arenaAttackTypeMultiplier); - if (cancelled.value) { - // Cancelled moves fail silently - source.stopMultiHit(this); - return HitResult.NO_EFFECT; - } else { - const typeBoost = source.findTag(t => t instanceof TypeBoostTag && t.boostedType === moveType) as TypeBoostTag; - if (typeBoost?.oneUse) { - source.removeTag(typeBoost.tagType); + const isTypeImmune = (typeMultiplier * arenaAttackTypeMultiplier.value) === 0; + + if (cancelled.value || isTypeImmune) { + return { + cancelled: cancelled.value, + result: move.id === Moves.SHEER_COLD ? HitResult.IMMUNE : HitResult.NO_EFFECT, + damage: 0 + }; + } + + // If the attack deals fixed damaged, return a result with that much damage + const fixedDamage = new Utils.IntegerHolder(0); + applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage); + if (fixedDamage.value) { + return { + cancelled: false, + result: HitResult.EFFECTIVE, + damage: fixedDamage.value + }; + } + + // If the attack is a one-hit KO move, return a result with damage equal to this Pokemon's HP + const isOneHitKo = new Utils.BooleanHolder(false); + applyMoveAttrs(OneHitKOAttr, source, this, move, isOneHitKo); + if (isOneHitKo.value) { + return { + cancelled: false, + result: HitResult.ONE_HIT_KO, + damage: this.hp + }; + } + + /** + * The attack's base damage, as determined by the source's level, move power + * and Attack stat as well as this Pokemon's Defense stat + */ + const baseDamage = this.getBaseDamage(source, move, moveCategory, ignoreAbility, ignoreSourceAbility, isCritical, simulated); + + /** 25% damage debuff on moves hitting more than one non-fainted target (regardless of immunities) */ + const { targets, multiple } = getMoveTargets(source, move.id); + const numTargets = multiple ? targets.length : 1; + const targetMultiplier = (numTargets > 1) ? 0.75 : 1; + + /** 0.25x multiplier if this is an added strike from the attacker's Parental Bond */ + const parentalBondMultiplier = new Utils.NumberHolder(1); + if (!ignoreSourceAbility) { + applyPreAttackAbAttrs(AddSecondStrikeAbAttr, source, this, move, simulated, numTargets, new Utils.IntegerHolder(0), parentalBondMultiplier); + } + + /** Doubles damage if this Pokemon's last move was Glaive Rush */ + const glaiveRushMultiplier = new Utils.IntegerHolder(1); + if (this.getTag(BattlerTagType.RECEIVE_DOUBLE_DAMAGE)) { + glaiveRushMultiplier.value = 2; + } + + /** The damage multiplier when the given move critically hits */ + const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1); + applyAbAttrs(MultCritAbAttr, source, null, simulated, criticalMultiplier); + + /** + * A multiplier for random damage spread in the range [0.85, 1] + * This is always 1 for simulated calls. + */ + const randomMultiplier = simulated ? 1 : ((this.randSeedIntRange(85, 100)) / 100); + + const sourceTypes = source.getTypes(); + const sourceTeraType = source.getTeraType(); + const matchesSourceType = sourceTypes.includes(moveType); + /** A damage multiplier for when the attack is of the attacker's type and/or Tera type. */ + const stabMultiplier = new Utils.NumberHolder(1); + if (matchesSourceType) { + stabMultiplier.value += 0.5; + } + if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === moveType) { + stabMultiplier.value += 0.5; + } + + if (!ignoreSourceAbility) { + applyAbAttrs(StabBoostAbAttr, source, null, simulated, stabMultiplier); + } + + stabMultiplier.value = Math.min(stabMultiplier.value, 2.25); + + /** Halves damage if the attacker is using a physical attack while burned */ + const burnMultiplier = new Utils.NumberHolder(1); + if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) { + if (!move.hasAttr(BypassBurnDamageReductionAttr)) { + const burnDamageReductionCancelled = new Utils.BooleanHolder(false); + if (!ignoreSourceAbility) { + applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled, simulated); } - - /** Combined damage multiplier from field effects such as weather, terrain, etc. */ - const arenaAttackTypeMultiplier = new Utils.NumberHolder(this.scene.arena.getAttackTypeMultiplier(moveType, source.isGrounded())); - applyMoveAttrs(IgnoreWeatherTypeDebuffAttr, source, this, move, arenaAttackTypeMultiplier); - - /** - * Whether or not this Pokemon is immune to the incoming move. - * Note that this isn't fully resolved in `getMoveEffectiveness` because - * of possible type-suppressing field effects (e.g. Desolate Land's effect on Water-type attacks). - */ - const isTypeImmune = (typeMultiplier * arenaAttackTypeMultiplier.value) === 0; - if (isTypeImmune) { - // Moves with no effect that were not cancelled queue a "no effect" message before failing - source.stopMultiHit(this); - result = (move.id === Moves.SHEER_COLD) - ? HitResult.IMMUNE - : HitResult.NO_EFFECT; - - if (result === HitResult.IMMUNE) { - this.scene.queueMessage(i18next.t("battle:hitResultImmune", { pokemonName: this.name })); - } else { - this.scene.queueMessage(i18next.t("battle:hitResultNoEffect", { pokemonName: getPokemonNameWithAffix(this) })); - } - - return result; - } - - const glaiveRushModifier = new Utils.IntegerHolder(1); - if (this.getTag(BattlerTagType.RECEIVE_DOUBLE_DAMAGE)) { - glaiveRushModifier.value = 2; - } - let isCritical: boolean; - const critOnly = new Utils.BooleanHolder(false); - const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT); - applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly); - applyAbAttrs(ConditionalCritAbAttr, source, null, false, critOnly, this, move); - if (critOnly.value || critAlways) { - isCritical = true; - } else { - 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; - } - } - if (isCritical) { - const noCritTag = this.scene.arena.getTagOnSide(NoCritTag, defendingSide); - const blockCrit = new Utils.BooleanHolder(false); - applyAbAttrs(BlockCritAbAttr, this, null, false, blockCrit); - if (noCritTag || blockCrit.value) { - isCritical = false; - } - } - 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); - if (!isCritical) { - this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, move.category, this.scene.currentBattle.double, screenMultiplier); - } - const sourceTypes = source.getTypes(); - const matchesSourceType = sourceTypes[0] === moveType || (sourceTypes.length > 1 && sourceTypes[1] === moveType); - const stabMultiplier = new Utils.NumberHolder(1); - if (sourceTeraType === Type.UNKNOWN && matchesSourceType) { - stabMultiplier.value += 0.5; - } else if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === moveType) { - stabMultiplier.value += 0.5; - } - - applyAbAttrs(StabBoostAbAttr, source, null, false, stabMultiplier); - - if (sourceTeraType !== Type.UNKNOWN && matchesSourceType) { - stabMultiplier.value = Math.min(stabMultiplier.value + 0.5, 2.25); - } - - // 25% damage debuff on moves hitting more than one non-fainted target (regardless of immunities) - const { targets, multiple } = getMoveTargets(source, move.id); - const targetMultiplier = (multiple && targets.length > 1) ? 0.75 : 1; - - applyMoveAttrs(VariableAtkAttr, source, this, move, sourceAtk); - applyMoveAttrs(VariableDefAttr, source, this, move, targetDef); - - const effectPhase = this.scene.getCurrentPhase(); - let numTargets = 1; - if (effectPhase instanceof MoveEffectPhase) { - numTargets = effectPhase.getTargets().length; - } - const twoStrikeMultiplier = new Utils.NumberHolder(1); - applyPreAttackAbAttrs(AddSecondStrikeAbAttr, source, this, move, false, numTargets, new Utils.IntegerHolder(0), twoStrikeMultiplier); - - if (!isTypeImmune) { - const levelMultiplier = (2 * source.level / 5 + 2); - const randomMultiplier = (this.randSeedIntRange(85, 100) / 100); - damage.value = Utils.toDmgValue((((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2) - * stabMultiplier.value - * typeMultiplier - * arenaAttackTypeMultiplier.value - * screenMultiplier.value - * twoStrikeMultiplier.value - * targetMultiplier - * criticalMultiplier.value - * glaiveRushModifier.value - * randomMultiplier); - - if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) { - if (!move.hasAttr(BypassBurnDamageReductionAttr)) { - const burnDamageReductionCancelled = new Utils.BooleanHolder(false); - applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled, false); - if (!burnDamageReductionCancelled.value) { - damage.value = Utils.toDmgValue(damage.value / 2); - } - } - } - - applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, false, damage); - - /** - * For each {@link HitsTagAttr} the move has, doubles the damage of the move if: - * The target has a {@link BattlerTagType} that this move interacts with - * AND - * The move doubles damage when used against that tag - */ - move.getAttrs(HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => { - if (this.getTag(hta.tagType)) { - damage.value *= 2; - } - }); - } - - if (this.scene.arena.terrain?.terrainType === TerrainType.MISTY && this.isGrounded() && moveType === Type.DRAGON) { - damage.value = Utils.toDmgValue(damage.value / 2); - } - - const fixedDamage = new Utils.IntegerHolder(0); - applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage); - if (!isTypeImmune && fixedDamage.value) { - damage.value = fixedDamage.value; - isCritical = false; - result = HitResult.EFFECTIVE; - } - result = result!; // telling TS compiler that result is defined! - - if (!result) { - const isOneHitKo = new Utils.BooleanHolder(false); - applyMoveAttrs(OneHitKOAttr, source, this, move, isOneHitKo); - if (isOneHitKo.value) { - result = HitResult.ONE_HIT_KO; - isCritical = false; - damage.value = this.hp; - } else if (typeMultiplier >= 2) { - result = HitResult.SUPER_EFFECTIVE; - } else if (typeMultiplier >= 1) { - result = HitResult.EFFECTIVE; - } else { - result = HitResult.NOT_VERY_EFFECTIVE; - } - } - - const isOneHitKo = result === HitResult.ONE_HIT_KO; - - if (!fixedDamage.value && !isOneHitKo) { - if (!source.isPlayer()) { - this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damage); - } - if (!this.isPlayer()) { - this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage); - } - - applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, false, damage); - } - - // This attribute may modify damage arbitrarily, so be careful about changing its order of application. - applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage); - - console.log("damage", damage.value, move.name, power, sourceAtk, targetDef); - - // In case of fatal damage, this tag would have gotten cleared before we could lapse it. - const destinyTag = this.getTag(BattlerTagType.DESTINY_BOND); - - if (damage.value) { - this.lapseTags(BattlerTagLapseType.HIT); - - const substitute = this.getTag(SubstituteTag); - if (substitute && move.hitsSubstitute(source, this)) { - substitute.hp -= damage.value; - damage.value = 0; - } - if (this.isFullHp()) { - applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, false, damage); - } else if (!this.isPlayer() && damage.value >= this.hp) { - this.scene.applyModifiers(EnemyEndureChanceModifier, false, this); - } - - /** - * We explicitly require to ignore the faint phase here, as we want to show the messages - * about the critical hit and the super effective/not very effective messages before the faint phase. - */ - damage.value = this.damageAndUpdate(damage.value, result as DamageResult, isCritical, isOneHitKo, isOneHitKo, true); - this.turnData.damageTaken += damage.value; - - if (isCritical) { - this.scene.queueMessage(i18next.t("battle:hitResultCriticalHit")); - } - if (source.isPlayer()) { - this.scene.validateAchvs(DamageAchv, damage); - if (damage.value > this.scene.gameData.gameStats.highestDamage) { - this.scene.gameData.gameStats.highestDamage = damage.value; - } - } - - if (damage.value > 0) { - source.turnData.damageDealt += damage.value; - source.turnData.currDamageDealt = damage.value; - this.battleData.hitCount++; - const attackResult = { move: move.id, result: result as DamageResult, damage: damage.value, critical: isCritical, sourceId: source.id, sourceBattlerIndex: source.getBattlerIndex() }; - this.turnData.attacksReceived.unshift(attackResult); - - if (source.isPlayer() && !this.isPlayer()) { - this.scene.applyModifiers(DamageMoneyRewardModifier, true, source, damage); - } - } - } - - // want to include is.Fainted() in case multi hit move ends early, still want to render message - if (source.turnData.hitsLeft === 1 || this.isFainted()) { - switch (result) { - case HitResult.SUPER_EFFECTIVE: - this.scene.queueMessage(i18next.t("battle:hitResultSuperEffective")); - break; - case HitResult.NOT_VERY_EFFECTIVE: - this.scene.queueMessage(i18next.t("battle:hitResultNotVeryEffective")); - break; - case HitResult.ONE_HIT_KO: - this.scene.queueMessage(i18next.t("battle:hitResultOneHitKO")); - break; - case HitResult.IMMUNE: - case HitResult.NO_EFFECT: - console.error("Unhandled move immunity!"); - break; - } - } - - if (this.isFainted()) { - // set splice index here, so future scene queues happen before FaintedPhase - this.scene.setPhaseQueueSplice(); - this.scene.unshiftPhase(new FaintPhase(this.scene, this.getBattlerIndex(), isOneHitKo)); - this.destroySubstitute(); - this.resetSummonData(); - } - - if (damage) { - destinyTag?.lapse(source, BattlerTagLapseType.CUSTOM); + if (!burnDamageReductionCancelled.value) { + burnMultiplier.value = 0.5; } } - break; - case MoveCategory.STATUS: + } + + /** Reduces damage if this Pokemon has a relevant screen (e.g. Light Screen for special attacks) */ + const screenMultiplier = new Utils.NumberHolder(1); + this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, move.category, this.scene.currentBattle.double, screenMultiplier); + + /** + * For each {@linkcode HitsTagAttr} the move has, doubles the damage of the move if: + * The target has a {@linkcode BattlerTagType} that this move interacts with + * AND + * The move doubles damage when used against that tag + */ + const hitsTagMultiplier = new Utils.NumberHolder(1); + move.getAttrs(HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => { + if (this.getTag(hta.tagType)) { + hitsTagMultiplier.value *= 2; + } + }); + + /** Halves damage if this Pokemon is grounded in Misty Terrain against a Dragon-type attack */ + const mistyTerrainMultiplier = (this.scene.arena.terrain?.terrainType === TerrainType.MISTY && this.isGrounded() && moveType === Type.DRAGON) + ? 0.5 + : 1; + + damage.value = Utils.toDmgValue( + baseDamage + * targetMultiplier + * parentalBondMultiplier.value + * arenaAttackTypeMultiplier.value + * glaiveRushMultiplier.value + * criticalMultiplier.value + * randomMultiplier + * stabMultiplier.value + * typeMultiplier + * burnMultiplier.value + * screenMultiplier.value + * hitsTagMultiplier.value + * mistyTerrainMultiplier + ); + + /** Doubles damage if the attacker has Tinted Lens and is using a resisted move */ + if (!ignoreSourceAbility) { + applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, simulated, damage); + } + + /** Apply the enemy's Damage and Resistance tokens */ + if (!source.isPlayer()) { + this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damage); + } + if (!this.isPlayer()) { + this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage); + } + + /** Apply this Pokemon's post-calc defensive modifiers (e.g. Fur Coat) */ + if (!ignoreAbility) { + applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, simulated, damage); + } + + // This attribute may modify damage arbitrarily, so be careful about changing its order of application. + applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage); + + if (this.isFullHp() && !ignoreAbility) { + applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, false, damage); + } + + // debug message for when damage is applied (i.e. not simulated) + if (!simulated) { + console.log("damage", damage.value, move.name); + } + + let hitResult: HitResult; + if (typeMultiplier < 1) { + hitResult = HitResult.NOT_VERY_EFFECTIVE; + } else if (typeMultiplier > 1) { + hitResult = HitResult.SUPER_EFFECTIVE; + } else { + hitResult = HitResult.EFFECTIVE; + } + + return { + cancelled: cancelled.value, + result: hitResult, + damage: damage.value + }; + } + + /** + * Applies the results of a move to this pokemon + * @param source The {@linkcode Pokemon} using the move + * @param move The {@linkcode Move} being used + * @returns The {@linkcode HitResult} of the attack + */ + apply(source: Pokemon, move: Move): HitResult { + const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + if (move.category === MoveCategory.STATUS) { + const cancelled = new Utils.BooleanHolder(false); + const typeMultiplier = this.getMoveEffectiveness(source, move, false, false, cancelled); + if (!cancelled.value && typeMultiplier === 0) { this.scene.queueMessage(i18next.t("battle:hitResultNoEffect", { pokemonName: getPokemonNameWithAffix(this) })); } - result = (cancelled.value || typeMultiplier === 0) ? HitResult.NO_EFFECT : HitResult.STATUS; - break; - } + return (typeMultiplier === 0) ? HitResult.NO_EFFECT : HitResult.STATUS; + } else { + /** Determines whether the attack critically hits */ + let isCritical: boolean; + const critOnly = new Utils.BooleanHolder(false); + const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT); + applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly); + applyAbAttrs(ConditionalCritAbAttr, source, null, false, critOnly, this, move); + if (critOnly.value || critAlways) { + isCritical = true; + } else { + const critChance = [24, 8, 2, 1][Math.max(0, Math.min(this.getCritStage(source, move), 3))]; + isCritical = critChance === 1 || !this.scene.randBattleSeedInt(critChance); + } - return result; + const noCritTag = this.scene.arena.getTagOnSide(NoCritTag, defendingSide); + const blockCrit = new Utils.BooleanHolder(false); + applyAbAttrs(BlockCritAbAttr, this, null, false, blockCrit); + if (noCritTag || blockCrit.value || Overrides.NEVER_CRIT_OVERRIDE) { + isCritical = false; + } + + const { cancelled, result, damage: dmg } = this.getAttackDamage(source, move, false, false, isCritical, false); + + const typeBoost = source.findTag(t => t instanceof TypeBoostTag && t.boostedType === source.getMoveType(move)) as TypeBoostTag; + if (typeBoost?.oneUse) { + source.removeTag(typeBoost.tagType); + } + + if (cancelled || result === HitResult.IMMUNE || result === HitResult.NO_EFFECT) { + source.stopMultiHit(this); + + if (!cancelled) { + if (result === HitResult.IMMUNE) { + this.scene.queueMessage(i18next.t("battle:hitResultImmune", { pokemonName: getPokemonNameWithAffix(this) })); + } else { + this.scene.queueMessage(i18next.t("battle:hitResultNoEffect", { pokemonName: getPokemonNameWithAffix(this) })); + } + } + return result; + } + + // In case of fatal damage, this tag would have gotten cleared before we could lapse it. + const destinyTag = this.getTag(BattlerTagType.DESTINY_BOND); + + const isOneHitKo = result === HitResult.ONE_HIT_KO; + + if (dmg) { + this.lapseTags(BattlerTagLapseType.HIT); + + const substitute = this.getTag(SubstituteTag); + const isBlockedBySubstitute = !!substitute && move.hitsSubstitute(source, this); + if (isBlockedBySubstitute) { + substitute.hp -= dmg; + } + if (!this.isPlayer() && dmg >= this.hp) { + this.scene.applyModifiers(EnemyEndureChanceModifier, false, this); + } + + /** + * We explicitly require to ignore the faint phase here, as we want to show the messages + * about the critical hit and the super effective/not very effective messages before the faint phase. + */ + const damage = this.damageAndUpdate(isBlockedBySubstitute ? 0 : dmg, result as DamageResult, isCritical, isOneHitKo, isOneHitKo, true); + + if (damage > 0) { + if (source.isPlayer()) { + this.scene.validateAchvs(DamageAchv, damage); + if (damage > this.scene.gameData.gameStats.highestDamage) { + this.scene.gameData.gameStats.highestDamage = damage; + } + } + source.turnData.damageDealt += damage; + source.turnData.currDamageDealt = damage; + this.turnData.damageTaken += damage; + this.battleData.hitCount++; + const attackResult = { move: move.id, result: result as DamageResult, damage: damage, critical: isCritical, sourceId: source.id, sourceBattlerIndex: source.getBattlerIndex() }; + this.turnData.attacksReceived.unshift(attackResult); + if (source.isPlayer() && !this.isPlayer()) { + this.scene.applyModifiers(DamageMoneyRewardModifier, true, source, new Utils.NumberHolder(damage)); + } + } + } + + if (isCritical) { + this.scene.queueMessage(i18next.t("battle:hitResultCriticalHit")); + } + + // want to include is.Fainted() in case multi hit move ends early, still want to render message + if (source.turnData.hitsLeft === 1 || this.isFainted()) { + switch (result) { + case HitResult.SUPER_EFFECTIVE: + this.scene.queueMessage(i18next.t("battle:hitResultSuperEffective")); + break; + case HitResult.NOT_VERY_EFFECTIVE: + this.scene.queueMessage(i18next.t("battle:hitResultNotVeryEffective")); + break; + case HitResult.ONE_HIT_KO: + this.scene.queueMessage(i18next.t("battle:hitResultOneHitKO")); + break; + } + } + + if (this.isFainted()) { + // set splice index here, so future scene queues happen before FaintedPhase + this.scene.setPhaseQueueSplice(); + this.scene.unshiftPhase(new FaintPhase(this.scene, this.getBattlerIndex(), isOneHitKo)); + this.destroySubstitute(); + this.resetSummonData(); + } + + if (dmg) { + destinyTag?.lapse(source, BattlerTagLapseType.CUSTOM); + } + + return result; + } } /** @@ -2862,16 +2971,40 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.getRestrictingTag(moveId) !== null; } + /** + * Gets whether the given move is currently disabled for the user based on the player's target selection + * + * @param {Moves} moveId {@linkcode Moves} ID of the move to check + * @param {Pokemon} user {@linkcode Pokemon} the move user + * @param {Pokemon} target {@linkcode Pokemon} the target of the move + * + * @returns {boolean} `true` if the move is disabled for this Pokemon due to the player's target selection + * + * @see {@linkcode MoveRestrictionBattlerTag} + */ + isMoveTargetRestricted(moveId: Moves, user: Pokemon, target: Pokemon): boolean { + for (const tag of this.findTags(t => t instanceof MoveRestrictionBattlerTag)) { + if ((tag as MoveRestrictionBattlerTag).isMoveTargetRestricted(moveId, user, target)) { + return (tag as MoveRestrictionBattlerTag !== null); + } + } + return false; + } + /** * Gets the {@link MoveRestrictionBattlerTag} that is restricting a move, if it exists. * * @param {Moves} moveId {@linkcode Moves} ID of the move to check + * @param {Pokemon} user {@linkcode Pokemon} the move user, optional and used when the target is a factor in the move's restricted status + * @param {Pokemon} target {@linkcode Pokemon} the target of the move, optional and used when the target is a factor in the move's restricted status * @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 { + getRestrictingTag(moveId: Moves, user?: Pokemon, target?: Pokemon): MoveRestrictionBattlerTag | null { for (const tag of this.findTags(t => t instanceof MoveRestrictionBattlerTag)) { if ((tag as MoveRestrictionBattlerTag).isMoveRestricted(moveId)) { return tag as MoveRestrictionBattlerTag; + } else if (user && target && (tag as MoveRestrictionBattlerTag).isMoveTargetRestricted(moveId, user, target)) { + return tag as MoveRestrictionBattlerTag; } } return null; @@ -3275,6 +3408,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.updateFusionPalette(); } this.summonData = new PokemonSummonData(); + this.setSwitchOutStatus(false); if (!this.battleData) { this.resetBattleData(); } @@ -3680,6 +3814,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.hideInfo(); } this.scene.field.remove(this); + this.setSwitchOutStatus(true); this.scene.triggerPokemonFormChange(this, SpeciesFormChangeActiveTrigger, true); } @@ -3703,6 +3838,25 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const rootForm = getPokemonSpecies(this.species.getRootSpeciesId()); return rootForm.getAbility(abilityIndex) === rootForm.getAbility(currentAbilityIndex); } + + /** + * Helper function to check if the player already owns the starter data of the Pokemon's + * current ability + * @param ownedAbilityAttrs the owned abilityAttr of this Pokemon's root form + * @returns true if the player already has it, false otherwise + */ + checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs: number): boolean { + if ((ownedAbilityAttrs & 1) > 0 && this.hasSameAbilityInRootForm(0)) { + return true; + } + if ((ownedAbilityAttrs & 2) > 0 && this.hasSameAbilityInRootForm(1)) { + return true; + } + if ((ownedAbilityAttrs & 4) > 0 && this.hasSameAbilityInRootForm(2)) { + return true; + } + return false; + } } export default interface Pokemon { @@ -3968,6 +4122,12 @@ export class PlayerPokemon extends Pokemon { this.updateInfo(true).then(() => resolve()); }); }; + if (preEvolution.speciesId === Species.GIMMIGHOUL) { + const evotracker = this.getHeldItems().filter(m => m instanceof EvoTrackerModifier)[0] ?? null; + if (evotracker) { + this.scene.removeModifier(evotracker); + } + } if (!this.scene.gameMode.isDaily || this.metBiome > -1) { this.scene.gameData.updateSpeciesDexIvs(this.species.speciesId, this.ivs); this.scene.gameData.setPokemonSeen(this, false); @@ -3995,6 +4155,7 @@ export class PlayerPokemon extends Pokemon { newPokemon.metLevel = this.metLevel; newPokemon.metBiome = this.metBiome; newPokemon.metSpecies = this.metSpecies; + newPokemon.metWave = this.metWave; newPokemon.fusionSpecies = this.fusionSpecies; newPokemon.fusionFormIndex = this.fusionFormIndex; newPokemon.fusionAbilityIndex = this.fusionAbilityIndex; @@ -4002,6 +4163,7 @@ export class PlayerPokemon extends Pokemon { newPokemon.fusionVariant = this.fusionVariant; newPokemon.fusionGender = this.fusionGender; newPokemon.fusionLuck = this.fusionLuck; + newPokemon.usedTMs = this.usedTMs; this.scene.getParty().push(newPokemon); newPokemon.evolve((!isFusion ? newEvolution : new FusionSpeciesFormEvolution(this.id, newEvolution)), evoSpecies); @@ -4287,7 +4449,7 @@ export class EnemyPokemon extends Pokemon { } // Filter out any moves this Pokemon cannot use - const movePool = this.getMoveset().filter(m => m?.isUsable(this)); + let movePool = this.getMoveset().filter(m => m?.isUsable(this)); // If no moves are left, use Struggle. Otherwise, continue with move selection if (movePool.length) { // If there's only 1 move in the move pool, use it. @@ -4308,6 +4470,39 @@ export class EnemyPokemon extends Pokemon { return { move: moveId, targets: this.getNextTargets(moveId) }; case AiType.SMART_RANDOM: case AiType.SMART: + /** + * Search this Pokemon's move pool for moves that will KO an opposing target. + * If there are any moves that can KO an opponent (i.e. a player Pokemon), + * those moves are the only ones considered for selection on this turn. + */ + const koMoves = movePool.filter(pkmnMove => { + if (!pkmnMove) { + return false; + } + + const move = pkmnMove.getMove()!; + if (move.moveTarget === MoveTarget.ATTACKER) { + return false; + } + + const fieldPokemon = this.scene.getField(); + const moveTargets = getMoveTargets(this, move.id).targets + .map(ind => fieldPokemon[ind]) + .filter(p => this.isPlayer() !== p.isPlayer()); + // Only considers critical hits for crit-only moves or when this Pokemon is under the effect of Laser Focus + const isCritical = move.hasAttr(CritOnlyAttr) || !!this.getTag(BattlerTagType.ALWAYS_CRIT); + + return move.category !== MoveCategory.STATUS + && moveTargets.some(p => { + const doesNotFail = move.applyConditions(this, p, move) || [Moves.SUCKER_PUNCH, Moves.UPPER_HAND, Moves.THUNDERCLAP].includes(move.id); + return doesNotFail && p.getAttackDamage(this, move, !p.battleData.abilityRevealed, false, isCritical).damage >= p.hp; + }); + }, this); + + if (koMoves.length > 0) { + movePool = koMoves; + } + /** * Move selection is based on the move's calculated "benefit score" against the * best possible target(s) (as determined by {@linkcode getNextTargets}). @@ -4578,8 +4773,15 @@ export class EnemyPokemon extends Pokemon { return true; } + /** + * Go through a boss' health segments and give stats boosts for each newly cleared segment + * The base boost is 1 to a random stat that's not already maxed out per broken shield + * For Pokemon with 3 health segments or more, breaking the last shield gives +2 instead + * For Pokemon with 5 health segments or more, breaking the last two shields give +2 each + * @param segmentIndex index of the segment to get down to (0 = no shield left, 1 = 1 shield left, etc.) + */ handleBossSegmentCleared(segmentIndex: integer): void { - while (segmentIndex - 1 < this.bossSegmentIndex) { + while (this.bossSegmentIndex > 0 && segmentIndex - 1 < this.bossSegmentIndex) { // 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)); @@ -4660,6 +4862,7 @@ export class EnemyPokemon extends Pokemon { this.pokeball = pokeballType; this.metLevel = this.level; this.metBiome = this.scene.arena.biomeType; + this.metWave = this.scene.currentBattle.waveIndex; 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); @@ -4779,6 +4982,16 @@ export enum HitResult { export type DamageResult = HitResult.EFFECTIVE | HitResult.SUPER_EFFECTIVE | HitResult.NOT_VERY_EFFECTIVE | HitResult.ONE_HIT_KO | HitResult.OTHER; +/** Interface containing the results of a damage calculation for a given move */ +export interface DamageCalculationResult { + /** `true` if the move was cancelled (thus suppressing "No Effect" messages) */ + cancelled: boolean; + /** The effectiveness of the move */ + result: HitResult; + /** The damage dealt by the move */ + damage: number; +} + /** * Wrapper class for the {@linkcode Move} class for Pokemon to interact with. * These are the moves assigned to a {@linkcode Pokemon} object. diff --git a/src/game-mode.ts b/src/game-mode.ts index a2d61d7cfff..525c975a19b 100644 --- a/src/game-mode.ts +++ b/src/game-mode.ts @@ -268,7 +268,6 @@ export class GameMode implements GameModeConfig { isFixedBattle(waveIndex: integer): boolean { const dummyConfig = new FixedBattleConfig(); return this.battleConfig.hasOwnProperty(waveIndex) || applyChallenges(this, ChallengeType.FIXED_BATTLES, waveIndex, dummyConfig); - } /** diff --git a/src/loading-scene.ts b/src/loading-scene.ts index d0818aa1e19..c3cb494d497 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -241,12 +241,15 @@ export class LoadingScene extends SceneBase { const lang = i18next.resolvedLanguage; if (lang !== "en") { if (Utils.verifyLang(lang)) { + this.loadAtlas(`statuses_${lang}`, ""); this.loadAtlas(`types_${lang}`, ""); } else { // Fallback to English + this.loadAtlas("statuses", ""); this.loadAtlas("types", ""); } } else { + this.loadAtlas("statuses", ""); this.loadAtlas("types", ""); } const availableLangs = ["en", "de", "it", "fr", "ja", "ko", "es", "pt-BR", "zh-CN"]; diff --git a/src/locales/de/battle.json b/src/locales/de/battle.json index 38e36d4b2da..7a9c2570211 100644 --- a/src/locales/de/battle.json +++ b/src/locales/de/battle.json @@ -96,5 +96,7 @@ "unlockedSomething": "{{unlockedThing}} wurde freigeschaltet.", "congratulations": "Glückwunsch!", "beatModeFirstTime": "{{speciesName}} hat den {{gameMode}} Modus zum ersten Mal beendet! Du erhältst {{newModifier}}!", - "eggSkipPrompt": "Zur Ei-Zusammenfassung springen?" + "eggSkipPrompt": "Zur Ei-Zusammenfassung springen?", + "battlerTagsHealBlock": "{{pokemonNameWithAffix}} kann nicht geheilt werden, da die Heilung blockiert wird!", + "battlerTagsHealBlockOnRemove": "{{pokemonNameWithAffix}} kann wieder geheilt werden!" } diff --git a/src/locales/de/move.json b/src/locales/de/move.json index 3c81ccfd7df..b7a42cb1787 100644 --- a/src/locales/de/move.json +++ b/src/locales/de/move.json @@ -3121,11 +3121,11 @@ }, "behemothBlade": { "name": "Gigantenhieb", - "effect": "Der Anwender wird zu einem riesigen Schwert und greift das Ziel an. Dynamaximierte Ziele erleiden doppelten Schaden." + "effect": "Der Anwender wird zu einem riesigen Schwert und greift das Ziel an." }, "behemothBash": { "name": "Gigantenstoß", - "effect": "Der Anwender wird zu einem riesigen Schild und greift das Ziel an. Dynamaximierte Ziele erleiden doppelten Schaden." + "effect": "Der Anwender wird zu einem riesigen Schild und greift das Ziel an." }, "auraWheel": { "name": "Aura-Rad", diff --git a/src/locales/de/pokemon-summary.json b/src/locales/de/pokemon-summary.json index 1790c6878b9..3104fc10151 100644 --- a/src/locales/de/pokemon-summary.json +++ b/src/locales/de/pokemon-summary.json @@ -11,7 +11,7 @@ "cancel": "Abbrechen", "memoString": "Wesen: {{natureFragment}}\n{{metFragment}}", "metFragment": { - "normal": "Herkunft: {{biome}}\nMit Lv. {{level}} erhalten.", + "normal": "Herkunft: {{biome}} - Welle {{wave}}\nMit Lv. {{level}} erhalten.", "apparently": "Herkunft: {{biome}}\nOffenbar mit Lv. {{level}} erhalten." }, "natureFragment": { diff --git a/src/locales/de/splash-messages.json b/src/locales/de/splash-messages.json index ac3fd345f3f..ba126393ccb 100644 --- a/src/locales/de/splash-messages.json +++ b/src/locales/de/splash-messages.json @@ -1,5 +1,5 @@ { - "battlesWon": "Kämpfe gewonnen!", + "battlesWon": "{{count, number}} Kämpfe gewonnen!", "joinTheDiscord": "Tritt dem Discord bei!", "infiniteLevels": "Unendliche Level!", "everythingStacks": "Alles stapelt sich!", diff --git a/src/locales/en/achv.json b/src/locales/en/achv.json index 32d519fbf78..b04f23d4209 100644 --- a/src/locales/en/achv.json +++ b/src/locales/en/achv.json @@ -283,5 +283,9 @@ "INVERSE_BATTLE": { "name": "Mirror rorriM", "description": "Complete the Inverse Battle challenge.\n.egnellahc elttaB esrevnI eht etelpmoC" + }, + "BREEDERS_IN_SPACE": { + "name": "Breeders in Space!", + "description": "Beat the Expert Pokémon Breeder in the Space Biome." } } diff --git a/src/locales/en/battle.json b/src/locales/en/battle.json index d04dd3eac1f..2559dafecae 100644 --- a/src/locales/en/battle.json +++ b/src/locales/en/battle.json @@ -105,5 +105,7 @@ "congratulations": "Congratulations!", "beatModeFirstTime": "{{speciesName}} beat {{gameMode}} Mode for the first time!\nYou received {{newModifier}}!", "ppReduced": "It reduced the PP of {{targetName}}'s\n{{moveName}} by {{reduction}}!", - "mysteryEncounterAppeared": "What's this?" + "mysteryEncounterAppeared": "What's this?", + "battlerTagsHealBlock": "{{pokemonNameWithAffix}} can't restore its HP!", + "battlerTagsHealBlockOnRemove": "{{pokemonNameWithAffix}} can restore its HP again!" } \ No newline at end of file diff --git a/src/locales/en/bgm-name.json b/src/locales/en/bgm-name.json index c2babed4dff..2da1b9e85a3 100644 --- a/src/locales/en/bgm-name.json +++ b/src/locales/en/bgm-name.json @@ -83,9 +83,11 @@ "battle_aether_grunt": "SM Aether Foundation Battle", "battle_skull_grunt": "SM Team Skull Battle", "battle_macro_grunt": "SWSH Trainer Battle", + "battle_star_grunt": "SV Team Star Battle", "battle_galactic_admin": "BDSP Team Galactic Admin Battle", "battle_skull_admin": "SM Team Skull Admin Battle", "battle_oleana": "SWSH Oleana Battle", + "battle_star_admin": "SV Team Star Boss", "battle_rocket_boss": "USUM Giovanni Battle", "battle_aqua_magma_boss": "ORAS Archie & Maxie Battle", "battle_galactic_boss": "BDSP Cyrus Battle", @@ -94,6 +96,7 @@ "battle_aether_boss": "SM Lusamine Battle", "battle_skull_boss": "SM Guzma Battle", "battle_macro_boss": "SWSH Rose Battle", + "battle_star_boss": "SV Cassiopeia Battle", "abyss": "PMD EoS Dark Crater", "badlands": "PMD EoS Barren Valley", @@ -108,17 +111,17 @@ "forest": "PMD EoS Dusk Forest", "grass": "PMD EoS Apple Woods", "graveyard": "PMD EoS Mystifying Forest", - "ice_cave": "PMD EoS Vast Ice Mountain", + "ice_cave": "Firel - -60F", "island": "PMD EoS Craggy Coast", "jungle": "Lmz - Jungle", "laboratory": "Firel - Laboratory", - "lake": "PMD EoS Crystal Cave", + "lake": "Lmz - Lake", "meadow": "PMD EoS Sky Peak Forest", "metropolis": "Firel - Metropolis", "mountain": "PMD EoS Mt. Horn", - "plains": "PMD EoS Sky Peak Prairie", - "power_plant": "PMD EoS Far Amp Plains", - "ruins": "PMD EoS Deep Sealed Ruin", + "plains": "Firel - Route 888", + "power_plant": "Firel - The Klink", + "ruins": "Lmz - Ancient Ruins", "sea": "Andr06 - Marine Mystique", "seabed": "Firel - Seabed", "slum": "Andr06 - Sneaky Snom", @@ -128,7 +131,7 @@ "tall_grass": "PMD EoS Foggy Forest", "temple": "PMD EoS Aegis Cave", "town": "PMD EoS Random Dungeon Theme 3", - "volcano": "PMD EoS Steam Cave", + "volcano": "Firel - Twisturn Volcano", "wasteland": "PMD EoS Hidden Highland", "encounter_ace_trainer": "BW Trainers' Eyes Meet (Ace Trainer)", "encounter_backpacker": "BW Trainers' Eyes Meet (Backpacker)", @@ -151,5 +154,6 @@ "mystery_encounter_weird_dream": "PMD EoS Temporal Spire", "mystery_encounter_fun_and_games": "PMD EoS Guildmaster Wigglytuff", "mystery_encounter_gen_5_gts": "BW GTS", - "mystery_encounter_gen_6_gts": "XY GTS" + "mystery_encounter_gen_6_gts": "XY GTS", + "mystery_encounter_delibirdy": "Firel - DeliDelivery!" } diff --git a/src/locales/en/config.ts b/src/locales/en/config.ts index f83fec5be26..35eef91e2ad 100644 --- a/src/locales/en/config.ts +++ b/src/locales/en/config.ts @@ -84,6 +84,7 @@ import bugTypeSuperfan from "#app/locales/en/mystery-encounters/bug-type-superfa import funAndGames from "#app/locales/en/mystery-encounters/fun-and-games-dialogue.json"; import uncommonBreed from "#app/locales/en/mystery-encounters/uncommon-breed-dialogue.json"; import globalTradeSystem from "#app/locales/en/mystery-encounters/global-trade-system-dialogue.json"; +import expertPokemonBreeder from "#app/locales/en/mystery-encounters/the-expert-pokemon-breeder-dialogue.json"; /** * Dialogue/Text token injection patterns that can be used: @@ -183,7 +184,8 @@ export const enConfig = { bugTypeSuperfan, funAndGames, uncommonBreed, - globalTradeSystem + globalTradeSystem, + expertPokemonBreeder }, mysteryEncounterMessages }; diff --git a/src/locales/en/dialogue.json b/src/locales/en/dialogue.json index 39a4238355c..66f9e8db7a5 100644 --- a/src/locales/en/dialogue.json +++ b/src/locales/en/dialogue.json @@ -764,12 +764,16 @@ "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?" + "3": "I'm from Macro Cosmos Insurance! Do you have a life insurance policy?", + "4": "I found you! In that case, time for a Pokémon battle!", + "5": "An earful from Ms. Oleana is way worse than anything you can do!" }, "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!" + "3": "Nobody can beat Macro Cosmos when it comes to our dedication to our work!", + "4": "I even switched up my Pokémon...", + "5": "Battles didn't work... Only thing to do now is run!" } }, "oleana": { @@ -785,6 +789,73 @@ "3": "*sigh* I am one tired Oleana..." } }, + "star_grunt": { + "encounter": { + "1": "We're Team Star, kid. We burn so bright, it hurts to look at us!", + "2": "We'll come at you full force - Hasta la vistaaar! ☆", + "3": "If you don't clear out real quick-like, I'll hafta come at you in self-defense. You get me?", + "4": "Sorry, but if you don't turn yourself around here, amigo, we'll have to send you packing!", + "4_female": "Sorry, but if you don't turn yourself around here, amiga, we'll have to send you packing!", + "5": "Oh great. Here comes another rando to ruin my day." + }, + "victory": { + "1": "How come I'M the one seeing stars?!", + "2": "You're scary, kid. If you joined Team Star, you'd be looking down from the top in no time!", + "3": "I defended myself all right... But it wasn't enough!", + "4": "H-hasta la vistar... ☆", + "5": "I didn't think grunt work for Team Star newbies would be this much of a chore..." + } + }, + "giacomo": { + "encounter": { + "1": "You don't really think things through, do ya? Declarin' war on Team Star is a real bad move.", + "2": "I'll play you a sick requiem as you crash and burn. Let's get this party staaarteeed!" + }, + "victory": { + "1": "Guess that's that...", + "2": "You turned my melody into a threnody..." + } + }, + "mela": { + "encounter": { + "1": "So you're the dope who picked a fight with Team Star... Prepare to get messed up.", + "2": "All riiight, BRING IT! I'll blow everythin' sky high!" + }, + "victory": { + "1": "Ugh. Is this really how it's gonna end? What a hassle...", + "2": "I burned through everythin' I had...and now I've sputtered out." + } + }, + "atticus": { + "encounter": { + "1": "You have some nerve baring your fangs at Team Star. Come, then, villainous wretch!", + "2": "Be warned—I shall spare thee no mercy! En garde!" + }, + "victory": { + "1": "Forgive me, my friends...", + "2": "You have utterly bested me. But thy victory stir'd no bitterness within me—such was its brilliance." + } + }, + "ortega": { + "encounter": { + "1": "I promise I'll play nice, so don't blame me when this battle sends you blubbering back home!", + "2": "I'll wipe that smug look off your face for sure! You're going down!" + }, + "victory": { + "1": "Ugh! How could I LOSE! What the HECK!", + "2": "Arrrrgggh! That strength of yours is SO. NOT. FAIR." + } + }, + "eri": { + "encounter": { + "1": "Doesn't matter who you are. I'll bury anyone who tries to take down Team Star!", + "2": "I give as good as I get—that's a promise! We'll see who's left standing in the end!" + }, + "victory": { + "1": "I'm so sorry, everyone...", + "2": "I gave it my all, but it wasn't enough—I wasn't enough..." + } + }, "rocket_boss_giovanni_1": { "encounter": { "1": "So! I must say, I am impressed you got here!" @@ -985,6 +1056,28 @@ "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." } }, + "star_boss_penny_1": { + "encounter": { + "1": "I'm the big boss of Team Star. The name's Cassiopeia. \n$Now, bow down before the overwhelming might of Team Star's founder!" + }, + "victory": { + "1": "... ... .." + }, + "defeat": { + "1": "Heh..." + } + }, + "star_boss_penny_2": { + "encounter": { + "1": "I won't hold back in this battle! I'll stay true to Team Star's code! \n$My Veevee power will crush you into stardust!" + }, + "victory": { + "1": "...It's all over now." + }, + "defeat": { + "1": "I can't fault you on your battle skills at all... Considering how the bosses fell at your hands." + } + }, "stat_trainer_buck": { "encounter": { "1": "...I'm telling you right now. I'm seriously tough. Act surprised!", diff --git a/src/locales/en/modifier-type.json b/src/locales/en/modifier-type.json index b57073325dc..c362b3f30d4 100644 --- a/src/locales/en/modifier-type.json +++ b/src/locales/en/modifier-type.json @@ -240,6 +240,8 @@ "TOXIC_ORB": { "name": "Toxic Orb", "description": "It's a bizarre orb that exudes toxins when touched and will badly poison the holder during battle." }, "FLAME_ORB": { "name": "Flame Orb", "description": "It's a bizarre orb that gives off heat when touched and will affect the holder with a burn during battle." }, + "EVOLUTION_TRACKER_GIMMIGHOUL": { "name": "Treasures", "description": "This Pokémon loves treasure! Keep collecting treasure and something might happen!"}, + "BATON": { "name": "Baton", "description": "Allows passing along effects when switching Pokémon, which also bypasses traps." }, "SHINY_CHARM": { "name": "Shiny Charm", "description": "Dramatically increases the chance of a wild Pokémon being Shiny." }, @@ -330,6 +332,21 @@ "TART_APPLE": "Tart Apple", "STRAWBERRY_SWEET": "Strawberry Sweet", "UNREMARKABLE_TEACUP": "Unremarkable Teacup", + "UPGRADE": "Upgrade", + "DUBIOUS_DISC": "Dubious Disc", + "DRAGON_SCALE": "Dragon Scale", + "PRISM_SCALE": "Prism Scale", + "RAZOR_CLAW": "Razor Claw", + "RAZOR_FANG": "Razor Fang", + "REAPER_CLOTH": "Reaper Cloth", + "ELECTIRIZER": "Electirizer", + "MAGMARIZER": "Magmarizer", + "PROTECTOR": "Protector", + "SACHET": "Sachet", + "WHIPPED_DREAM": "Whipped Dream", + "LEADERS_CREST": "Leader's Crest", + "SUN_FLUTE": "Sun Flute", + "MOON_FLUTE": "Moon Flute", "CHIPPED_POT": "Chipped Pot", "BLACK_AUGURITE": "Black Augurite", diff --git a/src/locales/en/mystery-encounters/an-offer-you-cant-refuse-dialogue.json b/src/locales/en/mystery-encounters/an-offer-you-cant-refuse-dialogue.json index 6dd54d302ab..e286d89691a 100644 --- a/src/locales/en/mystery-encounters/an-offer-you-cant-refuse-dialogue.json +++ b/src/locales/en/mystery-encounters/an-offer-you-cant-refuse-dialogue.json @@ -1,7 +1,7 @@ { "intro": "You're stopped by a rich looking boy.", "speaker": "Rich Boy", - "intro_dialogue": "Good day to you.$I can't help but notice that your\n{{strongestPokemon}} looks positively divine!$I've always wanted to have a pet like that!$I'd pay you handsomely,\nand also give you this old bauble!", + "intro_dialogue": "Good day to you.$I can't help but notice that your\n{{strongestPokemon}} looks positively divine!$I've always wanted to have a Pokémon like that!$I'd pay you handsomely,\nand also give you this old bauble!", "title": "An Offer You Can't Refuse", "description": "You're being offered a @[TOOLTIP_TITLE]{Shiny Charm} and {{price, money}} for your {{strongestPokemon}}!\n\nIt's an extremely good deal, but can you really bear to part with such a strong team member?", "query": "What will you do?", diff --git a/src/locales/en/mystery-encounters/bug-type-superfan-dialogue.json b/src/locales/en/mystery-encounters/bug-type-superfan-dialogue.json index 7df01326aed..09488addb98 100644 --- a/src/locales/en/mystery-encounters/bug-type-superfan-dialogue.json +++ b/src/locales/en/mystery-encounters/bug-type-superfan-dialogue.json @@ -17,9 +17,9 @@ "disabled_tooltip": "You need at least 1 Bug Type Pokémon on your team to select this.", "selected": "You show the trainer all your Bug Type Pokémon...", "selected_0_to_1": "Huh? You only have {{numBugTypes}}...$Guess I'm wasting my breath on someone like you...", - "selected_2_to_3": "Hey, you've got {{numBugTypes}} Bug Types!\nNot bad.$Here, this might help you on your journey to catch more!", - "selected_4_to_5": "What? You have {{numBugTypes}} Bug Types?\nNice!$You're not quite at my level, but I can see shades of myself in you!\n$Take this, my young apprentice!", - "selected_6": "Whoa! {{numBugTypes}} Bug Types!\n$You must love Bug Types almost as much as I do!$Here, take this as a token of our camaraderie!" + "selected_2_to_3": "Hey, you've got {{numBugTypes}}!\nNot bad.$Here, this might help you on your journey to catch more!", + "selected_4_to_5": "What? You have {{numBugTypes}}?\nNice!$You're not quite at my level, but I can see shades of myself in you!\n$Take this, my young apprentice!", + "selected_6": "Whoa! {{numBugTypes}}!\n$You must love Bug Types almost as much as I do!$Here, take this as a token of our camaraderie!" }, "3": { "label": "Gift a Bug Item", @@ -34,5 +34,7 @@ "battle_won": "Your knowledge and skill were perfect at exploiting our weaknesses!$In exchange for the valuable lesson,\nallow me to teach one of your Pokémon a Bug Type Move!", "teach_move_prompt": "Select a move to teach a Pokémon.", "confirm_no_teach": "You sure you don't want to learn one of these great moves?", - "outro": "I see great Bug Pokémon in your future!\nMay our paths cross again!$Bug out!" + "outro": "I see great Bug Pokémon in your future!\nMay our paths cross again!$Bug out!", + "numBugTypes_one": "{{count}} Bug Type", + "numBugTypes_other": "{{count}} Bug Types" } \ No newline at end of file diff --git a/src/locales/en/mystery-encounters/part-timer-dialogue.json b/src/locales/en/mystery-encounters/part-timer-dialogue.json index 614f1818e3f..801a409ee84 100644 --- a/src/locales/en/mystery-encounters/part-timer-dialogue.json +++ b/src/locales/en/mystery-encounters/part-timer-dialogue.json @@ -21,7 +21,7 @@ "label": "Sales Assistant", "tooltip": "(-) Your {{option3PrimaryName}} uses {{option3PrimaryMove}}\n(+) Earn @[MONEY]{Money}", "disabled_tooltip": "Your Pokémon need to know certain moves for this job", - "selected": "Your {{option3PrimaryName}} spends the day using {{option3PrimaryMove}} to attract customers to the business!" + "selected": "Your {{option3PrimaryName}} spends the day using {{option3PrimaryMove}} to draw customers to the business!" } }, "job_complete_good": "Thanks for the assistance!\nYour {{selectedPokemon}} was incredibly helpful!$Here's your check for the day.", diff --git a/src/locales/en/mystery-encounters/the-expert-pokemon-breeder-dialogue.json b/src/locales/en/mystery-encounters/the-expert-pokemon-breeder-dialogue.json new file mode 100644 index 00000000000..ebe3af38add --- /dev/null +++ b/src/locales/en/mystery-encounters/the-expert-pokemon-breeder-dialogue.json @@ -0,0 +1,30 @@ +{ + "intro": "It's a trainer carrying tons of Pokémon Eggs!", + "intro_dialogue": "Hey there, trainer!$It looks like some of your\npartner Pokémon are feeling a little down.$Why not have a battle with me to cheer them up?", + "title": "The Expert Pokémon Breeder", + "description": "You've been challenged to a battle where @[TOOLTIP_TITLE]{you can only use a single Pokémon}. It might be tough, but it would surely deepen the bond you have with the Pokémon you choose!\nThe breeder will also give you some @[TOOLTIP_TITLE]{Pokémon Eggs} if you win!", + "query": "Who will you battle with?", + "cleffa_1_nickname": "Ace", + "cleffa_2_nickname": "Clefablest", + "cleffa_3_nickname": "{{speciesName}} the Great", + "option": { + "1": { + "label": "{{pokemon1Name}}", + "tooltip_base": "(-) Tough Battle\n(+) Gain Friendship with {{pokemon1Name}}" + }, + "2": { + "label": "{{pokemon2Name}}", + "tooltip_base": "(-) Tough Battle\n(+) Gain Friendship with {{pokemon2Name}}" + }, + "3": { + "label": "{{pokemon3Name}}", + "tooltip_base": "(-) Tough Battle\n(+) Gain Friendship with {{pokemon3Name}}" + }, + "selected": "Let's do this!" + }, + "outro": "Look how happy your {{chosenPokemon}} is now!$Here, you can have these as well.", + "gained_eggs": "@s{item_fanfare}You received {{numEggs}}!", + "eggs_tooltip": "\n(+) Earn {{eggs}}", + "numEggs_one": "{{count}} {{rarity}} Egg", + "numEggs_other": "{{count}} {{rarity}} Eggs" +} \ No newline at end of file diff --git a/src/locales/en/mystery-encounters/trash-to-treasure-dialogue.json b/src/locales/en/mystery-encounters/trash-to-treasure-dialogue.json index ae6e63ed800..fe2cb54f5b1 100644 --- a/src/locales/en/mystery-encounters/trash-to-treasure-dialogue.json +++ b/src/locales/en/mystery-encounters/trash-to-treasure-dialogue.json @@ -6,8 +6,8 @@ "option": { "1": { "label": "Dig for Valuables", - "tooltip": "(-) Lose Healing Items in Shops\n(+) Gain Amazing Items", - "selected": "You wade through the garbage pile, becoming mired in filth.$There's no way any respectable shopkeepers\nwill sell you anything in your grimy state!$You'll just have to make do without shop healing items.$However, you found some incredible items in the garbage!" + "tooltip": "(-) Items in Shops Cost 3x\n(+) Gain Amazing Items", + "selected": "You wade through the garbage pile, becoming mired in filth.$There's no way any respectable shopkeeper would\nsell you items at the normal rate in your grimy state!$You'll have to pay extra for items now.$However, you found some incredible items in the garbage!" }, "2": { "label": "Investigate Further", diff --git a/src/locales/en/party-ui-handler.json b/src/locales/en/party-ui-handler.json index 338bdfaec80..8e6e8046c7e 100644 --- a/src/locales/en/party-ui-handler.json +++ b/src/locales/en/party-ui-handler.json @@ -13,6 +13,7 @@ "ALL": "All", "PASS_BATON": "Pass Baton", "UNPAUSE_EVOLUTION": "Unpause Evolution", + "PAUSE_EVOLUTION": "Pause Evolution", "REVIVE": "Revive", "RENAME": "Rename", "SELECT": "Select", @@ -24,6 +25,7 @@ "tooManyItems": "{{pokemonName}} has too many\nof this item!", "anyEffect": "It won't have any effect.", "unpausedEvolutions": "Evolutions have been unpaused for {{pokemonName}}.", + "pausedEvolutions": "Evolutions have been paused for {{pokemonName}}.", "unspliceConfirmation": "Do you really want to unsplice {{fusionName}}\nfrom {{pokemonName}}? {{fusionName}} will be lost.", "wasReverted": "{{fusionName}} was reverted to {{pokemonName}}.", "releaseConfirmation": "Do you really want to release {{pokemonName}}?", diff --git a/src/locales/en/pokemon-summary.json b/src/locales/en/pokemon-summary.json index 80e0cdab010..458fad0efe0 100644 --- a/src/locales/en/pokemon-summary.json +++ b/src/locales/en/pokemon-summary.json @@ -11,7 +11,7 @@ "cancel": "Cancel", "memoString": "{{natureFragment}} nature,\n{{metFragment}}", "metFragment": { - "normal": "met at Lv{{level}},\n{{biome}}.", + "normal": "met at Lv{{level}},\n{{biome}}, Wave {{wave}}.", "apparently": "apparently met at Lv{{level}},\n{{biome}}." }, "natureFragment": { diff --git a/src/locales/en/splash-messages.json b/src/locales/en/splash-messages.json index c0686e6ad75..168974525f8 100644 --- a/src/locales/en/splash-messages.json +++ b/src/locales/en/splash-messages.json @@ -1,5 +1,5 @@ { - "battlesWon": "Battles Won!", + "battlesWon": "{{count, number}} Battles Won!", "joinTheDiscord": "Join the Discord!", "infiniteLevels": "Infinite Levels!", "everythingStacks": "Everything Stacks!", @@ -32,5 +32,17 @@ "alsoTryRadicalRed": "Also Try Radical Red!", "eeveeExpo": "Eevee Expo!", "ynoproject": "YNOproject!", - "breedersInSpace": "Breeders in space!" + "breedersInSpace": "Breeders in space!", + "halloween": { + "pumpkaboosAbout": "Pumpkaboos about!", + "mayContainSpiders": "May contain spiders!", + "spookyScaryDuskulls": "Spooky, Scary Duskulls!" + }, + "xmas": { + "happyHolidays": "Happy Holidays!", + "delibirdSeason": "Delibird Season!" + }, + "newYears": { + "happyNewYear": "Happy New Year!" + } } \ No newline at end of file diff --git a/src/locales/en/trainer-classes.json b/src/locales/en/trainer-classes.json index 9e30915dee6..092bc8d895d 100644 --- a/src/locales/en/trainer-classes.json +++ b/src/locales/en/trainer-classes.json @@ -126,5 +126,8 @@ "skull_grunts": "Team Skull Grunts", "macro_grunt": "Macro Cosmos Trainer", "macro_grunt_female": "Macro Cosmos Trainer", - "macro_grunts": "Macro Cosmos Trainers" + "macro_grunts": "Macro Cosmos Trainers", + "star_grunt": "Star Grunt", + "star_grunt_female": "Star Grunt", + "star_grunts": "Star Grunts" } diff --git a/src/locales/en/trainer-names.json b/src/locales/en/trainer-names.json index 467ed03e044..1d51b48d43f 100644 --- a/src/locales/en/trainer-names.json +++ b/src/locales/en/trainer-names.json @@ -141,6 +141,11 @@ "faba": "Faba", "plumeria": "Plumeria", "oleana": "Oleana", + "giacomo": "Giacomo", + "mela": "Mela", + "atticus": "Atticus", + "ortega": "Ortega", + "eri": "Eri", "maxie": "Maxie", "archie": "Archie", @@ -150,6 +155,7 @@ "lusamine": "Lusamine", "guzma": "Guzma", "rose": "Rose", + "cassiopeia": "Cassiopeia", "blue_red_double": "Blue & Red", "red_blue_double": "Red & Blue", @@ -172,5 +178,6 @@ "vivi": "Vivi", "vicky": "Vicky", "vito": "Vito", - "bug_type_superfan": "Bug-Type Superfan" + "bug_type_superfan": "Bug-Type Superfan", + "expert_pokemon_breeder": "Expert Pokémon Breeder" } diff --git a/src/locales/en/trainer-titles.json b/src/locales/en/trainer-titles.json index 7ef715d115f..ae19fc30790 100644 --- a/src/locales/en/trainer-titles.json +++ b/src/locales/en/trainer-titles.json @@ -19,6 +19,7 @@ "aether_boss": "Aether President", "skull_boss": "Team Skull Boss", "macro_boss": "Macro Cosmos President", + "star_boss": "Team Star Leader", "rocket_admin": "Team Rocket Admin", "rocket_admin_female": "Team Rocket Admin", @@ -35,6 +36,7 @@ "aether_admin": "Aether Foundation Admin", "skull_admin": "Team Skull Admin", "macro_admin": "Macro Cosmos", + "star_admin": "Team Star Squad Boss", "the_winstrates": "The Winstrates'" } diff --git a/src/locales/es/battle.json b/src/locales/es/battle.json index c79315f297b..c5fbf25f3ae 100644 --- a/src/locales/es/battle.json +++ b/src/locales/es/battle.json @@ -85,5 +85,7 @@ "statSeverelyFell_one": "¡El {{stats}} de {{pokemonNameWithAffix}} ha bajado muchísimo!", "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!" + "statWontGoAnyLower_other": "¡{{stats}} de\n{{pokemonNameWithAffix}} no pueden bajar más!", + "battlerTagsHealBlock": "¡{{pokemonNameWithAffix}} no puede restaurar sus PS!", + "battlerTagsHealBlockOnRemove": "¡{{pokemonNameWithAffix}} ya puede recuperar PS!" } diff --git a/src/locales/es/menu.json b/src/locales/es/menu.json index ef1ae93dd82..a35025819fa 100644 --- a/src/locales/es/menu.json +++ b/src/locales/es/menu.json @@ -51,7 +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,\nel juego se reconectará automáticamente.", "noSaves": "No tienes ninguna partida guardada registrada!", "tooManySaves": "¡Tienes demasiadas partidas guardadas registradas!" } diff --git a/src/locales/es/pokemon-summary.json b/src/locales/es/pokemon-summary.json index e47335c8394..fe33c9418cc 100644 --- a/src/locales/es/pokemon-summary.json +++ b/src/locales/es/pokemon-summary.json @@ -11,7 +11,7 @@ "cancel": "Salir", "memoString": "Naturaleza {{natureFragment}},\n{{metFragment}}", "metFragment": { - "normal": "encontrado al Nv. {{level}},\n{{biome}}.", + "normal": "encontrado al Nv. {{level}},\n{{biome}}, Oleada {{wave}}.", "apparently": "aparentemente encontrado al Nv. {{level}},\n{{biome}}." } -} \ No newline at end of file +} diff --git a/src/locales/es/splash-messages.json b/src/locales/es/splash-messages.json index b1d4820b06e..da31d394c0f 100644 --- a/src/locales/es/splash-messages.json +++ b/src/locales/es/splash-messages.json @@ -1,5 +1,5 @@ { - "battlesWon": "¡Batallas ganadas!", + "battlesWon": "¡{{count, number}} Batallas ganadas!", "joinTheDiscord": "¡Únete al Discord!", "infiniteLevels": "¡Niveles infinitos!", "everythingStacks": "¡Todo se acumula!", diff --git a/src/locales/fr/battle.json b/src/locales/fr/battle.json index 7b78c963187..85f206937e7 100644 --- a/src/locales/fr/battle.json +++ b/src/locales/fr/battle.json @@ -44,7 +44,10 @@ "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 !", + "canOnlyUseMove": "{{pokemonName}} ne peut utiliser\nque la capacité {{moveName}} !", + "moveCannotBeSelected": "La capacité {{moveName}}\nne peut pas être choisie !", "disableInterruptedMove": "Il y a une entrave sur la capacité {{moveName}}\nde{{pokemonNameWithAffix}} !", + "throatChopInterruptedMove": "Exécu-Son empêche {{pokemonName}}\nd’utiliser la capacité !", "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 !", @@ -96,5 +99,7 @@ "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}} !", - "eggSkipPrompt": "Aller directement au résumé des Œufs éclos ?" + "eggSkipPrompt": "Aller directement au résumé des Œufs éclos ?", + "battlerTagsHealBlock": "{{pokemonNameWithAffix}} ne peut pas guérir !", + "battlerTagsHealBlockOnRemove": "Le blocage de soins qui affectait\n{{pokemonNameWithAffix}} s’est dissipé !" } diff --git a/src/locales/fr/menu.json b/src/locales/fr/menu.json index 277b0f5fd04..35cd06606a7 100644 --- a/src/locales/fr/menu.json +++ b/src/locales/fr/menu.json @@ -46,7 +46,7 @@ "yes": "Oui", "no": "Non", "disclaimer": "AVERTISSEMENT", - "disclaimerDescription": "Ce jeu n’est pas un produit fini et peut contenir des problèmes de jouabilité, dont de possibles pertes de sauvegardes,\ndes modifications sans avertissement et pourrait ou non encore être mis à jour ou terminé.", + "disclaimerDescription": "Ce jeu n’est pas un produit fini.\nIl peut contenir des problèmes de jouabilité, dont de possibles pertes de sauvegardes,\ndes modifications sans avertissement et pourrait à tout moment cesser d’être mis à jour.", "choosePokemon": "Sélectionnez un Pokémon.", "renamePokemon": "Renommer le Pokémon", "rename": "Renommer", diff --git a/src/locales/fr/move-trigger.json b/src/locales/fr/move-trigger.json index 3704bc90718..6f9d9d4dd63 100644 --- a/src/locales/fr/move-trigger.json +++ b/src/locales/fr/move-trigger.json @@ -7,6 +7,7 @@ "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 !", + "shiftedStats": "{{pokemonName}} échange {{statToSwitch}} et {{statToSwitchWith}} !", "goingAllOutForAttack": "{{pokemonName}} a pris\ncette capacité au sérieux !", "regainedHealth": "{{pokemonName}}\nrécupère des PV !", "keptGoingAndCrashed": "{{pokemonName}}\ns’écrase au sol !", @@ -67,5 +68,7 @@ "swapArenaTags": "Les effets affectant chaque côté du terrain\nont été échangés par {{pokemonName}} !", "exposedMove": "{{targetPokemonName}} est identifié\npar {{pokemonName}} !", "safeguard": "{{targetName}} est protégé\npar la capacité Rune Protect !", + "substituteOnOverlap": "{{pokemonName}} a déjà\nun clone !", + "substituteNotEnoughHp": "Mais il est trop faible\npour créer un clone !", "afterYou": "{{pokemonName}} accepte\navec joie !" } diff --git a/src/locales/fr/move.json b/src/locales/fr/move.json index a48e17b3fd9..da42f188a80 100644 --- a/src/locales/fr/move.json +++ b/src/locales/fr/move.json @@ -364,8 +364,8 @@ "effect": "Le lanceur creuse au premier tour et frappe au second." }, "toxic": { - "name": "Fil Toxique", - "effect": "Tisse un fil imprégné de venin. Empoisonne la cible et baisse sa Vitesse." + "name": "Toxik", + "effect": "Le lanceur empoisonne gravement la cible. Les dégâts dus au poison augmentent à chaque tour." }, "confusion": { "name": "Choc Mental", @@ -3121,11 +3121,11 @@ }, "behemothBlade": { "name": "Gladius Maximus", - "effect": "Le lanceur se transforme en une immense épée et pourfend sa cible. Cette capacité inflige le double de dégâts aux Pokémon Dynamax." + "effect": "Le lanceur se transforme en une immense épée et pourfend sa cible." }, "behemothBash": { "name": "Aegis Maxima", - "effect": "Le lanceur se transforme en un immense bouclier et charge sa cible. Cette capacité inflige le double de dégâts aux Pokémon Dynamax." + "effect": "Le lanceur se transforme en un immense bouclier et charge sa cible." }, "auraWheel": { "name": "Roue Libre", diff --git a/src/locales/fr/party-ui-handler.json b/src/locales/fr/party-ui-handler.json index 6adba2c8309..4eef55da790 100644 --- a/src/locales/fr/party-ui-handler.json +++ b/src/locales/fr/party-ui-handler.json @@ -13,6 +13,7 @@ "ALL": "Tout", "PASS_BATON": "Relais", "UNPAUSE_EVOLUTION": "Réactiver Évolution", + "PAUSE_EVOLUTION": "Empêcher Évolution", "REVIVE": "Ranimer", "RENAME": "Renommer", "choosePokemon": "Sélectionnez un Pokémon.", @@ -23,6 +24,7 @@ "tooManyItems": "{{pokemonName}} porte trop\nd’exemplaires de cet objet !", "anyEffect": "Cela n’aura aucun effet.", "unpausedEvolutions": "{{pokemonName}} peut de nouveau évoluer.", + "pausedEvolutions": "{{pokemonName}} ne peut plus évoluer.", "unspliceConfirmation": "Voulez-vous vraiment séparer {{fusionName}}\nde {{pokemonName}} ? {{fusionName}} sera perdu.", "wasReverted": "{{fusionName}} est redevenu {{pokemonName}}.", "releaseConfirmation": "Voulez-vous relâcher {{pokemonName}} ?", @@ -44,4 +46,4 @@ "untilWeMeetAgain": "À la prochaine, {{pokemonName}} !", "sayonara": "Sayonara, {{pokemonName}} !", "smellYaLater": "À la revoyure, {{pokemonName}} !" -} \ No newline at end of file +} diff --git a/src/locales/fr/pokemon-summary.json b/src/locales/fr/pokemon-summary.json index 01e712c8468..a038b3a51f9 100644 --- a/src/locales/fr/pokemon-summary.json +++ b/src/locales/fr/pokemon-summary.json @@ -11,7 +11,7 @@ "cancel": "Annuler", "memoString": "{{natureFragment}} de nature,\n{{metFragment}}", "metFragment": { - "normal": "rencontré au N.{{level}},\n{{biome}}.", + "normal": "rencontré au N.{{level}},\n{{biome}}, Vague {{wave}}.", "apparently": "apparemment rencontré au N.{{level}},\n{{biome}}." }, "natureFragment": { diff --git a/src/locales/fr/settings.json b/src/locales/fr/settings.json index c752b336b6e..e310f5d5733 100644 --- a/src/locales/fr/settings.json +++ b/src/locales/fr/settings.json @@ -11,6 +11,10 @@ "expGainsSpeed": "Vit. barre d’Exp", "expPartyDisplay": "Afficher Exp équipe", "skipSeenDialogues": "Passer dialogues connus", + "eggSkip": "Animation d’éclosion", + "never": "Jamais", + "always": "Toujours", + "ask": "Demander", "battleStyle": "Style de combat", "enableRetries": "Activer les réessais", "hideIvs": "Masquer Scanner d’IV", diff --git a/src/locales/fr/splash-messages.json b/src/locales/fr/splash-messages.json index 9dd3e86fb32..2ac85680e58 100644 --- a/src/locales/fr/splash-messages.json +++ b/src/locales/fr/splash-messages.json @@ -1,5 +1,5 @@ { - "battlesWon": "combats gagnés !", + "battlesWon": "{{count, number}} combats gagnés !", "joinTheDiscord": "Rejoins le Discord !", "infiniteLevels": "Niveaux infinis !", "everythingStacks": "Tout se cumule !", diff --git a/src/locales/fr/trainer-names.json b/src/locales/fr/trainer-names.json index 5864976cc34..beec7e0c313 100644 --- a/src/locales/fr/trainer-names.json +++ b/src/locales/fr/trainer-names.json @@ -94,7 +94,7 @@ "caitlin": "Percila", "malva": "Malva", "siebold": "Narcisse", - "wikstrom": "Tileo", + "wikstrom": "Thyméo", "drasna": "Dracéna", "hala": "Pectorius", "molayne": "Molène", diff --git a/src/locales/fr/tutorial.json b/src/locales/fr/tutorial.json index 7936987457f..3236bdafea2 100644 --- a/src/locales/fr/tutorial.json +++ b/src/locales/fr/tutorial.json @@ -6,5 +6,5 @@ "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\nles combats tant que le Pokémon n’est pas rappelé.\n$Vos Pokémon sont rappelés avant un combat de\nDresseur et avant d’entrer dans un nouveau biome.\n$Vous pouvez voir en combat les changements de stats\nd’un Pokémon en maintenant C ou Maj.\n$Vous pouvez également voir les capacités de l’adversaire\nen maintenant V.\n$Seules les capacités que le Pokémon a utilisées dans\nce 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\nPokémon en utilisant l’option de transfert.\n$L’option de transfert apparait en bas à droite dès\nqu’un Pokémon de l’équipe porte un objet.\n$Vous pouvez acheter des consommables avec de\nl’argent. Plus vous progressez, plus le choix sera large.\n$Choisir un des objets gratuits déclenchera le prochain\ncombat, 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 !" + "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.\n$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/it/pokemon-summary.json b/src/locales/it/pokemon-summary.json index f6c9290f783..81cd9a278b8 100644 --- a/src/locales/it/pokemon-summary.json +++ b/src/locales/it/pokemon-summary.json @@ -11,7 +11,7 @@ "cancel": "Annulla", "memoString": "Natura {{natureFragment}},\n{{metFragment}}", "metFragment": { - "normal": "incontrato al Lv.{{level}},\n{{biome}}.", + "normal": "incontrato al Lv.{{level}},\n{{biome}}, Onda {{wave}}.", "apparently": "apparentemente incontrato al Lv.{{level}},\n{{biome}}." } -} \ No newline at end of file +} diff --git a/src/locales/it/splash-messages.json b/src/locales/it/splash-messages.json index 55018d0ada0..d4b411241b6 100644 --- a/src/locales/it/splash-messages.json +++ b/src/locales/it/splash-messages.json @@ -1,5 +1,5 @@ { - "battlesWon": "Battaglie Vinte!", + "battlesWon": "{{count, number}} Battaglie Vinte!", "joinTheDiscord": "Entra nel Discord!", "infiniteLevels": "Livelli Infiniti!", "everythingStacks": "Tutto si impila!", diff --git a/src/locales/ja/dialogue-final-boss.json b/src/locales/ja/dialogue-final-boss.json index f20d0f013d1..2378a09f6d6 100644 --- a/src/locales/ja/dialogue-final-boss.json +++ b/src/locales/ja/dialogue-final-boss.json @@ -1,10 +1,10 @@ { - "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" + "encounter": "又しても 時が満ちた 様 である。\nこちらへ 至る 理由は 存知するな。\n$汝は この場所へ 引かれた……\nこの何度となく 至った場所。\n$けれども 数えられぬとも 限らぬ……\n正確に宣ふ(のたまう)と 現在の 循環は {{cycleCount}}回目 である。\n$各循環に 心… 意識… 両方も 元の有様に 戻る。\nなれども 故吾の 残影は 汝の 中に 存する。\n$未だに 成功せぬ ままでも\n異なる 存在を 感ずる。\n$御座在る者は 一人。\nなれども 感ずるは… もう 他人。\n$到頭 汝から いかめしい 挑戦は 我が目にかかるか?\n千歳 万歳 相まってた挑戦……\n$始もう。", + "encounter_female": "又しても 時が満ちた 様 である。\nこちらへ 至る 理由は 存知するな。\n$汝は この場所へ 引かれた……\nこの何度となく 至った場所。\n$けれども 数えられぬとも 限らぬ……\n正確に宣ふ(のたまう)と 現在の 循環は {{cycleCount}}回目 である。\n$各循環に 心… 意識… 両方も 元の有様に 戻る。\nなれども 故吾の 残影は 汝の 中に 存する。\n$未だに 成功せぬ ままでも\n異なる 存在を 感ずる。\n$御座在る者は 一人。\nなれども 感ずるは… もう 他人。\n$到頭 汝から いかめしい 挑戦は 我が目にかかるか?\n千歳 万歳 相まってた挑戦……\n$始もう。", + "firstStageWin": "成る程。 感じた 存在は 正身(むざね) であった。\n自分を括る 必要 有らぬ 様である。\n$失望させぬが良い。", + "secondStageWin": "……お見事でございます。", + "key_ordinal_one": "", + "key_ordinal_two": "", + "key_ordinal_few": "", + "key_ordinal_other": "" } diff --git a/src/locales/ja/dialogue-misc.json b/src/locales/ja/dialogue-misc.json index 2f333b5f383..d0527564613 100644 --- a/src/locales/ja/dialogue-misc.json +++ b/src/locales/ja/dialogue-misc.json @@ -1,6 +1,6 @@ { - "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" + "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$さあ、帰ろう。\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ずっと 残っているからね……\n$@c{smile_eclosed}じゃあ、こんなトコは もう飽きた だろう?\n帰ろうよ。\n$@c{serious_smile_fists}ふるさとに 着いたら、 また バトルしよう?\nやる気 あればね!", + "ending_endless": "現在のエンドまで やって来て おめでとう!\n更に多くの コンテンツは  近日公開予定です。", + "ending_name": "開発者" } diff --git a/src/locales/ja/dialogue.json b/src/locales/ja/dialogue.json index 6130ade1cb4..24cd19b6ffc 100644 --- a/src/locales/ja/dialogue.json +++ b/src/locales/ja/dialogue.json @@ -40,839 +40,839 @@ }, "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." + "1": "勝負しない?", + "2": "新人 トレーナーだろう? 勝負しよう!", + "2_female": "新しい トレーナーだろう? 勝負しよう!", + "3": "初めて 見る 顔ね! 勝負しない?", + "4": "ドッキドキで ワックワクな\nポケモン勝負に しよう!", + "5": "手取り 足取り 教えて あげる!\nホントの ポケモンの 使いかた!", + "6": "マジの 勝負 マジ始めっからー\n準備とか 覚悟とか オーケー?", + "6_female": "マジの 勝負 マジ始めっからー\n準備とか 覚悟とか オーケー?", + "7": "青春も バトルも 一度きり!!\nあなたを 思い出に させて!!", + "8": "あんた ちょっとは 手加減してよ…\nあたしは 本気で いくけど!", + "9": "学校 タルいし やることないし\nヒマだけあるから バトルしてんの。" }, "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…" + "1": "すごい! 覚えること いっぱいね。", + "2": "新人に こんなに ぶっ壊されて なんて……", + "2_female": "新人に こんなに ぶっ壊されて なんて……", + "3": "またいつか 勝負を できると いいね!", + "4": "キミに ドッキ ドキドキ\n私は ボッロ ボロボロ……", + "5": "逆に 教えられちゃった!\nキミって 結構 スゴいんだ!", + "6": "マジ負けてる マジ凹む マジ辛い\nでも アンタは マジで イケてるかも。", + "6_female": "マジ負けてる マジ凹む マジ辛い\nでも アンタは マジで イケてるかも。", + "7": "こんな 思い出 いらないし\n記憶から 消しちゃおっと…", + "8": "ぷんぷん! 手加減してよね!!\n……本気の あんたも いいけどさ。", + "8_female": "ぷんぷん! 手加減してよね!!\n……本気の あんたも いいけどさ。", + "9": "バトルも そろそろ あきたわ 実際…\n何か 新しいこと とか ない?" } }, "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." + "1": "素直なヤツに ワガママなヤツ……\nポケモンにも 個性が あるんだよな。", + "2": "育ちも 素行も 悪い オレが\n育てた割に いい ポケモン だぜ!", + "3": "きみは ポケモンを しつけてるか?\n甘やかすだけじゃ ダメなんだぜ!" }, "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." + "1": "ポケモンの 持っている 個性を\n活かして 育てることが 大切だ。", + "2": "極悪非道の オレと 違って\nこいつらは いいポケモン だよ!", + "3": "褒めてばっかりだと ポケモンも\n人も ダメに なっちゃうよな…" }, "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." + "1": "負けたからって むやみに ポケモンを\n怒ったりしては ダメなんだぞ。", + "2": "な? なかなか いいポケモン だろ?\n育てるってのが 向いてるんだな オレ。", + "3": "可愛くて 大好きなポケモンでも\n悪いこと したら 叱らないとな!" } }, "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." + "1": "ポケモンは 裏切らないからね、\nささげた 愛情は 返ってくる。", + "2": "いいポケモンを 育てるコツを\nキミに 教えてあげようかな?", + "3": "秘密の 方法で 育てあげた\nとっても スペシャルな ポケモンだ。" }, "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." + "1": "うーん…… こんな はずでは……\nエキスの 配合を 間違えたか?", + "2": "ワイの 育てた ポケモンが そんな……\nキミは ポケモンに なに あげとるんや?", + "3": "負けたところで ただのヒマつぶし\n心に ダメージ ございません…" }, "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." + "1": "私の 愛情が ポケモンに\n届いてる 証拠 だわね。", + "2": "いいポケモンを 育てるコツは\nいいポケモンを 捕まえることだ。", + "3": "育てかた 次第で ポケモンは\n強くも 弱くも なるのだ。" } }, "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!" + "1": "釣り糸が 絡まって イライラするーっ!\nええいっ 勝負だっ!", + "2": "海釣りと 川釣り キミは どっちが 好き?", + "3": "よーし! ポケモン好きと 釣り好きの 対決だ!" }, "victory": { - "1": "Just forget about it.", - "2": "Next time, I'll be reelin' in the triumph!", - "3": "Guess I underestimated the currents this time." + "1": "負けたっ! 余計に イライラするぞーっ!", + "2": "まるで 海釣りの ように 豪快に 負けたぞーっ!", + "3": "わあ! 糸が 絡まった! まさに 後の祭り…" } }, "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!" + "1": "でっかいの 釣れたっ でっかいの!", + "2": "うちは 常に 大物ねらい!\n雑魚だったら 引っ込んで おくれよ!", + "3": "釣りたて ピチピチの\nポケモンで 勝負してあげよう!!" }, "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!" + "1": "あれー 大きさで 負けたかな…", + "2": "なかなかの 引きな キミ!\nナイス ファイトだった!", + "3": "釣りあげられたのは……\nこの 私だったか!" } }, "swimmer": { "encounter": { - "1": "Time to dive in!", - "2": "Let's ride the waves of victory!", - "3": "Ready to make a splash!" + "1": "全力で 飛び込むよ!", + "2": "勝利の 大波に 乗っていくよ!", + "3": "完全に 浸しちゃうよ!" }, "victory": { - "1": "Drenched in defeat!", - "2": "A wave of defeat!", - "3": "Back to shore, I guess." + "1": "失敗で ぐしゃぐしゃ!", + "2": "敗北の 小波だった……", + "3": "そろそろ 漂って帰ろう……" } }, "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?" + "1": "旅する バックパッカー\n同じく 旅のトレーナーに 会う…", + "2": "私と 旅しているのは 人気の\nガイドブックで お薦めの ポケモン です!", + "3": "旅をしても していなくても\n私は 発見したいのです!", + "4": "旅を 続け 幾年月、\n知らない 国は ないくらい。" }, "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?" + "1": "きみと 出会うため\n旅していたのかも? なんて", + "2": "ガイドブックに 勝ち方は\n載っていないのよね…", + "3": "あっ! あなたの いいところ\n発見しちゃったかも!", + "4": "本当は ただ 「お帰り」 って\n言ってくれる ダーリンが 欲しい…" } }, "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?" + "1": "自信満々って 感じ ですな…", + "1_female": "自信満々って 感じ だね…", + "2": "君の ポケモン…… 私に 見せてごらんよ……", + "3": "エリートトレーナーなんて やってるから\n強い 人って 思われるんだ。", + "4": "エリートトレーナーたる 資格 って\nあなたは ご存じ かしら?" }, "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!" + "1": "うん…… いいポケモンだね……", + "2": "勝負の 天才 いわれる\n私も 負けるこつの あっけんか!", + "3": "やはり あなたこそ 主人公だ!", + "3_female": "やはり あなたこそ 主人公だ!", + "4": "戦う 姿が なじんでるのね\nエリートトレーナーに なれるよ キミ!", + "4_female": "戦う 姿が なじんでるのね\nエリートトレーナーに なれるよ キミ!" }, "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." + "1": "私はな ポケモン勝負に\n魂 かけてんだよォ!", + "2": "すべて 予想の はんちゅう……\n何の 驚きもないね……", + "3": "弱くて 抱きしめたら 折れそうな\n人に なるはずだったのにな。", + "4": "強いこと 負けないことは 当たり前……\nいかに 優雅に 勝つかが 重要 なの。" } }, "parasol_lady": { "encounter": { - "1": "Time to grace the battlefield with elegance and poise!" + "1": "傘が ないと パラソルおねえさんって\n言えないからさ 仕方なく 差してんの。" }, "victory": { - "1": "My elegance remains unbroken!" + "1": "傘がっ 傘が 折れちゃったっ!\nただの おねえさんに なっちゃう!" } }, "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!" + "1": "準備してね! あたしたちが 力を 組み合うと Wトラブルでちゅ!", + "2": "心は二つ、 作戦は一つ!\nツインパワーを 見せるなだ!", + "3": "二倍の 力で 熱いバトルを しましょうよ!", + "3_female": "二倍の 力で 熱いバトルを しましょうよ!" }, "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!" + "1": "バトル 負けたけど、 絆 壊れないんでちゅ!", + "2": "何が起こっても ツイン魂が あせてはしないよ!", + "3": "二倍、 いや、 四倍の 力で 戻って来るよ!" }, "defeat": { - "1": "Twin power reigns supreme!", - "2": "Two hearts, one triumph!", - "3": "Double the smiles, double the victory dance!" + "1": "ツインパワーに 誰も 勝てない!", + "2": "心は二つ、 勝利は一つ!", + "3": "笑顔も 勝利の舞も 二倍!" } }, "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!" + "1": "両足 砕けようとも 漕ぐ、\nこれ サイクリング 極意なり…", + "2": "やー 悪いねー ついつい パートナー\n連れてきちゃったわ ラブラブだからさ!", + "3": "もうダメェ! 物足りないのォ!\n自転車じゃ 物足りないよォ!" }, "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." + "1": "我が サイクリングは 無限\nいずれまた 貴様に 挑む…", + "2": "冷たいボディに ふんわりサドル\nコイツは 最高の パートナーだぜ…", + "3": "自転車じゃ 満足できないのォ!\n暴走族に 入れてもらうのォ!" } }, "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?" + "1": "宇宙一の キックを 持つ\nわしに 挑むとは 褒めてやろう!", + "2": "んー そうだね、 ズタズタ されたい?\nそれとも ボロボロが 好み かな?", + "2_female": "んー そうだね、 ズタズタ されたい?\nそれとも ボロボロが 好み かな?" }, "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." + "1": "あ 戦うのは ポケモンだっけ\nわしの キックは 関係ないわ。", + "2": "んー…… どうせ 負けるなら\nボクは メチャクチャに されたかったんだけどね。" } }, "battle_girl": { "encounter": { - "1": "You don't have to try to impress me. You can lose against me." + "1": "いいのよ、 見栄 張らなくても\n私には 負けてもいいのよ。" }, "victory": { - "1": "It's hard to say good-bye, but we are running out of time…" + "1": "さようならするのは つらいけれど\nもう 時間が ないわね……" } }, "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…" + "1": "中年太りの このボディ\n山の ごとき 貫禄で ゴワス!", + "2": "親 から もらった メタボな ボディ……\n生ける 山脈とは オレの こと……" }, "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…" + "1": "皮下脂肪 だったら\n負けないのにで ゴワス!", + "2": "足りぬ…… 足りぬぞ……\n悪玉コレステロールが 足りぬぞ……" } }, "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." + "1": "自然に 囲まれ 暮らしているとな\n大抵の ことが どうでも良くなる……", + "2": "自然から 離れ 暮らしているとな\n時々 急に 苦しくなるのさ。" }, "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." + "1": "ぼくが 負けたって 大自然に\nとっては どうだって いいことさ……", + "2": "都会の 息苦しさに 比べれば\nこんなこと へ でも ないさ……" }, "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…" + "1": "ぼくが 勝ったことも 大自然に\n比べたら どうだっていいのさ……", + "2": "僕の 不安に 比べれば\n大したこと ないさ きっと……" } }, "scientist": { "encounter": { - "1": "My research will lead this world to peace and joy." + "1": "ボクノ 研究ガ 世界ヲ\n平和ト 幸セニ 導クノデス。" }, "victory": { - "1": "I am a genius… I am not supposed to lose against someone like you…" + "1": "ボクハ 天才 ナンダ……\nコンナ 相手ニ 負ケルハズ……" } }, "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." + "1": "……グフフ 計算と 分析には\n自信が ありますからね 僕!", + "2": "ここで いっぱい 経験 積んで\nいつかは ジムリーダーを 目指すんだ!" }, "victory": { - "1": "Ohhhh… Calculation and analysis are perhaps no match for chance…", - "2": "Even difficult, trying experiences have their purpose, I suppose." + "1": "ムググ…… 計算も 分析も\n偶然には 敵わないか……", + "2": "辛く 悲しい 経験も\nいつかは 役に 立つはずです……" } }, "artist": { "encounter": { - "1": "I used to be popular, but now I am all washed up." + "1": "かつては おれも 売れっ子だったが\n今では だれにも 相手にされん……" }, "victory": { - "1": "As times change, values also change. I realized that too late." + "1": "時代が 変われば 価値も 移ろう\nそれに 気がつくのが 遅かったのだ……" } }, "guitarist": { "encounter": { - "1": "Get ready to feel the rhythm of defeat as I strum my way to victory!" + "1": "ロックン ロールッ!\n戦いは リズムッ!" }, "victory": { - "1": "Silenced for now, but my melody of resilience will play on." + "1": "ぎゅぎゅーん……\n悲しい メロディ だぜ……" } }, "worker": { "encounter": { - "1": "It bothers me that people always misunderstand me. I'm a lot more pure than everyone thinks." + "1": "作業員 ってのは 命がけ\nだけど やりがいは 十分よ!" }, "victory": { - "1": "I really don't want my skin to burn, so I want to stay in the shade while I work." + "1": "100年 残る ビルや 橋を\n建てるなんて ロマン だろう?" } }, "worker_female": { "encounter": { - "1": "It bothers me that people always misunderstand me.\n$I'm a lot more pure than everyone thinks." + "1": "よく 勘違い されて 困るんだけど\nみんなが 思うよりも ナイーブ なのね。" }, "victory": { - "1": "I really don't want my skin to burn, so I want to stay in the shade while I work." + "1": "本当は お肌 焼きたくない から\n日陰 でしか 作業 したくないのね……" }, "defeat": { - "1": "My body and mind aren't necessarily always in sync." + "1": "心と体は 必ずしも\n一致するわけでは ないのね……" } }, "worker_double": { "encounter": { - "1": "I'll show you we can break you. We've been training in the field!" + "1": "現場で 鍛えた ワシらの 力\n今 見せちゃるけえのぉ!" }, "victory": { - "1": "How strange… How could this be… I shouldn't have been outmuscled." + "1": "おかしいのぉ なんでかのぉ\n力で 負けるはず ないんじゃが……" } }, "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." + "1": "普段 クラシックしか 聴きませんが 負けたら ラップを してみます!", + "2": "涙の 数だけ 強くなれるの だって\nわたし 女の子 だもん。" }, "victory": { - "1": "Is this the dawning of the age of Aquarius?", - "2": "Now I can get even stronger. I grow with every grudge." + "1": "アタイの 想いは アイツに 重い!\n呪いと 願いは 大体 同じッ!", + "2": "これで わたしは また 強くなれる\n恨んだ数だけ 成長 するの。" }, "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." + "1": "ラッパーって ラップ現象を\n起こすのが 仕事なんですよね?", + "2": "辛いことや 悲しいことを バネにせず\nそのまま 恨みの 力に 変えるんだ。", + "2_female": "辛いことや 悲しいことを バネにせず\nそのまま 恨みの 力に 変えるんだ。" } }, "psychic": { "encounter": { - "1": "Hi! Focus!" + "1": "ハイッ!! 集中ッ!!" }, "victory": { - "1": "Eeeeek!" + "1": "フギャンッ!!" } }, "officer": { "encounter": { - "1": "Brace yourself, because justice is about to be served!", - "2": "Ready to uphold the law and serve justice on the battlefield!" + "1": "ボクは お巡り なんだよ!!\nつまり ボクは ジャスティスだよ!", + "2": "さぼってないのでー ありますっ!\nパトロールなのでー ありますっ!" }, "victory": { - "1": "The weight of justice feels heavier than ever…", - "2": "The shadows of defeat linger in the precinct." + "1": "ボクはっ ボクは お巡りだぞぉ!!\nジャスティス なのにーっ!!", + "2": "キミ 要注意でー ありますっ!\nマークしちゃうでー ありますっ!" } }, "beauty": { "encounter": { - "1": "My last ever battle… That's the way I'd like us to view this match…" + "1": "これが 最後の 勝負……\nそう 思って 相手するわね。" }, "victory": { - "1": "It's been fun… Let's have another last battle again someday…" + "1": "楽しかった……\nまた 最後の 勝負を したいものね。" } }, "baker": { "encounter": { - "1": "Hope you're ready to taste defeat!", - "1_female": "Hope you're ready to taste defeat!" + "1": "きみは もうすぐ\n敗北を 味わっちゃうわ!", + "1_female": "君は もうすぐ\n敗北を 味わっちゃうわ!" }, "victory": { - "1": "I'll bake a comeback." + "1": "諦める べーき かもね……" } }, "biker": { "encounter": { - "1": "Time to rev up and leave you in the dust!" + "1": "おんしゃー 覚悟は できとるんか?\n暴走族 なめたら いかんぜよ!" }, "victory": { - "1": "I'll tune up for the next race." + "1": "ボクらぁー ほんまは 真面目です!\nちっくとも 悪いことは せんです!!" } }, "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!" + "1": "やけたとうで 火を 吹く 練習をした!\n良い子は 真似 すんなよ!", + "2": "俺たち 火吹きやろうは 誰よりも 火の こわさを 知ってるのさ!", + "3": "よってらっしゃい みてらっしゃい!" }, "victory": { - "1": "I burned down to ashes...", - "2": "Yow! That's hot!", - "3": "Ow! I scorched the tip of my nose!" + "1": "おっと 誤解 しないでくれ!\nあのとうが 燃えたのは 俺のせいじゃ ないんだ!", + "2": "アツい 勝負を ありがとう!", + "3": "あちちち 鼻の 先っぽ 焦げちゃった!" } }, "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?" + "1": "うっしゃー! 負けたら 海に 落とすぞー!", + "2": "そら 来い! 船乗り 魂に かけて 勝つ!", + "3": "おい あんた! 乗ってて 船酔い しないか?", + "3_female": "おい あんた! 乗ってて 船酔い しないか?" }, "victory": { - "1": "Argh! Beaten by a kid!", - "2": "Your spirit sank me!", - "3": "I think it's me that's seasick..." + "1": "くー やられた!", + "2": "船乗り 魂も お前には 負けた!", + "3": "船酔いしてるのが 俺か……" } }, "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." + "1": "先に進む 前に 我々 ロケット団と 戦って もらおうか?", + "2": "あなたも 相当の腕だと 報告が 来ています。 確認しましょう。", + "3": "私は ロケット団幹部の アポロ。\n私達の 敵に 手加減 しませんよ!" }, "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..." + "1": "……なんという 失態!", + "2": "今の腕で やはり 私では 無理 でしたか……", + "3": "さ サカキ様 お許しください\n私とも あろう ものが……" } }, "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!" + "1": "そこまでよーーーっ!!\n$あなたみたいな ヤツを いつまでも のさばらせて おいたら\nロケット団の プライドは キズついて キズついて キズだらけに なっちゃうのよー!", + "2": "私たちの している ことが\n正しいかどうか なんて どうでもいい…\n$私は サカキ様を 信じて ただ 付いてきた のよ!", + "3": "ここは 通さないよ。\nだって 私が 勝つんだから!" }, "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…" + "1": "あら 強いのね 残念ね\nあなたなら ロケット団に くれば 幹部に だって なれるかもよ。", + "1_female": "あら 強いのね 残念ね\nあなたなら ロケット団に くれば 幹部に だって なれるかもよ。", + "2": "ま まけたわ……", + "3": "くききききーっ! 全力で 戦ったのに……\nこれでも 勝てない なんて!" } }, "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!" + "1": "まちな! ロケット団の 砦と言われた この私!\nこれ以上 先には 行かせません!", + "2": "なんですか? 私は ロケット団で もっとも 冷酷と 呼ばれた 男……\n$私達の 仕事の ジャマなど させはしませんよ!", + "3": "私は ロケット団幹部の ランス。\nこれ以上 仕事の ジャマは させませんよ!" }, "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!" + "1": "砦が 崩れました……", + "2": "私に 勝った ところで\n所詮は ロケット団の 怒りを 強めた だけですよ……", + "3": "くっ! なかなか やりますね。\nしかし、 次からは そうは 行きません!" } }, "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!" + "1": "ぐっふっふっ よくきたな…\nおや? 私が 誰か 分からんかね?\n$サカキだよ サカキ様 だよ!\nぐわぁーっはっはーっ!\n$……あれ? 全然 似てない? サカキ様に 見えない?\nくっそー 一生懸命 練習したのに!", + "2": "私は ロケット団幹部の ラムダ。\n計画を 邪魔するのは 許さない!", + "3": "侵入者には  ロケット団幹部の ラムダが 対処するぞ!" }, "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…" + "1": "わ 分かった…… 局長の 居場所 教える……", + "2": "ぐうう…… まったく 歯が立たない……\nサカキ様 お許し ください……", + "3": "いかん 負けて 落ち込んでる 場合じゃない\n仲間に 知らせなくては……" } }, "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!" + "1": "ウヒョヒョ! お前 ここまで 来たのか! だけど 遅かったぜ!", + "2": "ウヒョヒョ…… もう ここまで 来たのか!\n思っていたより やるな! だが ここまでだ!\n$俺は これまでの 下っ端ども とは 一味違う!\n時間稼ぎなんか しねえで コテンパンしてやるぜ!", + "3": "ウヒョヒョ! 痛みを 味わわせて あげますよ! 身を 委ねろ!" }, "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..." + "1": "「ウヒョヒョ‥‥! 俺に 勝てても リーダーには 勝てないぜ!\n$さっさと 帰ったほうが 痛い 思い しなくて 済むぜ!", + "2": "ウヒョヒョ…… 負けちまったか……", + "3": "うっひょーん……! なんたる事だろうか……!\nサブリーダーの ホムラさんが こんな デタラメなヤツ なぞに……", + "3_female": "うっひょーん……! なんたる事だろうか……!\nサブリーダーの ホムラさんが こんな デタラメなヤツ なぞに……" } }, "courtney": { "encounter": { - "1": "Don't. Get. In. My. Way.", - "2": "You... ...I want to...analyze. Ahahaha", - "3": "... Well then...Deleting..." + "1": "………ジャっ…マ……ッ\nするなあああああッッッッ!!!!!!", + "2": "キミを …………アナライズ\n…………したい …………ァハハハッ♪", + "3": "…………………じゃあ\n…………………デリートします" }, "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. ♪" + "1": "…………はぁはぁ……\n………んぅ…はぁはぁ………", + "2": "…………予想内 …………予想外\n…………キミ …………ターゲットロック …………したから\n$…………エクスペリメント …………するから\n…………キミを …………ずっと …………ァハッ……♪", + "3": "…………また …………予想外\n…………やっぱり …………キミ……オモチロイ……! ……ァハハッ……♪" } }, "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." + "1": "オーッホッホ! 我々 アクア団の\n邪魔を しようと 言うの!?\n$もう 怖いもの知らず と言おうか\nただの 愚か者 と言おうか……\n$かわいすぎて 憎らしく なっちゃう!\nやっつけて あげるわね!", + "2": "ああん? なんだい?\nこの クソ生意気な オコチャマは……?", + "3": "熱くなってん じゃないよ、 少しは 頭を 冷やしな。\nすぐ ぶっ壊すから。", + "3_female": "熱くなってん じゃないよ、 少しは 頭を 冷やしな。\nすぐ ぶっ壊すから。" }, "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." + "1": "オーッホッホ! 思わぬ 邪魔が 入っちゃったわ!\n仕方ないわね!\n$ここは 一度 引き上げちゃう! でもね アクア団の\n活動は まだまだ 続くんだから 覚えておきなさーい!", + "2": "くううっ……!?\n手を 抜きすぎちゃった かしら……!", + "3": "……うぅ ……この間 よりも\n更に ウデを上げてる ですって……!?\n$末恐ろしい オコチャマだわ……\n……アタシと ポケモンたちに もう 戦うチカラは 残っちゃいない\n$……行きなさいよ 行って\nアオギリ様に 粛清されるが いいわ。", + "3_female": "……うぅ ……この間 よりも\n更に ウデを上げてる ですって……!?\n$末恐ろしい オコチャマだわ……\n……アタシと ポケモンたちに もう 戦うチカラは 残っちゃいない\n$……行きなさいよ 行って\nアオギリ様に 粛清されるが いいわ。" } }, "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!" + "1": "……ってな ワケでヨォ あにィが ジカンが開くマデ\nオレッちの 相手を してもらうゼぃ!", + "2": "フウハアッ!! マックスッ!!\nタギってッ!! きたゼェェェェッ!!!!!\n$サアッ! サアッ! サアアアッ!!!\nチカラ 尽きハテるまで ヤリあおうゼッッ!!!", + "3": "UPAAAAA!!!\nモミツブシテ ヤルゼェェェ!!" }, "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!" + "1": "フゥーハッハッハァァァ!!!\nマケても タノしい ショウブ だったゼ!", + "2": "ビンビン かんじるゼェ!! オメェの ツヨサ!\n$モミつぶせる ときを タノシミに してるゼィ!", + "3": "オウホウッ! タカぶる ハイボク だぜっ!", + "3_female": "オウホウッ! タカぶる ハイボク だぜっ!" } }, "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!" + "1": "あたしは ギンガ団幹部の マーズ!\n強くて 美しいの!", + "2": "ギンガ団は 生み出そうと しているのは 新しい 世界。\nジャマは 許さないわ!", + "3": "ボスの ジャマは させないわよ!\nこの先に 進みたいなら あたしが 相手するわ!", + "3_female": "ボスの ジャマは させないわよ!\nこの先に 進みたいなら あたしが 相手するわ!" }, "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." + "1": "負けた……! ギンガ団の 幹部 として……\nこんなことって ありえない!!", + "2": "何なのよッ! あたしのこと 嫌いなの!?", + "3": "まさか! 負けるだなんて!?\n生意気な ヤツね!!" } }, "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?" + "1": "いいわ!\nこの ジュピターが 相手 してあげましょう!", + "2": "反対なんて 虚しい!\nギンガ団は 勝利するわ!", + "3": "どうしたの? もしかして 震えているのかしら?" }, "victory": { - "1": "No way... I lost?!", - "2": "Impressive, you've got guts!", - "3": "Losing like this... How embarrassing." + "1": "フン! 相変わらずの 強さ ちっとも かわいくないわね!", + "2": "フン! なかなか やるじゃない!", + "3": "フン! 次は 泣かして あげるんだから!" } }, "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?" + "1": "ミッションは 順調!\nボスも 満足 なさるだろう。\n$全ては みんなの ために\nそして ギンガ団の ために!", + "2": "ギンガ団の 使命を ジャマするなら\nどんな 可能性でも 潰す!", + "3": "わたしたち ギンガ団は 必要な ものを 独占し\n要らない ものは 捨てるだけ!" }, "victory": { - "1": "Impossible... Defeated by you?!", - "2": "You have proven yourself a worthy adversary.", - "3": "Bestowed in defeat... This is unacceptable." + "1": "強い! だが 哀れだな。", + "2": "……なるほど 強い!\nギンガ団に 歯向かう わけだ。", + "3": "くっ! この わたしが!\n時間稼ぎにしか ならないだと…\n$まあ いい! おまえが 何をしても\n流れる 時間は 止められない!" } }, "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." + "1": "さて…… おまえは プラズマ団に とって\n不安要素に なりかねない。\n$ここで 排除するのだ!", + "1_female": "さて…… おまえは プラズマ団に とって\n不安要素に なりかねない。\n$ここで 排除するのだ!", + "2": "諦めきれぬか? なら 引導を 渡すのが\nワタシなりの 優しさ なのだ!", + "3": "ここまで 来るとは 大した トレーナー だが、\n今は 終わりだ。", + "3_female": "ここまで 来るとは 大した トレーナー が、\n今は 終わりだ。" }, "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." + "1": "ゲーチス様…… 失望させました……", + "2": "寒い。 ワタシは 震えている。\n苦しいが いつか ワタシたちは 成功する。", + "3": "ふむう。 存外 さとい トレーナーだ。", + "3_female": "ふむう。 存外 さとい トレーナーだ。" } }, "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." + "1": "おまえ という トレーナーが\nどんな 人物か 見せてもらいたい…\n$そう、 ポケモン勝負 でな。", + "1_female": "おまえ という トレーナーが\nどんな 人物か 見せてもらいたい…\n$そう、 ポケモン勝負 でな。", + "2": "見限らぬようだ。 だれにも ジャマは させぬよ。", + "3": "……これが おまえの\n望みと あらば", + "3_female": "……これが おまえの\n望みと あらば" }, "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." + "1": "ほう! ポケモンと 心が\n通じているような 戦い方……", + "2": "失敗しました……\nそぞろに 潮の 匂いが 恋しくなる……", + "3": "正直言って…… ゲーチス様の なにが 真実で\nなにが 虚構か わからないがね……" } }, - "xerosic": { +"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!" + "1": "オマエを 倒せば ワタシの 科学力の\nすごさを 証明できるゾ! よし いくのだ!", + "1_female": "オマエを 倒せば ワタシの 科学力の\nすごさを 証明できるゾ! よし いくのだ!", + "2": "なるほど ジャマを したのは オマエだったのか!\nわかったゾ! よーし! オマエで 実験だゾ!", + "2_female": "なるほど ジャマを したのは オマエだったのか!\nわかったゾ! よーし! オマエで 実験だゾ!", + "3": "おおー ウワサの オマエか 待っていたゾ!\nオマエを 調べる ほら 始めるゾ!" }, "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!" + "1": "グヌウウ…… なぜだ……\nなぜ こんなことが 起こるのだ……?", + "2": "なんだと! オマエ すごいゾ!\nオマエの ポケモン すごいゾ!", + "2_female": "なんだと! オマエ すごいゾ!\nオマエの ポケモン すごいゾ!", + "3": "すごいな オマエ! すごいぞ オマエ!\nワタシは オマエを 認める イコール 金を あげる!" } }, "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?" + "1": "一人で やっつける。\n勝てる 確率を あげる 必要ない。", + "2": "どこかで 見た 顔?\nわかんないけど フレア団 じゃないし やっつけようよ…", + "2_female": "どこかで 見た 顔?\nわかんないけど フレア団 じゃないし やっつけようよ…", + "3": "あら? あら?" }, "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." + "1": "確率は あくまでも 確率。\n絶対では ないのよね……", + "2": "確率を 無視する トレーナー\nあなたの パワーの 源は?", + "3": "あらま! こいつめ!\nわたしが かわいそうでしょ!" } }, "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!" + "1": "なんだかんだと 聞かれたら\n答えてあげるのが 世の情け!", + "2": "おれ達は 大事な 仕事を してるんだ! お家へ 帰りな!", + "2_female": "おれ達は 大事な 仕事を してるんだ! お家へ 帰りな!", + "3": "ロケット団の 恐ろしさを 知りたくないなら\nおまえの ポケモンを よこせ!", + "4": "ロケット団の 本当の 恐ろしさ\nおまえに 教えて 差しあげよう!", + "5": "ロケット団に ガイコクジン\nワタシ オンリーワン だけど……\n$しかーし! そんなコト ノー カンケー!", + "5_female": "ロケット団に ガイコクジン\nワタシ オンリーワン だけど……\n$しかーし! そんなコト ノー カンケー!" }, "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?" + "1": "やな感じ~!", + "2": "しまった…! せっかく 隠して置いた エレベータの カギが…!", + "3": "しくじったか!", + "4": "くそ! 仲間が 黙っちゃ いねえぞ!", + "5": "オー ノー! キャント ビリーブ!\nユーは ブルーベリー ストロベリー!\n$……ソーリー ミステイク!\nユーは ベリー ストロング!\n$ティース キャント スタンダップ!\n歯が 立ちませーん!" } }, "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!" + "1": "おれたち マグマ団の 邪魔を するなら\n容赦は しないぜ!", + "2": "すべての 人々の ために\n我々 マグマ団は あるのよ!", + "3": "邪魔だ! マグマ団は お前の ような ヤツに\n用が ねーんだ! ほら さっさと 帰れよ!", + "4": "マシュマロ 持ってきた?\nすぐ 炎は 燃え上がっちゃう からー!!", + "5": "燃料の 力を 利用して 火山を 噴火させて やるのさ! ズドドーン とな!" }, "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?" + "1": "ぬぬぬー 敗北 するなどー!?", + "2": "負けちゃった! せっかくの 昼ご飯を 抜いたのにー", + "3": "ガキの くせに この 強さ だと!?", + "3_female": "ガキの くせに この 強さ だと!?", + "4": "うぐぐ‥‥ 早く アジトに 逃げ込めば 良かった……", + "5": "負けた… そんなことじゃ ボーナスが 減るぜ?" } }, "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!" + "1": "おれら アクア団の ジャマするつもりかい?\nガキでも 容赦せーへんで!", + "2": "うむむ…… アクア団に 逆らうと いい度胸 だろ!", + "3": "おれの みずポケモンが おまれを ずぶ濡れに しちゃうぞ!", + "4": "活動を 邪魔する つもりなら ぶっ壊す!\nこの世界を より良い 場所に してるぞ!", + "5": "俺の ポケモンの 水の に 押し流されちゃうぞ!" }, "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?" + "1": "冗談じゃねえ?!", + "2": "うむ‥‥ まさか ヤツに 邪魔 されるなんて\nこれっぽっちも おもってなかったぜ!", + "3": "チクショウ… アジトへ 泳ぎ帰らなくちゃ……", + "4": "……やばい! このまま じゃあ\nリーダーに 怒られちまうぞ……", + "5": "倒された… 板歩きの刑に なっちゃうかも……" } }, "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!" + "1": "ギンガ団に 逆らうな!", + "2": "我らの 技術の 力と\n目論む 未来 を目に当たりに しちゃえ!", + "3": "ギンガ団にかわって 邪魔する 誰もを 排除するぞ!", + "4": "負ける 準備 いいかい!?", + "5": "コスミックに 破壊しちゃうぞ!\n覚悟せよ!", + "5_female": "コスミックに 破壊しちゃうぞ!\n覚悟せよ!" }, "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." + "1": "墜落……", + "2": "この負けは 次のカケで\n取り返せば いいさ!", + "3": "かまわない!\n我らの 目的の方は この負けより 大きい!", + "4": "ホワイ?! なぜ?!", + "5": "自分への覚え書き:\nポケモン勝負練習 なる早" } }, "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." + "1": "あたしたちと 違う\n考えの 持ち主は 許さない!", + "2": "オレが 勝てば\nおまえの ポケモンを 解き放て!", + "3": "プラズマ団の ジャマをするなら\nアタシが やっつけてやる!!", + "4": "プラズマ団は お前のような 身勝手な\n人間から 開放する!", + "5": "おまえの ような トレーナーが\nポケモンを 苦しめているのだ!" }, "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!" + "1": "プラズマーーーーー!!!", + "2": "なんてこと……! おれが 負けるなど!", + "3": "……弱い ポケモンね\n別の ポケモンを 奪わなきゃ!!", + "4": "すばらしい 計画に\nジャマは 付き物 ね!", + "5": "マズイ……\nマズイマズイマズイマズイマズイマズイ\n$プラズマ団と して マズイ\n縮めて プラズマズイ!" } }, "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!" + "1": "お前の ポケモンが フレア団の 優美さには 敵わない!", + "2": "この サングラス いいだろう?\n羨ましいだろ? でも あげない!", + "2_female": "この サングラス いいだろう?\n羨ましいだろ? でも あげない!", + "3": "無風流は 許さない フレア団が お前を 打ち倒す!", + "4": "フレア団の 眩しさは お前を 圧倒するぞ!", + "5": "おれら 泣く子も 黙る\nオシャレチーム フレア団!" }, "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!" + "1": "敗北でも フレア団が 優美さが 華やかに 輝く!", + "2": "目の前が 真っ暗……\nあっ サングラス だからか?", + "3": "ぐあああ! 負けた! シャレにならない!", + "4": "やっぱり バトルには\nオシャレより 大事なことが あるかも…", + "5": "ちっ! フレア団 御用達の\nオシャレスーツが 汚れたぜ!" } }, "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!" + "1": "侵入者 発見!\nシークレットラボを 守ります!", + "2": "大事な 研究なのよ!\n子供とはいえ ぶっとばすわよ!", + "2_female": "大事な 研究なのよ!\n子供とはいえ ぶっとばすわよ!", + "3": "どんな トレーナーだろうと\n追い返すよう いわれてるのよ!", + "4": "エーテルパラダイスの\n開発力を みせてやろう!", + "5": "エーテルパラダイスの 闇を 知った\nおまえには 消えてもらうぜ!" }, "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!" + "1": "ふむう…… どうやら\nわたしは 負けたようですな。", + "2": "どういう ことだ……!\nどういう ことだ……?", + "3": "ちょっと! 強すぎて\n追い返すなんて ムリムリ!", + "4": "ポケモン勝負に 関する\n発見は してなかった!", + "5": "真似して ぎゃひーん!" } }, "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." + "1": "支部長 ザオボーは あなたに 現実を みせて さしあげますよ!", + "2": "ザオボー 人呼んで エーテルパラダイス\n最後の 最後の 最後の 砦は あなたを 壊します!", + "2_female": "ザオボー 人呼んで エーテルパラダイス\n最後の 最後の 最後の 砦は あなたを 壊します!", + "3": "エーテル財団の 支部長 といえば\n世界に ただ一人…… この ザオボーだけで ございます。" }, "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." + "1": "ぎゃひーん!!!", + "2": "な ななな なんということでしょう?\nこの わたしが お子さま相手に……", + "2_female": "な ななな なんということでしょう?\nこの わたしが お子さま相手に……", + "3": "なんということでしょう!?" } }, "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!" + "1": "まじめが キライでよ!\n$スカル団 やっているのに\nまじめに 下っ端 してるぜ?", + "2": "なんだ とは ご挨拶 ジャン!\nこいつら まとめて しめちゃおうよ!", + "2_female": "なんだ とは ご挨拶 ジャン!\nこいつら まとめて しめちゃおうよ!", + "3": "オレが ポケモンを 使いこなす\nすごいとこ みせてやりまスカ!", + "4": "あんたが ホネ身を 惜しまないかを 確かめたいから バトルッスカ!", + "4_female": "あんたが ホネ身を 惜しまないかを 確かめたいから バトルッスカ!", + "5": "だからYo♪ きけYo♪\n侵入者さんYo♪\n$オレら レペゼン サボる♪ スカル♪\nすこぶる♪ そそる♪ 話するYo♪", + "5_female": "だからYo♪ きけYo♪\n侵入者さんYo♪\n$オレら レペゼン サボる♪ スカル♪\nすこぶる♪ そそる♪ 話するYo♪" }, "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." + "1": "ちっ! まじめ だから\n……応援してやるよ\n$ヨーヨー 止まるなよ\n負けてもいいから 止まるなよ!", + "2": "しめるなよ! 袋叩き するなよ!", + "3": "オマエが ポケモンを 使いこなす\nすごいとこ みせられたのでスカ!?", + "4": "あんたらの ハート ホネ身に しみちゃう……!", + "5": "そーかYo♪\n後悔すんじゃねーYo♪" } }, "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." + "1": "あんたね…… さっき 聞いたの\n$……なんにも 感じない\nふつーのコに みえるけどねえ", + "1_female": " .あんたね…… さっき 聞いたの\n$……なんにも 感じない\nふつーのコに みえるけどねえ", + "2": "下っ端 あんたのような\n雑魚相手に モタモタ してるからさ", + "3": "スカル団を 束ねている……\n言うなれば あねごって ところ。\n$かわいい あいつらを\nいじめる あんたが ジャマなのよ!" }, "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." + "1": "ハンッ! たいした もんだよ\nただし 次 ジャマしたら 本気で やっちまうから", + "1_female": "ハンッ! たいした もんだよ\nただし 次 ジャマしたら 本気で やっちまうから", + "2": "あんた たいした もんだよ\nま 雑魚相手に 手間取るのも わかる 強さか。", + "3": "……チッ" } }, "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?" + "1": "不審者を 追い払って\nたんまり ボーナス いただくぜ!", + "2": "ジャマは 許しません!\n$マクロコスモスの さまざまな\n関連会社を 守るためにも 追い返します!", + "2_female": "ジャマは 許しません!\n$マクロコスモスの さまざまな\n関連会社を 守るためにも 追い返します!", + "3": "マクロコスモス生命です!\n保険に 入っていますか?" }, "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!" + "1": "ボーナスが……\n夢の マイホームが……", + "2": "負けたからには\n素直に 引き下がりましょう。\n$だが ローズ委員長の\nジャマは しないでくださいよ。", + "3": "マクロコスモス生命の 仕事なら\n誰にも 負けないのに…" } }, "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!" + "1": "ローズ様の ジャマ だなんて\nわたくし 絶対に 許せません!", + "2": "わたくしの オーダーを こなす 特別な\nスタッフ達を ものともせずに やってくるなんて……", + "3": "あなたを ボコボコに すれば\n委員長の 計画が すらっと 進めます!" }, "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..." + "1": "はあああぁ 勝てないなんて……\nオリーヴ…… ほんとに ダメな子", + "2": "はああ……! なんてこと……\n$勝ちあがった トレーナーの\n実力を みくびっていました……", + "2_female": "はああ……! なんてこと……\n$勝ちあがった トレーナーの\n実力を みくびっていました……", + "3": "まあ 生意気!\nオリーヴの パートナーを キズつけるなんて!" } }, "rocket_boss_giovanni_1": { "encounter": { - "1": "So! I must say, I am impressed you got here!" + "1": "こんな所 まで よく来た…" }, "victory": { - "1": "WHAT! This cannot be!" + "1": "ぐ ぐーッ! そんな ばかなーッ!" }, "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." + "1": "自分の 力を 把握できない 内は\nまだ 子供 ということだ…… 覚えておくがいい……", + "1_female": "自分の 力を 把握できない 内は\nまだ 子供 ということだ…… 覚えておくがいい……" } }, "rocket_boss_giovanni_2": { "encounter": { - "1": "My old associates need me... Are you going to get in my way?" + "1": "かつての 仲間たちが 私を 必要としてる…… 先の 失敗は もう 二度と 繰り返さない! " }, "victory": { - "1": "How is this possible...? The precious dream of Team Rocket has become little more than an illusion..." + "1": "なっ なぜだ……!\nロケット団 最高の 夢が 幻となって 消えていく……" }, "defeat": { - "1": "Team Rocket will be reborn again, and I will rule the world!" + "1": "ロケット団は 生まれ変わり\n世界を 我が物に するのだ!" } }, "magma_boss_maxie_1": { "encounter": { - "1": "I will bury you by my own hand. I hope you appreciate this honor!" + "1": "私 自らの 手で 葬ってやる……\n光栄に 思うが よい!" }, "victory": { - "1": "Ugh! You are... quite capable...\nI fell behind, but only by an inch..." + "1": "グッ…… やりおる……!\nわずか 1ミリ 及ばぬか……!" }, "defeat": { - "1": "Team Magma will prevail!" + "1": "マグマ団の 活動を 止めることなど 誰にも できぬ!" } }, "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!" + "1": "……キサマは 私が 図った日を\n迎えるための 最後の カベ―――\n$この マツブサが 治めし すべての チカラを もって 排除してやろう……!" }, "victory": { - "1": "This... This is not.. Ngh..." + "1": "こん…な……" }, "defeat": { - "1": "And now... I will transform this planet to a land ideal for humanity." + "1": "そして……\n$この世界は 人類にとって 理想の……" } }, "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." + "1": "アクア団 リーダーとして テメエの ポケモン\nもろとも バッキバキに 揉み潰して やるよ!" }, "victory": { - "1": "Let's meet again somewhere. I'll be sure to remember that face." + "1": "んじゃあ またな\n……そのツラ 忘れねえぜ" }, "defeat": { - "1": "Brilliant! My team won't hold back now!" + "1": "おもしれえッ!\n今は アクア団が 全開を!" } }, "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!" + "1": "前回の勝負 じゃあ 見せられなかった\nポケモン達と オレの 全開パワー……\n$たーんと 食らわせてやるぜえああああッ!" }, "victory": { - "1": "Like I figured..." + "1": "……流石…だな…ッ!" }, "defeat": { - "1": "I'll return everything in this world to its original, pure state!!" + "1": "オレは この日が 来るのを 長い間 待っていた‥‥\n今は この世界を あるがままの 姿にッ!" } }, "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!" + "1": "心いう 不完全なものが 感じる\n哀れみや 優しさ……\n$そんな 曖昧なものに 突き動かされ \nここに来たことを わたしが 公開させてあげよう。", + "1_female": "心いう 不完全なものが 感じる\n哀れみや 優しさ……\n$そんな 曖昧なものに 突き動かされ \nここに来たことを わたしが 公開させてあげよう" }, "victory": { - "1": "Interesting. And quite curious." + "1": "面白い\nそして 興味深い" }, "defeat": { - "1": "I will create my new world..." + "1": "まさに 新しい ギンガの! 宇宙の 誕生だ!" } }, "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!" + "1": "またキミか。\n$キミとは ほとほと 縁が あるね…\n$腐れ縁 といっても いいが\n今 ここで 断ち切ろう!" }, "victory": { - "1": "How? How? HOW?!" + "1": "まさか まさか まさかッ!" }, "defeat": { - "1": "Farewell." + "1": "さらばだ。" } }, "plasma_boss_ghetsis_1": { @@ -2718,7 +2718,7 @@ }, "rival": { "encounter": { - "1": "@c{smile}あっ、ここに いたんだ! 旅に 出る前に 「じゃ またね!」って くらい 聞きたかったよ……$@c{smile_eclosed}やっぱり 夢を 追ってこうと しているんだ? 信じられない ほどね……$@c{serious_smile_fists}じゃあ、 ここまで 来たから バトルしよっか? 覚悟してるかを 確かめたい から!$@c{serious_mopen_fists}遠慮せずに 全力で かかってこいぜ!" + "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}ここからも ガンバレ!" @@ -2734,7 +2734,7 @@ }, "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行こうぜ!" + "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相手に 利点を あげ続けると 行けないんだろう!" @@ -2753,10 +2753,10 @@ }, "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." + "1": "@c{smile}おお 誰かと思ったら な! 久しぶり!\n@c{neutral}もう… 倒されなかった か? フン\n$@c{neutral_eclosed}最近 なんか… 変な 気分だな。\nキミが いないと ふるさとは 同じ場所 じゃない。\n$@c{serious}わがまま かもしれないが、本音 明かさなくちゃ。\n@c{neutral_eclosed}このままで すぐ キミの 手に 負えなくなる。\n$@c{serious}一回も 負けないこと って むちゃくちゃだろう。\nみんなは 時々 失敗しなくちゃ。 そうでなけりゃ 成長できない。\n$@c{neutral_eclosed}ここまで よく やって来たが、\nまだまだ 先が 辛いこと ばかり。@c{neutral}覚悟してるか?\n$@c{serious_mopen_fists}それなら、見せてくれ。" }, "victory": { - "1": "@c{angry_mhalf}This is ridiculous… I've hardly stopped training…\nHow are we still so far apart?" + "1": "@c{angry_mhalf}こりゃ むっちゃ だろ… 訓練しか してないよ…\nなぜ 力が もう こんなに 違うなだ?" } }, "rival_3_female": { @@ -2772,10 +2772,10 @@ }, "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." + "1": "@c{neutral}よっ。\n$歯に 衣着せない。\n@c{neutral_eclosed}キミに 勝つために ここに 来た、それだけ。\n$@c{serious_mhalf_fists}地力を 最大限に 引き出す ために\n全労力を 費やして 訓練していた。\n$@c{smile}不要な 睡眠や 人間関係なんか 抜くと\n訓練の 時間は 割と 増えるね。\n$@c{serious_mopen_fists}そんなことは 勝てるときまで 全然 どうでもない。\n$@c{neutral_eclosed}今 負けられないとこまで やって来た。\n@c{smile_eclosed}キミの 考え方は 違いない ようだね。\n$@c{angry_mhalf}負けるのは 弱き者。 おれは もう 弱くない。\n$@c{serious_mopen_fists}覚悟せよ。" }, "victory": { - "1": "@c{neutral}What…@d{64} What are you?" + "1": "@c{neutral}一体…@d{64} 何モノか……?" } }, "rival_4_female": { @@ -2783,7 +2783,7 @@ "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} 何モノか…?" + "1": "@c{neutral}一体…@d{64} 何モノか……?" }, "defeat": { "1": "$@c{smile}ここまで 頑張ってたのを 誇りに思ってね。" @@ -2810,10 +2810,10 @@ }, "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." + "1": "@c{smile_eclosed}また 会ったね。\n$@c{neutral}今までの ことを 振り返る 時間があった。\n全てが 変に感じる 訳が あるよ。\n$@c{neutral_eclosed}キミの夢、おれが キミに 倒したい熱心……\nより大きい 何かの 部分だけだ。\n$@c{serious}おれや キミの 物語じゃない。これは 全世界の物語だ。\n@c{serious_mhalf_fists}この物語で、 おれの「役割」 っていうのは キミを 限界まで 押すこと。\n$@c{neutral_eclosed}その役割を 果たしたのかは 言えないが、 全力を尽くした。\n$@c{neutral}最後に行き着いた この場所って 恐ろしいが……\n以前 ここに来た ことがある ような 気がして、 怯えもしない。\n$@c{serious_mhalf_fists}キミも 同じ 気がするん だろう?\n$@c{serious}……何かが おれに 話してるようだ。\n昔から これだけしかは この世界こそ そのもの。\n$大事にしてた 最近だと思ってた 一緒にいた日々、\n今は もう 遠い記憶 だけだ。\n$@c{neutral_eclosed}そもそも 現実だったかは もう 言えなくなったな。\n$@c{serious_mopen_fists}キミは 頑張り続かないと、\n決して 終わらない。 キミしか できやしない。\n$@c{serious_smile_fists}全ての意味、 全然 分からない。\nしかし、 真実だと 知ってるな。\n$@c{serious_mopen_fists}今ここで おれを 倒せなきゃ、 最後に 勝ち目が ナイ。" }, "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." + "1": "@c{smile_eclosed}おれの 仕事が 終わったようだな。\n$一つだけの ことを 約束してほしい。\n@c{smile}この世界を 癒やした後、 お願い…… 帰ってくれ。" } }, "rival_6_female": { diff --git a/src/locales/ja/menu.json b/src/locales/ja/menu.json index f0914a7941c..b7db414493c 100644 --- a/src/locales/ja/menu.json +++ b/src/locales/ja/menu.json @@ -6,7 +6,7 @@ "newGame": "はじめから", "settings": "設定", "selectGameMode": "ゲームモードを 選んでください。", - "logInOrCreateAccount": "始めるには、ログイン、または 登録して ください。\nメールアドレスは 必要が ありません!", + "logInOrCreateAccount": "始めるには、ログイン、または 登録して ください。\nメールアドレスは 必要 ありません!", "username": "ユーザー名", "password": "パスワード", "login": "ログイン", @@ -14,7 +14,7 @@ "register": "登録", "emptyUsername": "ユーザー名を 空にする ことは できません", "invalidLoginUsername": "入力されたユーザー名は無効です", - "invalidRegisterUsername": "ユーザー名には 英文字、 数字、 アンダースコアのみを 含くむ必要が あります", + "invalidRegisterUsername": "ユーザー名には 英文字、 数字、 アンダースコアのみを 含くむことが 必要です", "invalidLoginPassword": "入力したパスワードは無効です", "invalidRegisterPassword": "パスワードは 6文字以上 でなければなりません", "usernameAlreadyUsed": "入力したユーザー名は すでに 使用されています", diff --git a/src/locales/ja/pokemon-summary.json b/src/locales/ja/pokemon-summary.json index cf35befe6fd..9465bcd346d 100644 --- a/src/locales/ja/pokemon-summary.json +++ b/src/locales/ja/pokemon-summary.json @@ -11,7 +11,7 @@ "cancel": "キャンセル", "memoString": "{{natureFragment}}な性格。\n{{metFragment}}", "metFragment": { - "normal": "{{biome}}で\nLv.{{level}}の時に出会った。", + "normal": "ラウンド{{wave}}に{{biome}}で\nLv.{{level}}の時に出会った。", "apparently": "{{biome}}で\nLv.{{level}}の時に出会ったようだ。" }, "natureFragment": { diff --git a/src/locales/ja/splash-messages.json b/src/locales/ja/splash-messages.json index b7378e7a916..db3948fa2f1 100644 --- a/src/locales/ja/splash-messages.json +++ b/src/locales/ja/splash-messages.json @@ -1,5 +1,5 @@ { - "battlesWon": "Battles Won!", + "battlesWon": "勝ったバトル:{{count, number}}回!", "joinTheDiscord": "Join the Discord!", "infiniteLevels": "Infinite Levels!", "everythingStacks": "Everything Stacks!", diff --git a/src/locales/ko/party-ui-handler.json b/src/locales/ko/party-ui-handler.json index 468f33bf960..6e7ba120b71 100644 --- a/src/locales/ko/party-ui-handler.json +++ b/src/locales/ko/party-ui-handler.json @@ -13,8 +13,10 @@ "ALL": "전부", "PASS_BATON": "배턴터치한다", "UNPAUSE_EVOLUTION": "진화 재개", + "PAUSE_EVOLUTION": "진화 중지", "REVIVE": "되살린다", "RENAME": "닉네임 바꾸기", + "SELECT": "선택한다", "choosePokemon": "포켓몬을 선택하세요.", "doWhatWithThisPokemon": "포켓몬을 어떻게 하겠습니까?", "noEnergy": "{{pokemonName}}[[는]] 싸울 수 있는\n기력이 남아 있지 않습니다!", @@ -23,6 +25,7 @@ "tooManyItems": "{{pokemonName}}[[는]] 지닌 도구의 수가\n너무 많습니다", "anyEffect": "써도 효과가 없다.", "unpausedEvolutions": "{{pokemonName}}의 진화가 재개되었다.", + "pausedEvolutions": "{{pokemonName}}[[가]] 진화하지 않도록 했다.", "unspliceConfirmation": "{{pokemonName}}로부터 {{fusionName}}의 융합을 해제하시겠습니까?\n{{fusionName}}는 사라지게 됩니다.", "wasReverted": "{{fusionName}}은 {{pokemonName}}의 모습으로 돌아갔습니다!", "releaseConfirmation": "{{pokemonName}}[[를]]\n정말 놓아주겠습니까?", diff --git a/src/locales/ko/pokemon-summary.json b/src/locales/ko/pokemon-summary.json index d9119623662..ca4b7a22b65 100644 --- a/src/locales/ko/pokemon-summary.json +++ b/src/locales/ko/pokemon-summary.json @@ -11,7 +11,7 @@ "cancel": "그만둔다", "memoString": "{{natureFragment}}.\n{{metFragment}}", "metFragment": { - "normal": "{{biome}}에서\n레벨 {{level}}일 때 만났다.", + "normal": "{{biome}}에서 웨이브{{wave}},\n레벨 {{level}}일 때 만났다.", "apparently": "{{biome}}에서\n레벨 {{level}}일 때 만난 것 같다." }, "natureFragment": { diff --git a/src/locales/ko/settings.json b/src/locales/ko/settings.json index c10046385e1..d0655009a3c 100644 --- a/src/locales/ko/settings.json +++ b/src/locales/ko/settings.json @@ -11,6 +11,10 @@ "expGainsSpeed": "경험치 획득 속도", "expPartyDisplay": "파티 경험치 표시", "skipSeenDialogues": "본 대화 생략", + "eggSkip": "알 스킵", + "never": "안 함", + "always": "항상", + "ask": "확인하기", "battleStyle": "시합 룰", "enableRetries": "재도전 허용", "hideIvs": "개체값탐지기 효과 끄기", diff --git a/src/locales/ko/splash-messages.json b/src/locales/ko/splash-messages.json index 6cf7ce050b7..1e89713ccde 100644 --- a/src/locales/ko/splash-messages.json +++ b/src/locales/ko/splash-messages.json @@ -1,5 +1,5 @@ { - "battlesWon": "전투에서 승리하세요!", + "battlesWon": "{{count, number}} 전투에서 승리하세요!", "joinTheDiscord": "디스코드에 가입하세요!", "infiniteLevels": "무한한 레벨!", "everythingStacks": "모든 것이 누적됩니다!", diff --git a/src/locales/pt_BR/battle.json b/src/locales/pt_BR/battle.json index 08eeb99e0cd..392f7b2ec38 100644 --- a/src/locales/pt_BR/battle.json +++ b/src/locales/pt_BR/battle.json @@ -96,5 +96,7 @@ "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}}!" + "beatModeFirstTime": "{{speciesName}} venceu o Modo {{gameMode}} pela primeira vez!\nVocê recebeu {{newModifier}}!", + "battlerTagsHealBlock": "{{pokemonNameWithAffix}} não pode restaurar seus PS!", + "battlerTagsHealBlockOnRemove": "{{pokemonNameWithAffix}} pode restaurar seus PS novamente!" } diff --git a/src/locales/pt_BR/pokemon-summary.json b/src/locales/pt_BR/pokemon-summary.json index 4c427dbac4f..14b736a0cf2 100644 --- a/src/locales/pt_BR/pokemon-summary.json +++ b/src/locales/pt_BR/pokemon-summary.json @@ -11,7 +11,7 @@ "cancel": "Cancelar", "memoString": "Natureza {{natureFragment}},\n{{metFragment}}", "metFragment": { - "normal": "encontrado no Nv.{{level}},\n{{biome}}.", + "normal": "encontrado no Nv.{{level}},\n{{biome}}, Onda {{wave}}.", "apparently": "aparentemente encontrado no Nv.{{level}},\n{{biome}}." }, "natureFragment": { diff --git a/src/locales/pt_BR/splash-messages.json b/src/locales/pt_BR/splash-messages.json index 55c0b1b9e74..237b0f21202 100644 --- a/src/locales/pt_BR/splash-messages.json +++ b/src/locales/pt_BR/splash-messages.json @@ -1,5 +1,5 @@ { - "battlesWon": "Batalhas Ganhas!", + "battlesWon": "{{count, number}} Batalhas Ganhas!", "joinTheDiscord": "Junte-se ao Discord!", "infiniteLevels": "Níveis Infinitos!", "everythingStacks": "Tudo Acumula!", diff --git a/src/locales/zh_CN/splash-messages.json b/src/locales/zh_CN/splash-messages.json index 4d2d208edfd..24981513afe 100644 --- a/src/locales/zh_CN/splash-messages.json +++ b/src/locales/zh_CN/splash-messages.json @@ -1,5 +1,5 @@ { - "battlesWon": "场胜利!", + "battlesWon": "{{count, number}} 场胜利!", "joinTheDiscord": "加入Discord!", "infiniteLevels": "等级无限!", "everythingStacks": "道具全部叠加!", diff --git a/src/locales/zh_TW/pokemon-summary.json b/src/locales/zh_TW/pokemon-summary.json index ddbbea63a3a..331330f5bdd 100644 --- a/src/locales/zh_TW/pokemon-summary.json +++ b/src/locales/zh_TW/pokemon-summary.json @@ -12,7 +12,7 @@ "memoString": "{{natureFragment}} 性格,\n{{metFragment}}", "metFragment": { - "normal": "met at Lv{{level}},\n{{biome}}.", + "normal": "met at Lv{{level}},\n{{biome}}, Wave {{wave}}.", "apparently": "命中注定般地相遇于Lv.{{level}},\n{{biome}}。" }, "natureFragment": { diff --git a/src/locales/zh_TW/splash-messages.json b/src/locales/zh_TW/splash-messages.json index a25e7dab97b..60b03549c2f 100644 --- a/src/locales/zh_TW/splash-messages.json +++ b/src/locales/zh_TW/splash-messages.json @@ -1,5 +1,5 @@ { - "battlesWon": "勝利場數!", + "battlesWon": "{{count, number}} 勝利場數!", "joinTheDiscord": "加入Discord!", "infiniteLevels": "無限等級!", "everythingStacks": "所有效果都能疊加!", diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index ec600a62210..ce2ffc6a462 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -1109,11 +1109,11 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator { } const evolutionItemPool = [ - party.filter(p => pokemonEvolutions.hasOwnProperty(p.species.speciesId)).map(p => { + party.filter(p => pokemonEvolutions.hasOwnProperty(p.species.speciesId) && (!p.pauseEvolutions || p.species.speciesId === Species.SLOWPOKE || p.species.speciesId === Species.EEVEE)).map(p => { const evolutions = pokemonEvolutions[p.species.speciesId]; return evolutions.filter(e => e.item !== EvolutionItem.NONE && (e.evoFormKey === null || (e.preFormKey || "") === p.getFormKey()) && (!e.condition || e.condition.predicate(p))); }).flat(), - party.filter(p => p.isFusion() && p.fusionSpecies && pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId)).map(p => { + party.filter(p => p.isFusion() && p.fusionSpecies && pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId) && (!p.pauseEvolutions || p.fusionSpecies.speciesId === Species.SLOWPOKE || p.fusionSpecies.speciesId === Species.EEVEE)).map(p => { const evolutions = pokemonEvolutions[p.fusionSpecies!.speciesId]; return evolutions.filter(e => e.item !== EvolutionItem.NONE && (e.evoFormKey === null || (e.preFormKey || "") === p.getFusionFormKey()) && (!e.condition || e.condition.predicate(p))); }).flat() @@ -1372,6 +1372,8 @@ export const modifierTypes = { FORM_CHANGE_ITEM: () => new FormChangeItemModifierTypeGenerator(false), RARE_FORM_CHANGE_ITEM: () => new FormChangeItemModifierTypeGenerator(true), + EVOLUTION_TRACKER_GIMMIGHOUL: () => new PokemonHeldItemModifierType("modifierType:ModifierType.EVOLUTION_TRACKER_GIMMIGHOUL", "relic_gold", (type, _args) => new Modifiers.EvoTrackerModifier(type, (_args[0] as Pokemon).id, Species.GIMMIGHOUL, 10)), + 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)), TERA_ORB: () => new ModifierType("modifierType:ModifierType.TERA_ORB", "tera_orb", (type, _args) => new Modifiers.TerastallizeAccessModifier(type)), @@ -1613,7 +1615,6 @@ const modifierPool: ModifierPool = { 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; }), @@ -1684,7 +1685,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(3 - rerollCount * 3, 0) : 0, 3), + 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.GREAT); return m; }), @@ -1760,12 +1761,12 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.ABILITY_CHARM, skipInClassicAfterWave(189, 6)), new WeightedModifierType(modifierTypes.FOCUS_BAND, 5), new WeightedModifierType(modifierTypes.KINGS_ROCK, 3), - new WeightedModifierType(modifierTypes.LOCK_CAPSULE, skipInLastClassicWaveOrDefault(3)), + new WeightedModifierType(modifierTypes.LOCK_CAPSULE, (party: Pokemon[]) => party[0].scene.gameMode.isClassic ? 0 : 3), new WeightedModifierType(modifierTypes.SUPER_EXP_CHARM, skipInLastClassicWaveOrDefault(8)), 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(9 - rerollCount * 3, 0) : 0, 9), + new WeightedModifierType(modifierTypes.VOUCHER_PLUS, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(3 - rerollCount * 1, 0) : 0, 3), ].map(m => { m.setTier(ModifierTier.ROGUE); return m; }), @@ -1774,7 +1775,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(15 - rerollCount * 5, 0) : 0, 15), + 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.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.isFreshStartChallenge() && party[0].scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE]) ? 1 : 0, 1), ].map(m => { diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index f2600214e45..3a021439f39 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -29,7 +29,6 @@ import { Abilities } from "#app/enums/abilities"; 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; @@ -601,7 +600,7 @@ export class TerastallizeAccessModifier extends PersistentModifier { export abstract class PokemonHeldItemModifier extends PersistentModifier { public pokemonId: integer; - readonly isTransferrable: boolean = true; + public isTransferable: boolean = true; constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) { super(type, stackCount); @@ -700,7 +699,7 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModifier { protected battlesLeft: integer; - readonly isTransferrable: boolean = false; + public isTransferable: boolean = false; constructor(type: ModifierTypes.ModifierType, pokemonId: integer, battlesLeft?: integer, stackCount?: integer) { super(type, pokemonId, stackCount); @@ -737,7 +736,7 @@ export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModi export class TerastallizeModifier extends LapsingPokemonHeldItemModifier { public teraType: Type; - readonly isTransferrable: boolean = false; + public isTransferable: boolean = false; constructor(type: ModifierTypes.TerastallizeModifierType, pokemonId: integer, teraType: Type, battlesLeft?: integer, stackCount?: integer) { super(type, pokemonId, battlesLeft || 10, stackCount); @@ -800,7 +799,7 @@ export class TerastallizeModifier extends LapsingPokemonHeldItemModifier { */ export class BaseStatModifier extends PokemonHeldItemModifier { protected stat: PermanentStat; - readonly isTransferrable: boolean = false; + public isTransferable: boolean = false; constructor(type: ModifierType, pokemonId: integer, stat: PermanentStat, stackCount?: integer) { super(type, pokemonId, stackCount); @@ -841,12 +840,47 @@ export class BaseStatModifier extends PokemonHeldItemModifier { } } +export class EvoTrackerModifier extends PokemonHeldItemModifier { + protected species: Species; + protected required: integer; + public isTransferable: boolean = false; + + constructor(type: ModifierType, pokemonId: integer, species: Species, required: integer, stackCount?: integer) { + super(type, pokemonId, stackCount); + this.species = species; + this.required = required; + } + + matchType(modifier: Modifier): boolean { + if (modifier instanceof EvoTrackerModifier) { + return (modifier as EvoTrackerModifier).species === this.species; + } + return false; + } + + clone(): PersistentModifier { + return new EvoTrackerModifier(this.type, this.pokemonId, this.species, this.stackCount); + } + + getArgs(): any[] { + return super.getArgs().concat(this.species); + } + + apply(args: any[]): boolean { + return true; + } + + getMaxHeldItemCount(_pokemon: Pokemon): integer { + return this.required; + } +} + /** * Currently used by Shuckle Juice item */ export class PokemonBaseStatTotalModifier extends PokemonHeldItemModifier { private statModifier: integer; - readonly isTransferrable: boolean = false; + public isTransferable: boolean = false; constructor(type: ModifierTypes.PokemonBaseStatTotalModifierType, pokemonId: integer, statModifier: integer, stackCount?: integer) { super(type, pokemonId, stackCount); @@ -895,7 +929,7 @@ export class PokemonBaseStatTotalModifier extends PokemonHeldItemModifier { export class PokemonBaseStatFlatModifier extends PokemonHeldItemModifier { private statModifier: integer; private stats: Stat[]; - readonly isTransferrable: boolean = false; + public isTransferable: boolean = false; constructor (type: ModifierType, pokemonId: integer, statModifier: integer, stats: Stat[], stackCount?: integer) { super(type, pokemonId, stackCount); @@ -945,7 +979,7 @@ export class PokemonBaseStatFlatModifier extends PokemonHeldItemModifier { * Currently used by Macho Brace item */ export class PokemonIncrementingStatModifier extends PokemonHeldItemModifier { - readonly isTransferrable: boolean = false; + public isTransferable: boolean = false; constructor (type: ModifierType, pokemonId: integer, stackCount?: integer) { super(type, pokemonId, stackCount); @@ -1086,7 +1120,7 @@ export class EvolutionStatBoosterModifier extends StatBoosterModifier { * @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); + return super.shouldApply(args) && !(args[0] as Pokemon).isMax(); } /** @@ -1273,7 +1307,7 @@ export class SpeciesCritBoosterModifier extends CritBoosterModifier { * Applies Specific Type item boosts (e.g., Magnet) */ export class AttackTypeBoosterModifier extends PokemonHeldItemModifier { - private moveType: Type; + public moveType: Type; private boostMultiplier: number; constructor(type: ModifierType, pokemonId: integer, moveType: Type, boostPercent: number, stackCount?: integer) { @@ -2312,7 +2346,7 @@ export class PokemonMultiHitModifier extends PokemonHeldItemModifier { export class PokemonFormChangeItemModifier extends PokemonHeldItemModifier { public formChangeItem: FormChangeItem; public active: boolean; - readonly isTransferrable: boolean = false; + public isTransferable: boolean = false; constructor(type: ModifierTypes.FormChangeItemModifierType, pokemonId: integer, formChangeItem: FormChangeItem, active: boolean, stackCount?: integer) { super(type, pokemonId, stackCount); @@ -2373,6 +2407,15 @@ export class MoneyRewardModifier extends ConsumableModifier { scene.addMoney(moneyAmount.value); + scene.getParty().map(p => { + if (p.species?.speciesId === Species.GIMMIGHOUL || p.fusionSpecies?.speciesId === Species.GIMMIGHOUL) { + p.evoCounter++; + const modifierType: ModifierType = modifierTypes.EVOLUTION_TRACKER_GIMMIGHOUL(); + const modifier = modifierType!.newModifier(p); + scene.addModifier(modifier); + } + }); + return true; } } @@ -2648,7 +2691,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier { const transferredModifierTypes: ModifierTypes.ModifierType[] = []; const itemModifiers = pokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier - && m.pokemonId === targetPokemon.id && m.isTransferrable, targetPokemon.isPlayer()) as PokemonHeldItemModifier[]; + && m.pokemonId === targetPokemon.id && m.isTransferable, targetPokemon.isPlayer()) as PokemonHeldItemModifier[]; let highestItemTier = itemModifiers.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is this bang correct? let tierItemModifiers = itemModifiers.filter(m => m.type.getOrInferTier(poolType) === highestItemTier); @@ -2693,7 +2736,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier { * @see {@linkcode modifierTypes[MINI_BLACK_HOLE]} */ export class TurnHeldItemTransferModifier extends HeldItemTransferModifier { - isTransferrable: boolean = true; + isTransferable: boolean = true; constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) { super(type, pokemonId, stackCount); } @@ -2719,7 +2762,7 @@ export class TurnHeldItemTransferModifier extends HeldItemTransferModifier { } setTransferrableFalse(): void { - this.isTransferrable = false; + this.isTransferable = false; } } diff --git a/src/overrides.ts b/src/overrides.ts index 6b550d152c2..35ca299721b 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -6,7 +6,7 @@ import { PokeballType } from "#enums/pokeball"; import { Species } from "#enums/species"; import { StatusEffect } from "#enums/status-effect"; import { TimeOfDay } from "#enums/time-of-day"; -import { VariantTier } from "#enums/variant-tiers"; +import { VariantTier } from "#enums/variant-tier"; import { WeatherType } from "#enums/weather-type"; import { type PokeballCounts } from "./battle-scene"; import { Gender } from "./data/gender"; @@ -70,6 +70,8 @@ class DefaultOverrides { [PokeballType.MASTER_BALL]: 0, }, }; + /** Set to `true` to show all tutorials */ + readonly BYPASS_TUTORIAL_SKIP: boolean = false; // ---------------- // PLAYER OVERRIDES diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 86e42acb26b..66e39cf98a5 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -16,6 +16,7 @@ import i18next from "i18next"; import { FieldPhase } from "./field-phase"; import { SelectTargetPhase } from "./select-target-phase"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +import { isNullOrUndefined } from "#app/utils"; export class CommandPhase extends FieldPhase { protected fieldIndex: integer; @@ -179,14 +180,16 @@ export class CommandPhase extends FieldPhase { case Command.POKEMON: case Command.RUN: const isSwitch = command === Command.POKEMON; - if (!isSwitch && this.scene.arena.biomeType === Biome.END) { + const { currentBattle, arena } = this.scene; + const mysteryEncounterFleeAllowed = currentBattle.mysteryEncounter?.fleeAllowed; + if (!isSwitch && (arena.biomeType === Biome.END || (!isNullOrUndefined(mysteryEncounterFleeAllowed) && !mysteryEncounterFleeAllowed))) { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noEscapeForce"), null, () => { this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); - } else if (!isSwitch && (this.scene.currentBattle.battleType === BattleType.TRAINER || this.scene.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE)) { + } else if (!isSwitch && (currentBattle.battleType === BattleType.TRAINER || currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE)) { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noEscapeTrainer"), null, () => { @@ -197,12 +200,12 @@ export class CommandPhase extends FieldPhase { const batonPass = isSwitch && args[0] as boolean; const trappedAbMessages: string[] = []; if (batonPass || !playerPokemon.isTrapped(trappedAbMessages)) { - this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch + currentBattle.turnCommands[this.fieldIndex] = isSwitch ? { command: Command.POKEMON, cursor: cursor, args: args } : { command: Command.RUN }; success = true; if (!isSwitch && this.fieldIndex) { - this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; + currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; } } else if (trappedAbMessages.length > 0) { if (!isSwitch) { @@ -219,7 +222,7 @@ export class CommandPhase extends FieldPhase { // trapTag should be defined at this point, but just in case... if (!trapTag) { - this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch + currentBattle.turnCommands[this.fieldIndex] = isSwitch ? { command: Command.POKEMON, cursor: cursor, args: args } : { command: Command.RUN }; break; diff --git a/src/phases/egg-lapse-phase.ts b/src/phases/egg-lapse-phase.ts index 65426846bb3..c251819f331 100644 --- a/src/phases/egg-lapse-phase.ts +++ b/src/phases/egg-lapse-phase.ts @@ -40,7 +40,7 @@ export class EggLapsePhase extends Phase { this.showSummary(); }, () => { this.hatchEggsRegular(eggsToHatch); - this.showSummary(); + this.end(); } ); }, 100, true); diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index 1d9567ee9b3..012738df9ab 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -216,8 +216,8 @@ export class EncounterPhase extends BattlePhase { this.scene.ui.setMode(Mode.MESSAGE).then(() => { if (!this.loaded) { - //@ts-ignore - this.scene.gameData.saveAll(this.scene, true, battle.waveIndex % 10 === 1 || this.scene.lastSavePlayTime >= 300).then(success => { // TODO: get rid of ts-ignore + this.trySetWeatherIfNewBiome(); // Set weather before session gets saved + this.scene.gameData.saveAll(this.scene, true, battle.waveIndex % 10 === 1 || (this.scene.lastSavePlayTime ?? 0) >= 300).then(success => { this.scene.disableMenu = false; if (!success) { return this.scene.reset(true); @@ -250,10 +250,6 @@ export class EncounterPhase extends BattlePhase { } } - if (!this.loaded) { - this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena), false); - } - const enemyField = this.scene.getEnemyField(); this.scene.tweens.add({ targets: [this.scene.arenaEnemy, this.scene.currentBattle.trainer, enemyField, this.scene.arenaPlayer, this.scene.trainer].flat(), @@ -519,4 +515,18 @@ export class EncounterPhase extends BattlePhase { } return false; } + + /** + * Set biome weather if and only if this encounter is the start of a new biome. + * + * By using function overrides, this should happen if and only if this phase + * is exactly a NewBiomeEncounterPhase or an EncounterPhase (to account for + * Wave 1 of a Daily Run), but NOT NextEncounterPhase (which starts the next + * wave in the same biome). + */ + trySetWeatherIfNewBiome(): void { + if (!this.loaded) { + this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena), false); + } + } } diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index ccb3262b1bb..41384e5e491 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -124,9 +124,6 @@ export class FaintPhase extends PokemonPhase { this.scene.redirectPokemonMoves(pokemon, allyPokemon); } - pokemon.lapseTags(BattlerTagLapseType.FAINT); - this.scene.getField(true).filter(p => p !== pokemon).forEach(p => p.removeTagsBySourceId(pokemon.id)); - pokemon.faintCry(() => { if (pokemon instanceof PlayerPokemon) { pokemon.addFriendship(-10); @@ -140,6 +137,9 @@ export class FaintPhase extends PokemonPhase { ease: "Sine.easeIn", onComplete: () => { pokemon.resetSprite(); + pokemon.lapseTags(BattlerTagLapseType.FAINT); + this.scene.getField(true).filter(p => p !== pokemon).forEach(p => p.removeTagsBySourceId(pokemon.id)); + pokemon.y -= 150; pokemon.trySetStatus(StatusEffect.FAINT); if (pokemon.isPlayer()) { diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts index 8ab191324c6..e6ccca6c95a 100644 --- a/src/phases/game-over-phase.ts +++ b/src/phases/game-over-phase.ts @@ -60,6 +60,11 @@ export class GameOverPhase extends BattlePhase { this.scene.ui.fadeOut(1250).then(() => { this.scene.reset(); this.scene.clearPhaseQueue(); + // If this is a ME, clear any residual visual sprites before reloading + const encounter = this.scene.currentBattle.mysteryEncounter; + if (encounter?.introVisuals) { + this.scene.field.remove(encounter.introVisuals, true); + } this.scene.gameData.loadSession(this.scene, this.scene.sessionSlotId).then(() => { this.scene.pushPhase(new EncounterPhase(this.scene, true)); @@ -238,7 +243,7 @@ export class GameOverPhase extends BattlePhase { gameVersion: this.scene.game.config.gameVersion, timestamp: new Date().getTime(), challenges: this.scene.gameMode.challenges.map(c => new ChallengeData(c)), - mysteryEncounterType: this.scene.currentBattle.mysteryEncounter?.encounterType, + mysteryEncounterType: this.scene.currentBattle.mysteryEncounter?.encounterType ?? -1, mysteryEncounterSaveData: this.scene.mysteryEncounterSaveData } as SessionSaveData; } diff --git a/src/phases/message-phase.ts b/src/phases/message-phase.ts index 2244980c899..1d953801178 100644 --- a/src/phases/message-phase.ts +++ b/src/phases/message-phase.ts @@ -6,14 +6,16 @@ export class MessagePhase extends Phase { private callbackDelay: integer | null; private prompt: boolean | null; private promptDelay: integer | null; + private speaker?: string; - constructor(scene: BattleScene, text: string, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) { + constructor(scene: BattleScene, text: string, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null, speaker?: string) { super(scene); this.text = text; this.callbackDelay = callbackDelay!; // TODO: is this bang correct? this.prompt = prompt!; // TODO: is this bang correct? this.promptDelay = promptDelay!; // TODO: is this bang correct? + this.speaker = speaker; } start() { @@ -21,11 +23,15 @@ export class MessagePhase extends Phase { if (this.text.indexOf("$") > -1) { const pageIndex = this.text.indexOf("$"); - this.scene.unshiftPhase(new MessagePhase(this.scene, this.text.slice(pageIndex + 1), this.callbackDelay, this.prompt, this.promptDelay)); + this.scene.unshiftPhase(new MessagePhase(this.scene, this.text.slice(pageIndex + 1), this.callbackDelay, this.prompt, this.promptDelay, this.speaker)); this.text = this.text.slice(0, pageIndex).trim(); } - this.scene.ui.showText(this.text, null, () => this.end(), this.callbackDelay || (this.prompt ? 0 : 1500), this.prompt, this.promptDelay); + if (this.speaker) { + this.scene.ui.showDialogue(this.text, this.speaker, null, () => this.end(), this.callbackDelay || (this.prompt ? 0 : 1500), this.promptDelay ?? 0); + } else { + this.scene.ui.showText(this.text, null, () => this.end(), this.callbackDelay || (this.prompt ? 0 : 1500), this.prompt, this.promptDelay); + } } end() { diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index ffd9d45b4bd..c3199166e84 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -102,7 +102,7 @@ export class MoveEffectPhase extends PokemonPhase { * (and not random target) and failed the hit check against its target (MISS), log the move * as FAILed or MISSed (depending on the conditions above) and end this phase. */ - if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) { + if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]] && !targets[0].getTag(ProtectedTag))) { this.stopMultiHit(); if (hasActiveTargets) { this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: this.getTarget()? getPokemonNameWithAffix(this.getTarget()!) : "" })); @@ -125,20 +125,6 @@ export class MoveEffectPhase extends PokemonPhase { /** Has the move successfully hit a target (for damage) yet? */ let hasHit: boolean = false; for (const target of targets) { - /** - * If the move missed a target, stop all future hits against that target - * and move on to the next target (if there is one). - */ - if (!targetHitChecks[target.getBattlerIndex()]) { - this.stopMultiHit(target); - this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); - if (moveHistoryEntry.result === MoveResult.PENDING) { - moveHistoryEntry.result = MoveResult.MISS; - } - user.pushMoveHistory(moveHistoryEntry); - applyMoveAttrs(MissEffectAttr, user, null, move); - continue; - } /** The {@linkcode ArenaTagSide} to which the target belongs */ const targetSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; @@ -156,6 +142,21 @@ export class MoveEffectPhase extends PokemonPhase { && (hasConditionalProtectApplied.value || (!target.findTags(t => t instanceof DamageProtectedTag).length && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))) || (this.move.getMove().category !== MoveCategory.STATUS && target.findTags(t => t instanceof DamageProtectedTag).find(t => target.lapseTag(t.tagType)))); + /** + * If the move missed a target, stop all future hits against that target + * and move on to the next target (if there is one). + */ + if (!isProtected && !targetHitChecks[target.getBattlerIndex()]) { + this.stopMultiHit(target); + this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); + if (moveHistoryEntry.result === MoveResult.PENDING) { + moveHistoryEntry.result = MoveResult.MISS; + } + user.pushMoveHistory(moveHistoryEntry); + applyMoveAttrs(MissEffectAttr, user, null, move); + continue; + } + /** Does this phase represent the invoked move's first strike? */ const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount); diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index 6c9d3fd8c1d..007b69650b9 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -3,7 +3,7 @@ import BattleScene from "../battle-scene"; import { Phase } from "../phase"; import { Mode } from "../ui/ui"; import { transitionMysteryEncounterIntroVisuals, OptionSelectSettings } from "../data/mystery-encounters/utils/encounter-phase-utils"; -import MysteryEncounterOption, { OptionPhaseCallback } from "../data/mystery-encounters/mystery-encounter-option"; +import MysteryEncounterOption, { OptionPhaseCallback } from "#app/data/mystery-encounters/mystery-encounter-option"; import { getCharVariantFromDialogue } from "../data/dialogue"; import { TrainerSlot } from "../data/trainer-config"; import { BattleSpec } from "#enums/battle-spec"; diff --git a/src/phases/new-biome-encounter-phase.ts b/src/phases/new-biome-encounter-phase.ts index 48d928402de..2a526a22ee2 100644 --- a/src/phases/new-biome-encounter-phase.ts +++ b/src/phases/new-biome-encounter-phase.ts @@ -17,8 +17,6 @@ export class NewBiomeEncounterPhase extends NextEncounterPhase { } } - this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena), false); - for (const pokemon of this.scene.getParty().filter(p => p.isOnField())) { applyAbAttrs(PostBiomeChangeAbAttr, pokemon, null); } @@ -41,4 +39,11 @@ export class NewBiomeEncounterPhase extends NextEncounterPhase { } }); } + + /** + * Set biome weather. + */ + trySetWeatherIfNewBiome(): void { + this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena), false); + } } diff --git a/src/phases/next-encounter-phase.ts b/src/phases/next-encounter-phase.ts index 7de2472dd35..d63823e4167 100644 --- a/src/phases/next-encounter-phase.ts +++ b/src/phases/next-encounter-phase.ts @@ -67,4 +67,10 @@ export class NextEncounterPhase extends EncounterPhase { } }); } + + /** + * Do nothing (since this is simply the next wave in the same biome). + */ + trySetWeatherIfNewBiome(): void { + } } diff --git a/src/phases/pokemon-heal-phase.ts b/src/phases/pokemon-heal-phase.ts index 49db2641e98..813d15ae87e 100644 --- a/src/phases/pokemon-heal-phase.ts +++ b/src/phases/pokemon-heal-phase.ts @@ -10,6 +10,8 @@ import { HealAchv } from "#app/system/achv"; import i18next from "i18next"; import * as Utils from "#app/utils"; import { CommonAnimPhase } from "./common-anim-phase"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { HealBlockTag } from "#app/data/battler-tags"; export class PokemonHealPhase extends CommonAnimPhase { private hpHealed: integer; @@ -50,9 +52,14 @@ export class PokemonHealPhase extends CommonAnimPhase { const hasMessage = !!this.message; const healOrDamage = (!pokemon.isFullHp() || this.hpHealed < 0); + const healBlock = pokemon.getTag(BattlerTagType.HEAL_BLOCK) as HealBlockTag; let lastStatusEffect = StatusEffect.NONE; - if (healOrDamage) { + if (healBlock && this.hpHealed > 0) { + this.scene.queueMessage(healBlock.onActivation(pokemon)); + this.message = null; + super.end(); + } else if (healOrDamage) { const hpRestoreMultiplier = new Utils.IntegerHolder(1); if (!this.revive) { this.scene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier); diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts index 39a0da1167f..58fb13ac466 100644 --- a/src/phases/select-modifier-phase.ts +++ b/src/phases/select-modifier-phase.ts @@ -1,7 +1,7 @@ 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 { ExtraModifierModifier, HealShopCostModifier, 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"; @@ -10,7 +10,7 @@ import * as Utils from "#app/utils"; import { BattlePhase } from "./battle-phase"; import Overrides from "#app/overrides"; import { CustomModifierSettings } from "#app/modifier/modifier-type"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined, NumberHolder } from "#app/utils"; export class SelectModifierPhase extends BattlePhase { private rerollCount: integer; @@ -69,11 +69,11 @@ export class SelectModifierPhase extends BattlePhase { } let modifierType: ModifierType; let cost: integer; + const rerollCost = this.getRerollCost(typeOptions, this.scene.lockModifierTiers); switch (rowCursor) { case 0: switch (cursor) { case 0: - const rerollCost = this.getRerollCost(typeOptions, this.scene.lockModifierTiers); if (rerollCost < 0 || this.scene.money < rerollCost) { this.scene.ui.playError(); return false; @@ -94,7 +94,7 @@ export class SelectModifierPhase extends BattlePhase { this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: integer, itemIndex: integer, itemQuantity: integer, toSlotIndex: integer) => { if (toSlotIndex !== undefined && fromSlotIndex < 6 && toSlotIndex < 6 && fromSlotIndex !== toSlotIndex && itemIndex > -1) { const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier - && m.isTransferrable && m.pokemonId === party[fromSlotIndex].id) as PokemonHeldItemModifier[]; + && m.isTransferable && m.pokemonId === party[fromSlotIndex].id) as PokemonHeldItemModifier[]; const itemModifier = itemModifiers[itemIndex]; this.scene.tryTransferHeldItemModifier(itemModifier, party[toSlotIndex], true, itemQuantity); } else { @@ -108,6 +108,11 @@ export class SelectModifierPhase extends BattlePhase { }); break; case 3: + if (rerollCost < 0) { + // Reroll lock button is also disabled when reroll is disabled + this.scene.ui.playError(); + return false; + } this.scene.lockModifierTiers = !this.scene.lockModifierTiers; const uiHandler = this.scene.ui.getHandler() as ModifierSelectUiHandler; uiHandler.setRerollCost(this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); @@ -133,7 +138,10 @@ export class SelectModifierPhase extends BattlePhase { if (shopOption.type) { modifierType = shopOption.type; } - cost = shopOption.cost; + // Apply Black Sludge to healing item cost + const healingItemCost = new NumberHolder(shopOption.cost); + this.scene.applyModifier(HealShopCostModifier, true, healingItemCost); + cost = healingItemCost.value; break; } diff --git a/src/phases/select-target-phase.ts b/src/phases/select-target-phase.ts index 716d2737a6c..6f11f984c4b 100644 --- a/src/phases/select-target-phase.ts +++ b/src/phases/select-target-phase.ts @@ -4,6 +4,8 @@ import { Command } from "#app/ui/command-ui-handler"; import { Mode } from "#app/ui/ui"; import { CommandPhase } from "./command-phase"; import { PokemonPhase } from "./pokemon-phase"; +import i18next from "#app/plugins/i18n"; +import { allMoves } from "#app/data/move"; export class SelectTargetPhase extends PokemonPhase { constructor(scene: BattleScene, fieldIndex: integer) { @@ -17,6 +19,14 @@ export class SelectTargetPhase extends PokemonPhase { const move = turnCommand?.move?.move; this.scene.ui.setMode(Mode.TARGET_SELECT, this.fieldIndex, move, (targets: BattlerIndex[]) => { this.scene.ui.setMode(Mode.MESSAGE); + const fieldSide = this.scene.getField(); + const user = fieldSide[this.fieldIndex]; + const moveObject = allMoves[move!]; + if (moveObject && user.isMoveTargetRestricted(moveObject.id, user, fieldSide[targets[0]])) { + const errorMessage = user.getRestrictingTag(move!, user, fieldSide[targets[0]])!.selectionDeniedText(user, moveObject.id); + user.scene.queueMessage(i18next.t(errorMessage, { moveName: moveObject.name }), 0, true); + targets = []; + } if (targets.length < 1) { this.scene.currentBattle.turnCommands[this.fieldIndex] = null; this.scene.unshiftPhase(new CommandPhase(this.scene, this.fieldIndex)); diff --git a/src/phases/show-party-exp-bar-phase.ts b/src/phases/show-party-exp-bar-phase.ts index 9e019b202a5..f1783e7715f 100644 --- a/src/phases/show-party-exp-bar-phase.ts +++ b/src/phases/show-party-exp-bar-phase.ts @@ -1,4 +1,5 @@ import BattleScene from "#app/battle-scene"; +import { ExpGainsSpeed } from "#app/enums/exp-gains-speed"; import { ExpNotification } from "#app/enums/exp-notification"; import { ExpBoosterModifier } from "#app/modifier/modifier"; import * as Utils from "#app/utils"; @@ -44,7 +45,7 @@ export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase { } else { this.end(); } - } else if (this.scene.expGainsSpeed < 3) { + } else if (this.scene.expGainsSpeed < ExpGainsSpeed.SKIP) { this.scene.partyExpBar.showPokemonExp(pokemon, exp.value, false, newLevel).then(() => { setTimeout(() => this.end(), 500 / Math.pow(2, this.scene.expGainsSpeed)); }); diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts index 55faaa29903..4418c38c849 100644 --- a/src/phases/stat-stage-change-phase.ts +++ b/src/phases/stat-stage-change-phase.ts @@ -6,7 +6,7 @@ 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 { NumberHolder, BooleanHolder } from "#app/utils"; import i18next from "i18next"; import { PokemonPhase } from "./pokemon-phase"; import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat"; @@ -42,17 +42,23 @@ export class StatStageChangePhase extends PokemonPhase { return this.end(); } + const stages = new NumberHolder(this.stages); + + if (!this.ignoreAbilities) { + applyAbAttrs(StatStageChangeMultiplierAbAttr, pokemon, null, false, stages); + } + let simulate = false; const filteredStats = this.stats.filter(stat => { - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); - if (!this.selfTarget && this.stages < 0) { + if (!this.selfTarget && stages.value < 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) { + if (!cancelled.value && !this.selfTarget && stages.value < 0) { applyPreStatStageChangeAbAttrs(ProtectStatAbAttr, pokemon, stat, cancelled, simulate); } @@ -64,12 +70,6 @@ export class StatStageChangePhase extends PokemonPhase { 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); diff --git a/src/phases/trainer-victory-phase.ts b/src/phases/trainer-victory-phase.ts index 55b2a1608c0..e925f0c47d4 100644 --- a/src/phases/trainer-victory-phase.ts +++ b/src/phases/trainer-victory-phase.ts @@ -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_PLUS, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][vouchers[TrainerType[trainerType]].voucherType])); + this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, [modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][vouchers[TrainerType[trainerType]].voucherType])); } } diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index c11dd80b3aa..c10adc5683d 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -1,6 +1,6 @@ import BattleScene from "#app/battle-scene"; -import { BattlerIndex, BattleType } from "#app/battle"; -import { modifierTypes } from "#app/modifier/modifier-type"; +import { BattlerIndex, BattleType, ClassicFixedBossWaves } from "#app/battle"; +import { CustomModifierSettings, modifierTypes } from "#app/modifier/modifier-type"; import { BattleEndPhase } from "./battle-end-phase"; import { NewBattlePhase } from "./new-battle-phase"; import { PokemonPhase } from "./pokemon-phase"; @@ -42,8 +42,12 @@ export class VictoryPhase extends PokemonPhase { } if (this.scene.gameMode.isEndless || !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) { this.scene.pushPhase(new EggLapsePhase(this.scene)); + if (this.scene.gameMode.isClassic && this.scene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_BOSS_2) { + // Should get Lock Capsule on 165 before shop phase so it can be used in the rewards shop + this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.LOCK_CAPSULE)); + } if (this.scene.currentBattle.waveIndex % 10) { - this.scene.pushPhase(new SelectModifierPhase(this.scene)); + this.scene.pushPhase(new SelectModifierPhase(this.scene, undefined, undefined, this.getFixedBattleCustomModifiers())); } else if (this.scene.gameMode.isDaily) { this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_CHARM)); if (this.scene.currentBattle.waveIndex > 10 && !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) { @@ -76,4 +80,18 @@ export class VictoryPhase extends PokemonPhase { this.end(); } + + /** + * If this wave is a fixed battle with special custom modifier rewards, + * will pass those settings to the upcoming {@linkcode SelectModifierPhase}`. + */ + getFixedBattleCustomModifiers(): CustomModifierSettings | undefined { + const gameMode = this.scene.gameMode; + const waveIndex = this.scene.currentBattle.waveIndex; + if (gameMode.isFixedBattle(waveIndex)) { + return gameMode.getFixedBattle(waveIndex).customModifierRewardSettings; + } + + return undefined; + } } diff --git a/src/system/achv.ts b/src/system/achv.ts index 6170fe23e1d..09ec74de50c 100644 --- a/src/system/achv.ts +++ b/src/system/achv.ts @@ -279,6 +279,8 @@ export function getAchievementDescription(localizationKey: string): string { return i18next.t("achv:FRESH_START.description", { context: genderStr }); case "INVERSE_BATTLE": return i18next.t("achv:INVERSE_BATTLE.description", { context: genderStr }); + case "BREEDERS_IN_SPACE": + return i18next.t("achv:BREEDERS_IN_SPACE.description", { context: genderStr }); default: return ""; } @@ -356,6 +358,7 @@ export const achvs = { MONO_FAIRY: new ChallengeAchv("MONO_FAIRY", "", "MONO_FAIRY.description", "fairy_feather", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 18 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)), FRESH_START: new ChallengeAchv("FRESH_START", "", "FRESH_START.description", "reviver_seed", 100, (c, scene) => c instanceof FreshStartChallenge && c.value > 0 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)), INVERSE_BATTLE: new ChallengeAchv("INVERSE_BATTLE", "", "INVERSE_BATTLE.description", "inverse", 100, c => c instanceof InverseBattleChallenge && c.value > 0), + BREEDERS_IN_SPACE: new Achv("BREEDERS_IN_SPACE", "", "BREEDERS_IN_SPACE.description", "moon_stone", 100).setSecret(), }; export function initAchievements() { diff --git a/src/system/egg-data.ts b/src/system/egg-data.ts index 785ae364efe..1c9c903688a 100644 --- a/src/system/egg-data.ts +++ b/src/system/egg-data.ts @@ -1,6 +1,6 @@ import { EggTier } from "#enums/egg-type"; import { Species } from "#enums/species"; -import { VariantTier } from "#enums/variant-tiers"; +import { VariantTier } from "#enums/variant-tier"; import { EGG_SEED, Egg } from "../data/egg"; import { EggSourceType } from "#app/enums/egg-source-types"; diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 92bfc627ddb..0fd90e448a1 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -38,12 +38,14 @@ export default class PokemonData { public status: Status | null; public friendship: integer; public metLevel: integer; - public metBiome: Biome | -1; + public metBiome: Biome | -1; // -1 for starters public metSpecies: Species; + public metWave: number; // 0 for unknown (previous saves), -1 for starters public luck: integer; public pauseEvolutions: boolean; public pokerus: boolean; public usedTMs: Moves[]; + public evoCounter: integer; public fusionSpecies: Species; public fusionFormIndex: integer; @@ -89,9 +91,11 @@ export default class PokemonData { this.metLevel = source.metLevel || 5; this.metBiome = source.metBiome !== undefined ? source.metBiome : -1; this.metSpecies = source.metSpecies; + this.metWave = source.metWave ?? (this.metBiome === -1 ? -1 : 0); this.luck = source.luck !== undefined ? source.luck : (source.shiny ? (source.variant + 1) : 0); if (!forHistory) { this.pauseEvolutions = !!source.pauseEvolutions; + this.evoCounter = source.evoCounter ?? 0; } this.pokerus = !!source.pokerus; diff --git a/src/test/abilities/battle_bond.test.ts b/src/test/abilities/battle_bond.test.ts index 71e9438db8f..4882001cc8d 100644 --- a/src/test/abilities/battle_bond.test.ts +++ b/src/test/abilities/battle_bond.test.ts @@ -7,7 +7,7 @@ import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Abilities - BATTLE BOND", () => { let phaserGame: Phaser.Game; @@ -60,6 +60,5 @@ describe("Abilities - BATTLE BOND", () => { expect(greninja!.formIndex).toBe(baseForm); }, - TIMEOUT ); }); diff --git a/src/test/abilities/contrary.test.ts b/src/test/abilities/contrary.test.ts index 95a209395dc..5221e821e70 100644 --- a/src/test/abilities/contrary.test.ts +++ b/src/test/abilities/contrary.test.ts @@ -31,7 +31,7 @@ describe("Abilities - Contrary", () => { }); it("should invert stat changes when applied", async() => { - await game.startBattle([ + await game.classicMode.startBattle([ Species.SLOWBRO ]); @@ -39,4 +39,39 @@ describe("Abilities - Contrary", () => { expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); }, 20000); + + describe("With Clear Body", () => { + it("should apply positive effects", async () => { + game.override + .enemyPassiveAbility(Abilities.CLEAR_BODY) + .moveset([Moves.TAIL_WHIP]); + await game.classicMode.startBattle([Species.SLOWBRO]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); + + game.move.select(Moves.TAIL_WHIP); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(1); + }); + + it("should block negative effects", async () => { + game.override + .enemyPassiveAbility(Abilities.CLEAR_BODY) + .enemyMoveset([Moves.HOWL, Moves.HOWL, Moves.HOWL, Moves.HOWL]) + .moveset([Moves.SPLASH]); + await game.classicMode.startBattle([Species.SLOWBRO]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); + + game.move.select(Moves.SPLASH); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); + }); + }); }); diff --git a/src/test/abilities/costar.test.ts b/src/test/abilities/costar.test.ts index 794bed0d3cf..2fd1cb26408 100644 --- a/src/test/abilities/costar.test.ts +++ b/src/test/abilities/costar.test.ts @@ -8,7 +8,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Abilities - COSTAR", () => { let phaserGame: Phaser.Game; @@ -59,7 +59,6 @@ describe("Abilities - COSTAR", () => { expect(leftPokemon.getStatStage(Stat.SPATK)).toBe(2); expect(rightPokemon.getStatStage(Stat.SPATK)).toBe(2); }, - TIMEOUT, ); test( @@ -83,6 +82,5 @@ describe("Abilities - COSTAR", () => { 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 index ec5ce53f4c3..7564a254dbe 100644 --- a/src/test/abilities/dancer.test.ts +++ b/src/test/abilities/dancer.test.ts @@ -7,7 +7,7 @@ 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; @@ -60,5 +60,5 @@ describe("Abilities - Dancer", () => { // 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 fa7f26d2716..0268a738c0e 100644 --- a/src/test/abilities/disguise.test.ts +++ b/src/test/abilities/disguise.test.ts @@ -7,7 +7,7 @@ import { Stat } from "#enums/stat"; import GameManager from "#test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Abilities - Disguise", () => { let phaserGame: Phaser.Game; @@ -33,7 +33,7 @@ describe("Abilities - Disguise", () => { .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.classicMode.startBattle(); @@ -50,7 +50,7 @@ describe("Abilities - Disguise", () => { 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.classicMode.startBattle(); @@ -64,7 +64,7 @@ describe("Abilities - Disguise", () => { 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 ]); @@ -88,7 +88,7 @@ describe("Abilities - Disguise", () => { 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.classicMode.startBattle(); @@ -104,7 +104,7 @@ describe("Abilities - Disguise", () => { expect(mimikyu.status?.effect).toBe(StatusEffect.POISON); 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([Moves.SHADOW_SNEAK]); @@ -129,7 +129,7 @@ describe("Abilities - Disguise", () => { await game.phaseInterceptor.to("TurnEndPhase"); expect(mimikyu.formIndex).toBe(bustedForm); - }, TIMEOUT); + }); it("persists form change when wave changes with no arena reset", async () => { game.override.starterSpecies(0); @@ -146,7 +146,7 @@ describe("Abilities - Disguise", () => { await game.toNextWave(); expect(mimikyu.formIndex).toBe(bustedForm); - }, TIMEOUT); + }); it("reverts to Disguised form on arena reset", async () => { game.override.startingWave(4); @@ -166,7 +166,7 @@ describe("Abilities - Disguise", () => { await game.toNextWave(); expect(mimikyu.formIndex).toBe(disguisedForm); - }, TIMEOUT); + }); it("reverts to Disguised form on biome change when fainted", async () => { game.override.startingWave(10); @@ -190,7 +190,7 @@ describe("Abilities - Disguise", () => { 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([Moves.ENDURE]); @@ -204,7 +204,7 @@ 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); @@ -222,5 +222,5 @@ describe("Abilities - Disguise", () => { expect(mimikyu.formIndex).toBe(bustedForm); expect(mimikyu.hp).toBe(maxHp - disguiseDamage); - }, TIMEOUT); + }); }); diff --git a/src/test/abilities/galvanize.test.ts b/src/test/abilities/galvanize.test.ts index f81b854180a..1b7dde9ba60 100644 --- a/src/test/abilities/galvanize.test.ts +++ b/src/test/abilities/galvanize.test.ts @@ -9,7 +9,7 @@ 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("Abilities - Galvanize", () => { let phaserGame: Phaser.Game; @@ -59,7 +59,7 @@ describe("Abilities - Galvanize", () => { expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.EFFECTIVE); expect(move.calculateBattlePower).toHaveReturnedWith(48); expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); - }, TIMEOUT); + }); it("should cause Normal-type attacks to activate Volt Absorb", async () => { game.override.enemyAbility(Abilities.VOLT_ABSORB); @@ -81,7 +81,7 @@ describe("Abilities - Galvanize", () => { expect(playerPokemon.getMoveType).toHaveLastReturnedWith(Type.ELECTRIC); expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.NO_EFFECT); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - }, TIMEOUT); + }); it("should not change the type of variable-type moves", async () => { game.override.enemySpecies(Species.MIGHTYENA); @@ -100,7 +100,7 @@ describe("Abilities - Galvanize", () => { expect(playerPokemon.getMoveType).not.toHaveLastReturnedWith(Type.ELECTRIC); expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.NO_EFFECT); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - }, TIMEOUT); + }); it("should affect all hits of a Normal-type multi-hit move", async () => { await game.startBattle(); @@ -128,5 +128,5 @@ describe("Abilities - Galvanize", () => { } expect(enemyPokemon.apply).not.toHaveReturnedWith(HitResult.NO_EFFECT); - }, TIMEOUT); + }); }); diff --git a/src/test/abilities/gorilla_tactics.test.ts b/src/test/abilities/gorilla_tactics.test.ts index df698194323..5e92950526e 100644 --- a/src/test/abilities/gorilla_tactics.test.ts +++ b/src/test/abilities/gorilla_tactics.test.ts @@ -10,8 +10,6 @@ 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, @@ -49,7 +47,7 @@ describe("Abilities - Gorilla Tactics", () => { // 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]); @@ -79,5 +77,5 @@ describe("Abilities - Gorilla Tactics", () => { await game.phaseInterceptor.to("MoveEndPhase"); expect(darmanitan.hp).toBeLessThan(darmanitan.getMaxHp()); - }, TIMEOUT); + }); }); diff --git a/src/test/abilities/libero.test.ts b/src/test/abilities/libero.test.ts index 51f182d5401..f429d9ffc72 100644 --- a/src/test/abilities/libero.test.ts +++ b/src/test/abilities/libero.test.ts @@ -12,7 +12,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Abilities - Libero", () => { let phaserGame: Phaser.Game; @@ -52,7 +52,6 @@ describe("Abilities - Libero", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); }, - TIMEOUT, ); test.skip( @@ -92,7 +91,6 @@ describe("Abilities - Libero", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); }, - TIMEOUT, ); test( @@ -115,7 +113,6 @@ describe("Abilities - Libero", () => { moveType = Type[Type.FIRE]; expect(leadPokemonType).toBe(moveType); }, - TIMEOUT, ); test( @@ -138,7 +135,6 @@ describe("Abilities - Libero", () => { moveType = Type[Type.ICE]; expect(leadPokemonType).toBe(moveType); }, - TIMEOUT, ); test( @@ -157,7 +153,6 @@ describe("Abilities - Libero", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.AIR_SLASH); }, - TIMEOUT, ); test( @@ -175,7 +170,6 @@ describe("Abilities - Libero", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.DIG); }, - TIMEOUT, ); test( @@ -197,7 +191,6 @@ describe("Abilities - Libero", () => { expect(enemyPokemon.isFullHp()).toBe(true); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); }, - TIMEOUT, ); test( @@ -216,7 +209,6 @@ describe("Abilities - Libero", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); }, - TIMEOUT, ); test( @@ -235,7 +227,6 @@ describe("Abilities - Libero", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); }, - TIMEOUT, ); test( @@ -254,7 +245,6 @@ describe("Abilities - Libero", () => { expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); }, - TIMEOUT, ); test( @@ -274,7 +264,6 @@ describe("Abilities - Libero", () => { expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); }, - TIMEOUT, ); test( @@ -292,7 +281,6 @@ describe("Abilities - Libero", () => { expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); }, - TIMEOUT, ); test( @@ -310,7 +298,6 @@ describe("Abilities - Libero", () => { expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); }, - TIMEOUT, ); test( @@ -329,7 +316,6 @@ describe("Abilities - Libero", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TRICK_OR_TREAT); }, - TIMEOUT, ); test( @@ -348,7 +334,6 @@ describe("Abilities - Libero", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.CURSE); expect(leadPokemon.getTag(BattlerTagType.CURSED)).not.toBe(undefined); }, - TIMEOUT, ); }); diff --git a/src/test/abilities/magic_guard.test.ts b/src/test/abilities/magic_guard.test.ts index 4b3fb0ba985..dd8b83f7601 100644 --- a/src/test/abilities/magic_guard.test.ts +++ b/src/test/abilities/magic_guard.test.ts @@ -11,8 +11,6 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -const TIMEOUT = 20 * 1000; // 20 sec timeout - describe("Abilities - Magic Guard", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -67,7 +65,7 @@ describe("Abilities - Magic Guard", () => { */ expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); - }, TIMEOUT + } ); it( @@ -91,7 +89,7 @@ describe("Abilities - Magic Guard", () => { */ expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); expect(getStatusEffectCatchRateMultiplier(leadPokemon.status!.effect)).toBe(1.5); - }, TIMEOUT + } ); it( @@ -113,7 +111,7 @@ describe("Abilities - Magic Guard", () => { * - The player Pokemon (that just lost its Magic Guard ability) has taken damage from poison */ expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); - }, TIMEOUT + } ); @@ -138,7 +136,7 @@ describe("Abilities - Magic Guard", () => { */ expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); expect(getStatusEffectCatchRateMultiplier(enemyPokemon.status!.effect)).toBe(1.5); - }, TIMEOUT + } ); it("Magic Guard prevents damage caused by toxic but other non-damaging effects are still applied", @@ -166,7 +164,7 @@ describe("Abilities - Magic Guard", () => { expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); expect(enemyPokemon.status!.turnCount).toBeGreaterThan(toxicStartCounter); expect(getStatusEffectCatchRateMultiplier(enemyPokemon.status!.effect)).toBe(1.5); - }, TIMEOUT + } ); @@ -191,7 +189,7 @@ describe("Abilities - Magic Guard", () => { */ expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); - }, TIMEOUT + } ); it("Magic Guard does not prevent poison from Toxic Spikes", async () => { @@ -220,7 +218,7 @@ describe("Abilities - Magic Guard", () => { expect(enemyPokemon.status!.effect).toBe(StatusEffect.POISON); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); - }, TIMEOUT + } ); it("Magic Guard prevents against damage from volatile status effects", @@ -246,7 +244,7 @@ describe("Abilities - Magic Guard", () => { expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); expect(enemyPokemon.getTag(BattlerTagType.CURSED)).not.toBe(undefined); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - }, TIMEOUT + } ); it("Magic Guard prevents crash damage", async () => { @@ -265,7 +263,7 @@ describe("Abilities - Magic Guard", () => { * - The player Pokemon (with Magic Guard) misses High Jump Kick but does not lose HP as a result */ expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - }, TIMEOUT + } ); it("Magic Guard prevents damage from recoil", async () => { @@ -283,7 +281,7 @@ describe("Abilities - Magic Guard", () => { * - The player Pokemon (with Magic Guard) uses a recoil move but does not lose HP from recoil */ expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - }, TIMEOUT + } ); it("Magic Guard does not prevent damage from Struggle's recoil", async () => { @@ -301,7 +299,7 @@ describe("Abilities - Magic Guard", () => { * - The player Pokemon (with Magic Guard) uses Struggle but does lose HP from Struggle's recoil */ expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); - }, TIMEOUT + } ); //This tests different move attributes than the recoil tests above @@ -320,7 +318,7 @@ describe("Abilities - Magic Guard", () => { * - The player Pokemon (with Magic Guard) uses a move with an HP cost but does not lose HP from using it */ expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - }, TIMEOUT + } ); /* @@ -348,7 +346,7 @@ describe("Abilities - Magic Guard", () => { * - The player Pokemon (with Magic Guard) uses a non-attacking move with an HP cost and thus loses HP from using it */ expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); - }, TIMEOUT + } ); it("Magic Guard prevents damage from abilities with PostTurnHurtIfSleepingAbAttr", async () => { @@ -373,7 +371,7 @@ describe("Abilities - Magic Guard", () => { */ expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); expect(leadPokemon.status!.effect).toBe(StatusEffect.SLEEP); - }, TIMEOUT + } ); it("Magic Guard prevents damage from abilities with PostFaintContactDamageAbAttr", async () => { @@ -398,7 +396,7 @@ describe("Abilities - Magic Guard", () => { */ expect(enemyPokemon.hp).toBe(0); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - }, TIMEOUT + } ); it("Magic Guard prevents damage from abilities with PostDefendContactDamageAbAttr", async () => { @@ -422,7 +420,7 @@ describe("Abilities - Magic Guard", () => { */ expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - }, TIMEOUT + } ); it("Magic Guard prevents damage from abilities with ReverseDrainAbAttr", async () => { @@ -446,7 +444,7 @@ describe("Abilities - Magic Guard", () => { */ expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - }, TIMEOUT + } ); it("Magic Guard prevents HP loss from abilities with PostWeatherLapseDamageAbAttr", async () => { @@ -464,6 +462,6 @@ describe("Abilities - Magic Guard", () => { * - The player Pokemon (with Magic Guard) should not lose HP due to this ability attribute */ expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - }, TIMEOUT + } ); }); diff --git a/src/test/abilities/parental_bond.test.ts b/src/test/abilities/parental_bond.test.ts index 2ad3f9e3f5c..22c9d8028be 100644 --- a/src/test/abilities/parental_bond.test.ts +++ b/src/test/abilities/parental_bond.test.ts @@ -10,7 +10,7 @@ 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 - Parental Bond", () => { let phaserGame: Phaser.Game; @@ -62,7 +62,7 @@ describe("Abilities - Parental Bond", () => { expect(leadPokemon.turnData.hitCount).toBe(2); expect(secondStrikeDamage).toBe(toDmgValue(0.25 * firstStrikeDamage)); - }, TIMEOUT + } ); it( @@ -81,7 +81,7 @@ describe("Abilities - Parental Bond", () => { expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.getStatStage(Stat.ATK)).toBe(2); - }, TIMEOUT + } ); it( @@ -98,7 +98,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); - }, TIMEOUT + } ); it( @@ -116,7 +116,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.turnData.hitCount).toBe(2); - }, TIMEOUT + } ); it( @@ -133,7 +133,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("DamagePhase", false); expect(leadPokemon.turnData.hitCount).toBe(1); - }, TIMEOUT + } ); it( @@ -151,7 +151,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("DamagePhase", false); expect(leadPokemon.turnData.hitCount).toBe(1); - }, TIMEOUT + } ); it( @@ -167,7 +167,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - 80); - }, TIMEOUT + } ); it( @@ -189,7 +189,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - 4 * playerDamage); - }, TIMEOUT + } ); it( @@ -209,7 +209,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("BerryPhase", false); playerPokemon.forEach(p => expect(p.turnData.hitCount).toBe(1)); - }, TIMEOUT + } ); it( @@ -225,7 +225,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("DamagePhase", false); expect(leadPokemon.turnData.hitCount).toBe(2); - }, TIMEOUT + } ); it( @@ -247,7 +247,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.hp).toBe(Math.ceil(leadPokemon.getMaxHp() / 2)); - }, TIMEOUT + } ); it( @@ -271,7 +271,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.isOfType(Type.FIRE)).toBe(false); - }, TIMEOUT + } ); it( @@ -289,7 +289,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("DamagePhase"); expect(leadPokemon.turnData.hitCount).toBe(4); - }, TIMEOUT + } ); it( @@ -313,7 +313,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("MoveEndPhase", false); expect(enemyPokemon.hp).toBe(Math.ceil(enemyPokemon.getMaxHp() * 0.25)); - }, TIMEOUT + } ); it( @@ -339,7 +339,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("MoveEndPhase", false); expect(enemyPokemon.hp).toBe(enemyStartingHp - 200); - }, TIMEOUT + } ); it( @@ -362,7 +362,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("TurnEndPhase"); expect(leadPokemon.getTag(BattlerTagType.RECHARGING)).toBeDefined(); - }, TIMEOUT + } ); it( @@ -389,7 +389,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("TurnEndPhase"); expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); - }, TIMEOUT + } ); it( @@ -413,7 +413,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("TurnEndPhase"); expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); - }, TIMEOUT + } ); it( @@ -433,7 +433,7 @@ describe("Abilities - Parental Bond", () => { // This will cause this test to time out if the switch was forced on the first hit. await game.phaseInterceptor.to("MoveEffectPhase", false); - }, TIMEOUT + } ); it( @@ -457,7 +457,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.status?.effect).toBeUndefined(); - }, TIMEOUT + } ); it( @@ -475,7 +475,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.getStatStage(Stat.ATK)).toBe(-1); - }, TIMEOUT + } ); it( @@ -493,7 +493,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(1); - }, TIMEOUT + } ); it( @@ -519,6 +519,6 @@ describe("Abilities - Parental Bond", () => { 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/power_construct.test.ts b/src/test/abilities/power_construct.test.ts index ec37bc96c2f..94cee82fb4a 100644 --- a/src/test/abilities/power_construct.test.ts +++ b/src/test/abilities/power_construct.test.ts @@ -7,7 +7,7 @@ import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Abilities - POWER CONSTRUCT", () => { let phaserGame: Phaser.Game; @@ -60,6 +60,5 @@ describe("Abilities - POWER CONSTRUCT", () => { expect(zygarde!.formIndex).toBe(baseForm); }, - TIMEOUT ); }); diff --git a/src/test/abilities/protean.test.ts b/src/test/abilities/protean.test.ts index 4be58a677a6..8479a293722 100644 --- a/src/test/abilities/protean.test.ts +++ b/src/test/abilities/protean.test.ts @@ -12,7 +12,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Abilities - Protean", () => { let phaserGame: Phaser.Game; @@ -52,7 +52,6 @@ describe("Abilities - Protean", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); }, - TIMEOUT, ); test.skip( @@ -92,7 +91,6 @@ describe("Abilities - Protean", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); }, - TIMEOUT, ); test( @@ -115,7 +113,6 @@ describe("Abilities - Protean", () => { moveType = Type[Type.FIRE]; expect(leadPokemonType).toBe(moveType); }, - TIMEOUT, ); test( @@ -138,7 +135,6 @@ describe("Abilities - Protean", () => { moveType = Type[Type.ICE]; expect(leadPokemonType).toBe(moveType); }, - TIMEOUT, ); test( @@ -157,7 +153,6 @@ describe("Abilities - Protean", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.AIR_SLASH); }, - TIMEOUT, ); test( @@ -175,7 +170,6 @@ describe("Abilities - Protean", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.DIG); }, - TIMEOUT, ); test( @@ -197,7 +191,6 @@ describe("Abilities - Protean", () => { expect(enemyPokemon.isFullHp()).toBe(true); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); }, - TIMEOUT, ); test( @@ -216,7 +209,6 @@ describe("Abilities - Protean", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); }, - TIMEOUT, ); test( @@ -235,7 +227,6 @@ describe("Abilities - Protean", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); }, - TIMEOUT, ); test( @@ -254,7 +245,6 @@ describe("Abilities - Protean", () => { expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.PROTEAN); }, - TIMEOUT, ); test( @@ -274,7 +264,6 @@ describe("Abilities - Protean", () => { expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.PROTEAN); }, - TIMEOUT, ); test( @@ -292,7 +281,6 @@ describe("Abilities - Protean", () => { expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.PROTEAN); }, - TIMEOUT, ); test( @@ -310,7 +298,6 @@ describe("Abilities - Protean", () => { expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.PROTEAN); }, - TIMEOUT, ); test( @@ -329,7 +316,6 @@ describe("Abilities - Protean", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TRICK_OR_TREAT); }, - TIMEOUT, ); test( @@ -348,7 +334,6 @@ describe("Abilities - Protean", () => { testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.CURSE); expect(leadPokemon.getTag(BattlerTagType.CURSED)).not.toBe(undefined); }, - TIMEOUT, ); }); diff --git a/src/test/abilities/quick_draw.test.ts b/src/test/abilities/quick_draw.test.ts index a02ee5cf56a..dbed6c822c4 100644 --- a/src/test/abilities/quick_draw.test.ts +++ b/src/test/abilities/quick_draw.test.ts @@ -55,7 +55,6 @@ describe("Abilities - Quick Draw", () => { }, 20000); test("does not triggered by non damage moves", { - timeout: 20000, retry: 5 }, async () => { await game.startBattle(); diff --git a/src/test/abilities/sand_veil.test.ts b/src/test/abilities/sand_veil.test.ts index da9fdcc01ab..201d8d89600 100644 --- a/src/test/abilities/sand_veil.test.ts +++ b/src/test/abilities/sand_veil.test.ts @@ -11,7 +11,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Abilities - Sand Veil", () => { let phaserGame: Phaser.Game; @@ -75,6 +75,6 @@ describe("Abilities - Sand Veil", () => { expect(leadPokemon[0].isFullHp()).toBe(true); expect(leadPokemon[1].hp).toBeLessThan(leadPokemon[1].getMaxHp()); - }, TIMEOUT + } ); }); diff --git a/src/test/abilities/schooling.test.ts b/src/test/abilities/schooling.test.ts index ad9663bf8e5..4c5a66a41b7 100644 --- a/src/test/abilities/schooling.test.ts +++ b/src/test/abilities/schooling.test.ts @@ -7,7 +7,7 @@ import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Abilities - SCHOOLING", () => { let phaserGame: Phaser.Game; @@ -60,6 +60,5 @@ describe("Abilities - SCHOOLING", () => { expect(wishiwashi.formIndex).toBe(soloForm); }, - TIMEOUT ); }); diff --git a/src/test/abilities/shields_down.test.ts b/src/test/abilities/shields_down.test.ts index 9bfec23ddf1..411c23fc652 100644 --- a/src/test/abilities/shields_down.test.ts +++ b/src/test/abilities/shields_down.test.ts @@ -7,7 +7,7 @@ import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Abilities - SHIELDS DOWN", () => { let phaserGame: Phaser.Game; @@ -60,6 +60,5 @@ describe("Abilities - SHIELDS DOWN", () => { expect(minior.formIndex).toBe(meteorForm); }, - TIMEOUT ); }); diff --git a/src/test/abilities/sturdy.test.ts b/src/test/abilities/sturdy.test.ts index dc9f774cc5b..c329d0830d3 100644 --- a/src/test/abilities/sturdy.test.ts +++ b/src/test/abilities/sturdy.test.ts @@ -8,7 +8,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Abilities - Sturdy", () => { let phaserGame: Phaser.Game; @@ -45,7 +45,6 @@ describe("Abilities - Sturdy", () => { await game.phaseInterceptor.to(MoveEndPhase); expect(game.scene.getEnemyParty()[0].hp).toBe(1); }, - TIMEOUT ); test( @@ -62,7 +61,6 @@ describe("Abilities - Sturdy", () => { expect(enemyPokemon.hp).toBe(0); expect(enemyPokemon.isFainted()).toBe(true); }, - TIMEOUT ); test( @@ -75,7 +73,6 @@ describe("Abilities - Sturdy", () => { const enemyPokemon: EnemyPokemon = game.scene.getEnemyParty()[0]; expect(enemyPokemon.isFullHp()).toBe(true); }, - TIMEOUT ); test( @@ -91,7 +88,6 @@ describe("Abilities - Sturdy", () => { expect(enemyPokemon.hp).toBe(0); expect(enemyPokemon.isFainted()).toBe(true); }, - TIMEOUT ); }); diff --git a/src/test/abilities/tera_shell.test.ts b/src/test/abilities/tera_shell.test.ts index 2826469f3bf..13df49136ca 100644 --- a/src/test/abilities/tera_shell.test.ts +++ b/src/test/abilities/tera_shell.test.ts @@ -6,8 +6,6 @@ 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; @@ -54,7 +52,7 @@ describe("Abilities - Tera Shell", () => { await game.phaseInterceptor.to("MoveEndPhase"); expect(playerPokemon.getMoveEffectiveness).toHaveLastReturnedWith(2); - }, TIMEOUT + } ); it( @@ -71,7 +69,7 @@ describe("Abilities - Tera Shell", () => { await game.phaseInterceptor.to("MoveEndPhase"); expect(playerPokemon.getMoveEffectiveness).toHaveLastReturnedWith(0); - }, TIMEOUT + } ); it( @@ -88,7 +86,7 @@ describe("Abilities - Tera Shell", () => { await game.phaseInterceptor.to("MoveEndPhase"); expect(playerPokemon.getMoveEffectiveness).toHaveLastReturnedWith(0.25); - }, TIMEOUT + } ); it( @@ -106,6 +104,6 @@ describe("Abilities - Tera Shell", () => { 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/unseen_fist.test.ts b/src/test/abilities/unseen_fist.test.ts index 813880c7326..0f285abd98f 100644 --- a/src/test/abilities/unseen_fist.test.ts +++ b/src/test/abilities/unseen_fist.test.ts @@ -8,7 +8,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { BerryPhase } from "#app/phases/berry-phase"; -const TIMEOUT = 20 * 1000; + describe("Abilities - Unseen Fist", () => { let phaserGame: Phaser.Game; @@ -37,13 +37,11 @@ describe("Abilities - Unseen Fist", () => { it( "should cause a contact move to ignore Protect", () => testUnseenFistHitResult(game, Moves.QUICK_ATTACK, Moves.PROTECT, true), - TIMEOUT ); it( "should not cause a non-contact move to ignore Protect", () => testUnseenFistHitResult(game, Moves.ABSORB, Moves.PROTECT, false), - TIMEOUT ); it( @@ -51,19 +49,17 @@ describe("Abilities - Unseen Fist", () => { () => { game.override.passiveAbility(Abilities.LONG_REACH); testUnseenFistHitResult(game, Moves.QUICK_ATTACK, Moves.PROTECT, false); - }, TIMEOUT + } ); it( "should cause a contact move to ignore Wide Guard", () => testUnseenFistHitResult(game, Moves.BREAKING_SWIPE, Moves.WIDE_GUARD, true), - TIMEOUT ); it( "should not cause a non-contact move to ignore Wide Guard", () => testUnseenFistHitResult(game, Moves.BULLDOZE, Moves.WIDE_GUARD, false), - TIMEOUT ); it( @@ -83,7 +79,7 @@ describe("Abilities - Unseen Fist", () => { expect(enemyPokemon.getTag(BattlerTagType.SUBSTITUTE)).toBeUndefined(); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - }, TIMEOUT + } ); }); diff --git a/src/test/abilities/zen_mode.test.ts b/src/test/abilities/zen_mode.test.ts index fd378647184..c7cbd9014e0 100644 --- a/src/test/abilities/zen_mode.test.ts +++ b/src/test/abilities/zen_mode.test.ts @@ -19,7 +19,7 @@ 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; + describe("Abilities - ZEN MODE", () => { let phaserGame: Phaser.Game; @@ -67,7 +67,6 @@ describe("Abilities - ZEN MODE", () => { expect(game.scene.getParty()[0].hp).toBeLessThan(100); expect(game.scene.getParty()[0].formIndex).toBe(0); }, - TIMEOUT ); test( @@ -87,7 +86,6 @@ describe("Abilities - ZEN MODE", () => { expect(game.scene.getParty()[0].hp).not.toBe(100); expect(game.scene.getParty()[0].formIndex).not.toBe(0); }, - TIMEOUT ); test( @@ -125,7 +123,6 @@ describe("Abilities - ZEN MODE", () => { await game.phaseInterceptor.to(PostSummonPhase); expect(game.scene.getParty()[1].formIndex).toBe(1); }, - TIMEOUT ); test( @@ -156,6 +153,5 @@ describe("Abilities - ZEN MODE", () => { expect(darmanitan.formIndex).toBe(baseForm); }, - TIMEOUT ); }); diff --git a/src/test/abilities/zero_to_hero.test.ts b/src/test/abilities/zero_to_hero.test.ts index eafc32b4c79..a7f7c970218 100644 --- a/src/test/abilities/zero_to_hero.test.ts +++ b/src/test/abilities/zero_to_hero.test.ts @@ -7,7 +7,7 @@ import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Abilities - ZERO TO HERO", () => { let phaserGame: Phaser.Game; @@ -59,7 +59,7 @@ describe("Abilities - ZERO TO HERO", () => { expect(palafin1.formIndex).toBe(baseForm); expect(palafin2.formIndex).toBe(baseForm); - }, TIMEOUT); + }); it("should swap to Hero form when switching out during a battle", async () => { await game.startBattle([Species.PALAFIN, Species.FEEBAS]); @@ -70,7 +70,7 @@ describe("Abilities - ZERO TO HERO", () => { game.doSwitchPokemon(1); await game.phaseInterceptor.to(QuietFormChangePhase); expect(palafin.formIndex).toBe(heroForm); - }, TIMEOUT); + }); it("should not swap to Hero form if switching due to faint", async () => { await game.startBattle([Species.PALAFIN, Species.FEEBAS]); @@ -83,7 +83,7 @@ describe("Abilities - ZERO TO HERO", () => { game.doSelectPartyPokemon(1); await game.toNextTurn(); expect(palafin.formIndex).toBe(baseForm); - }, TIMEOUT); + }); it("should stay hero form if fainted and then revived", async () => { game.override.starterForms({ @@ -105,5 +105,5 @@ describe("Abilities - ZERO TO HERO", () => { await game.toNextTurn(); expect(palafin.formIndex).toBe(heroForm); - }, TIMEOUT); + }); }); diff --git a/src/test/arena/grassy_terrain.test.ts b/src/test/arena/grassy_terrain.test.ts new file mode 100644 index 00000000000..01bbc778ded --- /dev/null +++ b/src/test/arena/grassy_terrain.test.ts @@ -0,0 +1,69 @@ +import { allMoves } from "#app/data/move"; +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, vi } from "vitest"; + +describe("Arena - Grassy Terrain", () => { + 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() + .enemyLevel(1) + .enemySpecies(Species.SHUCKLE) + .enemyAbility(Abilities.STURDY) + .enemyMoveset(Moves.FLY) + .moveset([Moves.GRASSY_TERRAIN, Moves.EARTHQUAKE]) + .ability(Abilities.NO_GUARD); + }); + + it("halves the damage of Earthquake", async () => { + await game.classicMode.startBattle([Species.TAUROS]); + + const eq = allMoves[Moves.EARTHQUAKE]; + vi.spyOn(eq, "calculateBattlePower"); + + game.move.select(Moves.EARTHQUAKE); + await game.toNextTurn(); + + expect(eq.calculateBattlePower).toHaveReturnedWith(100); + + game.move.select(Moves.GRASSY_TERRAIN); + await game.toNextTurn(); + + game.move.select(Moves.EARTHQUAKE); + await game.phaseInterceptor.to("BerryPhase"); + + expect(eq.calculateBattlePower).toHaveReturnedWith(50); + }); + + it("Does not halve the damage of Earthquake if opponent is not grounded", async () => { + await game.classicMode.startBattle([Species.NINJASK]); + + const eq = allMoves[Moves.EARTHQUAKE]; + vi.spyOn(eq, "calculateBattlePower"); + + game.move.select(Moves.GRASSY_TERRAIN); + await game.toNextTurn(); + + game.move.select(Moves.EARTHQUAKE); + await game.phaseInterceptor.to("BerryPhase"); + + expect(eq.calculateBattlePower).toHaveReturnedWith(100); + }); +}); diff --git a/src/test/battle/battle.test.ts b/src/test/battle/battle.test.ts index 6e15bbd99d9..554692374d2 100644 --- a/src/test/battle/battle.test.ts +++ b/src/test/battle/battle.test.ts @@ -24,7 +24,8 @@ import { Moves } from "#enums/moves"; 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 { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { Biome } from "#app/enums/biome"; describe("Test Battle Phase", () => { let phaserGame: Phaser.Game; @@ -290,22 +291,27 @@ describe("Test Battle Phase", () => { expect(game.scene.currentBattle.turn).toBeGreaterThan(turn); }, 20000); - it("to next wave with pokemon killed, single", async () => { + it("does not set new weather if staying in same biome", async () => { const moveToUse = Moves.SPLASH; - game.override.battleType("single"); - game.override.starterSpecies(Species.MEWTWO); - game.override.enemySpecies(Species.RATTATA); - game.override.enemyAbility(Abilities.HYDRATION); - game.override.ability(Abilities.ZEN_MODE); - game.override.startingLevel(2000); - game.override.startingWave(3); - game.override.moveset([moveToUse]); + game.override + .battleType("single") + .starterSpecies(Species.MEWTWO) + .enemySpecies(Species.RATTATA) + .enemyAbility(Abilities.HYDRATION) + .ability(Abilities.ZEN_MODE) + .startingLevel(2000) + .startingWave(3) + .startingBiome(Biome.LAKE) + .moveset([moveToUse]); game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); - await game.startBattle(); + await game.classicMode.startBattle(); const waveIndex = game.scene.currentBattle.waveIndex; game.move.select(moveToUse); + + vi.spyOn(game.scene.arena, "trySetWeather"); await game.doKillOpponents(); await game.toNextWave(); + expect(game.scene.arena.trySetWeather).not.toHaveBeenCalled(); expect(game.scene.currentBattle.waveIndex).toBeGreaterThan(waveIndex); }, 20000); diff --git a/src/test/battle/damage_calculation.test.ts b/src/test/battle/damage_calculation.test.ts index 89f2bb4c269..a348df6c085 100644 --- a/src/test/battle/damage_calculation.test.ts +++ b/src/test/battle/damage_calculation.test.ts @@ -1,14 +1,13 @@ -import { DamagePhase } from "#app/phases/damage-phase"; -import { toDmgValue } from "#app/utils"; +import { allMoves } from "#app/data/move"; 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 Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -describe("Round Down and Minimun 1 test in Damage Calculation", () => { +describe("Battle Mechanics - Damage Calculation", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -24,24 +23,86 @@ describe("Round Down and Minimun 1 test in Damage Calculation", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); - game.override.startingLevel(10); + game.override + .battleType("single") + .enemySpecies(Species.SNORLAX) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .startingLevel(100) + .enemyLevel(100) + .disableCrits() + .moveset([Moves.TACKLE, Moves.DRAGON_RAGE, Moves.FISSURE, Moves.JUMP_KICK]); + }); + + it("Tackle deals expected base damage", async () => { + await game.classicMode.startBattle([Species.CHARIZARD]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(playerPokemon, "getEffectiveStat").mockReturnValue(80); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + vi.spyOn(enemyPokemon, "getEffectiveStat").mockReturnValue(90); + + // expected base damage = [(2*level/5 + 2) * power * playerATK / enemyDEF / 50] + 2 + // = 31.8666... + expect(enemyPokemon.getAttackDamage(playerPokemon, allMoves[Moves.TACKLE]).damage).toBeCloseTo(31); + }); + + it("Attacks deal 1 damage at minimum", async () => { + game.override + .startingLevel(1) + .enemySpecies(Species.AGGRON); + + await game.classicMode.startBattle([Species.MAGIKARP]); + + const aggron = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.TACKLE); + + await game.phaseInterceptor.to("BerryPhase", false); + + // Lvl 1 0 Atk Magikarp Tackle vs. 0 HP / 0 Def Aggron: 1-1 (0.3 - 0.3%) -- possibly the worst move ever + expect(aggron.hp).toBe(aggron.getMaxHp() - 1); + }); + + it("Fixed-damage moves ignore damage multipliers", async () => { + game.override + .enemySpecies(Species.DRAGONITE) + .enemyAbility(Abilities.MULTISCALE); + + await game.classicMode.startBattle([Species.MAGIKARP]); + + const magikarp = game.scene.getPlayerPokemon()!; + const dragonite = game.scene.getEnemyPokemon()!; + + expect(dragonite.getAttackDamage(magikarp, allMoves[Moves.DRAGON_RAGE]).damage).toBe(40); + }); + + it("One-hit KO moves ignore damage multipliers", async () => { + game.override + .enemySpecies(Species.AGGRON) + .enemyAbility(Abilities.MULTISCALE); + + await game.classicMode.startBattle([Species.MAGIKARP]); + + const magikarp = game.scene.getPlayerPokemon()!; + const aggron = game.scene.getEnemyPokemon()!; + + expect(aggron.getAttackDamage(magikarp, allMoves[Moves.FISSURE]).damage).toBe(aggron.hp); }); 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(Moves.SPLASH); - game.override.starterSpecies(Species.SHEDINJA); - game.override.moveset([Moves.JUMP_KICK]); - game.override.ability(Abilities.WONDER_GUARD); + game.override + .enemySpecies(Species.GASTLY) + .ability(Abilities.WONDER_GUARD); - await game.startBattle(); + await game.classicMode.startBattle([Species.SHEDINJA]); const shedinja = game.scene.getPlayerPokemon()!; game.move.select(Moves.JUMP_KICK); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); expect(shedinja.hp).toBe(shedinja.getMaxHp() - 1); }); @@ -49,21 +110,19 @@ describe("Round Down and Minimun 1 test in Damage Calculation", () => { it("Charizard with odd HP survives Stealth Rock damage twice", async () => { game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0); - game.override.seed("Charizard Stealth Rock test"); - game.override.enemySpecies(Species.CHARIZARD); - game.override.enemyAbility(Abilities.BLAZE); - game.override.starterSpecies(Species.PIKACHU); - game.override.enemyLevel(100); + game.override + .seed("Charizard Stealth Rock test") + .enemySpecies(Species.CHARIZARD) + .enemyAbility(Abilities.BLAZE); - await game.startBattle(); + await game.classicMode.startBattle([Species.PIKACHU]); const charizard = game.scene.getEnemyPokemon()!; - const maxHp = charizard.getMaxHp(); - const damage_prediction = toDmgValue(charizard.getMaxHp() / 2); - const currentHp = charizard.hp; - const expectedHP = maxHp - damage_prediction; - - expect(currentHp).toBe(expectedHP); + if (charizard.getMaxHp() % 2 === 1) { + expect(charizard.hp).toBeGreaterThan(charizard.getMaxHp() / 2); + } else { + expect(charizard.hp).toBe(charizard.getMaxHp() / 2); + } }); }); diff --git a/src/test/battle/inverse_battle.test.ts b/src/test/battle/inverse_battle.test.ts index d808f71addb..01a0348e730 100644 --- a/src/test/battle/inverse_battle.test.ts +++ b/src/test/battle/inverse_battle.test.ts @@ -10,7 +10,7 @@ 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("Inverse Battle", () => { let phaserGame: Phaser.Game; @@ -56,7 +56,7 @@ describe("Inverse Battle", () => { await game.phaseInterceptor.to("MoveEffectPhase"); expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2); - }, TIMEOUT); + }); it("2x effective types are 0.5x effective - Thunderbolt against Flying Type", async () => { game.override @@ -73,7 +73,7 @@ describe("Inverse Battle", () => { await game.phaseInterceptor.to("MoveEffectPhase"); expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(0.5); - }, TIMEOUT); + }); it("0.5x effective types are 2x effective - Thunderbolt against Electric Type", async () => { game.override @@ -90,7 +90,7 @@ describe("Inverse Battle", () => { await game.phaseInterceptor.to("MoveEffectPhase"); expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2); - }, TIMEOUT); + }); 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); @@ -110,7 +110,7 @@ describe("Inverse Battle", () => { console.log("Charizard's max HP: " + maxHp, "Damage: " + damage_prediction, "Current HP: " + currentHp, "Expected HP: " + expectedHP); expect(currentHp).toBeGreaterThan(maxHp * 31 / 32 - 1); - }, TIMEOUT); + }); it("Freeze Dry is 2x effective against Water Type like other Ice type Move - Freeze Dry against Squirtle", async () => { game.override @@ -127,7 +127,7 @@ describe("Inverse Battle", () => { await game.phaseInterceptor.to("MoveEffectPhase"); expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2); - }, TIMEOUT); + }); it("Water Absorb should heal against water moves - Water Absorb against Water gun", async () => { game.override @@ -143,7 +143,7 @@ describe("Inverse Battle", () => { await game.phaseInterceptor.to("MoveEndPhase"); expect(enemy.hp).toBe(enemy.getMaxHp()); - }, TIMEOUT); + }); it("Fire type does not get burned - Will-O-Wisp against Charmander", async () => { game.override @@ -160,7 +160,7 @@ describe("Inverse Battle", () => { await game.phaseInterceptor.to("MoveEndPhase"); expect(enemy.status?.effect).not.toBe(StatusEffect.BURN); - }, TIMEOUT); + }); it("Electric type does not get paralyzed - Nuzzle against Pikachu", async () => { game.override @@ -177,7 +177,7 @@ describe("Inverse Battle", () => { 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 @@ -194,7 +194,7 @@ describe("Inverse Battle", () => { 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 () => { @@ -206,7 +206,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); expect(game.scene.getEnemyPokemon()?.summonData.abilitiesApplied[0]).toBe(Abilities.ANTICIPATION); - }, TIMEOUT); + }); it("Conversion 2 should change the type to the resistive type - Conversion 2 against Dragonite", async () => { game.override @@ -223,7 +223,7 @@ describe("Inverse Battle", () => { await game.phaseInterceptor.to("TurnEndPhase"); expect(player.getTypes()[0]).toBe(Type.DRAGON); - }, TIMEOUT); + }); it("Flying Press should be 0.25x effective against Grass + Dark Type - Flying Press against Meowscarada", async () => { game.override @@ -240,7 +240,7 @@ describe("Inverse Battle", () => { 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 @@ -258,7 +258,7 @@ describe("Inverse Battle", () => { 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 @@ -279,5 +279,5 @@ describe("Inverse Battle", () => { 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 7b1f9264370..ebd92dc6401 100644 --- a/src/test/battlerTags/octolock.test.ts +++ b/src/test/battlerTags/octolock.test.ts @@ -10,7 +10,7 @@ vi.mock("#app/battle-scene.js"); describe("BattlerTag - OctolockTag", () => { describe("lapse behavior", () => { - it("unshifts a StatStageChangePhase with expected stat stage changes", { timeout: 10000 }, async () => { + it("unshifts a StatStageChangePhase with expected stat stage changes", async () => { const mockPokemon = { scene: new BattleScene(), getBattlerIndex: () => 0, @@ -30,11 +30,11 @@ describe("BattlerTag - OctolockTag", () => { }); }); - it ("traps its target (extends TrappedTag)", { timeout: 2000 }, async () => { + it ("traps its target (extends TrappedTag)", async () => { expect(new OctolockTag(1)).toBeInstanceOf(TrappedTag); }); - it("can be added to pokemon who are not octolocked", { timeout: 2000 }, async => { + it("can be added to pokemon who are not octolocked", async => { const mockPokemon = { getTag: vi.fn().mockReturnValue(undefined) as Pokemon["getTag"], } as Pokemon; @@ -47,7 +47,7 @@ describe("BattlerTag - OctolockTag", () => { expect(mockPokemon.getTag).toHaveBeenCalledWith(BattlerTagType.OCTOLOCK); }); - it("cannot be added to pokemon who are octolocked", { timeout: 2000 }, async => { + it("cannot be added to pokemon who are octolocked", async => { const mockPokemon = { getTag: vi.fn().mockReturnValue(new BattlerTag(null!, null!, null!, null!)) as Pokemon["getTag"], } as Pokemon; diff --git a/src/test/battlerTags/stockpiling.test.ts b/src/test/battlerTags/stockpiling.test.ts index e568016dfef..ae2528e7b5f 100644 --- a/src/test/battlerTags/stockpiling.test.ts +++ b/src/test/battlerTags/stockpiling.test.ts @@ -12,7 +12,7 @@ beforeEach(() => { describe("BattlerTag - StockpilingTag", () => { describe("onAdd", () => { - it("unshifts a StatStageChangePhase with expected stat stage changes on add", { timeout: 10000 }, async () => { + it("unshifts a StatStageChangePhase with expected stat stage changes on add", async () => { const mockPokemon = { scene: vi.mocked(new BattleScene()) as BattleScene, getBattlerIndex: () => 0, @@ -35,7 +35,7 @@ describe("BattlerTag - StockpilingTag", () => { expect(mockPokemon.scene.unshiftPhase).toBeCalledTimes(1); }); - it("unshifts a StatStageChangePhase 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)", async () => { const mockPokemon = { scene: new BattleScene(), summonData: new PokemonSummonData(), @@ -64,7 +64,7 @@ describe("BattlerTag - StockpilingTag", () => { }); describe("onOverlap", () => { - it("unshifts a StatStageChangePhase with expected stat changes on overlap", { timeout: 10000 }, async () => { + it("unshifts a StatStageChangePhase with expected stat changes on overlap", async () => { const mockPokemon = { scene: new BattleScene(), getBattlerIndex: () => 0, @@ -89,7 +89,7 @@ describe("BattlerTag - StockpilingTag", () => { }); describe("stack limit, stat tracking, and removal", () => { - it("can be added up to three times, even when one stat does not change", { timeout: 10000 }, async () => { + it("can be added up to three times, even when one stat does not change", async () => { const mockPokemon = { scene: new BattleScene(), summonData: new PokemonSummonData(), diff --git a/src/test/battlerTags/substitute.test.ts b/src/test/battlerTags/substitute.test.ts index 1ce81850c13..0802b549823 100644 --- a/src/test/battlerTags/substitute.test.ts +++ b/src/test/battlerTags/substitute.test.ts @@ -1,8 +1,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import Pokemon, { MoveResult, PokemonTurnData, TurnMove, PokemonMove } from "#app/field/pokemon"; import BattleScene from "#app/battle-scene"; -import { BattlerTagLapseType, SubstituteTag, TrappedTag } from "#app/data/battler-tags"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BattlerTagLapseType, BindTag, SubstituteTag } from "#app/data/battler-tags"; import { Moves } from "#app/enums/moves"; import { PokemonAnimType } from "#app/enums/pokemon-anim-type"; import * as messages from "#app/messages"; @@ -11,8 +10,6 @@ import { MoveEffectPhase } from "#app/phases/move-effect-phase"; vi.mock("#app/battle-scene.js"); -const TIMEOUT = 5 * 1000; // 5 sec timeout - describe("BattlerTag - SubstituteTag", () => { let mockPokemon: Pokemon; @@ -25,7 +22,7 @@ describe("BattlerTag - SubstituteTag", () => { getMaxHp: vi.fn().mockReturnValue(101) as Pokemon["getMaxHp"], findAndRemoveTags: vi.fn().mockImplementation((tagFilter) => { // simulate a Trapped tag set by another Pokemon, then expect the filter to catch it. - const trapTag = new TrappedTag(BattlerTagType.TRAPPED, BattlerTagLapseType.CUSTOM, 0, Moves.NONE, 1); + const trapTag = new BindTag(5, 0); expect(tagFilter(trapTag)).toBeTruthy(); return true; }) as Pokemon["findAndRemoveTags"] @@ -46,7 +43,7 @@ describe("BattlerTag - SubstituteTag", () => { subject.onAdd(mockPokemon); expect(subject.hp).toBe(25); - }, TIMEOUT + } ); it( @@ -68,7 +65,7 @@ describe("BattlerTag - SubstituteTag", () => { expect(subject.sourceInFocus).toBeFalsy(); expect(mockPokemon.scene.triggerPokemonBattleAnim).toHaveBeenCalledTimes(1); expect(mockPokemon.scene.queueMessage).toHaveBeenCalledTimes(1); - }, TIMEOUT + } ); it( @@ -80,7 +77,7 @@ describe("BattlerTag - SubstituteTag", () => { subject.onAdd(mockPokemon); expect(mockPokemon.findAndRemoveTags).toHaveBeenCalledTimes(1); - }, TIMEOUT + } ); }); @@ -115,7 +112,7 @@ describe("BattlerTag - SubstituteTag", () => { expect(mockPokemon.scene.triggerPokemonBattleAnim).toHaveBeenCalledTimes(1); expect(mockPokemon.scene.queueMessage).toHaveBeenCalledTimes(1); - }, TIMEOUT + } ); }); @@ -151,7 +148,7 @@ describe("BattlerTag - SubstituteTag", () => { expect(subject.sourceInFocus).toBeTruthy(); expect(mockPokemon.scene.triggerPokemonBattleAnim).toHaveBeenCalledTimes(1); expect(mockPokemon.scene.queueMessage).not.toHaveBeenCalled(); - }, TIMEOUT + } ); it( @@ -173,7 +170,7 @@ describe("BattlerTag - SubstituteTag", () => { expect(subject.sourceInFocus).toBeFalsy(); expect(mockPokemon.scene.triggerPokemonBattleAnim).toHaveBeenCalledTimes(1); expect(mockPokemon.scene.queueMessage).not.toHaveBeenCalled(); - }, TIMEOUT + } ); /** TODO: Figure out how to mock a MoveEffectPhase correctly for this test */ @@ -201,7 +198,7 @@ describe("BattlerTag - SubstituteTag", () => { expect(mockPokemon.scene.triggerPokemonBattleAnim).not.toHaveBeenCalled(); expect(mockPokemon.scene.queueMessage).toHaveBeenCalledTimes(1); - }, TIMEOUT + } ); it( @@ -213,7 +210,7 @@ describe("BattlerTag - SubstituteTag", () => { vi.spyOn(mockPokemon.scene, "queueMessage").mockReturnValue(); expect(subject.lapse(mockPokemon, BattlerTagLapseType.CUSTOM)).toBeFalsy(); - }, TIMEOUT + } ); it( diff --git a/src/test/boss-pokemon.test.ts b/src/test/boss-pokemon.test.ts index 8a0a0e01617..e316cac0cf6 100644 --- a/src/test/boss-pokemon.test.ts +++ b/src/test/boss-pokemon.test.ts @@ -9,8 +9,6 @@ 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; @@ -35,7 +33,7 @@ describe("Boss Pokemon / Shields", () => { .enemyMoveset(Moves.SPLASH) .enemyHeldItems([]) .startingLevel(1000) - .moveset([Moves.FALSE_SWIPE, Moves.SUPER_FANG, Moves.SPLASH]) + .moveset([Moves.FALSE_SWIPE, Moves.SUPER_FANG, Moves.SPLASH, Moves.PSYCHIC]) .ability(Abilities.NO_GUARD); }); @@ -62,7 +60,7 @@ describe("Boss Pokemon / Shields", () => { // 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 @@ -77,7 +75,7 @@ describe("Boss Pokemon / Shields", () => { 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 @@ -107,7 +105,7 @@ describe("Boss Pokemon / Shields", () => { // 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 @@ -143,7 +141,7 @@ describe("Boss Pokemon / Shields", () => { 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; @@ -196,7 +194,29 @@ describe("Boss Pokemon / Shields", () => { await game.toNextTurn(); expect(getTotalStatStageBoosts(boss2)).toBe(totalStatStages); - }, TIMEOUT); + }); + + it("the boss enduring does not proc an extra stat boost", async () => { + game.override + .enemyHealthSegments(2) + .enemyAbility(Abilities.STURDY); + + await game.classicMode.startBattle([ Species.MEWTWO ]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + expect(enemyPokemon.isBoss()).toBe(true); + expect(enemyPokemon.bossSegments).toBe(2); + expect(getTotalStatStageBoosts(enemyPokemon)).toBe(0); + + game.move.select(Moves.PSYCHIC); + await game.toNextTurn(); + + // Enemy survived with Sturdy + expect(enemyPokemon.bossSegmentIndex).toBe(0); + expect(enemyPokemon.hp).toBe(1); + expect(getTotalStatStageBoosts(enemyPokemon)).toBe(1); + + }); /** * Gets the sum of the effective stat stage boosts for the given Pokemon diff --git a/src/test/data/splash_messages.test.ts b/src/test/data/splash_messages.test.ts new file mode 100644 index 00000000000..7e07b9a6e77 --- /dev/null +++ b/src/test/data/splash_messages.test.ts @@ -0,0 +1,66 @@ +import { getSplashMessages } from "#app/data/splash-messages"; +import { describe, expect, it, vi, afterEach, beforeEach } from "vitest"; +import * as Constants from "#app/constants"; + +describe("Data - Splash Messages", () => { + it("should contain at least 15 splash messages", () => { + expect(getSplashMessages().length).toBeGreaterThanOrEqual(15); + }); + + // make sure to adjust this test if the weight it changed! + it("should add contain 10 `battlesWon` splash messages", () => { + const battlesWonMessages = getSplashMessages().filter((message) => message === "splashMessages:battlesWon"); + expect(battlesWonMessages).toHaveLength(10); + }); + + describe("Seasonal", () => { + beforeEach(() => { + vi.spyOn(Constants, "USE_SEASONAL_SPLASH_MESSAGES", "get").mockReturnValue(true); + }); + + afterEach(() => { + vi.useRealTimers(); // reset system time + }); + + it("should contain halloween messages from Sep 15 to Oct 31", () => { + testSeason(new Date("2024-09-15"), new Date("2024-10-31"), "halloween"); + }); + + it("should contain xmas messages from Dec 1 to Dec 26", () => { + testSeason(new Date("2024-12-01"), new Date("2024-12-26"), "xmas"); + }); + + it("should contain new years messages frm Jan 1 to Jan 31", () => { + testSeason(new Date("2024-01-01"), new Date("2024-01-31"), "newYears"); + }); + }); +}); + +/** + * Helpoer method to test seasonal messages + * @param startDate The seasons start date + * @param endDate The seasons end date + * @param prefix the splash message prefix (e.g. `newYears` or `xmas`) + */ +function testSeason(startDate: Date, endDate: Date, prefix: string) { + const filterFn = (message: string) => message.startsWith(`splashMessages:${prefix}.`); + + const beforeDate = new Date(startDate); + beforeDate.setDate(startDate.getDate() - 1); + + const afterDate = new Date(endDate); + afterDate.setDate(endDate.getDate() + 1); + + const dates: Date[] = [beforeDate, startDate, endDate, afterDate]; + const [before, start, end, after] = dates.map((date) => { + vi.setSystemTime(date); + console.log("System time set to", date); + const count = getSplashMessages().filter(filterFn).length; + return count; + }); + + expect(before).toBe(0); + expect(start).toBeGreaterThanOrEqual(10); // make sure to adjust if weight is changed! + expect(end).toBeGreaterThanOrEqual(10); // make sure to adjust if weight is changed! + expect(after).toBe(0); +} diff --git a/src/test/eggs/egg.test.ts b/src/test/eggs/egg.test.ts index 4f00e843b47..053ff8f1112 100644 --- a/src/test/eggs/egg.test.ts +++ b/src/test/eggs/egg.test.ts @@ -1,7 +1,7 @@ import { Egg, getLegendaryGachaSpeciesForTimestamp } from "#app/data/egg"; import { EggSourceType } from "#app/enums/egg-source-types"; import { EggTier } from "#app/enums/egg-type"; -import { VariantTier } from "#app/enums/variant-tiers"; +import { VariantTier } from "#app/enums/variant-tier"; import EggData from "#app/system/egg-data"; import * as Utils from "#app/utils"; import { Species } from "#enums/species"; @@ -136,9 +136,9 @@ describe("Egg Generation Tests", () => { expect(result).toBe(expectedResult); }); - it("should return a shiny common variant", () => { + it("should return a shiny standard variant", () => { const scene = game.scene; - const expectedVariantTier = VariantTier.COMMON; + const expectedVariantTier = VariantTier.STANDARD; const result = new Egg({ scene, isShiny: true, variantTier: expectedVariantTier, species: Species.BULBASAUR }).generatePlayerPokemon(scene).variant; diff --git a/src/test/enemy_command.test.ts b/src/test/enemy_command.test.ts new file mode 100644 index 00000000000..53cddc86efb --- /dev/null +++ b/src/test/enemy_command.test.ts @@ -0,0 +1,106 @@ +import { allMoves, MoveCategory } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { AiType, EnemyPokemon } from "#app/field/pokemon"; +import { randSeedInt } from "#app/utils"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + + +const NUM_TRIALS = 300; + +type MoveChoiceSet = { [key: number]: number }; + +function getEnemyMoveChoices(pokemon: EnemyPokemon, moveChoices: MoveChoiceSet): void { + // Use an unseeded random number generator in place of the mocked-out randBattleSeedInt + vi.spyOn(pokemon.scene, "randBattleSeedInt").mockImplementation((range, min?) => { + return randSeedInt(range, min); + }); + for (let i = 0; i < NUM_TRIALS; i++) { + const queuedMove = pokemon.getNextMove(); + moveChoices[queuedMove.move]++; + } + + for (const [moveId, count] of Object.entries(moveChoices)) { + console.log(`Move: ${allMoves[moveId].name} Count: ${count} (${count / NUM_TRIALS * 100}%)`); + } +} + +describe("Enemy Commands - Move Selection", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + + game.override + .ability(Abilities.BALL_FETCH) + .enemyAbility(Abilities.BALL_FETCH); + }); + + it( + "should never use Status moves if an attack can KO", + async () => { + game.override + .enemySpecies(Species.ETERNATUS) + .enemyMoveset([Moves.ETERNABEAM, Moves.SLUDGE_BOMB, Moves.DRAGON_DANCE, Moves.COSMIC_POWER]) + .startingLevel(1) + .enemyLevel(100); + + await game.classicMode.startBattle([Species.MAGIKARP]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + enemyPokemon.aiType = AiType.SMART_RANDOM; + + const moveChoices: MoveChoiceSet = {}; + const enemyMoveset = enemyPokemon.getMoveset(); + enemyMoveset.forEach(mv => moveChoices[mv!.moveId] = 0); + getEnemyMoveChoices(enemyPokemon, moveChoices); + + enemyMoveset.forEach(mv => { + if (mv?.getMove().category === MoveCategory.STATUS) { + expect(moveChoices[mv.moveId]).toBe(0); + } + }); + } + ); + + it( + "should not select Last Resort if it would fail, even if the move KOs otherwise", + async () => { + game.override + .enemySpecies(Species.KANGASKHAN) + .enemyMoveset([Moves.LAST_RESORT, Moves.GIGA_IMPACT, Moves.SPLASH, Moves.SWORDS_DANCE]) + .startingLevel(1) + .enemyLevel(100); + + await game.classicMode.startBattle([Species.MAGIKARP]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + enemyPokemon.aiType = AiType.SMART_RANDOM; + + const moveChoices: MoveChoiceSet = {}; + const enemyMoveset = enemyPokemon.getMoveset(); + enemyMoveset.forEach(mv => moveChoices[mv!.moveId] = 0); + getEnemyMoveChoices(enemyPokemon, moveChoices); + + enemyMoveset.forEach(mv => { + if (mv?.getMove().category === MoveCategory.STATUS || mv?.moveId === Moves.LAST_RESORT) { + expect(moveChoices[mv.moveId]).toBe(0); + } + }); + } + ); +}); diff --git a/src/test/evolution.test.ts b/src/test/evolution.test.ts index 16922babd7c..07865d7e64a 100644 --- a/src/test/evolution.test.ts +++ b/src/test/evolution.test.ts @@ -10,7 +10,6 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite describe("Evolution", () => { let phaserGame: Phaser.Game; let game: GameManager; - const TIMEOUT = 1000 * 20; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -46,7 +45,7 @@ describe("Evolution", () => { trapinch.evolve(pokemonEvolutions[Species.TRAPINCH][0], trapinch.getSpeciesForm()); expect(trapinch.abilityIndex).toBe(1); - }, TIMEOUT); + }); it("should keep same ability slot after evolving", async () => { await game.classicMode.runToSummon([Species.BULBASAUR, Species.CHARMANDER]); @@ -61,7 +60,7 @@ describe("Evolution", () => { charmander.evolve(pokemonEvolutions[Species.CHARMANDER][0], charmander.getSpeciesForm()); expect(charmander.abilityIndex).toBe(1); - }, TIMEOUT); + }); it("should handle illegal abilityIndex values", async () => { await game.classicMode.runToSummon([Species.SQUIRTLE]); @@ -71,7 +70,7 @@ describe("Evolution", () => { squirtle.evolve(pokemonEvolutions[Species.SQUIRTLE][0], squirtle.getSpeciesForm()); expect(squirtle.abilityIndex).toBe(0); - }, TIMEOUT); + }); it("should handle nincada's unique evolution", async () => { await game.classicMode.runToSummon([Species.NINCADA]); @@ -87,7 +86,7 @@ describe("Evolution", () => { 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", () => { const speciesFormEvo = new SpeciesFormEvolution(Species.ABRA, null, null, 1000, null, null); @@ -120,7 +119,7 @@ describe("Evolution", () => { expect(totodile.hp).toBe(totodile.getMaxHp()); expect(totodile.hp).toBeGreaterThan(hpBefore); - }, TIMEOUT); + }); it("should not fully heal HP when evolving", async () => { game.override.moveset([Moves.SURF]) @@ -150,7 +149,7 @@ describe("Evolution", () => { expect(cyndaquil.getMaxHp()).toBeGreaterThan(maxHpBefore); 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 @@ -174,5 +173,5 @@ describe("Evolution", () => { 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/items/double_battle_chance_booster.test.ts b/src/test/items/double_battle_chance_booster.test.ts index 1d5051fa9e9..8d2bd7c9179 100644 --- a/src/test/items/double_battle_chance_booster.test.ts +++ b/src/test/items/double_battle_chance_booster.test.ts @@ -12,8 +12,6 @@ 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, @@ -39,7 +37,7 @@ describe("Items - Double Battle Chance Boosters", () => { 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 @@ -57,7 +55,7 @@ describe("Items - Double Battle Chance Boosters", () => { 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 @@ -100,5 +98,5 @@ describe("Items - Double Battle Chance Boosters", () => { } } expect(count).toBe(1); - }, TIMEOUT); + }); }); diff --git a/src/test/items/eviolite.test.ts b/src/test/items/eviolite.test.ts index d9991d47a89..7b2f9a15fce 100644 --- a/src/test/items/eviolite.test.ts +++ b/src/test/items/eviolite.test.ts @@ -9,8 +9,6 @@ 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({ type: Phaser.HEADLESS, @@ -50,7 +48,7 @@ describe("Items - Eviolite", () => { expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(Math.floor(defStat * 1.5)); expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(spDefStat * 1.5)); - }, TIMEOUT); + }); it("should not provide a boost for fully evolved, unfused pokemon", async() => { await game.classicMode.startBattle([ @@ -74,7 +72,7 @@ describe("Items - Eviolite", () => { expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(defStat); expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(spDefStat); - }, TIMEOUT); + }); it("should provide 50% boost to DEF and SPDEF for completely unevolved, fused pokemon", async() => { await game.classicMode.startBattle([ @@ -107,7 +105,7 @@ describe("Items - Eviolite", () => { expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(Math.floor(defStat * 1.5)); expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(spDefStat * 1.5)); - }, TIMEOUT); + }); it("should provide 25% boost to DEF and SPDEF for partially unevolved (base), fused pokemon", async() => { await game.classicMode.startBattle([ @@ -140,7 +138,7 @@ describe("Items - Eviolite", () => { expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(Math.floor(defStat * 1.25)); expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(spDefStat * 1.25)); - }, TIMEOUT); + }); it("should provide 25% boost to DEF and SPDEF for partially unevolved (fusion), fused pokemon", async() => { await game.classicMode.startBattle([ @@ -173,7 +171,7 @@ describe("Items - Eviolite", () => { expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(Math.floor(defStat * 1.25)); expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(spDefStat * 1.25)); - }, TIMEOUT); + }); it("should not provide a boost for fully evolved, fused pokemon", async() => { await game.classicMode.startBattle([ @@ -206,7 +204,7 @@ describe("Items - Eviolite", () => { 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({ @@ -238,5 +236,5 @@ describe("Items - Eviolite", () => { 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 d9871616449..29d39cabc3e 100644 --- a/src/test/items/grip_claw.test.ts +++ b/src/test/items/grip_claw.test.ts @@ -9,7 +9,7 @@ import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -const TIMEOUT = 20 * 1000; // 20 seconds +// 20 seconds describe("Items - Grip Claw", () => { let phaserGame: Phaser.Game; @@ -63,6 +63,6 @@ describe("Items - Grip Claw", () => { await game.phaseInterceptor.to(MoveEndPhase, false); expect(enemyPokemon[1].getHeldItems.length).toBe(enemyHeldItemCt[1]); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/after_you.test.ts b/src/test/moves/after_you.test.ts index efce1b28a17..025b4804bf1 100644 --- a/src/test/moves/after_you.test.ts +++ b/src/test/moves/after_you.test.ts @@ -8,7 +8,7 @@ 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; @@ -47,7 +47,7 @@ describe("Moves - After You", () => { 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); @@ -61,5 +61,5 @@ describe("Moves - After You", () => { 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 index b438d0f736a..3e86b46aa69 100644 --- a/src/test/moves/alluring_voice.test.ts +++ b/src/test/moves/alluring_voice.test.ts @@ -8,7 +8,7 @@ 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; @@ -50,5 +50,5 @@ describe("Moves - Alluring Voice", () => { await game.phaseInterceptor.to(BerryPhase); expect(enemy.getTag(BattlerTagType.CONFUSED)?.tagType).toBe("CONFUSED"); - }, TIMEOUT); + }); }); diff --git a/src/test/moves/astonish.test.ts b/src/test/moves/astonish.test.ts index b21e2a06051..694ad85803b 100644 --- a/src/test/moves/astonish.test.ts +++ b/src/test/moves/astonish.test.ts @@ -11,7 +11,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Moves - Astonish", () => { let phaserGame: Phaser.Game; @@ -67,6 +67,6 @@ describe("Moves - Astonish", () => { await game.phaseInterceptor.to(BerryPhase, false); expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/baddy_bad.test.ts b/src/test/moves/baddy_bad.test.ts index d1a221453a6..87a7e9e049d 100644 --- a/src/test/moves/baddy_bad.test.ts +++ b/src/test/moves/baddy_bad.test.ts @@ -8,8 +8,6 @@ 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, @@ -39,5 +37,5 @@ describe("Moves - Baddy Bad", () => { await game.phaseInterceptor.to("BerryPhase"); expect(game.scene.arena.tags.length).toBe(0); - }, TIMEOUT); + }); }); diff --git a/src/test/moves/baneful_bunker.test.ts b/src/test/moves/baneful_bunker.test.ts new file mode 100644 index 00000000000..5f63e3b4313 --- /dev/null +++ b/src/test/moves/baneful_bunker.test.ts @@ -0,0 +1,93 @@ +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 { BattlerIndex } from "#app/battle"; +import { StatusEffect } from "#app/enums/status-effect"; + + + +describe("Moves - Baneful Bunker", () => { + 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.moveset(Moves.SLASH); + + game.override.enemySpecies(Species.SNORLAX); + game.override.enemyAbility(Abilities.INSOMNIA); + game.override.enemyMoveset(Moves.BANEFUL_BUNKER); + + game.override.startingLevel(100); + game.override.enemyLevel(100); + }); + test( + "should protect the user and poison attackers that make contact", + async () => { + await game.classicMode.startBattle([Species.CHARIZARD]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.SLASH); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("BerryPhase", false); + expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); + expect(leadPokemon.status?.effect === StatusEffect.POISON).toBeTruthy(); + } + ); + test( + "should protect the user and poison attackers that make contact, regardless of accuracy checks", + async () => { + await game.classicMode.startBattle([Species.CHARIZARD]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.SLASH); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + await game.move.forceMiss(); + await game.phaseInterceptor.to("BerryPhase", false); + expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); + expect(leadPokemon.status?.effect === StatusEffect.POISON).toBeTruthy(); + } + ); + + test( + "should not poison attackers that don't make contact", + async () => { + game.override.moveset(Moves.FLASH_CANNON); + await game.classicMode.startBattle([Species.CHARIZARD]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.FLASH_CANNON); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + await game.move.forceMiss(); + await game.phaseInterceptor.to("BerryPhase", false); + expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); + expect(leadPokemon.status?.effect === StatusEffect.POISON).toBeFalsy(); + } + ); +}); diff --git a/src/test/moves/beak_blast.test.ts b/src/test/moves/beak_blast.test.ts index fe748c87826..3f4fe1d1d11 100644 --- a/src/test/moves/beak_blast.test.ts +++ b/src/test/moves/beak_blast.test.ts @@ -10,7 +10,7 @@ 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 - Beak Blast", () => { let phaserGame: Phaser.Game; @@ -54,7 +54,7 @@ describe("Moves - Beak Blast", () => { await game.phaseInterceptor.to(BerryPhase, false); expect(enemyPokemon.status?.effect).toBe(StatusEffect.BURN); - }, TIMEOUT + } ); it( @@ -74,7 +74,7 @@ describe("Moves - Beak Blast", () => { await game.phaseInterceptor.to(BerryPhase, false); expect(enemyPokemon.status?.effect).toBe(StatusEffect.BURN); - }, TIMEOUT + } ); it( @@ -94,7 +94,7 @@ describe("Moves - Beak Blast", () => { await game.phaseInterceptor.to(BerryPhase, false); expect(enemyPokemon.status?.effect).not.toBe(StatusEffect.BURN); - }, TIMEOUT + } ); it( @@ -110,7 +110,7 @@ describe("Moves - Beak Blast", () => { await game.phaseInterceptor.to(BerryPhase, false); expect(leadPokemon.turnData.hitCount).toBe(2); - }, TIMEOUT + } ); it( @@ -131,6 +131,6 @@ describe("Moves - Beak Blast", () => { await game.phaseInterceptor.to(TurnEndPhase); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); expect(leadPokemon.getTag(BattlerTagType.BEAK_BLAST_CHARGING)).toBeUndefined(); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/beat_up.test.ts b/src/test/moves/beat_up.test.ts index 70b33f56583..51ec768084c 100644 --- a/src/test/moves/beat_up.test.ts +++ b/src/test/moves/beat_up.test.ts @@ -7,8 +7,6 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -const TIMEOUT = 20 * 1000; // 20 sec timeout - describe("Moves - Beat Up", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -57,7 +55,7 @@ describe("Moves - Beat Up", () => { await game.phaseInterceptor.to(MoveEffectPhase); expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp); } - }, TIMEOUT + } ); it( @@ -74,7 +72,7 @@ describe("Moves - Beat Up", () => { await game.phaseInterceptor.to(MoveEffectPhase); expect(playerPokemon.turnData.hitCount).toBe(5); - }, TIMEOUT + } ); it( @@ -99,6 +97,6 @@ describe("Moves - Beat Up", () => { await game.phaseInterceptor.to(MoveEffectPhase); expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp); } - }, TIMEOUT + } ); }); diff --git a/src/test/moves/belly_drum.test.ts b/src/test/moves/belly_drum.test.ts index 3d85c59a2a5..494272089e2 100644 --- a/src/test/moves/belly_drum.test.ts +++ b/src/test/moves/belly_drum.test.ts @@ -8,7 +8,7 @@ import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import { Abilities } from "#app/enums/abilities"; -const TIMEOUT = 20 * 1000; + // RATIO : HP Cost of Move const RATIO = 2; // PREDAMAGE : Amount of extra HP lost @@ -54,7 +54,7 @@ describe("Moves - BELLY DRUM", () => { expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6); - }, TIMEOUT + } ); test("will still take effect if an uninvolved stat stage is at max", @@ -74,7 +74,7 @@ describe("Moves - BELLY DRUM", () => { expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6); expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(6); - }, TIMEOUT + } ); test("fails if the pokemon's ATK stat stage is at its maximum", @@ -90,7 +90,7 @@ describe("Moves - BELLY DRUM", () => { expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6); - }, TIMEOUT + } ); test("fails if the user's health is less than 1/2", @@ -106,6 +106,6 @@ describe("Moves - BELLY DRUM", () => { expect(leadPokemon.hp).toBe(hpLost - PREDAMAGE); 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 index 3f2bf453684..d6ebbf30bb1 100644 --- a/src/test/moves/burning_jealousy.test.ts +++ b/src/test/moves/burning_jealousy.test.ts @@ -8,7 +8,7 @@ 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; @@ -50,7 +50,7 @@ describe("Moves - Burning Jealousy", () => { 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 @@ -66,7 +66,7 @@ describe("Moves - Burning Jealousy", () => { await game.phaseInterceptor.to("BerryPhase"); expect(enemy.status?.effect).toBe(StatusEffect.BURN); - }, TIMEOUT); + }); it("should ignore stat stages raised by IMPOSTER", async () => { game.override @@ -81,11 +81,11 @@ describe("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 @@ -98,5 +98,5 @@ describe("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 8511b3179c6..e98fe462c62 100644 --- a/src/test/moves/ceaseless_edge.test.ts +++ b/src/test/moves/ceaseless_edge.test.ts @@ -10,7 +10,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Moves - Ceaseless Edge", () => { let phaserGame: Phaser.Game; @@ -61,7 +61,7 @@ describe("Moves - Ceaseless Edge", () => { expect(tagAfter instanceof ArenaTrapTag).toBeTruthy(); expect(tagAfter.layers).toBe(1); expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp); - }, TIMEOUT + } ); test( @@ -86,7 +86,7 @@ describe("Moves - Ceaseless Edge", () => { expect(tagAfter instanceof ArenaTrapTag).toBeTruthy(); expect(tagAfter.layers).toBe(2); expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp); - }, TIMEOUT + } ); test( @@ -114,6 +114,6 @@ describe("Moves - Ceaseless Edge", () => { game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase, false); expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(hpBeforeSpikes); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/clangorous_soul.test.ts b/src/test/moves/clangorous_soul.test.ts index 015b73b4dab..8f0bfb2549f 100644 --- a/src/test/moves/clangorous_soul.test.ts +++ b/src/test/moves/clangorous_soul.test.ts @@ -6,7 +6,7 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { Stat } from "#enums/stat"; -const TIMEOUT = 20 * 1000; + /** HP Cost of Move */ const RATIO = 3; /** Amount of extra HP lost */ @@ -54,7 +54,7 @@ describe("Moves - Clangorous Soul", () => { expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(1); expect(leadPokemon.getStatStage(Stat.SPDEF)).toBe(1); expect(leadPokemon.getStatStage(Stat.SPD)).toBe(1); - }, TIMEOUT + } ); it("will still take effect if one or more of the involved stat stages are not at max", @@ -79,7 +79,7 @@ describe("Moves - Clangorous Soul", () => { expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(6); expect(leadPokemon.getStatStage(Stat.SPDEF)).toBe(5); expect(leadPokemon.getStatStage(Stat.SPD)).toBe(1); - }, TIMEOUT + } ); it("fails if all stat stages involved are at max", @@ -103,7 +103,7 @@ describe("Moves - Clangorous Soul", () => { expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(6); expect(leadPokemon.getStatStage(Stat.SPDEF)).toBe(6); expect(leadPokemon.getStatStage(Stat.SPD)).toBe(6); - }, TIMEOUT + } ); it("fails if the user's health is less than 1/3", @@ -123,6 +123,6 @@ describe("Moves - Clangorous Soul", () => { 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 7b962518944..63399c3a86a 100644 --- a/src/test/moves/crafty_shield.test.ts +++ b/src/test/moves/crafty_shield.test.ts @@ -9,7 +9,7 @@ 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; + describe("Moves - Crafty Shield", () => { let phaserGame: Phaser.Game; @@ -56,7 +56,7 @@ describe("Moves - Crafty Shield", () => { await game.phaseInterceptor.to(BerryPhase, false); leadPokemon.forEach(p => expect(p.getStatStage(Stat.ATK)).toBe(0)); - }, TIMEOUT + } ); test( @@ -77,7 +77,7 @@ describe("Moves - Crafty Shield", () => { await game.phaseInterceptor.to(BerryPhase, false); expect(leadPokemon.some(p => p.hp < p.getMaxHp())).toBeTruthy(); - }, TIMEOUT + } ); test( @@ -99,7 +99,7 @@ describe("Moves - Crafty Shield", () => { await game.phaseInterceptor.to(BerryPhase, false); leadPokemon.forEach(p => expect(p.getTag(BattlerTagType.CURSED)).toBeUndefined()); - }, TIMEOUT + } ); test( diff --git a/src/test/moves/dragon_cheer.test.ts b/src/test/moves/dragon_cheer.test.ts index 0fc389ccfb6..beaf6ddb520 100644 --- a/src/test/moves/dragon_cheer.test.ts +++ b/src/test/moves/dragon_cheer.test.ts @@ -10,8 +10,6 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite describe("Moves - Dragon Cheer", () => { let phaserGame: Phaser.Game; let game: GameManager; - const TIMEOUT = 20 * 1000; - beforeAll(() => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, @@ -47,7 +45,7 @@ describe("Moves - Dragon Cheer", () => { // 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]); @@ -64,7 +62,7 @@ describe("Moves - Dragon Cheer", () => { // 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]); @@ -96,5 +94,5 @@ describe("Moves - Dragon Cheer", () => { await game.phaseInterceptor.to("MoveEndPhase"); expect(enemy.getCritStage).toHaveReturnedWith(1); // getCritStage is called on defender - }, TIMEOUT); + }); }); diff --git a/src/test/moves/dragon_tail.test.ts b/src/test/moves/dragon_tail.test.ts index e1af29b2db1..4b222a0c477 100644 --- a/src/test/moves/dragon_tail.test.ts +++ b/src/test/moves/dragon_tail.test.ts @@ -10,7 +10,7 @@ import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import GameManager from "../utils/gameManager"; -const TIMEOUT = 20 * 1000; + describe("Moves - Dragon Tail", () => { let phaserGame: Phaser.Game; @@ -50,12 +50,12 @@ describe("Moves - Dragon Tail", () => { await game.phaseInterceptor.to(BerryPhase); const isVisible = enemyPokemon.visible; - const hasFled = enemyPokemon.wildFlee; + const hasFled = enemyPokemon.switchOutStatus; expect(!isVisible && hasFled).toBe(true); // simply want to test that the game makes it this far without crashing await game.phaseInterceptor.to(BattleEndPhase); - }, TIMEOUT + } ); test( @@ -72,10 +72,10 @@ describe("Moves - Dragon Tail", () => { await game.phaseInterceptor.to(BerryPhase); const isVisible = enemyPokemon.visible; - const hasFled = enemyPokemon.wildFlee; + const hasFled = enemyPokemon.switchOutStatus; expect(!isVisible && hasFled).toBe(true); expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); - }, TIMEOUT + } ); test( @@ -97,9 +97,9 @@ describe("Moves - Dragon Tail", () => { await game.phaseInterceptor.to(TurnEndPhase); const isVisibleLead = enemyLeadPokemon.visible; - const hasFledLead = enemyLeadPokemon.wildFlee; + const hasFledLead = enemyLeadPokemon.switchOutStatus; const isVisibleSec = enemySecPokemon.visible; - const hasFledSec = enemySecPokemon.wildFlee; + const hasFledSec = enemySecPokemon.switchOutStatus; expect(!isVisibleLead && hasFledLead && isVisibleSec && !hasFledSec).toBe(true); expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); @@ -109,7 +109,7 @@ describe("Moves - Dragon Tail", () => { await game.phaseInterceptor.to(BerryPhase); expect(enemySecPokemon.hp).toBeLessThan(enemySecPokemon.getMaxHp()); - }, TIMEOUT + } ); test( @@ -133,14 +133,14 @@ describe("Moves - Dragon Tail", () => { await game.phaseInterceptor.to(BerryPhase); const isVisibleLead = enemyLeadPokemon.visible; - const hasFledLead = enemyLeadPokemon.wildFlee; + const hasFledLead = enemyLeadPokemon.switchOutStatus; const isVisibleSec = enemySecPokemon.visible; - const hasFledSec = enemySecPokemon.wildFlee; + const hasFledSec = enemySecPokemon.switchOutStatus; expect(!isVisibleLead && hasFledLead && !isVisibleSec && hasFledSec).toBe(true); expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); expect(secPokemon.hp).toBeLessThan(secPokemon.getMaxHp()); expect(enemyLeadPokemon.hp).toBeLessThan(enemyLeadPokemon.getMaxHp()); expect(enemySecPokemon.hp).toBeLessThan(enemySecPokemon.getMaxHp()); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/fillet_away.test.ts b/src/test/moves/fillet_away.test.ts index 68ace42c2ec..d8dd74a259c 100644 --- a/src/test/moves/fillet_away.test.ts +++ b/src/test/moves/fillet_away.test.ts @@ -7,7 +7,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -const TIMEOUT = 20 * 1000; + /** HP Cost of Move */ const RATIO = 2; /** Amount of extra HP lost */ @@ -53,7 +53,7 @@ describe("Moves - FILLET AWAY", () => { expect(leadPokemon.getStatStage(Stat.ATK)).toBe(2); expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(2); expect(leadPokemon.getStatStage(Stat.SPD)).toBe(2); - }, TIMEOUT + } ); test("still takes effect if one or more of the involved stat stages are not at max", @@ -74,7 +74,7 @@ describe("Moves - FILLET AWAY", () => { expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6); expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(5); expect(leadPokemon.getStatStage(Stat.SPD)).toBe(2); - }, TIMEOUT + } ); test("fails if all stat stages involved are at max", @@ -94,7 +94,7 @@ describe("Moves - FILLET AWAY", () => { expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6); expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(6); expect(leadPokemon.getStatStage(Stat.SPD)).toBe(6); - }, TIMEOUT + } ); test("fails if the user's health is less than 1/2", @@ -112,6 +112,6 @@ describe("Moves - FILLET AWAY", () => { 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/focus_punch.test.ts b/src/test/moves/focus_punch.test.ts index ca80c688169..b839c228b68 100644 --- a/src/test/moves/focus_punch.test.ts +++ b/src/test/moves/focus_punch.test.ts @@ -10,7 +10,7 @@ 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 - Focus Punch", () => { let phaserGame: Phaser.Game; @@ -61,7 +61,7 @@ describe("Moves - Focus Punch", () => { expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp); expect(leadPokemon.getMoveHistory().length).toBe(1); expect(leadPokemon.turnData.damageDealt).toBe(enemyStartingHp - enemyPokemon.hp); - }, TIMEOUT + } ); it( @@ -88,7 +88,7 @@ describe("Moves - Focus Punch", () => { expect(enemyPokemon.hp).toBe(enemyStartingHp); expect(leadPokemon.getMoveHistory().length).toBe(1); expect(leadPokemon.turnData.damageDealt).toBe(0); - }, TIMEOUT + } ); it( @@ -111,7 +111,7 @@ describe("Moves - Focus Punch", () => { expect(leadPokemon.getMoveHistory().length).toBe(1); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - }, TIMEOUT + } ); it( @@ -129,6 +129,6 @@ describe("Moves - Focus Punch", () => { expect(game.scene.getCurrentPhase() instanceof SwitchSummonPhase).toBeTruthy(); expect(game.scene.phaseQueue.find(phase => phase instanceof MoveHeaderPhase)).toBeDefined(); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/follow_me.test.ts b/src/test/moves/follow_me.test.ts index 7d0c4fdb546..28fb1045a8c 100644 --- a/src/test/moves/follow_me.test.ts +++ b/src/test/moves/follow_me.test.ts @@ -8,7 +8,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Moves - Follow Me", () => { let phaserGame: Phaser.Game; @@ -54,7 +54,7 @@ describe("Moves - Follow Me", () => { expect(playerPokemon[0].hp).toBeLessThan(playerPokemon[0].getMaxHp()); expect(playerPokemon[1].hp).toBe(playerPokemon[1].getMaxHp()); - }, TIMEOUT + } ); test( @@ -77,7 +77,7 @@ describe("Moves - Follow Me", () => { expect(playerPokemon[1].hp).toBeLessThan(playerPokemon[1].getMaxHp()); expect(playerPokemon[0].hp).toBe(playerPokemon[0].getMaxHp()); - }, TIMEOUT + } ); test( @@ -102,7 +102,7 @@ describe("Moves - Follow Me", () => { // If redirection was bypassed, both enemies should be damaged expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp()); expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[1].getMaxHp()); - }, TIMEOUT + } ); test( @@ -125,6 +125,6 @@ describe("Moves - Follow Me", () => { // If redirection was bypassed, both enemies should be damaged expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp()); expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[1].getMaxHp()); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/freeze_dry.test.ts b/src/test/moves/freeze_dry.test.ts index ff9e2f07162..b901f04e6a1 100644 --- a/src/test/moves/freeze_dry.test.ts +++ b/src/test/moves/freeze_dry.test.ts @@ -9,8 +9,6 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite describe("Moves - Freeze-Dry", () => { let phaserGame: Phaser.Game; let game: GameManager; - const TIMEOUT = 20 * 1000; - beforeAll(() => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, @@ -44,7 +42,7 @@ describe("Moves - Freeze-Dry", () => { 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); @@ -58,7 +56,7 @@ describe("Moves - Freeze-Dry", () => { 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); @@ -72,7 +70,7 @@ describe("Moves - Freeze-Dry", () => { 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 () => { @@ -87,7 +85,7 @@ describe("Moves - Freeze-Dry", () => { 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 () => { @@ -102,5 +100,5 @@ describe("Moves - Freeze-Dry", () => { await game.phaseInterceptor.to("BerryPhase"); expect(enemy.getMoveEffectiveness).toHaveReturnedWith(2); - }, TIMEOUT); + }); }); diff --git a/src/test/moves/gastro_acid.test.ts b/src/test/moves/gastro_acid.test.ts index cfc458a908f..60b2bd80c05 100644 --- a/src/test/moves/gastro_acid.test.ts +++ b/src/test/moves/gastro_acid.test.ts @@ -6,7 +6,7 @@ import { MoveResult } from "#app/field/pokemon"; import GameManager from "#test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Moves - Gastro Acid", () => { let phaserGame: Phaser.Game; @@ -60,7 +60,7 @@ describe("Moves - Gastro Acid", () => { expect(enemyField[0].hp).toBeLessThan(enemyField[0].getMaxHp()); expect(enemyField[1].isFullHp()).toBe(true); - }, TIMEOUT); + }); it("fails if used on an enemy with an already-suppressed ability", async () => { game.override.battleType(null); @@ -78,5 +78,5 @@ describe("Moves - Gastro Acid", () => { await game.phaseInterceptor.to("TurnInitPhase"); expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL); - }, TIMEOUT); + }); }); diff --git a/src/test/moves/glaive_rush.test.ts b/src/test/moves/glaive_rush.test.ts index 9eed6868432..1a524b4aef6 100644 --- a/src/test/moves/glaive_rush.test.ts +++ b/src/test/moves/glaive_rush.test.ts @@ -6,7 +6,7 @@ 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 - Glaive Rush", () => { let phaserGame: Phaser.Game; @@ -49,7 +49,7 @@ describe("Moves - Glaive Rush", () => { await game.phaseInterceptor.to("DamagePhase"); expect(enemy.hp).toBeLessThanOrEqual(1001 - (damageDealt * 3)); - }, TIMEOUT); + }); it("always gets hit by attacks", async () => { await game.classicMode.startBattle(); @@ -62,7 +62,7 @@ describe("Moves - Glaive Rush", () => { await game.phaseInterceptor.to("TurnEndPhase"); expect(enemy.hp).toBeLessThan(1000); - }, TIMEOUT); + }); it("interacts properly with multi-lens", async () => { game.override @@ -85,7 +85,7 @@ describe("Moves - Glaive Rush", () => { await game.phaseInterceptor.to("TurnEndPhase"); expect(player.hp).toBe(1000); - }, TIMEOUT); + }); it("secondary effects only last until next move", async () => { game.override.enemyMoveset([Moves.SHADOW_SNEAK]); @@ -111,7 +111,7 @@ describe("Moves - Glaive Rush", () => { await game.phaseInterceptor.to("TurnEndPhase"); expect(player.hp).toBe(damagedHp); - }, TIMEOUT); + }); it("secondary effects are removed upon switching", async () => { game.override @@ -135,7 +135,7 @@ describe("Moves - Glaive Rush", () => { await game.phaseInterceptor.to("TurnEndPhase"); expect(player.hp).toBe(player.getMaxHp()); - }, TIMEOUT); + }); it("secondary effects don't activate if move fails", async () => { game.override.moveset([Moves.SHADOW_SNEAK, Moves.PROTECT, Moves.SPLASH, Moves.GLAIVE_RUSH]); @@ -161,5 +161,5 @@ describe("Moves - Glaive Rush", () => { const damagedHP2 = 1000 - enemy.hp; expect(damagedHP2).toBeGreaterThanOrEqual((damagedHP1 * 2) - 1); - }, TIMEOUT); + }); }); diff --git a/src/test/moves/guard_swap.test.ts b/src/test/moves/guard_swap.test.ts index 0c24f69c32c..99769b32899 100644 --- a/src/test/moves/guard_swap.test.ts +++ b/src/test/moves/guard_swap.test.ts @@ -11,8 +11,6 @@ import { MoveEndPhase } from "#app/phases/move-end-phase"; describe("Moves - Guard Swap", () => { let phaserGame: Phaser.Game; let game: GameManager; - const TIMEOUT = 20 * 1000; - beforeAll(() => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, @@ -65,5 +63,5 @@ describe("Moves - Guard Swap", () => { expect(enemy.getStatStage(s)).toBe(1); } } - }, TIMEOUT); + }); }); diff --git a/src/test/moves/haze.test.ts b/src/test/moves/haze.test.ts index 211c1a41409..e5474801899 100644 --- a/src/test/moves/haze.test.ts +++ b/src/test/moves/haze.test.ts @@ -35,7 +35,7 @@ describe("Moves - Haze", () => { game.override.ability(Abilities.NONE); }); - it("should reset all stat changes of all Pokemon on field", { timeout: 10000 }, async () => { + it("should reset all stat changes of all Pokemon on field", async () => { await game.startBattle([Species.RATTATA]); const user = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; diff --git a/src/test/moves/heal_block.test.ts b/src/test/moves/heal_block.test.ts new file mode 100644 index 00000000000..8549d6211a9 --- /dev/null +++ b/src/test/moves/heal_block.test.ts @@ -0,0 +1,153 @@ +import { BattlerIndex } from "#app/battle"; +import { ArenaTagSide } from "#app/data/arena-tag"; +import { WeatherType } from "#app/data/weather"; +import GameManager from "#app/test/utils/gameManager"; +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 { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +const TIMEOUT = 20 * 1000; + +// Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Heal_Block_(move) +describe("Moves - Heal Block", () => { + 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 + .moveset([Moves.ABSORB, Moves.WISH, Moves.SPLASH, Moves.AQUA_RING]) + .enemyMoveset(Moves.HEAL_BLOCK) + .ability(Abilities.NO_GUARD) + .enemyAbility(Abilities.BALL_FETCH) + .enemySpecies(Species.BLISSEY) + .disableCrits(); + }); + + it("shouldn't stop damage from HP-drain attacks, just HP restoration", async() => { + + await game.classicMode.startBattle([Species.CHARIZARD]); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + player.damageAndUpdate(enemy.getMaxHp() - 1); + + game.move.select(Moves.ABSORB); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(player.hp).toBe(1); + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); + }, TIMEOUT + ); + + it("shouldn't stop Liquid Ooze from dealing damage", async() => { + game.override.enemyAbility(Abilities.LIQUID_OOZE); + + await game.classicMode.startBattle([Species.CHARIZARD]); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.ABSORB); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(player.isFullHp()).toBe(false); + expect(enemy.isFullHp()).toBe(false); + }, TIMEOUT); + + it("should stop delayed heals, such as from Wish", async() => { + await game.classicMode.startBattle([Species.CHARIZARD]); + + const player = game.scene.getPlayerPokemon()!; + + player.damageAndUpdate(player.getMaxHp() - 1); + + game.move.select(Moves.WISH); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(game.scene.arena.getTagOnSide(ArenaTagType.WISH, ArenaTagSide.PLAYER)).toBeDefined(); + while (game.scene.arena.getTagOnSide(ArenaTagType.WISH, ArenaTagSide.PLAYER)) { + game.move.select(Moves.SPLASH); + await game.phaseInterceptor.to("TurnEndPhase"); + } + + expect(player.hp).toBe(1); + }, TIMEOUT); + + it("should prevent Grassy Terrain from restoring HP", async() => { + game.override.enemyAbility(Abilities.GRASSY_SURGE); + + await game.classicMode.startBattle([Species.CHARIZARD]); + + const player = game.scene.getPlayerPokemon()!; + + player.damageAndUpdate(player.getMaxHp() - 1); + + game.move.select(Moves.SPLASH); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(player.hp).toBe(1); + }, TIMEOUT); + + it("should prevent healing from heal-over-time moves", async() => { + await game.classicMode.startBattle([Species.CHARIZARD]); + + const player = game.scene.getPlayerPokemon()!; + + player.damageAndUpdate(player.getMaxHp() - 1); + + game.move.select(Moves.AQUA_RING); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(player.getTag(BattlerTagType.AQUA_RING)).toBeDefined(); + expect(player.hp).toBe(1); + }, TIMEOUT); + + it("should prevent abilities from restoring HP", async() => { + game.override + .weather(WeatherType.RAIN) + .ability(Abilities.RAIN_DISH); + + await game.classicMode.startBattle([Species.CHARIZARD]); + + const player = game.scene.getPlayerPokemon()!; + + player.damageAndUpdate(player.getMaxHp() - 1); + + game.move.select(Moves.SPLASH); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(player.hp).toBe(1); + }, TIMEOUT); + + it("should stop healing from items", async() => { + game.override.startingHeldItems([{name: "LEFTOVERS"}]); + + await game.classicMode.startBattle([Species.CHARIZARD]); + + const player = game.scene.getPlayerPokemon()!; + player.damageAndUpdate(player.getMaxHp() - 1); + + game.move.select(Moves.SPLASH); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(player.hp).toBe(1); + }, TIMEOUT); +}); diff --git a/src/test/moves/heart_swap.test.ts b/src/test/moves/heart_swap.test.ts index f658641d46f..a128549c459 100644 --- a/src/test/moves/heart_swap.test.ts +++ b/src/test/moves/heart_swap.test.ts @@ -11,8 +11,6 @@ import { MoveEndPhase } from "#app/phases/move-end-phase"; describe("Moves - Heart Swap", () => { let phaserGame: Phaser.Game; let game: GameManager; - const TIMEOUT = 20 * 1000; - beforeAll(() => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, @@ -60,5 +58,5 @@ describe("Moves - Heart Swap", () => { expect(enemy.getStatStage(s)).toBe(0); expect(player.getStatStage(s)).toBe(1); } - }, TIMEOUT); + }); }); diff --git a/src/test/moves/hyper_beam.test.ts b/src/test/moves/hyper_beam.test.ts index 7aa2dbfec2b..a6a471569ed 100644 --- a/src/test/moves/hyper_beam.test.ts +++ b/src/test/moves/hyper_beam.test.ts @@ -9,8 +9,6 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -const TIMEOUT = 20 * 1000; // 20 sec timeout for all tests - describe("Moves - Hyper Beam", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -67,6 +65,6 @@ describe("Moves - Hyper Beam", () => { await game.phaseInterceptor.to(BerryPhase, false); expect(enemyPokemon.hp).toBeLessThan(enemyPostAttackHp); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/jaw_lock.test.ts b/src/test/moves/jaw_lock.test.ts index 75fd6f0ff32..3398ec00b3b 100644 --- a/src/test/moves/jaw_lock.test.ts +++ b/src/test/moves/jaw_lock.test.ts @@ -11,7 +11,7 @@ import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Moves - Jaw Lock", () => { let phaserGame: Phaser.Game; @@ -61,7 +61,7 @@ describe("Moves - Jaw Lock", () => { expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); - }, TIMEOUT + } ); it( @@ -90,7 +90,7 @@ describe("Moves - Jaw Lock", () => { expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); - }, TIMEOUT + } ); it( @@ -114,7 +114,7 @@ describe("Moves - Jaw Lock", () => { await game.doKillOpponents(); expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); - }, TIMEOUT + } ); it( @@ -146,7 +146,7 @@ describe("Moves - Jaw Lock", () => { expect(enemyPokemon[1].getTag(BattlerTagType.TRAPPED)).toBeUndefined(); expect(playerPokemon[0].getTag(BattlerTagType.TRAPPED)).toBeDefined(); expect(playerPokemon[0].getTag(BattlerTagType.TRAPPED)?.sourceId).toBe(enemyPokemon[0].id); - }, TIMEOUT + } ); it( @@ -165,6 +165,6 @@ describe("Moves - Jaw Lock", () => { expect(playerPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/lash_out.test.ts b/src/test/moves/lash_out.test.ts index 8c414832f36..7a8ab6c5bb6 100644 --- a/src/test/moves/lash_out.test.ts +++ b/src/test/moves/lash_out.test.ts @@ -7,7 +7,7 @@ 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; @@ -48,5 +48,5 @@ describe("Moves - Lash Out", () => { 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 57e5ff80f1d..77ea751aee1 100644 --- a/src/test/moves/lucky_chant.test.ts +++ b/src/test/moves/lucky_chant.test.ts @@ -7,7 +7,7 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "../utils/gameManager"; -const TIMEOUT = 20 * 1000; + describe("Moves - Lucky Chant", () => { let phaserGame: Phaser.Game; @@ -55,7 +55,7 @@ describe("Moves - Lucky Chant", () => { const secondTurnDamage = playerPokemon.getMaxHp() - playerPokemon.hp - firstTurnDamage; expect(secondTurnDamage).toBeLessThan(firstTurnDamage); - }, TIMEOUT + } ); it( @@ -81,7 +81,7 @@ describe("Moves - Lucky Chant", () => { const secondTurnDamage = playerPokemon[0].getMaxHp() - playerPokemon[0].hp - firstTurnDamage; expect(secondTurnDamage).toBeLessThan(firstTurnDamage); - }, TIMEOUT + } ); it( @@ -108,6 +108,6 @@ describe("Moves - Lucky Chant", () => { const secondTurnDamage = playerPokemon.getMaxHp() - playerPokemon.hp - firstTurnDamage; expect(secondTurnDamage).toBeLessThan(firstTurnDamage); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/make_it_rain.test.ts b/src/test/moves/make_it_rain.test.ts index 5ac35168f92..2b28a958ff0 100644 --- a/src/test/moves/make_it_rain.test.ts +++ b/src/test/moves/make_it_rain.test.ts @@ -8,7 +8,7 @@ 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; + describe("Moves - Make It Rain", () => { let phaserGame: Phaser.Game; @@ -46,7 +46,7 @@ describe("Moves - Make It Rain", () => { await game.phaseInterceptor.to(MoveEndPhase); expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(-1); - }, TIMEOUT); + }); it("should apply effects even if the target faints", async () => { game.override.enemyLevel(1); // ensures the enemy will faint @@ -63,7 +63,7 @@ describe("Moves - Make It Rain", () => { expect(enemyPokemon.isFainted()).toBe(true); expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(-1); - }, TIMEOUT); + }); it("should reduce Sp. Atk. once after KOing two enemies", async () => { game.override.enemyLevel(1); // ensures the enemy will faint @@ -80,7 +80,7 @@ describe("Moves - Make It Rain", () => { enemyPokemon.forEach(p => expect(p.isFainted()).toBe(true)); expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(-1); - }, TIMEOUT); + }); it("should lower SPATK stat stage by 1 if it only hits the second target", async () => { await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); @@ -96,5 +96,5 @@ describe("Moves - Make It Rain", () => { await game.phaseInterceptor.to(MoveEndPhase); 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 b759f49bf98..0746f9bcfa9 100644 --- a/src/test/moves/mat_block.test.ts +++ b/src/test/moves/mat_block.test.ts @@ -9,7 +9,7 @@ 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; + describe("Moves - Mat Block", () => { let phaserGame: Phaser.Game; @@ -56,7 +56,7 @@ describe("Moves - Mat Block", () => { await game.phaseInterceptor.to(BerryPhase, false); leadPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp())); - }, TIMEOUT + } ); test( @@ -77,7 +77,7 @@ describe("Moves - Mat Block", () => { await game.phaseInterceptor.to(BerryPhase, false); leadPokemon.forEach(p => expect(p.getStatStage(Stat.ATK)).toBe(-2)); - }, TIMEOUT + } ); test( @@ -103,6 +103,6 @@ describe("Moves - Mat Block", () => { await game.phaseInterceptor.to(BerryPhase, false); expect(leadPokemon.some((p, i) => p.hp < leadStartingHp[i])).toBeTruthy(); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/multi_target.test.ts b/src/test/moves/multi_target.test.ts index 5e830f23fc7..cd69482bd8e 100644 --- a/src/test/moves/multi_target.test.ts +++ b/src/test/moves/multi_target.test.ts @@ -7,7 +7,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Multi-target damage reduction", () => { let phaserGame: Phaser.Game; @@ -75,7 +75,7 @@ describe("Multi-target damage reduction", () => { // Moves that target all enemies get reduced if there's more than one enemy expect(gleam1).toBeLessThanOrEqual(Utils.toDmgValue(gleam2 * 0.75) + 1); expect(gleam1).toBeGreaterThanOrEqual(Utils.toDmgValue(gleam2 * 0.75) - 1); - }, TIMEOUT); + }); it("should reduce earthquake when more than one pokemon other than user is not fainted", async () => { await game.startBattle([Species.MAGIKARP, Species.FEEBAS]); @@ -126,5 +126,5 @@ describe("Multi-target damage reduction", () => { // Turn 3: 1 target, should be no damage reduction expect(damageEnemy1Turn1).toBeLessThanOrEqual(Utils.toDmgValue(damageEnemy1Turn3 * 0.75) + 1); expect(damageEnemy1Turn1).toBeGreaterThanOrEqual(Utils.toDmgValue(damageEnemy1Turn3 * 0.75) - 1); - }, TIMEOUT); + }); }); diff --git a/src/test/moves/obstruct.test.ts b/src/test/moves/obstruct.test.ts index 539b11090de..43706a5a1d6 100644 --- a/src/test/moves/obstruct.test.ts +++ b/src/test/moves/obstruct.test.ts @@ -8,8 +8,6 @@ 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, @@ -41,7 +39,24 @@ describe("Moves - Obstruct", () => { expect(player.isFullHp()).toBe(true); expect(enemy.getStatStage(Stat.DEF)).toBe(-2); - }, TIMEOUT); + }); + + it("bypasses accuracy checks when applying protection and defense reduction", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.ICE_PUNCH)); + await game.classicMode.startBattle(); + + game.move.select(Moves.OBSTRUCT); + await game.phaseInterceptor.to("MoveEffectPhase"); + await game.move.forceMiss(); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + await game.phaseInterceptor.to("TurnEndPhase"); + expect(player.isFullHp()).toBe(true); + expect(enemy.getStatStage(Stat.DEF)).toBe(-2); + } + ); 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)); @@ -55,7 +70,7 @@ describe("Moves - Obstruct", () => { 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)); @@ -67,5 +82,5 @@ describe("Moves - Obstruct", () => { 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 7618b08e9fc..d80b71a51e1 100644 --- a/src/test/moves/octolock.test.ts +++ b/src/test/moves/octolock.test.ts @@ -36,7 +36,7 @@ describe("Moves - Octolock", () => { .ability(Abilities.BALL_FETCH); }); - it("lowers DEF and SPDEF stat stages of the target Pokemon by 1 each turn", { timeout: 10000 }, async () => { + it("lowers DEF and SPDEF stat stages of the target Pokemon by 1 each turn", async () => { await game.classicMode.startBattle([ Species.GRAPPLOCT ]); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -57,7 +57,7 @@ describe("Moves - Octolock", () => { 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 () => { + it("if target pokemon has BIG_PECKS, should only lower SPDEF stat stage by 1", async () => { game.override.enemyAbility(Abilities.BIG_PECKS); await game.classicMode.startBattle([ Species.GRAPPLOCT ]); @@ -71,7 +71,7 @@ describe("Moves - Octolock", () => { expect(enemyPokemon.getStatStage(Stat.SPDEF)).toBe(-1); }); - it("if target pokemon has WHITE_SMOKE, should not reduce any stat stages", { timeout: 10000 }, async () => { + it("if target pokemon has WHITE_SMOKE, should not reduce any stat stages", async () => { game.override.enemyAbility(Abilities.WHITE_SMOKE); await game.classicMode.startBattle([ Species.GRAPPLOCT ]); @@ -85,7 +85,7 @@ describe("Moves - Octolock", () => { expect(enemyPokemon.getStatStage(Stat.SPDEF)).toBe(0); }); - it("if target pokemon has CLEAR_BODY, should not reduce any stat stages", { timeout: 10000 }, async () => { + it("if target pokemon has CLEAR_BODY, should not reduce any stat stages", async () => { game.override.enemyAbility(Abilities.CLEAR_BODY); await game.classicMode.startBattle([ Species.GRAPPLOCT ]); @@ -99,7 +99,7 @@ describe("Moves - Octolock", () => { expect(enemyPokemon.getStatStage(Stat.SPDEF)).toBe(0); }); - it("traps the target pokemon", { timeout: 10000 }, async () => { + it("traps the target pokemon", async () => { await game.classicMode.startBattle([ Species.GRAPPLOCT ]); const enemyPokemon = game.scene.getEnemyPokemon()!; diff --git a/src/test/moves/parting_shot.test.ts b/src/test/moves/parting_shot.test.ts index 52cfaf98111..fa328e15a32 100644 --- a/src/test/moves/parting_shot.test.ts +++ b/src/test/moves/parting_shot.test.ts @@ -10,7 +10,7 @@ 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; + describe("Moves - Parting Shot", () => { let phaserGame: Phaser.Game; @@ -53,7 +53,7 @@ describe("Moves - Parting Shot", () => { 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 + } ); test( @@ -73,7 +73,7 @@ describe("Moves - Parting Shot", () => { 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 + } ); it.skip( // TODO: fix this bug to pass the test! @@ -115,7 +115,7 @@ describe("Moves - Parting Shot", () => { 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 + } ); it.skip( // TODO: fix this bug to pass the test! @@ -136,7 +136,7 @@ describe("Moves - Parting Shot", () => { 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 + } ); it.skip( // TODO: fix this bug to pass the test! @@ -156,7 +156,7 @@ describe("Moves - Parting Shot", () => { 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 + } ); it.skip( // TODO: fix this bug to pass the test! @@ -173,7 +173,7 @@ describe("Moves - Parting Shot", () => { 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 + } ); it.skip( // TODO: fix this bug to pass the test! @@ -196,6 +196,6 @@ describe("Moves - Parting Shot", () => { 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 index 3fda315193e..f39759f278b 100644 --- a/src/test/moves/power_shift.test.ts +++ b/src/test/moves/power_shift.test.ts @@ -9,8 +9,6 @@ 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, @@ -59,5 +57,5 @@ describe("Moves - Power Shift", () => { // 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_swap.test.ts b/src/test/moves/power_swap.test.ts index 92cd786c050..e9a4b569c92 100644 --- a/src/test/moves/power_swap.test.ts +++ b/src/test/moves/power_swap.test.ts @@ -11,8 +11,6 @@ import { MoveEndPhase } from "#app/phases/move-end-phase"; describe("Moves - Power Swap", () => { let phaserGame: Phaser.Game; let game: GameManager; - const TIMEOUT = 20 * 1000; - beforeAll(() => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, @@ -65,5 +63,5 @@ describe("Moves - Power Swap", () => { expect(enemy.getStatStage(s)).toBe(1); } } - }, TIMEOUT); + }); }); diff --git a/src/test/moves/protect.test.ts b/src/test/moves/protect.test.ts index 24bbcbb9d34..dcf4211ac7f 100644 --- a/src/test/moves/protect.test.ts +++ b/src/test/moves/protect.test.ts @@ -10,7 +10,7 @@ import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; import { BattlerIndex } from "#app/battle"; import { MoveResult } from "#app/field/pokemon"; -const TIMEOUT = 20 * 1000; + describe("Moves - Protect", () => { let phaserGame: Phaser.Game; @@ -53,7 +53,7 @@ describe("Moves - Protect", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - }, TIMEOUT + } ); test( @@ -72,7 +72,7 @@ describe("Moves - Protect", () => { expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); expect(game.scene.arena.getTagOnSide(ArenaTrapTag, ArenaTagSide.ENEMY)).toBeUndefined(); - }, TIMEOUT + } ); test( @@ -89,7 +89,7 @@ describe("Moves - Protect", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.getStatStage(Stat.ATK)).toBe(0); - }, TIMEOUT + } ); test( @@ -108,7 +108,7 @@ describe("Moves - Protect", () => { expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); expect(enemyPokemon.turnData.hitCount).toBe(1); - }, TIMEOUT + } ); test( @@ -129,6 +129,6 @@ describe("Moves - Protect", () => { expect(enemyPokemon.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); expect(leadPokemon.getLastXMoves()[0].result).toBe(MoveResult.FAIL); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/purify.test.ts b/src/test/moves/purify.test.ts index 15d684b2d60..3ba9dfcbb65 100644 --- a/src/test/moves/purify.test.ts +++ b/src/test/moves/purify.test.ts @@ -8,7 +8,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Moves - Purify", () => { let phaserGame: Phaser.Game; @@ -55,7 +55,6 @@ describe("Moves - Purify", () => { expect(enemyPokemon.status).toBeNull(); expect(playerPokemon.isFullHp()).toBe(true); }, - TIMEOUT ); test( @@ -74,7 +73,6 @@ describe("Moves - Purify", () => { expect(playerPokemon.hp).toBe(playerInitialHp); }, - TIMEOUT ); }); diff --git a/src/test/moves/quick_guard.test.ts b/src/test/moves/quick_guard.test.ts index 9ab0fe1509c..e03beeac06a 100644 --- a/src/test/moves/quick_guard.test.ts +++ b/src/test/moves/quick_guard.test.ts @@ -8,7 +8,7 @@ import { Stat } from "#enums/stat"; import { BattlerIndex } from "#app/battle"; import { MoveResult } from "#app/field/pokemon"; -const TIMEOUT = 20 * 1000; + describe("Moves - Quick Guard", () => { let phaserGame: Phaser.Game; @@ -52,7 +52,7 @@ describe("Moves - Quick Guard", () => { await game.phaseInterceptor.to("BerryPhase", false); playerPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp())); - }, TIMEOUT + } ); test( @@ -71,7 +71,7 @@ describe("Moves - Quick Guard", () => { await game.phaseInterceptor.to("BerryPhase", false); playerPokemon.forEach(p => expect(p.getStatStage(Stat.ATK)).toBe(0)); - }, TIMEOUT + } ); test( @@ -113,6 +113,6 @@ describe("Moves - Quick Guard", () => { 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 86bc48ef882..bb31a1f2194 100644 --- a/src/test/moves/rage_powder.test.ts +++ b/src/test/moves/rage_powder.test.ts @@ -6,7 +6,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Moves - Rage Powder", () => { let phaserGame: Phaser.Game; @@ -50,7 +50,7 @@ describe("Moves - Rage Powder", () => { // If redirection was bypassed, both enemies should be damaged expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp()); expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[0].getMaxHp()); - }, TIMEOUT + } ); test( @@ -76,6 +76,6 @@ describe("Moves - Rage Powder", () => { // If redirection was bypassed, both enemies should be damaged expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]); expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/relic_song.test.ts b/src/test/moves/relic_song.test.ts index 373d88f0434..67fc557a318 100644 --- a/src/test/moves/relic_song.test.ts +++ b/src/test/moves/relic_song.test.ts @@ -10,8 +10,6 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Relic Song", () => { let phaserGame: Phaser.Game; let game: GameManager; - const TIMEOUT = 20 * 1000; - beforeAll(() => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, @@ -47,7 +45,7 @@ describe("Moves - Relic Song", () => { await game.phaseInterceptor.to("BerryPhase"); expect(meloetta.formIndex).toBe(0); - }, TIMEOUT); + }); it("doesn't swap Meloetta's form during a mono-type challenge", async () => { game.challengeMode.addChallenge(Challenges.SINGLE_TYPE, Type.PSYCHIC + 1, 0); @@ -62,7 +60,7 @@ describe("Moves - Relic Song", () => { await game.toNextTurn(); expect(meloetta.formIndex).toBe(0); - }, TIMEOUT); + }); it("doesn't swap Meloetta's form during biome change (arena reset)", async () => { game.override @@ -77,5 +75,5 @@ describe("Moves - Relic Song", () => { await game.toNextWave(); expect(meloetta.formIndex).toBe(1); - }, TIMEOUT); + }); }); diff --git a/src/test/moves/roost.test.ts b/src/test/moves/roost.test.ts index c1fc962e876..a1c473c0632 100644 --- a/src/test/moves/roost.test.ts +++ b/src/test/moves/roost.test.ts @@ -9,7 +9,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Moves - Roost", () => { let phaserGame: Phaser.Game; @@ -72,7 +72,7 @@ describe("Moves - Roost", () => { expect(playerPokemonTypes[0] === Type.NORMAL).toBeTruthy(); expect(playerPokemonTypes.length === 1).toBeTruthy(); expect(playerPokemon.isGrounded()).toBeTruthy(); - }, TIMEOUT + } ); test( @@ -100,7 +100,7 @@ describe("Moves - Roost", () => { expect(playerPokemonTypes[0] === Type.FLYING).toBeTruthy(); expect(playerPokemon.isGrounded()).toBeFalsy(); - }, TIMEOUT + } ); test( @@ -128,7 +128,7 @@ describe("Moves - Roost", () => { expect(playerPokemonTypes[1] === Type.FLYING).toBeTruthy(); expect(playerPokemon.isGrounded()).toBeFalsy(); - }, TIMEOUT + } ); test( @@ -157,7 +157,7 @@ describe("Moves - Roost", () => { expect(playerPokemonTypes[1] === Type.FLYING).toBeTruthy(); expect(playerPokemon.isGrounded()).toBeFalsy(); - }, TIMEOUT + } ); test( @@ -196,7 +196,7 @@ describe("Moves - Roost", () => { expect(playerPokemonTypes.length === 1).toBeTruthy(); expect(playerPokemon.isGrounded()).toBeFalsy(); - }, TIMEOUT + } ); test( @@ -236,7 +236,7 @@ describe("Moves - Roost", () => { expect(playerPokemonTypes.length === 1).toBeTruthy(); expect(playerPokemon.isGrounded()).toBeFalsy(); - }, TIMEOUT + } ); test( @@ -263,7 +263,7 @@ describe("Moves - Roost", () => { expect(playerPokemonTypes.length === 3).toBeTruthy(); expect(playerPokemon.isGrounded()).toBeFalsy(); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/safeguard.test.ts b/src/test/moves/safeguard.test.ts index 2caf698a73a..b21698d0298 100644 --- a/src/test/moves/safeguard.test.ts +++ b/src/test/moves/safeguard.test.ts @@ -8,7 +8,7 @@ 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; @@ -46,7 +46,7 @@ describe("Moves - Safeguard", () => { await game.toNextTurn(); expect(enemy.status).toBeUndefined(); - }, TIMEOUT); + }); it("protects from status moves", async () => { await game.classicMode.startBattle(); @@ -57,7 +57,7 @@ describe("Moves - Safeguard", () => { await game.toNextTurn(); expect(enemyPokemon.status).toBeUndefined(); - }, TIMEOUT); + }); it("protects from confusion", async () => { game.override.moveset([Moves.CONFUSE_RAY]); @@ -69,7 +69,7 @@ describe("Moves - Safeguard", () => { await game.toNextTurn(); expect(enemyPokemon.summonData.tags).toEqual([]); - }, TIMEOUT); + }); it("protects ally from status", async () => { game.override.battleType("double"); @@ -87,7 +87,7 @@ describe("Moves - Safeguard", () => { expect(enemyPokemon[0].status).toBeUndefined(); expect(enemyPokemon[1].status).toBeUndefined(); - }, TIMEOUT); + }); it("protects from Yawn", async () => { await game.classicMode.startBattle(); @@ -98,7 +98,7 @@ describe("Moves - Safeguard", () => { await game.toNextTurn(); expect(enemyPokemon.summonData.tags).toEqual([]); - }, TIMEOUT); + }); it("doesn't protect from already existing Yawn", async () => { await game.classicMode.startBattle(); @@ -112,7 +112,7 @@ describe("Moves - Safeguard", () => { 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"}]); @@ -135,7 +135,7 @@ describe("Moves - Safeguard", () => { await game.toNextTurn(); expect(enemyPokemon.status?.effect).toEqual(StatusEffect.SLEEP); - }, TIMEOUT); + }); it("protects from ability-inflicted status", async () => { game.override.ability(Abilities.STATIC); @@ -151,5 +151,5 @@ describe("Moves - Safeguard", () => { await game.toNextTurn(); expect(enemyPokemon.status).toBeUndefined(); - }, TIMEOUT); + }); }); diff --git a/src/test/moves/shell_side_arm.test.ts b/src/test/moves/shell_side_arm.test.ts new file mode 100644 index 00000000000..ded7ed82fd1 --- /dev/null +++ b/src/test/moves/shell_side_arm.test.ts @@ -0,0 +1,85 @@ +import { BattlerIndex } from "#app/battle"; +import { allMoves, ShellSideArmCategoryAttr } from "#app/data/move"; +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, vi } from "vitest"; + +describe("Moves - Shell Side Arm", () => { + 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 + .moveset([Moves.SHELL_SIDE_ARM]) + .battleType("single") + .startingLevel(100) + .enemyLevel(100) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("becomes a physical attack if forecasted to deal more damage as physical", async () => { + game.override.enemySpecies(Species.SNORLAX); + + await game.classicMode.startBattle([Species.MANAPHY]); + + const shellSideArm = allMoves[Moves.SHELL_SIDE_ARM]; + const shellSideArmAttr = shellSideArm.getAttrs(ShellSideArmCategoryAttr)[0]; + vi.spyOn(shellSideArmAttr, "apply"); + + game.move.select(Moves.SHELL_SIDE_ARM); + + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(shellSideArmAttr.apply).toHaveLastReturnedWith(true); + }); + + it("remains a special attack if forecasted to deal more damage as special", async () => { + game.override.enemySpecies(Species.SLOWBRO); + + await game.classicMode.startBattle([Species.MANAPHY]); + + const shellSideArm = allMoves[Moves.SHELL_SIDE_ARM]; + const shellSideArmAttr = shellSideArm.getAttrs(ShellSideArmCategoryAttr)[0]; + vi.spyOn(shellSideArmAttr, "apply"); + + game.move.select(Moves.SHELL_SIDE_ARM); + + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(shellSideArmAttr.apply).toHaveLastReturnedWith(false); + }); + + it("respects stat stage changes when forecasting base damage", async () => { + game.override + .enemySpecies(Species.SNORLAX) + .enemyMoveset(Moves.COTTON_GUARD); + + await game.classicMode.startBattle([Species.MANAPHY]); + + const shellSideArm = allMoves[Moves.SHELL_SIDE_ARM]; + const shellSideArmAttr = shellSideArm.getAttrs(ShellSideArmCategoryAttr)[0]; + vi.spyOn(shellSideArmAttr, "apply"); + + game.move.select(Moves.SHELL_SIDE_ARM); + + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + + await game.phaseInterceptor.to("BerryPhase", false); + + expect(shellSideArmAttr.apply).toHaveLastReturnedWith(false); + }); +}); diff --git a/src/test/moves/shell_trap.test.ts b/src/test/moves/shell_trap.test.ts index 213b9c3fd0a..1dae00e24a5 100644 --- a/src/test/moves/shell_trap.test.ts +++ b/src/test/moves/shell_trap.test.ts @@ -10,7 +10,7 @@ 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 - Shell Trap", () => { let phaserGame: Phaser.Game; @@ -60,7 +60,7 @@ describe("Moves - Shell Trap", () => { await game.phaseInterceptor.to(MoveEndPhase); enemyPokemon.forEach(p => expect(p.hp).toBeLessThan(p.getMaxHp())); - }, TIMEOUT + } ); it( @@ -86,7 +86,7 @@ describe("Moves - Shell Trap", () => { await game.phaseInterceptor.to(BerryPhase, false); enemyPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp())); - }, TIMEOUT + } ); it( @@ -112,7 +112,7 @@ describe("Moves - Shell Trap", () => { await game.phaseInterceptor.to(BerryPhase, false); enemyPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp())); - }, TIMEOUT + } ); it( @@ -138,7 +138,7 @@ describe("Moves - Shell Trap", () => { await game.phaseInterceptor.to(BerryPhase, false); enemyPokemon.forEach((p, i) => expect(p.hp).toBe(enemyStartingHp[i])); - }, TIMEOUT + } ); it( @@ -158,6 +158,6 @@ describe("Moves - Shell Trap", () => { expect(playerPokemon.getLastXMoves()[0].result).toBe(MoveResult.FAIL); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/spit_up.test.ts b/src/test/moves/spit_up.test.ts index acf7f01d991..412360c2664 100644 --- a/src/test/moves/spit_up.test.ts +++ b/src/test/moves/spit_up.test.ts @@ -43,7 +43,7 @@ describe("Moves - Spit Up", () => { }); describe("consumes all stockpile stacks to deal damage (scaling with stacks)", () => { - it("1 stack -> 100 power", { timeout: 10000 }, async () => { + it("1 stack -> 100 power", async () => { const stacksToSetup = 1; const expectedPower = 100; @@ -65,7 +65,7 @@ describe("Moves - Spit Up", () => { expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); - it("2 stacks -> 200 power", { timeout: 10000 }, async () => { + it("2 stacks -> 200 power", async () => { const stacksToSetup = 2; const expectedPower = 200; @@ -88,7 +88,7 @@ describe("Moves - Spit Up", () => { expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); - it("3 stacks -> 300 power", { timeout: 10000 }, async () => { + it("3 stacks -> 300 power", async () => { const stacksToSetup = 3; const expectedPower = 300; @@ -113,7 +113,7 @@ describe("Moves - Spit Up", () => { }); }); - it("fails without stacks", { timeout: 10000 }, async () => { + it("fails without stacks", async () => { await game.startBattle([Species.ABOMASNOW]); const pokemon = game.scene.getPlayerPokemon()!; @@ -130,7 +130,7 @@ describe("Moves - Spit Up", () => { }); describe("restores stat boosts granted by stacks", () => { - it("decreases stats based on stored values (both boosts equal)", { timeout: 10000 }, async () => { + it("decreases stats based on stored values (both boosts equal)", async () => { await game.startBattle([Species.ABOMASNOW]); const pokemon = game.scene.getPlayerPokemon()!; @@ -157,7 +157,7 @@ describe("Moves - Spit Up", () => { expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); - it("decreases stats based on stored values (different boosts)", { timeout: 10000 }, async () => { + it("decreases stats based on stored values (different boosts)", async () => { await game.startBattle([Species.ABOMASNOW]); const pokemon = game.scene.getPlayerPokemon()!; diff --git a/src/test/moves/spotlight.test.ts b/src/test/moves/spotlight.test.ts index aef44369642..6324c3dc6ec 100644 --- a/src/test/moves/spotlight.test.ts +++ b/src/test/moves/spotlight.test.ts @@ -6,7 +6,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -const TIMEOUT = 20 * 1000; + describe("Moves - Spotlight", () => { let phaserGame: Phaser.Game; @@ -50,7 +50,7 @@ describe("Moves - Spotlight", () => { expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp()); expect(enemyPokemon[1].hp).toBe(enemyPokemon[1].getMaxHp()); - }, TIMEOUT + } ); test( @@ -70,6 +70,6 @@ describe("Moves - Spotlight", () => { expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp()); expect(enemyPokemon[1].hp).toBe(enemyPokemon[1].getMaxHp()); - }, TIMEOUT + } ); }); diff --git a/src/test/moves/steamroller.test.ts b/src/test/moves/steamroller.test.ts new file mode 100644 index 00000000000..cbbb3a22593 --- /dev/null +++ b/src/test/moves/steamroller.test.ts @@ -0,0 +1,58 @@ +import { BattlerIndex } from "#app/battle"; +import { allMoves } from "#app/data/move"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { DamageCalculationResult } 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 Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Moves - Steamroller", () => { + 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.moveset([Moves.STEAMROLLER]).battleType("single").enemyAbility(Abilities.BALL_FETCH); + }); + + it("should always hit a minimzed target with double damage", async () => { + game.override.enemySpecies(Species.DITTO).enemyMoveset(Moves.MINIMIZE); + await game.classicMode.startBattle([Species.IRON_BOULDER]); + + const ditto = game.scene.getEnemyPokemon()!; + vi.spyOn(ditto, "getAttackDamage"); + ditto.hp = 5000; + const steamroller = allMoves[Moves.STEAMROLLER]; + vi.spyOn(steamroller, "calculateBattleAccuracy"); + const ironBoulder = game.scene.getPlayerPokemon()!; + vi.spyOn(ironBoulder, "getAccuracyMultiplier"); + // Turn 1 + game.move.select(Moves.STEAMROLLER); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toNextTurn(); + // Turn 2 + game.move.select(Moves.STEAMROLLER); + await game.toNextTurn(); + + const [dmgCalcTurn1, dmgCalcTurn2]: DamageCalculationResult[] = vi + .mocked(ditto.getAttackDamage) + .mock.results.map((r) => r.value); + + expect(dmgCalcTurn2.damage).toBeGreaterThanOrEqual(dmgCalcTurn1.damage * 2); + expect(ditto.getTag(BattlerTagType.MINIMIZED)).toBeDefined(); + expect(steamroller.calculateBattleAccuracy).toHaveReturnedWith(-1); + }); +}); diff --git a/src/test/moves/stockpile.test.ts b/src/test/moves/stockpile.test.ts index 8e7a44d053b..141ce79eb33 100644 --- a/src/test/moves/stockpile.test.ts +++ b/src/test/moves/stockpile.test.ts @@ -37,7 +37,7 @@ describe("Moves - Stockpile", () => { game.override.ability(Abilities.NONE); }); - 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 () => { + it("gains a stockpile stack and raises user's DEF and SPDEF stat stages by 1 on each use, fails at max stacks (3)", async () => { await game.startBattle([Species.ABOMASNOW]); const user = game.scene.getPlayerPokemon()!; @@ -76,7 +76,7 @@ describe("Moves - Stockpile", () => { } }); - it("gains a stockpile stack even if user's DEF and SPDEF stat stages are at +6", { timeout: 10000 }, async () => { + it("gains a stockpile stack even if user's DEF and SPDEF stat stages are at +6", async () => { await game.startBattle([Species.ABOMASNOW]); const user = game.scene.getPlayerPokemon()!; diff --git a/src/test/moves/substitute.test.ts b/src/test/moves/substitute.test.ts index 3976247d489..6c18579e7f6 100644 --- a/src/test/moves/substitute.test.ts +++ b/src/test/moves/substitute.test.ts @@ -16,8 +16,6 @@ import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -const TIMEOUT = 20 * 1000; // 20 sec timeout - describe("Moves - Substitute", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -57,7 +55,7 @@ describe("Moves - Substitute", () => { await game.phaseInterceptor.to("MoveEndPhase", false); expect(leadPokemon.hp).toBe(Math.ceil(leadPokemon.getMaxHp() * 3/4)); - }, TIMEOUT + } ); it( @@ -81,7 +79,7 @@ describe("Moves - Substitute", () => { expect(leadPokemon.hp).toBe(postSubHp); expect(leadPokemon.getTag(BattlerTagType.SUBSTITUTE)).toBeDefined(); - }, TIMEOUT + } ); it( @@ -107,7 +105,7 @@ describe("Moves - Substitute", () => { expect(leadPokemon.hp).toBe(postSubHp); expect(leadPokemon.getTag(BattlerTagType.SUBSTITUTE)).toBeUndefined(); - }, TIMEOUT + } ); it( @@ -148,7 +146,7 @@ describe("Moves - Substitute", () => { expect(leadPokemon.getTag(BattlerTagType.SUBSTITUTE)).toBeDefined(); expect(leadPokemon.hp).toBeLessThan(postSubHp); - }, TIMEOUT + } ); it( @@ -172,7 +170,7 @@ describe("Moves - Substitute", () => { expect(leadPokemon.getTag(BattlerTagType.SUBSTITUTE)).toBeDefined(); expect(leadPokemon.hp).toBeLessThan(postSubHp); - }, TIMEOUT + } ); it( @@ -192,7 +190,7 @@ describe("Moves - Substitute", () => { await game.phaseInterceptor.to("MoveEndPhase", false); expect(leadPokemon.getStatStage(Stat.ATK)).toBe(2); - }, TIMEOUT + } ); it( @@ -213,7 +211,7 @@ describe("Moves - Substitute", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); - }, TIMEOUT + } ); it( @@ -233,7 +231,7 @@ describe("Moves - Substitute", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.getTag(TrappedTag)).toBeUndefined(); - }, TIMEOUT + } ); it( @@ -253,7 +251,7 @@ describe("Moves - Substitute", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.getStatStage(Stat.DEF)).toBe(0); - }, TIMEOUT + } ); it( @@ -272,7 +270,7 @@ describe("Moves - Substitute", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.status?.effect).not.toBe(StatusEffect.PARALYSIS); - }, TIMEOUT + } ); it( @@ -293,7 +291,7 @@ describe("Moves - Substitute", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.getHeldItems().length).toBe(1); - }, TIMEOUT + } ); it( @@ -314,7 +312,7 @@ describe("Moves - Substitute", () => { await game.phaseInterceptor.to("MoveEndPhase", false); expect(enemyPokemon.getHeldItems().length).toBe(enemyNumItems); - }, TIMEOUT + } ); it( @@ -339,7 +337,7 @@ describe("Moves - Substitute", () => { expect(leadPokemon.getHeldItems().length).toBe(1); expect(enemyPokemon.hp).toBe(enemyPostAttackHp); - }, TIMEOUT + } ); it( @@ -358,7 +356,7 @@ describe("Moves - Substitute", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(leadPokemon.getStatStage(Stat.ATK)).toBe(2); - }, TIMEOUT + } ); it( @@ -404,7 +402,7 @@ describe("Moves - Substitute", () => { const subTag = switchedPokemon.getTag(SubstituteTag)!; expect(subTag).toBeDefined(); expect(subTag.hp).toBe(Math.floor(leadPokemon.getMaxHp() * 1/4)); - }, TIMEOUT + } ); it( @@ -422,7 +420,7 @@ describe("Moves - Substitute", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - }, TIMEOUT + } ); it( @@ -447,7 +445,7 @@ describe("Moves - Substitute", () => { expect(playerPokemon.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); - }, TIMEOUT + } ); it( @@ -467,7 +465,7 @@ describe("Moves - Substitute", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(playerPokemon.getLastXMoves()[0].result).toBe(MoveResult.FAIL); - }, TIMEOUT + } ); it( @@ -488,7 +486,7 @@ describe("Moves - Substitute", () => { await game.phaseInterceptor.to("MoveEndPhase"); expect(enemyPokemon.status?.effect).not.toBe(StatusEffect.BURN); - }, TIMEOUT + } ); it( diff --git a/src/test/moves/swallow.test.ts b/src/test/moves/swallow.test.ts index 5a0e63e6e78..b8ca941d0ee 100644 --- a/src/test/moves/swallow.test.ts +++ b/src/test/moves/swallow.test.ts @@ -38,7 +38,7 @@ describe("Moves - Swallow", () => { }); describe("consumes all stockpile stacks to heal (scaling with stacks)", () => { - it("1 stack -> 25% heal", { timeout: 10000 }, async () => { + it("1 stack -> 25% heal", async () => { const stacksToSetup = 1; const expectedHeal = 25; @@ -65,7 +65,7 @@ describe("Moves - Swallow", () => { expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); - it("2 stacks -> 50% heal", { timeout: 10000 }, async () => { + it("2 stacks -> 50% heal", async () => { const stacksToSetup = 2; const expectedHeal = 50; @@ -93,7 +93,7 @@ describe("Moves - Swallow", () => { expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); - it("3 stacks -> 100% heal", { timeout: 10000 }, async () => { + it("3 stacks -> 100% heal", async () => { const stacksToSetup = 3; const expectedHeal = 100; @@ -123,7 +123,7 @@ describe("Moves - Swallow", () => { }); }); - it("fails without stacks", { timeout: 10000 }, async () => { + it("fails without stacks", async () => { await game.startBattle([Species.ABOMASNOW]); const pokemon = game.scene.getPlayerPokemon()!; @@ -138,7 +138,7 @@ describe("Moves - Swallow", () => { }); describe("restores stat stage boosts granted by stacks", () => { - it("decreases stats based on stored values (both boosts equal)", { timeout: 10000 }, async () => { + it("decreases stats based on stored values (both boosts equal)", async () => { await game.startBattle([Species.ABOMASNOW]); const pokemon = game.scene.getPlayerPokemon()!; @@ -163,7 +163,7 @@ describe("Moves - Swallow", () => { expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); - it("lower stat stages based on stored values (different boosts)", { timeout: 10000 }, async () => { + it("lower stat stages based on stored values (different boosts)", async () => { await game.startBattle([Species.ABOMASNOW]); const pokemon = game.scene.getPlayerPokemon()!; diff --git a/src/test/moves/tar_shot.test.ts b/src/test/moves/tar_shot.test.ts index 2963f061fc6..2385bd18265 100644 --- a/src/test/moves/tar_shot.test.ts +++ b/src/test/moves/tar_shot.test.ts @@ -11,8 +11,6 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite describe("Moves - Tar Shot", () => { let phaserGame: Phaser.Game; let game: GameManager; - const TIMEOUT = 20 * 1000; - beforeAll(() => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, @@ -54,7 +52,7 @@ describe("Moves - Tar Shot", () => { 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]); @@ -82,7 +80,7 @@ describe("Moves - Tar Shot", () => { 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); @@ -104,7 +102,7 @@ describe("Moves - Tar Shot", () => { 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); @@ -128,5 +126,5 @@ describe("Moves - Tar Shot", () => { await game.phaseInterceptor.to("MoveEndPhase"); expect(enemy.getMoveEffectiveness).toHaveReturnedWith(4); - }, TIMEOUT); + }); }); diff --git a/src/test/moves/thousand_arrows.test.ts b/src/test/moves/thousand_arrows.test.ts index 8d1d6ee5f4a..ad9281dc45e 100644 --- a/src/test/moves/thousand_arrows.test.ts +++ b/src/test/moves/thousand_arrows.test.ts @@ -8,7 +8,7 @@ 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 - Thousand Arrows", () => { let phaserGame: Phaser.Game; @@ -51,7 +51,7 @@ describe("Moves - Thousand Arrows", () => { expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); - }, TIMEOUT + } ); it( @@ -74,7 +74,7 @@ describe("Moves - Thousand Arrows", () => { expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); - }, TIMEOUT + } ); it( diff --git a/src/test/moves/throat_chop.test.ts b/src/test/moves/throat_chop.test.ts index cb34b4bafff..2a0ab675b25 100644 --- a/src/test/moves/throat_chop.test.ts +++ b/src/test/moves/throat_chop.test.ts @@ -10,8 +10,6 @@ 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, @@ -53,5 +51,5 @@ describe("Moves - Throat Chop", () => { 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 7ad59518013..28c5da4717b 100644 --- a/src/test/moves/thunder_wave.test.ts +++ b/src/test/moves/thunder_wave.test.ts @@ -7,7 +7,7 @@ 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 - Thunder Wave", () => { let phaserGame: Phaser.Game; @@ -45,7 +45,7 @@ describe("Moves - Thunder Wave", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.status?.effect).toBe(StatusEffect.PARALYSIS); - }, TIMEOUT); + }); it("does not paralyze if the Pokemon is a Ground-type", async () => { game.override.enemySpecies(Species.DIGLETT); @@ -58,7 +58,7 @@ describe("Moves - Thunder Wave", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.status).toBeUndefined(); - }, TIMEOUT); + }); it("does not paralyze if the Pokemon already has a status effect", async () => { game.override.enemySpecies(Species.MAGIKARP).enemyStatusEffect(StatusEffect.BURN); @@ -71,7 +71,7 @@ describe("Moves - Thunder Wave", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.status?.effect).not.toBe(StatusEffect.PARALYSIS); - }, TIMEOUT); + }); it("affects Ground types if the user has Normalize", async () => { game.override.ability(Abilities.NORMALIZE).enemySpecies(Species.DIGLETT); @@ -84,7 +84,7 @@ describe("Moves - Thunder Wave", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.status?.effect).toBe(StatusEffect.PARALYSIS); - }, TIMEOUT); + }); it("does not affect Ghost types if the user has Normalize", async () => { game.override.ability(Abilities.NORMALIZE).enemySpecies(Species.HAUNTER); @@ -97,5 +97,5 @@ describe("Moves - Thunder Wave", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.status).toBeUndefined(); - }, TIMEOUT); + }); }); diff --git a/src/test/moves/whirlwind.test.ts b/src/test/moves/whirlwind.test.ts new file mode 100644 index 00000000000..a591a3cd6c5 --- /dev/null +++ b/src/test/moves/whirlwind.test.ts @@ -0,0 +1,53 @@ +import { BattlerIndex } from "#app/battle"; +import { allMoves } from "#app/data/move"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +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, vi } from "vitest"; + +describe("Moves - Whirlwind", () => { + 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.WHIRLWIND) + .enemySpecies(Species.PIDGEY); + }); + + it.each([ + { move: Moves.FLY, name: "Fly" }, + { move: Moves.BOUNCE, name: "Bounce" }, + { move: Moves.SKY_DROP, name: "Sky Drop" }, + ])("should not hit a flying target: $name (=$move)", async ({ move }) => { + game.override.moveset([move]); + await game.classicMode.startBattle([Species.STARAPTOR]); + + const staraptor = game.scene.getPlayerPokemon()!; + const whirlwind = allMoves[Moves.WHIRLWIND]; + vi.spyOn(whirlwind, "getFailedText"); + + game.move.select(move); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toNextTurn(); + + expect(staraptor.findTag((t) => t.tagType === BattlerTagType.FLYING)).toBeDefined(); + expect(whirlwind.getFailedText).toHaveBeenCalledOnce(); + }); +}); diff --git a/src/test/moves/wide_guard.test.ts b/src/test/moves/wide_guard.test.ts index b4e6e305539..9ddd8905ff6 100644 --- a/src/test/moves/wide_guard.test.ts +++ b/src/test/moves/wide_guard.test.ts @@ -8,7 +8,7 @@ import { Stat } from "#enums/stat"; import { BerryPhase } from "#app/phases/berry-phase"; import { CommandPhase } from "#app/phases/command-phase"; -const TIMEOUT = 20 * 1000; + describe("Moves - Wide Guard", () => { let phaserGame: Phaser.Game; @@ -55,7 +55,7 @@ describe("Moves - Wide Guard", () => { await game.phaseInterceptor.to(BerryPhase, false); leadPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp())); - }, TIMEOUT + } ); test( @@ -76,7 +76,7 @@ describe("Moves - Wide Guard", () => { await game.phaseInterceptor.to(BerryPhase, false); leadPokemon.forEach(p => expect(p.getStatStage(Stat.ATK)).toBe(0)); - }, TIMEOUT + } ); test( @@ -97,7 +97,7 @@ describe("Moves - Wide Guard", () => { await game.phaseInterceptor.to(BerryPhase, false); expect(leadPokemon.some(p => p.hp < p.getMaxHp())).toBeTruthy(); - }, TIMEOUT + } ); test( @@ -120,6 +120,6 @@ describe("Moves - Wide Guard", () => { expect(leadPokemon[0].hp).toBe(leadPokemon[0].getMaxHp()); enemyPokemon.forEach(p => expect(p.hp).toBeLessThan(p.getMaxHp())); - }, TIMEOUT + } ); }); diff --git a/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts b/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts index b4cc186864c..3dc90427eb2 100644 --- a/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts @@ -69,22 +69,6 @@ describe("A Trainer's Test - Mystery Encounter", () => { expect(ATrainersTestEncounter.options.length).toBe(2); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.A_TRAINERS_TEST); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully ", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = ATrainersTestEncounter; diff --git a/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts b/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts index 7cca7abba27..58c8e1fbc30 100644 --- a/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts @@ -66,22 +66,6 @@ describe("Absolute Avarice - Mystery Encounter", () => { expect(AbsoluteAvariceEncounter.options.length).toBe(3); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.ABSOLUTE_AVARICE); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should not spawn outside of proper biomes", async () => { game.override.mysteryEncounterTier(MysteryEncounterTier.GREAT); game.override.startingBiome(Biome.VOLCANO); diff --git a/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts b/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts index 1c68852a63d..c39e636b462 100644 --- a/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts @@ -80,22 +80,6 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => { expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully ", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = AnOfferYouCantRefuseEncounter; @@ -247,7 +231,7 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => { }); describe("Option 3 - Leave", () => { - it("should leave encounter without battle", async () => { + it.each(Array.from({length: 30}))("should leave encounter without battle", async () => { const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); await game.runToMysteryEncounter(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE, defaultParty); diff --git a/src/test/mystery-encounter/encounters/berries-abound-encounter.test.ts b/src/test/mystery-encounter/encounters/berries-abound-encounter.test.ts index 73ffad36258..78f4a477216 100644 --- a/src/test/mystery-encounter/encounters/berries-abound-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/berries-abound-encounter.test.ts @@ -19,7 +19,7 @@ import { CommandPhase } from "#app/phases/command-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; const namespace = "mysteryEncounter:berriesAbound"; -const defaultParty = [Species.PYUKUMUKU]; +const defaultParty = [Species.PYUKUMUKU, Species.MAGIKARP, Species.PIKACHU]; const defaultBiome = Biome.CAVE; const defaultWave = 45; @@ -69,22 +69,6 @@ describe("Berries Abound - Mystery Encounter", () => { expect(BerriesAboundEncounter.options.length).toBe(3); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.BERRIES_ABOUND); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = BerriesAboundEncounter; @@ -98,7 +82,6 @@ describe("Berries Abound - Mystery Encounter", () => { const config = BerriesAboundEncounter.enemyPartyConfigs[0]; expect(config).toBeDefined(); - expect(config.levelAdditiveMultiplier).toBe(1); expect(config.pokemonConfigs?.[0].isBoss).toBe(true); expect(onInitResult).toBe(true); }); @@ -133,8 +116,10 @@ describe("Berries Abound - Mystery Encounter", () => { expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); }); - // TODO: there is some severe test flakiness occurring for this file, needs to be looked at/addressed in separate issue - it.skip("should reward the player with X berries based on wave", async () => { + /** + * Related issue-comment: {@link https://github.com/pagefaultgames/pokerogue/issues/4300#issuecomment-2362849444} + */ + it("should reward the player with X berries based on wave", async () => { await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty); const numBerries = game.scene.currentBattle.mysteryEncounter!.misc.numBerries; @@ -205,15 +190,14 @@ describe("Berries Abound - Mystery Encounter", () => { }); it("Should skip battle when fastest pokemon is faster than boss", async () => { - const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); - const encounterTextSpy = vi.spyOn(EncounterDialogueUtils, "showEncounterText"); + vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); + vi.spyOn(EncounterDialogueUtils, "showEncounterText"); await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty); - // Setting party pokemon's level arbitrarily high to outspeed - const fastestPokemon = scene.getParty()[0]; - fastestPokemon.level = 1000; - fastestPokemon.calculateStats(); + scene.getParty().forEach(pkm => { + vi.spyOn(pkm, "getStat").mockReturnValue(9999); // for ease return for every stat + }); await runMysteryEncounterToEnd(game, 2); await game.phaseInterceptor.to(SelectModifierPhase, false); @@ -227,8 +211,8 @@ describe("Berries Abound - Mystery Encounter", () => { expect(option.modifierTypeOption.type.id).toContain("BERRY"); } - expect(encounterTextSpy).toHaveBeenCalledWith(expect.any(BattleScene), `${namespace}.option.2.selected`); - expect(leaveEncounterWithoutBattleSpy).toBeCalled(); + expect(EncounterDialogueUtils.showEncounterText).toHaveBeenCalledWith(expect.any(BattleScene), `${namespace}.option.2.selected`); + expect(EncounterPhaseUtils.leaveEncounterWithoutBattle).toBeCalled(); }); }); diff --git a/src/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts b/src/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts index 70adf93d502..247acc9e5b6 100644 --- a/src/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts @@ -201,22 +201,6 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { expect(BugTypeSuperfanEncounter.options.length).toBe(3); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.BUG_TYPE_SUPERFAN); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = BugTypeSuperfanEncounter; diff --git a/src/test/mystery-encounter/encounters/clowning-around-encounter.test.ts b/src/test/mystery-encounter/encounters/clowning-around-encounter.test.ts index 383e3bd3564..5ed5a9487de 100644 --- a/src/test/mystery-encounter/encounters/clowning-around-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/clowning-around-encounter.test.ts @@ -95,14 +95,6 @@ describe("Clowning Around - Mystery Encounter", () => { expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.CLOWNING_AROUND); }); - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = ClowningAroundEncounter; diff --git a/src/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts b/src/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts index 5a2512ddaf6..cbf8251f2e7 100644 --- a/src/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts @@ -69,22 +69,6 @@ describe("Dancing Lessons - Mystery Encounter", () => { expect(DancingLessonsEncounter.options.length).toBe(3); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DANCING_LESSONS); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should not spawn outside of proper biomes", async () => { game.override.mysteryEncounterTier(MysteryEncounterTier.GREAT); game.override.startingBiome(Biome.SPACE); diff --git a/src/test/mystery-encounter/encounters/delibirdy-encounter.test.ts b/src/test/mystery-encounter/encounters/delibirdy-encounter.test.ts index 969188dca06..7e452fd90c7 100644 --- a/src/test/mystery-encounter/encounters/delibirdy-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/delibirdy-encounter.test.ts @@ -66,22 +66,6 @@ describe("Delibird-y - Mystery Encounter", () => { expect(DelibirdyEncounter.options.length).toBe(3); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DELIBIRDY); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should not spawn if player does not have enough money", async () => { scene.money = 0; diff --git a/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts b/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts index f22bd832964..0b2d66db20b 100644 --- a/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts @@ -79,22 +79,6 @@ describe("Department Store Sale - Mystery Encounter", () => { expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DEPARTMENT_STORE_SALE); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DEPARTMENT_STORE_SALE); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - describe("Option 1 - TM Shop", () => { it("should have the correct properties", () => { const option = DepartmentStoreSaleEncounter.options[0]; diff --git a/src/test/mystery-encounter/encounters/field-trip-encounter.test.ts b/src/test/mystery-encounter/encounters/field-trip-encounter.test.ts index 7a8d951c5da..13550abb97c 100644 --- a/src/test/mystery-encounter/encounters/field-trip-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/field-trip-encounter.test.ts @@ -72,22 +72,6 @@ describe("Field Trip - Mystery Encounter", () => { expect(FieldTripEncounter.options.length).toBe(3); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.FIELD_TRIP); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - describe("Option 1 - Show off a physical move", () => { it("should have the correct properties", () => { const option = FieldTripEncounter.options[0]; diff --git a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts index 445ab4491a4..cd11aa2628b 100644 --- a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts @@ -88,14 +88,6 @@ describe("Fiery Fallout - Mystery Encounter", () => { expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT); }); - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully ", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = FieryFalloutEncounter; diff --git a/src/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts b/src/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts index 735dcc709bf..df2f32231ba 100644 --- a/src/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts @@ -67,22 +67,6 @@ describe("Fight or Flight - Mystery Encounter", () => { expect(FightOrFlightEncounter.options.length).toBe(3); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.FIGHT_OR_FLIGHT); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = FightOrFlightEncounter; @@ -96,7 +80,6 @@ describe("Fight or Flight - Mystery Encounter", () => { const config = FightOrFlightEncounter.enemyPartyConfigs[0]; expect(config).toBeDefined(); - expect(config.levelAdditiveMultiplier).toBe(1); expect(config.pokemonConfigs?.[0].isBoss).toBe(true); expect(onInitResult).toBe(true); }); diff --git a/src/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts b/src/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts index 70250350af4..c337556728b 100644 --- a/src/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts @@ -85,22 +85,6 @@ describe("Fun And Games! - Mystery Encounter", () => { expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.FUN_AND_GAMES); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.FUN_AND_GAMES); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = new MysteryEncounter(FunAndGamesEncounter); diff --git a/src/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts b/src/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts index e91b936cb9d..5a99b0450ca 100644 --- a/src/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts @@ -69,22 +69,6 @@ describe("Global Trade System - Mystery Encounter", () => { expect(GlobalTradeSystemEncounter.options.length).toBe(4); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.GLOBAL_TRADE_SYSTEM); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should not spawn outside of CIVILIZATION_ENCOUNTER_BIOMES", async () => { game.override.mysteryEncounterTier(MysteryEncounterTier.COMMON); game.override.startingBiome(Biome.VOLCANO); diff --git a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts index 5d43172f6c0..02872334fac 100644 --- a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -3,7 +3,6 @@ import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encount import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Biome } from "#app/enums/biome"; -import { Moves } from "#app/enums/moves"; import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; import { Species } from "#app/enums/species"; import GameManager from "#app/test/utils/gameManager"; @@ -16,6 +15,7 @@ import BattleScene from "#app/battle-scene"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { PartyExpPhase } from "#app/phases/party-exp-phase"; + const namespace = "mysteryEncounter:lostAtSea"; /** Blastoise for surf. Pidgeot for fly. Abra for none. */ const defaultParty = [Species.BLASTOISE, Species.PIDGEOT, Species.ABRA]; @@ -74,22 +74,6 @@ describe("Lost at Sea - Mystery Encounter", () => { expect(game.scene.currentBattle.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.LOST_AT_SEA); }); - it("should not run below wave 11", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(game.scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(game.scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully", () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = LostAtSeaEncounter; @@ -102,8 +86,8 @@ describe("Lost at Sea - Mystery Encounter", () => { const onInitResult = onInit!(scene); expect(LostAtSeaEncounter.dialogueTokens?.damagePercentage).toBe("25"); - expect(LostAtSeaEncounter.dialogueTokens?.option1RequiredMove).toBe(Moves[Moves.SURF]); - expect(LostAtSeaEncounter.dialogueTokens?.option2RequiredMove).toBe(Moves[Moves.FLY]); + expect(LostAtSeaEncounter.dialogueTokens?.option1RequiredMove).toBe("Surf"); + expect(LostAtSeaEncounter.dialogueTokens?.option2RequiredMove).toBe("Fly"); expect(onInitResult).toBe(true); }); diff --git a/src/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts b/src/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts index de527538711..15cd3338fff 100644 --- a/src/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts @@ -79,22 +79,6 @@ describe("Mysterious Challengers - Mystery Encounter", () => { expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.MYSTERIOUS_CHALLENGERS); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.MYSTERIOUS_CHALLENGERS); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = new MysteryEncounter(MysteriousChallengersEncounter); @@ -117,12 +101,12 @@ describe("Mysterious Challengers - Mystery Encounter", () => { }, { trainerConfig: expect.any(TrainerConfig), - levelAdditiveMultiplier: 1, + levelAdditiveModifier: 1, female: expect.any(Boolean), }, { trainerConfig: expect.any(TrainerConfig), - levelAdditiveMultiplier: 1.5, + levelAdditiveModifier: 1.5, female: expect.any(Boolean), } ]); diff --git a/src/test/mystery-encounter/encounters/part-timer-encounter.test.ts b/src/test/mystery-encounter/encounters/part-timer-encounter.test.ts index f73c1f437d0..061b6a61461 100644 --- a/src/test/mystery-encounter/encounters/part-timer-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/part-timer-encounter.test.ts @@ -80,22 +80,6 @@ describe("Part-Timer - Mystery Encounter", () => { expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.PART_TIMER); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.PART_TIMER); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - describe("Option 1 - Make Deliveries", () => { it("should have the correct properties", () => { const option = PartTimerEncounter.options[0]; diff --git a/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts b/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts index 13860e83baa..b02d00c7dbd 100644 --- a/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts @@ -15,12 +15,15 @@ import { TeleportingHijinksEncounter } from "#app/data/mystery-encounters/encoun import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import { Mode } from "#app/ui/ui"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; +import { Abilities } from "#app/enums/abilities"; const namespace = "mysteryEncounter:teleportingHijinks"; const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA]; const defaultBiome = Biome.CAVE; const defaultWave = 45; +const TRANSPORT_BIOMES = [Biome.SPACE, Biome.ISLAND, Biome.LABORATORY, Biome.FAIRY_CAVE, Biome.WASTELAND, Biome.DOJO]; + describe("Teleporting Hijinks - Mystery Encounter", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -34,10 +37,12 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { game = new GameManager(phaserGame); scene = game.scene; scene.money = 20000; - game.override.mysteryEncounterChance(100); - game.override.startingWave(defaultWave); - game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(); + game.override + .mysteryEncounterChance(100) + .startingWave(defaultWave) + .startingBiome(defaultBiome) + .disableTrainerWaves() + .enemyPassiveAbility(Abilities.BALL_FETCH); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -65,22 +70,6 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { expect(TeleportingHijinksEncounter.options.length).toBe(3); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.TELEPORTING_HIJINKS); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should run in waves that are X1", async () => { game.override.startingWave(11); game.override.mysteryEncounterTier(MysteryEncounterTier.COMMON); @@ -183,7 +172,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); expect(previousBiome).not.toBe(scene.arena.biomeType); - expect([Biome.SPACE, Biome.ISLAND, Biome.LABORATORY, Biome.FAIRY_CAVE]).toContain(scene.arena.biomeType); + expect(TRANSPORT_BIOMES).toContain(scene.arena.biomeType); }); it("should start a battle against an enraged boss", { retry: 5 }, async () => { @@ -246,7 +235,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2, undefined, true); expect(previousBiome).not.toBe(scene.arena.biomeType); - expect([Biome.SPACE, Biome.ISLAND, Biome.LABORATORY, Biome.FAIRY_CAVE]).toContain(scene.arena.biomeType); + expect(TRANSPORT_BIOMES).toContain(scene.arena.biomeType); }); it("should start a battle against an enraged boss", async () => { diff --git a/src/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts b/src/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts new file mode 100644 index 00000000000..59765148ead --- /dev/null +++ b/src/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts @@ -0,0 +1,283 @@ +import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; +import { Biome } from "#app/enums/biome"; +import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { Species } from "#app/enums/species"; +import GameManager from "#app/test/utils/gameManager"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { runMysteryEncounterToEnd, skipBattleRunMysteryEncounterRewardsPhase } from "#test/mystery-encounter/encounter-test-utils"; +import BattleScene from "#app/battle-scene"; +import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils"; +import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; +import { CommandPhase } from "#app/phases/command-phase"; +import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter"; +import { TrainerType } from "#enums/trainer-type"; +import { EggTier } from "#enums/egg-type"; +import { PostMysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; + +const namespace = "mysteryEncounter:expertPokemonBreeder"; +const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA]; +const defaultBiome = Biome.CAVE; +const defaultWave = 45; + +describe("The Expert Pokémon Breeder - Mystery Encounter", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + let scene: BattleScene; + + beforeAll(() => { + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS }); + }); + + beforeEach(async () => { + game = new GameManager(phaserGame); + scene = game.scene; + game.override.mysteryEncounterChance(100); + game.override.startingWave(defaultWave); + game.override.startingBiome(defaultBiome); + game.override.disableTrainerWaves(); + + const biomeMap = new Map([ + [Biome.VOLCANO, [MysteryEncounterType.FIGHT_OR_FLIGHT]], + ]); + HUMAN_TRANSITABLE_BIOMES.forEach(biome => { + biomeMap.set(biome, [MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER]); + }); + vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(biomeMap); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + vi.clearAllMocks(); + vi.resetAllMocks(); + }); + + it("should have the correct properties", async () => { + await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty); + + expect(TheExpertPokemonBreederEncounter.encounterType).toBe(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER); + expect(TheExpertPokemonBreederEncounter.encounterTier).toBe(MysteryEncounterTier.ULTRA); + expect(TheExpertPokemonBreederEncounter.dialogue).toBeDefined(); + expect(TheExpertPokemonBreederEncounter.dialogue.intro).toStrictEqual([ + { + text: `${namespace}.intro` + }, + { + speaker: "trainerNames:expert_pokemon_breeder", + text: `${namespace}.intro_dialogue` + }, + ]); + expect(TheExpertPokemonBreederEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(TheExpertPokemonBreederEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(TheExpertPokemonBreederEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); + expect(TheExpertPokemonBreederEncounter.options.length).toBe(3); + }); + + it("should not spawn outside of HUMAN_TRANSITABLE_BIOMES", async () => { + game.override.mysteryEncounterTier(MysteryEncounterTier.GREAT); + game.override.startingBiome(Biome.VOLCANO); + await game.runToMysteryEncounter(); + + expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER); + }); + + it("should initialize fully", async () => { + initSceneWithoutEncounterPhase(scene, defaultParty); + scene.currentBattle.mysteryEncounter = new MysteryEncounter(TheExpertPokemonBreederEncounter); + const encounter = scene.currentBattle.mysteryEncounter!; + scene.currentBattle.waveIndex = defaultWave; + + const { onInit } = encounter; + + expect(encounter.onInit).toBeDefined(); + + encounter.populateDialogueTokensFromRequirements(scene); + const onInitResult = onInit!(scene); + + expect(encounter.enemyPartyConfigs).toBeDefined(); + expect(encounter.enemyPartyConfigs.length).toBe(1); + expect(encounter.enemyPartyConfigs[0].trainerType).toBe(TrainerType.EXPERT_POKEMON_BREEDER); + expect(encounter.enemyPartyConfigs[0].pokemonConfigs?.length).toBe(3); + expect(encounter.spriteConfigs).toBeDefined(); + expect(encounter.spriteConfigs.length).toBe(2); + expect(onInitResult).toBe(true); + }); + + describe("Option 1 - Battle with Pokemon 1", () => { + it("should have the correct properties", () => { + const option = TheExpertPokemonBreederEncounter.options[0]; + expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT); + expect(option.dialogue).toBeDefined(); + expect(option.dialogue).toStrictEqual({ + buttonLabel: `${namespace}.option.1.label`, + buttonTooltip: expect.any(String), // Varies based on pokemon + selected: [ + { + speaker: "trainerNames:expert_pokemon_breeder", + text: `${namespace}.option.selected`, + }, + ], + }); + }); + + it("should start battle against the trainer", async () => { + await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty); + await runMysteryEncounterToEnd(game, 1, undefined, true); + + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.currentBattle.trainer).toBeDefined(); + expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); + expect(scene.getParty().length).toBe(1); + }); + + it("Should reward the player with friendship and eggs based on pokemon selected", async () => { + await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty); + + const friendshipBefore = scene.currentBattle.mysteryEncounter!.misc.pokemon1.friendship; + + scene.gameData.eggs = []; + const eggsBefore = scene.gameData.eggs; + expect(eggsBefore).toBeDefined(); + const eggsBeforeLength = eggsBefore.length; + + await runMysteryEncounterToEnd(game, 1, undefined, true); + await skipBattleRunMysteryEncounterRewardsPhase(game); + await game.phaseInterceptor.to(SelectModifierPhase, false); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + + const eggsAfter = scene.gameData.eggs; + const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon1CommonEggs; + const rareEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon1RareEggs; + expect(eggsAfter).toBeDefined(); + expect(eggsBeforeLength + commonEggs + rareEggs).toBe(eggsAfter.length); + expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs); + expect(eggsAfter.filter(egg => egg.tier === EggTier.GREAT).length).toBe(rareEggs); + + game.phaseInterceptor.superEndPhase(); + await game.phaseInterceptor.to(PostMysteryEncounterPhase); + + const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon1.friendship; + expect(friendshipAfter).toBe(friendshipBefore + 20 + 2); // +2 extra for friendship gained from winning battle + }); + }); + + describe("Option 2 - Battle with Pokemon 2", () => { + it("should have the correct properties", () => { + const option = TheExpertPokemonBreederEncounter.options[1]; + expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT); + expect(option.dialogue).toBeDefined(); + expect(option.dialogue).toStrictEqual({ + buttonLabel: `${namespace}.option.2.label`, + buttonTooltip: expect.any(String), // Varies based on pokemon + selected: [ + { + speaker: "trainerNames:expert_pokemon_breeder", + text: `${namespace}.option.selected`, + }, + ], + }); + }); + + it("should start battle against the trainer", async () => { + await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty); + await runMysteryEncounterToEnd(game, 2, undefined, true); + + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.currentBattle.trainer).toBeDefined(); + expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); + expect(scene.getParty().length).toBe(1); + }); + + it("Should reward the player with friendship and eggs based on pokemon selected", async () => { + await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty); + + const friendshipBefore = scene.currentBattle.mysteryEncounter!.misc.pokemon2.friendship; + + scene.gameData.eggs = []; + const eggsBefore = scene.gameData.eggs; + expect(eggsBefore).toBeDefined(); + const eggsBeforeLength = eggsBefore.length; + + await runMysteryEncounterToEnd(game, 2, undefined, true); + await skipBattleRunMysteryEncounterRewardsPhase(game); + await game.phaseInterceptor.to(SelectModifierPhase, false); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + + const eggsAfter = scene.gameData.eggs; + const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon2CommonEggs; + const rareEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon2RareEggs; + expect(eggsAfter).toBeDefined(); + expect(eggsBeforeLength + commonEggs + rareEggs).toBe(eggsAfter.length); + expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs); + expect(eggsAfter.filter(egg => egg.tier === EggTier.GREAT).length).toBe(rareEggs); + + game.phaseInterceptor.superEndPhase(); + await game.phaseInterceptor.to(PostMysteryEncounterPhase); + + const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon2.friendship; + expect(friendshipAfter).toBe(friendshipBefore + 20 + 2); // +2 extra for friendship gained from winning battle + }); + }); + + describe("Option 3 - Battle with Pokemon 3", () => { + it("should have the correct properties", () => { + const option = TheExpertPokemonBreederEncounter.options[2]; + expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT); + expect(option.dialogue).toBeDefined(); + expect(option.dialogue).toStrictEqual({ + buttonLabel: `${namespace}.option.3.label`, + buttonTooltip: expect.any(String), // Varies based on pokemon + selected: [ + { + speaker: "trainerNames:expert_pokemon_breeder", + text: `${namespace}.option.selected`, + }, + ], + }); + }); + + it("should start battle against the trainer", async () => { + await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty); + await runMysteryEncounterToEnd(game, 3, undefined, true); + + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.currentBattle.trainer).toBeDefined(); + expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); + expect(scene.getParty().length).toBe(1); + }); + + it("Should reward the player with friendship and eggs based on pokemon selected", async () => { + await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty); + + const friendshipBefore = scene.currentBattle.mysteryEncounter!.misc.pokemon3.friendship; + + scene.gameData.eggs = []; + const eggsBefore = scene.gameData.eggs; + expect(eggsBefore).toBeDefined(); + const eggsBeforeLength = eggsBefore.length; + + await runMysteryEncounterToEnd(game, 3, undefined, true); + await skipBattleRunMysteryEncounterRewardsPhase(game); + await game.phaseInterceptor.to(SelectModifierPhase, false); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + + const eggsAfter = scene.gameData.eggs; + const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon3CommonEggs; + const rareEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon3RareEggs; + expect(eggsAfter).toBeDefined(); + expect(eggsBeforeLength + commonEggs + rareEggs).toBe(eggsAfter.length); + expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs); + expect(eggsAfter.filter(egg => egg.tier === EggTier.GREAT).length).toBe(rareEggs); + + game.phaseInterceptor.superEndPhase(); + await game.phaseInterceptor.to(PostMysteryEncounterPhase); + + const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon3.friendship; + expect(friendshipAfter).toBe(friendshipBefore + 20 + 2); // +2 extra for friendship gained from winning battle + }); + }); +}); diff --git a/src/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts b/src/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts index c43577337da..e2b1fe8309b 100644 --- a/src/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts @@ -53,19 +53,22 @@ describe("The Pokemon Salesman - Mystery Encounter", () => { }); it("should have the correct properties", async () => { + const { encounterType, encounterTier, dialogue, options } = ThePokemonSalesmanEncounter; + await game.runToMysteryEncounter(MysteryEncounterType.THE_POKEMON_SALESMAN, defaultParty); - expect(ThePokemonSalesmanEncounter.encounterType).toBe(MysteryEncounterType.THE_POKEMON_SALESMAN); - expect(ThePokemonSalesmanEncounter.encounterTier).toBe(MysteryEncounterTier.ULTRA); - expect(ThePokemonSalesmanEncounter.dialogue).toBeDefined(); - expect(ThePokemonSalesmanEncounter.dialogue.intro).toStrictEqual([ + expect(encounterType).toBe(MysteryEncounterType.THE_POKEMON_SALESMAN); + expect(encounterTier).toBe(MysteryEncounterTier.ULTRA); + expect(dialogue).toBeDefined(); + expect(dialogue.intro).toStrictEqual([ { text: `${namespace}.intro` }, { speaker: `${namespace}.speaker`, text: `${namespace}.intro_dialogue` } ]); - expect(ThePokemonSalesmanEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); - expect(ThePokemonSalesmanEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); - expect(ThePokemonSalesmanEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); - expect(ThePokemonSalesmanEncounter.options.length).toBe(2); + const { title, description, query } = dialogue.encounterOptionsDialogue!; + expect(title).toBe(`${namespace}.title`); + expect(description).toMatch(new RegExp(`^${namespace}\\.description(_shiny)?$`)); + expect(query).toBe(`${namespace}.query`); + expect(options.length).toBe(2); }); it("should not spawn outside of HUMAN_TRANSITABLE_BIOMES", async () => { @@ -76,22 +79,6 @@ describe("The Pokemon Salesman - Mystery Encounter", () => { expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_POKEMON_SALESMAN); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_POKEMON_SALESMAN); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully ", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = ThePokemonSalesmanEncounter; @@ -120,12 +107,13 @@ describe("The Pokemon Salesman - Mystery Encounter", () => { describe("Option 1 - Purchase the pokemon", () => { it("should have the correct properties", () => { - const option = ThePokemonSalesmanEncounter.options[0]; - expect(option.optionMode).toBe(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT); - expect(option.dialogue).toBeDefined(); - expect(option.dialogue).toStrictEqual({ + const { optionMode, dialogue } = ThePokemonSalesmanEncounter.options[0]; + + expect(optionMode).toBe(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT); + expect(dialogue).toBeDefined(); + expect(dialogue).toStrictEqual({ buttonLabel: `${namespace}.option.1.label`, - buttonTooltip: `${namespace}.option.1.tooltip`, + buttonTooltip: expect.stringMatching(new RegExp(`^${namespace}\\.option\\.1\\.tooltip(_shiny)?$`)), selected: [ { text: `${namespace}.option.1.selected_message`, diff --git a/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts b/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts index be35ec31784..5c1353ee337 100644 --- a/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts @@ -83,22 +83,6 @@ describe("The Strong Stuff - Mystery Encounter", () => { expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_STRONG_STUFF); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_STRONG_STUFF); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully ", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = TheStrongStuffEncounter; @@ -114,7 +98,7 @@ describe("The Strong Stuff - Mystery Encounter", () => { expect(TheStrongStuffEncounter.enemyPartyConfigs).toEqual([ { - levelAdditiveMultiplier: 1, + levelAdditiveModifier: 1, disableSwitch: true, pokemonConfigs: [ { diff --git a/src/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts b/src/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts index 0c642225031..1efe6dbd7f8 100644 --- a/src/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts @@ -90,22 +90,6 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_WINSTRATE_CHALLENGE); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_WINSTRATE_CHALLENGE); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = new MysteryEncounter(TheWinstrateChallengeEncounter); diff --git a/src/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts b/src/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts index a4bfaea659a..bfeb249543f 100644 --- a/src/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts @@ -71,22 +71,6 @@ describe("Trash to Treasure - Mystery Encounter", () => { expect(TrashToTreasureEncounter.options.length).toBe(2); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.TRASH_TO_TREASURE); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = TrashToTreasureEncounter; @@ -102,7 +86,7 @@ describe("Trash to Treasure - Mystery Encounter", () => { expect(TrashToTreasureEncounter.enemyPartyConfigs).toEqual([ { - levelAdditiveMultiplier: 1, + levelAdditiveModifier: 1, disableSwitch: true, pokemonConfigs: [ { diff --git a/src/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts b/src/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts index ce9e37b8d68..2f8c4e5111a 100644 --- a/src/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts @@ -74,22 +74,6 @@ describe("Uncommon Breed - Mystery Encounter", () => { expect(UncommonBreedEncounter.options.length).toBe(3); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.UNCOMMON_BREED); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = UncommonBreedEncounter; @@ -123,7 +107,7 @@ describe("Uncommon Breed - Mystery Encounter", () => { }); }); - it("should start a fight against the boss", async () => { + it.skip("should start a fight against the boss", async () => { const phaseSpy = vi.spyOn(scene, "pushPhase"); const unshiftPhaseSpy = vi.spyOn(scene, "unshiftPhase"); await game.runToMysteryEncounter(MysteryEncounterType.UNCOMMON_BREED, defaultParty); diff --git a/src/test/mystery-encounter/encounters/weird-dream-encounter.test.ts b/src/test/mystery-encounter/encounters/weird-dream-encounter.test.ts index ef014c6949b..e532891810c 100644 --- a/src/test/mystery-encounter/encounters/weird-dream-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/weird-dream-encounter.test.ts @@ -73,22 +73,6 @@ describe("Weird Dream - Mystery Encounter", () => { expect(WeirdDreamEncounter.options.length).toBe(2); }); - it("should not run below wave 10", async () => { - game.override.startingWave(9); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.WEIRD_DREAM); - }); - - it("should not run above wave 179", async () => { - game.override.startingWave(181); - - await game.runToMysteryEncounter(); - - expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); - }); - it("should initialize fully", async () => { initSceneWithoutEncounterPhase(scene, defaultParty); scene.currentBattle.mysteryEncounter = WeirdDreamEncounter; diff --git a/src/test/mystery-encounter/mystery-encounter.test.ts b/src/test/mystery-encounter/mystery-encounter.test.ts index d2a2e7f9d92..38c999f8aac 100644 --- a/src/test/mystery-encounter/mystery-encounter.test.ts +++ b/src/test/mystery-encounter/mystery-encounter.test.ts @@ -4,10 +4,12 @@ import Phaser from "phaser"; import { Species } from "#enums/species"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import BattleScene from "#app/battle-scene"; describe("Mystery Encounters", () => { let phaserGame: Phaser.Game; let game: GameManager; + let scene: BattleScene; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -21,6 +23,7 @@ describe("Mystery Encounters", () => { beforeEach(() => { game = new GameManager(phaserGame); + scene = game.scene; game.override.startingWave(11); game.override.mysteryEncounterChance(100); }); @@ -32,23 +35,20 @@ describe("Mystery Encounters", () => { expect(game.scene.getCurrentPhase()!.constructor.name).toBe(MysteryEncounterPhase.name); }); - it("", async () => { - await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]); + it("Encounters should not run below wave 10", async () => { + game.override.startingWave(9); - await game.phaseInterceptor.to(MysteryEncounterPhase, false); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(MysteryEncounterPhase.name); + await game.runToMysteryEncounter(); + + expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.MYSTERIOUS_CHALLENGERS); }); - it("spawns mysterious challengers encounter", async () => { - }); + it("Encounters should not run above wave 180", async () => { + game.override.startingWave(181); - it("spawns mysterious chest encounter", async () => { - }); + await game.runToMysteryEncounter(); - it("spawns dark deal encounter", async () => { - }); - - it("spawns fight or flight encounter", async () => { + expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); }); }); diff --git a/src/test/reload.test.ts b/src/test/reload.test.ts index a96a525ca2d..5009d76d1a7 100644 --- a/src/test/reload.test.ts +++ b/src/test/reload.test.ts @@ -35,19 +35,18 @@ describe("Reload", () => { expect(preReloadRngState).toBe(postReloadRngState); }, 20000); - it("should not have RNG inconsistencies after a biome switch", async () => { + it.each(Array.from({length: 100}))("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) + .startingLevel(100) // Avoid levelling up + .enemyLevel(1000) // Avoid opponent dying before game.doKillOpponents() .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 + // Transition from 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(); @@ -63,6 +62,34 @@ describe("Reload", () => { expect(preReloadRngState).toBe(postReloadRngState); }, 20000); + it("should not have weather inconsistencies after a biome switch", async () => { + game.override + .startingWave(10) + .startingBiome(Biome.ICE_CAVE) // Will lead to Snowy Forest with randomly generated weather + .battleType("single") + .startingLevel(100) // Avoid levelling up + .enemyLevel(1000) // Avoid opponent dying before game.doKillOpponents() + .disableTrainerWaves() + .moveset([Moves.KOWTOW_CLEAVE]) + .enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle(); // Apparently daily mode would override the biome + + // Transition from 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 preReloadWeather = game.scene.arena.weather; + + await game.reload.reloadSession(); + + const postReloadWeather = game.scene.arena.weather; + + expect(postReloadWeather).toStrictEqual(preReloadWeather); + }, 20000); + it("should not have RNG inconsistencies at a Daily run wild Pokemon fight", async () => { await game.dailyMode.startBattle(); diff --git a/src/test/ui/battle_info.test.ts b/src/test/ui/battle_info.test.ts new file mode 100644 index 00000000000..4d511b75e6f --- /dev/null +++ b/src/test/ui/battle_info.test.ts @@ -0,0 +1,55 @@ +import { ExpGainsSpeed } from "#app/enums/exp-gains-speed"; +import { Species } from "#app/enums/species"; +import { ExpPhase } from "#app/phases/exp-phase"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +vi.mock("../data/exp", ({}) => { + return { + getLevelRelExp: vi.fn(() => 1), //consistent levelRelExp + }; +}); + +describe("UI - Battle Info", () => { + 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 + .moveset([Moves.GUILLOTINE, Moves.SPLASH]) + .battleType("single") + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.CATERPIE); + }); + + it.each([ExpGainsSpeed.FAST, ExpGainsSpeed.FASTER, ExpGainsSpeed.SKIP])( + "should increase exp gains animation by 2^%i", + async (expGainsSpeed) => { + game.settings.expGainsSpeed(expGainsSpeed); + vi.spyOn(Math, "pow"); + + await game.classicMode.startBattle([Species.CHARIZARD]); + + game.move.select(Moves.SPLASH); + await game.doKillOpponents(); + await game.phaseInterceptor.to(ExpPhase, true); + + expect(Math.pow).not.toHaveBeenCalledWith(2, expGainsSpeed); + } + ); +}); diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index a10ed70b97e..36423c5e18f 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -31,7 +31,6 @@ import TargetSelectUiHandler from "#app/ui/target-select-ui-handler"; import { Mode } from "#app/ui/ui"; import { Button } from "#enums/buttons"; import { ExpNotification } from "#enums/exp-notification"; -import { GameDataType } from "#enums/game-data-type"; import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; import { generateStarter, waitUntil } from "#test/utils/gameManagerUtils"; @@ -54,6 +53,7 @@ import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { expect } from "vitest"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { isNullOrUndefined } from "#app/utils"; +import { ExpGainsSpeed } from "#app/enums/exp-gains-speed"; /** * Class to manage the game state and transitions between phases. @@ -148,7 +148,7 @@ export default class GameManager { this.scene.gameSpeed = 5; this.scene.moveAnimations = false; this.scene.showLevelUpStats = false; - this.scene.expGainsSpeed = 3; + this.scene.expGainsSpeed = ExpGainsSpeed.SKIP; this.scene.expParty = ExpNotification.SKIP; this.scene.hpBarSpeed = 3; this.scene.enableTutorials = false; @@ -370,13 +370,11 @@ export default class GameManager { * @returns A promise that resolves with the exported save data. */ exportSaveToTest(): Promise { + const saveKey = "x0i2O7WRiANTqPmZ"; return new Promise(async (resolve) => { - await this.scene.gameData.saveAll(this.scene, true, true, true, true); - this.scene.reset(true); - await waitUntil(() => this.scene.ui?.getMode() === Mode.TITLE); - await this.scene.gameData.tryExportData(GameDataType.SESSION, 0); - await waitUntil(() => localStorage.hasOwnProperty("toExport")); - return resolve(localStorage.getItem("toExport")!); // TODO: is this bang correct?; + const sessionSaveData = this.scene.gameData.getSessionSaveData(this.scene); + const encryptedSaveData = AES.encrypt(JSON.stringify(sessionSaveData), saveKey).toString(); + resolve(encryptedSaveData); }); } diff --git a/src/test/utils/helpers/reloadHelper.ts b/src/test/utils/helpers/reloadHelper.ts index c15347b08c9..e0e538120cc 100644 --- a/src/test/utils/helpers/reloadHelper.ts +++ b/src/test/utils/helpers/reloadHelper.ts @@ -5,11 +5,27 @@ 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"; +import { SessionSaveData } from "#app/system/game-data"; +import GameManager from "../gameManager"; /** * Helper to allow reloading sessions in unit tests. */ export class ReloadHelper extends GameManagerHelper { + sessionData: SessionSaveData; + + constructor(game: GameManager) { + super(game); + + // Whenever the game saves the session, save it to the reloadHelper instead + vi.spyOn(game.scene.gameData, "saveAll").mockImplementation((scene) => { + return new Promise((resolve, reject) => { + this.sessionData = scene.gameData.getSessionSaveData(scene); + resolve(true); + }); + }); + } + /** * Simulate reloading the session from the title screen, until reaching the * beginning of the first turn (equivalent to running `startBattle()`) for @@ -17,7 +33,6 @@ export class ReloadHelper extends GameManagerHelper { */ async reloadSession() : Promise { const scene = this.game.scene; - const sessionData = scene.gameData.getSessionSaveData(scene); const titlePhase = new TitlePhase(scene); scene.clearPhaseQueue(); @@ -25,7 +40,7 @@ export class ReloadHelper extends GameManagerHelper { // Set the last saved session to the desired session data vi.spyOn(scene.gameData, "getSession").mockReturnValue( new Promise((resolve, reject) => { - resolve(sessionData); + resolve(this.sessionData); }) ); scene.unshiftPhase(titlePhase); diff --git a/src/test/utils/helpers/settingsHelper.ts b/src/test/utils/helpers/settingsHelper.ts index 8fca2a34d47..c611a705107 100644 --- a/src/test/utils/helpers/settingsHelper.ts +++ b/src/test/utils/helpers/settingsHelper.ts @@ -1,6 +1,7 @@ import { PlayerGender } from "#app/enums/player-gender"; import { BattleStyle } from "#app/enums/battle-style"; import { GameManagerHelper } from "./gameManagerHelper"; +import { ExpGainsSpeed } from "#app/enums/exp-gains-speed"; /** * Helper to handle settings for tests @@ -38,6 +39,15 @@ export class SettingsHelper extends GameManagerHelper { this.log(`Gender set to: ${PlayerGender[gender]} (=${gender})` ); } + /** + * Change the exp gains speed + * @param speed the {@linkcode ExpGainsSpeed} to set + */ + expGainsSpeed(speed: ExpGainsSpeed) { + this.game.scene.expGainsSpeed = speed; + this.log(`Exp Gains Speed set to: ${ExpGainsSpeed[speed]} (=${speed})` ); + } + private log(...params: any[]) { console.log("Settings:", ...params); } diff --git a/src/test/utils/phaseInterceptor.ts b/src/test/utils/phaseInterceptor.ts index cdf0ad41057..46bb757c867 100644 --- a/src/test/utils/phaseInterceptor.ts +++ b/src/test/utils/phaseInterceptor.ts @@ -60,6 +60,7 @@ export interface PromptHandler { expireFn?: () => void; awaitingActionInput?: boolean; } +import { ExpPhase } from "#app/phases/exp-phase"; export default class PhaseInterceptor { public scene; @@ -127,7 +128,8 @@ export default class PhaseInterceptor { [MysteryEncounterRewardsPhase, this.startPhase], [PostMysteryEncounterPhase, this.startPhase], [ModifierRewardPhase, this.startPhase], - [PartyExpPhase, this.startPhase] + [PartyExpPhase, this.startPhase], + [ExpPhase, this.startPhase], ]; private endBySetMode = [ diff --git a/src/test/vitest.setup.ts b/src/test/vitest.setup.ts index 3bb5c240d94..74129f20d26 100644 --- a/src/test/vitest.setup.ts +++ b/src/test/vitest.setup.ts @@ -14,6 +14,8 @@ import { initStatsKeys } from "#app/ui/game-stats-ui-handler"; import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters"; import { beforeAll, vi } from "vitest"; +process.env.TZ = "UTC"; + /** Mock the override import to always return default values, ignoring any custom overrides. */ vi.mock("#app/overrides", async (importOriginal) => { const { defaultOverrides } = await importOriginal(); diff --git a/src/tutorial.ts b/src/tutorial.ts index c4482839779..18d8291d227 100644 --- a/src/tutorial.ts +++ b/src/tutorial.ts @@ -1,7 +1,9 @@ import BattleScene from "./battle-scene"; import AwaitableUiHandler from "./ui/awaitable-ui-handler"; +import UiHandler from "./ui/ui-handler"; import { Mode } from "./ui/ui"; import i18next from "i18next"; +import Overrides from "#app/overrides"; export enum Tutorial { Intro = "INTRO", @@ -39,7 +41,7 @@ const tutorialHandlers = { scene.ui.showText(i18next.t("tutorial:starterSelect"), null, () => scene.ui.showText("", null, () => resolve()), null, true); }); }, - [Tutorial.Pokerus]: (scene: BattleScene) => { + [Tutorial.Pokerus]: (scene: BattleScene) => { return new Promise(resolve => { scene.ui.showText(i18next.t("tutorial:pokerus"), null, () => scene.ui.showText("", null, () => resolve()), null, true); }); @@ -63,26 +65,87 @@ const tutorialHandlers = { }, }; -export function handleTutorial(scene: BattleScene, tutorial: Tutorial): Promise { - return new Promise(resolve => { - if (!scene.enableTutorials) { - return resolve(false); - } +/** + * Run through the specified tutorial if it hasn't been seen before and mark it as seen once done + * This will show a tutorial overlay if defined in the current {@linkcode AwaitableUiHandler} + * The main menu will also get disabled while the tutorial is running + * @param scene the current {@linkcode BattleScene} + * @param tutorial the {@linkcode Tutorial} to play + * @returns a promise with result `true` if the tutorial was run and finished, `false` otherwise + */ +export async function handleTutorial(scene: BattleScene, tutorial: Tutorial): Promise { + if (!scene.enableTutorials && !Overrides.BYPASS_TUTORIAL_SKIP) { + return false; + } - if (scene.gameData.getTutorialFlags()[tutorial]) { - return resolve(false); - } + if (scene.gameData.getTutorialFlags()[tutorial] && !Overrides.BYPASS_TUTORIAL_SKIP) { + return false; + } - const handler = scene.ui.getHandler(); - if (handler instanceof AwaitableUiHandler) { - handler.tutorialActive = true; - } - tutorialHandlers[tutorial](scene).then(() => { - scene.gameData.saveTutorialFlag(tutorial, true); - if (handler instanceof AwaitableUiHandler) { - handler.tutorialActive = false; - } - resolve(true); - }); - }); + const handler = scene.ui.getHandler(); + const isMenuDisabled = scene.disableMenu; + + // starting tutorial, disable menu + scene.disableMenu = true; + if (handler instanceof AwaitableUiHandler) { + handler.tutorialActive = true; + } + + await showTutorialOverlay(scene, handler); + await tutorialHandlers[tutorial](scene); + await hideTutorialOverlay(scene, handler); + + // tutorial finished and overlay gone, re-enable menu, save tutorial as seen + scene.disableMenu = isMenuDisabled; + scene.gameData.saveTutorialFlag(tutorial, true); + if (handler instanceof AwaitableUiHandler) { + handler.tutorialActive = false; + } + + return true; } + +/** + * Show the tutorial overlay if there is one + * @param scene the current BattleScene + * @param handler the current UiHandler + * @returns `true` once the overlay has finished appearing, or if there is no overlay + */ +async function showTutorialOverlay(scene: BattleScene, handler: UiHandler) { + if (handler instanceof AwaitableUiHandler && handler.tutorialOverlay) { + scene.tweens.add({ + targets: handler.tutorialOverlay, + alpha: 0.5, + duration: 750, + ease: "Sine.easeOut", + onComplete: () => { + return true; + } + }); + } else { + return true; + } +} + +/** + * Hide the tutorial overlay if there is one + * @param scene the current BattleScene + * @param handler the current UiHandler + * @returns `true` once the overlay has finished disappearing, or if there is no overlay + */ +async function hideTutorialOverlay(scene: BattleScene, handler: UiHandler) { + if (handler instanceof AwaitableUiHandler && handler.tutorialOverlay) { + scene.tweens.add({ + targets: handler.tutorialOverlay, + alpha: 0, + duration: 500, + ease: "Sine.easeOut", + onComplete: () => { + return true; + } + }); + } else { + return true; + } +} + diff --git a/src/ui/abstact-option-select-ui-handler.ts b/src/ui/abstact-option-select-ui-handler.ts index c6abecda4c0..740830595ed 100644 --- a/src/ui/abstact-option-select-ui-handler.ts +++ b/src/ui/abstact-option-select-ui-handler.ts @@ -344,6 +344,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { super.clear(); this.config = null; this.optionSelectContainer.setVisible(false); + this.scrollCursor = 0; this.eraseCursor(); } diff --git a/src/ui/achvs-ui-handler.ts b/src/ui/achvs-ui-handler.ts index 605b8c538a9..491c4acb523 100644 --- a/src/ui/achvs-ui-handler.ts +++ b/src/ui/achvs-ui-handler.ts @@ -1,12 +1,13 @@ -import BattleScene from "../battle-scene"; +import BattleScene from "#app/battle-scene"; import { Button } from "#enums/buttons"; import i18next from "i18next"; -import { Achv, achvs, getAchievementDescription } from "../system/achv"; -import { Voucher, getVoucherTypeIcon, getVoucherTypeName, vouchers } from "../system/voucher"; -import MessageUiHandler from "./message-ui-handler"; -import { addTextObject, TextStyle } from "./text"; -import { Mode } from "./ui"; -import { addWindow } from "./ui-theme"; +import { Achv, achvs, getAchievementDescription } from "#app/system/achv"; +import { Voucher, getVoucherTypeIcon, getVoucherTypeName, vouchers } from "#app/system/voucher"; +import MessageUiHandler from "#app/ui/message-ui-handler"; +import { addTextObject, TextStyle } from "#app/ui/text"; +import { Mode } from "#app/ui/ui"; +import { addWindow } from "#app/ui/ui-theme"; +import { ScrollBar } from "#app/ui/scroll-bar"; import { PlayerGender } from "#enums/player-gender"; enum Page { @@ -49,6 +50,7 @@ export default class AchvsUiHandler extends MessageUiHandler { private vouchersTotal: number; private currentTotal: number; + private scrollBar: ScrollBar; private scrollCursor: number; private cursorObj: Phaser.GameObjects.NineSlice | null; private currentPage: Page; @@ -91,7 +93,10 @@ export default class AchvsUiHandler extends MessageUiHandler { this.iconsBg = addWindow(this.scene, 0, this.headerBg.height, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - this.headerBg.height - 68); this.iconsBg.setOrigin(0, 0); - this.iconsContainer = this.scene.add.container(6, this.headerBg.height + 6); + const yOffset = 6; + this.scrollBar = new ScrollBar(this.scene, this.iconsBg.width - 9, this.iconsBg.y + yOffset, 4, this.iconsBg.height - yOffset * 2, this.ROWS); + + this.iconsContainer = this.scene.add.container(5, this.headerBg.height + 8); this.icons = []; @@ -148,6 +153,7 @@ export default class AchvsUiHandler extends MessageUiHandler { this.mainContainer.add(this.headerText); this.mainContainer.add(this.headerActionText); this.mainContainer.add(this.iconsBg); + this.mainContainer.add(this.scrollBar); this.mainContainer.add(this.iconsContainer); this.mainContainer.add(titleBg); this.mainContainer.add(this.titleText); @@ -162,6 +168,7 @@ export default class AchvsUiHandler extends MessageUiHandler { this.currentPage = Page.ACHIEVEMENTS; this.setCursor(0); + this.setScrollCursor(0); this.mainContainer.setVisible(false); } @@ -175,6 +182,8 @@ export default class AchvsUiHandler extends MessageUiHandler { this.mainContainer.setVisible(true); this.setCursor(0); this.setScrollCursor(0); + this.scrollBar.setTotalRows(Math.ceil(this.currentTotal / this.COLS)); + this.scrollBar.setScrollCursor(0); this.getUi().moveTo(this.mainContainer, this.getUi().length - 1); @@ -224,6 +233,8 @@ export default class AchvsUiHandler extends MessageUiHandler { this.updateAchvIcons(); } this.setCursor(0, true); + this.scrollBar.setTotalRows(Math.ceil(this.currentTotal / this.COLS)); + this.scrollBar.setScrollCursor(0); this.mainContainer.update(); } if (button === Button.CANCEL) { @@ -237,32 +248,44 @@ export default class AchvsUiHandler extends MessageUiHandler { if (this.cursor < this.COLS) { if (this.scrollCursor) { success = this.setScrollCursor(this.scrollCursor - 1); + } else { + // Wrap around to the last row + success = this.setScrollCursor(Math.ceil(this.currentTotal / this.COLS) - this.ROWS); + let newCursorIndex = this.cursor + (this.ROWS - 1) * this.COLS; + if (newCursorIndex > this.currentTotal - this.scrollCursor * this.COLS -1) { + newCursorIndex -= this.COLS; + } + success = success && this.setCursor(newCursorIndex); } } else { success = this.setCursor(this.cursor - this.COLS); } break; case Button.DOWN: - const canMoveDown = (this.cursor + itemOffset) + this.COLS < this.currentTotal; + const canMoveDown = itemOffset + 1 < this.currentTotal; if (rowIndex >= this.ROWS - 1) { if (this.scrollCursor < Math.ceil(this.currentTotal / this.COLS) - this.ROWS && canMoveDown) { + // scroll down one row success = this.setScrollCursor(this.scrollCursor + 1); + } else { + // wrap back to the first row + success = this.setScrollCursor(0) && this.setCursor(this.cursor % this.COLS); } } else if (canMoveDown) { - success = this.setCursor(this.cursor + this.COLS); + success = this.setCursor(Math.min(this.cursor + this.COLS, this.currentTotal - itemOffset - 1)); } break; case Button.LEFT: - if (!this.cursor && this.scrollCursor) { - success = this.setScrollCursor(this.scrollCursor - 1) && this.setCursor(this.cursor + (this.COLS - 1)); - } else if (this.cursor) { + if (this.cursor % this.COLS === 0) { + success = this.setCursor(Math.min(this.cursor + this.COLS - 1, this.currentTotal - itemOffset - 1)); + } else { success = this.setCursor(this.cursor - 1); } break; case Button.RIGHT: - if (this.cursor + 1 === this.ROWS * this.COLS && this.scrollCursor < Math.ceil(this.currentTotal / this.COLS) - this.ROWS) { - success = this.setScrollCursor(this.scrollCursor + 1) && this.setCursor(this.cursor - (this.COLS - 1)); - } else if (this.cursor + itemOffset < this.currentTotal - 1) { + if ((this.cursor + 1) % this.COLS === 0 || (this.cursor + itemOffset) === (this.currentTotal - 1)) { + success = this.setCursor(this.cursor - this.cursor % this.COLS); + } else { success = this.setCursor(this.cursor + 1); } break; @@ -315,15 +338,22 @@ export default class AchvsUiHandler extends MessageUiHandler { } this.scrollCursor = scrollCursor; + this.scrollBar.setScrollCursor(this.scrollCursor); + + // Cursor cannot go farther than the last element in the list + const maxCursor = Math.min(this.cursor, this.currentTotal - this.scrollCursor * this.COLS - 1); + if (maxCursor !== this.cursor) { + this.setCursor(maxCursor); + } switch (this.currentPage) { case Page.ACHIEVEMENTS: this.updateAchvIcons(); - this.showAchv(achvs[Object.keys(achvs)[Math.min(this.cursor + this.scrollCursor * this.COLS, Object.values(achvs).length - 1)]]); + this.showAchv(achvs[Object.keys(achvs)[this.cursor + this.scrollCursor * this.COLS]]); break; case Page.VOUCHERS: this.updateVoucherIcons(); - this.showVoucher(vouchers[Object.keys(vouchers)[Math.min(this.cursor + this.scrollCursor * this.COLS, Object.values(vouchers).length - 1)]]); + this.showVoucher(vouchers[Object.keys(vouchers)[this.cursor + this.scrollCursor * this.COLS]]); break; } return true; @@ -411,6 +441,7 @@ export default class AchvsUiHandler extends MessageUiHandler { super.clear(); this.currentPage = Page.ACHIEVEMENTS; this.mainContainer.setVisible(false); + this.setScrollCursor(0); this.eraseCursor(); } diff --git a/src/ui/awaitable-ui-handler.ts b/src/ui/awaitable-ui-handler.ts index 2052c6e2ade..c6dc717aa3a 100644 --- a/src/ui/awaitable-ui-handler.ts +++ b/src/ui/awaitable-ui-handler.ts @@ -7,6 +7,7 @@ export default abstract class AwaitableUiHandler extends UiHandler { protected awaitingActionInput: boolean; protected onActionInput: Function | null; public tutorialActive: boolean = false; + public tutorialOverlay: Phaser.GameObjects.Rectangle; constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); @@ -24,4 +25,21 @@ export default abstract class AwaitableUiHandler extends UiHandler { return false; } + + /** + * Create a semi transparent overlay that will get shown during tutorials + * @param container the container to add the overlay to + */ + initTutorialOverlay(container: Phaser.GameObjects.Container) { + if (!this.tutorialOverlay) { + this.tutorialOverlay = new Phaser.GameObjects.Rectangle(this.scene, -1, -1, this.scene.scaledCanvas.width, this.scene.scaledCanvas.height, 0x070707); + this.tutorialOverlay.setName("tutorial-overlay"); + this.tutorialOverlay.setOrigin(0, 0); + this.tutorialOverlay.setAlpha(0); + } + + if (container) { + container.add(this.tutorialOverlay); + } + } } diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index c7b82dc826e..b3474bed5cd 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -11,8 +11,11 @@ import { Stat } from "#enums/stat"; import BattleFlyout from "./battle-flyout"; import { WindowVariant, addWindow } from "./ui-theme"; import i18next from "i18next"; +import { ExpGainsSpeed } from "#app/enums/exp-gains-speed"; export default class BattleInfo extends Phaser.GameObjects.Container { + public static readonly EXP_GAINS_DURATION_BASE = 1650; + private baseY: number; private player: boolean; @@ -159,7 +162,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.splicedIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 12, 15), Phaser.Geom.Rectangle.Contains); this.add(this.splicedIcon); - this.statusIndicator = this.scene.add.sprite(0, 0, "statuses"); + this.statusIndicator = this.scene.add.sprite(0, 0, Utils.getLocalizedSpriteKey("statuses")); this.statusIndicator.setName("icon_status"); this.statusIndicator.setVisible(false); this.statusIndicator.setOrigin(0, 0); @@ -378,17 +381,8 @@ export default class BattleInfo extends Phaser.GameObjects.Container { const ownedAbilityAttrs = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr; - let playerOwnsThisAbility = false; // Check if the player owns ability for the root form - if ((ownedAbilityAttrs & 1) > 0 && pokemon.hasSameAbilityInRootForm(0)) { - playerOwnsThisAbility = true; - } - if ((ownedAbilityAttrs & 2) > 0 && pokemon.hasSameAbilityInRootForm(1)) { - playerOwnsThisAbility = true; - } - if ((ownedAbilityAttrs & 4) > 0 && pokemon.hasSameAbilityInRootForm(2)) { - playerOwnsThisAbility = true; - } + const playerOwnsThisAbility = pokemon.checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs); if (missingDexAttrs || !playerOwnsThisAbility) { this.ownedIcon.setTint(0x808080); @@ -702,7 +696,11 @@ export default class BattleInfo extends Phaser.GameObjects.Container { instant = true; } const durationMultiplier = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn")(1 - (Math.max(this.lastLevel - 100, 0) / 150)); - const duration = this.visible && !instant ? (((levelExp - this.lastLevelExp) / relLevelExp) * 1650) * durationMultiplier * levelDurationMultiplier : 0; + let duration = this.visible && !instant ? (((levelExp - this.lastLevelExp) / relLevelExp) * BattleInfo.EXP_GAINS_DURATION_BASE) * durationMultiplier * levelDurationMultiplier : 0; + const speed = (this.scene as BattleScene).expGainsSpeed; + if (speed && speed >= ExpGainsSpeed.DEFAULT) { + duration = speed >= ExpGainsSpeed.SKIP ? ExpGainsSpeed.DEFAULT : duration / Math.pow(2, speed); + } if (ratio === 1) { this.lastLevelExp = 0; this.lastLevel++; diff --git a/src/ui/battle-message-ui-handler.ts b/src/ui/battle-message-ui-handler.ts index 9a694d50b29..c27c6974192 100644 --- a/src/ui/battle-message-ui-handler.ts +++ b/src/ui/battle-message-ui-handler.ts @@ -83,12 +83,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler { this.nameBoxContainer.add(this.nameText); messageContainer.add(this.nameBoxContainer); - const prompt = this.scene.add.sprite(0, 0, "prompt"); - prompt.setVisible(false); - prompt.setOrigin(0, 0); - messageContainer.add(prompt); - - this.prompt = prompt; + this.initPromptSprite(messageContainer); const levelUpStatsContainer = this.scene.add.container(0, 0); levelUpStatsContainer.setVisible(false); diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index 9497dfe58c6..b109eda5370 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -287,7 +287,6 @@ export default class EggGachaUiHandler extends MessageUiHandler { this.eggGachaContainer.add(this.eggGachaSummaryContainer); const gachaMessageBoxContainer = this.scene.add.container(0, 148); - this.eggGachaContainer.add(gachaMessageBoxContainer); const gachaMessageBox = addWindow(this.scene, 0, 0, 320, 32); gachaMessageBox.setOrigin(0, 0); @@ -301,8 +300,11 @@ export default class EggGachaUiHandler extends MessageUiHandler { this.message = gachaMessageText; + this.initTutorialOverlay(this.eggGachaContainer); this.eggGachaContainer.add(gachaMessageBoxContainer); + this.initPromptSprite(gachaMessageBoxContainer); + this.setCursor(0); } diff --git a/src/ui/evolution-scene-handler.ts b/src/ui/evolution-scene-handler.ts index ffbd06afde3..76d148d083e 100644 --- a/src/ui/evolution-scene-handler.ts +++ b/src/ui/evolution-scene-handler.ts @@ -45,12 +45,7 @@ export default class EvolutionSceneHandler extends MessageUiHandler { this.message = message; - const prompt = this.scene.add.sprite(0, 0, "prompt"); - prompt.setVisible(false); - prompt.setOrigin(0, 0); - this.messageContainer.add(prompt); - - this.prompt = prompt; + this.initPromptSprite(this.messageContainer); } show(_args: any[]): boolean { diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index b8c3cfd1364..0af527e518f 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -157,6 +157,9 @@ export default class MenuUiHandler extends MessageUiHandler { menuMessageText.setOrigin(0, 0); this.menuMessageBoxContainer.add(menuMessageText); + this.initTutorialOverlay(this.menuContainer); + this.initPromptSprite(this.menuMessageBoxContainer); + this.message = menuMessageText; // By default we use the general purpose message window @@ -433,6 +436,9 @@ export default class MenuUiHandler extends MessageUiHandler { this.scene.playSound("ui/menu_open"); + // Make sure the tutorial overlay sits above everything, but below the message box + this.menuContainer.bringToTop(this.tutorialOverlay); + this.menuContainer.bringToTop(this.menuMessageBoxContainer); handleTutorial(this.scene, Tutorial.Menu); this.bgmBar.toggleBgmBar(true); diff --git a/src/ui/message-ui-handler.ts b/src/ui/message-ui-handler.ts index 93e00cb6b70..54965a590fc 100644 --- a/src/ui/message-ui-handler.ts +++ b/src/ui/message-ui-handler.ts @@ -17,6 +17,23 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { this.pendingPrompt = false; } + /** + * Add the sprite to be displayed at the end of messages with prompts + * @param container the container to add the sprite to + */ + initPromptSprite(container: Phaser.GameObjects.Container) { + if (!this.prompt) { + const promptSprite = this.scene.add.sprite(0, 0, "prompt"); + promptSprite.setVisible(false); + promptSprite.setOrigin(0, 0); + this.prompt = promptSprite; + } + + if (container) { + container.add(this.prompt); + } + } + showText(text: string, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) { this.showTextInternal(text, delay, callback, callbackDelay, prompt, promptDelay); } @@ -180,7 +197,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { const lastLineWidth = lastLineTest.displayWidth; lastLineTest.destroy(); if (this.prompt) { - this.prompt.setPosition(lastLineWidth + 2, (textLinesCount - 1) * 18 + 2); + this.prompt.setPosition(this.message.x + lastLineWidth + 2, this.message.y + (textLinesCount - 1) * 18 + 2); this.prompt.play("prompt"); } this.pendingPrompt = false; diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index c9d3f195720..a1e10d74c64 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -160,7 +160,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { this.player = args[0]; - const partyHasHeldItem = this.player && !!this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isTransferrable).length; + const partyHasHeldItem = this.player && !!this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isTransferable).length; const canLockRarities = !!this.scene.findModifier(m => m instanceof LockModifierTiersModifier); this.transferButtonContainer.setVisible(false); @@ -277,13 +277,13 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { this.lockRarityButtonContainer.setVisible(canLockRarities); this.scene.tweens.add({ - targets: [ this.lockRarityButtonContainer, this.checkButtonContainer, this.continueButtonContainer ], + targets: [ this.checkButtonContainer, this.continueButtonContainer ], alpha: 1, duration: 250 }); this.scene.tweens.add({ - targets: [this.rerollButtonContainer], + targets: [this.rerollButtonContainer, this.lockRarityButtonContainer], alpha: this.rerollCost < 0 ? 0.5 : 1, duration: 250 }); diff --git a/src/ui/move-info-overlay.ts b/src/ui/move-info-overlay.ts index 77010f84528..a99e4c81e27 100644 --- a/src/ui/move-info-overlay.ts +++ b/src/ui/move-info-overlay.ts @@ -91,7 +91,7 @@ export default class MoveInfoOverlay extends Phaser.GameObjects.Container implem valuesBg.setOrigin(0, 0); this.val.add(valuesBg); - this.typ = this.scene.add.sprite(25, EFF_HEIGHT - 35, `types${Utils.verifyLang(i18next.language) ? `_${i18next.language}` : ""}`, "unknown"); + this.typ = this.scene.add.sprite(25, EFF_HEIGHT - 35, Utils.getLocalizedSpriteKey("types"), "unknown"); this.typ.setScale(0.8); this.val.add(this.typ); @@ -138,7 +138,7 @@ export default class MoveInfoOverlay extends Phaser.GameObjects.Container implem this.pow.setText(move.power >= 0 ? move.power.toString() : "---"); this.acc.setText(move.accuracy >= 0 ? move.accuracy.toString() : "---"); this.pp.setText(move.pp >= 0 ? move.pp.toString() : "---"); - this.typ.setTexture(`types${Utils.verifyLang(i18next.language) ? `_${i18next.language}` : ""}`, Type[move.type].toLowerCase()); + this.typ.setTexture(Utils.getLocalizedSpriteKey("types"), Type[move.type].toLowerCase()); this.cat.setFrame(MoveCategory[move.category].toLowerCase()); this.desc.setText(move?.effect || ""); diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 307bab0a3af..08de740e3ec 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -6,7 +6,7 @@ import { Button } from "#enums/buttons"; import { addWindow, WindowVariant } from "./ui-theme"; import { MysteryEncounterPhase } from "../phases/mystery-encounter-phases"; import { PartyUiMode } from "./party-ui-handler"; -import MysteryEncounterOption from "../data/mystery-encounters/mystery-encounter-option"; +import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; import * as Utils from "../utils"; import { isNullOrUndefined } from "../utils"; import { getPokeballAtlasKey } from "../data/pokeball"; @@ -42,7 +42,8 @@ export default class MysteryEncounterUiHandler extends UiHandler { private encounterOptions: MysteryEncounterOption[] = []; private optionsMeetsReqs: boolean[]; - protected viewPartyIndex: integer = 0; + protected viewPartyIndex: number = 0; + protected viewPartyXPosition: number = 0; protected blockInput: boolean = true; @@ -300,11 +301,11 @@ export default class MysteryEncounterUiHandler extends UiHandler { } } - override getCursor(): integer { + override getCursor(): number { return this.cursor ? this.cursor : 0; } - override setCursor(cursor: integer): boolean { + override setCursor(cursor: number): boolean { const prevCursor = this.getCursor(); const changed = prevCursor !== cursor; if (changed) { @@ -319,7 +320,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { } if (cursor === this.viewPartyIndex) { - this.cursorObj.setPosition(246, -17); + this.cursorObj.setPosition(this.viewPartyXPosition, -17); } else if (this.optionsContainer.getAll()?.length === 3) { // 2 Options this.cursorObj.setPosition(-10.5 + (cursor % 2 === 1 ? 100 : 0), 15); } else if (this.optionsContainer.getAll()?.length === 4) { // 3 Options @@ -419,8 +420,10 @@ export default class MysteryEncounterUiHandler extends UiHandler { } // View Party Button - const viewPartyText = addBBCodeTextObject(this.scene, 256, -24, getBBCodeFrag(i18next.t("mysteryEncounterMessages:view_party_button"), TextStyle.PARTY), TextStyle.PARTY); + const viewPartyText = addBBCodeTextObject(this.scene, (this.scene.game.canvas.width) / 6, -24, getBBCodeFrag(i18next.t("mysteryEncounterMessages:view_party_button"), TextStyle.PARTY), TextStyle.PARTY); this.optionsContainer.add(viewPartyText); + viewPartyText.x -= (viewPartyText.displayWidth + 16); + this.viewPartyXPosition = viewPartyText.x - 10; // Description Window const titleTextObject = addBBCodeTextObject(this.scene, 0, 0, titleText ?? "", TextStyle.TOOLTIP_TITLE, { wordWrap: { width: 750 }, align: "center", lineSpacing: -8 }); diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index aafcdc9bb34..6b6ce2aa789 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -355,7 +355,7 @@ export default class PartyUiHandler extends MessageUiHandler { const newPokemon = this.scene.getParty()[p]; // this next line gets all of the transferable items from pokemon [p]; it does this by getting all the held modifiers that are transferable and checking to see if they belong to pokemon [p] const getTransferrableItemsFromPokemon = (newPokemon: PlayerPokemon) => - this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).isTransferrable && (m as PokemonHeldItemModifier).pokemonId === newPokemon.id) as PokemonHeldItemModifier[]; + this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).isTransferable && (m as PokemonHeldItemModifier).pokemonId === newPokemon.id) as PokemonHeldItemModifier[]; // this next bit checks to see if the the selected item from the original transfer pokemon exists on the new pokemon [p]; this returns undefined if the new pokemon doesn't have the item at all, otherwise it returns the pokemonHeldItemModifier for that item const matchingModifier = newPokemon.scene.findModifier(m => m instanceof PokemonHeldItemModifier && m.pokemonId === newPokemon.id && m.matchType(getTransferrableItemsFromPokemon(pokemon)[this.transferOptionCursor])) as PokemonHeldItemModifier; const partySlot = this.partySlots.filter(m => m.getPokemon() === newPokemon)[0]; // this gets pokemon [p] for us @@ -399,7 +399,7 @@ export default class PartyUiHandler extends MessageUiHandler { || (option === PartyOption.RELEASE && this.partyUiMode === PartyUiMode.RELEASE)) { let filterResult: string | null; const getTransferrableItemsFromPokemon = (pokemon: PlayerPokemon) => - this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isTransferrable && m.pokemonId === pokemon.id) as PokemonHeldItemModifier[]; + this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isTransferable && m.pokemonId === pokemon.id) as PokemonHeldItemModifier[]; if (option !== PartyOption.TRANSFER && option !== PartyOption.SPLICE) { filterResult = (this.selectFilter as PokemonSelectFilter)(pokemon); if (filterResult === null && (option === PartyOption.SEND_OUT || option === PartyOption.PASS_BATON)) { @@ -467,8 +467,8 @@ export default class PartyUiHandler extends MessageUiHandler { } else if (option === PartyOption.UNPAUSE_EVOLUTION) { this.clearOptions(); ui.playSelect(); - pokemon.pauseEvolutions = false; - this.showText(i18next.t("partyUiHandler:unpausedEvolutions", { pokemonName: getPokemonNameWithAffix(pokemon) }), undefined, () => this.showText("", 0), null, true); + pokemon.pauseEvolutions = !pokemon.pauseEvolutions; + this.showText(i18next.t(pokemon.pauseEvolutions? "partyUiHandler:pausedEvolutions" : "partyUiHandler:unpausedEvolutions", { pokemonName: getPokemonNameWithAffix(pokemon) }), undefined, () => this.showText("", 0), null, true); } else if (option === PartyOption.UNSPLICE) { this.clearOptions(); ui.playSelect(); @@ -596,7 +596,7 @@ export default class PartyUiHandler extends MessageUiHandler { if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && !this.transferMode) { /** Initialize item quantities for the selected Pokemon */ const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier - && m.isTransferrable && m.pokemonId === this.scene.getParty()[this.cursor].id) as PokemonHeldItemModifier[]; + && m.isTransferable && m.pokemonId === this.scene.getParty()[this.cursor].id) as PokemonHeldItemModifier[]; this.transferQuantities = itemModifiers.map(item => item.getStackCount()); this.transferQuantitiesMax = itemModifiers.map(item => item.getStackCount()); } @@ -813,7 +813,7 @@ export default class PartyUiHandler extends MessageUiHandler { const itemModifiers = this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER ? this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier - && m.isTransferrable && m.pokemonId === pokemon.id) as PokemonHeldItemModifier[] + && m.isTransferable && m.pokemonId === pokemon.id) as PokemonHeldItemModifier[] : []; if (this.options.length) { @@ -889,7 +889,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.options.push(PartyOption.SUMMARY); this.options.push(PartyOption.RENAME); - if (pokemon.pauseEvolutions && (pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) || (pokemon.isFusion() && pokemon.fusionSpecies && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId)))) { + if ((pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) || (pokemon.isFusion() && pokemon.fusionSpecies && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId)))) { this.options.push(PartyOption.UNPAUSE_EVOLUTION); } @@ -976,6 +976,8 @@ export default class PartyUiHandler extends MessageUiHandler { if (formChangeItemModifiers && option >= PartyOption.FORM_CHANGE_ITEM) { const modifier = formChangeItemModifiers[option - PartyOption.FORM_CHANGE_ITEM]; optionName = `${modifier.active ? i18next.t("partyUiHandler:DEACTIVATE") : i18next.t("partyUiHandler:ACTIVATE")} ${modifier.type.name}`; + } else if (option === PartyOption.UNPAUSE_EVOLUTION) { + optionName = `${pokemon.pauseEvolutions ? i18next.t("partyUiHandler:UNPAUSE_EVOLUTION") : i18next.t("partyUiHandler:PAUSE_EVOLUTION")}`; } else { if (this.localizedOptions.includes(option)) { optionName = i18next.t(`partyUiHandler:${PartyOption[option]}`); @@ -1270,7 +1272,7 @@ class PartySlot extends Phaser.GameObjects.Container { } if (this.pokemon.status) { - const statusIndicator = this.scene.add.sprite(0, 0, "statuses"); + const statusIndicator = this.scene.add.sprite(0, 0, Utils.getLocalizedSpriteKey("statuses")); statusIndicator.setFrame(StatusEffect[this.pokemon.status?.effect].toLowerCase()); statusIndicator.setOrigin(0, 0); statusIndicator.setPositionRelative(slotLevelLabel, this.slotIndex >= battlerCount ? 43 : 55, 0); diff --git a/src/ui/pokemon-info-container.ts b/src/ui/pokemon-info-container.ts index 3c54e529d43..e9ad2a26c15 100644 --- a/src/ui/pokemon-info-container.ts +++ b/src/ui/pokemon-info-container.ts @@ -267,18 +267,13 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { this.pokemonAbilityText.setColor(getTextColor(abilityTextStyle, false, this.scene.uiTheme)); this.pokemonAbilityText.setShadowColor(getTextColor(abilityTextStyle, true, this.scene.uiTheme)); - /** - * If the opposing Pokemon only has 1 normal ability and is using the hidden ability it should have the same behavior - * if it had 2 normal abilities. This code checks if that is the case and uses the correct opponent Pokemon abilityIndex (2) - * for calculations so it aligns with where the hidden ability is stored in the starter data's abilityAttr (4) - */ - const opponentPokemonOneNormalAbility = (pokemon.species.getAbilityCount() === 2); - const opponentPokemonAbilityIndex = (opponentPokemonOneNormalAbility && pokemon.abilityIndex === 1) ? 2 : pokemon.abilityIndex; - const opponentPokemonAbilityAttr = 1 << opponentPokemonAbilityIndex; - const rootFormHasHiddenAbility = starterEntry.abilityAttr & opponentPokemonAbilityAttr; + const ownedAbilityAttrs = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr; - if (!rootFormHasHiddenAbility) { + // Check if the player owns ability for the root form + const playerOwnsThisAbility = pokemon.checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs); + + if (!playerOwnsThisAbility) { this.pokemonAbilityLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme)); this.pokemonAbilityLabelText.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true, this.scene.uiTheme)); } else { diff --git a/src/ui/run-history-ui-handler.ts b/src/ui/run-history-ui-handler.ts index 8f132a1ab1c..d983fb0b0b8 100644 --- a/src/ui/run-history-ui-handler.ts +++ b/src/ui/run-history-ui-handler.ts @@ -13,7 +13,7 @@ import { RunEntry } from "../system/game-data"; import { PlayerGender } from "#enums/player-gender"; import { TrainerVariant } from "../field/trainer"; -export type RunSelectCallback = (cursor: integer) => void; +export type RunSelectCallback = (cursor: number) => void; export const RUN_HISTORY_LIMIT: number = 25; @@ -25,15 +25,15 @@ export const RUN_HISTORY_LIMIT: number = 25; */ export default class RunHistoryUiHandler extends MessageUiHandler { + private readonly maxRows = 3; + private runSelectContainer: Phaser.GameObjects.Container; private runsContainer: Phaser.GameObjects.Container; - private runSelectMessageBox: Phaser.GameObjects.NineSlice; - private runSelectMessageBoxContainer: Phaser.GameObjects.Container; private runs: RunEntryContainer[]; private runSelectCallback: RunSelectCallback | null; - private scrollCursor: integer = 0; + private scrollCursor: number = 0; private cursorObj: Phaser.GameObjects.NineSlice | null; @@ -74,15 +74,15 @@ export default class RunHistoryUiHandler extends MessageUiHandler { this.getUi().bringToTop(this.runSelectContainer); this.runSelectContainer.setVisible(true); - this.populateRuns(this.scene); + this.populateRuns(this.scene).then(() => { + this.setScrollCursor(0); + this.setCursor(0); - this.setScrollCursor(0); - this.setCursor(0); - - //Destroys the cursor if there are no runs saved so far. - if (this.runs.length === 0) { - this.clearCursor(); - } + //Destroys the cursor if there are no runs saved so far. + if (this.runs.length === 0) { + this.clearCursor(); + } + }); return true; } @@ -122,13 +122,21 @@ export default class RunHistoryUiHandler extends MessageUiHandler { success = this.setCursor(this.cursor - 1); } else if (this.scrollCursor) { success = this.setScrollCursor(this.scrollCursor - 1); + } else if (this.runs.length > 1) { + // wrap around to the bottom + success = this.setCursor(Math.min(this.runs.length - 1, this.maxRows - 1)); + success = this.setScrollCursor(Math.max(0, this.runs.length - this.maxRows)) || success; } break; case Button.DOWN: - if (this.cursor < 2) { + if (this.cursor < Math.min(this.maxRows - 1, this.runs.length - this.scrollCursor - 1)) { success = this.setCursor(this.cursor + 1); - } else if (this.scrollCursor < this.runs.length - 3) { + } else if (this.scrollCursor < this.runs.length - this.maxRows) { success = this.setScrollCursor(this.scrollCursor + 1); + } else if (this.runs.length > 1) { + // wrap around to the top + success = this.setCursor(0); + success = this.setScrollCursor(0) || success; } break; } @@ -218,6 +226,7 @@ export default class RunHistoryUiHandler extends MessageUiHandler { override clear() { super.clear(); this.runSelectContainer.setVisible(false); + this.setScrollCursor(0); this.clearCursor(); this.runSelectCallback = null; this.clearRuns(); @@ -281,7 +290,7 @@ class RunEntryContainer extends Phaser.GameObjects.Container { const genderIndex = this.scene.gameData.gender ?? PlayerGender.UNSET; const genderStr = PlayerGender[genderIndex].toLowerCase(); // Defeats from wild Pokemon battles will show the Pokemon responsible by the text of the run result. - if (data.battleType === BattleType.WILD) { + if (data.battleType === BattleType.WILD || (data.battleType === BattleType.MYSTERY_ENCOUNTER && !data.trainer)) { const enemyContainer = this.scene.add.container(8, 5); const gameOutcomeLabel = addTextObject(this.scene, 0, 0, `${i18next.t("runHistory:defeatedWild", { context: genderStr })}`, TextStyle.WINDOW); enemyContainer.add(gameOutcomeLabel); @@ -302,7 +311,7 @@ class RunEntryContainer extends Phaser.GameObjects.Container { enemy.destroy(); }); this.add(enemyContainer); - } else if (data.battleType === BattleType.TRAINER) { // Defeats from Trainers show the trainer's title and name + } else if (data.battleType === BattleType.TRAINER || (data.battleType === BattleType.MYSTERY_ENCOUNTER && data.trainer)) { // Defeats from Trainers show the trainer's title and name const tObj = data.trainer.toTrainer(this.scene); // Because of the interesting mechanics behind rival names, the rival name and title have to be retrieved differently const RIVAL_TRAINER_ID_THRESHOLD = 375; @@ -360,7 +369,7 @@ class RunEntryContainer extends Phaser.GameObjects.Container { // The code here does not account for icon weirdness. const pokemonIconsContainer = this.scene.add.container(140, 17); - data.party.forEach((p: PokemonData, i: integer) => { + data.party.forEach((p: PokemonData, i: number) => { const iconContainer = this.scene.add.container(26 * i, 0); iconContainer.setScale(0.75); const pokemon = p.toPokemon(this.scene); diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index 8f0437002d4..119b7bc9c4a 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -49,15 +49,11 @@ export default class RunInfoUiHandler extends UiHandler { private runResultContainer: Phaser.GameObjects.Container; private runInfoContainer: Phaser.GameObjects.Container; private partyContainer: Phaser.GameObjects.Container; - private partyHeldItemsContainer: Phaser.GameObjects.Container; private statsBgWidth: integer; - private partyContainerHeight: integer; - private partyContainerWidth: integer; private hallofFameContainer: Phaser.GameObjects.Container; private endCardContainer: Phaser.GameObjects.Container; - private partyInfo: Phaser.GameObjects.Container[]; private partyVisibility: Boolean; private modifiersModule: any; @@ -211,7 +207,7 @@ export default class RunInfoUiHandler extends UiHandler { if (!this.isVictory) { const enemyContainer = this.scene.add.container(0, 0); // Wild - Single and Doubles - if (this.runInfo.battleType === BattleType.WILD) { + if (this.runInfo.battleType === BattleType.WILD || (this.runInfo.battleType === BattleType.MYSTERY_ENCOUNTER && !this.runInfo.trainer)) { switch (this.runInfo.enemyParty.length) { case 1: // Wild - Singles @@ -222,7 +218,7 @@ export default class RunInfoUiHandler extends UiHandler { this.parseWildDoubleDefeat(enemyContainer); break; } - } else if (this.runInfo.battleType === BattleType.TRAINER) { + } else if (this.runInfo.battleType === BattleType.TRAINER || (this.runInfo.battleType === BattleType.MYSTERY_ENCOUNTER && this.runInfo.trainer)) { this.parseTrainerDefeat(enemyContainer); } this.runResultContainer.add(enemyContainer); @@ -381,10 +377,6 @@ export default class RunInfoUiHandler extends UiHandler { break; case GameModes.SPLICED_ENDLESS: modeText.appendText(`${i18next.t("gameMode:endlessSpliced")}`, false); - if (this.runInfo.waveIndex === this.scene.gameData.gameStats.highestEndlessWave) { - modeText.appendText(` [${i18next.t("runHistory:personalBest")}]`, false); - modeText.setTint(0xffef5c, 0x47ff69, 0x6b6bff, 0xff6969); - } break; case GameModes.CHALLENGE: modeText.appendText(`${i18next.t("gameMode:challenge")}`, false); @@ -403,17 +395,18 @@ export default class RunInfoUiHandler extends UiHandler { break; case GameModes.ENDLESS: modeText.appendText(`${i18next.t("gameMode:endless")}`, false); - // If the player achieves a personal best in Endless, the mode text will be tinted similarly to SSS luck to celebrate their achievement. - if (this.runInfo.waveIndex === this.scene.gameData.gameStats.highestEndlessWave) { - modeText.appendText(` [${i18next.t("runHistory:personalBest")}]`, false); - modeText.setTint(0xffef5c, 0x47ff69, 0x6b6bff, 0xff6969); - } break; case GameModes.CLASSIC: modeText.appendText(`${i18next.t("gameMode:classic")}`, false); break; } + // If the player achieves a personal best in Endless, the mode text will be tinted similarly to SSS luck to celebrate their achievement. + if ((this.runInfo.gameMode === GameModes.ENDLESS || this.runInfo.gameMode === GameModes.SPLICED_ENDLESS) && this.runInfo.waveIndex === this.scene.gameData.gameStats.highestEndlessWave) { + modeText.appendText(` [${i18next.t("runHistory:personalBest")}]`); + modeText.setTint(0xffef5c, 0x47ff69, 0x6b6bff, 0xff6969); + } + // Duration + Money const runInfoTextContainer = this.scene.add.container(0, 0); // Japanese is set to a greater line spacing of 35px in addBBCodeTextObject() if lineSpacing < 12. @@ -866,7 +859,7 @@ export default class RunInfoUiHandler extends UiHandler { private buttonCycleOption(button: Button) { switch (button) { case Button.CYCLE_FORM: - if (this.isVictory) { + if (this.isVictory && this.pageMode !== RunInfoUiMode.HALL_OF_FAME) { if (!this.endCardContainer || !this.endCardContainer.visible) { this.createVictorySplash(); this.endCardContainer.setVisible(true); @@ -880,7 +873,7 @@ export default class RunInfoUiHandler extends UiHandler { } break; case Button.CYCLE_SHINY: - if (this.isVictory) { + if (this.isVictory && this.pageMode !== RunInfoUiMode.ENDING_ART) { if (!this.hallofFameContainer.visible) { this.hallofFameContainer.setVisible(true); this.pageMode = RunInfoUiMode.HALL_OF_FAME; @@ -891,7 +884,7 @@ export default class RunInfoUiHandler extends UiHandler { } break; case Button.CYCLE_ABILITY: - if (this.runInfo.modifiers.length !== 0) { + if (this.runInfo.modifiers.length !== 0 && this.pageMode === RunInfoUiMode.MAIN) { if (this.partyVisibility) { this.showParty(false); } else { diff --git a/src/ui/scroll-bar.ts b/src/ui/scroll-bar.ts index e756393ae1a..5ed79d0cdad 100644 --- a/src/ui/scroll-bar.ts +++ b/src/ui/scroll-bar.ts @@ -1,36 +1,65 @@ +/** + * A vertical scrollbar element that resizes dynamically based on the current scrolling + * and number of elements that can be shown on screen + */ export class ScrollBar extends Phaser.GameObjects.Container { - private bg: Phaser.GameObjects.Image; + private bg: Phaser.GameObjects.NineSlice; private handleBody: Phaser.GameObjects.Rectangle; - private handleBottom: Phaser.GameObjects.Image; - private pages: number; - private page: number; + private handleBottom: Phaser.GameObjects.NineSlice; + private currentRow: number; + private totalRows: number; + private maxRows: number; - constructor(scene: Phaser.Scene, x: number, y: number, pages: number) { + /** + * @param scene the current scene + * @param x the scrollbar's x position (origin: top left) + * @param y the scrollbar's y position (origin: top left) + * @param width the scrollbar's width + * @param height the scrollbar's height + * @param maxRows the maximum number of rows that can be shown at once + */ + constructor(scene: Phaser.Scene, x: number, y: number, width: number, height: number, maxRows: number) { super(scene, x, y); - this.bg = scene.add.image(0, 0, "scroll_bar"); + this.maxRows = maxRows; + + const borderSize = 2; + width = Math.max(width, 4); + + this.bg = scene.add.nineslice(0, 0, "scroll_bar", undefined, width, height, borderSize, borderSize, borderSize, borderSize); this.bg.setOrigin(0, 0); this.add(this.bg); - this.handleBody = scene.add.rectangle(1, 1, 3, 4, 0xaaaaaa); + this.handleBody = scene.add.rectangle(1, 1, width - 2, 4, 0xaaaaaa); this.handleBody.setOrigin(0, 0); this.add(this.handleBody); - this.handleBottom = scene.add.image(1, 1, "scroll_bar_handle"); + this.handleBottom = scene.add.nineslice(1, 1, "scroll_bar_handle", undefined, width - 2, 2, 2, 0, 0, 0); this.handleBottom.setOrigin(0, 0); this.add(this.handleBottom); } - setPage(page: number): void { - this.page = page; - this.handleBody.y = 1 + (this.bg.displayHeight - 1 - this.handleBottom.displayHeight) / this.pages * page; + /** + * Set the current row that is displayed + * Moves the bar handle up or down accordingly + * @param scrollCursor how many times the view was scrolled down + */ + setScrollCursor(scrollCursor: number): void { + this.currentRow = scrollCursor; + this.handleBody.y = 1 + (this.bg.displayHeight - 1 - this.handleBottom.displayHeight) / this.totalRows * this.currentRow; this.handleBottom.y = this.handleBody.y + this.handleBody.displayHeight; } - setPages(pages: number): void { - this.pages = pages; - this.handleBody.height = (this.bg.displayHeight - 1 - this.handleBottom.displayHeight) * 9 / this.pages; + /** + * Set the total number of rows to display + * If it's smaller than the maximum number of rows on screen the bar will get hidden + * Otherwise the scrollbar handle gets resized based on the ratio to the maximum number of rows + * @param rows how many rows of data there are in total + */ + setTotalRows(rows: number): void { + this.totalRows = rows; + this.handleBody.height = (this.bg.displayHeight - 1 - this.handleBottom.displayHeight) * this.maxRows / this.totalRows; - this.setVisible(this.pages > 9); + this.setVisible(this.totalRows > this.maxRows); } } diff --git a/src/ui/settings/abstract-control-settings-ui-handler.ts b/src/ui/settings/abstract-control-settings-ui-handler.ts index f8dab1bf7cc..efa262bb2e9 100644 --- a/src/ui/settings/abstract-control-settings-ui-handler.ts +++ b/src/ui/settings/abstract-control-settings-ui-handler.ts @@ -1,11 +1,12 @@ -import UiHandler from "../ui-handler"; -import BattleScene from "../../battle-scene"; -import {Mode} from "../ui"; -import {InterfaceConfig} from "../../inputs-controller"; -import {addWindow} from "../ui-theme"; -import {addTextObject, TextStyle} from "../text"; -import {getIconWithSettingName} from "#app/configs/inputs/configHandler"; -import NavigationMenu, {NavigationManager} from "#app/ui/settings/navigationMenu"; +import UiHandler from "#app/ui/ui-handler"; +import BattleScene from "#app/battle-scene"; +import { Mode } from "#app/ui/ui"; +import { InterfaceConfig } from "#app/inputs-controller"; +import { addWindow } from "#app/ui/ui-theme"; +import { addTextObject, TextStyle } from "#app/ui/text"; +import { ScrollBar } from "#app/ui/scroll-bar"; +import { getIconWithSettingName } from "#app/configs/inputs/configHandler"; +import NavigationMenu, { NavigationManager } from "#app/ui/settings/navigationMenu"; import { Device } from "#enums/devices"; import { Button } from "#enums/buttons"; import i18next from "i18next"; @@ -19,7 +20,7 @@ export interface LayoutConfig { inputsIcons: InputsIcons; settingLabels: Phaser.GameObjects.Text[]; optionValueLabels: Phaser.GameObjects.Text[][]; - optionCursors: integer[]; + optionCursors: number[]; keys: string[]; bindingSettings: Array; } @@ -31,8 +32,9 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler protected optionsContainer: Phaser.GameObjects.Container; protected navigationContainer: NavigationMenu; - protected scrollCursor: integer; - protected optionCursors: integer[]; + protected scrollBar: ScrollBar; + protected scrollCursor: number; + protected optionCursors: number[]; protected cursorObj: Phaser.GameObjects.NineSlice | null; protected optionsBg: Phaser.GameObjects.NineSlice; @@ -65,7 +67,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler protected device: Device; abstract saveSettingToLocalStorage(setting, cursor): void; - abstract setSetting(scene: BattleScene, setting, value: integer): boolean; + abstract setSetting(scene: BattleScene, setting, value: number): boolean; /** * Constructor for the AbstractSettingsUiHandler. @@ -241,7 +243,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler // Calculate the total available space for placing option labels next to their setting label // We reserve space for the setting label and then distribute the remaining space evenly - const totalSpace = (300 - labelWidth) - totalWidth / 6; + const totalSpace = (297 - labelWidth) - totalWidth / 6; // Calculate the spacing between options based on the available space divided by the number of gaps between labels const optionSpacing = Math.floor(totalSpace / (optionValueLabels[s].length - 1)); @@ -269,6 +271,11 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler // Add the options container to the overall settings container to be displayed in the UI. this.settingsContainer.add(optionsContainer); } + + // Add vertical scrollbar + this.scrollBar = new ScrollBar(this.scene, this.optionsBg.width - 9, this.optionsBg.y + 5, 4, this.optionsBg.height - 11, this.rowsToDisplay); + this.settingsContainer.add(this.scrollBar); + // Add the settings container to the UI. ui.add(this.settingsContainer); @@ -413,6 +420,8 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler this.optionCursors = layout.optionCursors; this.inputsIcons = layout.inputsIcons; this.bindingSettings = layout.bindingSettings; + this.scrollBar.setTotalRows(layout.settingLabels.length); + this.scrollBar.setScrollCursor(0); // Return true indicating the layout was successfully applied. return true; @@ -538,7 +547,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler * @param cursor - The cursor position to set. * @returns `true` if the cursor was set successfully. */ - setCursor(cursor: integer): boolean { + setCursor(cursor: number): boolean { const ret = super.setCursor(cursor); // If the optionsContainer is not initialized, return the result from the parent class directly. if (!this.optionsContainer) { @@ -547,7 +556,8 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler // Check if the cursor object exists, if not, create it. if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1); + const cursorWidth = (this.scene.game.canvas.width / 6) - (this.scrollBar.visible? 16 : 10); + this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, cursorWidth, 16, 1, 1, 1, 1); this.cursorObj.setOrigin(0, 0); // Set the origin to the top-left corner. this.optionsContainer.add(this.cursorObj); // Add the cursor to the options container. } @@ -564,7 +574,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler * @param scrollCursor - The scroll cursor position to set. * @returns `true` if the scroll cursor was set successfully. */ - setScrollCursor(scrollCursor: integer): boolean { + setScrollCursor(scrollCursor: number): boolean { // Check if the new scroll position is the same as the current one; if so, do not update. if (scrollCursor === this.scrollCursor) { return false; @@ -572,6 +582,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler // Update the internal scroll cursor state this.scrollCursor = scrollCursor; + this.scrollBar.setScrollCursor(this.scrollCursor); // Apply the new scroll position to the settings UI. this.updateSettingsScroll(); @@ -590,7 +601,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler * @param save - Whether to save the setting to local storage. * @returns `true` if the option cursor was set successfully. */ - setOptionCursor(settingIndex: integer, cursor: integer, save?: boolean): boolean { + setOptionCursor(settingIndex: number, cursor: number, save?: boolean): boolean { // Retrieve the specific setting using the settingIndex from the settingDevice enumeration. const setting = this.setting[Object.keys(this.setting)[settingIndex]]; diff --git a/src/ui/settings/abstract-settings-ui-handler.ts b/src/ui/settings/abstract-settings-ui-handler.ts index 570377eab43..975a32127ff 100644 --- a/src/ui/settings/abstract-settings-ui-handler.ts +++ b/src/ui/settings/abstract-settings-ui-handler.ts @@ -1,12 +1,13 @@ -import BattleScene from "../../battle-scene"; -import { hasTouchscreen, isMobile } from "../../touch-controls"; -import { TextStyle, addTextObject } from "../text"; -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"; -import NavigationMenu, {NavigationManager} from "#app/ui/settings/navigationMenu"; +import BattleScene from "#app/battle-scene"; +import { hasTouchscreen, isMobile } from "#app/touch-controls"; +import { TextStyle, addTextObject } from "#app/ui/text"; +import { Mode } from "#app/ui/ui"; +import UiHandler from "#app/ui/ui-handler"; +import { addWindow } from "#app/ui/ui-theme"; +import { ScrollBar } from "#app/ui/scroll-bar"; +import { Button } from "#enums/buttons"; +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"; @@ -19,11 +20,12 @@ export default class AbstractSettingsUiHandler extends UiHandler { private optionsContainer: Phaser.GameObjects.Container; private navigationContainer: NavigationMenu; - private scrollCursor: integer; + private scrollCursor: number; + private scrollBar: ScrollBar; private optionsBg: Phaser.GameObjects.NineSlice; - private optionCursors: integer[]; + private optionCursors: number[]; private settingLabels: Phaser.GameObjects.Text[]; private optionValueLabels: Phaser.GameObjects.Text[][]; @@ -117,7 +119,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { const labelWidth = Math.max(78, this.settingLabels[s].displayWidth + 8); - const totalSpace = (300 - labelWidth) - totalWidth / 6; + const totalSpace = (297 - labelWidth) - totalWidth / 6; const optionSpacing = Math.floor(totalSpace / (this.optionValueLabels[s].length - 1)); let xOffset = 0; @@ -130,7 +132,11 @@ export default class AbstractSettingsUiHandler extends UiHandler { this.optionCursors = this.settings.map(setting => setting.default); + this.scrollBar = new ScrollBar(this.scene, this.optionsBg.width - 9, this.optionsBg.y + 5, 4, this.optionsBg.height - 11, this.rowsToDisplay); + this.scrollBar.setTotalRows(this.settings.length); + this.settingsContainer.add(this.optionsBg); + this.settingsContainer.add(this.scrollBar); this.settingsContainer.add(this.navigationContainer); this.settingsContainer.add(actionsBg); this.settingsContainer.add(this.optionsContainer); @@ -186,6 +192,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { this.settingsContainer.setVisible(true); this.setCursor(0); + this.setScrollCursor(0); this.getUi().moveTo(this.settingsContainer, this.getUi().length - 1); @@ -301,11 +308,12 @@ export default class AbstractSettingsUiHandler extends UiHandler { * @param cursor - The cursor position to set. * @returns `true` if the cursor was set successfully. */ - setCursor(cursor: integer): boolean { + setCursor(cursor: number): boolean { const ret = super.setCursor(cursor); if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1); + const cursorWidth = (this.scene.game.canvas.width / 6) - (this.scrollBar.visible? 16 : 10); + this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, cursorWidth, 16, 1, 1, 1, 1); this.cursorObj.setOrigin(0, 0); this.optionsContainer.add(this.cursorObj); } @@ -323,7 +331,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { * @param save - Whether to save the setting to local storage. * @returns `true` if the option cursor was set successfully. */ - setOptionCursor(settingIndex: integer, cursor: integer, save?: boolean): boolean { + setOptionCursor(settingIndex: number, cursor: number, save?: boolean): boolean { const setting = this.settings[settingIndex]; if (setting.key === SettingKeys.Touch_Controls && cursor && hasTouchscreen() && isMobile()) { @@ -359,12 +367,13 @@ export default class AbstractSettingsUiHandler extends UiHandler { * @param scrollCursor - The scroll cursor position to set. * @returns `true` if the scroll cursor was set successfully. */ - setScrollCursor(scrollCursor: integer): boolean { + setScrollCursor(scrollCursor: number): boolean { if (scrollCursor === this.scrollCursor) { return false; } this.scrollCursor = scrollCursor; + this.scrollBar.setScrollCursor(this.scrollCursor); this.updateSettingsScroll(); @@ -394,6 +403,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { clear() { super.clear(); this.settingsContainer.setVisible(false); + this.setScrollCursor(0); this.eraseCursor(); this.getUi().bgmBar.toggleBgmBar(this.scene.showBgmBar); if (this.reloadRequired) { diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index ac6af5cbb04..5ef26d1ba88 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -13,7 +13,7 @@ import { allMoves } from "../data/move"; import { Nature, getNatureName } from "../data/nature"; import { pokemonFormChanges } from "../data/pokemon-forms"; import { LevelMoves, pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "../data/pokemon-level-moves"; -import PokemonSpecies, { allSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities, getPokerusStarters } from "../data/pokemon-species"; +import PokemonSpecies, { allSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities, POKERUS_STARTER_COUNT, getPokerusStarters } from "../data/pokemon-species"; import { Type } from "../data/type"; import { GameModes } from "../game-mode"; import { AbilityAttr, DexAttr, DexAttrProps, DexEntry, StarterMoveset, StarterAttributes, StarterPreferences, StarterPrefs } from "../system/game-data"; @@ -83,7 +83,7 @@ const languageSettings: { [key: string]: LanguageSetting } = { }, "fr":{ starterInfoTextSize: "54px", - instructionTextSize: "35px", + instructionTextSize: "38px", }, "it":{ starterInfoTextSize: "56px", @@ -627,11 +627,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const starterBoxContainer = this.scene.add.container(speciesContainerX + 6, 9); //115 - this.starterSelectScrollBar = new ScrollBar(this.scene, 161, 12, 0); + this.starterSelectScrollBar = new ScrollBar(this.scene, 161, 12, 5, starterContainerWindow.height - 6, 9); starterBoxContainer.add(this.starterSelectScrollBar); - this.pokerusCursorObjs = new Array(3).fill(null).map(() => { + this.pokerusCursorObjs = new Array(POKERUS_STARTER_COUNT).fill(null).map(() => { const cursorObj = this.scene.add.image(0, 0, "select_cursor_pokerus"); cursorObj.setVisible(false); cursorObj.setOrigin(0, 0); @@ -760,7 +760,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonCaughtHatchedContainer.add(this.pokemonHatchedCountText); this.pokemonMovesContainer = this.scene.add.container(102, 16); - this.pokemonMovesContainer.setScale(0.5); + this.pokemonMovesContainer.setScale(0.375); for (let m = 0; m < 4; m++) { const moveContainer = this.scene.add.container(0, 14 * m); @@ -894,6 +894,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.message.setOrigin(0, 0); this.starterSelectMessageBoxContainer.add(this.message); + // arrow icon for the message box + this.initPromptSprite(this.starterSelectMessageBoxContainer); + this.statsContainer = new StatsContainer(this.scene, 6, 16); this.scene.add.existing(this.statsContainer); @@ -911,7 +914,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler { y: this.scene.game.canvas.height / 6 - MoveInfoOverlay.getHeight(overlayScale) - 29, }); this.starterSelectContainer.add(this.moveInfoOverlay); + + // Filter bar sits above everything, except the tutorial overlay and message box this.starterSelectContainer.bringToTop(this.filterBarContainer); + this.initTutorialOverlay(this.starterSelectContainer); + this.starterSelectContainer.bringToTop(this.starterSelectMessageBoxContainer); this.scene.eventTarget.addEventListener(BattleSceneEventType.CANDY_UPGRADE_NOTIFICATION_CHANGED, (e) => this.onCandyUpgradeDisplayChanged(e)); @@ -2533,8 +2540,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } }); - this.starterSelectScrollBar.setPages(Math.max(Math.ceil(this.filteredStarterContainers.length / 9), 1)); - this.starterSelectScrollBar.setPage(0); + this.starterSelectScrollBar.setTotalRows(Math.max(Math.ceil(this.filteredStarterContainers.length / 9), 1)); + this.starterSelectScrollBar.setScrollCursor(0); // sort const sort = this.filterBar.getVals(DropDownColumn.SORT)[0]; @@ -2569,7 +2576,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const onScreenFirstIndex = this.scrollCursor * maxColumns; const onScreenLastIndex = Math.min(this.filteredStarterContainers.length - 1, onScreenFirstIndex + maxRows * maxColumns -1); - this.starterSelectScrollBar.setPage(this.scrollCursor); + this.starterSelectScrollBar.setScrollCursor(this.scrollCursor); let pokerusCursorIndex = 0; this.filteredStarterContainers.forEach((container, i) => { diff --git a/src/ui/stats-container.ts b/src/ui/stats-container.ts index c6e0ea3a71c..06dd6e7c035 100644 --- a/src/ui/stats-container.ts +++ b/src/ui/stats-container.ts @@ -4,12 +4,15 @@ import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./t 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]]; const speedLabelOffset = -3; const sideLabelOffset = 1; const ivLabelOffset = [0, sideLabelOffset, -sideLabelOffset, sideLabelOffset, -sideLabelOffset, speedLabelOffset]; +const ivChartLabelyOffset= [0, 5, 0, 5, 0, 0]; // doing this so attack does not overlap with (+N) const ivChartStatIndexes = [0, 1, 2, 5, 4, 3]; // swap special attack and speed + const defaultIvChartData = new Array(12).fill(null).map(() => 0); export class StatsContainer extends Phaser.GameObjects.Container { @@ -29,7 +32,6 @@ export class StatsContainer extends Phaser.GameObjects.Container { setup() { this.setName("stats"); const ivChartBgData = new Array(6).fill(null).map((_, i: integer) => [ ivChartSize * ivChartStatCoordMultipliers[ivChartStatIndexes[i]][0], ivChartSize * ivChartStatCoordMultipliers[ivChartStatIndexes[i]][1] ] ).flat(); - const ivChartBg = this.scene.add.polygon(48, 44, ivChartBgData, 0xd8e0f0, 0.625); ivChartBg.setOrigin(0, 0); @@ -55,12 +57,19 @@ export class StatsContainer extends Phaser.GameObjects.Container { this.ivStatValueTexts = []; 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); + const statLabel = addTextObject( + this.scene, + ivChartBg.x + (ivChartSize) * ivChartStatCoordMultipliers[s][0] * 1.325 + (this.showDiff ? 0 : ivLabelOffset[s]), + ivChartBg.y + (ivChartSize) * ivChartStatCoordMultipliers[s][1] * 1.325 - 4 + (this.showDiff ? 0 : ivChartLabelyOffset[s]), + i18next.t(getStatKey(s)), + TextStyle.TOOLTIP_CONTENT + ); statLabel.setOrigin(0.5); - this.ivStatValueTexts[s] = addBBCodeTextObject(this.scene, statLabel.x, statLabel.y + 8, "0", TextStyle.TOOLTIP_CONTENT); + this.ivStatValueTexts[s] = addBBCodeTextObject(this.scene, statLabel.x - (this.showDiff ? 0 : ivLabelOffset[s]), statLabel.y + 8, "0", TextStyle.TOOLTIP_CONTENT); this.ivStatValueTexts[s].setOrigin(0.5); + this.add(statLabel); this.add(this.ivStatValueTexts[s]); } diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index 8ae72f08edd..fb9f1561447 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -214,7 +214,7 @@ export default class SummaryUiHandler extends UiHandler { this.statusContainer.add(statusLabel); - this.status = this.scene.add.sprite(91, 4, "statuses"); + this.status = this.scene.add.sprite(91, 4, Utils.getLocalizedSpriteKey("statuses")); this.status.setOrigin(0.5, 0); this.statusContainer.add(this.status); @@ -824,6 +824,7 @@ export default class SummaryUiHandler extends UiHandler { metFragment: i18next.t(`pokemonSummary:metFragment.${this.pokemon?.metBiome === -1? "apparently": "normal"}`, { biome: `${getBBCodeFrag(getBiomeName(this.pokemon?.metBiome!), TextStyle.SUMMARY_RED)}${closeFragment}`, // TODO: is this bang correct? level: `${getBBCodeFrag(this.pokemon?.metLevel.toString()!, TextStyle.SUMMARY_RED)}${closeFragment}`, // TODO: is this bang correct? + wave: `${getBBCodeFrag((this.pokemon?.metWave ? this.pokemon.metWave.toString()! : i18next.t("pokemonSummary:unknownTrainer")), TextStyle.SUMMARY_RED)}${closeFragment}`, }), natureFragment: i18next.t(`pokemonSummary:natureFragment.${rawNature}`, { nature: nature }) }); diff --git a/src/ui/title-ui-handler.ts b/src/ui/title-ui-handler.ts index 67a4f7260e6..4087b397ff7 100644 --- a/src/ui/title-ui-handler.ts +++ b/src/ui/title-ui-handler.ts @@ -3,11 +3,14 @@ import OptionSelectUiHandler from "./settings/option-select-ui-handler"; import { Mode } from "./ui"; import * as Utils from "../utils"; import { TextStyle, addTextObject, getTextStyleOptions } from "./text"; -import { getBattleCountSplashMessage, getSplashMessages } from "../data/splash-messages"; +import { getSplashMessages } from "../data/splash-messages"; import i18next from "i18next"; import { TimedEventDisplay } from "#app/timed-event-manager"; export default class TitleUiHandler extends OptionSelectUiHandler { + /** If the stats can not be retrieved, use this fallback value */ + private static readonly BATTLES_WON_FALLBACK: number = -99999999; + private titleContainer: Phaser.GameObjects.Container; private playerCountLabel: Phaser.GameObjects.Text; private splashMessage: string; @@ -72,8 +75,8 @@ export default class TitleUiHandler extends OptionSelectUiHandler { .then(request => request.json()) .then(stats => { this.playerCountLabel.setText(`${stats.playerCount} ${i18next.t("menu:playersOnline")}`); - if (this.splashMessage === getBattleCountSplashMessage()) { - this.splashMessageText.setText(getBattleCountSplashMessage().replace("{COUNT}", stats.battleCount.toLocaleString("en-US"))); + if (this.splashMessage === "splashMessages:battlesWon") { + this.splashMessageText.setText(i18next.t(this.splashMessage, { count: stats.battleCount })); } }) .catch(err => { @@ -86,7 +89,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler { if (ret) { this.splashMessage = Utils.randItem(getSplashMessages()); - this.splashMessageText.setText(this.splashMessage.replace("{COUNT}", "?")); + this.splashMessageText.setText(i18next.t(this.splashMessage, { count: TitleUiHandler.BATTLES_WON_FALLBACK })); const ui = this.getUi(); diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 0f4fa52e41e..7e00c87cc5f 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -139,7 +139,8 @@ const noTransitionModes = [ Mode.TEST_DIALOGUE, Mode.AUTO_COMPLETE, Mode.ADMIN, - Mode.MYSTERY_ENCOUNTER + Mode.MYSTERY_ENCOUNTER, + Mode.RUN_INFO ]; export default class UI extends Phaser.GameObjects.Container { diff --git a/src/utils.ts b/src/utils.ts index a8206bf4dcf..e526d086316 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,5 @@ import { MoneyFormat } from "#enums/money-format"; +import { Moves } from "#enums/moves"; import i18next from "i18next"; export const MissingTextureKey = "__MISSING"; @@ -628,3 +629,12 @@ export function getLocalizedSpriteKey(baseKey: string) { export function isBetween(num: number, min: number, max: number): boolean { return num >= min && num <= max; } + +/** + * Helper method to return the animation filename for a given move + * + * @param move the move for which the animation filename is needed + */ +export function animationFileName(move: Moves): string { + return Moves[move].toLowerCase().replace(/\_/g, "-"); +} diff --git a/vite.config.ts b/vite.config.ts index 1fd85e2572f..946315c4b7b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -31,6 +31,7 @@ export default defineConfig(({mode}) => { return ({ ...defaultConfig, + base: '', esbuild: { pure: mode === 'production' ? ['console.log'] : [], keepNames: true, diff --git a/vitest.config.ts b/vitest.config.ts index 54462675704..9f9245687a1 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -4,6 +4,7 @@ import { defaultConfig } from "./vite.config"; export default defineProject(({ mode }) => ({ ...defaultConfig, test: { + testTimeout: 20000, setupFiles: ["./src/test/fontFace.setup.ts", "./src/test/vitest.setup.ts"], server: { deps: {