diff --git a/.env.beta b/.env.beta index 88147215258..38888ec3d97 100644 --- a/.env.beta +++ b/.env.beta @@ -1,6 +1,6 @@ VITE_BYPASS_LOGIN=0 VITE_BYPASS_TUTORIAL=0 -VITE_SERVER_URL=https://api.beta.pokerogue.net +VITE_SERVER_URL=https://apibeta.pokerogue.net VITE_DISCORD_CLIENT_ID=1248062921129459756 VITE_GOOGLE_CLIENT_ID=955345393540-2k6lfftf0fdnb0krqmpthjnqavfvvf73.apps.googleusercontent.com VITE_I18N_DEBUG=1 diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 180c7af1240..8c724cda762 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -github: patapancakes +github: pagefaultgames diff --git a/package-lock.json b/package-lock.json index 1f6fc6e8967..f633d427d6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,11 +33,12 @@ "inquirer": "^11.0.2", "jsdom": "^24.0.0", "lefthook": "^1.6.12", + "msw": "^2.4.9", "phaser3spectorjs": "^0.0.8", "typedoc": "^0.26.4", "typescript": "^5.5.3", "typescript-eslint": "^8.0.0-alpha.54", - "vite": "^5.3.5", + "vite": "^5.4.8", "vite-tsconfig-paths": "^4.3.2", "vitest": "^2.0.4", "vitest-canvas-mock": "^0.3.3" @@ -487,6 +488,37 @@ "node": ">=6.9.0" } }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz", + "integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cookie": "^0.5.0" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "license": "ISC", + "dependencies": { + "statuses": "^2.0.1" + } + }, + "node_modules/@bundled-es-modules/tough-cookie": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz", + "integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@types/tough-cookie": "^4.0.5", + "tough-cookie": "^4.1.4" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -1462,6 +1494,24 @@ "integrity": "sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==", "license": "Apache-2.0" }, + "node_modules/@mswjs/interceptors": { + "version": "0.35.8", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.35.8.tgz", + "integrity": "sha512-PFfqpHplKa7KMdoQdj5td03uG05VK2Ng1dG0sP4pT9h0dGSX2v9txYt/AnrzPb/vAmfyBBC0NQV7VaBEX+efgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1500,6 +1550,31 @@ "node": ">= 8" } }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true, + "license": "MIT" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1778,6 +1853,13 @@ "eslint": ">=8.40.0" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/eslint": { "version": "8.56.11", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.11.tgz", @@ -1843,6 +1925,13 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/statuses": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", + "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", @@ -2623,6 +2712,16 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cross-fetch": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", @@ -3552,7 +3651,6 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -3741,6 +3839,16 @@ "dev": true, "license": "MIT" }, + "node_modules/graphql": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", + "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3798,6 +3906,13 @@ "node": ">= 0.4" } }, + "node_modules/headers-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", @@ -4074,6 +4189,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true, + "license": "MIT" + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4807,6 +4929,90 @@ "dev": true, "license": "MIT" }, + "node_modules/msw": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.4.9.tgz", + "integrity": "sha512-1m8xccT6ipN4PTqLinPwmzhxQREuxaEJYdx4nIbggxP8aM7r1e71vE7RtOUSQoAm1LydjGfZKy7370XD/tsuYg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.0", + "@bundled-es-modules/statuses": "^1.0.1", + "@bundled-es-modules/tough-cookie": "^0.1.6", + "@inquirer/confirm": "^3.0.0", + "@mswjs/interceptors": "^0.35.8", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "chalk": "^4.1.2", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.2", + "path-to-regexp": "^6.3.0", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.9.0", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.8.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/msw/node_modules/@inquirer/confirm": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.2.0.tgz", + "integrity": "sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/msw/node_modules/@inquirer/type": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz", + "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/msw/node_modules/type-fest": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", + "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mustache": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", @@ -4837,7 +5043,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -4985,6 +5190,13 @@ "node": ">=0.10.0" } }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true, + "license": "MIT" + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5107,6 +5319,13 @@ "dev": true, "license": "ISC" }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -5222,9 +5441,9 @@ } }, "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -5240,16 +5459,21 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss/node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5646,11 +5870,10 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -5669,6 +5892,16 @@ "dev": true, "license": "MIT" }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/std-env": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", @@ -5676,6 +5909,13 @@ "dev": true, "license": "MIT" }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true, + "license": "MIT" + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -6233,15 +6473,14 @@ } }, "node_modules/vite": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz", - "integrity": "sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==", + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", "dev": true, - "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.39", - "rollup": "^4.13.0" + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -6260,6 +6499,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -6277,6 +6517,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, diff --git a/package.json b/package.json index 9262569c69a..f93392e1968 100644 --- a/package.json +++ b/package.json @@ -36,11 +36,12 @@ "inquirer": "^11.0.2", "jsdom": "^24.0.0", "lefthook": "^1.6.12", + "msw": "^2.4.9", "phaser3spectorjs": "^0.0.8", "typedoc": "^0.26.4", "typescript": "^5.5.3", "typescript-eslint": "^8.0.0-alpha.54", - "vite": "^5.3.5", + "vite": "^5.4.8", "vite-tsconfig-paths": "^4.3.2", "vitest": "^2.0.4", "vitest-canvas-mock": "^0.3.3" diff --git a/public/images/trainer/bug_type_superfan.json b/public/images/trainer/bug_type_superfan.json index 74dca3583d5..6a77fe69912 100644 --- a/public/images/trainer/bug_type_superfan.json +++ b/public/images/trainer/bug_type_superfan.json @@ -4,1458 +4,30 @@ "image": "bug_type_superfan.png", "format": "RGBA8888", "size": { - "w": 224, - "h": 224 + "w": 71, + "h": 71 }, "scale": 1, "frames": [ - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 5, - "y": 1, - "w": 52, - "h": 85 - }, - "frame": { - "x": 1, - "y": 1, - "w": 52, - "h": 85 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 5, - "y": 1, - "w": 52, - "h": 85 - }, - "frame": { - "x": 1, - "y": 1, - "w": 52, - "h": 85 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 9, - "y": 11, - "w": 60, - "h": 75 - }, - "frame": { - "x": 55, - "y": 1, - "w": 60, - "h": 75 - } - }, { "filename": "0001.png", "rotated": false, "trimmed": true, "sourceSize": { "w": 80, - "h": 86 + "h": 80 }, "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 + "x": 4, + "y": 9, + "w": 71, + "h": 71 }, "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 10, + "x": 0, "y": 0, - "w": 46, - "h": 86 - }, - "frame": { - "x": 1, - "y": 88, - "w": 46, - "h": 86 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 46, - "h": 86 - }, - "frame": { - "x": 1, - "y": 88, - "w": 46, - "h": 86 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 46, - "h": 86 - }, - "frame": { - "x": 1, - "y": 88, - "w": 46, - "h": 86 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 46, - "h": 86 - }, - "frame": { - "x": 1, - "y": 88, - "w": 46, - "h": 86 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 46, - "h": 86 - }, - "frame": { - "x": 1, - "y": 88, - "w": 46, - "h": 86 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 46, - "h": 86 - }, - "frame": { - "x": 1, - "y": 88, - "w": 46, - "h": 86 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 46, - "h": 86 - }, - "frame": { - "x": 1, - "y": 88, - "w": 46, - "h": 86 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 19, - "w": 66, - "h": 67 - }, - "frame": { - "x": 49, - "y": 88, - "w": 66, - "h": 67 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 19, - "w": 66, - "h": 67 - }, - "frame": { - "x": 49, - "y": 88, - "w": 66, - "h": 67 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 49, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 49, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 7, - "y": 19, - "w": 65, - "h": 67 - }, - "frame": { - "x": 117, - "y": 71, - "w": 65, - "h": 67 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 7, - "y": 19, - "w": 65, - "h": 67 - }, - "frame": { - "x": 117, - "y": 71, - "w": 65, - "h": 67 + "w": 71, + "h": 71 } } ] @@ -1464,6 +36,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:442c13442d70348845d7f5fcdfc121b3:3b8402aa64ee8990e64c7f03ffffbc55:568199339797fd79d11ae8d741953c1c$" + "smartupdate": "$TexturePacker:SmartUpdate:d051332055512f245ae68e912a28cd81:4926ba3fd9fe432bb535d13f4df1df4e:621aca0914900a3b9e235ebf0431ce2b$" } } diff --git a/public/images/trainer/bug_type_superfan.png b/public/images/trainer/bug_type_superfan.png index 59316fe6ed8..0d2fd7a68ae 100644 Binary files a/public/images/trainer/bug_type_superfan.png and b/public/images/trainer/bug_type_superfan.png differ diff --git a/public/locales/de/arena-tag.json b/public/locales/de/arena-tag.json index 3bed4fefbd0..93ceb06f308 100644 --- a/public/locales/de/arena-tag.json +++ b/public/locales/de/arena-tag.json @@ -28,6 +28,7 @@ "mudSportOnRemove": "Lehmsuhler hört auf zu wirken!", "waterSportOnAdd": "Die Stärke aller Feuer-Attacken wurde reduziert!", "waterSportOnRemove": "Nassmacher hört auf zu wirken!", + "plasmaFistsOnAdd": "Ein elektrisch geladener Niederschlag regnet auf das Kampffeld herab!", "spikesOnAdd": "Die {{opponentDesc}} sind von Stacheln umgeben!", "spikesActivateTrap": "Die {{pokemonNameWithAffix}} wurde durch Stachler verletzt!!", "toxicSpikesOnAdd": "Die {{opponentDesc}} sind überall von giftigen Stacheln umgeben", @@ -54,4 +55,4 @@ "safeguardOnRemove": "Der mystische Schleier, der das ganze Feld umgab, hat sich gelüftet!", "safeguardOnRemovePlayer": "Der mystische Schleier, der dein Team umgab, hat sich gelüftet!", "safeguardOnRemoveEnemy": "Der mystische Schleier, der das gegnerische Team umgab, hat sich gelüftet!" -} \ No newline at end of file +} diff --git a/public/locales/de/battler-tags.json b/public/locales/de/battler-tags.json index f4b9d71eb49..3a2f1b84e49 100644 --- a/public/locales/de/battler-tags.json +++ b/public/locales/de/battler-tags.json @@ -75,5 +75,5 @@ "substituteOnAdd": "Ein Delegator von {{pokemonNameWithAffix}} ist erschienen!", "substituteOnHit": "Der Delegator steckt den Schlag für {{pokemonNameWithAffix}} ein!", "substituteOnRemove": "Der Delegator von {{pokemonNameWithAffix}} hört auf zu wirken!", - "autotomizeOnAdd": "{{pokemonNameWIthAffix}} ist leichter geworden!" + "autotomizeOnAdd": "{{pokemonNameWithAffix}} ist leichter geworden!" } diff --git a/public/locales/de/mystery-encounters/the-expert-pokemon-breeder-dialogue.json b/public/locales/de/mystery-encounters/the-expert-pokemon-breeder-dialogue.json index 191fdaaf783..d2fcc91d153 100644 --- a/public/locales/de/mystery-encounters/the-expert-pokemon-breeder-dialogue.json +++ b/public/locales/de/mystery-encounters/the-expert-pokemon-breeder-dialogue.json @@ -25,7 +25,7 @@ "outro": "Schau wie glücklich dein {{chosenPokemon}} nun ist!$Hier, diese Pokémon-Eier kannst du auch haben.", "outro_failed": "Wie enttäuschend...$Es sieht so aus, als hättest du noch einen langen Weg vor dir, um das Vertrauen deines Pokémon zu gewinnen!", "gained_eggs": "@s{item_fanfare}Du erhählst {{numEggs}}!", - "eggs_tooltip": "\n(+) Erhalte @[TOOLTIP_TITLE]{{{eggs}}}", + "eggs_tooltip": "\n(+) Erhalte {{eggs}}", "numEggs_one": "{{count}} Ei der Stufe {{rarity}}", "numEggs_other": "{{count}} Eier der Stufe {{rarity}}" } diff --git a/public/locales/en/arena-tag.json b/public/locales/en/arena-tag.json index d8fed386b24..df79693c7bb 100644 --- a/public/locales/en/arena-tag.json +++ b/public/locales/en/arena-tag.json @@ -28,6 +28,7 @@ "mudSportOnRemove": "The effects of Mud Sport\nhave faded.", "waterSportOnAdd": "Fire's power was weakened!", "waterSportOnRemove": "The effects of Water Sport\nhave faded.", + "plasmaFistsOnAdd": "A deluge of ions showers the battlefield!", "spikesOnAdd": "{{moveName}} were scattered\nall around {{opponentDesc}}'s feet!", "spikesActivateTrap": "{{pokemonNameWithAffix}} is hurt\nby the spikes!", "toxicSpikesOnAdd": "{{moveName}} were scattered\nall around {{opponentDesc}}'s feet!", diff --git a/public/locales/en/battler-tags.json b/public/locales/en/battler-tags.json index 520ac2a6202..bb4b708b25f 100644 --- a/public/locales/en/battler-tags.json +++ b/public/locales/en/battler-tags.json @@ -78,5 +78,7 @@ "tormentOnAdd": "{{pokemonNameWithAffix}} was subjected to torment!", "tauntOnAdd": "{{pokemonNameWithAffix}} fell for the taunt!", "imprisonOnAdd": "{{pokemonNameWithAffix}} sealed the opponents move(s)!", - "autotomizeOnAdd": "{{pokemonNameWithAffix}} became nimble!" + "autotomizeOnAdd": "{{pokemonNameWithAffix}} became nimble!", + "syrupBombOnAdd": "{{pokemonNameWithAffix}} got covered in sticky, candy syrup!", + "syrupBombLapse": "The sticky syrup slowed down {{pokemonNameWithAffix}}!" } diff --git a/public/locales/en/move-trigger.json b/public/locales/en/move-trigger.json index 93d25e506ba..7ea5fe82fc2 100644 --- a/public/locales/en/move-trigger.json +++ b/public/locales/en/move-trigger.json @@ -71,5 +71,5 @@ "safeguard": "{{targetName}} is protected by Safeguard!", "substituteOnOverlap": "{{pokemonName}} already\nhas a substitute!", "substituteNotEnoughHp": "But it does not have enough HP\nleft to make a substitute!", - "afterYou": "{{pokemonName}} took the kind offer!" + "afterYou": "{{targetName}} took the kind offer!" } \ No newline at end of file diff --git a/public/locales/en/mystery-encounters/safari-zone-dialogue.json b/public/locales/en/mystery-encounters/safari-zone-dialogue.json index b96e3b5beb8..c1e4dd30852 100644 --- a/public/locales/en/mystery-encounters/safari-zone-dialogue.json +++ b/public/locales/en/mystery-encounters/safari-zone-dialogue.json @@ -1,7 +1,7 @@ { "intro": "It's a safari zone!", "title": "The Safari Zone", - "description": "There are all kinds of rare and special Pokémon that can be found here!\nIf you choose to enter, you'll have a time limit of @[TOOLTIP_TITLE]{{{numEncounters}} wild encounters} where you can try to catch these special Pokémon.\n\nBeware, though. These Pokémon may flee before you're able to catch them!", + "description": "There are all kinds of rare and special Pokémon that can be found here!\nIf you choose to enter, you'll have a time limit of@[TOOLTIP_TITLE]{ {{numEncounters}} wild encounters} where you can try to catch these special Pokémon.\n\nBeware, though. These Pokémon may flee before you're able to catch them!", "query": "Would you like to enter?", "option": { "1": { diff --git a/public/locales/es/arena-tag.json b/public/locales/es/arena-tag.json index 0f63b62e784..9aa37654c62 100644 --- a/public/locales/es/arena-tag.json +++ b/public/locales/es/arena-tag.json @@ -28,6 +28,7 @@ "mudSportOnRemove": "Chapoteo Lodo ha dejado de surtir efecto.", "waterSportOnAdd": "¡Se han debilitado los ataques\nde tipo Fuego!", "waterSportOnRemove": "Hidrochorro ha dejado de surtir efecto.", + "plasmaFistsOnAdd": "¡Una lluvia de electrones cae sobre\nel terreno de combate!", "spikesOnAdd": "¡El equipo de {{opponentDesc}} ha sido rodeado por {{moveName}}!", "spikesActivateTrap": "¡Las púas han herido a {{pokemonNameWithAffix}}!", "toxicSpikesOnAdd": "¡El equipo de {{opponentDesc}} ha sido rodeado por {{moveName}}!", @@ -54,4 +55,4 @@ "safeguardOnRemove": "¡Velo Sagrado dejó de hacer efecto!", "safeguardOnRemovePlayer": "El efecto de Velo Sagrado en tu equipo se ha disipado.", "safeguardOnRemoveEnemy": "El efecto de Velo Sagrado en el equipo enemigo se ha disipado." -} \ No newline at end of file +} diff --git a/public/locales/es/modifier-type.json b/public/locales/es/modifier-type.json index 365a8d9bfed..668f08e443d 100644 --- a/public/locales/es/modifier-type.json +++ b/public/locales/es/modifier-type.json @@ -358,6 +358,10 @@ "name": "Llamasfera", "description": "Extraña esfera que causa quemaduras a quien la usa en combate." }, + "EVOLUTION_TRACKER_GIMMIGHOUL": { + "name": "Tesoros", + "description": "¡A este Pokémon le encantan los tesoros! ¡Sigue coleccionandólos y tal vez pase algo!" + }, "BATON": { "name": "Testigo", "description": "Permite pasar los efectos al cambiar de Pokémon, también evita las trampas." @@ -495,6 +499,22 @@ "TART_APPLE": "Manzana ácida", "STRAWBERRY_SWEET": "Confite fresa", "UNREMARKABLE_TEACUP": "Cuenco mediocre", + "UPGRADE": "Mejora", + "DUBIOUS_DISC": "Disco Extraño", + "DRAGON_SCALE": "Escama Dragón", + "PRISM_SCALE": "Escama Bella", + "RAZOR_CLAW": "Garra Afilada", + "RAZOR_FANG": "Colmillo Agudo", + "REAPER_CLOTH": "Tela Terrible", + "ELECTIRIZER": "Electrizador", + "MAGMARIZER": "Magmatizador", + "PROTECTOR": "Protector", + "SACHET": "Saquito Fragante", + "WHIPPED_DREAM": "Dulce de Nata", + "LEADERS_CREST": "Distintivo de Líder", + "SUN_FLUTE": "Flauta Solar", + "MOON_FLUTE": "Flauta Lunar", + "CHIPPED_POT": "Tetera rota", "BLACK_AUGURITE": "Mineral negro", "GALARICA_CUFF": "Brazal galanuez", diff --git a/public/locales/es/move-trigger.json b/public/locales/es/move-trigger.json index b49a64ac42a..2e394dd72a7 100644 --- a/public/locales/es/move-trigger.json +++ b/public/locales/es/move-trigger.json @@ -67,5 +67,5 @@ "swapArenaTags": "¡{{pokemonName}} ha intercambiado los efectos del terreno de combate!", "exposedMove": "¡{{pokemonName}} ha identificado\n{{targetPokemonName}}!", "safeguard": "¡{{targetName}} está protegido por Velo Sagrado!", - "afterYou": "¡{{pokemonName}} ha decidido aprovechar la oportunidad!" + "afterYou": "¡{{targetName}} ha decidido aprovechar la oportunidad!" } diff --git a/public/locales/es/mystery-encounters/department-store-sale-dialogue.json b/public/locales/es/mystery-encounters/department-store-sale-dialogue.json index e17d57b8c3c..f3f64a9ed57 100644 --- a/public/locales/es/mystery-encounters/department-store-sale-dialogue.json +++ b/public/locales/es/mystery-encounters/department-store-sale-dialogue.json @@ -1,7 +1,7 @@ { "intro": "Es una señora con un montón de bolsas de compras.", "speaker": "Compradora", - "intro_dialogue": "¡Hola! ¿También estás aquí por las increíbles rebajas? Hay un cupón especial que puedes canjear por un artículo gratis durante la venta. ¡Tengo uno extra. ¡Aquí tienes!", + "intro_dialogue": "¡Hola! ¿También estás aquí por las increíbles rebajas?$Hay un cupón especial que puedes canjear por un artículo gratis durante la venta. ¡Tengo uno extra. ¡Aquí tienes!", "title": "¡Rebajas en el Centro Comercial!", "description": "¡Hay mercancía en todas direcciones! Parece que hay 4 mostradores donde puedes canjear el cupón por varios artículos. ¡Las posibilidades son infinitas!", "query": "¿A qué mostrador irás?", @@ -24,4 +24,4 @@ } }, "outro": "¡Qué chollo! Deberías comprar allí más a menudo." -} \ No newline at end of file +} diff --git a/public/locales/es/trainer-names.json b/public/locales/es/trainer-names.json index 14e31638d9c..2e76c24be9f 100644 --- a/public/locales/es/trainer-names.json +++ b/public/locales/es/trainer-names.json @@ -180,5 +180,5 @@ "vicky": "Vicky", "vito": "Vito", "bug_type_superfan": "Superfan de los Pokémon Bicho", - "expert_pokemon_breeder": "Criaokémon Experta" + "expert_pokemon_breeder": "Criapokémon Experta" } diff --git a/public/locales/fr/arena-tag.json b/public/locales/fr/arena-tag.json index 9cb2f342068..95e38cdbe9d 100644 --- a/public/locales/fr/arena-tag.json +++ b/public/locales/fr/arena-tag.json @@ -28,6 +28,7 @@ "mudSportOnRemove": "L’effet de Lance-Boue se dissipe !", "waterSportOnAdd": "La puissance des capacités\nde type Feu diminue !", "waterSportOnRemove": "L’effet de Tourniquet se dissipe !", + "plasmaFistsOnAdd": "Un déluge de plasma s’abat sur le terrain !", "spikesOnAdd": "Des {{moveName}} s’éparpillent autour de {{opponentDesc}} !", "spikesActivateTrap": "{{pokemonNameWithAffix}} est blessé\npar les picots !", "toxicSpikesOnAdd": "Des {{moveName}} s’éparpillent autour de {{opponentDesc}} !", diff --git a/public/locales/fr/modifier-type.json b/public/locales/fr/modifier-type.json index 74c7599fea7..465ffc82572 100644 --- a/public/locales/fr/modifier-type.json +++ b/public/locales/fr/modifier-type.json @@ -240,6 +240,8 @@ "TOXIC_ORB": { "name": "Orbe Toxique", "description": "Empoisonne gravement son porteur à la fin du tour s’il n’a pas déjà de problème de statut." }, "FLAME_ORB": { "name": "Orbe Flamme", "description": "Brule son porteur à la fin du tour s’il n’a pas déjà de problème de statut." }, + "EVOLUTION_TRACKER_GIMMIGHOUL": { "name": "Trésors", "description": "Ce Pokémon adore les trésors ! Ramassez-en le plus possible et voyez ce qu’il se passe !"}, + "BATON": { "name": "Témoin", "description": "Permet de transmettre les effets en cas de changement de Pokémon. Ignore les pièges." }, "SHINY_CHARM": { "name": "Charme Chroma", "description": "Augmente énormément les chances de rencontrer un Pokémon sauvage chromatique." }, @@ -330,6 +332,21 @@ "TART_APPLE": "Pomme Acidulée", "STRAWBERRY_SWEET": "Fraise en Sucre", "UNREMARKABLE_TEACUP": "Bol Médiocre", + "UPGRADE": "Améliorator", + "DUBIOUS_DISC": "CD Douteux", + "DRAGON_SCALE": "Écaille Draco", + "PRISM_SCALE": "Bel’Écaille", + "RAZOR_CLAW": "Griffe Rasoir", + "RAZOR_FANG": "Croc Rasoir", + "REAPER_CLOTH": "Tissu Fauche", + "ELECTIRIZER": "Électriseur", + "MAGMARIZER": "Magmariseur", + "PROTECTOR": "Protecteur", + "SACHET": "Sachet Senteur", + "WHIPPED_DREAM": "Chantibonbon", + "LEADERS_CREST": "Emblème du Général", + "SUN_FLUTE": "Flute du Soleil", + "MOON_FLUTE": "Flute de la Lune", "CHIPPED_POT": "Théière Ébréchée", "BLACK_AUGURITE": "Obsidienne", diff --git a/public/locales/fr/move-trigger.json b/public/locales/fr/move-trigger.json index 7564718e7ce..458b7ed8c4f 100644 --- a/public/locales/fr/move-trigger.json +++ b/public/locales/fr/move-trigger.json @@ -71,5 +71,5 @@ "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 !" + "afterYou": "{{targetName}} accepte\navec joie !" } diff --git a/public/locales/fr/mystery-encounters/bug-type-superfan-dialogue.json b/public/locales/fr/mystery-encounters/bug-type-superfan-dialogue.json index 63195e37062..7e81b99c739 100644 --- a/public/locales/fr/mystery-encounters/bug-type-superfan-dialogue.json +++ b/public/locales/fr/mystery-encounters/bug-type-superfan-dialogue.json @@ -28,7 +28,7 @@ "select_prompt": "Choisissez un objet à donner.", "invalid_selection": "Ce Pokémon ne porte pas ce genre d’objet.", "selected": "Vous remettez l’objet {{selectedItem}} au Dresseur.", - "selected_dialogue": "Sérieux ? {{selectedItem}}, comme ça en cadeau ?\nC’est très générieux !$En guise de ma reconnaissance, laisse-moi\nte faire un cadeau un peu spécial !$Il est dans ma famille depusi des générations, et ça me ferait plaisir de te l’offrir !" + "selected_dialogue": "Sérieux ? {{selectedItem}}, comme ça en cadeau ?\nC’est très générieux !$En guise de ma reconnaissance, laisse-moi\nte faire un cadeau un peu spécial !$Il est dans ma famille depuis des générations, et ça me ferait plaisir de te l’offrir !" } }, "battle_won": "Tes connaissances et tes capacités ont totalement exploité nos faiblesses !$En remerciement de cette leçon, permets-moi\nd’apprendre une capacité Insecte à un de tes Pokémon !", diff --git a/public/locales/fr/mystery-encounters/the-expert-pokemon-breeder-dialogue.json b/public/locales/fr/mystery-encounters/the-expert-pokemon-breeder-dialogue.json index 6112773aa36..8496b7e0472 100644 --- a/public/locales/fr/mystery-encounters/the-expert-pokemon-breeder-dialogue.json +++ b/public/locales/fr/mystery-encounters/the-expert-pokemon-breeder-dialogue.json @@ -25,7 +25,7 @@ "outro": "Ton {{chosenPokemon}} et toi avez\nl’air très heureux !$Tiens, prends ça aussi.", "outro_failed": "Voilà qui est bien décevant…$T’as encore visiblement bien du chemin à faire\npour acquérir la confiance de tes Pokémon !", "gained_eggs": "@s{item_fanfare}Vous recevez\n{{numEggs}} !", - "eggs_tooltip": "\n(+) Recevez @[TOOLTIP_TITLE]{{{eggs}}}", + "eggs_tooltip": "\n(+) Recevez {{eggs}}", "numEggs_one": "{{count}} Œuf {{rarity}}", "numEggs_other": "{{count}} Œufs {{rarity}}s" } diff --git a/public/locales/fr/mystery-encounters/training-session-dialogue.json b/public/locales/fr/mystery-encounters/training-session-dialogue.json index 9b06651eb59..9de246d3b6f 100644 --- a/public/locales/fr/mystery-encounters/training-session-dialogue.json +++ b/public/locales/fr/mystery-encounters/training-session-dialogue.json @@ -1,7 +1,7 @@ { "intro": "Vous tombez sur du matériel d’entrainement.", "title": "Session d’entrainement", - "description": "Ce matériel semble pouvoir être utilisé pour entrainer un membre de votre équipe ! Il existe plusieurs moyens avec lesquels vous pourriez entrainer un Pokémon, comme @[TOOLTIP_TITLE]{en le faisant combattre et vaincre le reste de votre équipe}.", + "description": "Ce matériel semble pouvoir être utilisé pour entrainer un membre de votre équipe ! Il existe plusieurs moyens avec lesquels vous pourriez entrainer un Pokémon, comme @[TOOLTIP_TITLE]{en le combattant et le vainquant avec le reste de votre équipe}.", "query": "Quel entrainement choisir ?", "invalid_selection": "Le Pokémon doit être en bonne santé.", "option": { diff --git a/public/locales/fr/mystery-encounters/uncommon-breed-dialogue.json b/public/locales/fr/mystery-encounters/uncommon-breed-dialogue.json index d5f5e7e336f..9d70d532f0a 100644 --- a/public/locales/fr/mystery-encounters/uncommon-breed-dialogue.json +++ b/public/locales/fr/mystery-encounters/uncommon-breed-dialogue.json @@ -14,13 +14,13 @@ "label": "Le nourrir", "disabled_tooltip": "Vous avez besoin de 4 Baies pour choisir cette option", "tooltip": "(-) Donner 4 Baies\n(+) Le {{enemyPokemon}} vous apprécie", - "selected": "Vous lancer quelques Baies\nau {{enemyPokemon}} !$Il les engloutit avec joie !$Le {{enemyPokemon}} veut se joindre\nà votre équipe !" + "selected": "Vous lancez quelques Baies\nau {{enemyPokemon}} !$Il les engloutit avec joie !$Le {{enemyPokemon}} veut se joindre\nà votre équipe !" }, "3": { "label": "Devenir amis", "disabled_tooltip": "Votre Pokémon doit connaitre certaines capacités pour choisir cette option", "tooltip": "(+) {{option3PrimaryName}} utilise {{option3PrimaryMove}}\n(+) Le {{enemyPokemon}} vous apprécie", - "selected": "Votre {{option3PrimaryName}} utilise {{option3PrimaryMove}} pour charmer le {{enemyPokemon}} !$The {{enemyPokemon}} veut se joindre\nà votre équipe !" + "selected": "Votre {{option3PrimaryName}} utilise {{option3PrimaryMove}} pour charmer le {{enemyPokemon}} !$Le {{enemyPokemon}} veut se joindre\nà votre équipe !" } } } diff --git a/public/locales/fr/settings.json b/public/locales/fr/settings.json index e310f5d5733..2dd150eed13 100644 --- a/public/locales/fr/settings.json +++ b/public/locales/fr/settings.json @@ -11,7 +11,7 @@ "expGainsSpeed": "Vit. barre d’Exp", "expPartyDisplay": "Afficher Exp équipe", "skipSeenDialogues": "Passer dialogues connus", - "eggSkip": "Animation d’éclosion", + "eggSkip": "Passer les éclosions", "never": "Jamais", "always": "Toujours", "ask": "Demander", diff --git a/public/locales/it/arena-tag.json b/public/locales/it/arena-tag.json index a1c5ee5b3c9..be2a06eb898 100644 --- a/public/locales/it/arena-tag.json +++ b/public/locales/it/arena-tag.json @@ -1,8 +1,9 @@ { + "plasmaFistsOnAdd": "Una pioggia di elettroni si rovescia sui Pokémon!", "safeguardOnAdd": "Un velo mistico ricopre il campo!", "safeguardOnAddPlayer": "Un velo mistico ricopre la tua squadra!", "safeguardOnAddEnemy": "Un velo mistico ricopre la squadra avversaria!", "safeguardOnRemove": "Il campo non è più protetto da Salvaguardia!", "safeguardOnRemovePlayer": "La tua squadra non è più protetta da Salvaguardia!", "safeguardOnRemoveEnemy": "La squadra avversaria non è più protetta da Salvaguardia!" -} \ No newline at end of file +} diff --git a/public/locales/it/modifier-type.json b/public/locales/it/modifier-type.json index 9edf6e5a53a..13b8a62e917 100644 --- a/public/locales/it/modifier-type.json +++ b/public/locales/it/modifier-type.json @@ -358,6 +358,10 @@ "name": "Fiammosfera", "description": "Sfera bizzarra che procura una scottatura a chi l’ha con sé in una lotta." }, + "EVOLUTION_TRACKER_GIMMIGHOUL": { + "name": "Tesori", + "description": "Questo Pokémon adora i tesori! Continua a collezionare i tesori e potrebbe accadere qualcosa!" + }, "BATON": { "name": "Staffetta", "description": "Permette di trasmettere gli effetti quando si cambia Pokémon, aggirando anche le trappole." @@ -495,6 +499,22 @@ "TART_APPLE": "Aspropomo", "STRAWBERRY_SWEET": "Bonbonfragola", "UNREMARKABLE_TEACUP": "Tazza dozzinale", + "UPGRADE": "Upgrade", + "DUBIOUS_DISC": "Dubbiodisco", + "DRAGON_SCALE": "Squama drago", + "PRISM_SCALE": "Squama bella", + "RAZOR_CLAW": "Affilartiglio", + "RAZOR_FANG": "Affilodente", + "REAPER_CLOTH": "Terrorpanno", + "ELECTIRIZER": "Elettritore", + "MAGMARIZER": "Magmatore", + "PROTECTOR": "Copertura", + "SACHET": "Bustina aromi", + "WHIPPED_DREAM": "Dolcespuma", + "LEADERS_CREST": "Simbolo del capo", + "SUN_FLUTE": "Flauto solare", + "MOON_FLUTE": "Flauto lunare", + "CHIPPED_POT": "Teiera crepata", "BLACK_AUGURITE": "Augite nera", "GALARICA_CUFF": "Fascia Galarnoce", diff --git a/public/locales/it/move-trigger.json b/public/locales/it/move-trigger.json index fba671a6813..04870015f2b 100644 --- a/public/locales/it/move-trigger.json +++ b/public/locales/it/move-trigger.json @@ -68,5 +68,5 @@ "exposedMove": "{{pokemonName}} ha identificato\n{{targetPokemonName}}!", "chillyReception": "{{pokemonName}} sta per fare una battuta!", "safeguard": "Salvaguardia protegge {{targetName}}!", - "afterYou": "{{pokemonName}} approfitta della cortesia!" + "afterYou": "{{targetName}} approfitta della cortesia!" } diff --git a/public/locales/it/mystery-encounters/the-expert-pokemon-breeder-dialogue.json b/public/locales/it/mystery-encounters/the-expert-pokemon-breeder-dialogue.json index a59cf55694e..12eaebfb33c 100644 --- a/public/locales/it/mystery-encounters/the-expert-pokemon-breeder-dialogue.json +++ b/public/locales/it/mystery-encounters/the-expert-pokemon-breeder-dialogue.json @@ -25,7 +25,7 @@ "outro": "ねえ、 {{chosenPokemon}} すごく 嬉しくなったわ!$そして、 これらも どうぞ!", "outro_failed": "なんか ガッカリ ね……$手持ち ポケモンの 信用を 得るまで\nまだまだ みたいんだわ!", "gained_eggs": "@s{item_fanfare}{{numEggs}}を もらいました!", - "eggs_tooltip": "\n(+) @[TOOLTIP_TITLE]{{{eggs}}}を得る", + "eggs_tooltip": "\n(+) {{eggs}}を得る", "numEggs_one": "{{count}} {{rarity}} タマゴ", "numEggs_other": "{{count}} {{rarity}} タマゴ" } diff --git a/public/locales/ja/ability-trigger.json b/public/locales/ja/ability-trigger.json index 26d27701aef..6897b466195 100644 --- a/public/locales/ja/ability-trigger.json +++ b/public/locales/ja/ability-trigger.json @@ -2,11 +2,11 @@ "blockRecoilDamage": "{{pokemonName}}は {{abilityName}}で 反動ダメージを 受けない!", "badDreams": "{{pokemonName}}は ナイトメアに うなされている!", "costar": "{{pokemonName}}は {{allyName}}の\n能力変化を コピーした!", - "iceFaceAvoidedDamage": "{{pokemonNameWithAffix}}は\n{{abilityName}}で ダメージを 受けない!", + "iceFaceAvoidedDamage": "{{pokemonNameWithAffix}} は\n{{abilityName}}で ダメージを 受けない!", "perishBody": "{{pokemonName}}の {{abilityName}}で\nおたがいは 3ターン後に ほろびいてしまう!", "poisonHeal": "{{pokemonName}}は {{abilityName}}で 回復した!", "trace": "{{pokemonName}}は 相手の {{targetName}}の\n{{abilityName}}を トレースした!", - "windPowerCharged": "{{pokemonNameWithAffix}}は\n{{moveName}}を 受けて じゅうでんした!", + "windPowerCharged": "{{pokemonName}} は\n{{moveName}}を 受けて じゅうでんした!", "quickDraw": "{{pokemonName}}は クイックドロウで\n行動が はやくなった!", "disguiseAvoidedDamage": "{{pokemonNameWithAffix}}の\nばけのかわが はがれた!", "blockItemTheft": "{{pokemonNameWithAffix}}の {{abilityName}}で\n道具を うばわれない!", @@ -48,8 +48,8 @@ "weatherEffectDisappeared": "天候の影響が なくなった!", "postSummonMoldBreaker": "{{pokemonNameWithAffix}}は\nかたやぶりだ!", "postSummonAnticipation": "{{pokemonNameWithAffix}}は\nみぶるいした!", - "postSummonTurboblaze": "{{pokemonNameWithAffix}}は\n燃え盛(もえさか)る オーラを 放っている!", - "postSummonTeravolt": "{{pokemonNameWithAffix}}は\n弾(はじ)ける オーラを 放っている!", + "postSummonTurboblaze": "{{pokemonNameWithAffix}}は\n燃え盛る オーラを 放っている!", + "postSummonTeravolt": "{{pokemonNameWithAffix}}は\n弾ける オーラを 放っている!", "postSummonDarkAura": "{{pokemonNameWithAffix}}は\nダークオーラを 放っている!", "postSummonFairyAura": "{{pokemonNameWithAffix}}は\nフェアリーオーラを 放っている!", "postSummonAuraBreak": "{{pokemonNameWithAffix}}は\nすべての オーラを 制圧する!", diff --git a/public/locales/ja/arena-tag.json b/public/locales/ja/arena-tag.json index a81942338fd..0da759884a5 100644 --- a/public/locales/ja/arena-tag.json +++ b/public/locales/ja/arena-tag.json @@ -28,6 +28,7 @@ "mudSportOnRemove": "どろあそびの 効果が なくなった!", "waterSportOnAdd": "炎の威力が 弱まった!", "waterSportOnRemove": "みずあそびの 効果が なくなった!", + "plasmaFistsOnAdd": "電子のシャワーが 降りそそいだ!", "spikesOnAdd": "{{opponentDesc}}の 足下に\n{{moveName}}が 散らばった!", "spikesActivateTrap": "{{pokemonNameWithAffix}}は\nまきびしの ダメージを 受けた!", "toxicSpikesOnAdd": "{{opponentDesc}}の 足下に\n{{moveName}}が 散らばった!", diff --git a/public/locales/ja/modifier-type.json b/public/locales/ja/modifier-type.json index b217ab054b7..5562a25f96f 100644 --- a/public/locales/ja/modifier-type.json +++ b/public/locales/ja/modifier-type.json @@ -240,6 +240,11 @@ "TOXIC_ORB": { "name": "どくどくだま", "description": "触ると 毒をだす 不思議な玉。\n持たせると 戦闘中に 猛毒の状態に なる" }, "FLAME_ORB": { "name": "かえんだま", "description": "触ると 熱をだす 不思議な玉。\n持たせると 戦闘中に やけどの状態に なる。" }, + "EVOLUTION_TRACKER_GIMMIGHOUL": { + "name": "宝物", + "description": "このポケモンは 宝物が 大好き! 宝物を 集め続けると 良いことが 起こる かもしれません!" + }, + "BATON": { "name": "バトン", "description": "持たせると 入れ替えるとき 控えのポケモンが\n能力変化を 受けつげる (逃げられなくする 技や 特性も 回避する)" }, "SHINY_CHARM": { "name": "ひかるおまもり", "description": "色違いの ポケモンと 大きく 出会いやすくなる" }, @@ -330,6 +335,21 @@ "TART_APPLE": "すっぱいりんご", "STRAWBERRY_SWEET": "いちごアメざいく", "UNREMARKABLE_TEACUP": "ボンサクのちゃわん", + "UPGRADE": "アップグレード", + "DUBIOUS_DISC": "あやしいパッチ", + "DRAGON_SCALE": "りゅうのウロコ", + "PRISM_SCALE": "きれいなウロコ", + "RAZOR_CLAW": "するどいツメ", + "RAZOR_FANG": "するどいキバ", + "REAPER_CLOTH": "れいかいのぬの", + "ELECTIRIZER": "エレキブースター", + "MAGMARIZER": "マグマブースター", + "PROTECTOR": "プロテクター", + "SACHET": "においぶくろ", + "WHIPPED_DREAM": "ホイップポップ", + "LEADERS_CREST": "かしらのしるし", + "SUN_FLUTE": "たいようのふえ", + "MOON_FLUTE": "つきのふえ", "CHIPPED_POT": "かけたポット", "BLACK_AUGURITE": "くろのきせき", diff --git a/public/locales/ja/move-trigger.json b/public/locales/ja/move-trigger.json index afede7edfb3..4c3d19240f6 100644 --- a/public/locales/ja/move-trigger.json +++ b/public/locales/ja/move-trigger.json @@ -68,5 +68,5 @@ "chillyReception": "{{pokemonName}}は\n寒い ギャグを かました!", "swapArenaTags": "{{pokemonName}}は\nお互いの 場の 効果を 入れ替えた!", "exposedMove": "{{pokemonName}}は {{targetPokemonName}}の\n正体を 見破った!", - "afterYou": "{{pokemonName}}は\nお言葉に 甘えることにした!" + "afterYou": "{{targetName}}は\nお言葉に 甘えることにした!" } diff --git a/public/locales/ko/arena-tag.json b/public/locales/ko/arena-tag.json index ce9922ab3bf..c1a7b1ca7ca 100644 --- a/public/locales/ko/arena-tag.json +++ b/public/locales/ko/arena-tag.json @@ -28,6 +28,7 @@ "mudSportOnRemove": "흙놀이의 효과가\n없어졌다!", "waterSportOnAdd": "불꽃의 위력이 약해졌다!", "waterSportOnRemove": "물놀이의 효과가\n없어졌다!", + "plasmaFistsOnAdd": "전기 입자가 쏟아졌다!", "spikesOnAdd": "{{opponentDesc}}의 발밑에\n압정이 뿌려졌다!", "spikesActivateTrap": "{{pokemonNameWithAffix}}[[는]]\n압정뿌리기의 데미지를 입었다!", "toxicSpikesOnAdd": "{{opponentDesc}}의 발밑에\n독압정이 뿌려졌다!", @@ -54,4 +55,4 @@ "safeguardOnRemove": "필드를 감싸던 신비의 베일이 없어졌다!", "safeguardOnRemovePlayer": "우리 편을 감싸던 신비의 베일이 없어졌다!", "safeguardOnRemoveEnemy": "상대 편을 감싸던 신비의 베일이 없어졌다!" -} \ No newline at end of file +} diff --git a/public/locales/ko/modifier-type.json b/public/locales/ko/modifier-type.json index f9d8fd1a861..d770b5fb068 100644 --- a/public/locales/ko/modifier-type.json +++ b/public/locales/ko/modifier-type.json @@ -358,9 +358,13 @@ "name": "화염구슬", "description": "이 도구를 지닌 포켓몬은 턴이 끝나는 시점에 상태이상에 걸리지 않았다면 화상 상태가 된다." }, + "EVOLUTION_TRACKER_GIMMIGHOUL": { + "name": "보물", + "description": "이 포켓몬은 보물을 좋아한다. 보물을 모으다 보면 어떤 일이 일어날 지도 모른다!" + }, "BATON": { "name": "배턴", - "description": "포켓몬을 교체할 때 효과를 넘겨줄 수 있으며, 함정의 영향을 받지 않게 함" + "description": "포켓몬을 교체할 때 효과를 넘겨줄 수 있으며, 함정의 영향을 받지 않게 한다." }, "SHINY_CHARM": { "name": "빛나는부적", @@ -495,6 +499,22 @@ "TART_APPLE": "새콤한사과", "STRAWBERRY_SWEET": "딸기사탕공예", "UNREMARKABLE_TEACUP": "범작찻잔", + "UPGRADE": "업그레이드", + "DUBIOUS_DISC": "괴상한패치", + "DRAGON_SCALE": "용의비늘", + "PRISM_SCALE": "고운비늘", + "RAZOR_CLAW": "예리한손톱", + "RAZOR_FANG": "예리한이빨", + "REAPER_CLOTH": "영계의천", + "ELECTIRIZER": "에레키부스터", + "MAGMARIZER": "마그마부스터", + "PROTECTOR": "프로텍터", + "SACHET": "향기주머니", + "WHIPPED_DREAM": "휘핑크림", + "LEADERS_CREST": "대장의징표", + "SUN_FLUTE": "태양의피리", + "MOON_FLUTE": "달의피리", + "CHIPPED_POT": "이빠진포트", "BLACK_AUGURITE": "검은휘석", "GALARICA_CUFF": "가라두구팔찌", diff --git a/public/locales/ko/move-trigger.json b/public/locales/ko/move-trigger.json index 12a126baf9d..4b994f315bf 100644 --- a/public/locales/ko/move-trigger.json +++ b/public/locales/ko/move-trigger.json @@ -69,5 +69,5 @@ "chillyReception": "{{pokemonName}}[[는]] 썰렁한 개그를 선보였다!", "exposedMove": "{{pokemonName}}[[는]]\n{{targetPokemonName}}의 정체를 꿰뚫어 보았다!", "safeguard": "{{targetName}}[[는]] 신비의 베일이 지켜 주고 있다!", - "afterYou": "{{pokemonName}}[[는]]\n배려를 받아들이기로 했다!" + "afterYou": "{{targetName}}[[는]]\n배려를 받아들이기로 했다!" } diff --git a/public/locales/ko/mystery-encounters/the-expert-pokemon-breeder-dialogue.json b/public/locales/ko/mystery-encounters/the-expert-pokemon-breeder-dialogue.json index 92aca142ee5..30442f42626 100644 --- a/public/locales/ko/mystery-encounters/the-expert-pokemon-breeder-dialogue.json +++ b/public/locales/ko/mystery-encounters/the-expert-pokemon-breeder-dialogue.json @@ -26,7 +26,7 @@ "outro": "{{chosenPokemon}}[[가]] 정말 행복해 보이네요!$여기, 이것도 드릴게요.", "outro_failed": "실망이네요….$포켓몬의 신뢰를 얻으려면 아직 멀었어요!", "gained_eggs": "@s{item_fanfare}{{numEggs}}[[를]] 받았습니다!", - "eggs_tooltip": "\n(+) @[TOOLTIP_TITLE]{{{eggs}}} 획득", + "eggs_tooltip": "\n(+) {{eggs}} 획득", "numEggs_one": "{{rarity}}알 {{count}}개", "numEggs_other": "{{rarity}}알 {{count}}개" } diff --git a/public/locales/pt_BR/arena-tag.json b/public/locales/pt_BR/arena-tag.json index 3a1476dcef6..5fb8b49565f 100644 --- a/public/locales/pt_BR/arena-tag.json +++ b/public/locales/pt_BR/arena-tag.json @@ -28,6 +28,7 @@ "mudSportOnRemove": "Os efeitos de Mud Sport\nsumiram.", "waterSportOnAdd": "O poder de movimentos de fogo foi enfraquecido!", "waterSportOnRemove": "Os efeitos de Water Sport\nsumiram.", + "plasmaFistsOnAdd": "Um dilúvio de íons chove sobre o campo de batalha!", "spikesOnAdd": "{{moveName}} foram espalhados\nno chão ao redor de {{opponentDesc}}!", "spikesActivateTrap": "{{pokemonNameWithAffix}} foi ferido\npelos espinhos!", "toxicSpikesOnAdd": "{{moveName}} foram espalhados\nno chão ao redor de {{opponentDesc}}!", diff --git a/public/locales/pt_BR/menu-ui-handler.json b/public/locales/pt_BR/menu-ui-handler.json index df654976d68..5141a4ea612 100644 --- a/public/locales/pt_BR/menu-ui-handler.json +++ b/public/locales/pt_BR/menu-ui-handler.json @@ -25,5 +25,6 @@ "unlinkGoogle": "Desconectar Google", "cancel": "Cancelar", "losingProgressionWarning": "Você vai perder todo o progresso desde o início da batalha. Confirmar?", - "noEggs": "Você não está chocando nenhum ovo\nno momento!" + "noEggs": "Você não está chocando nenhum ovo\nno momento!", + "donate": "Contribuir" } diff --git a/public/locales/pt_BR/modifier-type.json b/public/locales/pt_BR/modifier-type.json index ae7a2cba93e..a4b27b19fb7 100644 --- a/public/locales/pt_BR/modifier-type.json +++ b/public/locales/pt_BR/modifier-type.json @@ -358,6 +358,10 @@ "name": "Esfera da Chama", "description": "Uma esfera estranha que aquece quando tocada e queima quem a segurar." }, + "EVOLUTION_TRACKER_GIMMIGHOUL": { + "name": "Treasures", + "description": "This Pokémon loves treasure! Keep collecting treasure and something might happen!" + }, "BATON": { "name": "Bastão", "description": "Permite passar mudanças de atributo ao trocar Pokémon, ignorando armadilhas." @@ -507,6 +511,22 @@ "TART_APPLE": "Maçã Azeda", "STRAWBERRY_SWEET": "Doce de Morango", "UNREMARKABLE_TEACUP": "Xícara Comum", + "UPGRADE": "Melhora", + "DUBIOUS_DISC": "Dubious Disc", + "DRAGON_SCALE": "Escama de Dragão", + "PRISM_SCALE": "Prism Scale", + "RAZOR_CLAW": "Garra de Navalha", + "RAZOR_FANG": "Presa Afiada", + "REAPER_CLOTH": "Capa do Ceifador", + "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": "Pote Lascado", "BLACK_AUGURITE": "Mineral Negro", "GALARICA_CUFF": "Bracelete de Galar", diff --git a/public/locales/pt_BR/move-trigger.json b/public/locales/pt_BR/move-trigger.json index 307364e1b55..9a4d0b9e8b1 100644 --- a/public/locales/pt_BR/move-trigger.json +++ b/public/locales/pt_BR/move-trigger.json @@ -64,5 +64,5 @@ "chillyReception": "{{pokemonName}} está prestes a contar uma piada gelada!", "exposedMove": "{{pokemonName}} identificou\n{{targetPokemonName}}!", "safeguard": "{{targetName}} está protegido por Safeguard!", - "afterYou": "{{pokemonName}} aceitou a gentil oferta!" + "afterYou": "{{targetName}} aceitou a gentil oferta!" } diff --git a/public/locales/pt_BR/mystery-encounters/the-expert-pokemon-breeder-dialogue.json b/public/locales/pt_BR/mystery-encounters/the-expert-pokemon-breeder-dialogue.json index 1bd188a0251..29e8d78ce37 100644 --- a/public/locales/pt_BR/mystery-encounters/the-expert-pokemon-breeder-dialogue.json +++ b/public/locales/pt_BR/mystery-encounters/the-expert-pokemon-breeder-dialogue.json @@ -25,7 +25,7 @@ "outro": "Veja como seu {{chosenPokemon}} está feliz agora!$Aqui, você também pode ficar com isso.", "outro_failed": "Que decepção...$Parece que você ainda tem um longo caminho\na percorrer para ganhar a confiança dos seus Pokémon!", "gained_eggs": "@s{item_fanfare}Você recebeu {{numEggs}}!", - "eggs_tooltip": "\n(+) Ganhe @[TOOLTIP_TITLE]{{{eggs}}}", + "eggs_tooltip": "\n(+) Ganhe {{eggs}}", "numEggs_one": "{{count}} Ovo {{rarity}}", "numEggs_other": "{{count}} Ovos {{rarity}}" } diff --git a/public/locales/pt_BR/party-ui-handler.json b/public/locales/pt_BR/party-ui-handler.json index ef09e09fb2b..a8c492670dc 100644 --- a/public/locales/pt_BR/party-ui-handler.json +++ b/public/locales/pt_BR/party-ui-handler.json @@ -24,7 +24,7 @@ "tooManyItems": "{{pokemonName}} já tem\nmuitos desse item!", "anyEffect": "Isso não terá nenhum efeito.", "unpausedEvolutions": "Evoluções foram despausadas para {{pokemonName}}.", - "unspliceConfirmation": "Você realmente deseja desfazer a fusão de {{fusionName}}\ncom {{pokemonName}}? {{fusionName}} será perdido.", + "unspliceConfirmation": "Você realmente deseja desfazer a fusão de\n{{fusionName}} com {{pokemonName}}? {{fusionName}} será perdido.", "wasReverted": "{{fusionName}} foi revertido para {{pokemonName}}.", "releaseConfirmation": "Você realmente deseja soltar {{pokemonName}}?", "releaseInBattle": "Você não pode soltar um Pokémon que está em batalha!", diff --git a/public/locales/pt_BR/settings.json b/public/locales/pt_BR/settings.json index 6c4eae23a82..1d743f7b05a 100644 --- a/public/locales/pt_BR/settings.json +++ b/public/locales/pt_BR/settings.json @@ -11,6 +11,10 @@ "expGainsSpeed": "Velocidade do Ganho de EXP", "expPartyDisplay": "Exibição de EXP da Equipe", "skipSeenDialogues": "Pular Diálogos Vistos", + "eggSkip": "Pular Eclosões de Ovos", + "never": "Nunca", + "always": "Sempre", + "ask": "Perguntar", "battleStyle": "Estilo de Batalha", "enableRetries": "Habilitar Novas Tentativas", "hideIvs": "Esconder scanner de IV", diff --git a/public/locales/zh_CN/arena-tag.json b/public/locales/zh_CN/arena-tag.json index 74ad38ba9bf..d7ac1b9b04b 100644 --- a/public/locales/zh_CN/arena-tag.json +++ b/public/locales/zh_CN/arena-tag.json @@ -28,6 +28,7 @@ "mudSportOnRemove": "玩泥巴的效果消失了!", "waterSportOnAdd": "火焰的威力减弱了!", "waterSportOnRemove": "玩水的效果消失了!", + "plasmaFistsOnAdd": "等离子雨倾盆而下!", "spikesOnAdd": "{{opponentDesc}}脚下\n散落着{{moveName}}!", "spikesActivateTrap": "{{pokemonNameWithAffix}}\n受到了撒菱的伤害!", "toxicSpikesOnAdd": "{{opponentDesc}}脚下\n散落着{{moveName}}!", @@ -54,4 +55,4 @@ "safeguardOnRemove": "包围整个场地的\n神秘之幕消失了!", "safeguardOnRemovePlayer": "包围我方的\n神秘之幕消失了!", "safeguardOnRemoveEnemy": "包围对手的\n神秘之幕消失了!" -} \ No newline at end of file +} diff --git a/public/locales/zh_CN/battle.json b/public/locales/zh_CN/battle.json index ef716b0893a..4b31de41f9a 100644 --- a/public/locales/zh_CN/battle.json +++ b/public/locales/zh_CN/battle.json @@ -88,6 +88,14 @@ "statHarshlyFell_other": "{{pokemonNameWithAffix}}的{{stats}}大幅降低了!", "statSeverelyFell_other": "{{pokemonNameWithAffix}}的{{stats}}极大幅降低了!", "statWontGoAnyLower_other": "{{pokemonNameWithAffix}}的{{stats}}已经无法再降低了!", + "statRose_one": "{{pokemonNameWithAffix}}的{{stats}}提高了!", + "statSharplyRose_one": "{{pokemonNameWithAffix}}的{{stats}}大幅提高了!", + "statRoseDrastically_one": "{{pokemonNameWithAffix}}的{{stats}}极大幅提高了!", + "statWontGoAnyHigher_one": "{{pokemonNameWithAffix}}的{{stats}}已经无法再提高了!", + "statFell_one": "{{pokemonNameWithAffix}}的{{stats}}降低了!", + "statHarshlyFell_one": "{{pokemonNameWithAffix}}的{{stats}}大幅降低了!", + "statSeverelyFell_one": "{{pokemonNameWithAffix}}的{{stats}}极大幅降低了!", + "statWontGoAnyLower_one": "{{pokemonNameWithAffix}}的{{stats}}已经无法再降低了!", "transformedIntoType": "{{pokemonName}}变成了\n{{type}}属性!", "ppReduced": "降低了{{targetName}}的\n{{moveName}}的PP{{reduction}}点!", "retryBattle": "你要从对战开始时重试么?", diff --git a/public/locales/zh_CN/dialogue.json b/public/locales/zh_CN/dialogue.json index 78f0845b436..f2382299179 100644 --- a/public/locales/zh_CN/dialogue.json +++ b/public/locales/zh_CN/dialogue.json @@ -299,6 +299,28 @@ "1": "真奇怪…怎么会这样…我不应该被打败的。" } }, + "snow_worker": { + "encounter": { + "1": "天冷了,便要添衣。\n只有人类会有这么聪明的想法!" + }, + "victory": { + "1": "即使是徒劳的挣扎,\n到头来也还是展示了人类的智慧啊!" + }, + "defeat": { + "1": "我来告诉你一个小技巧。\n如果你对着冻住的宝可梦用火系技能,冰就化啦!" + } + }, + "snow_worker_double": { + "encounter": { + "1": "……\n……准……准备好战……战斗了吗?" + }, + "victory": { + "1": "……\n……我冻得直,直打哆嗦!" + }, + "defeat": { + "1": "……\n……知……知道吗?\n...这……这地方蛮冷。" + } + }, "hex_maniac": { "encounter": { "1": "我通常只听古典音乐,但如果我输了,$我想我应该试试新时代的音乐!", diff --git a/public/locales/zh_CN/egg.json b/public/locales/zh_CN/egg.json index f2b95bd40cb..3691749a971 100644 --- a/public/locales/zh_CN/egg.json +++ b/public/locales/zh_CN/egg.json @@ -11,7 +11,7 @@ "gachaTypeLegendary": "传说概率上升", "gachaTypeMove": "稀有概率上升", "gachaTypeShiny": "闪光概率上升", - "eventType": "Mystery Event", + "eventType": "神秘事件", "selectMachine": "选择一个机器。", "notEnoughVouchers": "你没有足够的兑换券!", "tooManyEggs": "你的蛋太多啦!", diff --git a/public/locales/zh_CN/modifier-type.json b/public/locales/zh_CN/modifier-type.json index 61c36078369..2dc44f0fb34 100644 --- a/public/locales/zh_CN/modifier-type.json +++ b/public/locales/zh_CN/modifier-type.json @@ -2,7 +2,7 @@ "ModifierType": { "AddPokeballModifierType": { "name": "{{modifierCount}}x {{pokeballName}}", - "description": "获得 {{pokeballName}} x{{modifierCount}} (已有:{{pokeballAmount}}) \n捕捉倍率:{{catchRate}}。" + "description": "获得 {{pokeballName}}x {{modifierCount}} (已有:{{pokeballAmount}}) \n捕捉倍率:{{catchRate}}。" }, "AddVoucherModifierType": { "name": "{{modifierCount}}x {{voucherTypeName}}", @@ -50,7 +50,7 @@ "description": "遭遇双打概率提升四倍,持续{{battleCount}}场战斗。" }, "TempStatStageBoosterModifierType": { - "description": "提升全队的{{stat}}{{amount}}级,持续5场战斗。", + "description": "提升全队的{{stat}}{{amount}},持续5场战斗。", "extra": { "stage": "1阶", "percentage": "30%" @@ -63,24 +63,24 @@ "description": "使一只宝可梦的等级提升{{levels}}级。" }, "AllPokemonLevelIncrementModifierType": { - "description": "使一只寶可夢的等級提升{{levels}}級。" + "description": "使所有宝可梦的等级提升{{levels}}级。" }, "BaseStatBoosterModifierType": { "description": "增加10%持有者的{{stat}},\n个体值越高堆叠上限越高。" }, "PokemonBaseStatTotalModifierType": { - "name": "Shuckle Juice", - "description": "{{increaseDecrease}} all of the holder's base stats by {{statValue}}. You were {{blessCurse}} by the Shuckle.", + "name": "壶壶果汁", + "description": "{{increaseDecrease}}持有者的所有基础属性{{statValue}}级。你被壶壶{{blessCurse}}了。", "extra": { - "increase": "Increases", - "decrease": "Decreases", - "blessed": "blessed", - "cursed": "cursed" + "increase": "提升", + "decrease": "降低", + "blessed": "强化", + "cursed": "诅咒" } }, "PokemonBaseStatFlatModifierType": { - "name": "Old Gateau", - "description": "Increases the holder's {{stats}} base stats by {{statValue}}. Found after a strange dream." + "name": "森之羊羹", + "description": "增加持有者{{stats}}的基础属性{{statValue}},来自于一场怪梦。" }, "AllPokemonFullHpRestoreModifierType": { "description": "所有宝可梦完全回复HP。" @@ -109,7 +109,7 @@ "description": "招式命中率增加{{accuracyAmount}}(最大100)。" }, "PokemonMultiHitModifierType": { - "description": "攻击以60/75/82.5%的伤害造成2/3/4次伤害。" + "description": "伤害降低60/75/82.5%的同时造成2/3/4次伤害。" }, "TmModifierType": { "name": "招式学习器\n{{moveId}} - {{moveName}}", @@ -126,7 +126,7 @@ "description": "使某些宝可梦更改形态。" }, "FusePokemonModifierType": { - "description": "融合两只宝可梦 (改变特性, 平分基础点数\n和属性, 共享招式池)。" + "description": "融合两只宝可梦(改变特性, 平分基础点数\n和属性, 共享招式池)。" }, "TerastallizeModifierType": { "name": "{{teraType}}太晶碎块", @@ -358,6 +358,10 @@ "name": "火焰宝珠", "description": "触碰后会放出热量的神奇宝珠。\n携带后,在战斗时会变成灼伤状态。" }, + "EVOLUTION_TRACKER_GIMMIGHOUL": { + "name": "宝藏金币", + "description": "这个宝可梦最爱金币!多收集点金币的话会发生什么呢?" + }, "BATON": { "name": "接力棒", "description": "允许在切换宝可梦时保留能力变化, 对陷阱\n同样生效。" @@ -417,11 +421,11 @@ "description": "增加1%野生融合宝可梦出现概率。" }, - "MYSTERY_ENCOUNTER_SHUCKLE_JUICE": { "name": "Shuckle Juice" }, - "MYSTERY_ENCOUNTER_BLACK_SLUDGE": { "name": "Black Sludge", "description": "The stench is so powerful that shops will only sell you items at a steep cost increase." }, - "MYSTERY_ENCOUNTER_MACHO_BRACE": { "name": "Macho Brace", "description": "Defeating a Pokémon grants the holder a Macho Brace stack. Each stack slightly boosts stats, with an extra bonus at max stacks." }, - "MYSTERY_ENCOUNTER_OLD_GATEAU": { "name": "Old Gateau", "description": "Increases the holder's {{stats}} stats by {{statValue}}." }, - "MYSTERY_ENCOUNTER_GOLDEN_BUG_NET": { "name": "Golden Bug Net", "description": "Imbues the owner with luck to find Bug Type Pokémon more often. Has a strange heft to it." } + "MYSTERY_ENCOUNTER_SHUCKLE_JUICE": { "name": "壶壶果汁" }, + "MYSTERY_ENCOUNTER_BLACK_SLUDGE": { "name": "黑色污泥", "description": "由于恶臭扑鼻,商店会以非常高昂的价格向您出售商品。" }, + "MYSTERY_ENCOUNTER_MACHO_BRACE": { "name": "强制锻炼器", "description": "击败对手后获得一层锻炼等级。每层会略微提升属性,\n达到最大层数时还会获得额外奖励。" }, + "MYSTERY_ENCOUNTER_OLD_GATEAU": { "name": "森之羊羔", "description": "提升持有者的{{stats}}的{{statValue}}。" }, + "MYSTERY_ENCOUNTER_GOLDEN_BUG_NET": { "name": "金捕虫网", "description": "赋予主人好运,使其更容易\n找到虫属性宝可梦,手感很奇妙。" } }, "SpeciesBoosterItem": { "LIGHT_BALL": { @@ -495,6 +499,22 @@ "TART_APPLE": "酸酸苹果", "STRAWBERRY_SWEET": "草莓糖饰", "UNREMARKABLE_TEACUP": "凡作茶碗", + "UPGRADE": "升级数据", + "DUBIOUS_DISC": "可疑补丁", + "DRAGON_SCALE": "龙之鳞片", + "PRISM_SCALE": "美丽鳞片", + "RAZOR_CLAW": "锐利之爪", + "RAZOR_FANG": "锐利之牙", + "REAPER_CLOTH": "灵界之布", + "ELECTIRIZER": "电力增幅器", + "MAGMARIZER": "熔岩增幅器", + "PROTECTOR": "护具", + "SACHET": "香袋", + "WHIPPED_DREAM": "泡沫奶油", + "LEADERS_CREST": "头领凭证", + "SUN_FLUTE": "太阳之笛", + "MOON_FLUTE": "月亮之笛", + "CHIPPED_POT": "缺损的茶壶", "BLACK_AUGURITE": "黑奇石", "GALARICA_CUFF": "伽勒豆蔻手环", diff --git a/public/locales/zh_CN/move-trigger.json b/public/locales/zh_CN/move-trigger.json index 60de3591915..32a4d0b4a7d 100644 --- a/public/locales/zh_CN/move-trigger.json +++ b/public/locales/zh_CN/move-trigger.json @@ -68,5 +68,5 @@ "chillyReception": "{{pokemonName}}\n说出了冷笑话!", "exposedMove": "{{pokemonName}}识破了\n{{targetPokemonName}}的原型!", "safeguard": "{{targetName}}\n正受到神秘之幕的保护!", - "afterYou": "{{pokemonName}}\n接受了对手的好意!" + "afterYou": "{{targetName}}\n接受了对手的好意!" } diff --git a/public/locales/zh_CN/mystery-encounters/delibirdy-dialogue.json b/public/locales/zh_CN/mystery-encounters/delibirdy-dialogue.json index 3a15f5f98be..6c43a95c782 100644 --- a/public/locales/zh_CN/mystery-encounters/delibirdy-dialogue.json +++ b/public/locales/zh_CN/mystery-encounters/delibirdy-dialogue.json @@ -3,7 +3,7 @@ { "intro": "一伙{{delibirdName}}出现了!", "title": "信使鸟快递", - "description": "{{delibirdName}}满怀期待地看着你,\n它们好像想要什么东西。\n\n也许给它们一件道具或一些钱能让它们满意?", + "description": "{{delibirdName}}满怀期待地看着你,\n它们好像想要什么东西。\n\n给它们一件道具或一些钱的话\n会不会让它们满意呢?", "query": "你要给它们什么?", "invalid_selection": "宝可梦身上并没有那种道具。", "option": { diff --git a/public/locales/zh_CN/mystery-encounters/department-store-sale-dialogue.json b/public/locales/zh_CN/mystery-encounters/department-store-sale-dialogue.json index 09c23762ea9..1d6adb28776 100644 --- a/public/locales/zh_CN/mystery-encounters/department-store-sale-dialogue.json +++ b/public/locales/zh_CN/mystery-encounters/department-store-sale-dialogue.json @@ -1,9 +1,9 @@ { "intro": "一位提着一大堆购物袋的女士。", "speaker": "大促销", - "intro_dialogue": "你好!你也是来参加促销活动的吗?\n在促销期间,可以领取一张特别优惠券,用于购买免费商品!\n我这有一张多余的优惠券。送你了!", + "intro_dialogue": "你好!你也是来参加促销活动的吗?\n在促销期间,可以领取一张特别优惠券!\n可以用于免费兑换商品哦!\n我这有一张多余的优惠券。送你了!", "title": "百货公司促销", - "description": "到处都是商品!\n好像有4个柜台可以让你用优惠券兑换商品。\n无尽可能!", + "description": "到处都是商品!\n共有4个柜台可以用优惠券兑换商品。\n好礼多多!", "query": "你要去哪里?", "option": { "1": { diff --git a/public/locales/zh_CN/mystery-encounters/global-trade-system-dialogue.json b/public/locales/zh_CN/mystery-encounters/global-trade-system-dialogue.json index 4b0d2e84edc..76748b8e71c 100644 --- a/public/locales/zh_CN/mystery-encounters/global-trade-system-dialogue.json +++ b/public/locales/zh_CN/mystery-encounters/global-trade-system-dialogue.json @@ -1,7 +1,7 @@ { "intro": "是GTS系统的交互面板!", "title": "全球交换系统(GTS)", - "description": "啊,GTS!科学的奇迹,\\nn你可以与世界各地的任何人联系,\n与他们交换宝可梦!\n今天你的交换会有好运吗?", + "description": "啊,GTS!科学的奇迹,\n你可以与世界各地的任何人联系,\n并与他们交换宝可梦!\n今天你的交换运会如何呢?", "query": "你要怎么做?", "option": { "1": { @@ -11,13 +11,13 @@ }, "2": { "label": "奇迹交换", - "tooltip": "(+)将一只宝可梦送至GTS,\n并获得一只随机宝可梦" + "tooltip": "(+)发送一只宝可梦,\n并获得一只随机宝可梦" }, "3": { "label": "道具交换", "trade_options_prompt": "选择要发送的道具", "invalid_selection": "这只宝可梦没有合法道具\n可以被交换。", - "tooltip": "(+)将你的一件道具发送到GTS并获得一件随机新道具" + "tooltip": "(+)发送一件道具\n并获得一件随机新道具" }, "4": { "label": "离开", diff --git a/public/locales/zh_CN/mystery-encounters/mysterious-chest-dialogue.json b/public/locales/zh_CN/mystery-encounters/mysterious-chest-dialogue.json index ff01386ca59..b26c5d38c03 100644 --- a/public/locales/zh_CN/mystery-encounters/mysterious-chest-dialogue.json +++ b/public/locales/zh_CN/mystery-encounters/mysterious-chest-dialogue.json @@ -12,7 +12,7 @@ "good": "一些不错的工具和物品!", "great": "一些很稀有的工具和物品!!", "amazing": "哇!金色传说!", - "bad": "哦不!@d{32}\n这个箱子实际上是一个伪装的{{gimmighoul Name}}!$你的{{pokeName}}跳到了你面前\n但为了保护你被打倒了!" + "bad": "哦不!@d{32}\n这个箱子实际上是一个伪装的{{gimmighoulName}}!$你的{{pokeName}}跳到了你面前\n但为了保护你被打倒了!" }, "2": { "label": "有诈,走了", diff --git a/public/locales/zh_CN/mystery-encounters/shady-vitamin-dealer-dialogue.json b/public/locales/zh_CN/mystery-encounters/shady-vitamin-dealer-dialogue.json index 359376eca3d..9412426d794 100644 --- a/public/locales/zh_CN/mystery-encounters/shady-vitamin-dealer-dialogue.json +++ b/public/locales/zh_CN/mystery-encounters/shady-vitamin-dealer-dialogue.json @@ -20,7 +20,7 @@ "tooltip": "(-)无奖励", "selected": "呵呵,没想到你竟然是个懦夫。" }, - "selected": "那人递给你两瓶东西后\n然后很快就消失了。${{selected Pokemon}}获得了{{boost}}和{{boost 2}}提升!" + "selected": "那人递给你两瓶东西后\n然后很快就消失了。${{selectedPokemon}}获得了{{boost1}}和{{boost2}}提升!" }, "cheap_side_effects": "但是药物有一些副作用!$你的{{selectedPokemon}}受到一些伤害,\n并且它的性格变为{{newNature}}!", "no_bad_effects": "看来药物完全没有副作用!" diff --git a/public/locales/zh_CN/mystery-encounters/slumbering-snorlax-dialogue.json b/public/locales/zh_CN/mystery-encounters/slumbering-snorlax-dialogue.json index ee3109e3a1f..8ab03382477 100644 --- a/public/locales/zh_CN/mystery-encounters/slumbering-snorlax-dialogue.json +++ b/public/locales/zh_CN/mystery-encounters/slumbering-snorlax-dialogue.json @@ -13,7 +13,7 @@ "label": "干等", "tooltip": "(-)漫长的等待\n(+)回复队伍", "selected": ".@d{32}.@d{32}.@d{32}$您等待了好一段时间,但是\n{{snorlaxName}}的哈欠让你的队伍昏昏欲睡…………", - "rest_result": "当你们醒来时,{{snorlax Name}}已无处可寻。\n但你所有的宝可梦都痊愈了!" + "rest_result": "当你们醒来时,{{snorlaxName}}已无处可寻。\n但你所有的宝可梦都痊愈了!" }, "3": { "label": "偷摸", diff --git a/public/locales/zh_CN/mystery-encounters/training-session-dialogue.json b/public/locales/zh_CN/mystery-encounters/training-session-dialogue.json index dab52bef1db..ba853f80b4d 100644 --- a/public/locales/zh_CN/mystery-encounters/training-session-dialogue.json +++ b/public/locales/zh_CN/mystery-encounters/training-session-dialogue.json @@ -8,26 +8,26 @@ "1": { "label": "轻量训练", "tooltip": "(-)轻松战斗\n(+)提升随机2只\n宝可梦的个体", - "finished": "{{selectedPokemon}}回来了,感觉\n虽然疲惫不堪,但很有成就!$它的{{stat1}}和{{stat2}}的个体得到了改善!" + "finished": "{{selectedPokemon}}回来了,感觉虽然疲惫不堪,\n但很有成就感!$它的{{stat1}}和{{stat2}}的个体得到了改善!" }, "2": { "label": "适度训练", "tooltip": "(-)适中的战斗\n(+)改变宝可梦的性格", "select_prompt": "选择一种想要的性格\n来训练你的神奇宝贝。", - "finished": "{{selectedPokemon}}回来了,感觉\n虽然疲惫不堪,但很有成就!$它的性格变成了{{nature}}!" + "finished": "{{selectedPokemon}}回来了,感觉虽然疲惫不堪,\n但很有成就感!$它的性格变成了{{nature}}!" }, "3": { "label": "重磅训练", "tooltip": "(-)艰苦的战斗\n(+)改变宝可梦的特性", "select_prompt": "选择一种想要的特性\n来训练你的神奇宝贝。", - "finished": "{{selectedPokemon}}回来了,感觉\n虽然疲惫不堪,但很有成就!$它的特性变成了{{nature}}!" + "finished": "{{selectedPokemon}}回来了,感觉虽然疲惫不堪,\n但很有成就感!$它的特性变成了{{ability}}!" }, "4": { "label": "离开", "tooltip": "(-)无奖励", "selected": "你无暇训练\n该动身了。" }, - "selected": "{{selected Pokemon}}穿过空地来到你面前……" + "selected": "{{selectedPokemon}}穿过空地来到你面前……" }, "outro": "这次训练成果不错!" } diff --git a/public/locales/zh_CN/mystery-encounters/trash-to-treasure-dialogue.json b/public/locales/zh_CN/mystery-encounters/trash-to-treasure-dialogue.json index 29e7817923b..9de9b30cbd9 100644 --- a/public/locales/zh_CN/mystery-encounters/trash-to-treasure-dialogue.json +++ b/public/locales/zh_CN/mystery-encounters/trash-to-treasure-dialogue.json @@ -12,7 +12,7 @@ "2": { "label": "探究来源", "tooltip": "(?)找到垃圾的来源", - "selected": "你在这堆东西周围徘徊,寻找表明这东西可能出现在这里的任何迹象……", + "selected": "你在这堆东西周围徘徊,\n寻找表明这东西可能出现在这里的任何迹象……", "selected_2": "突然,垃圾动了!那不是垃圾,是一只宝可梦!" } } diff --git a/public/locales/zh_CN/mystery-encounters/uncommon-breed-dialogue.json b/public/locales/zh_CN/mystery-encounters/uncommon-breed-dialogue.json index f844cae5908..1d01b894e27 100644 --- a/public/locales/zh_CN/mystery-encounters/uncommon-breed-dialogue.json +++ b/public/locales/zh_CN/mystery-encounters/uncommon-breed-dialogue.json @@ -1,7 +1,7 @@ { "intro": "这可不是一只普通的宝可梦!", "title": "罕见物种", - "description": "{{enemyPokemon}}看起来与其他同种宝可梦大相径庭。\n@[TOOLTIP_TITLE]{也许它会特殊招式?}\n你可以直接进行战斗并尝试捕获它,\n但可能也有办法和它交朋友。", + "description": "{{enemyPokemon}}看起来\n与其他同种宝可梦大相径庭。\n@[TOOLTIP_TITLE]{也许它会特殊招式?}\n你可以直接进行战斗并尝试捕获它,\n但可能也有办法和它交朋友。", "query": "你要怎么做?", "option": { "1": { diff --git a/public/locales/zh_CN/party-ui-handler.json b/public/locales/zh_CN/party-ui-handler.json index 2bc74991824..079fc551a40 100644 --- a/public/locales/zh_CN/party-ui-handler.json +++ b/public/locales/zh_CN/party-ui-handler.json @@ -13,6 +13,7 @@ "ALL": "全部道具", "PASS_BATON": "接棒", "UNPAUSE_EVOLUTION": "解除进化暂停", + "PAUSE_EVOLUTION": "暂停进化", "REVIVE": "复活", "RENAME": "起名", "SELECT": "选择", diff --git a/public/locales/zh_CN/settings.json b/public/locales/zh_CN/settings.json index dd001213b9e..3c0d2ed9e12 100644 --- a/public/locales/zh_CN/settings.json +++ b/public/locales/zh_CN/settings.json @@ -11,6 +11,10 @@ "expGainsSpeed": "经验值获取动画速度", "expPartyDisplay": "显示队伍经验", "skipSeenDialogues": "跳过已读对话", + "eggSkip": "孵蛋跳过", + "never": "从不", + "always": "永久", + "ask": "询问", "battleStyle": "对战模式", "enableRetries": "允许重试", "hideIvs": "禁用个体值探测器信息", @@ -60,9 +64,10 @@ "playerGender": "玩家性别", "typeHints": "属性提示", "masterVolume": "主音量", - "bgmVolume": "音乐", + "bgmVolume": "音乐音量", "fieldVolume": "场景音量", - "seVolume": "音效", + "seVolume": "音效音量", + "uiVolume": "界面音量", "musicPreference": "音乐偏好", "mixed": "全曲混合", "gamepadPleasePlug": "请链接手柄或按任意键", diff --git a/public/locales/zh_TW/arena-tag.json b/public/locales/zh_TW/arena-tag.json index a6224f300a3..4a08d268e20 100644 --- a/public/locales/zh_TW/arena-tag.json +++ b/public/locales/zh_TW/arena-tag.json @@ -28,6 +28,7 @@ "mudSportOnRemove": "玩泥巴的效果消失了!", "waterSportOnAdd": "火焰的威力減弱了!", "waterSportOnRemove": "玩水的效果消失了!", + "plasmaFistsOnAdd": "等離子雨傾盆而下!", "spikesOnAdd": "{{opponentDesc}}腳下\n散落著{{moveName}}!", "spikesActivateTrap": "{{pokemonNameWithAffix}}\n受到了撒菱的傷害!", "toxicSpikesOnAdd": "{{opponentDesc}}腳下\n散落著{{moveName}}!", diff --git a/public/locales/zh_TW/modifier-type.json b/public/locales/zh_TW/modifier-type.json index f305c0e2d62..25c79af821b 100644 --- a/public/locales/zh_TW/modifier-type.json +++ b/public/locales/zh_TW/modifier-type.json @@ -358,6 +358,10 @@ "name": "火焰寶珠", "description": "觸碰後會放出熱量的神奇寶珠。\n攜帶後,在戰鬥時會變成灼傷狀態。" }, + "EVOLUTION_TRACKER_GIMMIGHOUL": { + "name": "金子寶物", + "description": "這個小精靈愛金子! 繼續挑金子然後誰知道什麼會發生!" + }, "BATON": { "name": "接力棒", "description": "允許在切換寶可夢時保留能力變化, 對陷阱\n同樣生效。" @@ -495,6 +499,22 @@ "TART_APPLE": "酸酸蘋果", "STRAWBERRY_SWEET": "草莓糖飾", "UNREMARKABLE_TEACUP": "凡作茶碗", + "UPGRADE": "升級資料", + "DUBIOUS_DISC": "可疑修正檔", + "DRAGON_SCALE": "龍之鱗片", + "PRISM_SCALE": "美麗鱗片", + "RAZOR_CLAW": "銳利之爪", + "RAZOR_FANG": "銳利之牙", + "REAPER_CLOTH": "靈界之布", + "ELECTIRIZER": "電力增幅器", + "MAGMARIZER": "熔岩增幅器", + "PROTECTOR": "護具", + "SACHET": "香袋", + "WHIPPED_DREAM": "泡沫奶油", + "LEADERS_CREST": "頭領憑證", + "SUN_FLUTE": "太陽之笛", + "MOON_FLUTE": "月亮之笛", + "CHIPPED_POT": "缺損的茶壺", "BLACK_AUGURITE": "黑奇石", "GALARICA_CUFF": "伽勒豆蔻手環", diff --git a/public/locales/zh_TW/move-trigger.json b/public/locales/zh_TW/move-trigger.json index 2cd33a3a416..6b80196f166 100644 --- a/public/locales/zh_TW/move-trigger.json +++ b/public/locales/zh_TW/move-trigger.json @@ -68,5 +68,5 @@ "chillyReception": "{{pokemonName}}\n說了冷笑話!", "exposedMove": "{{pokemonName}}識破了\n{{targetPokemonName}}的原形!", "safeguard": "{{targetName}}\n正受到神秘之幕的保護!", - "afterYou": "{{pokemonName}}\n接受了對手的好意!" + "afterYou": "{{targetName}}\n接受了對手的好意!" } diff --git a/src/@types/pokerogue-api.ts b/src/@types/pokerogue-api.ts new file mode 100644 index 00000000000..892869968bb --- /dev/null +++ b/src/@types/pokerogue-api.ts @@ -0,0 +1,9 @@ +/** + * Pokerogue API response for path: `/savedata/session/clear` + */ +export interface PokerogueApiClearSessionData { + /** Contains the error message if any occured */ + error?: string; + /** Is `true` if the request was successfully processed */ + success?: boolean; +} diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 6b53ffd6a7f..c5283b091d4 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1040,10 +1040,6 @@ export default class BattleScene extends SceneBase { this.gameMode = getGameMode(GameModes.CLASSIC); - this.setSeed(Overrides.SEED_OVERRIDE || Utils.randomString(24)); - console.log("Seed:", this.seed); - this.resetSeed(); // Properly resets RNG after saving and quitting a session - this.disableMenu = false; this.score = 0; @@ -1078,6 +1074,12 @@ export default class BattleScene extends SceneBase { //@ts-ignore - allowing `null` for currentBattle causes a lot of trouble this.currentBattle = null; // TODO: resolve ts-ignore + // Reset RNG after end of game or save & quit. + // This needs to happen after clearing this.currentBattle or the seed will be affected by the last wave played + this.setSeed(Overrides.SEED_OVERRIDE || Utils.randomString(24)); + console.log("Seed:", this.seed); + this.resetSeed(); + this.biomeWaveText.setText(startingWave.toString()); this.biomeWaveText.setVisible(false); @@ -1201,7 +1203,7 @@ export default class BattleScene extends SceneBase { // Check for mystery encounter // Can only occur in place of a standard (non-boss) wild battle, waves 10-180 - if (this.isWaveMysteryEncounter(newBattleType, newWaveIndex, mysteryEncounterType) || newBattleType === BattleType.MYSTERY_ENCOUNTER || !isNullOrUndefined(mysteryEncounterType)) { + if (this.isWaveMysteryEncounter(newBattleType, newWaveIndex, mysteryEncounterType) || newBattleType === BattleType.MYSTERY_ENCOUNTER) { newBattleType = BattleType.MYSTERY_ENCOUNTER; // Reset base spawn weight this.mysteryEncounterSaveData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT; @@ -3002,7 +3004,7 @@ export default class BattleScene extends SceneBase { if (participantIds.size > 0) { if (this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) { expValue = Math.floor(expValue * 1.5); - } else if (this.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && this.currentBattle.mysteryEncounter) { + } else if (this.currentBattle.isBattleMysteryEncounter() && this.currentBattle.mysteryEncounter) { expValue = Math.floor(expValue * this.currentBattle.mysteryEncounter.expMultiplier); } for (const partyMember of nonFaintedPartyMembers) { @@ -3091,20 +3093,16 @@ export default class BattleScene extends SceneBase { private isWaveMysteryEncounter(newBattleType: BattleType, waveIndex: number, sessionDataEncounterType?: MysteryEncounterType): boolean { const [lowestMysteryEncounterWave, highestMysteryEncounterWave] = this.gameMode.getMysteryEncounterLegalWaves(); if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(waveIndex) && waveIndex < highestMysteryEncounterWave && waveIndex > lowestMysteryEncounterWave) { - // If ME type is already defined in session data, no need to roll RNG check - if (!isNullOrUndefined(sessionDataEncounterType)) { - return true; - } - // Base spawn weight is BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT/256, and increases by WEIGHT_INCREMENT_ON_SPAWN_MISS/256 for each missed attempt at spawning an encounter on a valid floor const sessionEncounterRate = this.mysteryEncounterSaveData.encounterSpawnChance; const encounteredEvents = this.mysteryEncounterSaveData.encounteredEvents; // If total number of encounters is lower than expected for the run, slightly favor a new encounter spawn (reverse as well) // Reduces occurrence of runs with total encounters significantly different from AVERAGE_ENCOUNTERS_PER_RUN_TARGET + // Favored rate changes can never exceed 50%. So if base rate is 15/256 and favored rate would add 200/256, result will be (15 + 128)/256 const expectedEncountersByFloor = AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave) * (waveIndex - lowestMysteryEncounterWave); const currentRunDiffFromAvg = expectedEncountersByFloor - encounteredEvents.length; - const favoredEncounterRate = sessionEncounterRate + currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER; + const favoredEncounterRate = sessionEncounterRate + Math.min(currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT / 2); const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE) ? favoredEncounterRate : Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE!; diff --git a/src/battle.ts b/src/battle.ts index f9c16d0189d..412fd1b5184 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -213,7 +213,7 @@ export default class Battle { getBgmOverride(scene: BattleScene): string | null { const battlers = this.enemyParty.slice(0, this.getBattlerCount()); - if (this.battleType === BattleType.MYSTERY_ENCOUNTER && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) { + if (this.isBattleMysteryEncounter() && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) { // Music is overridden for MEs during ME onInit() // Should not use any BGM overrides before swapping from DEFAULT mode return null; @@ -409,6 +409,13 @@ export default class Battle { scene.rngSeedOverride = tempSeedOverride; return ret; } + + /** + * Returns if the battle is of type {@linkcode BattleType.MYSTERY_ENCOUNTER} + */ + isBattleMysteryEncounter(): boolean { + return this.battleType === BattleType.MYSTERY_ENCOUNTER; + } } export class FixedBattle extends Battle { @@ -490,7 +497,7 @@ function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], rand } /* 1/3 chance for evil team grunts to be double battles */ - const evilTeamGrunts = [TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT]; + const evilTeamGrunts = [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]; const isEvilTeamGrunt = evilTeamGrunts.includes(trainerTypes[rand]); if (trainerConfigs[trainerTypes[rand]].hasDouble && isEvilTeamGrunt) { diff --git a/src/data/ability.ts b/src/data/ability.ts index 3ace872de3c..a698831a1eb 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -4,7 +4,7 @@ import { Constructor } from "#app/utils"; import * as Utils from "../utils"; import { getPokemonNameWithAffix } from "../messages"; import { Weather, WeatherType } from "./weather"; -import { BattlerTag, GroundedTag, GulpMissileTag, SemiInvulnerableTag } from "./battler-tags"; +import { BattlerTag, GroundedTag } from "./battler-tags"; import { StatusEffect, getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect"; import { Gender } from "./gender"; import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, ChargeAttr, SacrificialAttr, SacrificialAttrOnHit, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./move"; @@ -123,6 +123,7 @@ export class Ability implements Localizable { type AbAttrApplyFunc = (attr: TAttr, passive: boolean) => boolean | Promise; type AbAttrCondition = (pokemon: Pokemon) => boolean; +// TODO: Can this be improved? type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean; type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean; type PokemonStatStageChangeCondition = (target: Pokemon, statsChanged: BattleStat[], stages: number) => boolean; @@ -536,53 +537,6 @@ export class PostDefendAbAttr extends AbAttr { } } -/** - * Applies the effects of Gulp Missile when the user is hit by an attack. - * @extends PostDefendAbAttr - */ -export class PostDefendGulpMissileAbAttr extends PostDefendAbAttr { - constructor() { - super(true); - } - - /** - * Damages the attacker and triggers the secondary effect based on the form or the BattlerTagType. - * @param {Pokemon} pokemon - The defending Pokemon. - * @param passive - n/a - * @param {Pokemon} attacker - The attacking Pokemon. - * @param {Move} move - The move being used. - * @param {HitResult} hitResult - n/a - * @param {any[]} args - n/a - * @returns Whether the effects of the ability are applied. - */ - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { - const battlerTag = pokemon.getTag(GulpMissileTag); - if (!battlerTag || move.category === MoveCategory.STATUS || pokemon.getTag(SemiInvulnerableTag)) { - return false; - } - - if (simulated) { - return true; - } - - const cancelled = new Utils.BooleanHolder(false); - applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled); - - if (!cancelled.value) { - attacker.damageAndUpdate(Math.max(1, Math.floor(attacker.getMaxHp() / 4)), HitResult.OTHER); - } - - if (battlerTag.tagType === BattlerTagType.GULP_MISSILE_ARROKUDA) { - pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), false, [ Stat.DEF ], -1)); - } else { - attacker.trySetStatus(StatusEffect.PARALYSIS, true, pokemon); - } - - pokemon.removeTag(battlerTag.tagType); - return true; - } -} - export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { const attackPriority = new Utils.IntegerHolder(move.priority); @@ -2479,7 +2433,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { pokemon.setStatStage(s, target.getStatStage(s)); } - pokemon.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m!.moveId, m!.ppUsed, m!.ppUp)); // TODO: are those bangs correct? + pokemon.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m?.moveId ?? Moves.NONE, m?.ppUsed, m?.ppUp)); pokemon.summonData.types = target.getTypes(); @@ -3199,12 +3153,12 @@ export class ForewarnAbAttr extends PostSummonAbAttr { } else if (move?.getMove().power === -1) { movePower = 80; } else { - movePower = move!.getMove().power; // TODO: is this bang correct? + movePower = move?.getMove().power ?? 0; } if (movePower > maxPowerSeen) { maxPowerSeen = movePower; - maxMove = move!.getName(); // TODO: is this bang correct? + maxMove = move?.getName() ?? ""; } } } @@ -5210,8 +5164,7 @@ export function initAbilities() { .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonMoldBreaker", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) .attr(MoveAbilityBypassAbAttr), new Ability(Abilities.SUPER_LUCK, 4) - .attr(BonusCritAbAttr) - .partial(), + .attr(BonusCritAbAttr), new Ability(Abilities.AFTERMATH, 4) .attr(PostFaintContactDamageAbAttr, 4) .bypassFaint(), @@ -5223,8 +5176,7 @@ export function initAbilities() { .attr(IgnoreOpponentStatStagesAbAttr) .ignorable(), new Ability(Abilities.TINTED_LENS, 4) - //@ts-ignore - .attr(DamageBoostAbAttr, 2, (user, target, move) => target?.getMoveEffectiveness(user, move) <= 0.5), // TODO: fix TS issues + .attr(DamageBoostAbAttr, 2, (user, target, move) => (target?.getMoveEffectiveness(user!, move) ?? 1) <= 0.5), new Ability(Abilities.FILTER, 4) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75) .ignorable(), @@ -5342,8 +5294,9 @@ export function initAbilities() { .attr(WonderSkinAbAttr) .ignorable(), new Ability(Abilities.ANALYTIC, 5) - //@ts-ignore - .attr(MovePowerBoostAbAttr, (user, target, move) => !!target?.getLastXMoves(1).find(m => m.turn === target?.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command !== Command.FIGHT, 1.3), // TODO fix TS issues + .attr(MovePowerBoostAbAttr, (user, target, move) => + !!target?.getLastXMoves(1).find(m => m.turn === target?.scene.currentBattle.turn) + || user?.scene.currentBattle.turnCommands[target?.getBattlerIndex() ?? BattlerIndex.ATTACKER]?.command !== Command.FIGHT, 1.3), new Ability(Abilities.ILLUSION, 5) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) @@ -5508,8 +5461,7 @@ export function initAbilities() { .bypassFaint() .partial(), new Ability(Abilities.STAKEOUT, 7) - //@ts-ignore - .attr(MovePowerBoostAbAttr, (user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.POKEMON, 2), // TODO: fix TS issues + .attr(MovePowerBoostAbAttr, (user, target, move) => user?.scene.currentBattle.turnCommands[target?.getBattlerIndex() ?? BattlerIndex.ATTACKER]?.command === Command.POKEMON, 2), new Ability(Abilities.WATER_BUBBLE, 7) .attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5) .attr(MoveTypePowerBoostAbAttr, Type.WATER, 2) @@ -5647,8 +5599,7 @@ export function initAbilities() { new Ability(Abilities.PRISM_ARMOR, 7) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75), new Ability(Abilities.NEUROFORCE, 7) - //@ts-ignore - .attr(MovePowerBoostAbAttr, (user, target, move) => target?.getMoveEffectiveness(user, move) >= 2, 1.25), // TODO: fix TS issues + .attr(MovePowerBoostAbAttr, (user, target, move) => (target?.getMoveEffectiveness(user!, move) ?? 1) >= 2, 1.25), new Ability(Abilities.INTREPID_SWORD, 8) .attr(PostSummonStatStageChangeAbAttr, [ Stat.ATK ], 1, true) .condition(getOncePerBattleCondition(Abilities.INTREPID_SWORD)), @@ -5669,13 +5620,19 @@ export function initAbilities() { new Ability(Abilities.MIRROR_ARMOR, 8) .ignorable() .unimplemented(), + /** + * Right now, the logic is attached to Surf and Dive moves. Ideally, the post-defend/hit should be an + * ability attribute but the current implementation of move effects for BattlerTag does not support this- in the case + * where Cramorant is fainted. + * @see {@linkcode GulpMissileTagAttr} and {@linkcode GulpMissileTag} for Gulp Missile implementation + */ new Ability(Abilities.GULP_MISSILE, 8) .attr(UnsuppressableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) .attr(NoFusionAbilityAbAttr) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) - .attr(PostDefendGulpMissileAbAttr), + .bypassFaint(), new Ability(Abilities.STALWART, 8) .attr(BlockRedirectAbAttr), new Ability(Abilities.STEAM_ENGINE, 8) @@ -5740,10 +5697,8 @@ export function initAbilities() { .attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .ignorable(), new Ability(Abilities.HUNGER_SWITCH, 8) - //@ts-ignore - .attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 0 : 1) // TODO: fix ts-ignore - //@ts-ignore - .attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 1 : 0) // TODO: fix ts-ignore + .attr(PostTurnFormChangeAbAttr, p => p.getFormKey() ? 0 : 1) + .attr(PostTurnFormChangeAbAttr, p => p.getFormKey() ? 1 : 0) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index d783ea51056..9e121b81fea 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -511,6 +511,39 @@ class WaterSportTag extends WeakenMoveTypeTag { } } +/** + * Arena Tag class for the secondary effect of {@link https://bulbapedia.bulbagarden.net/wiki/Plasma_Fists_(move) | Plasma Fists}. + * Converts Normal-type moves to Electric type for the rest of the turn. + */ +export class PlasmaFistsTag extends ArenaTag { + constructor() { + super(ArenaTagType.PLASMA_FISTS, 1, Moves.PLASMA_FISTS); + } + + /** Queues Plasma Fists' on-add message */ + onAdd(arena: Arena): void { + arena.scene.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd")); + } + + onRemove(arena: Arena): void { } // Removes default on-remove message + + /** + * Converts Normal-type moves to Electric type + * @param arena n/a + * @param args + * - `[0]` {@linkcode Utils.NumberHolder} A container with a move's {@linkcode Type} + * @returns `true` if the given move type changed; `false` otherwise. + */ + apply(arena: Arena, args: any[]): boolean { + const moveType = args[0]; + if (moveType instanceof Utils.NumberHolder && moveType.value === Type.NORMAL) { + moveType.value = Type.ELECTRIC; + return true; + } + return false; + } +} + /** * Abstract class to implement arena traps. */ @@ -1010,6 +1043,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov return new MudSportTag(turnCount, sourceId); case ArenaTagType.WATER_SPORT: return new WaterSportTag(turnCount, sourceId); + case ArenaTagType.PLASMA_FISTS: + return new PlasmaFistsTag(); case ArenaTagType.SPIKES: return new SpikesTag(sourceId, side); case ArenaTagType.TOXIC_SPIKES: diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 4e688251147..d8094f96368 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -2123,7 +2123,36 @@ export class StockpilingTag extends BattlerTag { */ export class GulpMissileTag extends BattlerTag { constructor(tagType: BattlerTagType, sourceMove: Moves) { - super(tagType, BattlerTagLapseType.CUSTOM, 0, sourceMove); + super(tagType, BattlerTagLapseType.HIT, 0, sourceMove); + } + + override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { + if (pokemon.getTag(BattlerTagType.UNDERWATER)) { + return true; + } + + const moveEffectPhase = pokemon.scene.getCurrentPhase(); + if (moveEffectPhase instanceof MoveEffectPhase) { + const attacker = moveEffectPhase.getUserPokemon(); + + if (!attacker) { + return false; + } + + const cancelled = new Utils.BooleanHolder(false); + applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled); + + if (!cancelled.value) { + attacker.damageAndUpdate(Math.max(1, Math.floor(attacker.getMaxHp() / 4)), HitResult.OTHER); + } + + if (this.tagType === BattlerTagType.GULP_MISSILE_ARROKUDA) { + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), false, [ Stat.DEF ], -1)); + } else { + attacker.trySetStatus(StatusEffect.PARALYSIS, true, pokemon); + } + } + return false; } /** @@ -2589,6 +2618,43 @@ export class ImprisonTag extends MoveRestrictionBattlerTag { } } +/** + * Battler Tag that applies the effects of Syrup Bomb to the target Pokemon. + * For three turns, starting from the turn of hit, at the end of each turn, the target Pokemon's speed will decrease by 1. + * The tag can also expire by taking the target Pokemon off the field. + */ +export class SyrupBombTag extends BattlerTag { + constructor() { + super(BattlerTagType.SYRUP_BOMB, BattlerTagLapseType.TURN_END, 3, Moves.SYRUP_BOMB); + } + + /** + * Adds the Syrup Bomb battler tag to the target Pokemon. + * @param {Pokemon} pokemon the target Pokemon + */ + override onAdd(pokemon: Pokemon) { + super.onAdd(pokemon); + pokemon.scene.queueMessage(i18next.t("battlerTags:syrupBombOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + } + + /** + * Applies the single-stage speed down to the target Pokemon and decrements the tag's turn count + * @param {Pokemon} pokemon the target Pokemon + * @param {BattlerTagLapseType} _lapseType + * @returns `true` if the turnCount is still greater than 0 | `false` if the turnCount is 0 or the target Pokemon has been removed from the field + */ + override lapse(pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean { + if (!pokemon.isActive(true)) { + return false; + } + pokemon.scene.queueMessage(i18next.t("battlerTags:syrupBombLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); // Custom message in lieu of an animation in mainline + pokemon.scene.unshiftPhase(new StatStageChangePhase( + pokemon.scene, pokemon.getBattlerIndex(), true, + [Stat.SPD], -1, true, false, true + )); + return --this.turnCount > 0; + } +} /** * Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID. @@ -2763,6 +2829,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new TauntTag(); case BattlerTagType.IMPRISON: return new ImprisonTag(sourceId); + case BattlerTagType.SYRUP_BOMB: + return new SyrupBombTag(); 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 1afbfc932dc..4cdaef7b1b3 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -527,18 +527,19 @@ interface monotypeOverride { */ export class SingleTypeChallenge extends Challenge { private static TYPE_OVERRIDES: monotypeOverride[] = [ - {species: Species.MELOETTA, type: Type.PSYCHIC, fusion: true}, {species: Species.CASTFORM, type: Type.NORMAL, fusion: false}, ]; + // TODO: Find a solution for all Pokemon with this ssui issue, including Basculin and Burmy + private static SPECIES_OVERRIDES: Species[] = [Species.MELOETTA]; constructor() { super(Challenges.SINGLE_TYPE, 18); } - applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false): boolean { + override applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false): boolean { const speciesForm = getPokemonSpeciesForm(pokemon.speciesId, dexAttr.formIndex); const types = [speciesForm.type1, speciesForm.type2]; - if (soft) { + if (soft && !SingleTypeChallenge.SPECIES_OVERRIDES.includes(pokemon.speciesId)) { const speciesToCheck = [pokemon.speciesId]; while (speciesToCheck.length) { const checking = speciesToCheck.pop(); diff --git a/src/data/egg-moves.ts b/src/data/egg-moves.ts index 3e58f993df2..741b707af2f 100644 --- a/src/data/egg-moves.ts +++ b/src/data/egg-moves.ts @@ -5,12 +5,12 @@ import { Species } from "#enums/species"; export const speciesEggMoves = { - [Species.BULBASAUR]: [ Moves.SAPPY_SEED, Moves.SLUDGE_WAVE, Moves.EARTH_POWER, Moves.MATCHA_GOTCHA ], + [Species.BULBASAUR]: [ Moves.SAPPY_SEED, Moves.MALIGNANT_CHAIN, Moves.EARTH_POWER, Moves.MATCHA_GOTCHA ], [Species.CHARMANDER]: [ Moves.DRAGON_DANCE, Moves.BITTER_BLADE, Moves.EARTH_POWER, Moves.OBLIVION_WING ], [Species.SQUIRTLE]: [ Moves.FREEZE_DRY, Moves.SHORE_UP, Moves.BOUNCY_BUBBLE, Moves.ORIGIN_PULSE ], [Species.CATERPIE]: [ Moves.SANDSEAR_STORM, Moves.SILK_TRAP, Moves.TWIN_BEAM, Moves.BLEAKWIND_STORM ], - [Species.WEEDLE]: [ Moves.THOUSAND_ARROWS, Moves.SWORDS_DANCE, Moves.ATTACK_ORDER, Moves.NOXIOUS_TORQUE ], - [Species.PIDGEY]: [ Moves.WILDBOLT_STORM, Moves.SANDSEAR_STORM, Moves.CALM_MIND, Moves.BOOMBURST ], + [Species.WEEDLE]: [ Moves.THOUSAND_ARROWS, Moves.NOXIOUS_TORQUE, Moves.ATTACK_ORDER, Moves.VICTORY_DANCE ], + [Species.PIDGEY]: [ Moves.WILDBOLT_STORM, Moves.SANDSEAR_STORM, Moves.NASTY_PLOT, Moves.BOOMBURST ], [Species.RATTATA]: [ Moves.HYPER_FANG, Moves.PSYCHIC_FANGS, Moves.FIRE_FANG, Moves.EXTREME_SPEED ], [Species.SPEAROW]: [ Moves.FLOATY_FALL, Moves.EXTREME_SPEED, Moves.TIDY_UP, Moves.TRIPLE_ARROWS ], [Species.EKANS]: [ Moves.NOXIOUS_TORQUE, Moves.DRAGON_DANCE, Moves.SLACK_OFF, Moves.SHED_TAIL ], @@ -21,46 +21,46 @@ export const speciesEggMoves = { [Species.ZUBAT]: [ Moves.FLOATY_FALL, Moves.DIRE_CLAW, Moves.SWORDS_DANCE, Moves.COLLISION_COURSE ], [Species.ODDISH]: [ Moves.SLUDGE_BOMB, Moves.FIERY_DANCE, Moves.STRENGTH_SAP, Moves.SPORE ], [Species.PARAS]: [ Moves.LEECH_LIFE, Moves.HORN_LEECH, Moves.CRABHAMMER, Moves.SAPPY_SEED ], - [Species.VENONAT]: [ Moves.SLUDGE_BOMB, Moves.MOONLIGHT, Moves.EARTH_POWER, Moves.MYSTICAL_POWER ], + [Species.VENONAT]: [ Moves.SLUDGE_BOMB, Moves.TOXIC_THREAD, Moves.EARTH_POWER, Moves.STORED_POWER ], [Species.DIGLETT]: [ Moves.TRIPLE_DIVE, Moves.SWORDS_DANCE, Moves.TRIPLE_AXEL, Moves.HEADLONG_RUSH ], - [Species.MEOWTH]: [ Moves.COVET, Moves.SWORDS_DANCE, Moves.DOUBLE_KICK, Moves.TAIL_SLAP ], - [Species.PSYDUCK]: [ Moves.SPLISHY_SPLASH, Moves.AQUA_STEP, Moves.AURA_SPHERE, Moves.MYSTICAL_POWER ], - [Species.MANKEY]: [ Moves.DRAIN_PUNCH, Moves.PLAY_ROUGH, Moves.METEOR_MASH, Moves.NO_RETREAT ], + [Species.MEOWTH]: [ Moves.HEART_STAMP, Moves.SWORDS_DANCE, Moves.SIZZLY_SLIDE, Moves.TAIL_SLAP ], + [Species.PSYDUCK]: [ Moves.FROST_BREATH, Moves.AQUA_STEP, Moves.MYSTICAL_POWER, Moves.BOUNCY_BUBBLE ], + [Species.MANKEY]: [ Moves.DRAIN_PUNCH, Moves.SLACK_OFF, Moves.METEOR_MASH, Moves.NO_RETREAT ], [Species.GROWLITHE]: [ Moves.ZING_ZAP, Moves.PARTING_SHOT, Moves.MORNING_SUN, Moves.SACRED_FIRE ], [Species.POLIWAG]: [ Moves.SLACK_OFF, Moves.WILDBOLT_STORM, Moves.DRAIN_PUNCH, Moves.SURGING_STRIKES ], - [Species.ABRA]: [ Moves.AURA_SPHERE, Moves.BADDY_BAD, Moves.THUNDERBOLT, Moves.PSYSTRIKE ], + [Species.ABRA]: [ Moves.AURA_SPHERE, Moves.BADDY_BAD, Moves.ICE_BEAM, Moves.PSYSTRIKE ], [Species.MACHOP]: [ Moves.COMBAT_TORQUE, Moves.METEOR_MASH, Moves.MOUNTAIN_GALE, Moves.FISSURE ], [Species.BELLSPROUT]: [ Moves.SOLAR_BLADE, Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.VICTORY_DANCE ], [Species.TENTACOOL]: [ Moves.BANEFUL_BUNKER, Moves.STRENGTH_SAP, Moves.BOUNCY_BUBBLE, Moves.MALIGNANT_CHAIN ], - [Species.GEODUDE]: [ Moves.BODY_PRESS, Moves.BULK_UP, Moves.SHORE_UP, Moves.HEAD_SMASH ], + [Species.GEODUDE]: [ Moves.FLARE_BLITZ, Moves.HEAD_SMASH, Moves.SHORE_UP, Moves.SHELL_SMASH ], [Species.PONYTA]: [ Moves.HIGH_HORSEPOWER, Moves.FIRE_LASH, Moves.SWORDS_DANCE, Moves.VOLT_TACKLE ], [Species.SLOWPOKE]: [ Moves.BOUNCY_BUBBLE, Moves.FLAMETHROWER, Moves.MYSTICAL_POWER, Moves.SHED_TAIL ], - [Species.MAGNEMITE]: [ Moves.PARABOLIC_CHARGE, Moves.BODY_PRESS, Moves.ICE_BEAM, Moves.THUNDERCLAP ], - [Species.FARFETCHD]: [ Moves.IVY_CUDGEL, Moves.TRIPLE_ARROWS, Moves.ROOST, Moves.VICTORY_DANCE ], + [Species.MAGNEMITE]: [ Moves.PARABOLIC_CHARGE, Moves.FLAMETHROWER, Moves.ICE_BEAM, Moves.THUNDERCLAP ], + [Species.FARFETCHD]: [ Moves.IVY_CUDGEL, Moves.TRIPLE_ARROWS, Moves.DRILL_RUN, Moves.VICTORY_DANCE ], [Species.DODUO]: [ Moves.TRIPLE_AXEL, Moves.MULTI_ATTACK, Moves.FLOATY_FALL, Moves.TRIPLE_ARROWS ], [Species.SEEL]: [ Moves.FREEZE_DRY, Moves.BOUNCY_BUBBLE, Moves.SLACK_OFF, Moves.STEAM_ERUPTION ], [Species.GRIMER]: [ Moves.SUCKER_PUNCH, Moves.CURSE, Moves.STRENGTH_SAP, Moves.NOXIOUS_TORQUE ], [Species.SHELLDER]: [ Moves.ROCK_BLAST, Moves.WATER_SHURIKEN, Moves.BANEFUL_BUNKER, Moves.BONE_RUSH ], [Species.GASTLY]: [ Moves.SLUDGE_BOMB, Moves.AURA_SPHERE, Moves.NASTY_PLOT, Moves.ASTRAL_BARRAGE ], - [Species.ONIX]: [ Moves.SHORE_UP, Moves.BODY_PRESS, Moves.HEAVY_SLAM, Moves.DIAMOND_STORM ], + [Species.ONIX]: [ Moves.SHORE_UP, Moves.THOUSAND_WAVES, Moves.COIL, Moves.DIAMOND_STORM ], [Species.DROWZEE]: [ Moves.BADDY_BAD, Moves.STRENGTH_SAP, Moves.LUMINA_CRASH, Moves.DARK_VOID ], - [Species.KRABBY]: [ Moves.FIRE_LASH, Moves.PLAY_ROUGH, Moves.IVY_CUDGEL, Moves.SHELL_SMASH ], - [Species.VOLTORB]: [ Moves.NASTY_PLOT, Moves.OVERHEAT, Moves.FROST_BREATH, Moves.ELECTRO_DRIFT ], + [Species.KRABBY]: [ Moves.DIRE_CLAW, Moves.JET_PUNCH, Moves.IVY_CUDGEL, Moves.SHELL_SMASH ], + [Species.VOLTORB]: [ Moves.NASTY_PLOT, Moves.FUSION_FLARE, Moves.FROST_BREATH, Moves.ELECTRO_DRIFT ], [Species.EXEGGCUTE]: [ Moves.FICKLE_BEAM, Moves.APPLE_ACID, Moves.TRICK_ROOM, Moves.LUMINA_CRASH ], [Species.CUBONE]: [ Moves.HEAD_SMASH, Moves.WOOD_HAMMER, Moves.SHADOW_SNEAK, Moves.BITTER_BLADE ], - [Species.LICKITUNG]: [ Moves.BODY_SLAM, Moves.FIRE_LASH, Moves.GRAV_APPLE, Moves.MILK_DRINK ], + [Species.LICKITUNG]: [ Moves.CRUSH_GRIP, Moves.FIRE_LASH, Moves.SLACK_OFF, Moves.MAGICAL_TORQUE ], [Species.KOFFING]: [ Moves.SCALD, Moves.RECOVER, Moves.BODY_PRESS, Moves.MALIGNANT_CHAIN ], [Species.RHYHORN]: [ Moves.SHORE_UP, Moves.ICE_HAMMER, Moves.ACCELEROCK, Moves.HEAD_SMASH ], [Species.TANGELA]: [ Moves.STRENGTH_SAP, Moves.SNAP_TRAP, Moves.PARTING_SHOT, Moves.SAPPY_SEED ], [Species.KANGASKHAN]: [ Moves.POWER_UP_PUNCH, Moves.TRAILBLAZE, Moves.FACADE, Moves.SEISMIC_TOSS ], [Species.HORSEA]: [ Moves.SNIPE_SHOT, Moves.FROST_BREATH, Moves.HURRICANE, Moves.SPACIAL_REND ], - [Species.GOLDEEN]: [ Moves.DRILL_RUN, Moves.FLIP_TURN, Moves.DRAGON_DANCE, Moves.FISHIOUS_REND ], + [Species.GOLDEEN]: [ Moves.GLACIAL_LANCE, Moves.SUPERCELL_SLAM, Moves.DRAGON_DANCE, Moves.FISHIOUS_REND ], [Species.STARYU]: [ Moves.CALM_MIND, Moves.BOUNCY_BUBBLE, Moves.MOONBLAST, Moves.MYSTICAL_POWER ], [Species.SCYTHER]: [ Moves.MIGHTY_CLEAVE, Moves.BUG_BITE, Moves.STORM_THROW, Moves.DOUBLE_IRON_BASH ], - [Species.PINSIR]: [ Moves.EARTHQUAKE, Moves.LEECH_LIFE, Moves.CLOSE_COMBAT, Moves.EXTREME_SPEED ], - [Species.TAUROS]: [ Moves.HIGH_HORSEPOWER, Moves.BLAZING_TORQUE, Moves.LIQUIDATION, Moves.COMBAT_TORQUE ], + [Species.PINSIR]: [ Moves.HEADLONG_RUSH, Moves.LEECH_LIFE, Moves.CRUSH_GRIP, Moves.EXTREME_SPEED ], + [Species.TAUROS]: [ Moves.HIGH_HORSEPOWER, Moves.FIRE_LASH, Moves.LIQUIDATION, Moves.COMBAT_TORQUE ], [Species.MAGIKARP]: [ Moves.FLIP_TURN, Moves.ICE_SPINNER, Moves.DRAGON_ASCENT, Moves.SURGING_STRIKES ], - [Species.LAPRAS]: [ Moves.RECOVER, Moves.FREEZE_DRY, Moves.SHELL_SMASH, Moves.STEAM_ERUPTION ], + [Species.LAPRAS]: [ Moves.RECOVER, Moves.FREEZE_DRY, Moves.SCALD, Moves.SHELL_SMASH ], [Species.DITTO]: [ Moves.MIMIC, Moves.SKETCH, Moves.METRONOME, Moves.IMPRISON ], [Species.EEVEE]: [ Moves.WISH, Moves.NO_RETREAT, Moves.ZIPPY_ZAP, Moves.BOOMBURST ], [Species.PORYGON]: [ Moves.THUNDERCLAP, Moves.AURA_SPHERE, Moves.FLAMETHROWER, Moves.TECHNO_BLAST ], @@ -68,56 +68,56 @@ export const speciesEggMoves = { [Species.KABUTO]: [ Moves.CEASELESS_EDGE, Moves.HIGH_HORSEPOWER, Moves.TRIPLE_DIVE, Moves.MIGHTY_CLEAVE ], [Species.AERODACTYL]: [ Moves.FLOATY_FALL, Moves.FLARE_BLITZ, Moves.SWORDS_DANCE, Moves.MIGHTY_CLEAVE ], [Species.ARTICUNO]: [ Moves.EARTH_POWER, Moves.CALM_MIND, Moves.AURORA_VEIL, Moves.AEROBLAST ], - [Species.ZAPDOS]: [ Moves.WEATHER_BALL, Moves.CALM_MIND, Moves.SANDSEAR_STORM, Moves.ELECTRO_SHOT ], + [Species.ZAPDOS]: [ Moves.BLEAKWIND_STORM, Moves.CALM_MIND, Moves.SANDSEAR_STORM, Moves.ELECTRO_SHOT ], [Species.MOLTRES]: [ Moves.SCORCHING_SANDS, Moves.CALM_MIND, Moves.AEROBLAST, Moves.TORCH_SONG ], [Species.DRATINI]: [ Moves.DRAGON_HAMMER, Moves.CRUSH_GRIP, Moves.FIRE_LASH, Moves.GIGATON_HAMMER ], [Species.MEWTWO]: [ Moves.METEOR_MASH, Moves.MOONBLAST, Moves.THUNDEROUS_KICK, Moves.PHOTON_GEYSER ], [Species.MEW]: [ Moves.PHOTON_GEYSER, Moves.MOONBLAST, Moves.ASTRAL_BARRAGE, Moves.SHELL_SMASH ], [Species.CHIKORITA]: [ Moves.SAPPY_SEED, Moves.STONE_AXE, Moves.DRAGON_DANCE, Moves.SPORE ], - [Species.CYNDAQUIL]: [ Moves.NASTY_PLOT, Moves.SCORCHING_SANDS, Moves.FIERY_DANCE, Moves.ELECTRO_DRIFT ], + [Species.CYNDAQUIL]: [ Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.FIERY_DANCE, Moves.ELECTRO_DRIFT ], [Species.TOTODILE]: [ Moves.THUNDER_PUNCH, Moves.DRAGON_DANCE, Moves.TRIPLE_AXEL, Moves.FISHIOUS_REND ], - [Species.SENTRET]: [ Moves.TIDY_UP, Moves.THIEF, Moves.NUZZLE, Moves.EXTREME_SPEED ], + [Species.SENTRET]: [ Moves.TIDY_UP, Moves.FAKE_OUT, Moves.NUZZLE, Moves.EXTREME_SPEED ], [Species.HOOTHOOT]: [ Moves.CALM_MIND, Moves.ESPER_WING, Moves.AEROBLAST, Moves.BOOMBURST ], [Species.LEDYBA]: [ Moves.POLLEN_PUFF, Moves.THIEF, Moves.PARTING_SHOT, Moves.SPORE ], [Species.SPINARAK]: [ Moves.PARTING_SHOT, Moves.ATTACK_ORDER, Moves.GASTRO_ACID, Moves.STRENGTH_SAP ], [Species.CHINCHOU]: [ Moves.THUNDERCLAP, Moves.BOUNCY_BUBBLE, Moves.THUNDER_CAGE, Moves.TAIL_GLOW ], - [Species.PICHU]: [ Moves.RISING_VOLTAGE, Moves.SPLISHY_SPLASH, Moves.FLOATY_FALL, Moves.THUNDERCLAP ], + [Species.PICHU]: [ Moves.MOONBLAST, Moves.TRIPLE_AXEL, Moves.FIERY_DANCE, Moves.AURA_WHEEL ], [Species.CLEFFA]: [ Moves.CALM_MIND, Moves.EARTH_POWER, Moves.WISH, Moves.LIGHT_OF_RUIN ], [Species.IGGLYBUFF]: [ Moves.DRAIN_PUNCH, Moves.GRAV_APPLE, Moves.SOFT_BOILED, Moves.EXTREME_SPEED ], [Species.TOGEPI]: [ Moves.SCORCHING_SANDS, Moves.ROOST, Moves.RELIC_SONG, Moves.FIERY_DANCE ], - [Species.NATU]: [ Moves.AEROBLAST, Moves.ROOST, Moves.CALM_MIND, Moves.LUMINA_CRASH ], + [Species.NATU]: [ Moves.AEROBLAST, Moves.ROOST, Moves.MOONBLAST, Moves.LUMINA_CRASH ], [Species.MAREEP]: [ Moves.ICE_BEAM, Moves.PARABOLIC_CHARGE, Moves.CORE_ENFORCER, Moves.TAIL_GLOW ], [Species.HOPPIP]: [ Moves.FLOATY_FALL, Moves.STRENGTH_SAP, Moves.SAPPY_SEED, Moves.SPORE ], [Species.AIPOM]: [ Moves.TIDY_UP, Moves.STORM_THROW, Moves.FAKE_OUT, Moves.POPULATION_BOMB ], - [Species.SUNKERN]: [ Moves.SPORE, Moves.SAPPY_SEED, Moves.FIERY_DANCE, Moves.HYDRO_STEAM ], + [Species.SUNKERN]: [ Moves.SPORE, Moves.QUIVER_DANCE, Moves.FIERY_DANCE, Moves.HYDRO_STEAM ], [Species.YANMA]: [ Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.HEAT_WAVE, Moves.BLEAKWIND_STORM ], [Species.WOOPER]: [ Moves.SIZZLY_SLIDE, Moves.RECOVER, Moves.CURSE, Moves.SURGING_STRIKES ], [Species.MURKROW]: [ Moves.TRIPLE_ARROWS, Moves.FLOATY_FALL, Moves.TIDY_UP, Moves.WICKED_BLOW ], [Species.MISDREAVUS]: [ Moves.TAKE_HEART, Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.ASTRAL_BARRAGE ], [Species.UNOWN]: [ Moves.NATURE_POWER, Moves.COSMIC_POWER, Moves.ANCIENT_POWER, Moves.MYSTICAL_POWER ], [Species.GIRAFARIG]: [ Moves.MYSTICAL_POWER, Moves.NIGHT_DAZE, Moves.RECOVER, Moves.BOOMBURST ], - [Species.PINECO]: [ Moves.METAL_BURST, Moves.LUNGE, Moves.BODY_PRESS, Moves.SHORE_UP ], - [Species.DUNSPARCE]: [ Moves.BODY_SLAM, Moves.MAGICAL_TORQUE, Moves.BLAZING_TORQUE, Moves.EXTREME_SPEED ], + [Species.PINECO]: [ Moves.METAL_BURST, Moves.SHORE_UP, Moves.BODY_PRESS, Moves.DIAMOND_STORM ], + [Species.DUNSPARCE]: [ Moves.WICKED_TORQUE, Moves.MAGICAL_TORQUE, Moves.BLAZING_TORQUE, Moves.EXTREME_SPEED ], [Species.GLIGAR]: [ Moves.FLOATY_FALL, Moves.THOUSAND_WAVES, Moves.ROOST, Moves.MIGHTY_CLEAVE ], [Species.SNUBBULL]: [ Moves.FACADE, Moves.EARTHQUAKE, Moves.SWORDS_DANCE, Moves.EXTREME_SPEED ], [Species.QWILFISH]: [ Moves.BARB_BARRAGE, Moves.BANEFUL_BUNKER, Moves.KNOCK_OFF, Moves.FISHIOUS_REND ], [Species.SHUCKLE]: [ Moves.STUFF_CHEEKS, Moves.HEAL_ORDER, Moves.BODY_PRESS, Moves.SALT_CURE ], [Species.HERACROSS]: [ Moves.ROCK_BLAST, Moves.FIRST_IMPRESSION, Moves.ICICLE_SPEAR, Moves.DRAGON_DANCE ], [Species.SNEASEL]: [ Moves.DIRE_CLAW, Moves.STORM_THROW, Moves.TRIPLE_AXEL, Moves.WICKED_BLOW ], - [Species.TEDDIURSA]: [ Moves.MOUNTAIN_GALE, Moves.RAGING_BULL, Moves.SLACK_OFF, Moves.PRECIPICE_BLADES ], + [Species.TEDDIURSA]: [ Moves.MOUNTAIN_GALE, Moves.FAKE_OUT, Moves.SLACK_OFF, Moves.PRECIPICE_BLADES ], [Species.SLUGMA]: [ Moves.BURNING_BULWARK, Moves.POWER_GEM, Moves.SOLAR_BEAM, Moves.MAGMA_STORM ], [Species.SWINUB]: [ Moves.SLACK_OFF, Moves.LANDS_WRATH, Moves.MIGHTY_CLEAVE, Moves.GLACIAL_LANCE ], [Species.CORSOLA]: [ Moves.SCALD, Moves.FREEZE_DRY, Moves.STRENGTH_SAP, Moves.SALT_CURE ], [Species.REMORAID]: [ Moves.WATER_SHURIKEN, Moves.TAKE_HEART, Moves.SHELL_SIDE_ARM, Moves.BOUNCY_BUBBLE ], - [Species.DELIBIRD]: [ Moves.DRILL_RUN, Moves.FLOATY_FALL, Moves.VICTORY_DANCE, Moves.GLACIAL_LANCE ], + [Species.DELIBIRD]: [ Moves.BONEMERANG, Moves.FLOATY_FALL, Moves.VICTORY_DANCE, Moves.GLACIAL_LANCE ], [Species.SKARMORY]: [ Moves.ROOST, Moves.BODY_PRESS, Moves.SPIKY_SHIELD, Moves.BEAK_BLAST ], - [Species.HOUNDOUR]: [ Moves.HEAT_WAVE, Moves.FIERY_WRATH, Moves.SOLAR_BEAM, Moves.HYDRO_STEAM ], + [Species.HOUNDOUR]: [ Moves.MOONLIGHT, Moves.FIERY_WRATH, Moves.SECRET_SWORD, Moves.HYDRO_STEAM ], [Species.PHANPY]: [ Moves.SHORE_UP, Moves.SWORDS_DANCE, Moves.ICICLE_CRASH, Moves.COLLISION_COURSE ], - [Species.STANTLER]: [ Moves.THUNDEROUS_KICK, Moves.HYPER_VOICE, Moves.BULK_UP, Moves.PHOTON_GEYSER ], + [Species.STANTLER]: [ Moves.THUNDEROUS_KICK, Moves.PHOTON_GEYSER, Moves.SWORDS_DANCE, Moves.BOOMBURST ], [Species.SMEARGLE]: [ Moves.CONVERSION, Moves.BURNING_BULWARK, Moves.SALT_CURE, Moves.DARK_VOID ], [Species.TYROGUE]: [ Moves.VICTORY_DANCE, Moves.THUNDEROUS_KICK, Moves.METEOR_MASH, Moves.WICKED_BLOW ], [Species.SMOOCHUM]: [ Moves.EXPANDING_FORCE, Moves.AURA_SPHERE, Moves.FREEZE_DRY, Moves.QUIVER_DANCE ], - [Species.ELEKID]: [ Moves.BLAZING_TORQUE, Moves.TIDY_UP, Moves.MOUNTAIN_GALE, Moves.ZIPPY_ZAP ], + [Species.ELEKID]: [ Moves.FIRE_LASH, Moves.ZING_ZAP, Moves.MOUNTAIN_GALE, Moves.SHIFT_GEAR ], [Species.MAGBY]: [ Moves.THUNDERCLAP, Moves.EARTH_POWER, Moves.ARMOR_CANNON, Moves.FLEUR_CANNON ], [Species.MILTANK]: [ Moves.BODY_PRESS, Moves.BULK_UP, Moves.YAWN, Moves.SIZZLY_SLIDE ], [Species.RAIKOU]: [ Moves.PARABOLIC_CHARGE, Moves.NASTY_PLOT, Moves.FROST_BREATH, Moves.ELECTRO_DRIFT ], @@ -125,10 +125,10 @@ export const speciesEggMoves = { [Species.SUICUNE]: [ Moves.RECOVER, Moves.NASTY_PLOT, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ], [Species.LARVITAR]: [ Moves.DRAGON_DANCE, Moves.MOUNTAIN_GALE, Moves.SHORE_UP, Moves.DIAMOND_STORM ], [Species.LUGIA]: [ Moves.NASTY_PLOT, Moves.LUMINA_CRASH, Moves.AURA_SPHERE, Moves.OBLIVION_WING ], - [Species.HO_OH]: [ Moves.FLOATY_FALL, Moves.PRECIPICE_BLADES, Moves.REVIVAL_BLESSING, Moves.BOLT_BEAK ], + [Species.HO_OH]: [ Moves.BRAVE_BIRD, Moves.DRAGON_DANCE, Moves.REVIVAL_BLESSING, Moves.BOLT_BEAK ], [Species.CELEBI]: [ Moves.PHOTON_GEYSER, Moves.MATCHA_GOTCHA, Moves.REVIVAL_BLESSING, Moves.QUIVER_DANCE ], [Species.TREECKO]: [ Moves.NASTY_PLOT, Moves.APPLE_ACID, Moves.SECRET_SWORD, Moves.DRAGON_ENERGY ], - [Species.TORCHIC]: [ Moves.HIGH_JUMP_KICK, Moves.SUPERCELL_SLAM, Moves.KNOCK_OFF, Moves.V_CREATE ], + [Species.TORCHIC]: [ Moves.HIGH_JUMP_KICK, Moves.SUPERCELL_SLAM, Moves.BURNING_BULWARK, Moves.V_CREATE ], [Species.MUDKIP]: [ Moves.SHORE_UP, Moves.MOUNTAIN_GALE, Moves.BULK_UP, Moves.SURGING_STRIKES ], [Species.POOCHYENA]: [ Moves.JAW_LOCK, Moves.CLOSE_COMBAT, Moves.DIRE_CLAW, Moves.NO_RETREAT ], [Species.ZIGZAGOON]: [ Moves.EXTREME_SPEED, Moves.NUZZLE, Moves.HIGH_HORSEPOWER, Moves.TIDY_UP ], @@ -154,7 +154,7 @@ export const speciesEggMoves = { [Species.ELECTRIKE]: [ Moves.RISING_VOLTAGE, Moves.FLAMETHROWER, Moves.NASTY_PLOT, Moves.ICE_BEAM ], [Species.PLUSLE]: [ Moves.FLAMETHROWER, Moves.GLITZY_GLOW, Moves.SPLISHY_SPLASH, Moves.TAIL_GLOW ], [Species.MINUN]: [ Moves.ICE_BEAM, Moves.BADDY_BAD, Moves.SPARKLY_SWIRL, Moves.TAIL_GLOW ], - [Species.VOLBEAT]: [ Moves.BATON_PASS, Moves.LUNGE, Moves.DECORATE, Moves.VICTORY_DANCE ], + [Species.VOLBEAT]: [ Moves.BATON_PASS, Moves.STICKY_WEB, Moves.DECORATE, Moves.VICTORY_DANCE ], [Species.ILLUMISE]: [ Moves.PARTING_SHOT, Moves.GLITZY_GLOW, Moves.POWDER, Moves.QUIVER_DANCE ], [Species.GULPIN]: [ Moves.STRENGTH_SAP, Moves.EARTH_POWER, Moves.CALM_MIND, Moves.MALIGNANT_CHAIN ], [Species.CARVANHA]: [ Moves.THUNDER_FANG, Moves.SWORDS_DANCE, Moves.OBSTRUCT, Moves.SURGING_STRIKES ], @@ -168,7 +168,7 @@ export const speciesEggMoves = { [Species.SWABLU]: [ Moves.ROOST, Moves.NASTY_PLOT, Moves.FLOATY_FALL, Moves.BOOMBURST ], [Species.ZANGOOSE]: [ Moves.FACADE, Moves.HIGH_HORSEPOWER, Moves.EXTREME_SPEED, Moves.TIDY_UP ], [Species.SEVIPER]: [ Moves.ICE_BEAM, Moves.BITTER_BLADE, Moves.SUCKER_PUNCH, Moves.NO_RETREAT ], - [Species.LUNATONE]: [ Moves.POWER_GEM, Moves.NIGHT_DAZE, Moves.SHELL_SMASH, Moves.LUMINA_CRASH ], + [Species.LUNATONE]: [ Moves.POWER_GEM, Moves.MOONGEIST_BEAM, Moves.SHELL_SMASH, Moves.LUMINA_CRASH ], [Species.SOLROCK]: [ Moves.PSYSHIELD_BASH, Moves.MIGHTY_CLEAVE, Moves.SHELL_SMASH, Moves.SACRED_FIRE ], [Species.BARBOACH]: [ Moves.DRAGON_DANCE, Moves.ZING_ZAP, Moves.ICE_SPINNER, Moves.SURGING_STRIKES ], [Species.CORPHISH]: [ Moves.CEASELESS_EDGE, Moves.JET_PUNCH, Moves.SUCKER_PUNCH, Moves.SHELL_SMASH ], @@ -177,7 +177,7 @@ export const speciesEggMoves = { [Species.ANORITH]: [ Moves.FIRST_IMPRESSION, Moves.LEECH_LIFE, Moves.DRAGON_DANCE, Moves.MIGHTY_CLEAVE ], [Species.FEEBAS]: [ Moves.CALM_MIND, Moves.FREEZE_DRY, Moves.MOONBLAST, Moves.STEAM_ERUPTION ], [Species.CASTFORM]: [ Moves.BOOMBURST, Moves.HYDRO_STEAM, Moves.ERUPTION, Moves.QUIVER_DANCE ], - [Species.KECLEON]: [ Moves.DRAIN_PUNCH, Moves.DRAGON_DANCE, Moves.EXTREME_SPEED, Moves.MULTI_ATTACK ], + [Species.KECLEON]: [ Moves.ZIPPY_ZAP, Moves.COIL, Moves.EXTREME_SPEED, Moves.MULTI_ATTACK ], [Species.SHUPPET]: [ Moves.STORM_THROW, Moves.TIDY_UP, Moves.PARTING_SHOT, Moves.SPECTRAL_THIEF ], [Species.DUSKULL]: [ Moves.BULK_UP, Moves.DRAIN_PUNCH, Moves.STRENGTH_SAP, Moves.RAGE_FIST ], [Species.TROPIUS]: [ Moves.STUFF_CHEEKS, Moves.EARTH_POWER, Moves.APPLE_ACID, Moves.SAPPY_SEED ], @@ -189,13 +189,13 @@ export const speciesEggMoves = { [Species.RELICANTH]: [ Moves.DRAGON_DANCE, Moves.SHORE_UP, Moves.WAVE_CRASH, Moves.DIAMOND_STORM ], [Species.LUVDISC]: [ Moves.BATON_PASS, Moves.HEART_SWAP, Moves.GLITZY_GLOW, Moves.REVIVAL_BLESSING ], [Species.BAGON]: [ Moves.FLOATY_FALL, Moves.FIRE_LASH, Moves.DRAGON_DANCE, Moves.GLAIVE_RUSH ], - [Species.BELDUM]: [ Moves.HIGH_HORSEPOWER, Moves.RECOVER, Moves.TRIPLE_AXEL, Moves.SHIFT_GEAR ], + [Species.BELDUM]: [ Moves.HEADLONG_RUSH, Moves.DRAIN_PUNCH, Moves.TRIPLE_AXEL, Moves.SHIFT_GEAR ], [Species.REGIROCK]: [ Moves.STONE_AXE, Moves.BODY_PRESS, Moves.SHORE_UP, Moves.SALT_CURE ], [Species.REGICE]: [ Moves.EARTH_POWER, Moves.TAKE_HEART, Moves.RECOVER, Moves.FREEZE_DRY ], [Species.REGISTEEL]: [ Moves.BODY_PRESS, Moves.SIZZLY_SLIDE, Moves.RECOVER, Moves.GIGATON_HAMMER ], [Species.LATIAS]: [ Moves.CORE_ENFORCER, Moves.FUSION_FLARE, Moves.SPARKLY_SWIRL, Moves.MYSTICAL_POWER ], [Species.LATIOS]: [ Moves.CORE_ENFORCER, Moves.BLUE_FLARE, Moves.NASTY_PLOT, Moves.TACHYON_CUTTER ], - [Species.KYOGRE]: [ Moves.BOUNCY_BUBBLE, Moves.HURRICANE, Moves.FREEZE_DRY, Moves.ELECTRO_SHOT ], + [Species.KYOGRE]: [ Moves.RECOVER, Moves.HURRICANE, Moves.FREEZY_FROST, Moves.WILDBOLT_STORM ], [Species.GROUDON]: [ Moves.STONE_AXE, Moves.SOLAR_BLADE, Moves.MORNING_SUN, Moves.SACRED_FIRE ], [Species.RAYQUAZA]: [ Moves.V_CREATE, Moves.DRAGON_DARTS, Moves.CORE_ENFORCER, Moves.OBLIVION_WING ], [Species.JIRACHI]: [ Moves.TACHYON_CUTTER, Moves.TRIPLE_ARROWS, Moves.ROCK_SLIDE, Moves.SHELL_SMASH ], @@ -206,7 +206,7 @@ export const speciesEggMoves = { [Species.STARLY]: [ Moves.SWORDS_DANCE, Moves.HEAD_CHARGE, Moves.FLARE_BLITZ, Moves.EXTREME_SPEED ], [Species.BIDOOF]: [ Moves.EXTREME_SPEED, Moves.COSMIC_POWER, Moves.POWER_TRIP, Moves.AQUA_STEP ], [Species.KRICKETOT]: [ Moves.BONEMERANG, Moves.VICTORY_DANCE, Moves.STONE_AXE, Moves.POPULATION_BOMB ], - [Species.SHINX]: [ Moves.FIRE_LASH, Moves.TRIPLE_AXEL, Moves.FACADE, Moves.BOLT_STRIKE ], + [Species.SHINX]: [ Moves.FIRE_LASH, Moves.TRIPLE_AXEL, Moves.ZIPPY_ZAP, Moves.BOLT_STRIKE ], [Species.BUDEW]: [ Moves.FIERY_DANCE, Moves.ACID_SPRAY, Moves.BOUNCY_BUBBLE, Moves.QUIVER_DANCE ], [Species.CRANIDOS]: [ Moves.VOLT_TACKLE, Moves.ACCELEROCK, Moves.FLARE_BLITZ, Moves.SHIFT_GEAR ], [Species.SHIELDON]: [ Moves.SHORE_UP, Moves.BODY_PRESS, Moves.KINGS_SHIELD, Moves.DIAMOND_STORM ], @@ -223,7 +223,7 @@ export const speciesEggMoves = { [Species.STUNKY]: [ Moves.CEASELESS_EDGE, Moves.KNOCK_OFF, Moves.RECOVER, Moves.DIRE_CLAW ], [Species.BRONZOR]: [ Moves.RECOVER, Moves.TACHYON_CUTTER, Moves.GLARE, Moves.LUMINA_CRASH ], [Species.BONSLY]: [ Moves.ACCELEROCK, Moves.SWORDS_DANCE, Moves.STRENGTH_SAP, Moves.SAPPY_SEED ], - [Species.MIME_JR]: [ Moves.CALM_MIND, Moves.MOONBLAST, Moves.SIZZLY_SLIDE, Moves.LUMINA_CRASH ], + [Species.MIME_JR]: [ Moves.CHILLY_RECEPTION, Moves.MOONBLAST, Moves.FROST_BREATH, Moves.LUMINA_CRASH ], [Species.HAPPINY]: [ Moves.COTTON_GUARD, Moves.SEISMIC_TOSS, Moves.SIZZLY_SLIDE, Moves.REVIVAL_BLESSING ], [Species.CHATOT]: [ Moves.SPARKLING_ARIA, Moves.TORCH_SONG, Moves.BATON_PASS, Moves.BOOMBURST ], [Species.SPIRITOMB]: [ Moves.PARTING_SHOT, Moves.BADDY_BAD, Moves.STRENGTH_SAP, Moves.SPECTRAL_THIEF ], @@ -232,7 +232,7 @@ export const speciesEggMoves = { [Species.RIOLU]: [ Moves.THUNDEROUS_KICK, Moves.TACHYON_CUTTER, Moves.TRIPLE_AXEL, Moves.DOUBLE_IRON_BASH ], [Species.HIPPOPOTAS]: [ Moves.SHORE_UP, Moves.STONE_AXE, Moves.BULK_UP, Moves.SALT_CURE ], [Species.SKORUPI]: [ Moves.COIL, Moves.DIRE_CLAW, Moves.CRABHAMMER, Moves.WICKED_BLOW ], - [Species.CROAGUNK]: [ Moves.DIRE_CLAW, Moves.ICE_PUNCH, Moves.THUNDEROUS_KICK, Moves.VICTORY_DANCE ], + [Species.CROAGUNK]: [ Moves.DIRE_CLAW, Moves.ICE_SPINNER, Moves.THUNDEROUS_KICK, Moves.VICTORY_DANCE ], [Species.CARNIVINE]: [ Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.COIL, Moves.SAPPY_SEED ], [Species.FINNEON]: [ Moves.QUIVER_DANCE, Moves.BOUNCY_BUBBLE, Moves.FREEZE_DRY, Moves.ORIGIN_PULSE ], [Species.MANTYKE]: [ Moves.SPLISHY_SPLASH, Moves.FREEZY_FROST, Moves.NASTY_PLOT, Moves.OBLIVION_WING ], @@ -243,7 +243,7 @@ export const speciesEggMoves = { [Species.AZELF]: [ Moves.PSYSTRIKE, Moves.ICE_BEAM, Moves.MOONBLAST, Moves.TAIL_GLOW ], [Species.DIALGA]: [ Moves.CORE_ENFORCER, Moves.TAKE_HEART, Moves.RECOVER, Moves.MAKE_IT_RAIN ], [Species.PALKIA]: [ Moves.RECOVER, Moves.TAKE_HEART, Moves.FREEZE_DRY, Moves.ORIGIN_PULSE ], - [Species.HEATRAN]: [ Moves.TORCH_SONG, Moves.RECOVER, Moves.TACHYON_CUTTER, Moves.MATCHA_GOTCHA ], + [Species.HEATRAN]: [ Moves.MATCHA_GOTCHA, Moves.RECOVER, Moves.TACHYON_CUTTER, Moves.TORCH_SONG ], [Species.REGIGIGAS]: [ Moves.SKILL_SWAP, Moves.RECOVER, Moves.EXTREME_SPEED, Moves.GIGATON_HAMMER ], [Species.GIRATINA]: [ Moves.DRAGON_DANCE, Moves.GLAIVE_RUSH, Moves.RECOVER, Moves.SPECTRAL_THIEF ], [Species.CRESSELIA]: [ Moves.COSMIC_POWER, Moves.SECRET_SWORD, Moves.SIZZLY_SLIDE, Moves.LUMINA_CRASH ], @@ -274,18 +274,18 @@ export const speciesEggMoves = { [Species.THROH]: [ Moves.MACH_PUNCH, Moves.SLACK_OFF, Moves.METEOR_MASH, Moves.RAGE_FIST ], [Species.SAWK]: [ Moves.DRAIN_PUNCH, Moves.SUCKER_PUNCH, Moves.METEOR_MASH, Moves.VICTORY_DANCE ], [Species.SEWADDLE]: [ Moves.STONE_AXE, Moves.PSYCHO_CUT, Moves.BITTER_BLADE, Moves.VICTORY_DANCE ], - [Species.VENIPEDE]: [ Moves.SWORDS_DANCE, Moves.LEECH_LIFE, Moves.NOXIOUS_TORQUE, Moves.POWER_TRIP ], + [Species.VENIPEDE]: [ Moves.BANEFUL_BUNKER, Moves.LEECH_LIFE, Moves.NOXIOUS_TORQUE, Moves.POWER_TRIP ], [Species.COTTONEE]: [ Moves.POLLEN_PUFF, Moves.PARTING_SHOT, Moves.SLEEP_POWDER, Moves.SEED_FLARE ], [Species.PETILIL]: [ Moves.THUNDEROUS_KICK, Moves.SPARKLING_ARIA, Moves.FIERY_DANCE, Moves.FLOWER_TRICK ], [Species.BASCULIN]: [ Moves.LAST_RESPECTS, Moves.CLOSE_COMBAT, Moves.SPLISHY_SPLASH, Moves.NO_RETREAT ], [Species.SANDILE]: [ Moves.DIRE_CLAW, Moves.HIGH_HORSEPOWER, Moves.FIRE_LASH, Moves.WICKED_BLOW ], [Species.DARUMAKA]: [ Moves.DRAIN_PUNCH, Moves.ZIPPY_ZAP, Moves.EARTHQUAKE, Moves.PYRO_BALL ], - [Species.MARACTUS]: [ Moves.SCORCHING_SANDS, Moves.QUIVER_DANCE, Moves.FIERY_DANCE, Moves.SEED_FLARE ], + [Species.MARACTUS]: [ Moves.EARTH_POWER, Moves.QUIVER_DANCE, Moves.FIERY_DANCE, Moves.SEED_FLARE ], [Species.DWEBBLE]: [ Moves.CRABHAMMER, Moves.STONE_AXE, Moves.LEECH_LIFE, Moves.MIGHTY_CLEAVE ], - [Species.SCRAGGY]: [ Moves.SUCKER_PUNCH, Moves.BULLET_PUNCH, Moves.DRAGON_DANCE, Moves.COLLISION_COURSE ], + [Species.SCRAGGY]: [ Moves.SUCKER_PUNCH, Moves.BULLET_PUNCH, Moves.NOXIOUS_TORQUE, Moves.VICTORY_DANCE ], [Species.SIGILYPH]: [ Moves.MOONBLAST, Moves.CALM_MIND, Moves.FREEZING_GLARE, Moves.OBLIVION_WING ], - [Species.YAMASK]: [ Moves.STRENGTH_SAP, Moves.INFERNAL_PARADE, Moves.AURA_SPHERE, Moves.ASTRAL_BARRAGE ], - [Species.TIRTOUGA]: [ Moves.ICE_SPINNER, Moves.LIQUIDATION, Moves.SHORE_UP, Moves.MIGHTY_CLEAVE ], + [Species.YAMASK]: [ Moves.STRENGTH_SAP, Moves.GLARE, Moves.AURA_SPHERE, Moves.ASTRAL_BARRAGE ], + [Species.TIRTOUGA]: [ Moves.ICE_SPINNER, Moves.AQUA_STEP, Moves.SHORE_UP, Moves.MIGHTY_CLEAVE ], [Species.ARCHEN]: [ Moves.ROOST, Moves.EARTHQUAKE, Moves.FLOATY_FALL, Moves.MIGHTY_CLEAVE ], [Species.TRUBBISH]: [ Moves.COIL, Moves.RECOVER, Moves.DIRE_CLAW, Moves.GIGATON_HAMMER ], [Species.ZORUA]: [ Moves.FLAMETHROWER, Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.FIERY_WRATH ], @@ -295,19 +295,19 @@ export const speciesEggMoves = { [Species.DUCKLETT]: [ Moves.SPLISHY_SPLASH, Moves.EARTH_POWER, Moves.WILDBOLT_STORM, Moves.QUIVER_DANCE ], [Species.VANILLITE]: [ Moves.EARTH_POWER, Moves.AURORA_VEIL, Moves.CALM_MIND, Moves.SPARKLY_SWIRL ], [Species.DEERLING]: [ Moves.TIDY_UP, Moves.FLOWER_TRICK, Moves.BODY_SLAM, Moves.COMBAT_TORQUE ], - [Species.EMOLGA]: [ Moves.TRIPLE_AXEL, Moves.SPLISHY_SPLASH, Moves.TAILWIND, Moves.ZIPPY_ZAP ], - [Species.KARRABLAST]: [ Moves.LEECH_LIFE, Moves.HEAL_ORDER, Moves.HIGH_HORSEPOWER, Moves.DOUBLE_IRON_BASH ], + [Species.EMOLGA]: [ Moves.TRIPLE_AXEL, Moves.SPLISHY_SPLASH, Moves.FLOATY_FALL, Moves.AURA_WHEEL ], + [Species.KARRABLAST]: [ Moves.LEECH_LIFE, Moves.BITTER_BLADE, Moves.HIGH_HORSEPOWER, Moves.DOUBLE_IRON_BASH ], [Species.FOONGUS]: [ Moves.POLLEN_PUFF, Moves.PARTING_SHOT, Moves.FOUL_PLAY, Moves.SAPPY_SEED ], - [Species.FRILLISH]: [ Moves.STRENGTH_SAP, Moves.INFERNAL_PARADE, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ], - [Species.ALOMOMOLA]: [ Moves.FLIP_TURN, Moves.HEART_SWAP, Moves.TOXIC, Moves.GLITZY_GLOW ], - [Species.JOLTIK]: [ Moves.THUNDER, Moves.PARABOLIC_CHARGE, Moves.EARTH_POWER, Moves.QUIVER_DANCE ], + [Species.FRILLISH]: [ Moves.STRENGTH_SAP, Moves.BUZZY_BUZZ, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ], + [Species.ALOMOMOLA]: [ Moves.FLIP_TURN, Moves.HEART_SWAP, Moves.GLITZY_GLOW, Moves.REVIVAL_BLESSING ], + [Species.JOLTIK]: [ Moves.WILDBOLT_STORM, Moves.PARABOLIC_CHARGE, Moves.EARTH_POWER, Moves.QUIVER_DANCE ], [Species.FERROSEED]: [ Moves.STRENGTH_SAP, Moves.BODY_PRESS, Moves.SPIKY_SHIELD, Moves.SAPPY_SEED ], [Species.KLINK]: [ Moves.TRIPLE_AXEL, Moves.HIGH_HORSEPOWER, Moves.FUSION_BOLT, Moves.DOUBLE_IRON_BASH ], - [Species.TYNAMO]: [ Moves.SCALD, Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.PLASMA_FISTS ], - [Species.ELGYEM]: [ Moves.LUSTER_PURGE, Moves.TRICK_ROOM, Moves.AURA_SPHERE, Moves.TAIL_GLOW ], + [Species.TYNAMO]: [ Moves.SCALD, Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.AURA_WHEEL ], + [Species.ELGYEM]: [ Moves.LUSTER_PURGE, Moves.BADDY_BAD, Moves.AURA_SPHERE, Moves.TAIL_GLOW ], [Species.LITWICK]: [ Moves.FIERY_DANCE, Moves.EARTH_POWER, Moves.MOONBLAST, Moves.ASTRAL_BARRAGE ], [Species.AXEW]: [ Moves.STONE_AXE, Moves.DIRE_CLAW, Moves.BITTER_BLADE, Moves.GLAIVE_RUSH ], - [Species.CUBCHOO]: [ Moves.TRIPLE_AXEL, Moves.LIQUIDATION, Moves.SWORDS_DANCE, Moves.COLLISION_COURSE ], + [Species.CUBCHOO]: [ Moves.MOUNTAIN_GALE, Moves.AQUA_STEP, Moves.ICE_SHARD, Moves.COLLISION_COURSE ], [Species.CRYOGONAL]: [ Moves.FREEZING_GLARE, Moves.AURORA_VEIL, Moves.NASTY_PLOT, Moves.ORIGIN_PULSE ], [Species.SHELMET]: [ Moves.POWER_GEM, Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.STEAM_ERUPTION ], [Species.STUNFISK]: [ Moves.BANEFUL_BUNKER, Moves.SANDSEAR_STORM, Moves.STRENGTH_SAP, Moves.THUNDERCLAP ], @@ -315,24 +315,24 @@ export const speciesEggMoves = { [Species.DRUDDIGON]: [ Moves.FIRE_LASH, Moves.ROOST, Moves.DRAGON_DARTS, Moves.CLANGOROUS_SOUL ], [Species.GOLETT]: [ Moves.SHIFT_GEAR, Moves.DRAIN_PUNCH, Moves.HEADLONG_RUSH, Moves.RAGE_FIST ], [Species.PAWNIARD]: [ Moves.SUCKER_PUNCH, Moves.CEASELESS_EDGE, Moves.BITTER_BLADE, Moves.LAST_RESPECTS ], - [Species.BOUFFALANT]: [ Moves.SLACK_OFF, Moves.JUMP_KICK, Moves.HEAD_SMASH, Moves.FLARE_BLITZ ], + [Species.BOUFFALANT]: [ Moves.SLACK_OFF, Moves.HIGH_JUMP_KICK, Moves.HEAD_SMASH, Moves.FLARE_BLITZ ], [Species.RUFFLET]: [ Moves.FLOATY_FALL, Moves.AURA_SPHERE, Moves.NO_RETREAT, Moves.BOLT_BEAK ], [Species.VULLABY]: [ Moves.FOUL_PLAY, Moves.BODY_PRESS, Moves.ROOST, Moves.RUINATION ], [Species.HEATMOR]: [ Moves.EARTH_POWER, Moves.OVERHEAT, Moves.THUNDERBOLT, Moves.V_CREATE ], [Species.DURANT]: [ Moves.HIGH_HORSEPOWER, Moves.FIRST_IMPRESSION, Moves.SWORDS_DANCE, Moves.BEHEMOTH_BASH ], [Species.DEINO]: [ Moves.FIERY_WRATH, Moves.ESPER_WING, Moves.SLUDGE_BOMB, Moves.FICKLE_BEAM ], [Species.LARVESTA]: [ Moves.THUNDERBOLT, Moves.MATCHA_GOTCHA, Moves.EARTH_POWER, Moves.TORCH_SONG ], - [Species.COBALION]: [ Moves.BEHEMOTH_BLADE, Moves.BODY_PRESS, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ], + [Species.COBALION]: [ Moves.BEHEMOTH_BLADE, Moves.MIGHTY_CLEAVE, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ], [Species.TERRAKION]: [ Moves.MIGHTY_CLEAVE, Moves.HEADLONG_RUSH, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ], [Species.VIRIZION]: [ Moves.PSYBLADE, Moves.SAPPY_SEED, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ], [Species.TORNADUS]: [ Moves.EARTH_POWER, Moves.PARTING_SHOT, Moves.ICE_BEAM, Moves.OBLIVION_WING ], [Species.THUNDURUS]: [ Moves.EARTH_POWER, Moves.HURRICANE, Moves.FROST_BREATH, Moves.ELECTRO_SHOT ], - [Species.RESHIRAM]: [ Moves.MORNING_SUN, Moves.TAKE_HEART, Moves.FICKLE_BEAM, Moves.ERUPTION ], - [Species.ZEKROM]: [ Moves.DRAGON_DANCE, Moves.THUNDEROUS_KICK, Moves.DRAGON_HAMMER, Moves.BOLT_BEAK ], + [Species.RESHIRAM]: [ Moves.ENERGY_BALL, Moves.TAKE_HEART, Moves.FICKLE_BEAM, Moves.ERUPTION ], + [Species.ZEKROM]: [ Moves.TRIPLE_AXEL, Moves.THUNDEROUS_KICK, Moves.DRAGON_HAMMER, Moves.BOLT_BEAK ], [Species.LANDORUS]: [ Moves.STONE_AXE, Moves.FLOATY_FALL, Moves.ROOST, Moves.BLEAKWIND_STORM ], - [Species.KYUREM]: [ Moves.DRAGON_DARTS, Moves.CORE_ENFORCER, Moves.NO_RETREAT, Moves.GLACIAL_LANCE ], + [Species.KYUREM]: [ Moves.DRAGON_DARTS, Moves.GLACIAL_LANCE, Moves.NO_RETREAT, Moves.DRAGON_ENERGY ], [Species.KELDEO]: [ Moves.BOUNCY_BUBBLE, Moves.THUNDERBOLT, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ], - [Species.MELOETTA]: [ Moves.VICTORY_DANCE, Moves.QUIVER_DANCE, Moves.TRIPLE_ARROWS, Moves.TORCH_SONG ], + [Species.MELOETTA]: [ Moves.BODY_SLAM, Moves.TORCH_SONG, Moves.TRIPLE_ARROWS, Moves.BOOMBURST ], [Species.GENESECT]: [ Moves.EXTREME_SPEED, Moves.U_TURN, Moves.TACHYON_CUTTER, Moves.TAIL_GLOW ], [Species.CHESPIN]: [ Moves.BODY_PRESS, Moves.SYNTHESIS, Moves.CEASELESS_EDGE, Moves.SAPPY_SEED ], [Species.FENNEKIN]: [ Moves.EXPANDING_FORCE, Moves.MOONBLAST, Moves.THUNDERBOLT, Moves.TORCH_SONG ], @@ -344,13 +344,13 @@ export const speciesEggMoves = { [Species.FLABEBE]: [ Moves.GLITZY_GLOW, Moves.MYSTICAL_FIRE, Moves.TAKE_HEART, Moves.SEED_FLARE ], [Species.SKIDDO]: [ Moves.HIGH_HORSEPOWER, Moves.GRASSY_GLIDE, Moves.STONE_AXE, Moves.SAPPY_SEED ], [Species.PANCHAM]: [ Moves.DRAIN_PUNCH, Moves.SUCKER_PUNCH, Moves.METEOR_MASH, Moves.WICKED_BLOW ], - [Species.FURFROU]: [ Moves.TIDY_UP, Moves.SLACK_OFF, Moves.COVET, Moves.MULTI_ATTACK ], - [Species.ESPURR]: [ Moves.GLARE, Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.PSYSTRIKE ], + [Species.FURFROU]: [ Moves.TIDY_UP, Moves.SLACK_OFF, Moves.COMBAT_TORQUE, Moves.MULTI_ATTACK ], + [Species.ESPURR]: [ Moves.LUSTER_PURGE, Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.DARK_VOID ], [Species.HONEDGE]: [ Moves.TACHYON_CUTTER, Moves.SHADOW_BONE, Moves.BITTER_BLADE, Moves.BEHEMOTH_BLADE ], - [Species.SPRITZEE]: [ Moves.TRICK_ROOM, Moves.FOUL_PLAY, Moves.WISH, Moves.REVIVAL_BLESSING ], + [Species.SPRITZEE]: [ Moves.SPEED_SWAP, Moves.TORCH_SONG, Moves.ROOST, Moves.REVIVAL_BLESSING ], [Species.SWIRLIX]: [ Moves.BELLY_DRUM, Moves.HEADLONG_RUSH, Moves.MAGICAL_TORQUE, Moves.REVIVAL_BLESSING ], [Species.INKAY]: [ Moves.POWER_TRIP, Moves.SPIN_OUT, Moves.RECOVER, Moves.PSYCHO_BOOST ], - [Species.BINACLE]: [ Moves.TRIPLE_AXEL, Moves.ACCELEROCK, Moves.DIRE_CLAW, Moves.MIGHTY_CLEAVE ], + [Species.BINACLE]: [ Moves.TRIPLE_AXEL, Moves.CRABHAMMER, Moves.DIRE_CLAW, Moves.MIGHTY_CLEAVE ], [Species.SKRELP]: [ Moves.STRENGTH_SAP, Moves.TRICK_ROOM, Moves.CALM_MIND, Moves.CORE_ENFORCER ], [Species.CLAUNCHER]: [ Moves.SHELL_SMASH, Moves.ARMOR_CANNON, Moves.WATER_SHURIKEN, Moves.ORIGIN_PULSE ], [Species.HELIOPTILE]: [ Moves.WEATHER_BALL, Moves.HYDRO_STEAM, Moves.EARTH_POWER, Moves.BOOMBURST ], @@ -360,15 +360,15 @@ export const speciesEggMoves = { [Species.DEDENNE]: [ Moves.BOOMBURST, Moves.FAKE_OUT, Moves.NASTY_PLOT, Moves.REVIVAL_BLESSING ], [Species.CARBINK]: [ Moves.BODY_PRESS, Moves.SHORE_UP, Moves.SPARKLY_SWIRL, Moves.DIAMOND_STORM ], [Species.GOOMY]: [ Moves.SCALD, Moves.RECOVER, Moves.CALM_MIND, Moves.MAKE_IT_RAIN ], - [Species.KLEFKI]: [ Moves.HEAL_BELL, Moves.ENCORE, Moves.TOPSY_TURVY, Moves.INSTRUCT ], + [Species.KLEFKI]: [ Moves.HEAL_BELL, Moves.ENCORE, Moves.INSTRUCT, Moves.TOPSY_TURVY ], [Species.PHANTUMP]: [ Moves.RAGE_FIST, Moves.TRICK_ROOM, Moves.SYNTHESIS, Moves.SAPPY_SEED ], [Species.PUMPKABOO]: [ Moves.SPIRIT_SHACKLE, Moves.FIRE_LASH, Moves.DIRE_CLAW, Moves.SAPPY_SEED ], [Species.BERGMITE]: [ Moves.STONE_AXE, Moves.METAL_BURST, Moves.BODY_PRESS, Moves.GLACIAL_LANCE ], [Species.NOIBAT]: [ Moves.AEROBLAST, Moves.OVERDRIVE, Moves.NASTY_PLOT, Moves.CLANGING_SCALES ], - [Species.XERNEAS]: [ Moves.SEARING_SHOT, Moves.LUMINA_CRASH, Moves.STRENGTH_SAP, Moves.TAIL_GLOW ], + [Species.XERNEAS]: [ Moves.EARTH_POWER, Moves.SPRINGTIDE_STORM, Moves.STRENGTH_SAP, Moves.TAIL_GLOW ], [Species.YVELTAL]: [ Moves.SHELL_SIDE_ARM, Moves.POWER_TRIP, Moves.FIERY_WRATH, Moves.CLANGOROUS_SOUL ], [Species.ZYGARDE]: [ Moves.DRAGON_DARTS, Moves.HEAL_ORDER, Moves.CLANGOROUS_SOUL, Moves.DOUBLE_IRON_BASH ], - [Species.DIANCIE]: [ Moves.MAGICAL_TORQUE, Moves.BODY_PRESS, Moves.SHORE_UP, Moves.GEOMANCY ], + [Species.DIANCIE]: [ Moves.MAGICAL_TORQUE, Moves.AURA_SPHERE, Moves.SHORE_UP, Moves.GEOMANCY ], [Species.HOOPA]: [ Moves.PHOTON_GEYSER, Moves.SECRET_SWORD, Moves.FIERY_WRATH, Moves.SHELL_SMASH ], [Species.VOLCANION]: [ Moves.HYDRO_STEAM, Moves.CALM_MIND, Moves.ENERGY_BALL, Moves.MAGMA_STORM ], [Species.ROWLET]: [ Moves.THOUSAND_ARROWS, Moves.SHADOW_BONE, Moves.FIRST_IMPRESSION, Moves.VICTORY_DANCE ], @@ -379,7 +379,7 @@ export const speciesEggMoves = { [Species.GRUBBIN]: [ Moves.ICE_BEAM, Moves.EARTH_POWER, Moves.THUNDERCLAP, Moves.QUIVER_DANCE ], [Species.CRABRAWLER]: [ Moves.JET_PUNCH, Moves.SHORE_UP, Moves.SUCKER_PUNCH, Moves.SURGING_STRIKES ], [Species.ORICORIO]: [ Moves.QUIVER_DANCE, Moves.FIERY_DANCE, Moves.THUNDERCLAP, Moves.OBLIVION_WING ], - [Species.CUTIEFLY]: [ Moves.STICKY_WEB, Moves.MOONBLAST, Moves.HEAT_WAVE, Moves.SPORE ], + [Species.CUTIEFLY]: [ Moves.STICKY_WEB, Moves.SLEEP_POWDER, Moves.HEAT_WAVE, Moves.SPARKLY_SWIRL ], [Species.ROCKRUFF]: [ Moves.HIGH_HORSEPOWER, Moves.TIDY_UP, Moves.ICE_SPINNER, Moves.MIGHTY_CLEAVE ], [Species.WISHIWASHI]: [ Moves.HEAL_ORDER, Moves.ICE_SPINNER, Moves.DRAGON_DANCE, Moves.JET_PUNCH ], [Species.MAREANIE]: [ Moves.CEASELESS_EDGE, Moves.SIZZLY_SLIDE, Moves.BODY_PRESS, Moves.LEECH_SEED ], @@ -394,31 +394,31 @@ export const speciesEggMoves = { [Species.ORANGURU]: [ Moves.JUNGLE_HEALING, Moves.YAWN, Moves.FOLLOW_ME, Moves.LUMINA_CRASH ], [Species.PASSIMIAN]: [ Moves.FAKE_OUT, Moves.SUCKER_PUNCH, Moves.ZING_ZAP, Moves.PYRO_BALL ], [Species.WIMPOD]: [ Moves.TRIPLE_AXEL, Moves.OBSTRUCT, Moves.JET_PUNCH, Moves.SURGING_STRIKES ], - [Species.SANDYGAST]: [ Moves.SCORCHING_SANDS, Moves.SPLISHY_SPLASH, Moves.TAKE_HEART, Moves.SALT_CURE ], + [Species.SANDYGAST]: [ Moves.SANDSEAR_STORM, Moves.SPLISHY_SPLASH, Moves.TAKE_HEART, Moves.SALT_CURE ], [Species.PYUKUMUKU]: [ Moves.COMEUPPANCE, Moves.BANEFUL_BUNKER, Moves.TOXIC_SPIKES, Moves.SALT_CURE ], [Species.TYPE_NULL]: [ Moves.DIRE_CLAW, Moves.RECOVER, Moves.EXTREME_SPEED, Moves.SHELL_SMASH ], [Species.MINIOR]: [ Moves.EARTH_POWER, Moves.FLOATY_FALL, Moves.ZING_ZAP, Moves.DIAMOND_STORM ], [Species.KOMALA]: [ Moves.SLACK_OFF, Moves.EXTREME_SPEED, Moves.KNOCK_OFF, Moves.COLLISION_COURSE ], [Species.TURTONATOR]: [ Moves.BURNING_BULWARK, Moves.MORNING_SUN, Moves.BODY_PRESS, Moves.CORE_ENFORCER ], - [Species.TOGEDEMARU]: [ Moves.FAKE_OUT, Moves.METAL_BURST, Moves.METEOR_MASH, Moves.BOLT_STRIKE ], + [Species.TOGEDEMARU]: [ Moves.FAKE_OUT, Moves.METAL_BURST, Moves.METEOR_MASH, Moves.AURA_WHEEL ], [Species.MIMIKYU]: [ Moves.SPIRIT_BREAK, Moves.TIDY_UP, Moves.BITTER_BLADE, Moves.SPECTRAL_THIEF ], [Species.BRUXISH]: [ Moves.PLAY_ROUGH, Moves.FIRE_FANG, Moves.DRAGON_DANCE, Moves.SURGING_STRIKES ], [Species.DRAMPA]: [ Moves.SLACK_OFF, Moves.TRICK_ROOM, Moves.CORE_ENFORCER, Moves.BOOMBURST ], [Species.DHELMISE]: [ Moves.SHADOW_BONE, Moves.STRENGTH_SAP, Moves.LIQUIDATION, Moves.SAPPY_SEED ], [Species.JANGMO_O]: [ Moves.BODY_PRESS, Moves.SHELL_SIDE_ARM, Moves.SECRET_SWORD, Moves.GLAIVE_RUSH ], - [Species.TAPU_KOKO]: [ Moves.MAGICAL_TORQUE, Moves.TRIPLE_AXEL, Moves.RISING_VOLTAGE, Moves.PLASMA_FISTS ], + [Species.TAPU_KOKO]: [ Moves.MAGICAL_TORQUE, Moves.TRIPLE_AXEL, Moves.RISING_VOLTAGE, Moves.BOLT_STRIKE ], [Species.TAPU_LELE]: [ Moves.MOONLIGHT, Moves.NASTY_PLOT, Moves.HEAT_WAVE, Moves.EXPANDING_FORCE ], [Species.TAPU_BULU]: [ Moves.SAPPY_SEED, Moves.DRAIN_PUNCH, Moves.MAGICAL_TORQUE, Moves.VICTORY_DANCE ], [Species.TAPU_FINI]: [ Moves.AURA_SPHERE, Moves.EARTH_POWER, Moves.RECOVER, Moves.QUIVER_DANCE ], [Species.COSMOG]: [ Moves.PHOTON_GEYSER, Moves.PRECIPICE_BLADES, Moves.SACRED_FIRE, Moves.ASTRAL_BARRAGE ], [Species.NIHILEGO]: [ Moves.STRENGTH_SAP, Moves.MALIGNANT_CHAIN, Moves.EARTH_POWER, Moves.QUIVER_DANCE ], [Species.BUZZWOLE]: [ Moves.FIRST_IMPRESSION, Moves.COMBAT_TORQUE, Moves.ROCK_WRECKER, Moves.DOUBLE_IRON_BASH ], - [Species.PHEROMOSA]: [ Moves.AURA_SPHERE, Moves.MAKE_IT_RAIN, Moves.ATTACK_ORDER, Moves.DIAMOND_STORM ], + [Species.PHEROMOSA]: [ Moves.SECRET_SWORD, Moves.MAKE_IT_RAIN, Moves.ATTACK_ORDER, Moves.DIAMOND_STORM ], [Species.XURKITREE]: [ Moves.FLAMETHROWER, Moves.GIGA_DRAIN, Moves.TAIL_GLOW, Moves.THUNDERCLAP ], [Species.CELESTEELA]: [ Moves.RECOVER, Moves.BUZZY_BUZZ, Moves.SANDSEAR_STORM, Moves.OBLIVION_WING ], [Species.KARTANA]: [ Moves.MIGHTY_CLEAVE, Moves.PSYBLADE, Moves.BITTER_BLADE, Moves.BEHEMOTH_BLADE ], [Species.GUZZLORD]: [ Moves.SUCKER_PUNCH, Moves.COMEUPPANCE, Moves.SLACK_OFF, Moves.SHED_TAIL ], - [Species.NECROZMA]: [ Moves.CLANGOROUS_SOUL, Moves.SACRED_FIRE, Moves.ASTRAL_BARRAGE, Moves.DYNAMAX_CANNON ], + [Species.NECROZMA]: [ Moves.DYNAMAX_CANNON, Moves.SACRED_FIRE, Moves.ASTRAL_BARRAGE, Moves.CLANGOROUS_SOUL ], [Species.MAGEARNA]: [ Moves.STRENGTH_SAP, Moves.EARTH_POWER, Moves.MOONBLAST, Moves.MAKE_IT_RAIN ], [Species.MARSHADOW]: [ Moves.POWER_UP_PUNCH, Moves.TRIPLE_AXEL, Moves.METEOR_MASH, Moves.STORM_THROW ], [Species.POIPOLE]: [ Moves.CORE_ENFORCER, Moves.ICE_BEAM, Moves.SEARING_SHOT, Moves.MALIGNANT_CHAIN ], @@ -428,11 +428,11 @@ export const speciesEggMoves = { [Species.MELTAN]: [ Moves.BULLET_PUNCH, Moves.DRAIN_PUNCH, Moves.BULK_UP, Moves.PLASMA_FISTS ], [Species.GROOKEY]: [ Moves.HIGH_HORSEPOWER, Moves.CLANGOROUS_SOUL, Moves.GRASSY_GLIDE, Moves.SAPPY_SEED ], [Species.SCORBUNNY]: [ Moves.EXTREME_SPEED, Moves.HIGH_JUMP_KICK, Moves.TRIPLE_AXEL, Moves.BOLT_STRIKE ], - [Species.SOBBLE]: [ Moves.AEROBLAST, Moves.FROST_BREATH, Moves.SCORCHING_SANDS, Moves.NASTY_PLOT ], - [Species.SKWOVET]: [ Moves.KNOCK_OFF, Moves.SLACK_OFF, Moves.BODY_PRESS, Moves.POPULATION_BOMB ], + [Species.SOBBLE]: [ Moves.AEROBLAST, Moves.FROST_BREATH, Moves.ENERGY_BALL, Moves.NASTY_PLOT ], + [Species.SKWOVET]: [ Moves.SUCKER_PUNCH, Moves.SLACK_OFF, Moves.COIL, Moves.POPULATION_BOMB ], [Species.ROOKIDEE]: [ Moves.ROOST, Moves.BODY_PRESS, Moves.KINGS_SHIELD, Moves.BEHEMOTH_BASH ], - [Species.BLIPBUG]: [ Moves.HEAL_ORDER, Moves.EXPANDING_FORCE, Moves.SPORE, Moves.TAIL_GLOW ], - [Species.NICKIT]: [ Moves.BADDY_BAD, Moves.BURNING_JEALOUSY, Moves.SPARKLY_SWIRL, Moves.FIERY_WRATH ], + [Species.BLIPBUG]: [ Moves.HEAL_ORDER, Moves.LUSTER_PURGE, Moves.SLEEP_POWDER, Moves.TAIL_GLOW ], + [Species.NICKIT]: [ Moves.BADDY_BAD, Moves.FLAMETHROWER, Moves.SPARKLY_SWIRL, Moves.MAKE_IT_RAIN ], [Species.GOSSIFLEUR]: [ Moves.TAILWIND, Moves.STRENGTH_SAP, Moves.SAPPY_SEED, Moves.SEED_FLARE ], [Species.WOOLOO]: [ Moves.PSYSHIELD_BASH, Moves.MILK_DRINK, Moves.BODY_PRESS, Moves.MULTI_ATTACK ], [Species.CHEWTLE]: [ Moves.ICE_FANG, Moves.ACCELEROCK, Moves.SHELL_SMASH, Moves.FISHIOUS_REND ], @@ -446,25 +446,25 @@ export const speciesEggMoves = { [Species.SIZZLIPEDE]: [ Moves.BURNING_BULWARK, Moves.ZING_ZAP, Moves.FIRST_IMPRESSION, Moves.BITTER_BLADE ], [Species.CLOBBOPUS]: [ Moves.STORM_THROW, Moves.JET_PUNCH, Moves.MACH_PUNCH, Moves.SURGING_STRIKES ], [Species.SINISTEA]: [ Moves.SCALD, Moves.TAKE_HEART, Moves.SPARKLY_SWIRL, Moves.MATCHA_GOTCHA ], - [Species.HATENNA]: [ Moves.RECOVER, Moves.MOONBLAST, Moves.BUZZY_BUZZ, Moves.SEARING_SHOT ], + [Species.HATENNA]: [ Moves.RECOVER, Moves.MOONBLAST, Moves.BUZZY_BUZZ, Moves.TORCH_SONG ], [Species.IMPIDIMP]: [ Moves.ENCORE, Moves.PARTING_SHOT, Moves.TOPSY_TURVY, Moves.WICKED_BLOW ], [Species.MILCERY]: [ Moves.MOONBLAST, Moves.CHILLY_RECEPTION, Moves.EARTH_POWER, Moves.GEOMANCY ], [Species.FALINKS]: [ Moves.COMBAT_TORQUE, Moves.PSYSHIELD_BASH, Moves.HEAL_ORDER, Moves.POPULATION_BOMB ], [Species.PINCURCHIN]: [ Moves.TRICK_ROOM, Moves.RISING_VOLTAGE, Moves.STRENGTH_SAP, Moves.THUNDERCLAP ], - [Species.SNOM]: [ Moves.MOONBLAST, Moves.SURF, Moves.EARTH_POWER, Moves.FIERY_DANCE ], + [Species.SNOM]: [ Moves.FROST_BREATH, Moves.HEAL_ORDER, Moves.EARTH_POWER, Moves.SPORE ], [Species.STONJOURNER]: [ Moves.BODY_PRESS, Moves.HELPING_HAND, Moves.ACCELEROCK, Moves.DIAMOND_STORM ], [Species.EISCUE]: [ Moves.TRIPLE_AXEL, Moves.AQUA_STEP, Moves.SHELL_SMASH, Moves.GLACIAL_LANCE ], [Species.INDEEDEE]: [ Moves.MATCHA_GOTCHA, Moves.EXPANDING_FORCE, Moves.MOONBLAST, Moves.REVIVAL_BLESSING ], [Species.MORPEKO]: [ Moves.TRIPLE_AXEL, Moves.OBSTRUCT, Moves.SWORDS_DANCE, Moves.COLLISION_COURSE ], [Species.CUFANT]: [ Moves.LIQUIDATION, Moves.CURSE, Moves.COMBAT_TORQUE, Moves.GIGATON_HAMMER ], - [Species.DRACOZOLT]: [ Moves.TRIPLE_AXEL, Moves.DRAGON_HAMMER, Moves.FIRE_LASH, Moves.DRAGON_DANCE ], + [Species.DRACOZOLT]: [ Moves.TRIPLE_AXEL, Moves.SCALE_SHOT, Moves.FIRE_LASH, Moves.DRAGON_DANCE ], [Species.ARCTOZOLT]: [ Moves.MOUNTAIN_GALE, Moves.AQUA_STEP, Moves.HIGH_HORSEPOWER, Moves.SHIFT_GEAR ], [Species.DRACOVISH]: [ Moves.TRIPLE_AXEL, Moves.DRAGON_HAMMER, Moves.THUNDER_FANG, Moves.DRAGON_DANCE ], [Species.ARCTOVISH]: [ Moves.ICE_FANG, Moves.THUNDER_FANG, Moves.HIGH_HORSEPOWER, Moves.SHIFT_GEAR ], [Species.DURALUDON]: [ Moves.CORE_ENFORCER, Moves.BODY_PRESS, Moves.RECOVER, Moves.TACHYON_CUTTER ], - [Species.DREEPY]: [ Moves.SHADOW_BONE, Moves.POWER_UP_PUNCH, Moves.BLAZING_TORQUE, Moves.GLAIVE_RUSH ], + [Species.DREEPY]: [ Moves.SHADOW_BONE, Moves.NASTY_PLOT, Moves.FIRE_LASH, Moves.COLLISION_COURSE ], [Species.ZACIAN]: [ Moves.MAGICAL_TORQUE, Moves.MIGHTY_CLEAVE, Moves.BITTER_BLADE, Moves.PRECIPICE_BLADES ], - [Species.ZAMAZENTA]: [ Moves.PSYSHIELD_BASH, Moves.BODY_PRESS, Moves.SLACK_OFF, Moves.VICTORY_DANCE ], + [Species.ZAMAZENTA]: [ Moves.BULK_UP, Moves.BODY_PRESS, Moves.SLACK_OFF, Moves.DIAMOND_STORM ], [Species.ETERNATUS]: [ Moves.BODY_PRESS, Moves.NASTY_PLOT, Moves.MALIGNANT_CHAIN, Moves.DRAGON_ENERGY ], [Species.KUBFU]: [ Moves.METEOR_MASH, Moves.DRAIN_PUNCH, Moves.JET_PUNCH, Moves.DRAGON_DANCE ], [Species.ZARUDE]: [ Moves.SAPPY_SEED, Moves.MIGHTY_CLEAVE, Moves.WICKED_BLOW, Moves.VICTORY_DANCE ], @@ -482,7 +482,7 @@ export const speciesEggMoves = { [Species.NYMBLE]: [ Moves.KNOCK_OFF, Moves.FELL_STINGER, Moves.ATTACK_ORDER, Moves.WICKED_BLOW ], [Species.PAWMI]: [ Moves.DRAIN_PUNCH, Moves.ICE_PUNCH, Moves.MACH_PUNCH, Moves.PLASMA_FISTS ], [Species.TANDEMAUS]: [ Moves.BATON_PASS, Moves.THIEF, Moves.SIZZLY_SLIDE, Moves.REVIVAL_BLESSING ], - [Species.FIDOUGH]: [ Moves.WISH, Moves.BODY_PRESS, Moves.SIZZLY_SLIDE, Moves.TIDY_UP ], + [Species.FIDOUGH]: [ Moves.SOFT_BOILED, Moves.HIGH_HORSEPOWER, Moves.SIZZLY_SLIDE, Moves.TIDY_UP ], [Species.SMOLIV]: [ Moves.STRENGTH_SAP, Moves.EARTH_POWER, Moves.CALM_MIND, Moves.BOOMBURST ], [Species.SQUAWKABILLY]: [ Moves.PARTING_SHOT, Moves.EARTHQUAKE, Moves.FLARE_BLITZ, Moves.EXTREME_SPEED ], [Species.NACLI]: [ Moves.BODY_PRESS, Moves.TOXIC, Moves.CURSE, Moves.DIAMOND_STORM ], @@ -492,7 +492,7 @@ export const speciesEggMoves = { [Species.MASCHIFF]: [ Moves.PARTING_SHOT, Moves.CLOSE_COMBAT, Moves.PSYCHIC_FANGS, Moves.NO_RETREAT ], [Species.SHROODLE]: [ Moves.GASTRO_ACID, Moves.PARTING_SHOT, Moves.TOXIC, Moves.SKETCH ], [Species.BRAMBLIN]: [ Moves.TAILWIND, Moves.STRENGTH_SAP, Moves.FLOWER_TRICK, Moves.LAST_RESPECTS ], - [Species.TOEDSCOOL]: [ Moves.STRENGTH_SAP, Moves.TOPSY_TURVY, Moves.PARTING_SHOT, Moves.SAPPY_SEED ], + [Species.TOEDSCOOL]: [ Moves.STRENGTH_SAP, Moves.TOPSY_TURVY, Moves.SAPPY_SEED, Moves.TAIL_GLOW ], [Species.KLAWF]: [ Moves.CRABHAMMER, Moves.SHORE_UP, Moves.MIGHTY_CLEAVE, Moves.SHELL_SMASH ], [Species.CAPSAKID]: [ Moves.STRENGTH_SAP, Moves.APPLE_ACID, Moves.FROST_BREATH, Moves.TORCH_SONG ], [Species.RELLOR]: [ Moves.HEAL_BLOCK, Moves.RECOVER, Moves.HEAT_WAVE, Moves.LUMINA_CRASH ], @@ -509,7 +509,7 @@ export const speciesEggMoves = { [Species.FLAMIGO]: [ Moves.THUNDEROUS_KICK, Moves.TRIPLE_AXEL, Moves.FLOATY_FALL, Moves.VICTORY_DANCE ], [Species.CETODDLE]: [ Moves.MOUNTAIN_GALE, Moves.HIGH_HORSEPOWER, Moves.RECOVER, Moves.DRAGON_DANCE ], [Species.VELUZA]: [ Moves.PSYBLADE, Moves.FLIP_TURN, Moves.ICE_SPINNER, Moves.BITTER_BLADE ], - [Species.DONDOZO]: [ Moves.SOFT_BOILED, Moves.ICE_SPINNER, Moves.TOXIC, Moves.SALT_CURE ], + [Species.DONDOZO]: [ Moves.SOFT_BOILED, Moves.SIZZLY_SLIDE, Moves.TOXIC, Moves.SALT_CURE ], [Species.TATSUGIRI]: [ Moves.ICE_BEAM, Moves.FILLET_AWAY, Moves.CORE_ENFORCER, Moves.STEAM_ERUPTION ], [Species.GREAT_TUSK]: [ Moves.STONE_AXE, Moves.MORNING_SUN, Moves.COLLISION_COURSE, Moves.SHIFT_GEAR ], [Species.SCREAM_TAIL]: [ Moves.TORCH_SONG, Moves.GLITZY_GLOW, Moves.MOONLIGHT, Moves.SPARKLY_SWIRL ], @@ -521,7 +521,7 @@ export const speciesEggMoves = { [Species.IRON_BUNDLE]: [ Moves.EARTH_POWER, Moves.BOUNCY_BUBBLE, Moves.NASTY_PLOT, Moves.STEAM_ERUPTION ], [Species.IRON_HANDS]: [ Moves.DRAIN_PUNCH, Moves.BULK_UP, Moves.PLASMA_FISTS, Moves.ICE_HAMMER ], [Species.IRON_JUGULIS]: [ Moves.FIERY_WRATH, Moves.ROOST, Moves.NASTY_PLOT, Moves.OBLIVION_WING ], - [Species.IRON_MOTH]: [ Moves.EARTH_POWER, Moves.SEARING_SHOT, Moves.QUIVER_DANCE, Moves.MALIGNANT_CHAIN ], + [Species.IRON_MOTH]: [ Moves.EARTH_POWER, Moves.SEARING_SHOT, Moves.MALIGNANT_CHAIN, Moves.QUIVER_DANCE ], [Species.IRON_THORNS]: [ Moves.DIAMOND_STORM, Moves.SHORE_UP, Moves.SHIFT_GEAR, Moves.PLASMA_FISTS ], [Species.FRIGIBAX]: [ Moves.DRAGON_DARTS, Moves.DRAGON_DANCE, Moves.EARTHQUAKE, Moves.GLACIAL_LANCE ], [Species.GIMMIGHOUL]: [ Moves.HAPPY_HOUR, Moves.AURA_SPHERE, Moves.SURF, Moves.ASTRAL_BARRAGE ], @@ -540,7 +540,7 @@ export const speciesEggMoves = { [Species.MUNKIDORI]: [ Moves.PSYSTRIKE, Moves.HEAT_WAVE, Moves.EARTH_POWER, Moves.MALIGNANT_CHAIN ], [Species.FEZANDIPITI]: [ Moves.BARB_BARRAGE, Moves.VICTORY_DANCE, Moves.TRIPLE_AXEL, Moves.MAGICAL_TORQUE ], [Species.OGERPON]: [ Moves.FLOWER_TRICK, Moves.BONEMERANG, Moves.TRIPLE_AXEL, Moves.GIGATON_HAMMER ], - [Species.GOUGING_FIRE]: [ Moves.SUPERCELL_SLAM, Moves.BULK_UP, Moves.SACRED_FIRE, Moves.GLAIVE_RUSH ], + [Species.GOUGING_FIRE]: [ Moves.EXTREME_SPEED, Moves.BULK_UP, Moves.SACRED_FIRE, Moves.GLAIVE_RUSH ], [Species.RAGING_BOLT]: [ Moves.NASTY_PLOT, Moves.FLAMETHROWER, Moves.MORNING_SUN, Moves.ELECTRO_DRIFT ], [Species.IRON_BOULDER]: [ Moves.PSYBLADE, Moves.KOWTOW_CLEAVE, Moves.STONE_AXE, Moves.BITTER_BLADE ], [Species.IRON_CROWN]: [ Moves.NASTY_PLOT, Moves.SECRET_SWORD, Moves.PSYSTRIKE, Moves.ELECTRO_DRIFT ], @@ -566,7 +566,7 @@ export const speciesEggMoves = { [Species.GALAR_DARUMAKA]: [ Moves.ICE_SPINNER, Moves.ENDURE, Moves.DRAIN_PUNCH, Moves.V_CREATE ], [Species.GALAR_YAMASK]: [ Moves.STRENGTH_SAP, Moves.DIRE_CLAW, Moves.THOUSAND_WAVES, Moves.SPECTRAL_THIEF ], [Species.GALAR_STUNFISK]: [ Moves.SPIKY_SHIELD, Moves.THOUSAND_ARROWS, Moves.STRENGTH_SAP, Moves.DOUBLE_IRON_BASH ], - [Species.HISUI_GROWLITHE]: [ Moves.WOOD_HAMMER, Moves.HEAD_SMASH, Moves.MORNING_SUN, Moves.DRAGON_DANCE ], + [Species.HISUI_GROWLITHE]: [ Moves.WAVE_CRASH, Moves.HEAD_SMASH, Moves.VOLT_TACKLE, Moves.DRAGON_DANCE ], [Species.HISUI_VOLTORB]: [ Moves.FROST_BREATH, Moves.NASTY_PLOT, Moves.APPLE_ACID, Moves.ELECTRO_DRIFT ], [Species.HISUI_QWILFISH]: [ Moves.CEASELESS_EDGE, Moves.KNOCK_OFF, Moves.STRENGTH_SAP, Moves.FISHIOUS_REND ], [Species.HISUI_SNEASEL]: [ Moves.THUNDEROUS_KICK, Moves.KNOCK_OFF, Moves.TRIPLE_AXEL, Moves.VICTORY_DANCE ], diff --git a/src/data/move.ts b/src/data/move.ts index 5320501cc0d..d4b2b2f0e75 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -341,7 +341,8 @@ export default class Move implements Localizable { * @returns `true` if the move can bypass the target's Substitute; `false` otherwise. */ hitsSubstitute(user: Pokemon, target: Pokemon | null): boolean { - if (this.moveTarget === MoveTarget.USER || !target?.getTag(BattlerTagType.SUBSTITUTE)) { + if ([MoveTarget.USER, MoveTarget.USER_SIDE, MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES].includes(this.moveTarget) + || !target?.getTag(BattlerTagType.SUBSTITUTE)) { return false; } @@ -398,220 +399,202 @@ export default class Move implements Localizable { /** * Sets the {@linkcode MoveFlags.MAKES_CONTACT} flag for the calling Move - * @param makesContact The value (boolean) to set the flag to + * @param setFlag Default `true`, set to `false` if the move doesn't make contact + * @see {@linkcode Abilities.STATIC} * @returns The {@linkcode Move} that called this function */ - makesContact(makesContact: boolean = true): this { - this.setFlag(MoveFlags.MAKES_CONTACT, makesContact); + makesContact(setFlag: boolean = true): this { + this.setFlag(MoveFlags.MAKES_CONTACT, setFlag); return this; } /** * Sets the {@linkcode MoveFlags.IGNORE_PROTECT} flag for the calling Move - * @param ignoresProtect The value (boolean) to set the flag to - * example: @see {@linkcode Moves.CURSE} + * @see {@linkcode Moves.CURSE} * @returns The {@linkcode Move} that called this function */ - ignoresProtect(ignoresProtect: boolean = true): this { - this.setFlag(MoveFlags.IGNORE_PROTECT, ignoresProtect); + ignoresProtect(): this { + this.setFlag(MoveFlags.IGNORE_PROTECT, true); return this; } /** * Sets the {@linkcode MoveFlags.IGNORE_VIRTUAL} flag for the calling Move - * @param ignoresVirtual The value (boolean) to set the flag to - * example: @see {@linkcode Moves.NATURE_POWER} + * @see {@linkcode Moves.NATURE_POWER} * @returns The {@linkcode Move} that called this function */ - ignoresVirtual(ignoresVirtual: boolean = true): this { - this.setFlag(MoveFlags.IGNORE_VIRTUAL, ignoresVirtual); + ignoresVirtual(): this { + this.setFlag(MoveFlags.IGNORE_VIRTUAL, true); return this; } /** * Sets the {@linkcode MoveFlags.SOUND_BASED} flag for the calling Move - * @param soundBased The value (boolean) to set the flag to - * example: @see {@linkcode Moves.UPROAR} + * @see {@linkcode Moves.UPROAR} * @returns The {@linkcode Move} that called this function */ - soundBased(soundBased: boolean = true): this { - this.setFlag(MoveFlags.SOUND_BASED, soundBased); + soundBased(): this { + this.setFlag(MoveFlags.SOUND_BASED, true); return this; } /** * Sets the {@linkcode MoveFlags.HIDE_USER} flag for the calling Move - * @param hidesUser The value (boolean) to set the flag to - * example: @see {@linkcode Moves.TELEPORT} + * @see {@linkcode Moves.TELEPORT} * @returns The {@linkcode Move} that called this function */ - hidesUser(hidesUser: boolean = true): this { - this.setFlag(MoveFlags.HIDE_USER, hidesUser); + hidesUser(): this { + this.setFlag(MoveFlags.HIDE_USER, true); return this; } /** * Sets the {@linkcode MoveFlags.HIDE_TARGET} flag for the calling Move - * @param hidesTarget The value (boolean) to set the flag to - * example: @see {@linkcode Moves.WHIRLWIND} + * @see {@linkcode Moves.WHIRLWIND} * @returns The {@linkcode Move} that called this function */ - hidesTarget(hidesTarget: boolean = true): this { - this.setFlag(MoveFlags.HIDE_TARGET, hidesTarget); + hidesTarget(): this { + this.setFlag(MoveFlags.HIDE_TARGET, true); return this; } /** * Sets the {@linkcode MoveFlags.BITING_MOVE} flag for the calling Move - * @param bitingMove The value (boolean) to set the flag to - * example: @see {@linkcode Moves.BITE} + * @see {@linkcode Moves.BITE} * @returns The {@linkcode Move} that called this function */ - bitingMove(bitingMove: boolean = true): this { - this.setFlag(MoveFlags.BITING_MOVE, bitingMove); + bitingMove(): this { + this.setFlag(MoveFlags.BITING_MOVE, true); return this; } /** * Sets the {@linkcode MoveFlags.PULSE_MOVE} flag for the calling Move - * @param pulseMove The value (boolean) to set the flag to - * example: @see {@linkcode Moves.WATER_PULSE} + * @see {@linkcode Moves.WATER_PULSE} * @returns The {@linkcode Move} that called this function */ - pulseMove(pulseMove: boolean = true): this { - this.setFlag(MoveFlags.PULSE_MOVE, pulseMove); + pulseMove(): this { + this.setFlag(MoveFlags.PULSE_MOVE, true); return this; } /** * Sets the {@linkcode MoveFlags.PUNCHING_MOVE} flag for the calling Move - * @param punchingMove The value (boolean) to set the flag to - * example: @see {@linkcode Moves.DRAIN_PUNCH} + * @see {@linkcode Moves.DRAIN_PUNCH} * @returns The {@linkcode Move} that called this function */ - punchingMove(punchingMove: boolean = true): this { - this.setFlag(MoveFlags.PUNCHING_MOVE, punchingMove); + punchingMove(): this { + this.setFlag(MoveFlags.PUNCHING_MOVE, true); return this; } /** * Sets the {@linkcode MoveFlags.SLICING_MOVE} flag for the calling Move - * @param slicingMove The value (boolean) to set the flag to - * example: @see {@linkcode Moves.X_SCISSOR} + * @see {@linkcode Moves.X_SCISSOR} * @returns The {@linkcode Move} that called this function */ - slicingMove(slicingMove: boolean = true): this { - this.setFlag(MoveFlags.SLICING_MOVE, slicingMove); + slicingMove(): this { + this.setFlag(MoveFlags.SLICING_MOVE, true); return this; } /** * Sets the {@linkcode MoveFlags.RECKLESS_MOVE} flag for the calling Move * @see {@linkcode Abilities.RECKLESS} - * @param recklessMove The value to set the flag to * @returns The {@linkcode Move} that called this function */ - recklessMove(recklessMove: boolean = true): this { - this.setFlag(MoveFlags.RECKLESS_MOVE, recklessMove); + recklessMove(): this { + this.setFlag(MoveFlags.RECKLESS_MOVE, true); return this; } /** * Sets the {@linkcode MoveFlags.BALLBOMB_MOVE} flag for the calling Move - * @param ballBombMove The value (boolean) to set the flag to - * example: @see {@linkcode Moves.ELECTRO_BALL} + * @see {@linkcode Moves.ELECTRO_BALL} * @returns The {@linkcode Move} that called this function */ - ballBombMove(ballBombMove: boolean = true): this { - this.setFlag(MoveFlags.BALLBOMB_MOVE, ballBombMove); + ballBombMove(): this { + this.setFlag(MoveFlags.BALLBOMB_MOVE, true); return this; } /** * Sets the {@linkcode MoveFlags.POWDER_MOVE} flag for the calling Move - * @param powderMove The value (boolean) to set the flag to - * example: @see {@linkcode Moves.STUN_SPORE} + * @see {@linkcode Moves.STUN_SPORE} * @returns The {@linkcode Move} that called this function */ - powderMove(powderMove: boolean = true): this { - this.setFlag(MoveFlags.POWDER_MOVE, powderMove); + powderMove(): this { + this.setFlag(MoveFlags.POWDER_MOVE, true); return this; } /** * Sets the {@linkcode MoveFlags.DANCE_MOVE} flag for the calling Move - * @param danceMove The value (boolean) to set the flag to - * example: @see {@linkcode Moves.PETAL_DANCE} + * @see {@linkcode Moves.PETAL_DANCE} * @returns The {@linkcode Move} that called this function */ - danceMove(danceMove: boolean = true): this { - this.setFlag(MoveFlags.DANCE_MOVE, danceMove); + danceMove(): this { + this.setFlag(MoveFlags.DANCE_MOVE, true); return this; } /** * Sets the {@linkcode MoveFlags.WIND_MOVE} flag for the calling Move - * @param windMove The value (boolean) to set the flag to - * example: @see {@linkcode Moves.HURRICANE} + * @see {@linkcode Moves.HURRICANE} * @returns The {@linkcode Move} that called this function */ - windMove(windMove: boolean = true): this { - this.setFlag(MoveFlags.WIND_MOVE, windMove); + windMove(): this { + this.setFlag(MoveFlags.WIND_MOVE, true); return this; } /** * Sets the {@linkcode MoveFlags.TRIAGE_MOVE} flag for the calling Move - * @param triageMove The value (boolean) to set the flag to - * example: @see {@linkcode Moves.ABSORB} + * @see {@linkcode Moves.ABSORB} * @returns The {@linkcode Move} that called this function */ - triageMove(triageMove: boolean = true): this { - this.setFlag(MoveFlags.TRIAGE_MOVE, triageMove); + triageMove(): this { + this.setFlag(MoveFlags.TRIAGE_MOVE, true); return this; } /** * Sets the {@linkcode MoveFlags.IGNORE_ABILITIES} flag for the calling Move - * @param ignoresAbilities sThe value (boolean) to set the flag to - * example: @see {@linkcode Moves.SUNSTEEL_STRIKE} + * @see {@linkcode Moves.SUNSTEEL_STRIKE} * @returns The {@linkcode Move} that called this function */ - ignoresAbilities(ignoresAbilities: boolean = true): this { - this.setFlag(MoveFlags.IGNORE_ABILITIES, ignoresAbilities); + ignoresAbilities(): this { + this.setFlag(MoveFlags.IGNORE_ABILITIES, true); return this; } /** * Sets the {@linkcode MoveFlags.CHECK_ALL_HITS} flag for the calling Move - * @param checkAllHits The value (boolean) to set the flag to - * example: @see {@linkcode Moves.TRIPLE_AXEL} + * @see {@linkcode Moves.TRIPLE_AXEL} * @returns The {@linkcode Move} that called this function */ - checkAllHits(checkAllHits: boolean = true): this { - this.setFlag(MoveFlags.CHECK_ALL_HITS, checkAllHits); + checkAllHits(): this { + this.setFlag(MoveFlags.CHECK_ALL_HITS, true); return this; } /** * Sets the {@linkcode MoveFlags.IGNORE_SUBSTITUTE} flag for the calling Move - * @param ignoresSubstitute The value (boolean) to set the flag to - * example: @see {@linkcode Moves.WHIRLWIND} + * @see {@linkcode Moves.WHIRLWIND} * @returns The {@linkcode Move} that called this function */ - ignoresSubstitute(ignoresSubstitute: boolean = true): this { - this.setFlag(MoveFlags.IGNORE_SUBSTITUTE, ignoresSubstitute); + ignoresSubstitute(): this { + this.setFlag(MoveFlags.IGNORE_SUBSTITUTE, true); return this; } /** * Sets the {@linkcode MoveFlags.REDIRECT_COUNTER} flag for the calling Move - * @param redirectCounter The value (boolean) to set the flag to - * example: @see {@linkcode Moves.METAL_BURST} + * @see {@linkcode Moves.METAL_BURST} * @returns The {@linkcode Move} that called this function */ - redirectCounter(redirectCounter: boolean = true): this { - this.setFlag(MoveFlags.REDIRECT_COUNTER, redirectCounter); + redirectCounter(): this { + this.setFlag(MoveFlags.REDIRECT_COUNTER, true); return this; } @@ -4544,6 +4527,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr { case BattlerTagType.DROWSY: case BattlerTagType.DISABLED: case BattlerTagType.HEAL_BLOCK: + case BattlerTagType.RECEIVE_DOUBLE_DAMAGE: return -5; case BattlerTagType.SEEDED: case BattlerTagType.SALT_CURED: @@ -4564,6 +4548,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr { case BattlerTagType.ENCORE: return -2; case BattlerTagType.MINIMIZED: + case BattlerTagType.ALWAYS_GET_HIT: return 0; case BattlerTagType.INGRAIN: case BattlerTagType.IGNORE_ACCURACY: @@ -5226,6 +5211,9 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { */ const switchOutTarget = this.selfSwitch ? user : target; if (switchOutTarget instanceof PlayerPokemon) { + if (switchOutTarget.scene.getParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) { + return false; + } switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); if (switchOutTarget.hp > 0) { @@ -5234,6 +5222,9 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { } return false; } else if (user.scene.currentBattle.battleType !== BattleType.WILD) { + if (switchOutTarget.scene.getEnemyParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) { + return false; + } // Switch out logic for trainer battles switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); @@ -5244,6 +5235,9 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { false, false), MoveEndPhase); } } else { + if (user.scene.currentBattle.waveIndex % 10 === 0) { + return false; + } // Switch out logic for everything else (eg: WILD battles) switchOutTarget.leaveField(false); @@ -5290,6 +5284,11 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { return false; } + if (!player && user.scene.currentBattle.isBattleMysteryEncounter() && !user.scene.currentBattle.mysteryEncounter?.fleeAllowed) { + // Don't allow wild opponents to be force switched during MEs with flee disabled + return false; + } + const blockedByAbility = new Utils.BooleanHolder(false); applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility); return !blockedByAbility.value; @@ -6575,7 +6574,7 @@ export class MoveCondition { export class FirstMoveCondition extends MoveCondition { constructor() { - super((user, target, move) => user.battleSummonData?.turnCount === 1); + super((user, target, move) => user.battleSummonData?.waveTurnCount === 1); } getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { @@ -7252,7 +7251,7 @@ export function initMoves() { new StatusMove(Moves.CURSE, Type.GHOST, -1, 10, -1, 0, 2) .attr(CurseAttr) .ignoresSubstitute() - .ignoresProtect(true) + .ignoresProtect() .target(MoveTarget.CURSE), new AttackMove(Moves.FLAIL, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2) .attr(LowHpPowerAttr), @@ -8080,7 +8079,7 @@ export function initMoves() { .makesContact(false), new SelfStatusMove(Moves.DEFEND_ORDER, Type.BUG, -1, 10, -1, 0, 4) .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], 1, true), - new SelfStatusMove(Moves.HEAL_ORDER, Type.BUG, -1, 10, -1, 0, 4) + new SelfStatusMove(Moves.HEAL_ORDER, Type.BUG, -1, 5, -1, 0, 4) .attr(HealAttr, 0.5) .triageMove(), new AttackMove(Moves.HEAD_SMASH, Type.ROCK, MoveCategory.PHYSICAL, 150, 80, 5, -1, 0, 4) @@ -8892,8 +8891,8 @@ export function initMoves() { .attr(HalfSacrificialAttr) .target(MoveTarget.ALL_NEAR_OTHERS), new AttackMove(Moves.PLASMA_FISTS, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 100, 15, -1, 0, 7) - .punchingMove() - .partial(), + .attr(AddArenaTagAttr, ArenaTagType.PLASMA_FISTS, 1) + .punchingMove(), new AttackMove(Moves.PHOTON_GEYSER, Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7) .attr(PhotonGeyserCategoryAttr) .ignoresAbilities() @@ -8923,7 +8922,7 @@ export function initMoves() { .partial() .ignoresVirtual(), /* End Unused */ - new AttackMove(Moves.ZIPPY_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 50, 100, 15, 100, 2, 7) //LGPE Implementation + new AttackMove(Moves.ZIPPY_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 50, 100, 15, -1, 2, 7) //LGPE Implementation .attr(CritOnlyAttr), new AttackMove(Moves.SPLISHY_SPLASH, Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, 30, 0, 7) .attr(StatusEffectAttr, StatusEffect.PARALYSIS) @@ -9608,9 +9607,8 @@ export function initMoves() { .target(MoveTarget.ALL_NEAR_ENEMIES) .triageMove(), new AttackMove(Moves.SYRUP_BOMB, Type.GRASS, MoveCategory.SPECIAL, 60, 85, 10, -1, 0, 9) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1) //Temporary - .ballBombMove() - .partial(), + .attr(AddBattlerTagAttr, BattlerTagType.SYRUP_BOMB, false, false, 3) + .ballBombMove(), new AttackMove(Moves.IVY_CUDGEL, Type.GRASS, MoveCategory.PHYSICAL, 100, 100, 10, -1, 0, 9) .attr(IvyCudgelTypeAttr) .attr(HighCritAttr) diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index c0e9f4e51a0..0b32f857d75 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -24,6 +24,7 @@ import { BerryType } from "#enums/berry-type"; 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 i18next from "i18next"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounters/absoluteAvarice"; @@ -38,6 +39,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = .withEncounterTier(MysteryEncounterTier.GREAT) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withSceneRequirement(new PersistentModifierRequirement("BerryModifier", 4)) // Must have at least 4 berries to spawn + .withFleeAllowed(false) .withIntroSpriteConfigs([ { // This sprite has the shadow @@ -262,15 +264,13 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = // Provides 1x Reviver Seed to each party member at end of battle const revSeed = generateModifierType(scene, modifierTypes.REVIVER_SEED); + encounter.setDialogueToken("foodReward", revSeed?.name ?? i18next.t("modifierType:ModifierType.REVIVER_SEED.name")); const givePartyPokemonReviverSeeds = () => { const party = scene.getParty(); party.forEach(p => { const heldItems = p.getHeldItems(); if (revSeed && !heldItems.some(item => item instanceof PokemonInstantReviveModifier)) { const seedModifier = revSeed.newModifier(p); - if (seedModifier) { - encounter.setDialogueToken("foodReward", seedModifier.type.name); - } scene.addModifier(seedModifier, false, false, false, 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 4309689bc42..1de83374db2 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 @@ -70,7 +70,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter!; const pokemon = getHighestStatTotalPlayerPokemon(scene, true, true); - const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId(true); + const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId(); const starterValue: number = speciesStarters[baseSpecies] ?? 1; const multiplier = Math.max(MONEY_MAXIMUM_MULTIPLIER / 10 * starterValue, MONEY_MINIMUM_MULTIPLIER); const price = scene.getWaveMoneyAmount(multiplier); diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index 823bed21ffa..55c66642944 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -46,6 +46,7 @@ export const BerriesAboundEncounter: MysteryEncounter = .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withCatchAllowed(true) .withHideWildIntroMessage(true) + .withFleeAllowed(false) .withIntroSpriteConfigs([]) // Set in onInit() .withIntroDialogue([ { 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 384a3806804..6847ab8b832 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -49,6 +49,7 @@ import MoveInfoOverlay from "#app/ui/move-info-overlay"; import { allMoves } from "#app/data/move"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/bugTypeSuperfan"; @@ -177,9 +178,14 @@ const MISC_TUTOR_MOVES = [ Moves.U_TURN ]; +/** + * Wave breakpoints that determine how strong to make the Bug-Type Superfan's team + */ +const WAVE_LEVEL_BREAKPOINTS = [30, 50, 70, 100, 120, 140, 160]; + /** * Bug Type Superfan encounter. - * @see {@link https://github.com/pagefaultgames/pokerogue/issues/3810 | GitHub Issue #3810} + * @see {@link https://github.com/pagefaultgames/pokerogue/issues/3820 | GitHub Issue #3820} * @see For biome requirements check {@linkcode mysteryEncountersByBiome} */ export const BugTypeSuperfanEncounter: MysteryEncounter = @@ -216,11 +222,46 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = female: true, }); + let beedrillKeys: { spriteKey: string, fileRoot: string }, butterfreeKeys: { spriteKey: string, fileRoot: string }; + if (scene.currentBattle.waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) { + beedrillKeys = getSpriteKeysFromSpecies(Species.BEEDRILL, false); + butterfreeKeys = getSpriteKeysFromSpecies(Species.BUTTERFREE, false); + } else { + // Mega Beedrill/Gmax Butterfree + beedrillKeys = getSpriteKeysFromSpecies(Species.BEEDRILL, false, 1); + butterfreeKeys = getSpriteKeysFromSpecies(Species.BUTTERFREE, false, 1); + } + encounter.spriteConfigs = [ + { + spriteKey: beedrillKeys.spriteKey, + fileRoot: beedrillKeys.fileRoot, + hasShadow: true, + repeat: true, + isPokemon: true, + x: -30, + tint: 0.15, + y: -4, + yShadow: -4 + }, + { + spriteKey: butterfreeKeys.spriteKey, + fileRoot: butterfreeKeys.fileRoot, + hasShadow: true, + repeat: true, + isPokemon: true, + x: 30, + tint: 0.15, + y: -4, + yShadow: -4 + }, { spriteKey: spriteKey, fileRoot: "trainer", hasShadow: true, + x: 4, + y: 7, + yShadow: 7 }, ]; @@ -330,6 +371,10 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = if (formChangeModifier) { specialOptions.push(formChangeModifier); } + const rareFormChangeModifier = generateModifierTypeOption(scene, modifierTypes.RARE_FORM_CHANGE_ITEM); + if (rareFormChangeModifier) { + specialOptions.push(rareFormChangeModifier); + } if (specialOptions.length > 0) { modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]); } @@ -445,29 +490,29 @@ function getTrainerConfigForWave(waveIndex: number) { const config = trainerConfigs[TrainerType.BUG_TYPE_SUPERFAN].clone(); config.name = i18next.t("trainerNames:bug_type_superfan"); - const pool3Copy = POOL_3_POKEMON.slice(0); - randSeedShuffle(pool3Copy); + let pool3Copy = POOL_3_POKEMON.slice(0); + pool3Copy = randSeedShuffle(pool3Copy); const pool3Mon = pool3Copy.pop()!; - if (waveIndex < 30) { + if (waveIndex < WAVE_LEVEL_BREAKPOINTS[0]) { // Use default template (2 AVG) config .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true)); - } else if (waveIndex < 50) { + } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[1]) { config .setPartyTemplates(new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE)) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true)); - } else if (waveIndex < 70) { + } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[2]) { config .setPartyTemplates(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE)) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true)) .setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true)); - } else if (waveIndex < 100) { + } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) { config .setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE)) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true)) @@ -475,7 +520,7 @@ function getTrainerConfigForWave(waveIndex: number) { .setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true)) .setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true)) .setPartyMemberFunc(4, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true)); - } else if (waveIndex < 120) { + } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[4]) { config .setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE)) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => { @@ -497,8 +542,8 @@ function getTrainerConfigForWave(waveIndex: number) { p.generateName(); } })); - } else if (waveIndex < 140) { - randSeedShuffle(pool3Copy); + } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[5]) { + pool3Copy = randSeedShuffle(pool3Copy); const pool3Mon2 = pool3Copy.pop()!; config .setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE)) @@ -527,7 +572,7 @@ function getTrainerConfigForWave(waveIndex: number) { p.generateName(); } })); - } else if (waveIndex < 160) { + } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[6]) { config .setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(1, PartyMemberStrength.STRONG))) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => { @@ -550,6 +595,8 @@ function getTrainerConfigForWave(waveIndex: number) { })) .setPartyMemberFunc(4, getRandomPartyMemberFunc(POOL_4_POKEMON, TrainerSlot.TRAINER, true)); } else { + pool3Copy = randSeedShuffle(pool3Copy); + const pool3Mon2 = pool3Copy.pop()!; config .setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(1, PartyMemberStrength.STRONG))) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => { @@ -571,9 +618,9 @@ function getTrainerConfigForWave(waveIndex: number) { p.generateName(); } })) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => { - if (!isNullOrUndefined(pool3Mon.formIndex)) { - p.formIndex = pool3Mon.formIndex; + .setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon2.species], TrainerSlot.TRAINER, true, p => { + if (!isNullOrUndefined(pool3Mon2.formIndex)) { + p.formIndex = pool3Mon2.formIndex; p.generateAndPopulateMoveset(); p.generateName(); } @@ -629,27 +676,7 @@ function doBugTypeMoveTutor(scene: BattleScene): Promise { moveInfoOverlay.setVisible(false); } - // TODO: add menu to confirm player doesn't want to teach a move - // while (!result && !forceExit) { - // // Didn't teach a move, ask the player to confirm they don't want to teach a move - // await showEncounterDialogue(scene, `${namespace}:confirm_no_teach`, `${namespace}:speaker`); - // const confirm = await new Promise(confirmResolve => { - // scene.ui.setMode(Mode.CONFIRM, () => confirmResolve(true), () => confirmResolve(false)); - // }); - // scene.ui.clearText(); - // await scene.ui.setMode(Mode.MESSAGE); - // if (confirm) { - // // No teach, break out of loop - // forceExit = true; - // } else { - // // Re-show learn menu - // result = await selectOptionThenPokemon(scene, optionSelectItems, `${namespace}:teach_move_prompt`, undefined, onHoverOverCancel); - // if (!result) { - // moveInfoOverlay.active = false; - // moveInfoOverlay.setVisible(false); - // } - // } - // } + // TODO: add menu to confirm player doesn't want to teach a move? // Option select complete, handle if they are learning a move if (result && result.selectedOptionIndex < moveOptions.length) { diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index 0d085f016ee..e2bf3bf79fe 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -336,8 +336,8 @@ export const ClowningAroundEncounter: MysteryEncounter = .filter(move => move && !originalTypes.includes(move.getMove().type) && move.getMove().category !== MoveCategory.STATUS) .map(move => move!.getMove().type); if (priorityTypes?.length > 0) { - priorityTypes = [...new Set(priorityTypes)]; - randSeedShuffle(priorityTypes); + priorityTypes = [...new Set(priorityTypes)].sort(); + priorityTypes = randSeedShuffle(priorityTypes); } const newTypes = [originalTypes[0]]; @@ -494,9 +494,13 @@ function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItem } for (let i = 0; i < numItems; i++) { + if (pool.length === 0) { + // Stop generating new items if somehow runs out of items to spawn + return; + } const randIndex = randSeedInt(pool.length); const newItemType = pool[randIndex]; - let newMod; + let newMod: PokemonHeldItemModifierType; if (tier === "Berries") { newMod = generateModifierType(scene, modifierTypes.BERRY, [newItemType[0]]) as PokemonHeldItemModifierType; } else { diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index af71afb548e..655cc3e937e 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -90,6 +90,7 @@ export const DancingLessonsEncounter: MysteryEncounter = .withHideWildIntroMessage(true) .withAutoHideIntroVisuals(false) .withCatchAllowed(true) + .withFleeAllowed(false) .withOnVisualsStart((scene: BattleScene) => { const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getParty()[0]); danceAnim.play(scene); diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index b7698d6e4a1..cfb87a9e862 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -46,6 +46,7 @@ export const FieryFalloutEncounter: MysteryEncounter = .withIntroSpriteConfigs([]) // Set in onInit() .withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT) .withAutoHideIntroVisuals(false) + .withFleeAllowed(false) .withIntroDialogue([ { text: `${namespace}:intro`, 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 f4352b256cc..aa3bace67f0 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -44,6 +44,7 @@ export const FightOrFlightEncounter: MysteryEncounter = .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withCatchAllowed(true) .withHideWildIntroMessage(true) + .withFleeAllowed(false) .withIntroSpriteConfigs([]) // Set in onInit() .withIntroDialogue([ { 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 38c7405c48b..c690faf28b7 100644 --- a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts +++ b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts @@ -44,6 +44,7 @@ export const FunAndGamesEncounter: MysteryEncounter = .withSkipEnemyBattleTurns(true) // Will skip COMMAND selection menu and go straight to FIGHT (move select) menu .withSkipToFightInput(true) + .withFleeAllowed(false) .withIntroSpriteConfigs([ { spriteKey: "fun_and_games_game", 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 6e557ca7619..cc66bdfb4c9 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -159,7 +159,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = return true; }, onHover: () => { - const formName = tradePokemon.species.forms && tradePokemon.species.forms.length > tradePokemon.formIndex ? tradePokemon.species.forms[pokemon.formIndex].formName : null; + const formName = tradePokemon.species.forms && tradePokemon.species.forms.length > tradePokemon.formIndex ? tradePokemon.species.forms[tradePokemon.formIndex].formName : null; const line1 = i18next.t("pokemonInfoContainer:ability") + " " + tradePokemon.getAbility().name + (tradePokemon.getGender() !== Gender.GENDERLESS ? " | " + i18next.t("pokemonInfoContainer:gender") + " " + getGenderSymbol(tradePokemon.getGender()) : ""); const line2 = i18next.t("pokemonInfoContainer:nature") + " " + getNatureName(tradePokemon.getNature()) + (formName ? " | " + i18next.t("pokemonInfoContainer:form") + " " + formName : ""); showEncounterText(scene, `${line1}\n${line2}`, 0, 0, false); diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index e1c6b6c35cf..5abd4839c0c 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -34,6 +34,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withCatchAllowed(true) .withHideWildIntroMessage(true) + .withFleeAllowed(false) .withIntroSpriteConfigs([ { spriteKey: Species.SNORLAX.toString(), diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts index a01f9dfeed3..f685bdae2a1 100644 --- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts +++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts @@ -42,6 +42,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter = .withSceneRequirement(new MoneyRequirement(0, MONEY_COST_MULTIPLIER)) // Must be able to pay teleport cost .withAutoHideIntroVisuals(false) .withCatchAllowed(true) + .withFleeAllowed(false) .withIntroSpriteConfigs([ { spriteKey: "teleporting_hijinks_teleporter", 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 index 1c200a306ac..2b5ae89dc6b 100644 --- a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts @@ -437,8 +437,7 @@ function getPartyConfig(scene: BattleScene): EnemyPartyConfig { } function getSpeciesFromPool(speciesPool: (Species | BreederSpeciesEvolution)[][], waveIndex: number): Species { - const poolCopy = speciesPool.slice(0); - randSeedShuffle(poolCopy); + const poolCopy = randSeedShuffle(speciesPool.slice(0)); const speciesEvolutions = poolCopy.pop()!.slice(0); let speciesObject = speciesEvolutions.pop()!; while (speciesObject instanceof BreederSpeciesEvolution && speciesObject.evolution > waveIndex) { @@ -452,7 +451,7 @@ function calculateEggRewardsForPokemon(pokemon: PlayerPokemon): [number, number] // 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); + const rootSpecies = pokemon.species.getRootSpeciesId(); 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)) { @@ -559,6 +558,10 @@ function onGameOver(scene: BattleScene) { // Revert BGM scene.playBgm(scene.arena.bgm); + // Clear any leftover battle phases + scene.clearPhaseQueue(); + scene.clearPhaseQueueSplice(); + // Return enemy Pokemon const pokemon = scene.getEnemyPokemon(); if (pokemon) { 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 8be3cdaee1e..bd99c8babf7 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -39,6 +39,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = .withMaxAllowedEncounters(1) .withHideWildIntroMessage(true) .withAutoHideIntroVisuals(false) + .withFleeAllowed(false) .withIntroSpriteConfigs([ { spriteKey: "berry_juice", diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index 9dde24103a4..107e73c2fb7 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -101,7 +101,7 @@ export const TrainingSessionEncounter: MysteryEncounter = encounter.setDialogueToken("stat1", "-"); encounter.setDialogueToken("stat2", "-"); // Add the pokemon back to party with IV boost - const ivIndexes: any[] = []; + let ivIndexes: any[] = []; playerPokemon.ivs.forEach((iv, index) => { if (iv < 31) { ivIndexes.push({ iv: iv, index: index }); @@ -117,7 +117,7 @@ export const TrainingSessionEncounter: MysteryEncounter = // 25-27 starting IV caps in 2 encounters let improvedCount = 0; while (ivIndexes.length > 0 && improvedCount < 2) { - randSeedShuffle(ivIndexes); + ivIndexes = randSeedShuffle(ivIndexes); const ivToChange = ivIndexes.pop(); let newVal = ivToChange.iv; if (improvedCount === 0) { @@ -145,10 +145,7 @@ export const TrainingSessionEncounter: MysteryEncounter = if (improvedCount > 0) { playerPokemon.calculateStats(); - scene.gameData.updateSpeciesDexIvs( - playerPokemon.species.getRootSpeciesId(true), - playerPokemon.ivs - ); + scene.gameData.updateSpeciesDexIvs(playerPokemon.species.getRootSpeciesId(true), playerPokemon.ivs); scene.gameData.setPokemonCaught(playerPokemon, false); } @@ -322,27 +319,23 @@ export const TrainingSessionEncounter: MysteryEncounter = queueEncounterMessage(scene, `${namespace}:option.3.finished`); // Add the pokemon back to party with ability change const abilityIndex = encounter.misc.abilityIndex; + if (!!playerPokemon.getFusionSpeciesForm()) { playerPokemon.fusionAbilityIndex = abilityIndex; - if (!isNullOrUndefined(playerPokemon.fusionSpecies?.speciesId) && speciesStarters.hasOwnProperty(playerPokemon.fusionSpecies.speciesId)) { - scene.gameData.starterData[playerPokemon.fusionSpecies.speciesId] - .abilityAttr |= - abilityIndex !== 1 || playerPokemon.fusionSpecies.ability2 - ? Math.pow(2, playerPokemon.fusionAbilityIndex) - : AbilityAttr.ABILITY_HIDDEN; + + // Only update the fusion's dex data if the Pokemon is already caught in dex (ignore rentals) + const rootFusionSpecies = playerPokemon.fusionSpecies?.getRootSpeciesId(); + if (!isNullOrUndefined(rootFusionSpecies) + && speciesStarters.hasOwnProperty(rootFusionSpecies) + && !!scene.gameData.dexData[rootFusionSpecies].caughtAttr) { + scene.gameData.starterData[rootFusionSpecies].abilityAttr |= playerPokemon.fusionAbilityIndex !== 1 || playerPokemon.fusionSpecies?.ability2 + ? 1 << playerPokemon.fusionAbilityIndex + : AbilityAttr.ABILITY_HIDDEN; } } else { playerPokemon.abilityIndex = abilityIndex; - if (speciesStarters.hasOwnProperty(playerPokemon.species.speciesId)) { - scene.gameData.starterData[playerPokemon.species.speciesId] - .abilityAttr |= - abilityIndex !== 1 || playerPokemon.species.ability2 - ? Math.pow(2, playerPokemon.abilityIndex) - : AbilityAttr.ABILITY_HIDDEN; - } } - playerPokemon.getAbility(); playerPokemon.calculateStats(); scene.gameData.setPokemonCaught(playerPokemon, false); 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 6f90040b08d..9673924e6d1 100644 --- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts +++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts @@ -36,6 +36,7 @@ export const TrashToTreasureEncounter: MysteryEncounter = .withEncounterTier(MysteryEncounterTier.ULTRA) .withSceneWaveRangeRequirement(60, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1]) .withMaxAllowedEncounters(1) + .withFleeAllowed(false) .withIntroSpriteConfigs([ { spriteKey: Species.GARBODOR.toString() + "-gigantamax", diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index 835451c29c5..d06720400c2 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -38,6 +38,7 @@ export const UncommonBreedEncounter: MysteryEncounter = .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withCatchAllowed(true) .withHideWildIntroMessage(true) + .withFleeAllowed(false) .withIntroSpriteConfigs([]) // Set in onInit() .withIntroDialogue([ { @@ -59,17 +60,18 @@ export const UncommonBreedEncounter: MysteryEncounter = const eggMoveIndex = randSeedInt(4); const randomEggMove: Moves = eggMoves[eggMoveIndex]; encounter.misc = { - eggMove: randomEggMove + eggMove: randomEggMove, + pokemon: pokemon }; if (pokemon.moveset.length < 4) { pokemon.moveset.push(new PokemonMove(randomEggMove)); } else { pokemon.moveset[0] = new PokemonMove(randomEggMove); } + } else { + encounter.misc.pokemon = pokemon; } - encounter.misc.pokemon = pokemon; - // Defense/Spd buffs below wave 50, +1 to all stats otherwise const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ? [Stat.DEF, Stat.SPDEF, Stat.SPD] : diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index be2e72c0ea4..1e68a894bc4 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -368,7 +368,7 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon const newEggMoveIndex = await addEggMoveToNewPokemonMoveset(scene, newPokemon, speciesRootForm); // Try to add a favored STAB move (might fail if Pokemon already knows a bunch of moves from newPokemonGeneratedMoveset) - addFavoredMoveToNewPokemonMoveset(scene, newPokemon, newPokemonGeneratedMoveset, newEggMoveIndex); + addFavoredMoveToNewPokemonMoveset(newPokemon, newPokemonGeneratedMoveset, newEggMoveIndex); // Randomize the second type of the pokemon // If the pokemon does not normally have a second type, it will gain 1 @@ -553,8 +553,7 @@ async function addEggMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Pla let eggMoveIndex: null | number = null; const eggMoves = newPokemon.getEggMoves()?.slice(0); if (eggMoves) { - const eggMoveIndices = [0, 1, 2, 3]; - randSeedShuffle(eggMoveIndices); + const eggMoveIndices = randSeedShuffle([0, 1, 2, 3]); let randomEggMoveIndex = eggMoveIndices.pop(); let randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex] : null; let retries = 0; @@ -587,12 +586,11 @@ async function addEggMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Pla /** * Returns index of the new egg move within the Pokemon's moveset (not the index of the move in `speciesEggMoves`) - * @param scene * @param newPokemon * @param newPokemonGeneratedMoveset * @param newEggMoveIndex */ -function addFavoredMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: PlayerPokemon, newPokemonGeneratedMoveset: (PokemonMove | null)[], newEggMoveIndex: number | null) { +function addFavoredMoveToNewPokemonMoveset(newPokemon: PlayerPokemon, newPokemonGeneratedMoveset: (PokemonMove | null)[], newEggMoveIndex: number | null) { let favoredMove: PokemonMove | null = null; for (const move of newPokemonGeneratedMoveset) { // Needs to match first type, second type will be replaced @@ -614,11 +612,15 @@ function addFavoredMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Playe } // Finally, assign favored move to random index that isn't the new egg move index if (favoredMove) { - let favoredMoveIndex = randSeedInt(4); - while (newEggMoveIndex !== null && favoredMoveIndex === newEggMoveIndex) { - favoredMoveIndex = randSeedInt(4); - } + if (newPokemon.moveset.length < 4) { + newPokemon.moveset.push(favoredMove); + } else { + let favoredMoveIndex = randSeedInt(4); + while (newEggMoveIndex !== null && favoredMoveIndex === newEggMoveIndex) { + favoredMoveIndex = randSeedInt(4); + } - newPokemon.moveset[favoredMoveIndex] = favoredMove; + newPokemon.moveset[favoredMoveIndex] = favoredMove; + } } } diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index ea04241e663..ca02c8bdfd6 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -832,7 +832,7 @@ export function transitionMysteryEncounterIntroVisuals(scene: BattleScene, hide: */ export function handleMysteryEncounterBattleStartEffects(scene: BattleScene) { const encounter = scene.currentBattle.mysteryEncounter; - if (scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && encounter && encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE && !encounter.startOfBattleEffectsComplete) { + if (scene.currentBattle.isBattleMysteryEncounter() && encounter && encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE && !encounter.startOfBattleEffectsComplete) { const effects = encounter.startOfBattleEffects; effects.forEach(effect => { let source; @@ -871,7 +871,7 @@ export function handleMysteryEncounterBattleStartEffects(scene: BattleScene) { */ export function handleMysteryEncounterTurnStartEffects(scene: BattleScene): boolean { const encounter = scene.currentBattle.mysteryEncounter; - if (scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && encounter && encounter.onTurnStart) { + if (scene.currentBattle.isBattleMysteryEncounter() && encounter && encounter.onTurnStart) { return encounter.onTurnStart(scene); } diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index d7e596af879..fb67305c78d 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -210,10 +210,10 @@ export function getRandomSpeciesByStarterTier(starterTiers: number | [number, nu .map(s => [parseInt(s) as Species, speciesStarters[s] as number]) .filter(s => { const pokemonSpecies = getPokemonSpecies(s[0]); - return pokemonSpecies && (!excludedSpecies || !excludedSpecies.includes(s[0]) + return pokemonSpecies && (!excludedSpecies || !excludedSpecies.includes(s[0])) && (allowSubLegendary || !pokemonSpecies.subLegendary) && (allowLegendary || !pokemonSpecies.legendary) - && (allowMythical || !pokemonSpecies.mythical)); + && (allowMythical || !pokemonSpecies.mythical); }) .map(s => [getPokemonSpecies(s[0]), s[1]]); @@ -224,7 +224,7 @@ export function getRandomSpeciesByStarterTier(starterTiers: number | [number, nu // If no filtered mons exist at specified starter tiers, will expand starter search range until there are // Starts by decrementing starter tier min until it is 0, then increments tier max up to 10 let tryFilterStarterTiers: [PokemonSpecies, number][] = filteredSpecies.filter(s => (s[1] >= min && s[1] <= max)); - while (tryFilterStarterTiers.length === 0 && (min !== 0 && max !== 10)) { + while (tryFilterStarterTiers.length === 0 && !(min === 0 && max === 10)) { if (min > 0) { min--; } else { @@ -757,9 +757,10 @@ const GOLDEN_BUG_NET_SPECIES_POOL: [Species, number][] = [ ]; /** - * Will randomly return one of the species from GOLDEN_BUG_NET_SPECIES_POOL, based on their weights + * Will randomly return one of the species from GOLDEN_BUG_NET_SPECIES_POOL, based on their weights. + * Will also check for and evolve pokemon based on level. */ -export function getGoldenBugNetSpecies(): PokemonSpecies { +export function getGoldenBugNetSpecies(level: number): PokemonSpecies { const totalWeight = GOLDEN_BUG_NET_SPECIES_POOL.reduce((a, b) => a + b[1], 0); const roll = randSeedInt(totalWeight); @@ -767,7 +768,8 @@ export function getGoldenBugNetSpecies(): PokemonSpecies { for (const speciesWeightPair of GOLDEN_BUG_NET_SPECIES_POOL) { w += speciesWeightPair[1]; if (roll < w) { - return getPokemonSpecies(speciesWeightPair[0]); + const initialSpecies = getPokemonSpecies(speciesWeightPair[0]); + return getPokemonSpecies(initialSpecies.getSpeciesForLevel(level, true)); } } diff --git a/src/data/pokemon-evolutions.ts b/src/data/pokemon-evolutions.ts index f9602d1386a..698bbb8e9c9 100644 --- a/src/data/pokemon-evolutions.ts +++ b/src/data/pokemon-evolutions.ts @@ -11,6 +11,7 @@ import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { TimeOfDay } from "#enums/time-of-day"; +import { DamageMoneyRewardModifier, ExtraModifierModifier, MoneyMultiplierModifier } from "#app/modifier/modifier"; export enum SpeciesWildEvolutionDelay { NONE, @@ -1647,8 +1648,14 @@ export const pokemonEvolutions: PokemonEvolutions = { 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 SpeciesEvolutionCondition( p => p.evoCounter > 9 ), SpeciesWildEvolutionDelay.VERY_LONG), - new SpeciesFormEvolution(Species.GHOLDENGO, "roaming", "", 1, null, new SpeciesEvolutionCondition( p => p.evoCounter > 9 ), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 1, null, new SpeciesEvolutionCondition(p => p.evoCounter + + p.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length + + p.scene.findModifiers(m => m instanceof MoneyMultiplierModifier + || m instanceof ExtraModifierModifier).length > 9), SpeciesWildEvolutionDelay.VERY_LONG), + new SpeciesFormEvolution(Species.GHOLDENGO, "roaming", "", 1, null, new SpeciesEvolutionCondition(p => p.evoCounter + + p.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length + + p.scene.findModifiers(m => m instanceof MoneyMultiplierModifier + || m instanceof ExtraModifierModifier).length > 9), SpeciesWildEvolutionDelay.VERY_LONG) ] }; diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index a904f497b0f..5c2dd06f7b3 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -4,7 +4,7 @@ import { SpeciesFormKey } from "./pokemon-species"; import { StatusEffect } from "./status-effect"; import { MoveCategory, allMoves } from "./move"; import { Type } from "./type"; -import { Constructor } from "#app/utils"; +import { Constructor, nil } from "#app/utils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -185,7 +185,7 @@ export class SpeciesFormChange { return true; } - findTrigger(triggerType: Constructor): SpeciesFormChangeTrigger | null { + findTrigger(triggerType: Constructor): SpeciesFormChangeTrigger | nil { if (!this.trigger.hasTriggerType(triggerType)) { return null; } @@ -193,7 +193,7 @@ export class SpeciesFormChange { const trigger = this.trigger; if (trigger instanceof SpeciesFormChangeCompoundTrigger) { - return trigger.triggers.find(t => t.hasTriggerType(triggerType))!; // TODO: is this bang correct? + return trigger.triggers.find(t => t.hasTriggerType(triggerType)); } return trigger; @@ -202,11 +202,11 @@ export class SpeciesFormChange { export class SpeciesFormChangeCondition { public predicate: SpeciesFormChangeConditionPredicate; - public enforceFunc: SpeciesFormChangeConditionEnforceFunc | null; + public enforceFunc: SpeciesFormChangeConditionEnforceFunc | nil; constructor(predicate: SpeciesFormChangeConditionPredicate, enforceFunc?: SpeciesFormChangeConditionEnforceFunc) { this.predicate = predicate; - this.enforceFunc = enforceFunc!; // TODO: is this bang correct? + this.enforceFunc = enforceFunc; } } diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index b8ddd826035..94dbba958b5 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -762,6 +762,13 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali } } + //TODO: Adjust templates and delays so we don't have to hardcode it + /* TEMPORARY! (Most) Trainers shouldn't be using unevolved Pokemon by the third gym leader / wave 80. Exceptions to this include Breeders, whose large teams are balanced by the use of weaker pokemon */ + if (currentWave >= 80 && forTrainer && strength > PartyMemberStrength.WEAKER) { + evolutionChance = 1; + noEvolutionChance = 0; + } + if (evolutionChance > 0) { if (isRegionalEvolution) { evolutionChance /= (evolutionSpecies.isRareRegional() ? 16 : 4); @@ -944,7 +951,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, 999.9, 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.EFFECT_SPORE, Abilities.EFFECT_SPORE, Abilities.EFFECT_SPORE, 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), @@ -959,7 +966,7 @@ export function initSpecies() { 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, 999.9, 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, 135, 115, 110, 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), @@ -1131,7 +1138,7 @@ 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, 999.9, 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.SHIELD_DUST, Abilities.SHIELD_DUST, Abilities.SHIELD_DUST, 635, 170, 85, 85, 105, 130, 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, @@ -1492,9 +1499,9 @@ export function initSpecies() { new PokemonForm("Normal", "", Type.DRAGON, Type.FLYING, 1.5, 102.6, Abilities.INTIMIDATE, Abilities.NONE, Abilities.MOXIE, 600, 95, 135, 80, 110, 80, 100, 45, 35, 300, false, null, true), new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.DRAGON, Type.FLYING, 1.8, 112.6, Abilities.AERILATE, Abilities.NONE, Abilities.AERILATE, 700, 95, 145, 130, 120, 90, 120, 45, 35, 300), ), - new PokemonSpecies(Species.BELDUM, 3, false, false, false, "Iron Ball Pokémon", Type.STEEL, Type.PSYCHIC, 0.6, 95.2, Abilities.CLEAR_BODY, Abilities.NONE, Abilities.LIGHT_METAL, 300, 40, 55, 80, 35, 60, 30, 3, 35, 60, GrowthRate.SLOW, null, false), - new PokemonSpecies(Species.METANG, 3, false, false, false, "Iron Claw Pokémon", Type.STEEL, Type.PSYCHIC, 1.2, 202.5, Abilities.CLEAR_BODY, Abilities.NONE, Abilities.LIGHT_METAL, 420, 60, 75, 100, 55, 80, 50, 3, 35, 147, GrowthRate.SLOW, null, false), - new PokemonSpecies(Species.METAGROSS, 3, false, false, false, "Iron Leg Pokémon", Type.STEEL, Type.PSYCHIC, 1.6, 550, Abilities.CLEAR_BODY, Abilities.NONE, Abilities.LIGHT_METAL, 600, 80, 135, 130, 95, 90, 70, 3, 35, 300, GrowthRate.SLOW, null, false, true, + new PokemonSpecies(Species.BELDUM, 3, false, false, false, "Iron Ball Pokémon", Type.STEEL, Type.PSYCHIC, 0.6, 95.2, Abilities.CLEAR_BODY, Abilities.NONE, Abilities.LIGHT_METAL, 300, 40, 55, 80, 35, 60, 30, 45, 35, 60, GrowthRate.SLOW, null, false), //Custom Catchrate, matching Frigibax + new PokemonSpecies(Species.METANG, 3, false, false, false, "Iron Claw Pokémon", Type.STEEL, Type.PSYCHIC, 1.2, 202.5, Abilities.CLEAR_BODY, Abilities.NONE, Abilities.LIGHT_METAL, 420, 60, 75, 100, 55, 80, 50, 25, 35, 147, GrowthRate.SLOW, null, false), //Custom Catchrate, matching Arctibax + new PokemonSpecies(Species.METAGROSS, 3, false, false, false, "Iron Leg Pokémon", Type.STEEL, Type.PSYCHIC, 1.6, 550, Abilities.CLEAR_BODY, Abilities.NONE, Abilities.LIGHT_METAL, 600, 80, 135, 130, 95, 90, 70, 10, 35, 300, GrowthRate.SLOW, null, false, true, //Custom Catchrate, matching Baxcalibur new PokemonForm("Normal", "", Type.STEEL, Type.PSYCHIC, 1.6, 550, Abilities.CLEAR_BODY, Abilities.NONE, Abilities.LIGHT_METAL, 600, 80, 135, 130, 95, 90, 70, 3, 35, 300, false, null, true), new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.STEEL, Type.PSYCHIC, 2.5, 942.9, Abilities.TOUGH_CLAWS, Abilities.NONE, Abilities.TOUGH_CLAWS, 700, 80, 145, 150, 105, 110, 110, 3, 35, 300), ), @@ -1745,7 +1752,7 @@ export function initSpecies() { new PokemonSpecies(Species.EXCADRILL, 5, false, false, false, "Subterrene Pokémon", Type.GROUND, Type.STEEL, 0.7, 40.4, Abilities.SAND_RUSH, Abilities.SAND_FORCE, Abilities.MOLD_BREAKER, 508, 110, 135, 60, 50, 65, 88, 60, 50, 178, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.AUDINO, 5, false, false, false, "Hearing Pokémon", Type.NORMAL, null, 1.1, 31, Abilities.HEALER, Abilities.REGENERATOR, Abilities.KLUTZ, 445, 103, 60, 86, 60, 86, 50, 255, 50, 390, GrowthRate.FAST, 50, false, true, new PokemonForm("Normal", "", Type.NORMAL, null, 1.1, 31, Abilities.HEALER, Abilities.REGENERATOR, Abilities.KLUTZ, 445, 103, 60, 86, 60, 86, 50, 255, 50, 390, false, null, true), - new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.NORMAL, Type.FAIRY, 1.5, 32, Abilities.HEALER, Abilities.HEALER, Abilities.HEALER, 545, 103, 60, 126, 80, 126, 50, 255, 50, 390), + new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.NORMAL, Type.FAIRY, 1.5, 32, Abilities.REGENERATOR, Abilities.REGENERATOR, Abilities.REGENERATOR, 545, 103, 60, 126, 80, 126, 50, 255, 50, 390), //Custom Ability, base form Hidden Ability ), new PokemonSpecies(Species.TIMBURR, 5, false, false, false, "Muscular Pokémon", Type.FIGHTING, null, 0.6, 12.5, Abilities.GUTS, Abilities.SHEER_FORCE, Abilities.IRON_FIST, 305, 75, 80, 55, 25, 35, 35, 180, 70, 61, GrowthRate.MEDIUM_SLOW, 75, false), new PokemonSpecies(Species.GURDURR, 5, false, false, false, "Muscular Pokémon", Type.FIGHTING, null, 1.2, 40, Abilities.GUTS, Abilities.SHEER_FORCE, Abilities.IRON_FIST, 405, 85, 105, 85, 40, 50, 40, 90, 50, 142, GrowthRate.MEDIUM_SLOW, 75, false), @@ -2617,8 +2624,8 @@ export function initSpecies() { new PokemonForm("Aquatic Mode", "aquatic-mode", Type.ELECTRIC, Type.DRAGON, 2.8, 240, Abilities.HADRON_ENGINE, Abilities.NONE, Abilities.NONE, 670, 100, 85, 100, 135, 115, 135, 3, 0, 335, false, null, true), new PokemonForm("Glide Mode", "glide-mode", Type.ELECTRIC, Type.DRAGON, 2.8, 240, Abilities.HADRON_ENGINE, Abilities.NONE, Abilities.NONE, 670, 100, 85, 100, 135, 115, 135, 3, 0, 335, false, null, true), ), - new PokemonSpecies(Species.WALKING_WAKE, 9, false, false, false, "Paradox Pokémon", Type.WATER, Type.DRAGON, 3.5, 280, Abilities.PROTOSYNTHESIS, Abilities.NONE, Abilities.NONE, 590, 99, 83, 91, 125, 83, 109, 5, 0, 295, GrowthRate.SLOW, null, false), - new PokemonSpecies(Species.IRON_LEAVES, 9, false, false, false, "Paradox Pokémon", Type.GRASS, Type.PSYCHIC, 1.5, 125, Abilities.QUARK_DRIVE, Abilities.NONE, Abilities.NONE, 590, 90, 130, 88, 70, 108, 104, 5, 0, 295, GrowthRate.SLOW, null, false), + new PokemonSpecies(Species.WALKING_WAKE, 9, false, false, false, "Paradox Pokémon", Type.WATER, Type.DRAGON, 3.5, 280, Abilities.PROTOSYNTHESIS, Abilities.NONE, Abilities.NONE, 590, 99, 83, 91, 125, 83, 109, 10, 0, 295, GrowthRate.SLOW, null, false), //Custom Catchrate, matching Gouging Fire and Raging Bolt + new PokemonSpecies(Species.IRON_LEAVES, 9, false, false, false, "Paradox Pokémon", Type.GRASS, Type.PSYCHIC, 1.5, 125, Abilities.QUARK_DRIVE, Abilities.NONE, Abilities.NONE, 590, 90, 130, 88, 70, 108, 104, 10, 0, 295, GrowthRate.SLOW, null, false), //Custom Catchrate, matching Iron Boulder and Iron Crown new PokemonSpecies(Species.DIPPLIN, 9, false, false, false, "Candy Apple Pokémon", Type.GRASS, Type.DRAGON, 0.4, 9.7, Abilities.SUPERSWEET_SYRUP, Abilities.GLUTTONY, Abilities.STICKY_HOLD, 485, 80, 80, 110, 95, 80, 40, 45, 50, 170, GrowthRate.ERRATIC, 50, false), new PokemonSpecies(Species.POLTCHAGEIST, 9, false, false, false, "Matcha Pokémon", Type.GRASS, Type.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, GrowthRate.SLOW, null, false, false, new PokemonForm("Counterfeit Form", "counterfeit", Type.GRASS, Type.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, true), @@ -2671,7 +2678,7 @@ export function initSpecies() { new PokemonSpecies(Species.ALOLA_MUK, 7, false, false, false, "Sludge Pokémon", Type.POISON, Type.DARK, 1, 52, Abilities.POISON_TOUCH, Abilities.GLUTTONY, Abilities.POWER_OF_ALCHEMY, 500, 105, 105, 75, 65, 100, 50, 75, 70, 175, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.ALOLA_EXEGGUTOR, 7, false, false, false, "Coconut Pokémon", Type.GRASS, Type.DRAGON, 10.9, 415.6, Abilities.FRISK, Abilities.NONE, Abilities.HARVEST, 530, 95, 105, 85, 125, 75, 45, 45, 50, 186, GrowthRate.SLOW, 50, false), new PokemonSpecies(Species.ALOLA_MAROWAK, 7, false, false, false, "Bone Keeper Pokémon", Type.FIRE, Type.GHOST, 1, 34, Abilities.CURSED_BODY, Abilities.LIGHTNING_ROD, Abilities.ROCK_HEAD, 425, 60, 80, 110, 50, 80, 45, 75, 50, 149, GrowthRate.MEDIUM_FAST, 50, false), - new PokemonSpecies(Species.ETERNAL_FLOETTE, 6, false, false, false, "Single Bloom Pokémon", Type.FAIRY, null, 0.2, 0.9, Abilities.FLOWER_VEIL, Abilities.NONE, Abilities.SYMBIOSIS, 551, 74, 65, 67, 125, 128, 92, 120, 70, 130, GrowthRate.MEDIUM_FAST, 0, false), + new PokemonSpecies(Species.ETERNAL_FLOETTE, 6, true, false, false, "Single Bloom Pokémon", Type.FAIRY, null, 0.2, 0.9, Abilities.FLOWER_VEIL, Abilities.NONE, Abilities.SYMBIOSIS, 551, 74, 65, 67, 125, 128, 92, 120, 70, 130, GrowthRate.MEDIUM_FAST, 0, false), //Marked as Sub-Legend, for casing purposes new PokemonSpecies(Species.GALAR_MEOWTH, 8, false, false, false, "Scratch Cat Pokémon", Type.STEEL, null, 0.4, 7.5, Abilities.PICKUP, Abilities.TOUGH_CLAWS, Abilities.UNNERVE, 290, 50, 65, 55, 40, 40, 40, 255, 50, 58, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.GALAR_PONYTA, 8, false, false, false, "Fire Horse Pokémon", Type.PSYCHIC, null, 0.8, 24, Abilities.RUN_AWAY, Abilities.PASTEL_VEIL, Abilities.ANTICIPATION, 410, 50, 85, 55, 65, 65, 90, 190, 50, 82, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.GALAR_RAPIDASH, 8, false, false, false, "Fire Horse Pokémon", Type.PSYCHIC, Type.FAIRY, 1.7, 80, Abilities.RUN_AWAY, Abilities.PASTEL_VEIL, Abilities.ANTICIPATION, 500, 65, 100, 70, 80, 80, 105, 60, 50, 175, GrowthRate.MEDIUM_FAST, 50, false), @@ -2716,7 +2723,7 @@ export function initSpecies() { new PokemonForm("Aqua Breed", "aqua", Type.FIGHTING, Type.WATER, 1.4, 110, Abilities.INTIMIDATE, Abilities.ANGER_POINT, Abilities.CUD_CHEW, 490, 75, 110, 105, 30, 70, 100, 45, 50, 172, false, null, true), ), new PokemonSpecies(Species.PALDEA_WOOPER, 9, false, false, false, "Water Fish Pokémon", Type.POISON, Type.GROUND, 0.4, 11, Abilities.POISON_POINT, Abilities.WATER_ABSORB, Abilities.UNAWARE, 210, 55, 45, 45, 25, 25, 15, 255, 50, 42, GrowthRate.MEDIUM_FAST, 50, false), - new PokemonSpecies(Species.BLOODMOON_URSALUNA, 9, false, false, false, "Peat Pokémon", Type.GROUND, Type.NORMAL, 2.7, 333, Abilities.MINDS_EYE, Abilities.NONE, Abilities.NONE, 555, 113, 70, 120, 135, 65, 52, 75, 50, 275, GrowthRate.MEDIUM_FAST, 50, false), + new PokemonSpecies(Species.BLOODMOON_URSALUNA, 9, true, false, false, "Peat Pokémon", Type.GROUND, Type.NORMAL, 2.7, 333, Abilities.MINDS_EYE, Abilities.NONE, Abilities.NONE, 555, 113, 70, 120, 135, 65, 52, 75, 50, 275, GrowthRate.MEDIUM_FAST, 50, false), //Marked as Sub-Legend, for casing purposes ); } @@ -2985,7 +2992,7 @@ export const speciesStarters = { [Species.CRESSELIA]: 6, [Species.PHIONE]: 4, [Species.MANAPHY]: 7, - [Species.DARKRAI]: 6, + [Species.DARKRAI]: 7, [Species.SHAYMIN]: 6, [Species.ARCEUS]: 9, @@ -3069,7 +3076,7 @@ export const speciesStarters = { [Species.LANDORUS]: 7, [Species.KYUREM]: 8, [Species.KELDEO]: 6, - [Species.MELOETTA]: 6, + [Species.MELOETTA]: 7, [Species.GENESECT]: 6, [Species.CHESPIN]: 3, @@ -3150,7 +3157,7 @@ export const speciesStarters = { [Species.TAPU_LELE]: 6, [Species.TAPU_BULU]: 6, [Species.TAPU_FINI]: 6, - [Species.COSMOG]: 6, + [Species.COSMOG]: 7, [Species.NIHILEGO]: 6, [Species.BUZZWOLE]: 6, [Species.PHEROMOSA]: 7, @@ -3383,7 +3390,7 @@ export const starterPassiveAbilities = { [Species.SQUIRTLE]: Abilities.STURDY, [Species.CATERPIE]: Abilities.MAGICIAN, [Species.WEEDLE]: Abilities.TINTED_LENS, - [Species.PIDGEY]: Abilities.FLARE_BOOST, + [Species.PIDGEY]: Abilities.SHEER_FORCE, [Species.RATTATA]: Abilities.STRONG_JAW, [Species.SPEAROW]: Abilities.MOXIE, [Species.EKANS]: Abilities.REGENERATOR, @@ -3417,11 +3424,11 @@ export const starterPassiveAbilities = { [Species.GASTLY]: Abilities.SHADOW_SHIELD, [Species.ONIX]: Abilities.ROCKY_PAYLOAD, [Species.DROWZEE]: Abilities.MAGICIAN, - [Species.KRABBY]: Abilities.UNBURDEN, + [Species.KRABBY]: Abilities.THERMAL_EXCHANGE, [Species.VOLTORB]: Abilities.TRANSISTOR, [Species.EXEGGCUTE]: Abilities.RIPEN, [Species.CUBONE]: Abilities.PARENTAL_BOND, - [Species.LICKITUNG]: Abilities.THICK_FAT, + [Species.LICKITUNG]: Abilities.CHEEK_POUCH, [Species.KOFFING]: Abilities.PARENTAL_BOND, [Species.RHYHORN]: Abilities.FILTER, [Species.TANGELA]: Abilities.SEED_SOWER, @@ -3486,7 +3493,7 @@ export const starterPassiveAbilities = { [Species.SKARMORY]: Abilities.LIGHTNING_ROD, [Species.HOUNDOUR]: Abilities.DROUGHT, [Species.PHANPY]: Abilities.SPEED_BOOST, - [Species.STANTLER]: Abilities.ANALYTIC, + [Species.STANTLER]: Abilities.SPEED_BOOST, [Species.SMEARGLE]: Abilities.PRANKSTER, [Species.TYROGUE]: Abilities.MOXIE, [Species.SMOOCHUM]: Abilities.PSYCHIC_SURGE, @@ -3617,7 +3624,7 @@ export const starterPassiveAbilities = { [Species.DIALGA]: Abilities.LEVITATE, [Species.PALKIA]: Abilities.SPEED_BOOST, [Species.HEATRAN]: Abilities.EARTH_EATER, - [Species.REGIGIGAS]: Abilities.MINDS_EYE, + [Species.REGIGIGAS]: Abilities.SCRAPPY, [Species.GIRATINA]: Abilities.SHADOW_SHIELD, [Species.CRESSELIA]: Abilities.SHADOW_SHIELD, [Species.PHIONE]: Abilities.SIMPLE, @@ -3671,7 +3678,7 @@ export const starterPassiveAbilities = { [Species.EMOLGA]: Abilities.TRANSISTOR, [Species.KARRABLAST]: Abilities.QUICK_DRAW, [Species.FOONGUS]: Abilities.THICK_FAT, - [Species.FRILLISH]: Abilities.UNAWARE, + [Species.FRILLISH]: Abilities.POISON_HEAL, [Species.ALOMOMOLA]: Abilities.MULTISCALE, [Species.JOLTIK]: Abilities.TRANSISTOR, [Species.FERROSEED]: Abilities.ROUGH_SKIN, @@ -3745,7 +3752,7 @@ export const starterPassiveAbilities = { [Species.HOOPA]: Abilities.OPPORTUNIST, [Species.VOLCANION]: Abilities.FILTER, [Species.ROWLET]: Abilities.SNIPER, - [Species.LITTEN]: Abilities.FUR_COAT, + [Species.LITTEN]: Abilities.OPPORTUNIST, [Species.POPPLIO]: Abilities.PUNK_ROCK, [Species.PIKIPEK]: Abilities.TECHNICIAN, [Species.YUNGOOS]: Abilities.TOUGH_CLAWS, @@ -3899,7 +3906,7 @@ export const starterPassiveAbilities = { [Species.FRIGIBAX]: Abilities.SNOW_WARNING, [Species.GIMMIGHOUL]: Abilities.HONEY_GATHER, [Species.WO_CHIEN]: Abilities.VESSEL_OF_RUIN, - [Species.CHIEN_PAO]: Abilities.SNOW_WARNING, + [Species.CHIEN_PAO]: Abilities.INTIMIDATE, [Species.TING_LU]: Abilities.STAMINA, [Species.CHI_YU]: Abilities.BERSERK, [Species.ROARING_MOON]: Abilities.TOUGH_CLAWS, diff --git a/src/data/trainer-config.ts b/src/data/trainer-config.ts index 75a849574ea..296e4e9b1b5 100644 --- a/src/data/trainer-config.ts +++ b/src/data/trainer-config.ts @@ -18,6 +18,11 @@ import {Species} from "#enums/species"; import {TrainerType} from "#enums/trainer-type"; import {Gender} from "./gender"; +/** Minimum BST for Pokemon generated onto the Elite Four's teams */ +const ELITE_FOUR_MINIMUM_BST = 460; +/** Minimum BST for Pokemon generated onto the E4 Champion's team */ +const CHAMPION_MINIMUM_BST = 508; + export enum TrainerPoolTier { COMMON, UNCOMMON, @@ -860,10 +865,10 @@ export class TrainerConfig { // Set species filter and specialty types if provided, otherwise filter by base total. if (specialtyTypes.length) { - this.setSpeciesFilter(p => specialtyTypes.some(t => p.isOfType(t)) && p.baseTotal >= 450); + this.setSpeciesFilter(p => specialtyTypes.some(t => p.isOfType(t)) && p.baseTotal >= ELITE_FOUR_MINIMUM_BST); this.setSpecialtyTypes(...specialtyTypes); } else { - this.setSpeciesFilter(p => p.baseTotal >= 450); + this.setSpeciesFilter(p => p.baseTotal >= ELITE_FOUR_MINIMUM_BST); } // Localize the trainer's name by converting it to lowercase and replacing spaces with underscores. @@ -913,8 +918,7 @@ export class TrainerConfig { this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool)); }); - // Set species filter to only include species with a base total of 470 or higher. - this.setSpeciesFilter(p => p.baseTotal >= 470); + this.setSpeciesFilter(p => p.baseTotal >= CHAMPION_MINIMUM_BST); // Localize the trainer's name by converting it to lowercase and replacing spaces with underscores. const nameForCall = this.name.toLowerCase().replace(/\s/g, "_"); diff --git a/src/enums/arena-tag-type.ts b/src/enums/arena-tag-type.ts index c6f911cb493..123d70b64fa 100644 --- a/src/enums/arena-tag-type.ts +++ b/src/enums/arena-tag-type.ts @@ -25,4 +25,5 @@ export enum ArenaTagType { SAFEGUARD = "SAFEGUARD", NO_CRIT = "NO_CRIT", IMPRISON = "IMPRISON", + PLASMA_FISTS = "PLASMA_FISTS", } diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index 9ed3b629746..209d36316f9 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -85,4 +85,5 @@ export enum BattlerTagType { TORMENT = "TORMENT", TAUNT = "TAUNT", IMPRISON = "IMPRISON", + SYRUP_BOMB = "SYRUP_BOMB", } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index fda4d2226c0..c66da0572ad 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -63,6 +63,12 @@ import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data"; import { SwitchType } from "#enums/switch-type"; +/** `64/65536 -> 1/1024` */ +const BASE_SHINY_CHANCE = 64; + +/** `1/256` */ +const BASE_HIDDEN_ABILITY_CHANCE = 256; + export enum FieldPosition { CENTER, LEFT, @@ -139,7 +145,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { throw `Cannot create a player Pokemon for species '${species.getName(formIndex)}'`; } - const hiddenAbilityChance = new Utils.IntegerHolder(256); + const hiddenAbilityChance = new Utils.IntegerHolder(BASE_HIDDEN_ABILITY_CHANCE); if (!this.hasTrainer()) { this.scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); } @@ -952,32 +958,35 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const baseStats = this.calculateBaseStats(); // Using base stats, calculate and store stats one by one for (const s of PERMANENT_STATS) { - let value = Math.floor(((2 * baseStats[s] + this.ivs[s]) * this.level) * 0.01); + const statHolder = new Utils.IntegerHolder(Math.floor(((2 * baseStats[s] + this.ivs[s]) * this.level) * 0.01)); if (s === Stat.HP) { - value = value + this.level + 10; + statHolder.value = statHolder.value + this.level + 10; + this.scene.applyModifier(PokemonIncrementingStatModifier, this.isPlayer(), this, s, statHolder); if (this.hasAbility(Abilities.WONDER_GUARD, false, true)) { - value = 1; + statHolder.value = 1; } - if (this.hp > value || this.hp === undefined) { - this.hp = value; + if (this.hp > statHolder.value || this.hp === undefined) { + this.hp = statHolder.value; } else if (this.hp) { const lastMaxHp = this.getMaxHp(); - if (lastMaxHp && value > lastMaxHp) { - this.hp += value - lastMaxHp; + if (lastMaxHp && statHolder.value > lastMaxHp) { + this.hp += statHolder.value - lastMaxHp; } } } else { - value += 5; + statHolder.value += 5; const natureStatMultiplier = new Utils.NumberHolder(getNatureStatMultiplier(this.getNature(), s)); this.scene.applyModifier(PokemonNatureWeightModifier, this.isPlayer(), this, natureStatMultiplier); if (natureStatMultiplier.value !== 1) { - value = Math.max(Math[natureStatMultiplier.value > 1 ? "ceil" : "floor"](value * natureStatMultiplier.value), 1); + statHolder.value = Math.max(Math[natureStatMultiplier.value > 1 ? "ceil" : "floor"](statHolder.value * natureStatMultiplier.value), 1); } + this.scene.applyModifier(PokemonIncrementingStatModifier, this.isPlayer(), this, s, statHolder); } - this.setStat(s, value); + statHolder.value = Utils.clampInt(statHolder.value, 1, Number.MAX_SAFE_INTEGER); + + this.setStat(s, statHolder.value); } - this.scene.applyModifier(PokemonIncrementingStatModifier, this.isPlayer(), this, this.stats); } calculateBaseStats(): number[] { @@ -1509,6 +1518,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder); applyPreAttackAbAttrs(MoveTypeChangeAbAttr, this, null, move, simulated, moveTypeHolder); + this.scene.arena.applyTags(ArenaTagType.PLASMA_FISTS, moveTypeHolder); + return moveTypeHolder.value as Type; } @@ -1801,7 +1812,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns list of egg moves */ getEggMoves() : Moves[] | undefined { - return speciesEggMoves[this.getSpeciesForm().getRootSpeciesId(true)]; + return speciesEggMoves[this.getSpeciesForm().getRootSpeciesId()]; } setMove(moveIndex: integer, moveId: Moves): void { @@ -1819,7 +1830,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * The exact mechanic is that it calculates E as the XOR of the player's trainer ID and secret ID. * F is calculated as the XOR of the first 16 bits of the Pokemon's ID with the last 16 bits. * The XOR of E and F are then compared to the {@linkcode shinyThreshold} (or {@linkcode thresholdOverride} if set) to see whether or not to generate a shiny. - * The base shiny odds are {@linkcode baseShinyChance} / 65536 + * The base shiny odds are {@linkcode BASE_SHINY_CHANCE} / 65536 * @param thresholdOverride number that is divided by 2^16 (65536) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm) * @returns true if the Pokemon has been set as a shiny, false otherwise */ @@ -1835,9 +1846,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const E = this.scene.gameData.trainerId ^ this.scene.gameData.secretId; const F = rand1 ^ rand2; - /** `64/65536 -> 1/1024` */ - const baseShinyChance = 64; - const shinyThreshold = new Utils.IntegerHolder(baseShinyChance); + const shinyThreshold = new Utils.IntegerHolder(BASE_SHINY_CHANCE); if (thresholdOverride === undefined) { if (this.scene.eventManager.isEventActive()) { shinyThreshold.value *= this.scene.eventManager.getShinyMultiplier(); @@ -1862,15 +1871,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * Function that tries to set a Pokemon shiny based on seed. * For manual use only, usually to roll a Pokemon's shiny chance a second time. * - * The base shiny odds are {@linkcode baseShinyChance} / 65536 + * The base shiny odds are {@linkcode BASE_SHINY_CHANCE} / 65536 * @param thresholdOverride number that is divided by 2^16 (65536) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm) * @param applyModifiersToOverride If {@linkcode thresholdOverride} is set and this is true, will apply Shiny Charm and event modifiers to {@linkcode thresholdOverride} * @returns true if the Pokemon has been set as a shiny, false otherwise */ trySetShinySeed(thresholdOverride?: integer, applyModifiersToOverride?: boolean): boolean { - /** `64/65536 -> 1/1024` */ - const baseShinyChance = 64; - const shinyThreshold = new Utils.IntegerHolder(baseShinyChance); + const shinyThreshold = new Utils.IntegerHolder(BASE_SHINY_CHANCE); if (thresholdOverride === undefined || applyModifiersToOverride) { if (thresholdOverride !== undefined && applyModifiersToOverride) { shinyThreshold.value = thresholdOverride; @@ -1928,7 +1935,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } generateFusionSpecies(forStarter?: boolean): void { - const hiddenAbilityChance = new Utils.IntegerHolder(256); + const hiddenAbilityChance = new Utils.IntegerHolder(BASE_HIDDEN_ABILITY_CHANCE); if (!this.hasTrainer()) { this.scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); } @@ -3976,7 +3983,8 @@ export class PlayerPokemon extends Pokemon { let compatible = false; for (const p of tmSpecies[tm]) { if (Array.isArray(p)) { - if (p[0] === this.species.speciesId || (this.fusionSpecies && p[0] === this.fusionSpecies.speciesId) && p.slice(1).indexOf(this.species.forms[this.formIndex]) > -1) { + const [pkm, form] = p; + if ((pkm === this.species.speciesId || this.fusionSpecies && pkm === this.fusionSpecies.speciesId) && form === this.getFormKey()) { compatible = true; break; } @@ -4992,6 +5000,8 @@ export class PokemonBattleData { export class PokemonBattleSummonData { /** The number of turns the pokemon has passed since entering the battle */ public turnCount: number = 1; + /** The number of turns the pokemon has passed since the start of the wave */ + public waveTurnCount: number = 1; /** The list of moves the pokemon has used since entering the battle */ public moveHistory: TurnMove[] = []; } diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 9ce555a617e..fb69a96448a 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -1387,7 +1387,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)), + 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)), diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 6c998105c1d..537e2327a21 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1,4 +1,5 @@ import * as ModifierTypes from "./modifier-type"; +import { getModifierType, ModifierType, modifierTypes } from "./modifier-type"; import BattleScene from "../battle-scene"; import { getLevelTotalExp } from "../data/exp"; import { MAX_PER_TYPE_POKEBALLS, PokeballType } from "../data/pokeball"; @@ -12,16 +13,15 @@ import * as Utils from "../utils"; import { getBerryEffectFunc, getBerryPredicate } from "../data/berry"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; -import { StatusEffect, getStatusEffectHealText } from "../data/status-effect"; +import { getStatusEffectHealText, StatusEffect } from "#app/data/status-effect"; import { achvs } from "../system/achv"; import { VoucherType } from "../system/voucher"; import { FormChangeItem, SpeciesFormChangeItemTrigger, SpeciesFormChangeLapseTeraTrigger, SpeciesFormChangeTeraTrigger } from "../data/pokemon-forms"; import { Nature } from "#app/data/nature"; import Overrides from "#app/overrides"; -import { ModifierType, modifierTypes } from "./modifier-type"; import { Command } from "#app/ui/command-ui-handler"; import { Species } from "#enums/species"; -import { Stat, type PermanentStat, type TempBattleStat, BATTLE_STATS, TEMP_BATTLE_STATS } from "#app/enums/stat"; +import { BATTLE_STATS, type PermanentStat, Stat, TEMP_BATTLE_STATS, type TempBattleStat } from "#app/enums/stat"; import i18next from "i18next"; import { allMoves } from "#app/data/move"; @@ -852,26 +852,47 @@ export class EvoTrackerModifier extends PokemonHeldItemModifier { } matchType(modifier: Modifier): boolean { - if (modifier instanceof EvoTrackerModifier) { - return (modifier as EvoTrackerModifier).species === this.species; - } - return false; + return modifier instanceof EvoTrackerModifier && modifier.species === this.species && modifier.required === this.required; } clone(): PersistentModifier { - return new EvoTrackerModifier(this.type, this.pokemonId, this.species, this.stackCount); + return new EvoTrackerModifier(this.type, this.pokemonId, this.species, this.required, this.stackCount); } getArgs(): any[] { - return super.getArgs().concat(this.species); + return super.getArgs().concat([this.species, this.required]); } apply(args: any[]): boolean { return true; } - getMaxHeldItemCount(_pokemon: Pokemon): integer { - return this.required; + getIconStackText(scene: BattleScene, virtual?: boolean): Phaser.GameObjects.BitmapText | null { + if (this.getMaxStackCount(scene) === 1 || (virtual && !this.virtualStackCount)) { + return null; + } + + const pokemon = scene.getPokemonById(this.pokemonId); + + this.stackCount = pokemon + ? pokemon.evoCounter + pokemon.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length + + pokemon.scene.findModifiers(m => m instanceof MoneyMultiplierModifier || m instanceof ExtraModifierModifier).length + : this.stackCount; + + const text = scene.add.bitmapText(10, 15, "item-count", this.stackCount.toString(), 11); + text.letterSpacing = -0.5; + if (this.getStackCount() >= this.required) { + text.setTint(0xf89890); + } + text.setOrigin(0, 0); + + return text; + } + + getMaxHeldItemCount(pokemon: Pokemon): integer { + this.stackCount = pokemon.evoCounter + pokemon.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length + + pokemon.scene.findModifiers(m => m instanceof MoneyMultiplierModifier || m instanceof ExtraModifierModifier).length; + return 999; } } @@ -882,7 +903,7 @@ export class PokemonBaseStatTotalModifier extends PokemonHeldItemModifier { private statModifier: integer; public isTransferable: boolean = false; - constructor(type: ModifierTypes.PokemonBaseStatTotalModifierType, pokemonId: integer, statModifier: integer, stackCount?: integer) { + constructor(type: ModifierTypes.PokemonBaseStatTotalModifierType, pokemonId: number, statModifier: number, stackCount?: integer) { super(type, pokemonId, stackCount); this.statModifier = statModifier; } @@ -927,11 +948,11 @@ export class PokemonBaseStatTotalModifier extends PokemonHeldItemModifier { * Currently used by Old Gateau item */ export class PokemonBaseStatFlatModifier extends PokemonHeldItemModifier { - private statModifier: integer; + private statModifier: number; private stats: Stat[]; public isTransferable: boolean = false; - constructor (type: ModifierType, pokemonId: integer, statModifier: integer, stats: Stat[], stackCount?: integer) { + constructor (type: ModifierType, pokemonId: number, statModifier: number, stats: Stat[], stackCount?: number) { super(type, pokemonId, stackCount); this.statModifier = statModifier; @@ -947,7 +968,7 @@ export class PokemonBaseStatFlatModifier extends PokemonHeldItemModifier { } override getArgs(): any[] { - return super.getArgs().concat(this.statModifier, this.stats); + return [ ...super.getArgs(), this.statModifier, this.stats ]; } override shouldApply(args: any[]): boolean { @@ -989,8 +1010,8 @@ export class PokemonIncrementingStatModifier extends PokemonHeldItemModifier { return modifier instanceof PokemonIncrementingStatModifier; } - clone(): PersistentModifier { - return new PokemonIncrementingStatModifier(this.type, this.pokemonId); + clone(): PokemonIncrementingStatModifier { + return new PokemonIncrementingStatModifier(this.type, this.pokemonId, this.stackCount); } getArgs(): any[] { @@ -998,21 +1019,26 @@ export class PokemonIncrementingStatModifier extends PokemonHeldItemModifier { } shouldApply(args: any[]): boolean { - return super.shouldApply(args) && args.length === 2 && args[1] instanceof Array; + return super.shouldApply(args) && args.length === 3 && args[2] instanceof Utils.IntegerHolder; } apply(args: any[]): boolean { - // Modifies the passed in stats[] array by +1 per stack for HP, +2 per stack for other stats + // Modifies the passed in stat integer holder by +1 per stack for HP, +2 per stack for other stats // If the Macho Brace is at max stacks (50), adds additional 5% to total HP and 10% to other stats - args[1].forEach((v, i) => { - const isHp = i === 0; - let mult = 1; + const isHp = args[1] === Stat.HP; + const statHolder = args[2] as Utils.IntegerHolder; + + if (isHp) { + statHolder.value += this.stackCount; if (this.stackCount === this.getMaxHeldItemCount()) { - mult = isHp ? 1.05 : 1.1; + statHolder.value = Math.floor(statHolder.value * 1.05); } - const newVal = Math.floor((v + this.stackCount * (isHp ? 1 : 2)) * mult); - args[1][i] = Math.min(Math.max(newVal, 1), 999999); - }); + } else { + statHolder.value += 2 * this.stackCount; + if (this.stackCount === this.getMaxHeldItemCount()) { + statHolder.value = Math.floor(statHolder.value * 1.1); + } + } return true; } @@ -2409,9 +2435,8 @@ export class MoneyRewardModifier extends ConsumableModifier { 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); + p.evoCounter ? p.evoCounter++ : p.evoCounter = 1; + const modifier = getModifierType(modifierTypes.EVOLUTION_TRACKER_GIMMIGHOUL).newModifier(p) as EvoTrackerModifier; scene.addModifier(modifier); } }); @@ -2581,7 +2606,7 @@ export class HealShopCostModifier extends PersistentModifier { constructor(type: ModifierType, shopMultiplier: number, stackCount?: integer) { super(type, stackCount); - this.shopMultiplier = shopMultiplier; + this.shopMultiplier = shopMultiplier ?? 2.5; } match(modifier: Modifier): boolean { @@ -2593,11 +2618,16 @@ export class HealShopCostModifier extends PersistentModifier { } apply(args: any[]): boolean { - (args[0] as Utils.IntegerHolder).value *= this.shopMultiplier; + const moneyCost = args[0] as Utils.NumberHolder; + moneyCost.value = Math.floor(moneyCost.value * this.shopMultiplier); return true; } + getArgs(): any[] { + return super.getArgs().concat(this.shopMultiplier); + } + getMaxStackCount(scene: BattleScene): integer { return 1; } diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index 902a85325ad..eaa458af904 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -35,6 +35,12 @@ export class BattleEndPhase extends BattlePhase { this.scene.unshiftPhase(new GameOverPhase(this.scene, true)); } + for (const pokemon of this.scene.getField()) { + if (pokemon && pokemon.battleSummonData) { + pokemon.battleSummonData.waveTurnCount = 1; + } + } + for (const pokemon of this.scene.getParty().filter(p => p.isAllowedInBattle())) { applyPostBattleAbAttrs(PostBattleAbAttr, pokemon); } diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 66e39cf98a5..a374f885d3f 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -70,7 +70,7 @@ export class CommandPhase extends FieldPhase { } } } else { - if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && this.scene.currentBattle.mysteryEncounter?.skipToFightInput) { + if (this.scene.currentBattle.isBattleMysteryEncounter() && this.scene.currentBattle.mysteryEncounter?.skipToFightInput) { this.scene.ui.clearText(); this.scene.ui.setMode(Mode.FIGHT, this.fieldIndex); } else { @@ -141,7 +141,7 @@ export class CommandPhase extends FieldPhase { this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); - } else if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && !this.scene.currentBattle.mysteryEncounter!.catchAllowed) { + } else if (this.scene.currentBattle.isBattleMysteryEncounter() && !this.scene.currentBattle.mysteryEncounter!.catchAllowed) { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noPokeballMysteryEncounter"), null, () => { diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index 25b4398025f..798ee3dca3c 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -34,6 +34,7 @@ import { doTrainerExclamation } from "#app/data/mystery-encounters/utils/encount import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { getGoldenBugNetSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; +import { Biome } from "#enums/biome"; export class EncounterPhase extends BattlePhase { private loaded: boolean; @@ -63,7 +64,7 @@ export class EncounterPhase extends BattlePhase { const battle = this.scene.currentBattle; // Generate and Init Mystery Encounter - if (battle.battleType === BattleType.MYSTERY_ENCOUNTER && !battle.mysteryEncounter) { + if (battle.isBattleMysteryEncounter() && !battle.mysteryEncounter) { this.scene.executeWithSeedOffset(() => { const currentSessionEncounterType = battle.mysteryEncounterType; battle.mysteryEncounter = this.scene.getMysteryEncounter(currentSessionEncounterType); @@ -79,7 +80,7 @@ export class EncounterPhase extends BattlePhase { mysteryEncounter.onInit(this.scene); } mysteryEncounter.populateDialogueTokensFromRequirements(this.scene); - }, this.scene.currentBattle.waveIndex); + }, battle.waveIndex); // Add any special encounter animations to load if (mysteryEncounter.encounterAnimations && mysteryEncounter.encounterAnimations.length > 0) { @@ -94,7 +95,7 @@ export class EncounterPhase extends BattlePhase { let totalBst = 0; battle.enemyLevels?.every((level, e) => { - if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) { + if (battle.isBattleMysteryEncounter()) { // Skip enemy loading for MEs, those are loaded elsewhere return false; } @@ -103,9 +104,12 @@ export class EncounterPhase extends BattlePhase { battle.enemyParty[e] = battle.trainer?.genPartyMember(e)!; // TODO:: is the bang correct here? } else { let enemySpecies = this.scene.randomSpecies(battle.waveIndex, level, true); - // If player has golden bug net, rolls 10% chance to replace with species from the golden bug net bug pool - if (this.scene.findModifier(m => m instanceof BoostBugSpawnModifier) && randSeedInt(10) === 0) { - enemySpecies = getGoldenBugNetSpecies(); + // If player has golden bug net, rolls 10% chance to replace non-boss wave wild species from the golden bug net bug pool + if (this.scene.findModifier(m => m instanceof BoostBugSpawnModifier) + && !this.scene.gameMode.isBoss(battle.waveIndex) + && this.scene.arena.biomeType !== Biome.END + && randSeedInt(10) === 0) { + enemySpecies = getGoldenBugNetSpecies(level); } battle.enemyParty[e] = this.scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, !!this.scene.getEncounterBossSegments(battle.waveIndex, level, enemySpecies)); if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { @@ -157,7 +161,7 @@ export class EncounterPhase extends BattlePhase { if (battle.battleType === BattleType.TRAINER) { loadEnemyAssets.push(battle.trainer?.loadAssets().then(() => battle.trainer?.initSprite())!); // TODO: is this bang correct? - } else if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) { + } else if (battle.isBattleMysteryEncounter()) { if (battle.mysteryEncounter?.introVisuals) { loadEnemyAssets.push(battle.mysteryEncounter.introVisuals.loadAssets().then(() => battle.mysteryEncounter!.introVisuals!.initSprite())); } @@ -189,7 +193,7 @@ export class EncounterPhase extends BattlePhase { Promise.all(loadEnemyAssets).then(() => { battle.enemyParty.every((enemyPokemon, e) => { - if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) { + if (battle.isBattleMysteryEncounter()) { return false; } if (e < (battle.double ? 2 : 1)) { @@ -363,7 +367,7 @@ export class EncounterPhase extends BattlePhase { showDialogueAndSummon(); } } - } else if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && this.scene.currentBattle.mysteryEncounter) { + } else if (this.scene.currentBattle.isBattleMysteryEncounter() && this.scene.currentBattle.mysteryEncounter) { const encounter = this.scene.currentBattle.mysteryEncounter; const introVisuals = encounter.introVisuals; introVisuals?.playAnim(); diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts index b0cfa5c55e2..1db47e9e289 100644 --- a/src/phases/game-over-phase.ts +++ b/src/phases/game-over-phase.ts @@ -1,34 +1,28 @@ import { clientSessionId } from "#app/account"; -import BattleScene from "#app/battle-scene"; import { BattleType } from "#app/battle"; +import BattleScene from "#app/battle-scene"; import { getCharVariantFromDialogue } from "#app/data/dialogue"; import { pokemonEvolutions } from "#app/data/pokemon-evolutions"; import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species"; import { trainerConfigs } from "#app/data/trainer-config"; -import { PlayerGender } from "#app/enums/player-gender"; -import { TrainerType } from "#app/enums/trainer-type"; import Pokemon from "#app/field/pokemon"; import { modifierTypes } from "#app/modifier/modifier-type"; +import { BattlePhase } from "#app/phases/battle-phase"; +import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { EndCardPhase } from "#app/phases/end-card-phase"; +import { GameOverModifierRewardPhase } from "#app/phases/game-over-modifier-reward-phase"; +import { PostGameOverPhase } from "#app/phases/post-game-over-phase"; +import { RibbonModifierRewardPhase } from "#app/phases/ribbon-modifier-reward-phase"; +import { SummonPhase } from "#app/phases/summon-phase"; +import { UnlockPhase } from "#app/phases/unlock-phase"; import { achvs, ChallengeAchv } from "#app/system/achv"; import { Unlockables } from "#app/system/unlockables"; import { Mode } from "#app/ui/ui"; -import i18next from "i18next"; import * as Utils from "#app/utils"; -import { BattlePhase } from "./battle-phase"; -import { CheckSwitchPhase } from "./check-switch-phase"; -import { EncounterPhase } from "./encounter-phase"; -import { GameOverModifierRewardPhase } from "./game-over-modifier-reward-phase"; -import { RibbonModifierRewardPhase } from "./ribbon-modifier-reward-phase"; -import { SummonPhase } from "./summon-phase"; -import { EndCardPhase } from "./end-card-phase"; -import { PostGameOverPhase } from "./post-game-over-phase"; -import { UnlockPhase } from "./unlock-phase"; -import { SessionSaveData } from "../system/game-data"; -import TrainerData from "../system/trainer-data"; -import PokemonData from "../system/pokemon-data"; -import PersistentModifierData from "../system/modifier-data"; -import ChallengeData from "../system/challenge-data"; -import ArenaData from "../system/arena-data"; +import { PlayerGender } from "#enums/player-gender"; +import { TrainerType } from "#enums/trainer-type"; +import i18next from "i18next"; export class GameOverPhase extends BattlePhase { private victory: boolean; @@ -114,7 +108,7 @@ export class GameOverPhase extends BattlePhase { this.scene.gameData.gameStats.dailyRunSessionsWon++; } } - this.scene.gameData.saveRunHistory(this.scene, this.getFinalSessionData(), this.victory); + this.scene.gameData.saveRunHistory(this.scene, this.scene.gameData.getSessionSaveData(this.scene), this.victory); const fadeDuration = this.victory ? 10000 : 5000; this.scene.fadeOutBgm(fadeDuration, true); const activeBattlers = this.scene.getField().filter(p => p?.isActive(true)); @@ -221,34 +215,5 @@ export class GameOverPhase extends BattlePhase { this.firstRibbons.push(getPokemonSpecies(pokemon.species.getRootSpeciesId(forStarter))); } } - - /** - * This function mirrors game-data.ts' getSessionSaveData() to update the session data to reflect any changes that occurred within the last wave - * This means that level ups, item usage, evolutions, etc. will all be accurately reflected. - * @returns {@linkCode SessionSaveData} an updated version of the wave's SessionSaveData that accurately reflects the events of the wave - */ - private getFinalSessionData(): SessionSaveData { - return { - seed: this.scene.seed, - playTime: this.scene.sessionPlayTime, - gameMode: this.scene.gameMode.modeId, - party: this.scene.getParty().map(p => new PokemonData(p)), - enemyParty: this.scene.getEnemyParty().map(p => new PokemonData(p)), - modifiers: this.scene.findModifiers(() => true).map(m => new PersistentModifierData(m, true)), - enemyModifiers: this.scene.findModifiers(() => true, false).map(m => new PersistentModifierData(m, false)), - arena: new ArenaData(this.scene.arena), - pokeballCounts: this.scene.pokeballCounts, - money: this.scene.money, - score: this.scene.score, - waveIndex: this.scene.currentBattle.waveIndex, - battleType: this.scene.currentBattle.battleType, - trainer: this.scene.currentBattle.battleType === BattleType.TRAINER ? new TrainerData(this.scene.currentBattle.trainer) : null, - 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 ?? -1, - mysteryEncounterSaveData: this.scene.mysteryEncounterSaveData - } as SessionSaveData; - } } diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index 60755095cca..0efaf1bf4ca 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -25,6 +25,7 @@ import { GameOverPhase } from "#app/phases/game-over-phase"; import { SwitchPhase } from "#app/phases/switch-phase"; import { SeenEncounterData } from "#app/data/mystery-encounters/mystery-encounter-save-data"; import { SwitchType } from "#enums/switch-type"; +import { BattlerTagType } from "#enums/battler-tag-type"; /** * Will handle (in order): @@ -218,9 +219,17 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase { start() { super.start(); + // Lapse any residual flinches/endures but ignore all other turn-end battle tags + const includedLapseTags = [BattlerTagType.FLINCHED, BattlerTagType.ENDURING]; const field = this.scene.getField(true).filter(p => p.summonData); field.forEach(pokemon => { - pokemon.lapseTags(BattlerTagLapseType.TURN_END); + const tags = pokemon.summonData.tags; + tags.filter(t => includedLapseTags.includes(t.tagType) + && t.lapseTypes.includes(BattlerTagLapseType.TURN_END) + && !(t.lapse(pokemon, BattlerTagLapseType.TURN_END))).forEach(t => { + t.onRemove(pokemon); + tags.splice(tags.indexOf(t), 1); + }); }); // Remove any status tick phases diff --git a/src/phases/pokemon-heal-phase.ts b/src/phases/pokemon-heal-phase.ts index 813d15ae87e..dc0bd235bb5 100644 --- a/src/phases/pokemon-heal-phase.ts +++ b/src/phases/pokemon-heal-phase.ts @@ -46,8 +46,7 @@ export class PokemonHealPhase extends CommonAnimPhase { const pokemon = this.getPokemon(); if (!pokemon.isOnField() || (!this.revive && !pokemon.isActive())) { - super.end(); - return; + return super.end(); } const hasMessage = !!this.message; @@ -58,7 +57,7 @@ export class PokemonHealPhase extends CommonAnimPhase { if (healBlock && this.hpHealed > 0) { this.scene.queueMessage(healBlock.onActivation(pokemon)); this.message = null; - super.end(); + return super.end(); } else if (healOrDamage) { const hpRestoreMultiplier = new Utils.IntegerHolder(1); if (!this.revive) { diff --git a/src/phases/post-summon-phase.ts b/src/phases/post-summon-phase.ts index e7f6c6ea3db..b99c0b90fd8 100644 --- a/src/phases/post-summon-phase.ts +++ b/src/phases/post-summon-phase.ts @@ -6,7 +6,6 @@ import { StatusEffect } from "#app/enums/status-effect"; import { PokemonPhase } from "./pokemon-phase"; import { MysteryEncounterPostSummonTag } from "#app/data/battler-tags"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { BattleType } from "#app/battle"; export class PostSummonPhase extends PokemonPhase { constructor(scene: BattleScene, battlerIndex: BattlerIndex) { @@ -24,7 +23,7 @@ export class PostSummonPhase extends PokemonPhase { this.scene.arena.applyTags(ArenaTrapTag, pokemon); // If this is mystery encounter and has post summon phase tag, apply post summon effects - if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && pokemon.findTags(t => t instanceof MysteryEncounterPostSummonTag).length > 0) { + if (this.scene.currentBattle.isBattleMysteryEncounter() && pokemon.findTags(t => t instanceof MysteryEncounterPostSummonTag).length > 0) { pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON); } diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index d909c5c3501..dfa374307d5 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -87,7 +87,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { this.scene.pbTrayEnemy.hide(); this.scene.ui.showText(message, null, () => this.summon()); - } else if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER) { + } else if (this.scene.currentBattle.isBattleMysteryEncounter()) { this.scene.pbTrayEnemy.hide(); this.summonWild(); } diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index eb1e089543b..07761b10d6e 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -173,6 +173,7 @@ export class SwitchSummonPhase extends SummonPhase { // Or compensate for force switch move if switched out pokemon is not fainted if (currentCommand === Command.POKEMON || lastPokemonIsForceSwitchedAndNotFainted) { pokemon.battleSummonData.turnCount--; + pokemon.battleSummonData.waveTurnCount--; } if (this.switchType === SwitchType.BATON_PASS && pokemon) { diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index 724a5206d74..60a2e6600db 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -44,6 +44,7 @@ export class TurnEndPhase extends FieldPhase { this.scene.applyModifiers(TurnHeldItemTransferModifier, pokemon.isPlayer(), pokemon); pokemon.battleSummonData.turnCount++; + pokemon.battleSummonData.waveTurnCount++; }; this.executeForAll(handlePokemon); diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index b070abb390a..95d55986185 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -43,20 +43,17 @@ export class TurnStartPhase extends FieldPhase { orderedTargets = Utils.randSeedShuffle(orderedTargets); }, this.scene.currentBattle.turn, this.scene.waveSeed); - orderedTargets.sort((a: Pokemon, b: Pokemon) => { - const aSpeed = a?.getEffectiveStat(Stat.SPD) || 0; - const bSpeed = b?.getEffectiveStat(Stat.SPD) || 0; - - return bSpeed - aSpeed; - }); - - // Next, a check for Trick Room is applied. If Trick Room is present, the order is reversed. + // Next, a check for Trick Room is applied to determine sort order. const speedReversed = new Utils.BooleanHolder(false); this.scene.arena.applyTags(TrickRoomTag, speedReversed); - if (speedReversed.value) { - orderedTargets = orderedTargets.reverse(); - } + // Adjust the sort function based on whether Trick Room is active. + orderedTargets.sort((a: Pokemon, b: Pokemon) => { + const aSpeed = a?.getEffectiveStat(Stat.SPD) ?? 0; + const bSpeed = b?.getEffectiveStat(Stat.SPD) ?? 0; + + return speedReversed.value ? aSpeed - bSpeed : bSpeed - aSpeed; + }); return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : BattlerIndex.PLAYER)); } diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index c10adc5683d..e900ff97fc6 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -30,7 +30,7 @@ export class VictoryPhase extends PokemonPhase { const expValue = this.getPokemon().getExpValue(); this.scene.applyPartyExp(expValue, true); - if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER) { + if (this.scene.currentBattle.isBattleMysteryEncounter()) { handleMysteryEncounterVictory(this.scene, false, this.isExpOnly); return this.end(); } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 22a793e9aca..af5e717c17b 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -1,5 +1,5 @@ import i18next from "i18next"; -import BattleScene, { PokeballCounts, bypassLogin } from "../battle-scene"; +import BattleScene, { bypassLogin, PokeballCounts } from "../battle-scene"; import Pokemon, { EnemyPokemon, PlayerPokemon } from "../field/pokemon"; import { pokemonPrevolutions } from "../data/pokemon-evolutions"; import PokemonSpecies, { allSpecies, getPokemonSpecies, noStarterFormKeys, speciesStarters } from "../data/pokemon-species"; @@ -13,11 +13,11 @@ import { GameModes, getGameMode } from "../game-mode"; import { BattleType } from "../battle"; import TrainerData from "./trainer-data"; import { trainerConfigs } from "../data/trainer-config"; -import { SettingKeys, resetSettings, setSetting } from "./settings/settings"; +import { resetSettings, setSetting, SettingKeys } from "./settings/settings"; import { achvs } from "./achv"; import EggData from "./egg-data"; import { Egg } from "../data/egg"; -import { VoucherType, vouchers } from "./voucher"; +import { vouchers, VoucherType } from "./voucher"; import { AES, enc } from "crypto-js"; import { Mode } from "../ui/ui"; import { clientSessionId, loggedInUser, updateUserInfo } from "../account"; @@ -28,8 +28,8 @@ import { speciesEggMoves } from "../data/egg-moves"; import { allMoves } from "../data/move"; import { TrainerVariant } from "../field/trainer"; import { Variant } from "#app/data/variant"; -import {setSettingGamepad, SettingGamepad, settingGamepadDefaults} from "./settings/settings-gamepad"; -import {setSettingKeyboard, SettingKeyboard} from "#app/system/settings/settings-keyboard"; +import { setSettingGamepad, SettingGamepad, settingGamepadDefaults } from "./settings/settings-gamepad"; +import { setSettingKeyboard, SettingKeyboard } from "#app/system/settings/settings-keyboard"; import { TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; import * as Modifier from "../modifier/modifier"; import { StatusEffect } from "#app/data/status-effect"; @@ -48,6 +48,7 @@ import { RUN_HISTORY_LIMIT } from "#app/ui/run-history-ui-handler"; import { applySessionDataPatches, applySettingsDataPatches, applySystemDataPatches } from "./version-converter"; import { MysteryEncounterSaveData } from "../data/mystery-encounters/mystery-encounter-save-data"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { PokerogueApiClearSessionData } from "#app/@types/pokerogue-api"; export const defaultStarterSpecies: Species[] = [ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, @@ -956,7 +957,7 @@ export class GameData { enemyModifiers: scene.findModifiers(() => true, false).map(m => new PersistentModifierData(m, false)), arena: new ArenaData(scene.arena), pokeballCounts: scene.pokeballCounts, - money: scene.money, + money: Math.floor(scene.money), score: scene.score, waveIndex: scene.currentBattle.waveIndex, battleType: scene.currentBattle.battleType, @@ -1046,7 +1047,7 @@ export class GameData { scene.pokeballCounts = Overrides.POKEBALL_OVERRIDE.pokeballs; } - scene.money = sessionData.money || 0; + scene.money = Math.floor(sessionData.money || 0); scene.updateMoneyText(); if (scene.money > this.gameStats.highestMoney) { @@ -1186,37 +1187,43 @@ export class GameData { }); } - tryClearSession(scene: BattleScene, slotId: integer): Promise<[success: boolean, newClear: boolean]> { - return new Promise<[boolean, boolean]>(resolve => { - if (bypassLogin) { - localStorage.removeItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`); - return resolve([true, true]); + + /** + * Attempt to clear session data. After session data is removed, attempt to update user info so the menu updates + */ + async tryClearSession(scene: BattleScene, slotId: integer): Promise<[success: boolean, newClear: boolean]> { + let result: [boolean, boolean] = [false, false]; + + if (bypassLogin) { + localStorage.removeItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`); + result = [true, true]; + } else { + const sessionData = this.getSessionSaveData(scene); + const response = await Utils.apiPost(`savedata/session/clear?slot=${slotId}&trainerId=${this.trainerId}&secretId=${this.secretId}&clientSessionId=${clientSessionId}`, JSON.stringify(sessionData), undefined, true); + + if (response.ok) { + loggedInUser!.lastSessionSlot = -1; // TODO: is the bang correct? + localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser?.username}`); } - updateUserInfo().then(success => { - if (success !== null && !success) { - return resolve([false, false]); + const jsonResponse: PokerogueApiClearSessionData = await response.json(); + + if (!jsonResponse.error) { + result = [true, jsonResponse.success ?? false]; + } else { + if (jsonResponse && jsonResponse.error?.startsWith("session out of date")) { + this.scene.clearPhaseQueue(); + this.scene.unshiftPhase(new ReloadSessionPhase(this.scene)); } - const sessionData = this.getSessionSaveData(scene); - Utils.apiPost(`savedata/session/clear?slot=${slotId}&trainerId=${this.trainerId}&secretId=${this.secretId}&clientSessionId=${clientSessionId}`, JSON.stringify(sessionData), undefined, true).then(response => { - if (response.ok) { - loggedInUser!.lastSessionSlot = -1; // TODO: is the bang correct? - localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser?.username}`); - } - return response.json(); - }).then(jsonResponse => { - if (!jsonResponse.error) { - return resolve([true, jsonResponse.success as boolean]); - } - if (jsonResponse && jsonResponse.error.startsWith("session out of date")) { - this.scene.clearPhaseQueue(); - this.scene.unshiftPhase(new ReloadSessionPhase(this.scene)); - } - console.error(jsonResponse); - resolve([false, false]); - }); - }); - }); + + console.error(jsonResponse); + result = [false, false]; + } + } + + await updateUserInfo(); + + return result; } parseSessionData(dataStr: string): SessionSaveData { @@ -1576,12 +1583,20 @@ export class GameData { * @returns `true` if Pokemon catch unlocked a new starter, `false` if Pokemon catch did not unlock a starter */ setPokemonCaught(pokemon: Pokemon, incrementCount: boolean = true, fromEgg: boolean = false, showMessage: boolean = true): Promise { - return this.setPokemonSpeciesCaught(pokemon, pokemon.species, incrementCount, fromEgg, showMessage); + // If incrementCount === false (not a catch scenario), only update the pokemon's dex data if the Pokemon has already been marked as caught in dex + // Prevents form changes, nature changes, etc. from unintentionally updating the dex data of a "rental" pokemon + const speciesRootForm = pokemon.species.getRootSpeciesId(); + if (!incrementCount && !this.scene.gameData.dexData[speciesRootForm].caughtAttr) { + return Promise.resolve(false); + } else { + return this.setPokemonSpeciesCaught(pokemon, pokemon.species, incrementCount, fromEgg, showMessage); + } } /** * * @param pokemon + * @param species * @param incrementCount * @param fromEgg * @param showMessage @@ -1597,12 +1612,18 @@ export class GameData { } const dexAttr = pokemon.getDexAttr(); pokemon.formIndex = formIndex; + + // Mark as caught dexEntry.caughtAttr |= dexAttr; + + // Unlock ability if (speciesStarters.hasOwnProperty(species.speciesId)) { this.starterData[species.speciesId].abilityAttr |= pokemon.abilityIndex !== 1 || pokemon.species.ability2 ? 1 << pokemon.abilityIndex : AbilityAttr.ABILITY_HIDDEN; } + + // Unlock nature dexEntry.natureAttr |= 1 << (pokemon.nature + 1); const hasPrevolution = pokemonPrevolutions.hasOwnProperty(species.speciesId); @@ -1697,9 +1718,19 @@ export class GameData { return ++this.starterData[speciesIdToIncrement].classicWinCount; } + /** + * Adds a candy to the player's game data for a given {@linkcode PokemonSpecies}. + * Will do nothing if the player does not have the Pokemon owned in their system save data. + * @param species + * @param count + */ addStarterCandy(species: PokemonSpecies, count: integer): void { - this.scene.candyBar.showStarterSpeciesCandy(species.speciesId, count); - this.starterData[species.speciesId].candyCount += count; + // Only gain candies if the Pokemon has already been marked as caught in dex (ignore "rental" pokemon) + const speciesRootForm = species.getRootSpeciesId(); + if (this.scene.gameData.dexData[speciesRootForm].caughtAttr) { + this.scene.candyBar.showStarterSpeciesCandy(species.speciesId, count); + this.starterData[species.speciesId].candyCount += count; + } } /** diff --git a/src/system/version-converter.ts b/src/system/version-converter.ts index 0591647aeaa..3a4416a975e 100644 --- a/src/system/version-converter.ts +++ b/src/system/version-converter.ts @@ -6,6 +6,10 @@ const LATEST_VERSION = "1.0.5"; export function applySessionDataPatches(data: SessionSaveData) { const curVersion = data.gameVersion; + + // Always sanitize money as a safeguard + data.money = Math.floor(data.money); + if (curVersion !== LATEST_VERSION) { switch (curVersion) { case "1.0.0": diff --git a/src/test/abilities/gulp_missile.test.ts b/src/test/abilities/gulp_missile.test.ts index ac0efd8e8d1..d981f009974 100644 --- a/src/test/abilities/gulp_missile.test.ts +++ b/src/test/abilities/gulp_missile.test.ts @@ -1,11 +1,7 @@ -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { StatusEffect } from "#app/enums/status-effect"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { StatusEffect } from "#enums/status-effect"; import Pokemon from "#app/field/pokemon"; -import { BerryPhase } from "#app/phases/berry-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { TurnStartPhase } from "#app/phases/turn-start-phase"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -53,13 +49,13 @@ describe("Abilities - Gulp Missile", () => { }); it("changes to Gulping Form if HP is over half when Surf or Dive is used", async () => { - await game.startBattle([Species.CRAMORANT]); + await game.classicMode.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; game.move.select(Moves.DIVE); await game.toNextTurn(); game.move.select(Moves.DIVE); - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(cramorant.getHpRatio()).toBeGreaterThanOrEqual(.5); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -67,21 +63,21 @@ describe("Abilities - Gulp Missile", () => { }); it("changes to Gorging Form if HP is under half when Surf or Dive is used", async () => { - await game.startBattle([Species.CRAMORANT]); + await game.classicMode.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.49); expect(cramorant.getHpRatio()).toBe(.49); game.move.select(Moves.SURF); - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeDefined(); expect(cramorant.formIndex).toBe(GORGING_FORM); }); it("changes to base form when switched out after Surf or Dive is used", async () => { - await game.startBattle([Species.CRAMORANT, Species.MAGIKARP]); + await game.classicMode.startBattle([Species.CRAMORANT, Species.MAGIKARP]); const cramorant = game.scene.getPlayerPokemon()!; game.move.select(Moves.SURF); @@ -96,51 +92,51 @@ describe("Abilities - Gulp Missile", () => { }); it("changes form during Dive's charge turn", async () => { - await game.startBattle([Species.CRAMORANT]); + await game.classicMode.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; game.move.select(Moves.DIVE); - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); expect(cramorant.formIndex).toBe(GULPING_FORM); }); it("deals 1/4 of the attacker's maximum HP when hit by a damaging attack", async () => { - game.override.enemyMoveset([Moves.TACKLE]); - await game.startBattle([Species.CRAMORANT]); + game.override.enemyMoveset(Moves.TACKLE); + await game.classicMode.startBattle([Species.CRAMORANT]); const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "damageAndUpdate"); game.move.select(Moves.SURF); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy)); }); it("does not have any effect when hit by non-damaging attack", async () => { - game.override.enemyMoveset([Moves.TAIL_WHIP]); - await game.startBattle([Species.CRAMORANT]); + game.override.enemyMoveset(Moves.TAIL_WHIP); + await game.classicMode.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); game.move.select(Moves.SURF); - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); expect(cramorant.formIndex).toBe(GULPING_FORM); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); expect(cramorant.formIndex).toBe(GULPING_FORM); }); it("lowers attacker's DEF stat stage by 1 when hit in Gulping form", async () => { - game.override.enemyMoveset([Moves.TACKLE]); - await game.startBattle([Species.CRAMORANT]); + game.override.enemyMoveset(Moves.TACKLE); + await game.classicMode.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; @@ -149,12 +145,12 @@ describe("Abilities - Gulp Missile", () => { vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); game.move.select(Moves.SURF); - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); expect(cramorant.formIndex).toBe(GULPING_FORM); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy)); expect(enemy.getStatStage(Stat.DEF)).toBe(-1); @@ -163,8 +159,8 @@ describe("Abilities - Gulp Missile", () => { }); it("paralyzes the enemy when hit in Gorging form", async () => { - game.override.enemyMoveset([Moves.TACKLE]); - await game.startBattle([Species.CRAMORANT]); + game.override.enemyMoveset(Moves.TACKLE); + await game.classicMode.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; @@ -173,12 +169,12 @@ describe("Abilities - Gulp Missile", () => { vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.45); game.move.select(Moves.SURF); - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeDefined(); expect(cramorant.formIndex).toBe(GORGING_FORM); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy)); expect(enemy.status?.effect).toBe(StatusEffect.PARALYSIS); @@ -187,21 +183,21 @@ describe("Abilities - Gulp Missile", () => { }); it("does not activate the ability when underwater", async () => { - game.override.enemyMoveset([Moves.SURF]); - await game.startBattle([Species.CRAMORANT]); + game.override.enemyMoveset(Moves.SURF); + await game.classicMode.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; game.move.select(Moves.DIVE); - await game.phaseInterceptor.to(BerryPhase, false); + await game.phaseInterceptor.to("BerryPhase", false); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); expect(cramorant.formIndex).toBe(GULPING_FORM); }); it("prevents effect damage but inflicts secondary effect on attacker with Magic Guard", async () => { - game.override.enemyMoveset([Moves.TACKLE]).enemyAbility(Abilities.MAGIC_GUARD); - await game.startBattle([Species.CRAMORANT]); + game.override.enemyMoveset(Moves.TACKLE).enemyAbility(Abilities.MAGIC_GUARD); + await game.classicMode.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; @@ -209,13 +205,13 @@ describe("Abilities - Gulp Missile", () => { vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); game.move.select(Moves.SURF); - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); const enemyHpPreEffect = enemy.hp; expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); expect(cramorant.formIndex).toBe(GULPING_FORM); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(enemy.hp).toBe(enemyHpPreEffect); expect(enemy.getStatStage(Stat.DEF)).toBe(-1); @@ -223,20 +219,36 @@ describe("Abilities - Gulp Missile", () => { expect(cramorant.formIndex).toBe(NORMAL_FORM); }); + it("activates on faint", async () => { + game.override.enemyMoveset(Moves.THUNDERBOLT); + await game.classicMode.startBattle([Species.CRAMORANT]); + + const cramorant = game.scene.getPlayerPokemon()!; + + game.move.select(Moves.SURF); + await game.phaseInterceptor.to("FaintPhase"); + + expect(cramorant.hp).toBe(0); + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeUndefined(); + expect(cramorant.formIndex).toBe(NORMAL_FORM); + expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.DEF)).toBe(-1); + }); + + it("cannot be suppressed", async () => { - game.override.enemyMoveset([Moves.GASTRO_ACID]); - await game.startBattle([Species.CRAMORANT]); + game.override.enemyMoveset(Moves.GASTRO_ACID); + await game.classicMode.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); game.move.select(Moves.SURF); - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); expect(cramorant.formIndex).toBe(GULPING_FORM); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(cramorant.hasAbility(Abilities.GULP_MISSILE)).toBe(true); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -244,19 +256,19 @@ describe("Abilities - Gulp Missile", () => { }); it("cannot be swapped with another ability", async () => { - game.override.enemyMoveset([Moves.SKILL_SWAP]); - await game.startBattle([Species.CRAMORANT]); + game.override.enemyMoveset(Moves.SKILL_SWAP); + await game.classicMode.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); game.move.select(Moves.SURF); - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); expect(cramorant.formIndex).toBe(GULPING_FORM); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(cramorant.hasAbility(Abilities.GULP_MISSILE)).toBe(true); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -266,9 +278,9 @@ describe("Abilities - Gulp Missile", () => { it("cannot be copied", async () => { game.override.enemyAbility(Abilities.TRACE); - await game.startBattle([Species.CRAMORANT]); + await game.classicMode.startBattle([Species.CRAMORANT]); game.move.select(Moves.SPLASH); - await game.phaseInterceptor.to(TurnStartPhase); + await game.phaseInterceptor.to("TurnStartPhase"); expect(game.scene.getEnemyPokemon()?.hasAbility(Abilities.GULP_MISSILE)).toBe(false); }); diff --git a/src/test/field/pokemon.test.ts b/src/test/field/pokemon.test.ts index f7c1cf8bc3d..225f302ff0c 100644 --- a/src/test/field/pokemon.test.ts +++ b/src/test/field/pokemon.test.ts @@ -3,6 +3,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "../utils/gameManager"; import { PokeballType } from "#app/enums/pokeball"; import BattleScene from "#app/battle-scene"; +import { Moves } from "#app/enums/moves"; describe("Spec - Pokemon", () => { let phaserGame: Phaser.Game; @@ -63,4 +64,15 @@ describe("Spec - Pokemon", () => { }); }); }); + + it("should not share tms between different forms", async () => { + game.override.starterForms({ [Species.ROTOM]: 4 }); + + await game.classicMode.startBattle([Species.ROTOM]); + + const fanRotom = game.scene.getPlayerPokemon()!; + + expect(fanRotom.compatibleTms).not.toContain(Moves.BLIZZARD); + expect(fanRotom.compatibleTms).toContain(Moves.AIR_SLASH); + }); }); diff --git a/src/test/moves/fake_out.test.ts b/src/test/moves/fake_out.test.ts index 04d6216b952..e306ab12a3f 100644 --- a/src/test/moves/fake_out.test.ts +++ b/src/test/moves/fake_out.test.ts @@ -23,14 +23,15 @@ describe("Moves - Fake Out", () => { game.override .battleType("single") .enemySpecies(Species.CORVIKNIGHT) - .starterSpecies(Species.FEEBAS) .moveset([Moves.FAKE_OUT, Moves.SPLASH]) .enemyMoveset(Moves.SPLASH) + .enemyLevel(10) + .startingLevel(10) // prevent LevelUpPhase from happening .disableCrits(); }); - it("can only be used on the first turn a pokemon is sent out", async() => { - await game.classicMode.startBattle(); + it("can only be used on the first turn a pokemon is sent out in a battle", async() => { + await game.classicMode.startBattle([Species.FEEBAS]); const enemy = game.scene.getEnemyPokemon()!; @@ -44,22 +45,27 @@ describe("Moves - Fake Out", () => { await game.toNextTurn(); expect(enemy.hp).toBe(postTurnOneHp); + }, 20000); - game.move.select(Moves.SPLASH); - await game.doKillOpponents(); + // This is a PokeRogue buff to Fake Out + it("can be used at the start of every wave even if the pokemon wasn't recalled", async() => { + await game.classicMode.startBattle([Species.FEEBAS]); + + const enemy = game.scene.getEnemyPokemon()!; + enemy.damageAndUpdate(enemy.getMaxHp() - 1); + + game.move.select(Moves.FAKE_OUT); await game.toNextWave(); - const newEnemy = game.scene.getEnemyPokemon()!; - game.move.select(Moves.FAKE_OUT); await game.toNextTurn(); - expect(newEnemy.hp).toBe(newEnemy.getMaxHp()); + expect(game.scene.getEnemyPokemon()!.isFullHp()).toBe(false); }, 20000); it("can be used again if recalled and sent back out", async() => { game.override.startingWave(4); - await game.classicMode.startBattle(); + await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]); const enemy1 = game.scene.getEnemyPokemon()!; @@ -76,6 +82,18 @@ describe("Moves - Fake Out", () => { const enemy2 = game.scene.getEnemyPokemon()!; + expect(enemy2.hp).toBeLessThan(enemy2.getMaxHp()); + enemy2.hp = enemy2.getMaxHp(); + + game.doSwitchPokemon(1); + await game.toNextTurn(); + + game.doSwitchPokemon(1); + await game.toNextTurn(); + + game.move.select(Moves.FAKE_OUT); + await game.toNextTurn(); + expect(enemy2.hp).toBeLessThan(enemy2.getMaxHp()); }, 20000); }); diff --git a/src/test/moves/plasma_fists.test.ts b/src/test/moves/plasma_fists.test.ts new file mode 100644 index 00000000000..a9bd7660dfd --- /dev/null +++ b/src/test/moves/plasma_fists.test.ts @@ -0,0 +1,98 @@ +import { BattlerIndex } from "#app/battle"; +import { Type } from "#app/data/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 - Plasma Fists", () => { + 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.PLASMA_FISTS, Moves.TACKLE]) + .battleType("double") + .startingLevel(100) + .enemySpecies(Species.DUSCLOPS) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.TACKLE) + .enemyLevel(100); + }); + + it("should convert all subsequent Normal-type attacks to Electric-type", async () => { + await game.classicMode.startBattle([Species.DUSCLOPS, Species.BLASTOISE]); + + const field = game.scene.getField(true); + field.forEach(p => vi.spyOn(p, "getMoveType")); + + game.move.select(Moves.PLASMA_FISTS, 0, BattlerIndex.ENEMY); + game.move.select(Moves.TACKLE, 1, BattlerIndex.ENEMY_2); + + await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER); + await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2); + + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); + + await game.phaseInterceptor.to("BerryPhase", false); + + field.forEach(p => { + expect(p.getMoveType).toHaveLastReturnedWith(Type.ELECTRIC); + expect(p.hp).toBeLessThan(p.getMaxHp()); + }); + }); + + it("should not affect Normal-type attacks boosted by Pixilate", async () => { + game.override + .battleType("single") + .enemyAbility(Abilities.PIXILATE); + + await game.classicMode.startBattle([Species.ONIX]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + vi.spyOn(enemyPokemon, "getMoveType"); + + game.move.select(Moves.PLASMA_FISTS); + + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("BerryPhase", false); + + expect(enemyPokemon.getMoveType).toHaveLastReturnedWith(Type.FAIRY); + expect(playerPokemon.hp).toBeLessThan(playerPokemon.getMaxHp()); + }); + + it("should affect moves that become Normal type due to Normalize", async () => { + game.override + .battleType("single") + .enemyAbility(Abilities.NORMALIZE) + .enemyMoveset(Moves.WATER_GUN); + + await game.classicMode.startBattle([Species.DUSCLOPS]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + vi.spyOn(enemyPokemon, "getMoveType"); + + game.move.select(Moves.PLASMA_FISTS); + + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("BerryPhase", false); + + expect(enemyPokemon.getMoveType).toHaveLastReturnedWith(Type.ELECTRIC); + expect(playerPokemon.hp).toBeLessThan(playerPokemon.getMaxHp()); + }); +}); diff --git a/src/test/moves/substitute.test.ts b/src/test/moves/substitute.test.ts index d185c0ab471..3099446f081 100644 --- a/src/test/moves/substitute.test.ts +++ b/src/test/moves/substitute.test.ts @@ -1,13 +1,15 @@ import { BattlerIndex } from "#app/battle"; +import { ArenaTagSide } from "#app/data/arena-tag"; import { SubstituteTag, TrappedTag } from "#app/data/battler-tags"; import { allMoves, StealHeldItemChanceAttr } from "#app/data/move"; import { StatusEffect } from "#app/data/status-effect"; -import { Abilities } from "#app/enums/abilities"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { BerryType } from "#app/enums/berry-type"; -import { Moves } from "#app/enums/moves"; -import { Species } from "#app/enums/species"; -import { Stat } from "#app/enums/stat"; +import { Abilities } from "#enums/abilities"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { BerryType } from "#enums/berry-type"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { Stat } from "#enums/stat"; import { MoveResult } from "#app/field/pokemon"; import { CommandPhase } from "#app/phases/command-phase"; import GameManager from "#app/test/utils/gameManager"; @@ -194,6 +196,71 @@ describe("Moves - Substitute", () => { } ); + it( + "shouldn't block moves that target the user's side of the field", + async () => { + game.override.moveset(Moves.LIGHT_SCREEN); + + await game.classicMode.startBattle([Species.BLASTOISE]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(leadPokemon, "getMoveEffectiveness"); + + leadPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, Moves.NONE, leadPokemon.id); + + game.move.select(Moves.LIGHT_SCREEN); + + await game.toNextTurn(); + + expect(leadPokemon.getMoveEffectiveness).not.toHaveReturnedWith(0); + expect(game.scene.arena.getTagOnSide(ArenaTagType.LIGHT_SCREEN, ArenaTagSide.PLAYER)).toBeDefined(); + } + ); + + it( + "shouldn't block the opponent from setting hazards", + async () => { + game.override.enemyMoveset(Moves.STEALTH_ROCK); + + await game.classicMode.startBattle([Species.BLASTOISE]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(leadPokemon, "getMoveEffectiveness"); + + game.move.select(Moves.SUBSTITUTE); + + await game.toNextTurn(); + + expect(leadPokemon.getMoveEffectiveness).not.toHaveReturnedWith(0); + expect(game.scene.arena.getTagOnSide(ArenaTagType.STEALTH_ROCK, ArenaTagSide.PLAYER)).toBeDefined(); + } + ); + + it( + "shouldn't block moves that target both sides of the field", + async () => { + game.override + .moveset(Moves.TRICK_ROOM) + .enemyMoveset(Moves.GRAVITY); + + await game.classicMode.startBattle([Species.BLASTOISE]); + + const pokemon = game.scene.getField(true); + pokemon.forEach(p => { + vi.spyOn(p, "getMoveEffectiveness"); + p.addTag(BattlerTagType.SUBSTITUTE, 0, Moves.NONE, p.id); + }); + + game.move.select(Moves.TRICK_ROOM); + + await game.toNextTurn(); + + pokemon.forEach(p => expect(p.getMoveEffectiveness).not.toHaveReturnedWith(0)); + expect(game.scene.arena.getTag(ArenaTagType.TRICK_ROOM)).toBeDefined(); + expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined(); + } + ); + it( "should protect the user from flinching", async () => { diff --git a/src/test/moves/syrup_bomb.test.ts b/src/test/moves/syrup_bomb.test.ts new file mode 100644 index 00000000000..20cd590e457 --- /dev/null +++ b/src/test/moves/syrup_bomb.test.ts @@ -0,0 +1,82 @@ +import { allMoves } from "#app/data/move"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { Abilities } from "#enums/abilities"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { Stat } from "#enums/stat"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { BattlerIndex } from "#app/battle"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Moves - SYRUP BOMB", () => { + 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 + .starterSpecies(Species.MAGIKARP) + .enemySpecies(Species.SNORLAX) + .startingLevel(30) + .enemyLevel(100) + .moveset([Moves.SYRUP_BOMB, Moves.SPLASH]) + .enemyMoveset(Moves.SPLASH); + vi.spyOn(allMoves[Moves.SYRUP_BOMB], "accuracy", "get").mockReturnValue(100); + }); + + //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/syrup_bomb_(move) + + it("decreases the target Pokemon's speed stat once per turn for 3 turns", + async () => { + await game.startBattle([Species.MAGIKARP]); + + const targetPokemon = game.scene.getEnemyPokemon()!; + expect(targetPokemon.getStatStage(Stat.SPD)).toBe(0); + + game.move.select(Moves.SYRUP_BOMB); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.move.forceHit(); + await game.toNextTurn(); + expect(targetPokemon.getTag(BattlerTagType.SYRUP_BOMB)).toBeDefined(); + expect(targetPokemon.getStatStage(Stat.SPD)).toBe(-1); + + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + expect(targetPokemon.getTag(BattlerTagType.SYRUP_BOMB)).toBeDefined(); + expect(targetPokemon.getStatStage(Stat.SPD)).toBe(-2); + + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + expect(targetPokemon.getTag(BattlerTagType.SYRUP_BOMB)).toBeUndefined(); + expect(targetPokemon.getStatStage(Stat.SPD)).toBe(-3); + } + ); + + it("does not affect Pokemon with the ability Bulletproof", + async () => { + game.override.enemyAbility(Abilities.BULLETPROOF); + await game.startBattle([Species.MAGIKARP]); + + const targetPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.SYRUP_BOMB); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.move.forceHit(); + await game.toNextTurn(); + expect(targetPokemon.isFullHp()).toBe(true); + expect(targetPokemon.getTag(BattlerTagType.SYRUP_BOMB)).toBeUndefined(); + expect(targetPokemon.getStatStage(Stat.SPD)).toBe(0); + } + ); +}); diff --git a/src/test/system/game_data.test.ts b/src/test/system/game_data.test.ts new file mode 100644 index 00000000000..e3550e44fff --- /dev/null +++ b/src/test/system/game_data.test.ts @@ -0,0 +1,89 @@ +import * as BattleScene from "#app/battle-scene"; +import { SessionSaveData } from "#app/system/game-data"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import GameManager from "#test/utils/gameManager"; +import { http, HttpResponse } from "msw"; +import { setupServer } from "msw/node"; +import Phaser from "phaser"; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import * as account from "../../account"; + +const apiBase = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8001"; + +export const server = setupServer(); + +describe("System - Game Data", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + server.listen(); + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterAll(() => { + server.close(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([Moves.SPLASH]) + .battleType("single") + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + afterEach(() => { + server.resetHandlers(); + game.phaseInterceptor.restoreOg(); + }); + + describe("tryClearSession", () => { + beforeEach(() => { + vi.spyOn(BattleScene, "bypassLogin", "get").mockReturnValue(false); + vi.spyOn(game.scene.gameData, "getSessionSaveData").mockReturnValue({} as SessionSaveData); + vi.spyOn(account, "updateUserInfo").mockImplementation(async () => [true, 1]); + }); + + it("should return [true, true] if bypassLogin is true", async () => { + vi.spyOn(BattleScene, "bypassLogin", "get").mockReturnValue(true); + + const result = await game.scene.gameData.tryClearSession(game.scene, 0); + + expect(result).toEqual([true, true]); + }); + + it("should return [true, true] if successful", async () => { + server.use(http.post(`${apiBase}/savedata/session/clear`, () => HttpResponse.json({ success: true }))); + + const result = await game.scene.gameData.tryClearSession(game.scene, 0); + + expect(result).toEqual([true, true]); + expect(account.updateUserInfo).toHaveBeenCalled(); + }); + + it("should return [true, false] if not successful", async () => { + server.use(http.post(`${apiBase}/savedata/session/clear`, () => HttpResponse.json({ success: false }))); + + const result = await game.scene.gameData.tryClearSession(game.scene, 0); + + expect(result).toEqual([true, false]); + expect(account.updateUserInfo).toHaveBeenCalled(); + }); + + it("should return [false, false] session is out of date", async () => { + server.use( + http.post(`${apiBase}/savedata/session/clear`, () => HttpResponse.json({ error: "session out of date" })) + ); + + const result = await game.scene.gameData.tryClearSession(game.scene, 0); + + expect(result).toEqual([false, false]); + expect(account.updateUserInfo).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 6fad87df182..a2403de7e18 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -303,7 +303,7 @@ export default class GameManager { vi.spyOn(enemy, "getNextMove").mockReturnValueOnce({ move: moveId, - targets: (target && !legalTargets.multiple && legalTargets.targets.includes(target)) + targets: (target !== undefined && !legalTargets.multiple && legalTargets.targets.includes(target)) ? [target] : enemy.getNextTargets(moveId) }); diff --git a/src/timed-event-manager.ts b/src/timed-event-manager.ts index 9bfa3bdf54a..3caa84fd39b 100644 --- a/src/timed-event-manager.ts +++ b/src/timed-event-manager.ts @@ -1,5 +1,6 @@ import BattleScene from "#app/battle-scene"; import { TextStyle, addTextObject } from "#app/ui/text"; +import { nil } from "#app/utils"; import i18next from "i18next"; export enum EventType { @@ -64,19 +65,19 @@ export class TimedEventManager { let multiplier = 1; const shinyEvents = timedEvents.filter((te) => te.eventType === EventType.SHINY && this.isActive(te)); shinyEvents.forEach((se) => { - multiplier *= se.shinyMultiplier!; // TODO: is this bang correct? + multiplier *= se.shinyMultiplier ?? 1; }); return multiplier; } getEventBannerFilename(): string { - return timedEvents.find((te: TimedEvent) => this.isActive(te))?.bannerKey!; // TODO: is this bang correct? + return timedEvents.find((te: TimedEvent) => this.isActive(te))?.bannerKey ?? ""; } } export class TimedEventDisplay extends Phaser.GameObjects.Container { - private event: TimedEvent | null; + private event: TimedEvent | nil; private eventTimerText: Phaser.GameObjects.Text; private banner: Phaser.GameObjects.Image; private bannerShadow: Phaser.GameObjects.Rectangle; @@ -84,7 +85,7 @@ export class TimedEventDisplay extends Phaser.GameObjects.Container { constructor(scene: BattleScene, x: number, y: number, event?: TimedEvent) { super(scene, x, y); - this.event = event!; // TODO: is this bang correct? + this.event = event; this.setVisible(false); } diff --git a/src/ui/egg-summary-ui-handler.ts b/src/ui/egg-summary-ui-handler.ts index f4c3e056360..519722b1505 100644 --- a/src/ui/egg-summary-ui-handler.ts +++ b/src/ui/egg-summary-ui-handler.ts @@ -99,8 +99,9 @@ export default class EggSummaryUiHandler extends MessageUiHandler { clear() { super.clear(); - this.cursor = -1; this.scrollGridHandler.reset(); + this.cursor = -1; + this.summaryContainer.setVisible(false); this.pokemonIconsContainer.removeAll(true); this.pokemonContainers = []; @@ -164,8 +165,8 @@ export default class EggSummaryUiHandler extends MessageUiHandler { this.scrollGridHandler.setTotalElements(this.eggHatchData.length); this.updatePokemonIcons(); - this.setCursor(0); + this.scene.playSoundWithoutBgm("evolution_fanfare"); return true; } diff --git a/src/ui/title-ui-handler.ts b/src/ui/title-ui-handler.ts index 4087b397ff7..79baf407fb6 100644 --- a/src/ui/title-ui-handler.ts +++ b/src/ui/title-ui-handler.ts @@ -6,6 +6,7 @@ import { TextStyle, addTextObject, getTextStyleOptions } from "./text"; import { getSplashMessages } from "../data/splash-messages"; import i18next from "i18next"; import { TimedEventDisplay } from "#app/timed-event-manager"; +import { version } from "../../package.json"; export default class TitleUiHandler extends OptionSelectUiHandler { /** If the stats can not be retrieved, use this fallback value */ @@ -16,6 +17,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler { private splashMessage: string; private splashMessageText: Phaser.GameObjects.Text; private eventDisplay: TimedEventDisplay; + private appVersionText: Phaser.GameObjects.Text; private titleStatsTimer: NodeJS.Timeout | null; @@ -68,6 +70,11 @@ export default class TitleUiHandler extends OptionSelectUiHandler { loop: -1, yoyo: true, }); + + this.appVersionText = addTextObject(this.scene, logo.x - 60, logo.y + logo.displayHeight + 4, "", TextStyle.MONEY, { fontSize: "54px" }); + this.appVersionText.setOrigin(0.5, 0.5); + this.appVersionText.setAngle(0); + this.titleContainer.add(this.appVersionText); } updateTitleStats(): void { @@ -91,6 +98,8 @@ export default class TitleUiHandler extends OptionSelectUiHandler { this.splashMessage = Utils.randItem(getSplashMessages()); this.splashMessageText.setText(i18next.t(this.splashMessage, { count: TitleUiHandler.BATTLES_WON_FALLBACK })); + this.appVersionText.setText("v"+version); + const ui = this.getUi(); if (this.scene.eventManager.isEventActive()) { diff --git a/src/utils.ts b/src/utils.ts index 7faca0e5f6a..7a0def1a950 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,6 +2,8 @@ import { MoneyFormat } from "#enums/money-format"; import { Moves } from "#enums/moves"; import i18next from "i18next"; +export type nil = null | undefined; + export const MissingTextureKey = "__MISSING"; export function toReadableString(str: string): string { @@ -443,7 +445,7 @@ export function deltaRgb(rgb1: integer[], rgb2: integer[]): integer { } export function rgbHexToRgba(hex: string) { - const color = hex.match(/^([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i)!; // TODO: is this bang correct? + const color = hex.match(/^([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i) ?? ["000000", "00", "00", "00"]; return { r: parseInt(color[1], 16), g: parseInt(color[2], 16),