diff --git a/README.md b/README.md index c5d4f1f0525..72887579f17 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Check out our [Trello Board](https://trello.com/b/z10B703R/pokerogue-board) to s ### 🎨 Trainer Portraits - pkmn_realidea (Paid Commissions) -### 🎨 Pokemon Sprites +### 🎨 Pokemon Sprites and Animation - GAMEFREAK (Pokémon Black/White 2) - Smogon Sprite Project (Various Artists) - Skyflyer @@ -100,6 +100,7 @@ Check out our [Trello Board](https://trello.com/b/z10B703R/pokerogue-board) to s - bizcoeindoloro - mangalos810 - Involuntary-Twitch + - selstar ### 🎨 Move Animations - Pokémon Reborn diff --git a/public/images/pokemon/variant/169.json b/public/images/pokemon/variant/169.json index 92577fd0c34..3824fc5e351 100644 --- a/public/images/pokemon/variant/169.json +++ b/public/images/pokemon/variant/169.json @@ -1,15 +1,15 @@ { "0": { - "7b4a9c": "323f81", - "63197b": "142557", - "a55ace": "6265b4", + "7b4a9c": "d684ce", + "63197b": "9c528c", + "a55ace": "ffb5f7", "101010": "101010", - "b57bce": "99a3ee", - "08426b": "277eb2", - "ce0021": "ce0021", - "ffd600": "ffc3f4", - "d69400": "ff61e2", - "216b94": "4aa6ce", + "b57bce": "ffd6ef", + "08426b": "638400", + "ce0021": "940821", + "ffd600": "ffd600", + "d69400": "d69400", + "216b94": "8ca508", "ffffff": "ffffff", "a5a5a5": "a5a5a5", "6b6b6b": "6b6b6b" diff --git a/public/images/pokemon/variant/42.json b/public/images/pokemon/variant/42.json index 4ae5bf088cf..74311a59fb2 100644 --- a/public/images/pokemon/variant/42.json +++ b/public/images/pokemon/variant/42.json @@ -14,26 +14,26 @@ "943a7b": "175990" }, "1": { - "3a3a7b": "1d0f4e", - "5aadef": "3d4381", - "6384ce": "2f2a5f", - "631052": "892d03", - "ce6bb5": "f1a139", - "adceff": "666fb4", + "3a3a7b": "084a00", + "5aadef": "6b9c29", + "6384ce": "317300", + "631052": "c52931", + "ce6bb5": "ffada5", + "adceff": "84d64a", "000000": "000000", - "ad52ad": "d5711b", + "ad52ad": "e6737b", "636363": "636363", "ffffff": "ffffff", "d6d6d6": "d6d6d6", - "943a7b": "af4e0c" + "943a7b": "d6525a" }, "2": { - "3a3a7b": "584055", - "5aadef": "c1aec0", - "6384ce": "866881", + "3a3a7b": "3d2349", + "5aadef": "cbabca", + "6384ce": "916c8b", "631052": "54070c", "ce6bb5": "bc3b1d", - "adceff": "dfcddd", + "adceff": "e8d2e6", "000000": "000000", "ad52ad": "94241c", "636363": "636363", diff --git a/public/images/pokemon/variant/890-eternamax_2.png b/public/images/pokemon/variant/890-eternamax_2.png index 30c8333a431..b5aab00c241 100644 Binary files a/public/images/pokemon/variant/890-eternamax_2.png and b/public/images/pokemon/variant/890-eternamax_2.png differ diff --git a/public/images/pokemon/variant/890-eternamax_3.png b/public/images/pokemon/variant/890-eternamax_3.png index 48cd4dbe14a..29a3d84c0d6 100644 Binary files a/public/images/pokemon/variant/890-eternamax_3.png and b/public/images/pokemon/variant/890-eternamax_3.png differ diff --git a/public/images/pokemon/variant/_masterlist.json b/public/images/pokemon/variant/_masterlist.json index 22cb44852ea..ea9fe53622b 100644 --- a/public/images/pokemon/variant/_masterlist.json +++ b/public/images/pokemon/variant/_masterlist.json @@ -909,6 +909,11 @@ 2, 2 ], + "472": [ + 0, + 0, + 0 + ], "475-mega": [ 0, 2, @@ -2050,6 +2055,16 @@ 2, 1 ], + "41": [ + 0, + 1, + 1 + ], + "42": [ + 0, + 1, + 1 + ], "308": [ 0, 1, @@ -2869,7 +2884,7 @@ ], "383": [ 0, - 2, + 1, 1 ], "384-mega": [ @@ -4143,6 +4158,16 @@ 1, 1 ], + "41": [ + 0, + 1, + 1 + ], + "42": [ + 0, + 1, + 1 + ], "308": [ 0, 1, @@ -4538,7 +4563,7 @@ ], "747": [ 0, - 1, + 2, 1 ], "748": [ @@ -4591,6 +4616,11 @@ 1, 1 ], + "770": [ + 0, + 0, + 0 + ], "771": [ 0, 2, diff --git a/public/images/pokemon/variant/back/383.json b/public/images/pokemon/variant/back/383.json index bfa8917302b..f3760c0244e 100644 --- a/public/images/pokemon/variant/back/383.json +++ b/public/images/pokemon/variant/back/383.json @@ -1,4 +1,21 @@ { + "1": { + "000000": "000000", + "7b2129": "032a10", + "9c2929": "10371a", + "ff736b": "419e49", + "ff2129": "2b5b32", + "bd3131": "0f461c", + "3a3a3a": "383540", + "736363": "625769", + "ffffff": "fff6de", + "bdbdd6": "e5d4b6", + "9c6b31": "d51b3e", + "ffce31": "ff435d", + "94848c": "72798b", + "ffbdbd": "49c74f", + "ad9ca5": "ad9ca5" + }, "2": { "000000": "000000", "7b2129": "123953", diff --git a/public/images/pokemon/variant/back/622.json b/public/images/pokemon/variant/back/622.json index 4f0338f4e15..9ad7426a9d7 100644 --- a/public/images/pokemon/variant/back/622.json +++ b/public/images/pokemon/variant/back/622.json @@ -1,19 +1,19 @@ { "0": { - "298c8c": "1c3820", - "5aada5": "3e5d43", - "84cece": "758076", - "004a52": "102c16", - "106b63": "0d1e10", + "298c8c": "427373", + "5aada5": "6b9c94", + "84cece": "94bdbd", + "004a52": "192121", + "106b63": "21524a", "191921": "191921", - "106b7b": "102c16", - "29848c": "224427", - "6b4200": "54190e", - "c59c52": "a65c3f", - "9c7329": "763826", - "dece94": "e46424", - "ffefa5": "ff9942", - "bdad73": "cb3000" + "106b7b": "293a42", + "29848c": "424a5a", + "6b4200": "523a10", + "c59c52": "b59463", + "9c7329": "846b3a", + "dece94": "b5de21", + "ffefa5": "d6ff42", + "bdad73": "94bd00" }, "1": { "298c8c": "793907", diff --git a/public/images/pokemon/variant/back/female/41.json b/public/images/pokemon/variant/back/female/41.json new file mode 100644 index 00000000000..87c18df01ac --- /dev/null +++ b/public/images/pokemon/variant/back/female/41.json @@ -0,0 +1,24 @@ +{ + "1": { + "101010": "101010", + "8cb5ef": "4e538f", + "4a427b": "14093b", + "637bb5": "37326f", + "73215a": "aa4c18", + "b5529c": "cc7b32", + "bdceff": "868ecc", + "ffffff": "ffffff", + "636363": "636363" + }, + "2": { + "101010": "101010", + "8cb5ef": "cbabca", + "4a427b": "4d3259", + "637bb5": "916c8b", + "73215a": "670f10", + "b5529c": "94241c", + "bdceff": "e8d2e6", + "ffffff": "ffffff", + "636363": "636363" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/back/female/42.json b/public/images/pokemon/variant/back/female/42.json new file mode 100644 index 00000000000..d2be9f7ced5 --- /dev/null +++ b/public/images/pokemon/variant/back/female/42.json @@ -0,0 +1,24 @@ +{ + "1": { + "3a3a7b": "1d0f4e", + "6384ce": "2f2a5f", + "adceff": "666fb4", + "5aadef": "3d4381", + "631052": "892d03", + "000000": "000000", + "ce6bb5": "f1a139", + "ad52ad": "d5711b", + "943a7b": "af4e0c" + }, + "2": { + "3a3a7b": "3d2349", + "6384ce": "916c8b", + "adceff": "e8d2e6", + "5aadef": "cbabca", + "631052": "54070c", + "000000": "000000", + "ce6bb5": "bc3b1d", + "ad52ad": "94241c", + "943a7b": "6c1314" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/747_2.json b/public/images/pokemon/variant/exp/747_2.json new file mode 100644 index 00000000000..4273c853522 --- /dev/null +++ b/public/images/pokemon/variant/exp/747_2.json @@ -0,0 +1,188 @@ +{ + "textures": [ + { + "image": "747_2.png", + "format": "RGBA8888", + "size": { + "w": 110, + "h": 110 + }, + "scale": 1, + "frames": [ + { + "filename": "0003.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 55, + "h": 47 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 55, + "h": 47 + }, + "frame": { + "x": 0, + "y": 0, + "w": 55, + "h": 47 + } + }, + { + "filename": "0004.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 55, + "h": 47 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 55, + "h": 47 + }, + "frame": { + "x": 0, + "y": 0, + "w": 55, + "h": 47 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 55, + "h": 47 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 55, + "h": 47 + }, + "frame": { + "x": 0, + "y": 0, + "w": 55, + "h": 47 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 55, + "h": 47 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 55, + "h": 47 + }, + "frame": { + "x": 0, + "y": 0, + "w": 55, + "h": 47 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 55, + "h": 47 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 55, + "h": 46 + }, + "frame": { + "x": 55, + "y": 0, + "w": 55, + "h": 46 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 55, + "h": 47 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 55, + "h": 46 + }, + "frame": { + "x": 55, + "y": 0, + "w": 55, + "h": 46 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 55, + "h": 47 + }, + "spriteSourceSize": { + "x": 0, + "y": 1, + "w": 55, + "h": 46 + }, + "frame": { + "x": 55, + "y": 46, + "w": 55, + "h": 46 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 55, + "h": 47 + }, + "spriteSourceSize": { + "x": 0, + "y": 1, + "w": 55, + "h": 46 + }, + "frame": { + "x": 55, + "y": 46, + "w": 55, + "h": 46 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:e4c4f790c4f0286f608dcdb15a609059:464f7ae7db1c0d034c2a1a65612b0da8:b26f7254994561969f00f765318acf1c$" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/890-eternamax_2.json b/public/images/pokemon/variant/exp/890-eternamax_2.json new file mode 100644 index 00000000000..895a2f27841 --- /dev/null +++ b/public/images/pokemon/variant/exp/890-eternamax_2.json @@ -0,0 +1,755 @@ +{ + "textures": [ + { + "image": "890-eternamax_2.png", + "format": "RGBA8888", + "size": { + "w": 579, + "h": 579 + }, + "scale": 1, + "frames": [ + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 100, + "h": 98 + }, + "frame": { + "x": 0, + "y": 0, + "w": 100, + "h": 98 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 95, + "h": 100 + }, + "frame": { + "x": 100, + "y": 0, + "w": 95, + "h": 100 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 8, + "w": 91, + "h": 100 + }, + "frame": { + "x": 0, + "y": 98, + "w": 91, + "h": 100 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 96, + "h": 98 + }, + "frame": { + "x": 91, + "y": 100, + "w": 96, + "h": 98 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 95, + "h": 99 + }, + "frame": { + "x": 187, + "y": 100, + "w": 95, + "h": 99 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 9, + "y": 10, + "w": 91, + "h": 98 + }, + "frame": { + "x": 0, + "y": 198, + "w": 91, + "h": 98 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 10, + "w": 88, + "h": 98 + }, + "frame": { + "x": 91, + "y": 198, + "w": 88, + "h": 98 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 10, + "w": 95, + "h": 97 + }, + "frame": { + "x": 195, + "y": 0, + "w": 95, + "h": 97 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 179, + "y": 199, + "w": 95, + "h": 97 + } + }, + { + "filename": "0004.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 274, + "y": 199, + "w": 95, + "h": 97 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 290, + "y": 0, + "w": 95, + "h": 97 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 94, + "h": 96 + }, + "frame": { + "x": 282, + "y": 97, + "w": 94, + "h": 96 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 11, + "w": 90, + "h": 97 + }, + "frame": { + "x": 369, + "y": 193, + "w": 90, + "h": 97 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 13, + "w": 93, + "h": 95 + }, + "frame": { + "x": 385, + "y": 0, + "w": 93, + "h": 95 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 9, + "w": 91, + "h": 96 + }, + "frame": { + "x": 385, + "y": 95, + "w": 91, + "h": 96 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 97 + }, + "frame": { + "x": 369, + "y": 290, + "w": 87, + "h": 97 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 90, + "h": 96 + }, + "frame": { + "x": 456, + "y": 290, + "w": 90, + "h": 96 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 8, + "w": 90, + "h": 96 + }, + "frame": { + "x": 459, + "y": 191, + "w": 90, + "h": 96 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 8, + "w": 90, + "h": 95 + }, + "frame": { + "x": 476, + "y": 95, + "w": 90, + "h": 95 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 89, + "h": 95 + }, + "frame": { + "x": 478, + "y": 0, + "w": 89, + "h": 95 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 89, + "h": 96 + }, + "frame": { + "x": 456, + "y": 386, + "w": 89, + "h": 96 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 11, + "w": 89, + "h": 95 + }, + "frame": { + "x": 0, + "y": 296, + "w": 89, + "h": 95 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 9, + "y": 14, + "w": 89, + "h": 94 + }, + "frame": { + "x": 89, + "y": 296, + "w": 89, + "h": 94 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 88, + "h": 95 + }, + "frame": { + "x": 178, + "y": 296, + "w": 88, + "h": 95 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 95 + }, + "frame": { + "x": 89, + "y": 390, + "w": 87, + "h": 95 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 89, + "h": 94 + }, + "frame": { + "x": 0, + "y": 391, + "w": 89, + "h": 94 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 14, + "w": 89, + "h": 93 + }, + "frame": { + "x": 266, + "y": 387, + "w": 89, + "h": 93 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 16, + "y": 13, + "w": 85, + "h": 91 + }, + "frame": { + "x": 266, + "y": 296, + "w": 85, + "h": 91 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 13, + "w": 88, + "h": 94 + }, + "frame": { + "x": 176, + "y": 391, + "w": 88, + "h": 94 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 13, + "w": 87, + "h": 94 + }, + "frame": { + "x": 355, + "y": 387, + "w": 87, + "h": 94 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 16, + "y": 11, + "w": 87, + "h": 94 + }, + "frame": { + "x": 264, + "y": 480, + "w": 87, + "h": 94 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 14, + "w": 89, + "h": 93 + }, + "frame": { + "x": 351, + "y": 481, + "w": 89, + "h": 93 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 93 + }, + "frame": { + "x": 440, + "y": 482, + "w": 87, + "h": 93 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 10, + "w": 86, + "h": 94 + }, + "frame": { + "x": 0, + "y": 485, + "w": 86, + "h": 94 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 14, + "w": 85, + "h": 91 + }, + "frame": { + "x": 86, + "y": 485, + "w": 85, + "h": 91 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:8fd9e1830200ec8e4aac8571cc2d27a6:c966e3efce03c7bae43d7bca6d6dfa62:cedd2711a12bbacba5623505fe88bd92$" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/890-eternamax_3.json b/public/images/pokemon/variant/exp/890-eternamax_3.json new file mode 100644 index 00000000000..ecc084c689f --- /dev/null +++ b/public/images/pokemon/variant/exp/890-eternamax_3.json @@ -0,0 +1,755 @@ +{ + "textures": [ + { + "image": "890-eternamax_3.png", + "format": "RGBA8888", + "size": { + "w": 579, + "h": 579 + }, + "scale": 1, + "frames": [ + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 100, + "h": 98 + }, + "frame": { + "x": 0, + "y": 0, + "w": 100, + "h": 98 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 95, + "h": 100 + }, + "frame": { + "x": 100, + "y": 0, + "w": 95, + "h": 100 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 8, + "w": 91, + "h": 100 + }, + "frame": { + "x": 0, + "y": 98, + "w": 91, + "h": 100 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 96, + "h": 98 + }, + "frame": { + "x": 91, + "y": 100, + "w": 96, + "h": 98 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 9, + "w": 95, + "h": 99 + }, + "frame": { + "x": 187, + "y": 100, + "w": 95, + "h": 99 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 9, + "y": 10, + "w": 91, + "h": 98 + }, + "frame": { + "x": 0, + "y": 198, + "w": 91, + "h": 98 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 10, + "w": 88, + "h": 98 + }, + "frame": { + "x": 91, + "y": 198, + "w": 88, + "h": 98 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 10, + "w": 95, + "h": 97 + }, + "frame": { + "x": 195, + "y": 0, + "w": 95, + "h": 97 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 179, + "y": 199, + "w": 95, + "h": 97 + } + }, + { + "filename": "0004.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 274, + "y": 199, + "w": 95, + "h": 97 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 95, + "h": 97 + }, + "frame": { + "x": 290, + "y": 0, + "w": 95, + "h": 97 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 11, + "w": 94, + "h": 96 + }, + "frame": { + "x": 282, + "y": 97, + "w": 94, + "h": 96 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 11, + "w": 90, + "h": 97 + }, + "frame": { + "x": 369, + "y": 193, + "w": 90, + "h": 97 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 8, + "y": 13, + "w": 93, + "h": 95 + }, + "frame": { + "x": 385, + "y": 0, + "w": 93, + "h": 95 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 9, + "w": 91, + "h": 96 + }, + "frame": { + "x": 385, + "y": 95, + "w": 91, + "h": 96 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 97 + }, + "frame": { + "x": 369, + "y": 290, + "w": 87, + "h": 97 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 90, + "h": 96 + }, + "frame": { + "x": 456, + "y": 290, + "w": 90, + "h": 96 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 8, + "w": 90, + "h": 96 + }, + "frame": { + "x": 459, + "y": 191, + "w": 90, + "h": 96 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 8, + "w": 90, + "h": 95 + }, + "frame": { + "x": 476, + "y": 95, + "w": 90, + "h": 95 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 89, + "h": 95 + }, + "frame": { + "x": 478, + "y": 0, + "w": 89, + "h": 95 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 89, + "h": 96 + }, + "frame": { + "x": 456, + "y": 386, + "w": 89, + "h": 96 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 11, + "w": 89, + "h": 95 + }, + "frame": { + "x": 0, + "y": 296, + "w": 89, + "h": 95 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 9, + "y": 14, + "w": 89, + "h": 94 + }, + "frame": { + "x": 89, + "y": 296, + "w": 89, + "h": 94 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 88, + "h": 95 + }, + "frame": { + "x": 178, + "y": 296, + "w": 88, + "h": 95 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 95 + }, + "frame": { + "x": 89, + "y": 390, + "w": 87, + "h": 95 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 12, + "w": 89, + "h": 94 + }, + "frame": { + "x": 0, + "y": 391, + "w": 89, + "h": 94 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 14, + "w": 89, + "h": 93 + }, + "frame": { + "x": 266, + "y": 387, + "w": 89, + "h": 93 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 16, + "y": 13, + "w": 85, + "h": 91 + }, + "frame": { + "x": 266, + "y": 296, + "w": 85, + "h": 91 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 13, + "w": 88, + "h": 94 + }, + "frame": { + "x": 176, + "y": 391, + "w": 88, + "h": 94 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 13, + "w": 87, + "h": 94 + }, + "frame": { + "x": 355, + "y": 387, + "w": 87, + "h": 94 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 16, + "y": 11, + "w": 87, + "h": 94 + }, + "frame": { + "x": 264, + "y": 480, + "w": 87, + "h": 94 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 11, + "y": 14, + "w": 89, + "h": 93 + }, + "frame": { + "x": 351, + "y": 481, + "w": 89, + "h": 93 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 87, + "h": 93 + }, + "frame": { + "x": 440, + "y": 482, + "w": 87, + "h": 93 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 10, + "w": 86, + "h": 94 + }, + "frame": { + "x": 0, + "y": 485, + "w": 86, + "h": 94 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 112, + "h": 112 + }, + "spriteSourceSize": { + "x": 13, + "y": 14, + "w": 85, + "h": 91 + }, + "frame": { + "x": 86, + "y": 485, + "w": 85, + "h": 91 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:8fd9e1830200ec8e4aac8571cc2d27a6:c966e3efce03c7bae43d7bca6d6dfa62:cedd2711a12bbacba5623505fe88bd92$" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/female/41.json b/public/images/pokemon/variant/female/41.json new file mode 100644 index 00000000000..08446ef4908 --- /dev/null +++ b/public/images/pokemon/variant/female/41.json @@ -0,0 +1,26 @@ +{ + "1": { + "101010": "101010", + "637bb5": "37326f", + "4a427b": "14093b", + "bdceff": "868ecc", + "8cb5ef": "4e538f", + "b5529c": "cc7b32", + "73215a": "aa4c18", + "d673bd": "f0ad57", + "ffffff": "ffffff", + "636363": "636363" + }, + "2": { + "101010": "101010", + "637bb5": "916c8b", + "4a427b": "4d3259", + "bdceff": "e8d2e6", + "8cb5ef": "cbabca", + "b5529c": "94241c", + "73215a": "670f10", + "d673bd": "bc3b1d", + "ffffff": "ffffff", + "636363": "636363" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/female/42.json b/public/images/pokemon/variant/female/42.json new file mode 100644 index 00000000000..000e127793e --- /dev/null +++ b/public/images/pokemon/variant/female/42.json @@ -0,0 +1,30 @@ +{ + "1": { + "3a3a7b": "1d0f4e", + "5aadef": "3d4381", + "6384ce": "2f2a5f", + "631052": "892d03", + "ce6bb5": "f1a139", + "adceff": "666fb4", + "000000": "000000", + "ad52ad": "d5711b", + "636363": "636363", + "ffffff": "ffffff", + "d6d6d6": "d6d6d6", + "943a7b": "af4e0c" + }, + "2": { + "3a3a7b": "3d2349", + "5aadef": "cbabca", + "6384ce": "916c8b", + "631052": "54070c", + "ce6bb5": "bc3b1d", + "adceff": "e8d2e6", + "000000": "000000", + "ad52ad": "94241c", + "636363": "636363", + "ffffff": "ffffff", + "d6d6d6": "d6d6d6", + "943a7b": "6c1314" + } +} \ No newline at end of file diff --git a/src/battle.ts b/src/battle.ts index 580bad9a508..601cbeb9cb6 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -11,6 +11,7 @@ import { BattleSpec } from "./enums/battle-spec"; import { PlayerGender } from "./system/game-data"; import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier"; import { MoneyAchv } from "./system/achv"; +import { PokeballType } from "./data/pokeball"; export enum BattleType { WILD, @@ -61,6 +62,7 @@ export default class Battle { public battleSeed: string; private battleSeedState: string; public moneyScattered: number; + public lastUsedPokeball: PokeballType; private rngCounter: integer = 0; @@ -86,6 +88,7 @@ export default class Battle { this.battleSeed = Utils.randomString(16, true); this.battleSeedState = null; this.moneyScattered = 0; + this.lastUsedPokeball = null; } private initBattleSpec(): void { diff --git a/src/data/ability.ts b/src/data/ability.ts index a257525efd0..c236aa85805 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -9,7 +9,7 @@ import { BattlerTag } from "./battler-tags"; import { BattlerTagType } from "./enums/battler-tag-type"; import { StatusEffect, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect"; import { Gender } from "./gender"; -import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, RecoilAttr, StatusMoveTypeImmunityAttr, FlinchAttr, OneHitKOAttr, HitHealAttr, StrengthSapHealAttr, allMoves, StatusMove, VariablePowerAttr, applyMoveAttrs } from "./move"; +import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, RecoilAttr, StatusMoveTypeImmunityAttr, FlinchAttr, OneHitKOAttr, HitHealAttr, StrengthSapHealAttr, allMoves, StatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr } from "./move"; import { ArenaTagSide, ArenaTrapTag } from "./arena-tag"; import { ArenaTagType } from "./enums/arena-tag-type"; import { Stat } from "./pokemon-stat"; @@ -22,6 +22,7 @@ import i18next, { Localizable } from "#app/plugins/i18n.js"; import { Command } from "../ui/command-ui-handler"; import Battle from "#app/battle.js"; import { ability } from "#app/locales/en/ability.js"; +import { PokeballType, getPokeballName } from "./pokeball"; export class Ability implements Localizable { public id: Abilities; @@ -490,8 +491,13 @@ export class PostDefendFormChangeAbAttr extends PostDefendAbAttr { export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean { const attackPriority = new Utils.IntegerHolder(move.getMove().priority); + applyMoveAttrs(IncrementMovePriorityAttr,attacker,null,move.getMove(),attackPriority); applyAbAttrs(IncrementMovePriorityAbAttr, attacker, null, move.getMove(), attackPriority); + if(move.getMove().moveTarget===MoveTarget.USER) { + return false; + } + if(attackPriority.value > 0 && !move.getMove().isMultiTarget()) { cancelled.value = true; return true; @@ -1822,6 +1828,36 @@ export class MultCritAbAttr extends AbAttr { } } +/** + * Guarantees a critical hit according to the given condition, except if target prevents critical hits. ie. Merciless + * @extends AbAttr + * @see {@linkcode apply} + */ +export class ConditionalCritAbAttr extends AbAttr { + private condition: PokemonAttackCondition; + + constructor(condition: PokemonAttackCondition, checkUser?: Boolean) { + super(); + + this.condition = condition; + } + + /** + * @param pokemon {@linkcode Pokemon} user. + * @param args [0] {@linkcode Utils.BooleanHolder} If true critical hit is guaranteed. + * [1] {@linkcode Pokemon} Target. + * [2] {@linkcode Move} used by ability user. + */ + apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + const target = (args[1] as Pokemon); + const move = (args[2] as Move); + if(!this.condition(pokemon,target,move)) + return false; + + (args[0] as Utils.BooleanHolder).value = true; + return true; + } +} export class BlockNonDirectDamageAbAttr extends AbAttr { apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { @@ -2246,6 +2282,61 @@ export class PostTurnFormChangeAbAttr extends PostTurnAbAttr { } } + +/** + * Attribute used for abilities (Bad Dreams) that damages the opponents for being asleep + */ +export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { + + /** + * Deals damage to all sleeping opponents equal to 1/8 of their max hp (min 1) + * @param {Pokemon} pokemon Pokemon that has this ability + * @param {boolean} passive N/A + * @param {any[]} args N/A + * @returns {boolean} true if any opponents are sleeping + */ + applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + let hadEffect: boolean = false; + for(let opp of pokemon.getOpponents()) { + if(opp.status !== undefined && opp.status.effect === StatusEffect.SLEEP) { + opp.damageAndUpdate(Math.floor(Math.max(1, opp.getMaxHp() / 8)), HitResult.OTHER); + pokemon.scene.queueMessage(i18next.t('abilityTriggers:badDreams', {pokemonName: `${getPokemonPrefix(opp)}${opp.name}`})); + hadEffect = true; + } + + } + return hadEffect; + } +} + + +/** + * Grabs the last failed Pokeball used + * @extends PostTurnAbAttr + * @see {@linkcode applyPostTurn} */ +export class FetchBallAbAttr extends PostTurnAbAttr { + constructor() { + super(); + } + /** + * Adds the last used Pokeball back into the player's inventory + * @param pokemon {@linkcode Pokemon} with this ability + * @param passive N/A + * @param args N/A + * @returns true if player has used a pokeball and this pokemon is owned by the player + */ + applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + let lastUsed = pokemon.scene.currentBattle.lastUsedPokeball; + if(lastUsed != null && pokemon.isPlayer) { + pokemon.scene.pokeballCounts[lastUsed]++; + pokemon.scene.currentBattle.lastUsedPokeball = null; + pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` found a\n${getPokeballName(lastUsed)}!`)); + return true; + } + return false; + } +} + export class PostBiomeChangeAbAttr extends AbAttr { } export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr { @@ -2338,18 +2429,42 @@ export class RunSuccessAbAttr extends AbAttr { } } +/** + * Base class for checking if a Pokemon is trapped by arena trap + * @extends AbAttr + * @see {@linkcode applyCheckTrapped} + */ export class CheckTrappedAbAttr extends AbAttr { constructor() { super(false); } - - applyCheckTrapped(pokemon: Pokemon, passive: boolean, trapped: Utils.BooleanHolder, args: any[]): boolean | Promise { + + applyCheckTrapped(pokemon: Pokemon, passive: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean | Promise { return false; } } +/** + * Determines whether a Pokemon is blocked from switching/running away + * because of a trapping ability or move. + * @extends CheckTrappedAbAttr + * @see {@linkcode applyCheckTrapped} + */ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { - applyCheckTrapped(pokemon: Pokemon, passive: boolean, trapped: Utils.BooleanHolder, args: any[]): boolean { + /** + * Checks if enemy Pokemon is trapped by an Arena Trap-esque ability + * @param pokemon The {@link Pokemon} with this {@link AbAttr} + * @param passive N/A + * @param trapped {@link Utils.BooleanHolder} indicating whether the other Pokemon is trapped or not + * @param otherPokemon The {@link Pokemon} that is affected by an Arena Trap ability + * @param args N/A + * @returns if enemy Pokemon is trapped or not + */ + applyCheckTrapped(pokemon: Pokemon, passive: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean { + if (otherPokemon.getTypes().includes(Type.GHOST)){ + trapped.value = false; + return false; + } trapped.value = true; return true; } @@ -2614,7 +2729,6 @@ export class SuppressFieldAbilitiesAbAttr extends AbAttr { } } - export class AlwaysHitAbAttr extends AbAttr { } export class UncopiableAbilityAbAttr extends AbAttr { @@ -2859,8 +2973,8 @@ export function applyPostTerrainChangeAbAttrs(attrType: { new(...args: any[]): P } export function applyCheckTrappedAbAttrs(attrType: { new(...args: any[]): CheckTrappedAbAttr }, - pokemon: Pokemon, trapped: Utils.BooleanHolder, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyCheckTrapped(pokemon, passive, trapped, args), args, true); + pokemon: Pokemon, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyCheckTrapped(pokemon, passive, trapped, otherPokemon, args), args, true); } export function applyPostBattleAbAttrs(attrType: { new(...args: any[]): PostBattleAbAttr }, @@ -3241,7 +3355,7 @@ export function initAbilities() { .ignorable() .partial(), new Ability(Abilities.BAD_DREAMS, 4) - .unimplemented(), + .attr(PostTurnHurtIfSleepingAbAttr), new Ability(Abilities.PICKPOCKET, 5) .attr(PostDefendStealHeldItemAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT)), new Ability(Abilities.SHEER_FORCE, 5) @@ -3442,7 +3556,7 @@ export function initAbilities() { new Ability(Abilities.WATER_COMPACTION, 7) .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.WATER && move.category !== MoveCategory.STATUS, BattleStat.DEF, 2), new Ability(Abilities.MERCILESS, 7) - .unimplemented(), + .attr(ConditionalCritAbAttr, (user, target, move) => target.status?.effect === StatusEffect.TOXIC || target.status?.effect === StatusEffect.POISON), new Ability(Abilities.SHIELDS_DOWN, 7) .attr(PostBattleInitFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) .attr(PostSummonFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) @@ -3597,7 +3711,8 @@ export function initAbilities() { new Ability(Abilities.LIBERO, 8) .unimplemented(), new Ability(Abilities.BALL_FETCH, 8) - .unimplemented(), + .attr(FetchBallAbAttr) + .condition(getOncePerBattleCondition(Abilities.BALL_FETCH)), new Ability(Abilities.COTTON_DOWN, 8) .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattleStat.SPD, -1, false, true) .bypassFaint(), diff --git a/src/data/berry.ts b/src/data/berry.ts index 1521f3488ef..e13d4532b3e 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -130,9 +130,11 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { return (pokemon: Pokemon) => { if (pokemon.battleData) pokemon.battleData.berriesEaten.push(berryType); - const ppRestoreMove = pokemon.getMoveset().find(m => !m.getPpRatio()); - ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0); - pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` restored PP to its move ${ppRestoreMove.getName()}\nusing its ${getBerryName(berryType)}!`)); + const ppRestoreMove = pokemon.getMoveset().find(m => !m.getPpRatio()) ? pokemon.getMoveset().find(m => !m.getPpRatio()) : pokemon.getMoveset().find(m => m.getPpRatio() < 1); + if(ppRestoreMove !== undefined){ + ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0); + pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` restored PP to its move ${ppRestoreMove.getName()}\nusing its ${getBerryName(berryType)}!`)); + } }; } } \ No newline at end of file diff --git a/src/data/egg-moves.ts b/src/data/egg-moves.ts index 61099c72fdf..49203026a3c 100644 --- a/src/data/egg-moves.ts +++ b/src/data/egg-moves.ts @@ -15,14 +15,14 @@ export const speciesEggMoves = { [Species.SPEAROW]: [ Moves.FLOATY_FALL, Moves.EXTREME_SPEED, Moves.TIDY_UP, Moves.TRIPLE_ARROWS ], [Species.EKANS]: [ Moves.SHED_TAIL, Moves.DRAGON_DANCE, Moves.SLACK_OFF, Moves.NOXIOUS_TORQUE ], [Species.SANDSHREW]: [ Moves.DIRE_CLAW, Moves.CEASELESS_EDGE, Moves.SHORE_UP, Moves.PRECIPICE_BLADES ], - [Species.NIDORAN_F]: [ Moves.DIRE_CLAW, Moves.SHORE_UP, Moves.THOUSAND_WAVES, Moves.SALT_CURE ], + [Species.NIDORAN_F]: [ Moves.SANDSEAR_STORM, Moves.BANEFUL_BUNKER, Moves.NO_RETREAT, Moves.MALIGNANT_CHAIN ], [Species.NIDORAN_M]: [ Moves.NOXIOUS_TORQUE, Moves.KINGS_SHIELD, Moves.NO_RETREAT, Moves.PRECIPICE_BLADES ], - [Species.VULPIX]: [ Moves.MOONBLAST, Moves.PSYCHIC, Moves.MORNING_SUN, Moves.TAIL_GLOW ], - [Species.ZUBAT]: [ Moves.FLOATY_FALL, Moves.DIRE_CLAW, Moves.SWORDS_DANCE, Moves.BRAVE_BIRD ], + [Species.VULPIX]: [ Moves.MOONBLAST, Moves.ICE_BEAM, Moves.MORNING_SUN, Moves.TAIL_GLOW ], + [Species.ZUBAT]: [ Moves.FLOATY_FALL, Moves.DIRE_CLAW, Moves.PARTING_SHOT, Moves.WICKED_BLOW ], [Species.ODDISH]: [ Moves.SLUDGE_BOMB, Moves.FIERY_DANCE, Moves.STRENGTH_SAP, Moves.SPORE ], [Species.PARAS]: [ Moves.FIRST_IMPRESSION, Moves.SAPPY_SEED, Moves.CRABHAMMER, Moves.DIRE_CLAW ], [Species.VENONAT]: [ Moves.SLUDGE_BOMB, Moves.MOONLIGHT, Moves.EARTH_POWER, Moves.MYSTICAL_POWER ], - [Species.DIGLETT]: [ Moves.STRENGTH_SAP, Moves.SWORDS_DANCE, Moves.ICE_SPINNER, Moves.HEADLONG_RUSH ], + [Species.DIGLETT]: [ Moves.REVERSAL, Moves.SWORDS_DANCE, Moves.ICE_SPINNER, Moves.HEADLONG_RUSH ], [Species.MEOWTH]: [ Moves.COVET, Moves.HAPPY_HOUR, Moves.PARTING_SHOT, Moves.MAKE_IT_RAIN ], [Species.PSYDUCK]: [ Moves.MYSTICAL_POWER, Moves.AQUA_STEP, Moves.AURA_SPHERE, Moves.MIND_BLOWN ], [Species.MANKEY]: [ Moves.DRAIN_PUNCH, Moves.RAGING_FURY, Moves.METEOR_MASH, Moves.NO_RETREAT ], @@ -34,18 +34,18 @@ export const speciesEggMoves = { [Species.TENTACOOL]: [ Moves.BANEFUL_BUNKER, Moves.STRENGTH_SAP, Moves.HAZE, Moves.MALIGNANT_CHAIN ], [Species.GEODUDE]: [ Moves.BODY_PRESS, Moves.BULK_UP, Moves.SHORE_UP, Moves.HEAD_SMASH ], [Species.PONYTA]: [ Moves.HIGH_HORSEPOWER, Moves.FIRE_LASH, Moves.SWORDS_DANCE, Moves.VOLT_TACKLE ], - [Species.SLOWPOKE]: [ Moves.BOUNCY_BUBBLE, Moves.PARTING_SHOT, Moves.COSMIC_POWER, Moves.LUMINA_CRASH ], + [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.BATON_PASS, Moves.SACRED_SWORD, Moves.ROOST, Moves.VICTORY_DANCE ], [Species.DODUO]: [ Moves.TRIPLE_AXEL, Moves.MULTI_ATTACK, Moves.FLOATY_FALL, Moves.TRIPLE_ARROWS ], [Species.SEEL]: [ Moves.FREEZE_DRY, Moves.CHILLY_RECEPTION, Moves.SLACK_OFF, Moves.BOUNCY_BUBBLE ], [Species.GRIMER]: [ Moves.SHADOW_SNEAK, 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.FROST_BREATH, Moves.AURA_SPHERE, Moves.NASTY_PLOT, Moves.MALIGNANT_CHAIN ], + [Species.GASTLY]: [ Moves.ICE_BEAM, Moves.AURA_SPHERE, Moves.NASTY_PLOT, Moves.MALIGNANT_CHAIN ], [Species.ONIX]: [ Moves.SHORE_UP, Moves.BODY_PRESS, Moves.HEAD_SMASH, Moves.SPIN_OUT ], [Species.DROWZEE]: [ Moves.DREAM_EATER, Moves.RECOVER, Moves.NIGHTMARE, Moves.SPORE ], [Species.KRABBY]: [ Moves.ICICLE_CRASH, Moves.LIQUIDATION, Moves.IVY_CUDGEL, Moves.SHELL_SMASH ], - [Species.VOLTORB]: [ Moves.BUZZY_BUZZ, Moves.OVERHEAT, Moves.FROST_BREATH, Moves.TAIL_GLOW ], + [Species.VOLTORB]: [ Moves.BUZZY_BUZZ, Moves.OVERHEAT, Moves.ICE_BEAM, Moves.TAIL_GLOW ], [Species.EXEGGCUTE]: [ Moves.MYSTICAL_POWER, Moves.APPLE_ACID, Moves.TRICK_ROOM, Moves.FICKLE_BEAM ], [Species.CUBONE]: [ Moves.HEAD_SMASH, Moves.WOOD_HAMMER, Moves.PAIN_SPLIT, Moves.VOLT_TACKLE ], [Species.LICKITUNG]: [ Moves.BODY_SLAM, Moves.FIRE_LASH, Moves.GRAV_APPLE, Moves.MILK_DRINK ], @@ -57,7 +57,7 @@ export const speciesEggMoves = { [Species.GOLDEEN]: [ Moves.DRILL_RUN, Moves.FLIP_TURN, Moves.DRAGON_DANCE, Moves.FISHIOUS_REND ], [Species.STARYU]: [ Moves.CALM_MIND, Moves.BOUNCY_BUBBLE, Moves.MOONBLAST, Moves.MYSTICAL_POWER ], [Species.SCYTHER]: [ Moves.GEAR_GRIND, Moves.BUG_BITE, Moves.STORM_THROW, Moves.MIGHTY_CLEAVE ], - [Species.PINSIR]: [ Moves.CRUSH_GRIP, Moves.U_TURN, Moves.CLOSE_COMBAT, Moves.EXTREME_SPEED ], + [Species.PINSIR]: [ Moves.EARTHQUAKE, Moves.LEECH_LIFE, Moves.CLOSE_COMBAT, Moves.EXTREME_SPEED ], [Species.TAUROS]: [ Moves.HIGH_HORSEPOWER, Moves.FLARE_BLITZ, Moves.WAVE_CRASH, Moves.HEAD_CHARGE ], [Species.MAGIKARP]: [ Moves.FLIP_TURN, Moves.ICE_SPINNER, Moves.LIQUIDATION, Moves.DRAGON_ASCENT ], [Species.LAPRAS]: [ Moves.RECOVER, Moves.FREEZE_DRY, Moves.CHILLY_RECEPTION, Moves.BOOMBURST ], @@ -79,7 +79,7 @@ export const speciesEggMoves = { [Species.SENTRET]: [ Moves.TIDY_UP, Moves.THIEF, Moves.NUZZLE, Moves.EXTREME_SPEED ], [Species.HOOTHOOT]: [ Moves.CALM_MIND, Moves.ESPER_WING, Moves.BOOMBURST, Moves.OBLIVION_WING ], [Species.LEDYBA]: [ Moves.POLLEN_PUFF, Moves.THIEF, Moves.PARTING_SHOT, Moves.SPORE ], - [Species.SPINARAK]: [ Moves.PARTING_SHOT, Moves.MEGAHORN, Moves.SILK_TRAP, Moves.STRENGTH_SAP ], + [Species.SPINARAK]: [ Moves.GASTRO_ACID, Moves.ATTACK_ORDER, Moves.SUPER_FANG, Moves.STRENGTH_SAP ], [Species.CHINCHOU]: [ Moves.THUNDERCLAP, Moves.BOUNCY_BUBBLE, Moves.VOLT_SWITCH, Moves.TAIL_GLOW ], [Species.PICHU]: [ Moves.THUNDERCLAP, Moves.SPLISHY_SPLASH, Moves.FLOATY_FALL, Moves.THUNDER_CAGE ], [Species.CLEFFA]: [ Moves.TAKE_HEART, Moves.POWER_GEM, Moves.WISH, Moves.LIGHT_OF_RUIN ], @@ -100,16 +100,16 @@ export const speciesEggMoves = { [Species.DUNSPARCE]: [ Moves.BODY_SLAM, Moves.WICKED_TORQUE, Moves.BLAZING_TORQUE, Moves.EXTREME_SPEED ], [Species.GLIGAR]: [ Moves.STONE_AXE, Moves.EARTHQUAKE, Moves.ROOST, Moves.FLOATY_FALL ], [Species.SNUBBULL]: [ Moves.PARTING_SHOT, Moves.EARTHQUAKE, Moves.STUFF_CHEEKS, Moves.MAGICAL_TORQUE ], - [Species.QWILFISH]: [ Moves.BARB_BARRAGE, Moves.BANEFUL_BUNKER, Moves.NUZZLE, Moves.FISHIOUS_REND ], + [Species.QWILFISH]: [ Moves.BARB_BARRAGE, Moves.BANEFUL_BUNKER, Moves.KNOCK_OFF, Moves.FISHIOUS_REND ], [Species.SHUCKLE]: [ Moves.COSMIC_POWER, Moves.SHORE_UP, Moves.BODY_PRESS, Moves.SALT_CURE ], - [Species.HERACROSS]: [ Moves.ROCK_BLAST, Moves.LUNGE, Moves.ICICLE_SPEAR, Moves.TIDY_UP ], - [Species.SNEASEL]: [ Moves.DIRE_CLAW, Moves.KOWTOW_CLEAVE, Moves.TRIPLE_AXEL, Moves.GLACIAL_LANCE ], + [Species.HERACROSS]: [ Moves.ROCK_BLAST, Moves.FIRST_IMPRESSION, Moves.ICICLE_SPEAR, Moves.TIDY_UP ], + [Species.SNEASEL]: [ Moves.DIRE_CLAW, Moves.SUCKER_PUNCH, Moves.TRIPLE_AXEL, Moves.WICKED_BLOW ], [Species.TEDDIURSA]: [ Moves.DIRE_CLAW, Moves.FACADE, Moves.BULK_UP, Moves.SLACK_OFF ], [Species.SLUGMA]: [ Moves.BURNING_BULWARK, Moves.POWER_GEM, Moves.MAGMA_STORM, Moves.HYDRO_STEAM ], [Species.SWINUB]: [ Moves.ICE_SPINNER, Moves.HEADLONG_RUSH, Moves.MIGHTY_CLEAVE, Moves.GLACIAL_LANCE ], [Species.CORSOLA]: [ Moves.SCALD, Moves.FREEZE_DRY, Moves.STRENGTH_SAP, Moves.SALT_CURE ], - [Species.REMORAID]: [ Moves.TWIN_BEAM, Moves.SNIPE_SHOT, Moves.SEARING_SHOT, Moves.ELECTRO_SHOT ], - [Species.DELIBIRD]: [ Moves.GLACIATE, Moves.FREEZE_DRY, Moves.CHILLY_RECEPTION, Moves.BLEAKWIND_STORM ], + [Species.REMORAID]: [ Moves.WATER_SHURIKEN, Moves.SNIPE_SHOT, Moves.SEARING_SHOT, Moves.ELECTRO_SHOT ], + [Species.DELIBIRD]: [ Moves.DRILL_RUN, Moves.FLOATY_FALL, Moves.NO_RETREAT, Moves.GLACIAL_LANCE ], [Species.SKARMORY]: [ Moves.ROOST, Moves.BODY_PRESS, Moves.SPIKY_SHIELD, Moves.BEAK_BLAST ], [Species.HOUNDOUR]: [ Moves.SEARING_SHOT, Moves.FIERY_WRATH, Moves.PARTING_SHOT, Moves.HYDRO_STEAM ], [Species.PHANPY]: [ Moves.SHORE_UP, Moves.HEAD_SMASH, Moves.MOUNTAIN_GALE, Moves.VOLT_TACKLE ], @@ -122,10 +122,10 @@ export const speciesEggMoves = { [Species.MILTANK]: [ Moves.BODY_PRESS, Moves.BULK_UP, Moves.YAWN, Moves.SIZZLY_SLIDE ], [Species.RAIKOU]: [ Moves.THUNDERCLAP, Moves.NASTY_PLOT, Moves.ICE_BEAM, Moves.PARABOLIC_CHARGE ], [Species.ENTEI]: [ Moves.BURNING_BULWARK, Moves.DRAGON_DANCE, Moves.EARTHQUAKE, Moves.MIGHTY_CLEAVE ], - [Species.SUICUNE]: [ Moves.HYDRO_STEAM, Moves.CALM_MIND, Moves.FREEZE_DRY, Moves.BOUNCY_BUBBLE ], - [Species.LARVITAR]: [ Moves.DRAGON_DANCE, Moves.MOUNTAIN_GALE, Moves.MIGHTY_CLEAVE, Moves.SHORE_UP ], + [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.TAKE_HEART, Moves.STORED_POWER, Moves.SCALD, Moves.OBLIVION_WING ], - [Species.HO_OH]: [ Moves.BURNING_BULWARK, Moves.U_TURN, Moves.BRAVE_BIRD, Moves.REVIVAL_BLESSING ], + [Species.HO_OH]: [ Moves.SWORDS_DANCE, Moves.EARTHQUAKE, Moves.BRAVE_BIRD, Moves.REVIVAL_BLESSING ], [Species.CELEBI]: [ Moves.MYSTICAL_POWER, Moves.STORED_POWER, Moves.COSMIC_POWER, Moves.SEED_FLARE ], [Species.TREECKO]: [ Moves.DRAGON_PULSE, Moves.DRAGON_ENERGY, Moves.SECRET_SWORD, Moves.SEED_FLARE ], [Species.TORCHIC]: [ Moves.HIGH_JUMP_KICK, Moves.SUPERCELL_SLAM, Moves.KNOCK_OFF, Moves.V_CREATE ], @@ -138,7 +138,7 @@ export const speciesEggMoves = { [Species.TAILLOW]: [ Moves.SWORDS_DANCE, Moves.FACADE, Moves.DRILL_RUN, Moves.EXTREME_SPEED ], [Species.WINGULL]: [ Moves.THUNDER, Moves.FLIP_TURN, Moves.DEFOG, Moves.STEAM_ERUPTION ], [Species.RALTS]: [ Moves.BOOMBURST, Moves.BITTER_BLADE, Moves.QUIVER_DANCE, Moves.VICTORY_DANCE ], - [Species.SURSKIT]: [ Moves.ROOST, Moves.FIERY_DANCE, Moves.STICKY_WEB, Moves.BLEAKWIND_STORM ], + [Species.SURSKIT]: [ Moves.POLLEN_PUFF, Moves.FIERY_DANCE, Moves.BOUNCY_BUBBLE, Moves.AEROBLAST ], [Species.SHROOMISH]: [ Moves.ACCELEROCK, Moves.TRAILBLAZE, Moves.STORM_THROW, Moves.SAPPY_SEED ], [Species.SLAKOTH]: [ Moves.FACADE, Moves.JUMP_KICK, Moves.KNOCK_OFF, Moves.SKILL_SWAP ], [Species.NINCADA]: [ Moves.ATTACK_ORDER, Moves.STICKY_WEB, Moves.SPIRIT_SHACKLE, Moves.SHELL_SMASH ], @@ -146,7 +146,7 @@ export const speciesEggMoves = { [Species.MAKUHITA]: [ Moves.STORM_THROW, Moves.SLACK_OFF, Moves.HEAT_CRASH, Moves.DOUBLE_IRON_BASH ], [Species.AZURILL]: [ Moves.JET_PUNCH, Moves.SPIRIT_BREAK, Moves.SWORDS_DANCE, Moves.SURGING_STRIKES ], [Species.NOSEPASS]: [ Moves.SHORE_UP, Moves.BODY_PRESS, Moves.CALM_MIND, Moves.TACHYON_CUTTER ], - [Species.SKITTY]: [ Moves.THUNDEROUS_KICK, Moves.SKETCH, Moves.TIDY_UP, Moves.V_CREATE ], + [Species.SKITTY]: [ Moves.THUNDEROUS_KICK, Moves.ENTRAINMENT, Moves.TIDY_UP, Moves.V_CREATE ], [Species.SABLEYE]: [ Moves.RECOVER, Moves.TOPSY_TURVY, Moves.PARTING_SHOT, Moves.SALT_CURE ], [Species.MAWILE]: [ Moves.BULLET_PUNCH, Moves.MAGICAL_TORQUE, Moves.EARTHQUAKE, Moves.DOUBLE_IRON_BASH ], [Species.ARON]: [ Moves.HEAD_SMASH, Moves.BODY_PRESS, Moves.SHORE_UP, Moves.SALT_CURE ], @@ -159,8 +159,8 @@ export const speciesEggMoves = { [Species.GULPIN]: [ Moves.STRENGTH_SAP, Moves.EARTH_POWER, Moves.GROWTH, Moves.MALIGNANT_CHAIN ], [Species.CARVANHA]: [ Moves.DRILL_RUN, Moves.ICE_SPINNER, Moves.WAVE_CRASH, Moves.SWORDS_DANCE ], [Species.WAILMER]: [ Moves.CURSE, Moves.LIQUIDATION, Moves.FLOATY_FALL, Moves.RECOVER ], - [Species.NUMEL]: [ Moves.SANDSEAR_STORM, Moves.SPIKES, Moves.SHORE_UP, Moves.SEARING_SHOT ], - [Species.TORKOAL]: [ Moves.SLACK_OFF, Moves.SPIKES, Moves.BODY_PRESS, Moves.BURNING_BULWARK ], + [Species.NUMEL]: [ Moves.TRICK_ROOM, Moves.ENERGY_BALL, Moves.MORNING_SUN, Moves.BLUE_FLARE ], + [Species.TORKOAL]: [ Moves.MORNING_SUN, Moves.BURNING_BULWARK, Moves.BODY_PRESS, Moves.HYDRO_STEAM ], [Species.SPOINK]: [ Moves.AURA_SPHERE, Moves.MILK_DRINK, Moves.COSMIC_POWER, Moves.EXPANDING_FORCE ], [Species.SPINDA]: [ Moves.SUPERPOWER, Moves.SLACK_OFF, Moves.FLEUR_CANNON, Moves.V_CREATE ], [Species.TRAPINCH]: [ Moves.FIRE_LASH, Moves.DRAGON_DARTS, Moves.THOUSAND_ARROWS, Moves.DRAGON_ENERGY ], @@ -176,20 +176,20 @@ export const speciesEggMoves = { [Species.LILEEP]: [ Moves.POWER_GEM, Moves.SCALD, Moves.STONE_AXE, Moves.SAPPY_SEED ], [Species.ANORITH]: [ Moves.LIQUIDATION, 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.CLEAR_SMOG, Moves.QUIVER_DANCE ], + [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.SHUPPET]: [ Moves.DRAIN_PUNCH, Moves.TOPSY_TURVY, Moves.PARTING_SHOT, Moves.SPECTRAL_THIEF ], + [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 ], [Species.ABSOL]: [ Moves.KOWTOW_CLEAVE, Moves.SACRED_SWORD, Moves.DIRE_CLAW, Moves.BITTER_BLADE ], - [Species.WYNAUT]: [ Moves.RECOVER, Moves.PERISH_SONG, Moves.TAUNT, Moves.SHED_TAIL ], + [Species.WYNAUT]: [ Moves.RECOVER, Moves.SHED_TAIL, Moves.TAUNT, Moves.COMEUPPANCE ], [Species.SNORUNT]: [ Moves.AURORA_VEIL, Moves.HYPER_VOICE, Moves.EARTH_POWER, Moves.NO_RETREAT ], [Species.SPHEAL]: [ Moves.FLIP_TURN, Moves.FREEZE_DRY, Moves.SLACK_OFF, Moves.STEAM_ERUPTION ], - [Species.CLAMPERL]: [ Moves.ICE_SPINNER, Moves.LIQUIDATION, Moves.EARTH_POWER, Moves.ORIGIN_PULSE ], + [Species.CLAMPERL]: [ Moves.ICE_SPINNER, Moves.LIQUIDATION, Moves.EARTH_POWER, Moves.STEAM_ERUPTION ], [Species.RELICANTH]: [ Moves.BODY_PRESS, Moves.SHORE_UP, Moves.WAVE_CRASH, Moves.FISHIOUS_REND ], [Species.LUVDISC]: [ Moves.BATON_PASS, Moves.THIEF, Moves.BOUNCY_BUBBLE, Moves.TAKE_HEART ], [Species.BAGON]: [ Moves.FLOATY_FALL, Moves.FIRE_LASH, Moves.DRAGON_DANCE, Moves.GLAIVE_RUSH ], - [Species.BELDUM]: [ Moves.PSYCHIC_FANGS, Moves.RECOVER, Moves.MOUNTAIN_GALE, Moves.SHIFT_GEAR ], + [Species.BELDUM]: [ Moves.PSYCHIC_FANGS, Moves.RECOVER, Moves.TRIPLE_AXEL, Moves.SHIFT_GEAR ], [Species.REGIROCK]: [ Moves.STONE_AXE, Moves.BODY_PRESS, Moves.RECOVER, Moves.SALT_CURE ], [Species.REGICE]: [ Moves.EARTH_POWER, Moves.COSMIC_POWER, Moves.RECOVER, Moves.FREEZE_DRY ], [Species.REGISTEEL]: [ Moves.BODY_PRESS, Moves.HEAT_CRASH, Moves.RECOVER, Moves.GIGATON_HAMMER ], @@ -208,9 +208,9 @@ export const speciesEggMoves = { [Species.KRICKETOT]: [ Moves.BONEMERANG, Moves.ROOST, Moves.ROCK_BLAST, Moves.VICTORY_DANCE ], [Species.SHINX]: [ Moves.FIRE_LASH, Moves.TRIPLE_AXEL, Moves.FACADE, Moves.BOLT_STRIKE ], [Species.BUDEW]: [ Moves.FIERY_DANCE, Moves.SLUDGE_WAVE, Moves.SPORE, Moves.QUIVER_DANCE ], - [Species.CRANIDOS]: [ Moves.STONE_AXE, Moves.ACCELEROCK, Moves.HEADLONG_RUSH, Moves.DRAGON_DANCE ], + [Species.CRANIDOS]: [ Moves.DRAGON_DANCE, Moves.ACCELEROCK, Moves.HEADLONG_RUSH, Moves.VOLT_TACKLE ], [Species.SHIELDON]: [ Moves.PAIN_SPLIT, Moves.BODY_PRESS, Moves.KINGS_SHIELD, Moves.DIAMOND_STORM ], - [Species.BURMY]: [ Moves.BODY_PRESS, Moves.TOXIC, Moves.HEAL_ORDER, Moves.DEFEND_ORDER ], + [Species.BURMY]: [ Moves.BODY_PRESS, Moves.DEFEND_ORDER, Moves.HEAL_ORDER, Moves.SAPPY_SEED ], [Species.COMBEE]: [ Moves.SPORE, Moves.HEAT_WAVE, Moves.KINGS_SHIELD, Moves.QUIVER_DANCE ], [Species.PACHIRISU]: [ Moves.BADDY_BAD, Moves.SIZZLY_SLIDE, Moves.U_TURN, Moves.ZIPPY_ZAP ], [Species.BUIZEL]: [ Moves.JET_PUNCH, Moves.TRIPLE_AXEL, Moves.SUPERCELL_SLAM, Moves.SURGING_STRIKES ], @@ -235,16 +235,16 @@ export const speciesEggMoves = { [Species.CROAGUNK]: [ Moves.DIRE_CLAW, Moves.ICE_PUNCH, Moves.THUNDEROUS_KICK, Moves.VICTORY_DANCE ], [Species.CARNIVINE]: [ Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.MIGHTY_CLEAVE, Moves.FLOWER_TRICK ], [Species.FINNEON]: [ Moves.QUIVER_DANCE, Moves.BOUNCY_BUBBLE, Moves.FREEZE_DRY, Moves.ORIGIN_PULSE ], - [Species.MANTYKE]: [ Moves.BOUNCY_BUBBLE, Moves.SPIKES, Moves.ROOST, Moves.STEAM_ERUPTION ], + [Species.MANTYKE]: [ Moves.BOUNCY_BUBBLE, Moves.STEALTH_ROCK, Moves.NASTY_PLOT, Moves.STEAM_ERUPTION ], [Species.SNOVER]: [ Moves.HIGH_HORSEPOWER, Moves.STRENGTH_SAP, Moves.AURORA_VEIL, Moves.IVY_CUDGEL ], - [Species.ROTOM]: [ Moves.STRENGTH_SAP, Moves.FIERY_DANCE, Moves.SPLISHY_SPLASH, Moves.RISING_VOLTAGE ], - [Species.UXIE]: [ Moves.COSMIC_POWER, Moves.BODY_PRESS, Moves.RECOVER, Moves.LUMINA_CRASH ], - [Species.MESPRIT]: [ Moves.QUIVER_DANCE, Moves.AURA_SPHERE, Moves.RECOVER, Moves.LUMINA_CRASH ], - [Species.AZELF]: [ Moves.PHOTON_GEYSER, Moves.ICE_BEAM, Moves.MOONBLAST, Moves.LUMINA_CRASH ], + [Species.ROTOM]: [ Moves.STRENGTH_SAP, Moves.FIERY_DANCE, Moves.SPLISHY_SPLASH, Moves.ELECTRO_DRIFT ], + [Species.UXIE]: [ Moves.COSMIC_POWER, Moves.BODY_PRESS, Moves.RECOVER, Moves.SPARKLY_SWIRL ], + [Species.MESPRIT]: [ Moves.TAIL_GLOW, Moves.AURA_SPHERE, Moves.RECOVER, Moves.LUMINA_CRASH ], + [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.WATER_SPOUT, Moves.DRAGON_ENERGY ], [Species.HEATRAN]: [ Moves.TORCH_SONG, Moves.RECOVER, Moves.FLASH_CANNON, Moves.MATCHA_GOTCHA ], - [Species.REGIGIGAS]: [ Moves.SKILL_SWAP, Moves.SHORE_UP, Moves.EXTREME_SPEED, Moves.GIGATON_HAMMER ], + [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 ], [Species.PHIONE]: [ Moves.BOUNCY_BUBBLE, Moves.FREEZE_DRY, Moves.SPLISHY_SPLASH, Moves.QUIVER_DANCE ], @@ -260,37 +260,37 @@ export const speciesEggMoves = { [Species.LILLIPUP]: [ Moves.CLOSE_COMBAT, Moves.THIEF, Moves.HIGH_HORSEPOWER, Moves.LAST_RESPECTS ], [Species.PURRLOIN]: [ Moves.ENCORE, Moves.ASSIST, Moves.PARTING_SHOT, Moves.WICKED_BLOW ], [Species.PANSAGE]: [ Moves.SWORDS_DANCE, Moves.TEMPER_FLARE, Moves.EARTHQUAKE, Moves.IVY_CUDGEL ], - [Species.PANSEAR]: [ Moves.NASTY_PLOT, Moves.SCALD, Moves.SCORCHING_SANDS, Moves.SEARING_SHOT ], - [Species.PANPOUR]: [ Moves.NASTY_PLOT, Moves.ENERGY_BALL, Moves.AURA_SPHERE, Moves.STEAM_ERUPTION ], - [Species.MUNNA]: [ Moves.COSMIC_POWER, Moves.AURA_SPHERE, Moves.EARTH_POWER, Moves.LUMINA_CRASH ], + [Species.PANSEAR]: [ Moves.NASTY_PLOT, Moves.SCALD, Moves.SCORCHING_SANDS, Moves.TORCH_SONG ], + [Species.PANPOUR]: [ Moves.NASTY_PLOT, Moves.ENERGY_BALL, Moves.EARTH_POWER, Moves.STEAM_ERUPTION ], + [Species.MUNNA]: [ Moves.COSMIC_POWER, Moves.AURA_SPHERE, Moves.EARTH_POWER, Moves.MYSTICAL_POWER ], [Species.PIDOVE]: [ Moves.GUNK_SHOT, Moves.TIDY_UP, Moves.FLOATY_FALL, Moves.TRIPLE_ARROWS ], [Species.BLITZLE]: [ Moves.HIGH_HORSEPOWER, Moves.THUNDEROUS_KICK, Moves.FLARE_BLITZ, Moves.VOLT_TACKLE ], [Species.ROGGENROLA]: [ Moves.BODY_PRESS, Moves.CURSE, Moves.SHORE_UP, Moves.DIAMOND_STORM ], - [Species.WOOBAT]: [ Moves.TAKE_HEART, Moves.STORED_POWER, Moves.MYSTICAL_FIRE, Moves.OBLIVION_WING ], - [Species.DRILBUR]: [ Moves.IRON_HEAD, Moves.ICE_SPINNER, Moves.SHIFT_GEAR, Moves.HEADLONG_RUSH ], + [Species.WOOBAT]: [ Moves.ESPER_WING, Moves.STORED_POWER, Moves.MYSTICAL_FIRE, Moves.OBLIVION_WING ], + [Species.DRILBUR]: [ Moves.IRON_HEAD, Moves.ICE_SPINNER, Moves.SHIFT_GEAR, Moves.THOUSAND_ARROWS ], [Species.AUDINO]: [ Moves.FOLLOW_ME, Moves.MOONBLAST, Moves.WISH, Moves.LUNAR_BLESSING ], [Species.TIMBURR]: [ Moves.MACH_PUNCH, Moves.DRAIN_PUNCH, Moves.ICE_HAMMER, Moves.DOUBLE_IRON_BASH ], - [Species.TYMPOLE]: [ Moves.LIQUIDATION, Moves.HIGH_HORSEPOWER, Moves.TOXIC, Moves.SHORE_UP ], + [Species.TYMPOLE]: [ Moves.JET_PUNCH, Moves.HIGH_HORSEPOWER, Moves.BULK_UP, Moves.SURGING_STRIKES ], [Species.THROH]: [ Moves.DRAIN_PUNCH, Moves.SLACK_OFF, Moves.METEOR_MASH, Moves.NO_RETREAT ], [Species.SAWK]: [ Moves.DRAIN_PUNCH, Moves.MACH_PUNCH, Moves.ENDEAVOR, Moves.VICTORY_DANCE ], [Species.SEWADDLE]: [ Moves.STONE_AXE, Moves.PSYCHO_CUT, Moves.TIDY_UP, Moves.BITTER_BLADE ], [Species.VENIPEDE]: [ Moves.SWORDS_DANCE, Moves.BATON_PASS, Moves.NOXIOUS_TORQUE, Moves.BLAZING_TORQUE ], [Species.COTTONEE]: [ Moves.POLLEN_PUFF, Moves.PARTING_SHOT, Moves.SLEEP_POWDER, Moves.SEED_FLARE ], - [Species.PETILIL]: [ Moves.THUNDEROUS_KICK, Moves.BATON_PASS, Moves.AQUA_STEP, Moves.FIERY_DANCE ], + [Species.PETILIL]: [ Moves.THUNDEROUS_KICK, Moves.SPARKLING_ARIA, Moves.AQUA_STEP, Moves.FIERY_DANCE ], [Species.BASCULIN]: [ Moves.LAST_RESPECTS, Moves.CLOSE_COMBAT, Moves.TRIPLE_DIVE, Moves.DRAGON_DANCE ], [Species.SANDILE]: [ Moves.DIRE_CLAW, Moves.PARTING_SHOT, Moves.FIRE_LASH, Moves.PRECIPICE_BLADES ], [Species.DARUMAKA]: [ Moves.DRAIN_PUNCH, Moves.THUNDER_PUNCH, Moves.BLAZING_TORQUE, Moves.V_CREATE ], [Species.MARACTUS]: [ Moves.SCORCHING_SANDS, Moves.QUIVER_DANCE, Moves.FIERY_DANCE, Moves.SEED_FLARE ], - [Species.DWEBBLE]: [ Moves.CRABHAMMER, Moves.STONE_AXE, Moves.LEECH_LIFE, Moves.SHORE_UP ], + [Species.DWEBBLE]: [ Moves.CRABHAMMER, Moves.STONE_AXE, Moves.LEECH_LIFE, Moves.MIGHTY_CLEAVE ], [Species.SCRAGGY]: [ Moves.SUCKER_PUNCH, Moves.TRIPLE_AXEL, Moves.DRAGON_DANCE, Moves.COLLISION_COURSE ], [Species.SIGILYPH]: [ Moves.STORED_POWER, Moves.TAKE_HEART, Moves.FREEZING_GLARE, Moves.OBLIVION_WING ], [Species.YAMASK]: [ Moves.RECOVER, Moves.INFERNAL_PARADE, Moves.AURA_SPHERE, Moves.TOPSY_TURVY ], - [Species.TIRTOUGA]: [ Moves.ICE_SPINNER, Moves.WAVE_CRASH, Moves.SHORE_UP, Moves.MIGHTY_CLEAVE ], + [Species.TIRTOUGA]: [ Moves.ICE_SPINNER, Moves.LIQUIDATION, Moves.SHORE_UP, Moves.MIGHTY_CLEAVE ], [Species.ARCHEN]: [ Moves.ROOST, Moves.MIGHTY_CLEAVE, Moves.FLOATY_FALL, Moves.SKILL_SWAP ], [Species.TRUBBISH]: [ Moves.TIDY_UP, Moves.RECOVER, Moves.DIRE_CLAW, Moves.GIGATON_HAMMER ], - [Species.ZORUA]: [ Moves.FLAMETHROWER, Moves.PSYCHIC, Moves.AURA_SPHERE, Moves.BADDY_BAD ], + [Species.ZORUA]: [ Moves.FLAMETHROWER, Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.FIERY_WRATH ], [Species.MINCCINO]: [ Moves.ICICLE_SPEAR, Moves.TIDY_UP, Moves.KNOCK_OFF, Moves.POPULATION_BOMB ], - [Species.GOTHITA]: [ Moves.MILK_DRINK, Moves.COSMIC_POWER, Moves.AURA_SPHERE, Moves.PSYSTRIKE ], + [Species.GOTHITA]: [ Moves.MILK_DRINK, Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.PSYSTRIKE ], [Species.SOLOSIS]: [ Moves.COSMIC_POWER, Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.PSYSTRIKE ], [Species.DUCKLETT]: [ Moves.QUIVER_DANCE, Moves.EARTH_POWER, Moves.FREEZE_DRY, Moves.OBLIVION_WING ], [Species.VANILLITE]: [ Moves.EARTH_POWER, Moves.AURORA_VEIL, Moves.DECORATE, Moves.MILK_DRINK ], @@ -304,7 +304,7 @@ export const speciesEggMoves = { [Species.FERROSEED]: [ Moves.STRENGTH_SAP, Moves.BODY_PRESS, Moves.SPIKY_SHIELD, Moves.SAPPY_SEED ], [Species.KLINK]: [ Moves.FLARE_BLITZ, 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.MYSTICAL_POWER, Moves.TRICK_ROOM, Moves.STORED_POWER, Moves.LUMINA_CRASH ], + [Species.ELGYEM]: [ Moves.MYSTICAL_POWER, Moves.TRICK_ROOM, Moves.STORED_POWER, Moves.ASTRAL_BARRAGE ], [Species.LITWICK]: [ Moves.FIERY_DANCE, Moves.EARTH_POWER, Moves.MOONBLAST, Moves.ASTRAL_BARRAGE ], [Species.AXEW]: [ Moves.STONE_AXE, Moves.DIRE_CLAW, Moves.FIRE_LASH, Moves.GLAIVE_RUSH ], [Species.CUBCHOO]: [ Moves.TRIPLE_AXEL, Moves.LIQUIDATION, Moves.SWORDS_DANCE, Moves.COLLISION_COURSE ], @@ -312,7 +312,7 @@ export const speciesEggMoves = { [Species.SHELMET]: [ Moves.SHED_TAIL, Moves.NASTY_PLOT, Moves.BATON_PASS, Moves.HEAT_WAVE ], [Species.STUNFISK]: [ Moves.SHORE_UP, Moves.BANEFUL_BUNKER, Moves.THUNDER_CAGE, Moves.THUNDERCLAP ], [Species.MIENFOO]: [ Moves.GUNK_SHOT, Moves.SUPERCELL_SLAM, Moves.KNOCK_OFF, Moves.MOUNTAIN_GALE ], - [Species.DRUDDIGON]: [ Moves.GLARE, Moves.ROOST, Moves.DRAGON_HAMMER, Moves.FIRE_LASH ], + [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 ], @@ -330,7 +330,7 @@ export const speciesEggMoves = { [Species.RESHIRAM]: [ Moves.ROOST, Moves.TAKE_HEART, Moves.ERUPTION, Moves.DRAGON_ENERGY ], [Species.ZEKROM]: [ Moves.DRAGON_DANCE, Moves.THUNDEROUS_KICK, Moves.DRAGON_HAMMER, Moves.BOLT_BEAK ], [Species.LANDORUS]: [ Moves.STONE_AXE, Moves.THOUSAND_ARROWS, Moves.ROOST, Moves.FLOATY_FALL ], - [Species.KYUREM]: [ Moves.ICICLE_CRASH, Moves.DRAGON_ENERGY, Moves.NASTY_PLOT, Moves.GLACIAL_LANCE ], + [Species.KYUREM]: [ Moves.DRAGON_DARTS, Moves.DRAGON_ENERGY, Moves.NO_RETREAT, Moves.GLACIAL_LANCE ], [Species.KELDEO]: [ Moves.BOUNCY_BUBBLE, Moves.THUNDERBOLT, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ], [Species.MELOETTA]: [ Moves.TORCH_SONG, Moves.QUIVER_DANCE, Moves.THUNDEROUS_KICK, Moves.BOOMBURST ], [Species.GENESECT]: [ Moves.EXTREME_SPEED, Moves.U_TURN, Moves.SHIFT_GEAR, Moves.TAIL_GLOW ], @@ -352,7 +352,7 @@ export const speciesEggMoves = { [Species.INKAY]: [ Moves.POWER_TRIP, Moves.STORED_POWER, Moves.RECOVER, Moves.PSYCHO_BOOST ], [Species.BINACLE]: [ Moves.TRIPLE_AXEL, Moves.ACCELEROCK, Moves.DIRE_CLAW, Moves.MIGHTY_CLEAVE ], [Species.SKRELP]: [ Moves.RECOVER, Moves.CORE_ENFORCER, Moves.CALM_MIND, Moves.MALIGNANT_CHAIN ], - [Species.CLAUNCHER]: [ Moves.SHELL_SMASH, Moves.ARMOR_CANNON, Moves.TERRAIN_PULSE, Moves.ORIGIN_PULSE ], + [Species.CLAUNCHER]: [ Moves.SHELL_SMASH, Moves.ARMOR_CANNON, Moves.WATER_SHURIKEN, Moves.ORIGIN_PULSE ], [Species.HELIOPTILE]: [ Moves.WEATHER_BALL, Moves.BOOMBURST, Moves.EARTH_POWER, Moves.TAIL_GLOW ], [Species.TYRUNT]: [ Moves.DRAGON_HAMMER, Moves.FLARE_BLITZ, Moves.VOLT_TACKLE, Moves.AXE_KICK ], [Species.AMAURA]: [ Moves.RECOVER, Moves.AURORA_VEIL, Moves.POWER_GEM, Moves.GEOMANCY ], @@ -369,7 +369,7 @@ export const speciesEggMoves = { [Species.YVELTAL]: [ Moves.SLUDGE_WAVE, Moves.POWER_TRIP, Moves.FIERY_WRATH, Moves.CLANGOROUS_SOUL ], [Species.ZYGARDE]: [ Moves.DRAGON_DARTS, Moves.HEAL_ORDER, Moves.VICTORY_DANCE, Moves.DOUBLE_IRON_BASH ], [Species.DIANCIE]: [ Moves.MAGICAL_TORQUE, Moves.BODY_PRESS, Moves.SHORE_UP, Moves.GEOMANCY ], - [Species.HOOPA]: [ Moves.PHOTON_GEYSER, Moves.EARTH_POWER, Moves.BATON_PASS, Moves.TIDY_UP ], + [Species.HOOPA]: [ Moves.PHOTON_GEYSER, Moves.SECRET_SWORD, Moves.TIDY_UP, Moves.WICKED_BLOW ], [Species.VOLCANION]: [ Moves.HYDRO_STEAM, Moves.CALM_MIND, Moves.ENERGY_BALL, Moves.SEARING_SHOT ], [Species.ROWLET]: [ Moves.SNIPE_SHOT, Moves.POLTERGEIST, Moves.FIRST_IMPRESSION, Moves.VICTORY_DANCE ], [Species.LITTEN]: [ Moves.FAKE_OUT, Moves.PARTING_SHOT, Moves.MORNING_SUN, Moves.SACRED_FIRE ], @@ -386,7 +386,7 @@ export const speciesEggMoves = { [Species.MUDBRAY]: [ Moves.BODY_PRESS, Moves.YAWN, Moves.SHORE_UP, Moves.THOUSAND_WAVES ], [Species.DEWPIDER]: [ Moves.AQUA_JET, Moves.SILK_TRAP, Moves.SWORDS_DANCE, Moves.AQUA_STEP ], [Species.FOMANTIS]: [ Moves.SUPERPOWER, Moves.HEADLONG_RUSH, Moves.ICE_HAMMER, Moves.BITTER_BLADE ], - [Species.MORELULL]: [ Moves.CALM_MIND, Moves.LEECH_SEED, Moves.STRENGTH_SAP, Moves.SPARKLY_SWIRL ], + [Species.MORELULL]: [ Moves.CALM_MIND, Moves.SAPPY_SEED, Moves.SPARKLY_SWIRL, Moves.MATCHA_GOTCHA ], [Species.SALANDIT]: [ Moves.FAKE_OUT, Moves.FIERY_DANCE, Moves.SCALD, Moves.MALIGNANT_CHAIN ], [Species.STUFFUL]: [ Moves.DRAIN_PUNCH, Moves.METEOR_MASH, Moves.ICE_HAMMER, Moves.RAGE_FIST ], [Species.BOUNSWEET]: [ Moves.TRIPLE_AXEL, Moves.AQUA_STEP, Moves.THUNDEROUS_KICK, Moves.SAPPY_SEED ], @@ -394,12 +394,12 @@ export const speciesEggMoves = { [Species.ORANGURU]: [ Moves.FOUL_PLAY, Moves.YAWN, Moves.FOLLOW_ME, Moves.LUNAR_BLESSING ], [Species.PASSIMIAN]: [ Moves.FAKE_OUT, Moves.SUCKER_PUNCH, Moves.SWORDS_DANCE, Moves.PYRO_BALL ], [Species.WIMPOD]: [ Moves.ICE_SPINNER, Moves.OBSTRUCT, Moves.KNOCK_OFF, Moves.SURGING_STRIKES ], - [Species.SANDYGAST]: [ Moves.SCORCHING_SANDS, Moves.PARTING_SHOT, Moves.CURSE, Moves.SALT_CURE ], + [Species.SANDYGAST]: [ Moves.SCORCHING_SANDS, Moves.SPLISHY_SPLASH, Moves.CURSE, Moves.SALT_CURE ], [Species.PYUKUMUKU]: [ Moves.MIRROR_COAT, Moves.BANEFUL_BUNKER, Moves.TOXIC_SPIKES, Moves.SALT_CURE ], [Species.TYPE_NULL]: [ Moves.DIRE_CLAW, Moves.RECOVER, Moves.EXTREME_SPEED, Moves.NO_RETREAT ], [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.CLOSE_COMBAT ], - [Species.TURTONATOR]: [ Moves.SHELL_SMASH, Moves.ARMOR_CANNON, Moves.EARTH_POWER, Moves.CLANGING_SCALES ], + [Species.TURTONATOR]: [ Moves.BURNING_BULWARK, Moves.ARMOR_CANNON, Moves.EARTH_POWER, Moves.CLANGING_SCALES ], [Species.TOGEDEMARU]: [ Moves.FAKE_OUT, Moves.METAL_BURST, Moves.METEOR_MASH, Moves.BOLT_STRIKE ], [Species.MIMIKYU]: [ Moves.SPIRIT_BREAK, Moves.TIDY_UP, Moves.SIZZLY_SLIDE, Moves.SPECTRAL_THIEF ], [Species.BRUXISH]: [ Moves.ICE_FANG, Moves.FIRE_FANG, Moves.FLIP_TURN, Moves.FILLET_AWAY ], @@ -415,7 +415,7 @@ export const speciesEggMoves = { [Species.BUZZWOLE]: [ Moves.LEECH_LIFE, Moves.BULLET_PUNCH, Moves.DARKEST_LARIAT, Moves.COLLISION_COURSE ], [Species.PHEROMOSA]: [ Moves.AURA_SPHERE, Moves.MAKE_IT_RAIN, Moves.ATTACK_ORDER, Moves.COLLISION_COURSE ], [Species.XURKITREE]: [ Moves.OVERHEAT, Moves.GIGA_DRAIN, Moves.TAIL_GLOW, Moves.THUNDERCLAP ], - [Species.CELESTEELA]: [ Moves.ROOST, Moves.BUZZY_BUZZ, Moves.SPIKES, Moves.OBLIVION_WING ], + [Species.CELESTEELA]: [ Moves.RECOVER, Moves.BUZZY_BUZZ, Moves.EARTH_POWER, Moves.OBLIVION_WING ], [Species.KARTANA]: [ Moves.MIGHTY_CLEAVE, Moves.CEASELESS_EDGE, Moves.BITTER_BLADE, Moves.BEHEMOTH_BLADE ], [Species.GUZZLORD]: [ Moves.SUCKER_PUNCH, Moves.COMEUPPANCE, Moves.SLACK_OFF, Moves.RUINATION ], [Species.NECROZMA]: [ Moves.COSMIC_POWER, Moves.SACRED_FIRE, Moves.ASTRAL_BARRAGE, Moves.CLANGOROUS_SOUL ], @@ -426,9 +426,9 @@ export const speciesEggMoves = { [Species.BLACEPHALON]: [ Moves.NASTY_PLOT, Moves.SEARING_SHOT, Moves.GIGA_DRAIN, Moves.ASTRAL_BARRAGE ], [Species.ZERAORA]: [ Moves.SWORDS_DANCE, Moves.TRIPLE_AXEL, Moves.BOLT_STRIKE, Moves.PYRO_BALL ], [Species.MELTAN]: [ Moves.BULLET_PUNCH, Moves.DRAIN_PUNCH, Moves.BULK_UP, Moves.PLASMA_FISTS ], - [Species.GROOKEY]: [ Moves.HEADLONG_RUSH, Moves.CLOSE_COMBAT, Moves.GRASSY_GLIDE, Moves.CLANGOROUS_SOUL ], - [Species.SCORBUNNY]: [ Moves.EXTREME_SPEED, Moves.TROP_KICK, Moves.TRIPLE_AXEL, Moves.THUNDEROUS_KICK ], - [Species.SOBBLE]: [ Moves.AEROBLAST, Moves.FROST_BREATH, Moves.SEARING_SHOT, Moves.SURGING_STRIKES ], + [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.ESPER_WING, Moves.FROST_BREATH, Moves.SEARING_SHOT, Moves.STEAM_ERUPTION ], [Species.SKWOVET]: [ Moves.KNOCK_OFF, Moves.GRAV_APPLE, Moves.BODY_PRESS, Moves.SLACK_OFF ], [Species.ROOKIDEE]: [ Moves.ROOST, Moves.BODY_PRESS, Moves.IRON_HEAD, Moves.KINGS_SHIELD ], [Species.BLIPBUG]: [ Moves.HEAL_ORDER, Moves.EXPANDING_FORCE, Moves.SPORE, Moves.TAIL_GLOW ], @@ -452,11 +452,11 @@ export const speciesEggMoves = { [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.STONJOURNER]: [ Moves.BODY_PRESS, Moves.BULK_UP, Moves.SHORE_UP, Moves.ACCELEROCK ], + [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.PARTING_SHOT, Moves.SWORDS_DANCE ], - [Species.CUFANT]: [ Moves.LIQUIDATION, Moves.HEAVY_SLAM, Moves.CLOSE_COMBAT, Moves.GIGATON_HAMMER ], + [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.ARCTOZOLT]: [ Moves.TRIPLE_AXEL, Moves.LIQUIDATION, Moves.HIGH_HORSEPOWER, Moves.SHIFT_GEAR ], [Species.DRACOVISH]: [ Moves.TRIPLE_AXEL, Moves.DRAGON_HAMMER, Moves.THUNDER_FANG, Moves.DRAGON_DANCE ], @@ -467,8 +467,8 @@ export const speciesEggMoves = { [Species.ZAMAZENTA]: [ Moves.PSYSHIELD_BASH, Moves.BODY_PRESS, Moves.SLACK_OFF, Moves.VICTORY_DANCE ], [Species.KUBFU]: [ Moves.METEOR_MASH, Moves.DRAIN_PUNCH, Moves.JET_PUNCH, Moves.DRAGON_DANCE ], [Species.ZARUDE]: [ Moves.SAPPY_SEED, Moves.PARTING_SHOT, Moves.WICKED_BLOW, Moves.VICTORY_DANCE ], - [Species.REGIELEKI]: [ Moves.NASTY_PLOT, Moves.FROST_BREATH, Moves.PARTING_SHOT, Moves.ELECTRO_DRIFT ], - [Species.REGIDRAGO]: [ Moves.METEOR_MASH, Moves.FLAMETHROWER, Moves.CALM_MIND, Moves.DRAGON_DARTS ], + [Species.REGIELEKI]: [ Moves.NASTY_PLOT, Moves.ICE_BEAM, Moves.EARTH_POWER, Moves.ELECTRO_DRIFT ], + [Species.REGIDRAGO]: [ Moves.METEOR_MASH, Moves.FLAMETHROWER, Moves.TAKE_HEART, Moves.DRAGON_DARTS ], [Species.GLASTRIER]: [ Moves.TRICK_ROOM, Moves.SLACK_OFF, Moves.HIGH_HORSEPOWER, Moves.GLACIAL_LANCE ], [Species.SPECTRIER]: [ Moves.EARTH_POWER, Moves.PARTING_SHOT, Moves.AURA_SPHERE, Moves.ASTRAL_BARRAGE ], [Species.CALYREX]: [ Moves.SAPPY_SEED, Moves.RECOVER, Moves.SECRET_SWORD, Moves.PHOTON_GEYSER ], @@ -478,40 +478,40 @@ export const speciesEggMoves = { [Species.QUAXLY]: [ Moves.DRAGON_DANCE, Moves.TRIPLE_AXEL, Moves.TROP_KICK, Moves.THUNDEROUS_KICK ], [Species.LECHONK]: [ Moves.MILK_DRINK, Moves.BLAZING_TORQUE, Moves.FILLET_AWAY, Moves.MULTI_ATTACK ], [Species.TAROUNTULA]: [ Moves.STONE_AXE, Moves.LEECH_LIFE, Moves.THIEF, Moves.SPORE ], - [Species.NYMBLE]: [ Moves.CEASELESS_EDGE, Moves.FELL_STINGER, Moves.LEECH_LIFE, Moves.WICKED_BLOW ], - [Species.PAWMI]: [ Moves.DRAIN_PUNCH, Moves.WISH, Moves.PARTING_SHOT, Moves.PLASMA_FISTS ], - [Species.TANDEMAUS]: [ Moves.BATON_PASS, Moves.BITE, Moves.SIZZLY_SLIDE, Moves.REVIVAL_BLESSING ], - [Species.FIDOUGH]: [ Moves.WISH, Moves.BODY_PRESS, Moves.PARTING_SHOT, Moves.SIZZLY_SLIDE ], + [Species.NYMBLE]: [ Moves.CEASELESS_EDGE, Moves.FELL_STINGER, Moves.ATTACK_ORDER, Moves.WICKED_BLOW ], + [Species.PAWMI]: [ Moves.DRAIN_PUNCH, Moves.PARTING_SHOT, 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.SMOLIV]: [ Moves.STRENGTH_SAP, Moves.EARTH_POWER, Moves.CALM_MIND, Moves.BOOMBURST ], - [Species.SQUAWKABILLY]: [ Moves.PARTING_SHOT, Moves.BULK_UP, Moves.FLARE_BLITZ, Moves.HEAD_CHARGE ], - [Species.NACLI]: [ Moves.BODY_PRESS, Moves.SPIKES, Moves.CURSE, Moves.DIAMOND_STORM ], + [Species.SQUAWKABILLY]: [ Moves.PARTING_SHOT, Moves.EARTHQUAKE, Moves.FLARE_BLITZ, Moves.EXTREME_SPEED ], + [Species.NACLI]: [ Moves.BODY_PRESS, Moves.SOAK, Moves.CURSE, Moves.DIAMOND_STORM ], [Species.CHARCADET]: [ Moves.SACRED_SWORD, Moves.PHOTON_GEYSER, Moves.MOONBLAST, Moves.SPECTRAL_THIEF ], [Species.TADBULB]: [ Moves.PARABOLIC_CHARGE, Moves.SCALD, Moves.EARTH_POWER, Moves.ELECTRO_SHOT ], [Species.WATTREL]: [ Moves.NASTY_PLOT, Moves.TAILWIND, Moves.HEAT_WAVE, Moves.AEROBLAST ], - [Species.MASCHIFF]: [ Moves.PARTING_SHOT, Moves.KNOCK_OFF, Moves.NUZZLE, Moves.COLLISION_COURSE ], - [Species.SHROODLE]: [ Moves.FIRE_LASH, Moves.PARTING_SHOT, Moves.TOXIC, Moves.TOPSY_TURVY ], + [Species.MASCHIFF]: [ Moves.PARTING_SHOT, Moves.KNOCK_OFF, Moves.PLAY_ROUGH, Moves.COLLISION_COURSE ], + [Species.SHROODLE]: [ Moves.GASTRO_ACID, Moves.PARTING_SHOT, Moves.TOXIC, Moves.SKETCH ], [Species.BRAMBLIN]: [ Moves.TAILWIND, Moves.STRENGTH_SAP, Moves.CEASELESS_EDGE, Moves.LAST_RESPECTS ], [Species.TOEDSCOOL]: [ Moves.STRENGTH_SAP, Moves.TOPSY_TURVY, Moves.PARTING_SHOT, Moves.SAPPY_SEED ], [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.TOXIC_SPIKES, Moves.RECOVER, Moves.HEAT_WAVE, Moves.LUMINA_CRASH ], - [Species.FLITTLE]: [ Moves.COSMIC_POWER, Moves.AURA_SPHERE, Moves.ROOST, Moves.SEARING_SHOT ], - [Species.TINKATINK]: [ Moves.NUZZLE, Moves.SHIFT_GEAR, Moves.ICE_HAMMER, Moves.PYRO_BALL ], + [Species.FLITTLE]: [ Moves.COSMIC_POWER, Moves.MOONBLAST, Moves.ROOST, Moves.FIERY_DANCE ], + [Species.TINKATINK]: [ Moves.MAGICAL_TORQUE, Moves.SHIFT_GEAR, Moves.ICE_HAMMER, Moves.PYRO_BALL ], [Species.WIGLETT]: [ Moves.SHELL_SMASH, Moves.ICICLE_CRASH, Moves.SEED_BOMB, Moves.SURGING_STRIKES ], [Species.BOMBIRDIER]: [ Moves.U_TURN, Moves.TIDY_UP, Moves.SUCKER_PUNCH, Moves.MIGHTY_CLEAVE ], [Species.FINIZEN]: [ Moves.TRIPLE_AXEL, Moves.DRAIN_PUNCH, Moves.HEADLONG_RUSH, Moves.SURGING_STRIKES ], [Species.VAROOM]: [ Moves.COMBAT_TORQUE, Moves.U_TURN, Moves.BLAZING_TORQUE, Moves.NOXIOUS_TORQUE ], [Species.CYCLIZAR]: [ Moves.BATON_PASS, Moves.BLAZING_TORQUE, Moves.HEAD_CHARGE, Moves.CLANGOROUS_SOUL ], - [Species.ORTHWORM]: [ Moves.GLARE, Moves.COIL, Moves.BODY_PRESS, Moves.SHORE_UP ], - [Species.GLIMMET]: [ Moves.CALM_MIND, Moves.SHORE_UP, Moves.PARTING_SHOT, Moves.FIERY_DANCE ], - [Species.GREAVARD]: [ Moves.TIDY_UP, Moves.YAWN, Moves.SHORE_UP, Moves.COLLISION_COURSE ], + [Species.ORTHWORM]: [ Moves.SIZZLY_SLIDE, Moves.COIL, Moves.BODY_PRESS, Moves.SHORE_UP ], + [Species.GLIMMET]: [ Moves.CALM_MIND, Moves.EARTH_POWER, Moves.FIERY_DANCE, Moves.MALIGNANT_CHAIN ], + [Species.GREAVARD]: [ Moves.SHADOW_BONE, Moves.YAWN, Moves.SHORE_UP, Moves.COLLISION_COURSE ], [Species.FLAMIGO]: [ Moves.THUNDEROUS_KICK, Moves.TRIPLE_AXEL, Moves.U_TURN, Moves.VICTORY_DANCE ], - [Species.CETODDLE]: [ Moves.ICICLE_CRASH, Moves.HIGH_HORSEPOWER, Moves.RECOVER, Moves.DRAGON_DANCE ], + [Species.CETODDLE]: [ Moves.TRIPLE_AXEL, Moves.HIGH_HORSEPOWER, Moves.RECOVER, Moves.DRAGON_DANCE ], [Species.VELUZA]: [ Moves.CEASELESS_EDGE, Moves.FLIP_TURN, Moves.ICE_SPINNER, Moves.PSYBLADE ], [Species.DONDOZO]: [ Moves.SOFT_BOILED, Moves.ICE_SPINNER, Moves.TOXIC, Moves.SALT_CURE ], - [Species.TATSUGIRI]: [ Moves.ICE_BEAM, Moves.BATON_PASS, Moves.SCALD, Moves.FILLET_AWAY ], + [Species.TATSUGIRI]: [ Moves.ICE_BEAM, Moves.FILLET_AWAY, Moves.CORE_ENFORCER, Moves.STEAM_ERUPTION ], [Species.GREAT_TUSK]: [ Moves.STONE_AXE, Moves.MORNING_SUN, Moves.DRAGON_DANCE, Moves.COLLISION_COURSE ], - [Species.SCREAM_TAIL]: [ Moves.COSMIC_POWER, Moves.LUMINA_CRASH, Moves.MOONLIGHT, Moves.SHED_TAIL ], + [Species.SCREAM_TAIL]: [ Moves.TORCH_SONG, Moves.GLITZY_GLOW, Moves.MOONLIGHT, Moves.SPARKLY_SWIRL ], [Species.BRUTE_BONNET]: [ Moves.DARKEST_LARIAT, Moves.STRENGTH_SAP, Moves.EARTHQUAKE, Moves.SAPPY_SEED ], [Species.FLUTTER_MANE]: [ Moves.MOONLIGHT, Moves.FLAMETHROWER, Moves.EARTH_POWER, Moves.ASTRAL_BARRAGE ], [Species.SLITHER_WING]: [ Moves.KNOCK_OFF, Moves.VICTORY_DANCE, Moves.FIRE_LASH, Moves.THUNDEROUS_KICK ], @@ -521,12 +521,12 @@ export const speciesEggMoves = { [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_THORNS]: [ Moves.MIGHTY_CLEAVE, Moves.SHORE_UP, Moves.SHIFT_GEAR, Moves.FUSION_BOLT ], - [Species.FRIGIBAX]: [ Moves.DRAGON_DARTS, Moves.BULK_UP, Moves.SHORE_UP, Moves.GLACIAL_LANCE ], - [Species.GIMMIGHOUL]: [ Moves.COSMIC_POWER, Moves.STORED_POWER, Moves.BATON_PASS, Moves.ASTRAL_BARRAGE ], - [Species.WO_CHIEN]: [ Moves.SPORE, Moves.RAGE_POWDER, Moves.SAPPY_SEED, Moves.STRENGTH_SAP ], + [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.COSMIC_POWER, Moves.STORED_POWER, Moves.EARTH_POWER, Moves.ASTRAL_BARRAGE ], + [Species.WO_CHIEN]: [ Moves.SPORE, Moves.FIERY_WRATH, Moves.SAPPY_SEED, Moves.STRENGTH_SAP ], [Species.CHIEN_PAO]: [ Moves.KNOCK_OFF, Moves.PARTING_SHOT, Moves.BITTER_BLADE, Moves.GLACIAL_LANCE ], - [Species.TING_LU]: [ Moves.SHORE_UP, Moves.CURSE, Moves.SAPPY_SEED, Moves.THOUSAND_ARROWS ], + [Species.TING_LU]: [ Moves.SHORE_UP, Moves.WICKED_BLOW, Moves.SAPPY_SEED, Moves.THOUSAND_ARROWS ], [Species.CHI_YU]: [ Moves.FIERY_WRATH, Moves.HYDRO_STEAM, Moves.TORCH_SONG, Moves.ERUPTION ], [Species.ROARING_MOON]: [ Moves.FIRE_LASH, Moves.DRAGON_HAMMER, Moves.SUCKER_PUNCH, Moves.WICKED_BLOW ], [Species.IRON_VALIANT]: [ Moves.PLASMA_FISTS, Moves.VICTORY_DANCE, Moves.QUIVER_DANCE, Moves.MAGICAL_TORQUE ], @@ -534,28 +534,28 @@ export const speciesEggMoves = { [Species.MIRAIDON]: [ Moves.ICE_BEAM, Moves.CLANGOROUS_SOUL, Moves.RISING_VOLTAGE, Moves.DRAGON_ENERGY ], [Species.WALKING_WAKE]: [ Moves.BOUNCY_BUBBLE, Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.DRAGON_ENERGY ], [Species.IRON_LEAVES]: [ Moves.SPORE, Moves.U_TURN, Moves.MIGHTY_CLEAVE, Moves.BITTER_BLADE ], - [Species.POLTCHAGEIST]: [ Moves.COSMIC_POWER, Moves.INFERNAL_PARADE, Moves.LEECH_SEED, Moves.SPARKLY_SWIRL ], - [Species.OKIDOGI]: [ Moves.SLACK_OFF, Moves.OBSTRUCT, Moves.DIRE_CLAW, Moves.COLLISION_COURSE ], + [Species.POLTCHAGEIST]: [ Moves.SHELL_SMASH, Moves.INFERNAL_PARADE, Moves.LEECH_SEED, Moves.SPARKLY_SWIRL ], + [Species.OKIDOGI]: [ Moves.DRAIN_PUNCH, Moves.KNOCK_OFF, Moves.DIRE_CLAW, Moves.VICTORY_DANCE ], [Species.MUNKIDORI]: [ Moves.PSYSTRIKE, Moves.HEAT_WAVE, Moves.EARTH_POWER, Moves.MALIGNANT_CHAIN ], - [Species.FEZANDIPITI]: [ Moves.BATON_PASS, Moves.COSMIC_POWER, Moves.SIZZLY_SLIDE, 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.RAGING_BOLT]: [ Moves.NASTY_PLOT, Moves.FLAMETHROWER, Moves.RECOVER, 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.PHOTON_GEYSER, Moves.ELECTRO_DRIFT ], - [Species.TERAPAGOS]: [ Moves.EARTH_POWER, Moves.SHORE_UP, Moves.ICE_BEAM, Moves.SHELL_SMASH ], + [Species.TERAPAGOS]: [ Moves.MOONBLAST, Moves.RECOVER, Moves.ICE_BEAM, Moves.SHELL_SMASH ], [Species.PECHARUNT]: [ Moves.TOXIC_SPIKES, Moves.BODY_PRESS, Moves.HEX, Moves.BANEFUL_BUNKER ], [Species.ALOLA_RATTATA]: [ Moves.STORM_THROW, Moves.PLAY_ROUGH, Moves.TIDY_UP, Moves.POPULATION_BOMB ], [Species.ALOLA_SANDSHREW]: [ Moves.SPIKY_SHIELD, Moves.AQUA_CUTTER, Moves.SHIFT_GEAR, Moves.GLACIAL_LANCE ], - [Species.ALOLA_VULPIX]: [ Moves.MOONBLAST, Moves.AURORA_VEIL, Moves.PARTING_SHOT, Moves.FREEZY_FROST ], + [Species.ALOLA_VULPIX]: [ Moves.MOONBLAST, Moves.AURORA_VEIL, Moves.FLAMETHROWER, Moves.FREEZY_FROST ], [Species.ALOLA_DIGLETT]: [ Moves.THOUSAND_WAVES, Moves.SWORDS_DANCE, Moves.TRIPLE_DIVE, Moves.MOUNTAIN_GALE ], - [Species.ALOLA_MEOWTH]: [ Moves.MAKE_IT_RAIN, Moves.BUZZY_BUZZ, Moves.PARTING_SHOT, Moves.BADDY_BAD ], + [Species.ALOLA_MEOWTH]: [ Moves.BADDY_BAD, Moves.BUZZY_BUZZ, Moves.PARTING_SHOT, Moves.MAKE_IT_RAIN ], [Species.ALOLA_GEODUDE]: [ Moves.HIGH_HORSEPOWER, Moves.BULK_UP, Moves.STONE_AXE, Moves.EXTREME_SPEED ], [Species.ALOLA_GRIMER]: [ Moves.SUCKER_PUNCH, Moves.DIRE_CLAW, Moves.STRENGTH_SAP, Moves.SURGING_STRIKES ], - [Species.ETERNAL_FLOETTE]: [ Moves.FIERY_DANCE, Moves.GIGA_DRAIN, Moves.POLLEN_PUFF, Moves.QUIVER_DANCE ], + [Species.ETERNAL_FLOETTE]: [ Moves.FIERY_DANCE, Moves.CHLOROBLAST, Moves.POLLEN_PUFF, Moves.QUIVER_DANCE ], [Species.GALAR_MEOWTH]: [ Moves.AQUA_CUTTER, Moves.KNOCK_OFF, Moves.BULLET_PUNCH, Moves.BEHEMOTH_BASH ], [Species.GALAR_PONYTA]: [ Moves.SPIRIT_BREAK, Moves.EXTREME_SPEED, Moves.FLARE_BLITZ, Moves.PHOTON_GEYSER ], - [Species.GALAR_SLOWPOKE]: [ Moves.TRICK_ROOM, Moves.BADDY_BAD, Moves.MOONBLAST, Moves.LUMINA_CRASH ], + [Species.GALAR_SLOWPOKE]: [ Moves.TRICK_ROOM, Moves.BADDY_BAD, Moves.MOONBLAST, Moves.TORCH_SONG ], [Species.GALAR_FARFETCHD]: [ Moves.ROOST, Moves.SACRED_SWORD, Moves.KINGS_SHIELD, Moves.BEHEMOTH_BLADE ], [Species.GALAR_ARTICUNO]: [ Moves.AURA_SPHERE, Moves.OBLIVION_WING, Moves.ICE_BEAM, Moves.PSYSTRIKE ], [Species.GALAR_ZAPDOS]: [ Moves.TIDY_UP, Moves.FLOATY_FALL, Moves.ROOST, Moves.BOLT_BEAK ], @@ -566,13 +566,13 @@ export const speciesEggMoves = { [Species.GALAR_YAMASK]: [ Moves.STRENGTH_SAP, Moves.DIRE_CLAW, Moves.THOUSAND_WAVES, Moves.SPECTRAL_THIEF ], [Species.GALAR_STUNFISK]: [ Moves.SPIKY_SHIELD, Moves.TRICK_ROOM, Moves.SHORE_UP, Moves.SALT_CURE ], [Species.HISUI_GROWLITHE]: [ Moves.WOOD_HAMMER, Moves.HEAD_SMASH, Moves.VOLT_TACKLE, Moves.MORNING_SUN ], - [Species.HISUI_VOLTORB]: [ Moves.FROST_BREATH, Moves.NASTY_PLOT, Moves.PARABOLIC_CHARGE, Moves.SEED_FLARE ], + [Species.HISUI_VOLTORB]: [ Moves.ICE_BEAM, Moves.NASTY_PLOT, Moves.PARABOLIC_CHARGE, Moves.SEED_FLARE ], [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.ICE_SPINNER, Moves.VICTORY_DANCE ], [Species.HISUI_ZORUA]: [ Moves.MOONBLAST, Moves.AURA_SPHERE, Moves.PARTING_SHOT, Moves.BLOOD_MOON ], [Species.PALDEA_TAUROS]: [ Moves.NO_RETREAT, Moves.BLAZING_TORQUE, Moves.AQUA_STEP, Moves.THUNDEROUS_KICK ], [Species.PALDEA_WOOPER]: [ Moves.RECOVER, Moves.STONE_AXE, Moves.BANEFUL_BUNKER, Moves.SAPPY_SEED ], - [Species.BLOODMOON_URSALUNA]: [ Moves.GLARE, Moves.TRICK_ROOM, Moves.PARTING_SHOT, Moves.MIND_BLOWN ] + [Species.BLOODMOON_URSALUNA]: [ Moves.NASTY_PLOT, Moves.TRICK_ROOM, Moves.THUNDERBOLT, Moves.BOOMBURST ] }; function parseEggMoves(content: string): void { diff --git a/src/data/move.ts b/src/data/move.ts index 6849a0fe7ce..2d27afcbe47 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1,6 +1,6 @@ import { Moves } from "./enums/moves"; import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims"; -import { BattleEndPhase, MoveEffectPhase, MovePhase, NewBattlePhase, PartyStatusCurePhase, PokemonHealPhase, StatChangePhase, SwitchSummonPhase } from "../phases"; +import { BattleEndPhase, MoveEffectPhase, MovePhase, NewBattlePhase, PartyStatusCurePhase, PokemonHealPhase, StatChangePhase, SwitchSummonPhase, ToggleDoublePositionPhase } from "../phases"; import { BattleStat, getBattleStatName } from "./battle-stat"; import { EncoreTag } from "./battler-tags"; import { BattlerTagType } from "./enums/battler-tag-type"; @@ -12,10 +12,10 @@ import * as Utils from "../utils"; import { WeatherType } from "./weather"; import { ArenaTagSide, ArenaTrapTag } from "./arena-tag"; import { ArenaTagType } from "./enums/arena-tag-type"; -import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, NoTransformAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr } from "./ability"; +import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, NoTransformAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, PreventBerryUseAbAttr, BlockItemTheftAbAttr } from "./ability"; import { Abilities } from "./enums/abilities"; import { allAbilities } from './ability'; -import { PokemonHeldItemModifier } from "../modifier/modifier"; +import { PokemonHeldItemModifier, BerryModifier, PreserveBerryModifier } from "../modifier/modifier"; import { BattlerIndex } from "../battle"; import { Stat } from "./pokemon-stat"; import { TerrainType } from "./terrain"; @@ -25,6 +25,7 @@ import { ModifierPoolType } from "#app/modifier/modifier-type"; import { Command } from "../ui/command-ui-handler"; import { Biome } from "./enums/biome"; import i18next, { Localizable } from '../plugins/i18n'; +import { BerryType, BerryEffectFunc, getBerryEffectFunc } from './berry'; export enum MoveCategory { PHYSICAL, @@ -58,7 +59,8 @@ export enum MoveTarget { /** {@link https://bulbapedia.bulbagarden.net/wiki/Category:Entry_hazard-creating_moves Entry hazard-creating moves} */ ENEMY_SIDE, BOTH_SIDES, - PARTY + PARTY, + CURSE } export enum MoveFlags { @@ -780,8 +782,8 @@ export class RecoilAttr extends MoveEffectAttr { if (cancelled.value) return false; - const recoilDamage = Math.max(Math.floor((!this.useHp ? user.turnData.damageDealt : user.getMaxHp()) * this.damageRatio), - user.turnData.damageDealt ? 1 : 0); + const recoilDamage = Math.max(Math.floor((!this.useHp ? user.turnData.currDamageDealt : user.getMaxHp()) * this.damageRatio), + user.turnData.currDamageDealt ? 1 : 0); if (!recoilDamage) return false; @@ -916,6 +918,7 @@ export enum MultiHitType { _3, _3_INCR, _1_TO_10, + BEAT_UP, } /** @@ -1166,6 +1169,42 @@ export class StrengthSapHealAttr extends MoveEffectAttr { return true; } } +/** + * Attribute used for moves that change priority in a turn given a condition, + * e.g. Grassy Glide + * Called when move order is calculated in {@linkcode TurnStartPhase}. + * @extends MoveAttr + * @see {@linkcode apply} + */ +export class IncrementMovePriorityAttr extends MoveAttr { + /** The condition for a move's priority being incremented */ + private moveIncrementFunc: (pokemon: Pokemon, target:Pokemon, move: Move) => boolean; + /** The amount to increment priority by, if condition passes. */ + private increaseAmount: integer; + + constructor(moveIncrementFunc: (pokemon: Pokemon, target:Pokemon, move: Move) => boolean, increaseAmount = 1) { + super(); + + this.moveIncrementFunc = moveIncrementFunc; + this.increaseAmount = increaseAmount; + } + + /** + * Increments move priority by set amount if condition passes + * @param user {@linkcode Pokemon} using this move + * @param target {@linkcode Pokemon} target of this move + * @param move {@linkcode Move} being used + * @param args [0] {@linkcode Utils.IntegerHolder} for move priority. + * @returns true if function succeeds + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!this.moveIncrementFunc(user, target, move)) + return false; + + (args[0] as Utils.IntegerHolder).value += this.increaseAmount; + return true; + } +} export class MultiHitAttr extends MoveAttr { private multiHitType: MultiHitType; @@ -1233,6 +1272,11 @@ export class MultiHitAttr extends MoveAttr { hitTimes = 10; } break; + case MultiHitType.BEAT_UP: + // No status means the ally pokemon can contribute to Beat Up + hitTimes = user.scene.getParty().reduce((total, pokemon) => { + return total + (pokemon.id === user.id ? 1 : pokemon?.status && pokemon.status.effect !== StatusEffect.NONE ? 0 : 1) + }, 0); } (args[0] as Utils.IntegerHolder).value = hitTimes; return true; @@ -1436,6 +1480,95 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { } } +/** + * Attribute that causes targets of the move to eat a berry. If chosenBerry is not overriden, a random berry will be picked from the target's inventory. + */ +export class EatBerryAttr extends MoveEffectAttr { + protected chosenBerry: BerryModifier; + constructor() { + super(true, MoveEffectTrigger.HIT); + this.chosenBerry = undefined; + } +/** + * Causes the target to eat a berry. + * @param user {@linkcode Pokemon} Pokemon that used the move + * @param target {@linkcode Pokemon} Pokemon that will eat a berry + * @param move {@linkcode Move} The move being used + * @param args Unused + * @returns {boolean} true if the function succeeds + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!super.apply(user, target, move, args)) + return false; + + if(this.chosenBerry === undefined) { // if no berry has been provided, pick a random berry from their inventory + const heldBerries = this.getTargetHeldBerries(target); + if(heldBerries.length <= 0) + return false; + this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length)]; + } + + getBerryEffectFunc(this.chosenBerry.berryType)(target); // target eats the berry + + const preserve = new Utils.BooleanHolder(false); + target.scene.applyModifiers(PreserveBerryModifier, target.isPlayer(), target, preserve); + + if (!preserve.value){ // remove the eaten berry if not preserved + if (!--this.chosenBerry.stackCount) + target.scene.removeModifier(this.chosenBerry, !target.isPlayer()); + target.scene.updateModifiers(target.isPlayer()); +} + this.chosenBerry = undefined; + + return true; + } + + getTargetHeldBerries(target: Pokemon): BerryModifier[] { + return target.scene.findModifiers(m => m instanceof BerryModifier + && (m as BerryModifier).pokemonId === target.id, target.isPlayer()) as BerryModifier[]; + } + +} +/** + * Attribute used for moves that steal a random berry from the target. The user then eats the stolen berry. + * Used for Pluck & Bug Bite. + */ +export class StealEatBerryAttr extends EatBerryAttr { + constructor() { + super(); + } +/** + * User steals a random berry from the target and then eats it. + * @param {Pokemon} user Pokemon that used the move and will eat the stolen berry + * @param {Pokemon} target Pokemon that will have its berry stolen + * @param {Move} move Move being used + * @param {any[]} args Unused + * @returns {boolean} true if the function succeeds + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + + const cancelled = new Utils.BooleanHolder(false); + applyAbAttrs(BlockItemTheftAbAttr, target, cancelled); // check for abilities that block item theft + if(cancelled.value == true) + return false; + + const heldBerries = this.getTargetHeldBerries(target).filter(i => i.getTransferrable(false)); + + if (heldBerries.length) { // if the target has berries, pick a random berry and steal it + this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length)]; + + if (this.chosenBerry.stackCount == 1) // remove modifier if its the last berry + target.scene.removeModifier(this.chosenBerry, !target.isPlayer()); + target.scene.updateModifiers(target.isPlayer()); + + user.scene.queueMessage(getPokemonMessage(user, ` stole and ate\n${target.name}'s ${this.chosenBerry.type.name}!`)); + return super.apply(user, user, move, args); + } + + return false; + } +} + export class HealStatusEffectAttr extends MoveEffectAttr { private effects: StatusEffect[]; @@ -2034,6 +2167,46 @@ export class VariablePowerAttr extends MoveAttr { } } +export class LessPPMorePowerAttr extends VariablePowerAttr { + /** + * Power up moves when less PP user has + * @param user {@linkcode Pokemon} using this move + * @param target {@linkcode Pokemon} target of this move + * @param move {@linkcode Move} being used + * @param args [0] {@linkcode Utils.NumberHolder} of power + * @returns true if the function succeeds + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + const ppMax = move.pp; + let ppUsed = user.moveset.find((m) => m.moveId === move.id).ppUsed; + + let ppRemains = ppMax - ppUsed; + /** Reduce to 0 to avoid negative numbers if user has 1PP before attack and target has Ability.PRESSURE */ + if(ppRemains < 0) ppRemains = 0; + + const power = args[0] as Utils.NumberHolder; + + switch (ppRemains) { + case 0: + power.value = 200; + break; + case 1: + power.value = 80; + break; + case 2: + power.value = 60; + break; + case 3: + power.value = 50; + break; + default: + power.value = 40; + break; + } + return true; + } +} + export class MovePowerMultiplierAttr extends VariablePowerAttr { private powerMultiplierFunc: (user: Pokemon, target: Pokemon, move: Move) => number; @@ -2051,6 +2224,45 @@ export class MovePowerMultiplierAttr extends VariablePowerAttr { } } +/** + * Helper function to calculate the the base power of an ally's hit when using Beat Up. + * @param user The Pokemon that used Beat Up. + * @param allyIndex The party position of the ally contributing to Beat Up. + * @returns The base power of the Beat Up hit. + */ +const beatUpFunc = (user: Pokemon, allyIndex: number): number => { + const party = user.scene.getParty(); + + for (let i = allyIndex; i < party.length; i++) { + const pokemon = party[i]; + + // The user contributes to Beat Up regardless of status condition. + // Allies can contribute only if they do not have a non-volatile status condition. + if (pokemon.id !== user.id && pokemon?.status && pokemon.status.effect !== StatusEffect.NONE) { + continue; + } + return (pokemon.species.getBaseStat(Stat.ATK) / 10) + 5; + } +} + +export class BeatUpAttr extends VariablePowerAttr { + + /** + * Gets the next party member to contribute to a Beat Up hit, and calculates the base power for it. + * @param user Pokemon that used the move + * @param _target N/A + * @param _move Move with this attribute + * @param args N/A + * @returns true if the function succeeds + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + const power = args[0] as Utils.NumberHolder; + const allyIndex = user.turnData.hitCount - user.turnData.hitsLeft; + power.value = beatUpFunc(user, allyIndex); + return true; + } +} + const doublePowerChanceMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => { let message: string = null; user.scene.executeWithSeedOffset(() => { @@ -2778,6 +2990,47 @@ export class WeatherBallTypeAttr extends VariableMoveTypeAttr { } } +/** + * Changes the move's type to match the current terrain. + * Has no effect if the user is not grounded. + * @extends VariableMoveTypeAttr + * @see {@linkcode apply} + */ +export class TerrainPulseTypeAttr extends VariableMoveTypeAttr { + /** + * @param user {@linkcode Pokemon} using this move + * @param target N/A + * @param move N/A + * @param args [0] {@linkcode Utils.IntegerHolder} The move's type to be modified + * @returns true if the function succeeds + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if(!user.isGrounded) + return false; + + const currentTerrain = user.scene.arena.getTerrainType(); + const type = (args[0] as Utils.IntegerHolder); + + switch (currentTerrain) { + case TerrainType.MISTY: + type.value = Type.FAIRY; + break; + case TerrainType.ELECTRIC: + type.value = Type.ELECTRIC; + break; + case TerrainType.GRASSY: + type.value = Type.GRASS; + break; + case TerrainType.PSYCHIC: + type.value = Type.PSYCHIC; + break; + default: + return false; + } + return true; + } +} + export class HiddenPowerTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const type = (args[0] as Utils.IntegerHolder); @@ -3441,6 +3694,67 @@ export class RemoveScreensAttr extends MoveEffectAttr { } } +/** + * Attribute used for Revival Blessing. + * @extends MoveEffectAttr + * @see {@linkcode apply} + */ +export class RevivalBlessingAttr extends MoveEffectAttr { + constructor(user?: boolean) { + super(true); + } + + /** + * + * @param user {@linkcode Pokemon} using this move + * @param target {@linkcode Pokemon} target of this move + * @param move {@linkcode Move} being used + * @param args N/A + * @returns Promise, true if function succeeds. + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { + return new Promise(resolve => { + // If user is player, checks if the user has fainted pokemon + if(user instanceof PlayerPokemon + && user.scene.getParty().findIndex(p => p.isFainted())>-1) { + (user as PlayerPokemon).revivalBlessing().then(() => { + resolve(true) + }); + // If user is enemy, checks that it is a trainer, and it has fainted non-boss pokemon in party + } else if(user instanceof EnemyPokemon + && user.hasTrainer() + && user.scene.getEnemyParty().findIndex(p => p.isFainted() && !p.isBoss()) > -1) { + // Selects a random fainted pokemon + const faintedPokemon = user.scene.getEnemyParty().filter(p => p.isFainted() && !p.isBoss()); + const pokemon = faintedPokemon[user.randSeedInt(faintedPokemon.length)]; + const slotIndex = user.scene.getEnemyParty().findIndex(p => pokemon.id === p.id); + pokemon.resetStatus(); + pokemon.heal(Math.min(Math.max(Math.ceil(Math.floor(0.5 * pokemon.getMaxHp())), 1), pokemon.getMaxHp())); + user.scene.queueMessage(`${pokemon.name} was revived!`,0,true); + + if(user.scene.currentBattle.double && user.scene.getEnemyParty().length > 1) { + const allyPokemon = user.getAlly(); + if(slotIndex<=1) { + user.scene.unshiftPhase(new SwitchSummonPhase(user.scene, pokemon.getFieldIndex(), slotIndex, false, false, false)); + } else if(allyPokemon.isFainted()){ + user.scene.unshiftPhase(new SwitchSummonPhase(user.scene, allyPokemon.getFieldIndex(), slotIndex, false, false,false)); + } + } + resolve(true); + } else { + user.scene.queueMessage(`But it failed!`); + resolve(false); + } + }) + } + + getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { + if(user.hasTrainer() && user.scene.getEnemyParty().findIndex(p => p.isFainted() && !p.isBoss()) > -1) + return 20; + + return -20; + } +} export class ForceSwitchOutAttr extends MoveEffectAttr { private user: boolean; @@ -4375,7 +4689,7 @@ export function getMoveTargets(user: Pokemon, move: Moves): MoveTargetSet { switch (moveTarget) { case MoveTarget.USER: case MoveTarget.PARTY: - set = [ user]; + set = [ user ]; break; case MoveTarget.NEAR_OTHER: case MoveTarget.OTHER: @@ -4411,6 +4725,9 @@ export function getMoveTargets(user: Pokemon, move: Moves): MoveTargetSet { set = [ user, user.getAlly() ].concat(opponents); multiple = true; break; + case MoveTarget.CURSE: + set = user.getTypes(true).includes(Type.GHOST) ? (opponents.concat([ user.getAlly() ])) : [ user ]; + break; } return { targets: set.filter(p => p?.isActive(true)).map(p => p.getBattlerIndex()).filter(t => t !== undefined), multiple }; @@ -4897,7 +5214,8 @@ export function initMoves() { .soundBased(), new StatusMove(Moves.CURSE, Type.GHOST, -1, 10, -1, 0, 2) .attr(CurseAttr) - .ignoresProtect(true), + .ignoresProtect(true) + .target(MoveTarget.CURSE), new AttackMove(Moves.FLAIL, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2) .attr(LowHpPowerAttr), new StatusMove(Moves.CONVERSION_2, Type.NORMAL, -1, 30, -1, 0, 2) @@ -5115,8 +5433,9 @@ export function initMoves() { .attr(TrapAttr, BattlerTagType.WHIRLPOOL) .attr(HitsTagAttr, BattlerTagType.UNDERWATER, true), new AttackMove(Moves.BEAT_UP, Type.DARK, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 2) - .makesContact(false) - .unimplemented(), + .attr(MultiHitAttr, MultiHitType.BEAT_UP) + .attr(BeatUpAttr) + .makesContact(false), new AttackMove(Moves.FAKE_OUT, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 10, 100, 3, 3) .attr(FlinchAttr) .condition(new FirstMoveCondition()), @@ -5424,7 +5743,7 @@ export function initMoves() { .makesContact(false) .ignoresProtect(), new AttackMove(Moves.PLUCK, Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 4) - .partial(), + .attr(StealEatBerryAttr), new StatusMove(Moves.TAILWIND, Type.FLYING, -1, 15, -1, 0, 4) .windMove() .attr(AddArenaTagAttr, ArenaTagType.TAILWIND, 4, true) @@ -5460,7 +5779,7 @@ export function initMoves() { ), new AttackMove(Moves.TRUMP_CARD, Type.NORMAL, MoveCategory.SPECIAL, -1, -1, 5, -1, 0, 4) .makesContact() - .unimplemented(), + .attr(LessPPMorePowerAttr), new StatusMove(Moves.HEAL_BLOCK, Type.PSYCHIC, 100, 15, -1, 0, 4) .target(MoveTarget.ALL_NEAR_ENEMIES) .unimplemented(), @@ -5661,7 +5980,7 @@ export function initMoves() { new AttackMove(Moves.JUDGMENT, Type.NORMAL, MoveCategory.SPECIAL, 100, 100, 10, -1, 0, 4) .partial(), new AttackMove(Moves.BUG_BITE, Type.BUG, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 4) - .partial(), + .attr(StealEatBerryAttr), new AttackMove(Moves.CHARGE_BEAM, Type.ELECTRIC, MoveCategory.SPECIAL, 50, 90, 10, 70, 0, 4) .attr(StatChangeAttr, BattleStat.SPATK, 1, true), new AttackMove(Moves.WOOD_HAMMER, Type.GRASS, MoveCategory.PHYSICAL, 120, 100, 15, -1, 0, 4) @@ -6523,8 +6842,8 @@ export function initMoves() { .makesContact(false) .partial(), new StatusMove(Moves.TEATIME, Type.NORMAL, -1, 10, -1, 0, 8) - .target(MoveTarget.ALL) - .unimplemented(), + .attr(EatBerryAttr) + .target(MoveTarget.ALL), new StatusMove(Moves.OCTOLOCK, Type.FIGHTING, 100, 15, -1, 0, 8) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1) .partial(), @@ -6691,14 +7010,16 @@ export function initMoves() { .attr(SacrificialAttr) .target(MoveTarget.ALL_NEAR_OTHERS) .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.MISTY && user.isGrounded() ? 1.5 : 1) - .condition(failIfDampCondition), + .condition(failIfDampCondition) + .makesContact(false), new AttackMove(Moves.GRASSY_GLIDE, Type.GRASS, MoveCategory.PHYSICAL, 55, 100, 20, -1, 0, 8) - .partial(), + .attr(IncrementMovePriorityAttr,(user,target,move) =>user.scene.arena.getTerrainType()===TerrainType.GRASSY&&user.isGrounded()), new AttackMove(Moves.RISING_VOLTAGE, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 20, -1, 0, 8) .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.ELECTRIC && target.isGrounded() ? 2 : 1), new AttackMove(Moves.TERRAIN_PULSE, Type.NORMAL, MoveCategory.SPECIAL, 50, 100, 10, -1, 0, 8) - .pulseMove() - .partial(), + .attr(TerrainPulseTypeAttr) + .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() !== TerrainType.NONE && user.isGrounded() ? 2 : 1) + .pulseMove(), new AttackMove(Moves.SKITTER_SMACK, Type.BUG, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8) .attr(StatChangeAttr, BattleStat.SPATK, -1), new AttackMove(Moves.BURNING_JEALOUSY, Type.FIRE, MoveCategory.SPECIAL, 70, 100, 5, 100, 0, 8) @@ -6975,7 +7296,8 @@ export function initMoves() { .partial(), new StatusMove(Moves.REVIVAL_BLESSING, Type.NORMAL, -1, 1, -1, 0, 9) .triageMove() - .unimplemented(), + .attr(RevivalBlessingAttr) + .target(MoveTarget.USER), new AttackMove(Moves.SALT_CURE, Type.ROCK, MoveCategory.PHYSICAL, 40, 100, 15, -1, 0, 9) .attr(AddBattlerTagAttr, BattlerTagType.SALT_CURED) .makesContact(false), @@ -7158,7 +7480,10 @@ export function initMoves() { new AttackMove(Moves.PSYCHIC_NOISE, Type.PSYCHIC, MoveCategory.SPECIAL, 75, 100, 10, -1, 0, 9) .soundBased() .partial(), - new AttackMove(Moves.UPPER_HAND, Type.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, -1, 3, 9) + new AttackMove(Moves.UPPER_HAND, Type.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, 100, 3, 9) + .attr(FlinchAttr) + .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].priority > 0 ) + //TODO: Should also apply when target move priority increased by ability ex. gale wings .partial(), new AttackMove(Moves.MALIGNANT_CHAIN, Type.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9) .attr(StatusEffectAttr, StatusEffect.TOXIC) diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 218423d3232..deb3fdbc00e 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -16,6 +16,7 @@ import { GameMode } from '../game-mode'; import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities"; import { VariantSet } from './variant'; import i18next, { Localizable } from '../plugins/i18n'; +import { Stat } from "./pokemon-stat"; export enum Region { NORMAL, @@ -192,6 +193,15 @@ export abstract class PokemonSpeciesForm { return false; } + /** + * Gets the species' base stat amount for the given stat. + * @param stat The desired stat. + * @returns The species' base stat amount. + */ + getBaseStat(stat: Stat): integer { + return this.baseStats[stat] + } + getBaseExp(): integer { let ret = this.baseExp; switch (this.getFormSpriteKey()) { @@ -2624,7 +2634,7 @@ export const speciesStarters = { [Species.MOLTRES]: 6, [Species.DRATINI]: 4, [Species.MEWTWO]: 8, - [Species.MEW]: 7, + [Species.MEW]: 6, [Species.CHIKORITA]: 3, [Species.CYNDAQUIL]: 3, @@ -2683,7 +2693,7 @@ export const speciesStarters = { [Species.LARVITAR]: 4, [Species.LUGIA]: 8, [Species.HO_OH]: 8, - [Species.CELEBI]: 7, + [Species.CELEBI]: 6, [Species.TREECKO]: 3, [Species.TORCHIC]: 3, @@ -2755,9 +2765,9 @@ export const speciesStarters = { [Species.REGISTEEL]: 6, [Species.LATIAS]: 7, [Species.LATIOS]: 7, - [Species.KYOGRE]: 8, - [Species.GROUDON]: 8, - [Species.RAYQUAZA]: 8, + [Species.KYOGRE]: 9, + [Species.GROUDON]: 9, + [Species.RAYQUAZA]: 9, [Species.JIRACHI]: 7, [Species.DEOXYS]: 7, @@ -2799,19 +2809,19 @@ export const speciesStarters = { [Species.MANTYKE]: 3, [Species.SNOVER]: 3, [Species.ROTOM]: 5, - [Species.UXIE]: 7, - [Species.MESPRIT]: 7, - [Species.AZELF]: 7, + [Species.UXIE]: 6, + [Species.MESPRIT]: 6, + [Species.AZELF]: 6, [Species.DIALGA]: 8, [Species.PALKIA]: 8, - [Species.HEATRAN]: 7, - [Species.REGIGIGAS]: 8, + [Species.HEATRAN]: 6, + [Species.REGIGIGAS]: 7, [Species.GIRATINA]: 8, - [Species.CRESSELIA]: 7, - [Species.PHIONE]: 5, + [Species.CRESSELIA]: 6, + [Species.PHIONE]: 4, [Species.MANAPHY]: 7, - [Species.DARKRAI]: 7, - [Species.SHAYMIN]: 7, + [Species.DARKRAI]: 6, + [Species.SHAYMIN]: 6, [Species.ARCEUS]: 9, [Species.VICTINI]: 7, @@ -2893,9 +2903,9 @@ export const speciesStarters = { [Species.ZEKROM]: 8, [Species.LANDORUS]: 7, [Species.KYUREM]: 8, - [Species.KELDEO]: 7, - [Species.MELOETTA]: 7, - [Species.GENESECT]: 7, + [Species.KELDEO]: 6, + [Species.MELOETTA]: 6, + [Species.GENESECT]: 6, [Species.CHESPIN]: 3, [Species.FENNEKIN]: 3, @@ -2933,7 +2943,7 @@ export const speciesStarters = { [Species.ZYGARDE]: 8, [Species.DIANCIE]: 7, [Species.HOOPA]: 7, - [Species.VOLCANION]: 7, + [Species.VOLCANION]: 6, [Species.ETERNAL_FLOETTE]: 5, [Species.ROWLET]: 3, @@ -2961,7 +2971,7 @@ export const speciesStarters = { [Species.WIMPOD]: 3, [Species.SANDYGAST]: 3, [Species.PYUKUMUKU]: 3, - [Species.TYPE_NULL]: 6, + [Species.TYPE_NULL]: 5, [Species.MINIOR]: 5, [Species.KOMALA]: 5, [Species.TURTONATOR]: 5, @@ -2975,21 +2985,21 @@ export const speciesStarters = { [Species.TAPU_LELE]: 6, [Species.TAPU_BULU]: 6, [Species.TAPU_FINI]: 6, - [Species.COSMOG]: 7, - [Species.NIHILEGO]: 7, - [Species.BUZZWOLE]: 7, + [Species.COSMOG]: 6, + [Species.NIHILEGO]: 6, + [Species.BUZZWOLE]: 6, [Species.PHEROMOSA]: 7, - [Species.XURKITREE]: 7, - [Species.CELESTEELA]: 7, + [Species.XURKITREE]: 6, + [Species.CELESTEELA]: 6, [Species.KARTANA]: 7, - [Species.GUZZLORD]: 7, + [Species.GUZZLORD]: 6, [Species.NECROZMA]: 8, [Species.MAGEARNA]: 7, [Species.MARSHADOW]: 7, [Species.POIPOLE]: 7, - [Species.STAKATAKA]: 7, + [Species.STAKATAKA]: 6, [Species.BLACEPHALON]: 7, - [Species.ZERAORA]: 7, + [Species.ZERAORA]: 6, [Species.MELTAN]: 6, [Species.ALOLA_RATTATA]: 2, [Species.ALOLA_SANDSHREW]: 4, @@ -3036,14 +3046,14 @@ export const speciesStarters = { [Species.ARCTOVISH]: 5, [Species.DURALUDON]: 5, [Species.DREEPY]: 4, - [Species.ZACIAN]: 8, + [Species.ZACIAN]: 9, [Species.ZAMAZENTA]: 8, [Species.ETERNATUS]: 10, - [Species.KUBFU]: 7, - [Species.ZARUDE]: 7, + [Species.KUBFU]: 6, + [Species.ZARUDE]: 6, [Species.REGIELEKI]: 6, [Species.REGIDRAGO]: 6, - [Species.GLASTRIER]: 7, + [Species.GLASTRIER]: 6, [Species.SPECTRIER]: 7, [Species.CALYREX]: 8, [Species.GALAR_MEOWTH]: 4, @@ -3117,27 +3127,27 @@ export const speciesStarters = { [Species.IRON_THORNS]: 6, [Species.FRIGIBAX]: 4, [Species.GIMMIGHOUL]: 5, - [Species.WO_CHIEN]: 7, + [Species.WO_CHIEN]: 6, [Species.CHIEN_PAO]: 7, - [Species.TING_LU]: 7, + [Species.TING_LU]: 6, [Species.CHI_YU]: 7, [Species.ROARING_MOON]: 6, [Species.IRON_VALIANT]: 6, - [Species.KORAIDON]: 8, - [Species.MIRAIDON]: 8, - [Species.WALKING_WAKE]: 7, - [Species.IRON_LEAVES]: 7, + [Species.KORAIDON]: 9, + [Species.MIRAIDON]: 9, + [Species.WALKING_WAKE]: 6, + [Species.IRON_LEAVES]: 6, [Species.POLTCHAGEIST]: 4, - [Species.OKIDOGI]: 7, - [Species.MUNKIDORI]: 7, - [Species.FEZANDIPITI]: 7, - [Species.OGERPON]: 8, + [Species.OKIDOGI]: 6, + [Species.MUNKIDORI]: 6, + [Species.FEZANDIPITI]: 6, + [Species.OGERPON]: 7, [Species.GOUGING_FIRE]: 7, - [Species.RAGING_BOLT]: 7, + [Species.RAGING_BOLT]: 6, [Species.IRON_BOULDER]: 7, - [Species.IRON_CROWN]: 7, + [Species.IRON_CROWN]: 6, [Species.TERAPAGOS]: 8, - [Species.PECHARUNT]: 7, + [Species.PECHARUNT]: 6, [Species.PALDEA_TAUROS]: 5, [Species.PALDEA_WOOPER]: 3, [Species.BLOODMOON_URSALUNA]: 7, diff --git a/src/data/tms.ts b/src/data/tms.ts index 1eff9102c4c..d8b22438c37 100644 --- a/src/data/tms.ts +++ b/src/data/tms.ts @@ -43762,6 +43762,7 @@ export const tmSpecies: TmSpecies = { Species.TAUROS, Species.MEW, Species.SNUBBULL, + Species.GRANBULL, Species.SCIZOR, Species.HERACROSS, Species.TEDDIURSA, @@ -43785,11 +43786,14 @@ export const tmSpecies: TmSpecies = { Species.LUCARIO, Species.TOXICROAK, Species.GALLADE, + Species.PIGNITE, + Species.EMBOAR, Species.TIMBURR, Species.GURDURR, Species.CONKELDURR, Species.SAWK, Species.KROOKODILE, + Species.SCRAGGY, Species.SCRAFTY, Species.ESCAVALIER, Species.EELEKTROSS, @@ -60022,10 +60026,14 @@ export const tmSpecies: TmSpecies = { Species.ARCANINE, Species.AERODACTYL, Species.MEW, + Species.CROCONAW, + Species.FERALIGATR, Species.ESPEON, Species.GIRAFARIG, Species.GLIGAR, Species.STEELIX, + Species.SNUBBULL, + Species.GRANBULL, Species.HOUNDOUR, Species.HOUNDOOM, Species.POOCHYENA, diff --git a/src/data/trainer-config.ts b/src/data/trainer-config.ts index 685662a2114..0abd153ff25 100644 --- a/src/data/trainer-config.ts +++ b/src/data/trainer-config.ts @@ -235,11 +235,10 @@ export class TrainerConfig { if (!getIsInitialized()) { initI18n(); } + // This is only the male name, because the female name is handled in a different function (setHasGenders) if (name === 'Finn') { name = i18next.t('trainerNames:rival'); } - - } this.name = name; return this; @@ -254,6 +253,7 @@ export class TrainerConfig { // Make the title lowercase and replace spaces with underscores title = title.toLowerCase().replace(/\s/g, '_'); + // Get the title from the i18n file this.title = i18next.t(`titles:${title}`); @@ -281,21 +281,40 @@ export class TrainerConfig { return trainerType; } + /** + * Sets the configuration for trainers with genders, including the female name and encounter background music (BGM). + * @param {string} [nameFemale] - The name of the female trainer. If 'Ivy', a localized name will be assigned. + * @param {TrainerType | string} [femaleEncounterBgm] - The encounter BGM for the female trainer, which can be a TrainerType or a string. + * @returns {TrainerConfig} - The updated TrainerConfig instance. + **/ setHasGenders(nameFemale?: string, femaleEncounterBgm?: TrainerType | string): TrainerConfig { + // If the female name is 'Ivy' (the rival), assign a localized name. if (nameFemale === 'Ivy') { - // Give the rival a localized name - // First check if i18n is initialized + // Check if the internationalization (i18n) system is initialized. if (!getIsInitialized()) { + // Initialize the i18n system if it is not already initialized. initI18n(); } - this.nameFemale = i18next.t('trainerNames:rival_female'); - } else { + // Set the localized name for the female rival. + this.nameFemale = i18next.t('trainerNames:rival_female'); + } else { + // Otherwise, assign the provided female name. this.nameFemale = nameFemale; } + // Indicate that this trainer configuration includes genders. this.hasGenders = true; - if (femaleEncounterBgm) - this.femaleEncounterBgm = typeof femaleEncounterBgm === 'number' ? TrainerType[femaleEncounterBgm].toString().replace(/\_/g, ' ').toLowerCase() : femaleEncounterBgm; + + // If a female encounter BGM is provided. + if (femaleEncounterBgm) { + // If the BGM is a TrainerType (number), convert it to a string, replace underscores with spaces, and convert to lowercase. + // Otherwise, assign the provided string as the BGM. + this.femaleEncounterBgm = typeof femaleEncounterBgm === 'number' + ? TrainerType[femaleEncounterBgm].toString().replace(/_/g, ' ').toLowerCase() + : femaleEncounterBgm; + } + + // Return the updated TrainerConfig instance. return this; } @@ -398,29 +417,44 @@ export class TrainerConfig { return this; } + /** + * Initializes the trainer configuration for a Gym Leader. + * @param {Species | Species[]} signatureSpecies - The signature species for the Gym Leader. + * @param {Type[]} specialtyTypes - The specialty types for the Gym Leader. + * @returns {TrainerConfig} - The updated TrainerConfig instance. + * **/ initForGymLeader(signatureSpecies: (Species | Species[])[], ...specialtyTypes: Type[]): TrainerConfig { + // Check if the internationalization (i18n) system is initialized. if (!getIsInitialized()) { initI18n(); } + // Set the function to generate the Gym Leader's party template. this.setPartyTemplateFunc(getGymLeaderPartyTemplate); + + // Set up party members with their corresponding species. signatureSpecies.forEach((speciesPool, s) => { + // Ensure speciesPool is an array. if (!Array.isArray(speciesPool)) speciesPool = [speciesPool]; + // Set a function to get a random party member from the species pool. this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool)); }); + + // If specialty types are provided, set species filter and specialty types. if (specialtyTypes.length) { this.setSpeciesFilter(p => specialtyTypes.find(t => p.isOfType(t)) !== undefined); this.setSpecialtyTypes(...specialtyTypes); } - // Handle name by checking this.name - making it lowercase and replacing spaces with underscores and then calling i18next.t with the name + // Localize the trainer's name by converting it to lowercase and replacing spaces with underscores. const nameForCall = this.name.toLowerCase().replace(/\s/g, '_'); this.name = i18next.t(`trainerNames:${nameForCall}`); + // Set the title to "gym_leader". (this is the key in the i18n file) + this.setTitle('gym_leader'); - - this.setTitle("gym_leader"); + // Configure various properties for the Gym Leader. this.setMoneyMultiplier(2.5); this.setBoss(); this.setStaticParty(); @@ -430,75 +464,125 @@ export class TrainerConfig { const waveIndex = party[0].scene.currentBattle.waveIndex; return getRandomTeraModifiers(party, waveIndex >= 100 ? 1 : 0, specialtyTypes.length ? specialtyTypes : null); }); + return this; } + /** + * Initializes the trainer configuration for an Elite Four member. + * @param {Species | Species[]} signatureSpecies - The signature species for the Elite Four member. + * @param {Type[]} specialtyTypes - The specialty types for the Elite Four member. + * @returns {TrainerConfig} - The updated TrainerConfig instance. + **/ initForEliteFour(signatureSpecies: (Species | Species[])[], ...specialtyTypes: Type[]): TrainerConfig { + // Check if the internationalization (i18n) system is initialized. if (!getIsInitialized()) { initI18n(); } + // Set the party templates for the Elite Four. this.setPartyTemplates(trainerPartyTemplates.ELITE_FOUR); + + // Set up party members with their corresponding species. signatureSpecies.forEach((speciesPool, s) => { + // Ensure speciesPool is an array. if (!Array.isArray(speciesPool)) speciesPool = [speciesPool]; + // Set a function to get a random party member from the species pool. this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool)); }); + + // Set species filter and specialty types if provided, otherwise filter by base total. if (specialtyTypes.length) { this.setSpeciesFilter(p => specialtyTypes.find(t => p.isOfType(t)) && p.baseTotal >= 450); this.setSpecialtyTypes(...specialtyTypes); - } else + } else { this.setSpeciesFilter(p => p.baseTotal >= 450); - // Handle name by checking this.name - making it lowercase and replacing spaces with underscores and then calling i18next.t with the name + } + + // Localize the trainer's name by converting it to lowercase and replacing spaces with underscores. const nameForCall = this.name.toLowerCase().replace(/\s/g, '_'); this.name = i18next.t(`trainerNames:${nameForCall}`); - this.setTitle("elite_four"); + // Set the title to "elite_four". (this is the key in the i18n file) + this.setTitle('elite_four'); + + // Configure various properties for the Elite Four member. this.setMoneyMultiplier(3.25); this.setBoss(); this.setStaticParty(); this.setBattleBgm('battle_elite'); this.setVictoryBgm('victory_gym'); this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 2, specialtyTypes.length ? specialtyTypes : null)); + return this; } + /** + * Initializes the trainer configuration for a Champion. + * @param {Species | Species[]} signatureSpecies - The signature species for the Champion. + * @returns {TrainerConfig} - The updated TrainerConfig instance. + **/ initForChampion(signatureSpecies: (Species | Species[])[]): TrainerConfig { + // Check if the internationalization (i18n) system is initialized. if (!getIsInitialized()) { initI18n(); } + + // Set the party templates for the Champion. this.setPartyTemplates(trainerPartyTemplates.CHAMPION); + + // Set up party members with their corresponding species. signatureSpecies.forEach((speciesPool, s) => { + // Ensure speciesPool is an array. if (!Array.isArray(speciesPool)) speciesPool = [speciesPool]; + // Set a function to get a random party member from the species pool. 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); - // Handle name by checking this.name - making it lowercase and replacing spaces with underscores and then calling i18next.t with the name + + // Localize the trainer's name by converting it to lowercase and replacing spaces with underscores. const nameForCall = this.name.toLowerCase().replace(/\s/g, '_'); this.name = i18next.t(`trainerNames:${nameForCall}`); - this.setTitle("champion"); + + // Set the title to "champion". (this is the key in the i18n file) + this.setTitle('champion'); + + // Configure various properties for the Champion. this.setMoneyMultiplier(10); this.setBoss(); this.setStaticParty(); this.setBattleBgm('battle_champion_alder'); this.setVictoryBgm('victory_champion'); this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 3)); + return this; } + /** + * Retrieves the title for the trainer based on the provided trainer slot and variant. + * @param {TrainerSlot} trainerSlot - The slot to determine which title to use. Defaults to TrainerSlot.NONE. + * @param {TrainerVariant} variant - The variant of the trainer to determine the specific title. + * @returns {string} - The title of the trainer. + **/ getTitle(trainerSlot: TrainerSlot = TrainerSlot.NONE, variant: TrainerVariant): string { let ret = this.name; + // Check if the variant is double and the name for double exists if (!trainerSlot && variant === TrainerVariant.DOUBLE && this.nameDouble) return this.nameDouble; + // Female variant if (this.hasGenders) { + // If the name is already set if (this.nameFemale) { + // Check if the variant is either female or this is for the partner in a double battle if (variant === TrainerVariant.FEMALE || (variant === TrainerVariant.DOUBLE && trainerSlot === TrainerSlot.TRAINER_PARTNER)) return this.nameFemale; } else - // Check if !variant is true, if so return the name, else return the name with _female appended if (variant) { if (!getIsInitialized()) { @@ -603,12 +687,12 @@ function getRandomTeraModifiers(party: EnemyPokemon[], count: integer, types?: T export const trainerConfigs: TrainerConfigs = { [TrainerType.UNKNOWN]: new TrainerConfig(0).setHasGenders(), - [TrainerType.ACE_TRAINER]: new TrainerConfig(++t).setHasGenders().setHasDouble('Ace Duo').setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.ACE_TRAINER) + [TrainerType.ACE_TRAINER]: new TrainerConfig(++t).setHasGenders('Ace Trainer Female').setHasDouble('Ace Duo').setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.ACE_TRAINER) .setPartyTemplateFunc(scene => getWavePartyTemplate(scene, trainerPartyTemplates.THREE_WEAK_BALANCED, trainerPartyTemplates.FOUR_WEAK_BALANCED, trainerPartyTemplates.FIVE_WEAK_BALANCED, trainerPartyTemplates.SIX_WEAK_BALANCED)), [TrainerType.ARTIST]: new TrainerConfig(++t).setEncounterBgm(TrainerType.RICH).setPartyTemplates(trainerPartyTemplates.ONE_STRONG, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.THREE_AVG) .setSpeciesPools([ Species.SMEARGLE ]), - [TrainerType.BACKERS]: new TrainerConfig(++t).setHasGenders().setDoubleOnly().setEncounterBgm(TrainerType.CYCLIST), - [TrainerType.BACKPACKER]: new TrainerConfig(++t).setHasGenders().setHasDouble('Backpackers').setSpeciesFilter(s => s.isOfType(Type.FLYING) || s.isOfType(Type.ROCK)).setEncounterBgm(TrainerType.BACKPACKER) + [TrainerType.BACKERS]: new TrainerConfig(++t).setHasGenders("Backers").setDoubleOnly().setEncounterBgm(TrainerType.CYCLIST), + [TrainerType.BACKPACKER]: new TrainerConfig(++t).setHasGenders("Backpacker Female").setHasDouble('Backpackers').setSpeciesFilter(s => s.isOfType(Type.FLYING) || s.isOfType(Type.ROCK)).setEncounterBgm(TrainerType.BACKPACKER) .setPartyTemplates(trainerPartyTemplates.ONE_STRONG, trainerPartyTemplates.ONE_WEAK_ONE_STRONG, trainerPartyTemplates.ONE_AVG_ONE_STRONG) .setSpeciesPools({ [TrainerPoolTier.COMMON]: [ Species.RHYHORN, Species.AIPOM, Species.MAKUHITA, Species.MAWILE, Species.NUMEL, Species.LILLIPUP, Species.SANDILE, Species.WOOLOO ], @@ -628,17 +712,17 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.SUPER_RARE]: [ Species.HITMONTOP, Species.INFERNAPE, Species.GALLADE, Species.HAWLUCHA, Species.HAKAMO_O ], [TrainerPoolTier.ULTRA_RARE]: [ Species.KUBFU ] }), - [TrainerType.BREEDER]: new TrainerConfig(++t).setMoneyMultiplier(1.325).setEncounterBgm(TrainerType.POKEFAN).setHasGenders().setHasDouble('Breeders') + [TrainerType.BREEDER]: new TrainerConfig(++t).setMoneyMultiplier(1.325).setEncounterBgm(TrainerType.POKEFAN).setHasGenders("Breeder Female").setHasDouble('Breeders') .setPartyTemplateFunc(scene => getWavePartyTemplate(scene, trainerPartyTemplates.FOUR_WEAKER, trainerPartyTemplates.FIVE_WEAKER, trainerPartyTemplates.SIX_WEAKER)) .setSpeciesFilter(s => s.baseTotal < 450), - [TrainerType.CLERK]: new TrainerConfig(++t).setHasGenders().setHasDouble('Colleagues').setEncounterBgm(TrainerType.CLERK) + [TrainerType.CLERK]: new TrainerConfig(++t).setHasGenders("Clerk Female").setHasDouble('Colleagues').setEncounterBgm(TrainerType.CLERK) .setPartyTemplates(trainerPartyTemplates.TWO_WEAK, trainerPartyTemplates.THREE_WEAK, trainerPartyTemplates.ONE_AVG, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.TWO_WEAK_ONE_AVG) .setSpeciesPools({ [TrainerPoolTier.COMMON]: [ Species.MEOWTH, Species.PSYDUCK, Species.BUDEW, Species.PIDOVE, Species.CINCCINO, Species.LITLEO ], [TrainerPoolTier.UNCOMMON]: [ Species.JIGGLYPUFF, Species.MAGNEMITE, Species.MARILL, Species.COTTONEE, Species.SKIDDO ], [TrainerPoolTier.RARE]: [ Species.BUIZEL, Species.SNEASEL, Species.KLEFKI, Species.INDEEDEE ] }), - [TrainerType.CYCLIST]: new TrainerConfig(++t).setMoneyMultiplier(1.3).setHasGenders().setHasDouble('Cyclists').setEncounterBgm(TrainerType.CYCLIST) + [TrainerType.CYCLIST]: new TrainerConfig(++t).setMoneyMultiplier(1.3).setHasGenders("Cyclist Female").setHasDouble('Cyclists').setEncounterBgm(TrainerType.CYCLIST) .setPartyTemplates(trainerPartyTemplates.TWO_WEAK, trainerPartyTemplates.ONE_AVG) .setSpeciesPools({ [TrainerPoolTier.COMMON]: [ Species.PICHU, Species.STARLY, Species.TAILLOW, Species.BOLTUND ], @@ -697,9 +781,9 @@ export const trainerConfigs: TrainerConfigs = { }), [TrainerType.PARASOL_LADY]: new TrainerConfig(++t).setMoneyMultiplier(1.55).setEncounterBgm(TrainerType.PARASOL_LADY).setSpeciesFilter(s => s.isOfType(Type.WATER)), [TrainerType.PILOT]: new TrainerConfig(++t).setEncounterBgm(TrainerType.CLERK).setSpeciesFilter(s => tmSpecies[Moves.FLY].indexOf(s.speciesId) > -1), - [TrainerType.POKEFAN]: new TrainerConfig(++t).setMoneyMultiplier(1.4).setName('Poké Fan').setHasGenders().setHasDouble('Poké Fan Family').setEncounterBgm(TrainerType.POKEFAN) + [TrainerType.POKEFAN]: new TrainerConfig(++t).setMoneyMultiplier(1.4).setName('PokéFan').setHasGenders("PokéFan Female").setHasDouble('PokéFan Family').setEncounterBgm(TrainerType.POKEFAN) .setPartyTemplates(trainerPartyTemplates.SIX_WEAKER, trainerPartyTemplates.FOUR_WEAK, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.ONE_STRONG, trainerPartyTemplates.FOUR_WEAK_SAME, trainerPartyTemplates.FIVE_WEAK, trainerPartyTemplates.SIX_WEAKER_SAME), - [TrainerType.PRESCHOOLER]: new TrainerConfig(++t).setMoneyMultiplier(0.2).setEncounterBgm(TrainerType.YOUNGSTER).setHasGenders(undefined, 'lass').setHasDouble('Preschoolers') + [TrainerType.PRESCHOOLER]: new TrainerConfig(++t).setMoneyMultiplier(0.2).setEncounterBgm(TrainerType.YOUNGSTER).setHasGenders("Preschooler Female", 'lass').setHasDouble('Preschoolers') .setPartyTemplates(trainerPartyTemplates.THREE_WEAK, trainerPartyTemplates.FOUR_WEAKER, trainerPartyTemplates.TWO_WEAK_SAME_ONE_AVG, trainerPartyTemplates.FIVE_WEAKER) .setSpeciesPools({ [TrainerPoolTier.COMMON]: [ Species.CATERPIE, Species.PICHU, Species.SANDSHREW, Species.LEDYBA, Species.BUDEW, Species.BURMY, Species.WOOLOO, Species.PAWMI, Species.SMOLIV ], @@ -707,7 +791,7 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.RARE]: [ Species.RALTS, Species.RIOLU, Species.JOLTIK, Species.TANDEMAUS ], [TrainerPoolTier.SUPER_RARE]: [ Species.DARUMAKA, Species.TINKATINK ], }), - [TrainerType.PSYCHIC]: new TrainerConfig(++t).setHasGenders().setHasDouble('Psychics').setMoneyMultiplier(1.4).setEncounterBgm(TrainerType.PSYCHIC) + [TrainerType.PSYCHIC]: new TrainerConfig(++t).setHasGenders("Psychic Female").setHasDouble('Psychics').setMoneyMultiplier(1.4).setEncounterBgm(TrainerType.PSYCHIC) .setPartyTemplates(trainerPartyTemplates.TWO_WEAK, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.TWO_WEAK_SAME_ONE_AVG, trainerPartyTemplates.TWO_WEAK_SAME_TWO_WEAK_SAME, trainerPartyTemplates.ONE_STRONGER) .setSpeciesPools({ [TrainerPoolTier.COMMON]: [ Species.ABRA, Species.DROWZEE, Species.RALTS, Species.SPOINK, Species.GOTHITA, Species.SOLOSIS, Species.BLIPBUG, Species.ESPURR, Species.HATENNA ], @@ -715,7 +799,7 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.RARE]: [ Species.ELGYEM, Species.SIGILYPH, Species.BALTOY, Species.GIRAFARIG, Species.MEOWSTIC ], [TrainerPoolTier.SUPER_RARE]: [ Species.BELDUM, Species.ESPEON, Species.STANTLER ], }), - [TrainerType.RANGER]: new TrainerConfig(++t).setMoneyMultiplier(1.4).setName('Pokémon Ranger').setEncounterBgm(TrainerType.BACKPACKER).setHasGenders().setHasDouble('Pokémon Rangers') + [TrainerType.RANGER]: new TrainerConfig(++t).setMoneyMultiplier(1.4).setName('Pokémon Ranger').setEncounterBgm(TrainerType.BACKPACKER).setHasGenders("Pokémon Ranger Female").setHasDouble('Pokémon Rangers') .setSpeciesPools({ [TrainerPoolTier.COMMON]: [ Species.PICHU, Species.GROWLITHE, Species.PONYTA, Species.ZIGZAGOON, Species.SEEDOT, Species.BIDOOF, Species.RIOLU, Species.SEWADDLE, Species.SKIDDO, Species.SALANDIT, Species.YAMPER ], [TrainerPoolTier.UNCOMMON]: [ Species.AZURILL, Species.TAUROS, Species.MAREEP, Species.FARFETCHD, Species.TEDDIURSA, Species.SHROOMISH, Species.ELECTRIKE, Species.BUDEW, Species.BUIZEL, Species.MUDBRAY, Species.STUFFUL ], @@ -725,7 +809,7 @@ export const trainerConfigs: TrainerConfigs = { [TrainerType.RICH]: new TrainerConfig(++t).setMoneyMultiplier(5).setName('Gentleman').setHasGenders('Madame').setHasDouble('Rich Couple'), [TrainerType.RICH_KID]: new TrainerConfig(++t).setMoneyMultiplier(3.75).setName('Rich Boy').setHasGenders('Lady').setHasDouble('Rich Kids').setEncounterBgm(TrainerType.RICH), [TrainerType.ROUGHNECK]: new TrainerConfig(++t).setMoneyMultiplier(1.4).setEncounterBgm(TrainerType.ROUGHNECK).setSpeciesFilter(s => s.isOfType(Type.DARK)), - [TrainerType.SCIENTIST]: new TrainerConfig(++t).setHasGenders().setHasDouble('Scientists').setMoneyMultiplier(1.7).setEncounterBgm(TrainerType.SCIENTIST) + [TrainerType.SCIENTIST]: new TrainerConfig(++t).setHasGenders("Scientist Female").setHasDouble('Scientists').setMoneyMultiplier(1.7).setEncounterBgm(TrainerType.SCIENTIST) .setSpeciesPools({ [TrainerPoolTier.COMMON]: [ Species.MAGNEMITE, Species.GRIMER, Species.DROWZEE, Species.VOLTORB, Species.KOFFING ], [TrainerPoolTier.UNCOMMON]: [ Species.BALTOY, Species.BRONZOR, Species.FERROSEED, Species.KLINK, Species.CHARJABUG, Species.BLIPBUG, Species.HELIOPTILE ], @@ -734,29 +818,29 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.ULTRA_RARE]: [ Species.ROTOM, Species.MELTAN ] }), [TrainerType.SMASHER]: new TrainerConfig(++t).setMoneyMultiplier(1.2).setEncounterBgm(TrainerType.CYCLIST), - [TrainerType.SNOW_WORKER]: new TrainerConfig(++t).setName('Worker').setHasGenders().setHasDouble('Workers').setMoneyMultiplier(1.7).setEncounterBgm(TrainerType.CLERK).setSpeciesFilter(s => s.isOfType(Type.ICE) || s.isOfType(Type.STEEL)), + [TrainerType.SNOW_WORKER]: new TrainerConfig(++t).setName('Worker').setHasGenders("Worker Female").setHasDouble('Workers').setMoneyMultiplier(1.7).setEncounterBgm(TrainerType.CLERK).setSpeciesFilter(s => s.isOfType(Type.ICE) || s.isOfType(Type.STEEL)), [TrainerType.STRIKER]: new TrainerConfig(++t).setMoneyMultiplier(1.2).setEncounterBgm(TrainerType.CYCLIST), - [TrainerType.SCHOOL_KID]: new TrainerConfig(++t).setMoneyMultiplier(0.75).setEncounterBgm(TrainerType.YOUNGSTER).setHasGenders(undefined, 'lass').setHasDouble('School Kids') + [TrainerType.SCHOOL_KID]: new TrainerConfig(++t).setMoneyMultiplier(0.75).setEncounterBgm(TrainerType.YOUNGSTER).setHasGenders("School Kid Female", 'lass').setHasDouble('School Kids') .setSpeciesPools({ [TrainerPoolTier.COMMON]: [ Species.ODDISH, Species.EXEGGCUTE, Species.TEDDIURSA, Species.WURMPLE, Species.RALTS, Species.SHROOMISH, Species.FLETCHLING ], [TrainerPoolTier.UNCOMMON]: [ Species.VOLTORB, Species.WHISMUR, Species.MEDITITE, Species.MIME_JR, Species.NYMBLE ], [TrainerPoolTier.RARE]: [ Species.TANGELA, Species.EEVEE, Species.YANMA ], [TrainerPoolTier.SUPER_RARE]: [ Species.TADBULB ] }), - [TrainerType.SWIMMER]: new TrainerConfig(++t).setMoneyMultiplier(1.3).setEncounterBgm(TrainerType.PARASOL_LADY).setHasGenders().setHasDouble('Swimmers').setSpecialtyTypes(Type.WATER).setSpeciesFilter(s => s.isOfType(Type.WATER)), + [TrainerType.SWIMMER]: new TrainerConfig(++t).setMoneyMultiplier(1.3).setEncounterBgm(TrainerType.PARASOL_LADY).setHasGenders("Swimmer Female").setHasDouble('Swimmers').setSpecialtyTypes(Type.WATER).setSpeciesFilter(s => s.isOfType(Type.WATER)), [TrainerType.TWINS]: new TrainerConfig(++t).setDoubleOnly().setMoneyMultiplier(0.65).setUseSameSeedForAllMembers() .setPartyTemplateFunc(scene => getWavePartyTemplate(scene, trainerPartyTemplates.TWO_WEAK, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.TWO_STRONG)) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PLUSLE, Species.VOLBEAT, Species.PACHIRISU, Species.SILCOON, Species.METAPOD, Species.IGGLYBUFF, Species.PETILIL, Species.EEVEE ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.MINUN, Species.ILLUMISE, Species.EMOLGA, Species.CASCOON, Species.KAKUNA, Species.CLEFFA, Species.COTTONEE, Species.EEVEE ], TrainerSlot.TRAINER_PARTNER)) .setEncounterBgm(TrainerType.TWINS), - [TrainerType.VETERAN]: new TrainerConfig(++t).setHasGenders().setHasDouble('Veteran Duo').setMoneyMultiplier(2.5).setEncounterBgm(TrainerType.ACE_TRAINER).setSpeciesFilter(s => s.isOfType(Type.DRAGON)), + [TrainerType.VETERAN]: new TrainerConfig(++t).setHasGenders("Veteran Female").setHasDouble('Veteran Duo').setMoneyMultiplier(2.5).setEncounterBgm(TrainerType.ACE_TRAINER).setSpeciesFilter(s => s.isOfType(Type.DRAGON)), [TrainerType.WAITER]: new TrainerConfig(++t).setHasGenders('Waitress').setHasDouble('Restaurant Staff').setMoneyMultiplier(1.5).setEncounterBgm(TrainerType.CLERK) .setSpeciesPools({ [TrainerPoolTier.COMMON]: [ Species.CLEFFA, Species.CHATOT, Species.PANSAGE, Species.PANSEAR, Species.PANPOUR, Species.MINCCINO ], [TrainerPoolTier.UNCOMMON]: [ Species.TROPIUS, Species.PETILIL, Species.BOUNSWEET, Species.INDEEDEE ], [TrainerPoolTier.RARE]: [ Species.APPLIN, Species.SINISTEA, Species.POLTCHAGEIST ] }), - [TrainerType.WORKER]: new TrainerConfig(++t).setHasGenders().setHasDouble('Workers').setEncounterBgm(TrainerType.CLERK).setMoneyMultiplier(1.7).setSpeciesFilter(s => s.isOfType(Type.ROCK) || s.isOfType(Type.STEEL)), + [TrainerType.WORKER]: new TrainerConfig(++t).setHasGenders("Worker Female").setHasDouble('Workers').setEncounterBgm(TrainerType.CLERK).setMoneyMultiplier(1.7).setSpeciesFilter(s => s.isOfType(Type.ROCK) || s.isOfType(Type.STEEL)), [TrainerType.YOUNGSTER]: new TrainerConfig(++t).setMoneyMultiplier(0.5).setEncounterBgm(TrainerType.YOUNGSTER).setHasGenders('Lass', 'lass').setHasDouble('Beginners').setPartyTemplates(trainerPartyTemplates.TWO_WEAKER) .setSpeciesPools( [ Species.CATERPIE, Species.WEEDLE, Species.RATTATA, Species.SENTRET, Species.POOCHYENA, Species.ZIGZAGOON, Species.WURMPLE, Species.BIDOOF, Species.PATRAT, Species.LILLIPUP ] diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 0a5e0a6a991..abce327a645 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -4,7 +4,7 @@ import { Variant, VariantSet, variantColorCache } from '#app/data/variant'; import { variantData } from '#app/data/variant'; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from '../ui/battle-info'; import { Moves } from "../data/enums/moves"; -import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, VariablePowerAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, MultiHitAttr, StatusMoveTypeImmunityAttr, MoveTarget, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatChangesAttr, SacrificialAttr, VariableMoveTypeAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr } from "../data/move"; +import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, VariablePowerAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, MultiHitAttr, StatusMoveTypeImmunityAttr, MoveTarget, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatChangesAttr, SacrificialAttr, VariableMoveTypeAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit } from "../data/move"; import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from '../data/pokemon-species'; import * as Utils from '../utils'; import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from '../data/type'; @@ -17,7 +17,7 @@ import { initMoveAnim, loadMoveAnimAssets } from '../data/battle-anims'; import { Status, StatusEffect, getRandomStatus } from '../data/status-effect'; import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from '../data/pokemon-evolutions'; import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from '../data/tms'; -import { DamagePhase, FaintPhase, LearnMovePhase, ObtainStatusEffectPhase, StatChangePhase, SwitchSummonPhase } from '../phases'; +import { DamagePhase, FaintPhase, LearnMovePhase, ObtainStatusEffectPhase, StatChangePhase, SwitchPhase, SwitchSummonPhase, ToggleDoublePositionPhase } from '../phases'; import { BattleStat } from '../data/battle-stat'; import { BattlerTag, BattlerTagLapseType, EncoreTag, HelpingHandTag, HighestStatBoostTag, TypeBoostTag, getBattlerTag } from '../data/battler-tags'; import { BattlerTagType } from "../data/enums/battler-tag-type"; @@ -27,7 +27,7 @@ import { TempBattleStat } from '../data/temp-battle-stat'; import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag'; import { ArenaTagType } from "../data/enums/arena-tag-type"; import { Biome } from "../data/enums/biome"; -import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr } from '../data/ability'; +import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr } from '../data/ability'; import { Abilities } from "#app/data/enums/abilities"; import PokemonData from '../system/pokemon-data'; import Battle, { BattlerIndex } from '../battle'; @@ -329,9 +329,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (variantSet && variantSet[this.variant] === 1) { if (variantColorCache.hasOwnProperty(key)) return resolve(); - this.scene.cachedFetch(`./images/pokemon/variant/${useExpSprite ? 'exp/' : ''}${battleSpritePath}.json`).then(res => res.json()).then(c => { - variantColorCache[key] = c; - resolve(); + this.scene.cachedFetch(`./images/pokemon/variant/${useExpSprite ? 'exp/' : ''}${battleSpritePath}.json`). + then(res => { + // Prevent the JSON from processing if it failed to load + if (!res.ok) { + console.error(`Could not load ${res.url}!`); + return; + } + return res.json() + }).then(c => { + variantColorCache[key] = c; + resolve(); }); } else resolve(); @@ -493,9 +501,31 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.shinySparkle = shinySparkle; } + /** + * Attempts to animate a given {@linkcode Phaser.GameObjects.Sprite} + * @see {@linkcode Phaser.GameObjects.Sprite.play} + * @param sprite {@linkcode Phaser.GameObjects.Sprite} to animate + * @param tintSprite {@linkcode Phaser.GameObjects.Sprite} placed on top of the sprite to add a color tint + * @param animConfig {@linkcode String} to pass to {@linkcode Phaser.GameObjects.Sprite.play} + * @returns true if the sprite was able to be animated + */ + tryPlaySprite(sprite: Phaser.GameObjects.Sprite, tintSprite: Phaser.GameObjects.Sprite, key: string): boolean { + // Catch errors when trying to play an animation that doesn't exist + try { + sprite.play(key); + tintSprite.play(key); + } + catch(error: unknown) { + console.error(`Couldn't play animation for '${key}'!\nIs the image for this Pokemon missing?\n`, error); + + return false; + } + + return true; + } + playAnim(): void { - this.getSprite().play(this.getBattleSpriteKey()); - this.getTintSprite().play(this.getBattleSpriteKey()); + this.tryPlaySprite(this.getSprite(), this.getTintSprite(), this.getBattleSpriteKey()); } getFieldPositionOffset(): [ number, number ] { @@ -1097,7 +1127,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let shinyThreshold = new Utils.IntegerHolder(32); if (thresholdOverride === undefined) { if (!this.hasTrainer()) { - if (new Date() < new Date('2024-05-21')) + if (new Date() < new Date(Date.UTC(2024, 4, 22, 0))) shinyThreshold.value *= 3; this.scene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold); } @@ -1252,11 +1282,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.isBoss()) // Bosses never get self ko moves movePool = movePool.filter(m => !allMoves[m[0]].getAttrs(SacrificialAttr).length); + movePool = movePool.filter(m => !allMoves[m[0]].getAttrs(SacrificialAttrOnHit).length); if (this.hasTrainer()) { // Trainers never get OHKO moves movePool = movePool.filter(m => !allMoves[m[0]].getAttrs(OneHitKOAttr).length); // Half the weight of self KO moves movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].getAttrs(SacrificialAttr).length ? 0.5 : 1)]); + movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].getAttrs(SacrificialAttrOnHit).length ? 0.5 : 1)]); // Trainers get a weight bump to stat buffing moves movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].getAttrs(StatChangeAttr).some(a => (a as StatChangeAttr).levels > 1 && (a as StatChangeAttr).selfTarget) ? 1.25 : 1)]); // Trainers get a weight decrease to multiturn moves @@ -1493,6 +1525,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const critOnly = new Utils.BooleanHolder(false); const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT); applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly); + applyAbAttrs(ConditionalCritAbAttr, source, null, critOnly, this, move); if (critOnly.value || critAlways) isCritical = true; else { @@ -1508,12 +1541,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { critLevel.value += 2; const critChance = [24, 8, 2, 1][Math.max(0, Math.min(critLevel.value, 3))]; isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !this.scene.randBattleSeedInt(critChance)); - if (isCritical) { - const blockCrit = new Utils.BooleanHolder(false); - applyAbAttrs(BlockCritAbAttr, this, null, blockCrit); - if (blockCrit.value) - isCritical = false; - } + } + if (isCritical) { + const blockCrit = new Utils.BooleanHolder(false); + applyAbAttrs(BlockCritAbAttr, this, null, blockCrit); + if (blockCrit.value) + isCritical = false; } const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, null, isCritical)); const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical)); @@ -1628,6 +1661,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.gameData.gameStats.highestDamage = damage.value; } source.turnData.damageDealt += damage.value; + source.turnData.currDamageDealt = damage.value; this.battleData.hitCount++; const attackResult = { move: move.id, result: result as DamageResult, damage: damage.value, critical: isCritical, sourceId: source.id }; this.turnData.attacksReceived.unshift(attackResult); @@ -2649,6 +2683,42 @@ export class PlayerPokemon extends Pokemon { sd.friendship = Math.max((sd.friendship || 0) + starterAmount.value, 0); } } + /** + * Handles Revival Blessing when used by player. + * @returns Promise to revive a pokemon. + * @see {@linkcode RevivalBlessingAttr} + */ + revivalBlessing(): Promise { + return new Promise(resolve => { + this.scene.ui.setMode(Mode.PARTY, PartyUiMode.REVIVAL_BLESSING, this.getFieldIndex(), (slotIndex:integer, option: PartyOption) => { + if(slotIndex >= 0 && slotIndex<6) { + const pokemon = this.scene.getParty()[slotIndex]; + if(!pokemon || !pokemon.isFainted()) + resolve(); + + pokemon.resetTurnData(); + pokemon.resetStatus(); + pokemon.heal(Math.min(Math.max(Math.ceil(Math.floor(0.5 * pokemon.getMaxHp())), 1), pokemon.getMaxHp())); + this.scene.queueMessage(`${pokemon.name} was revived!`,0,true); + + if(this.scene.currentBattle.double && this.scene.getParty().length > 1) { + const allyPokemon = this.getAlly(); + if(slotIndex<=1) { + // Revived ally pokemon + this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, pokemon.getFieldIndex(), slotIndex, false, false, true)); + this.scene.unshiftPhase(new ToggleDoublePositionPhase(this.scene, true)); + } else if(allyPokemon.isFainted()) { + // Revived party pokemon, and ally pokemon is fainted + this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, allyPokemon.getFieldIndex(), slotIndex, false, false, true)); + this.scene.unshiftPhase(new ToggleDoublePositionPhase(this.scene, true)); + } + } + + } + this.scene.ui.setMode(Mode.MESSAGE).then(() => resolve()); + }, PartyUiHandler.FilterFainted) + }) + } getPossibleEvolution(evolution: SpeciesFormEvolution): Promise { return new Promise(resolve => { @@ -3350,6 +3420,7 @@ export class PokemonTurnData { public hitCount: integer; public hitsLeft: integer; public damageDealt: integer = 0; + public currDamageDealt: integer = 0; public damageTaken: integer = 0; public attacksReceived: AttackMoveResult[] = []; } diff --git a/src/field/trainer.ts b/src/field/trainer.ts index 60497239488..8cd06cac12f 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -96,30 +96,54 @@ export default class Trainer extends Phaser.GameObjects.Container { return this.config.getSpriteKey(this.variant === TrainerVariant.FEMALE || forceFemale); } + /** + * Returns the name of the trainer based on the provided trainer slot and the option to include a title. + * @param {TrainerSlot} trainerSlot - The slot to determine which name to use. Defaults to TrainerSlot.NONE. + * @param {boolean} includeTitle - Whether to include the title in the returned name. Defaults to false. + * @returns {string} - The formatted name of the trainer. + **/ getName(trainerSlot: TrainerSlot = TrainerSlot.NONE, includeTitle: boolean = false): string { + // Get the base title based on the trainer slot and variant. let name = this.config.getTitle(trainerSlot, this.variant); + + // Determine the title to include based on the configuration and includeTitle flag. let title = includeTitle && this.config.title ? this.config.title : null; - + // If the trainer has a name (not null or undefined). if (this.name) { - if (includeTitle) - - // Check if i18n is initialized + // If the title should be included. + if (includeTitle) { + // Check if the internationalization (i18n) system is initialized. if (!getIsInitialized()) { - initI18n() + // Initialize the i18n system if it is not already initialized. + initI18n(); } + // Get the localized trainer class name from the i18n file and set it as the title. + // This is used for trainer class names, not titles like "Elite Four, Champion, etc." title = i18next.t(`trainerClasses:${name.toLowerCase().replace(/\s/g, '_')}`); + } + + // If no specific trainer slot is set. if (!trainerSlot) { + // Use the trainer's name. name = this.name; - if (this.partnerName) + // If there is a partner name, concatenate it with the trainer's name using "&". + if (this.partnerName) { name = `${name} & ${this.partnerName}`; - } else + } + } else { + // Assign the name based on the trainer slot: + // Use 'this.name' if 'trainerSlot' is TRAINER. + // Otherwise, use 'this.partnerName' if it exists, or 'this.name' if it doesn't. name = trainerSlot === TrainerSlot.TRAINER ? this.name : this.partnerName || this.name; + } } + // Return the formatted name, including the title if it is set. return title ? `${title} ${name}` : name; } + isDouble(): boolean { return this.config.doubleOnly || this.variant === TrainerVariant.DOUBLE; } diff --git a/src/locales/de/ability-trigger.ts b/src/locales/de/ability-trigger.ts index 27d2053b621..eb5022996d4 100644 --- a/src/locales/de/ability-trigger.ts +++ b/src/locales/de/ability-trigger.ts @@ -2,4 +2,5 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n"; export const abilityTriggers: SimpleTranslationEntries = { 'blockRecoilDamage' : `{{pokemonName}} wurde durch {{abilityName}}\nvor Rückstoß geschützt!`, + 'badDreams': `{{pokemonName}} ist in einem Alptraum gefangen!`, } as const; diff --git a/src/locales/de/config.ts b/src/locales/de/config.ts index af52d8bc51b..3580d74876d 100644 --- a/src/locales/de/config.ts +++ b/src/locales/de/config.ts @@ -20,6 +20,7 @@ import { tutorial } from "./tutorial"; import { weather } from "./weather"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { berry } from "./berry"; +import { voucher } from "./voucher"; export const deConfig = { ability: ability, @@ -46,4 +47,5 @@ export const deConfig = { weather: weather, battleMessageUiHandler: battleMessageUiHandler, berry: berry, -} \ No newline at end of file + voucher: voucher, +} diff --git a/src/locales/de/trainers.ts b/src/locales/de/trainers.ts index 11e8d914842..93eb04cca67 100644 --- a/src/locales/de/trainers.ts +++ b/src/locales/de/trainers.ts @@ -68,8 +68,9 @@ export const trainerClasses: SimpleTranslationEntries = { "officer": "Polizist", "parasol_lady": "Schirmdame", "pilot": "Pilot", - "pokefan": "Pokéfan", - "pokefan_family": "Pokéfan-Pärchen", + "pokéfan": "Pokéfan", + "pokéfan_female": "Pokéfan", + "pokéfan_family": "Pokéfan-Pärchen", "preschooler": "Vorschüler", "preschooler_female": "Vorschülerin", "preschoolers": "Vorschüler", @@ -77,6 +78,7 @@ export const trainerClasses: SimpleTranslationEntries = { "psychic_female": "Seherin", "psychics": "Seher", "pokémon_ranger": "Pokémon-Ranger", + "pokémon_ranger_female": "Pokémon-Ranger", "pokémon_rangers": "Pokémon-Ranger", "ranger": "Ranger", "restaurant_staff": "Restaurant Angestellte", diff --git a/src/locales/de/voucher.ts b/src/locales/de/voucher.ts new file mode 100644 index 00000000000..7af569e88cb --- /dev/null +++ b/src/locales/de/voucher.ts @@ -0,0 +1,11 @@ +import { SimpleTranslationEntries } from "#app/plugins/i18n"; + +export const voucher: SimpleTranslationEntries = { + "vouchers": "Vouchers", + "eggVoucher": "Egg Voucher", + "eggVoucherPlus": "Egg Voucher Plus", + "eggVoucherPremium": "Egg Voucher Premium", + "eggVoucherGold": "Egg Voucher Gold", + "locked": "Locked", + "defeatTrainer": "Defeat {{trainerName}}" +} as const; \ No newline at end of file diff --git a/src/locales/en/ability-trigger.ts b/src/locales/en/ability-trigger.ts index 88900741218..49505217126 100644 --- a/src/locales/en/ability-trigger.ts +++ b/src/locales/en/ability-trigger.ts @@ -2,4 +2,5 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n"; export const abilityTriggers: SimpleTranslationEntries = { 'blockRecoilDamage' : `{{pokemonName}}'s {{abilityName}}\nprotected it from recoil!`, + 'badDreams': `{{pokemonName}} is tormented!`, } as const; \ No newline at end of file diff --git a/src/locales/en/config.ts b/src/locales/en/config.ts index 984b0fa239f..f25c0b5e278 100644 --- a/src/locales/en/config.ts +++ b/src/locales/en/config.ts @@ -20,6 +20,7 @@ import { tutorial } from "./tutorial"; import { weather } from "./weather"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { berry } from "./berry"; +import { voucher } from "./voucher"; export const enConfig = { ability: ability, @@ -46,4 +47,5 @@ export const enConfig = { weather: weather, battleMessageUiHandler: battleMessageUiHandler, berry: berry, -} \ No newline at end of file + voucher: voucher, +} diff --git a/src/locales/en/trainers.ts b/src/locales/en/trainers.ts index 03c3e22c12c..1eff742e23a 100644 --- a/src/locales/en/trainers.ts +++ b/src/locales/en/trainers.ts @@ -68,8 +68,9 @@ export const trainerClasses: SimpleTranslationEntries = { "officer": "Officer", "parasol_lady": "Parasol Lady", "pilot": "Pilot", - "pokefan": "Poké Fan", - "pokefan_family": "Poké Fan Family", + "pokéfan": "Poké Fan", + "pokéfan_female": "Poké Fan", + "pokéfan_family": "Poké Fan Family", "preschooler": "Preschooler", "preschooler_female": "Preschooler", "preschoolers": "Preschoolers", @@ -77,6 +78,7 @@ export const trainerClasses: SimpleTranslationEntries = { "psychic_female": "Psychic", "psychics": "Psychics", "pokémon_ranger": "Pokémon Ranger", + "pokémon_ranger_female": "Pokémon Ranger", "pokémon_rangers": "Pokémon Ranger", "ranger": "Ranger", "restaurant_staff": "Restaurant Staff", diff --git a/src/locales/en/voucher.ts b/src/locales/en/voucher.ts new file mode 100644 index 00000000000..7af569e88cb --- /dev/null +++ b/src/locales/en/voucher.ts @@ -0,0 +1,11 @@ +import { SimpleTranslationEntries } from "#app/plugins/i18n"; + +export const voucher: SimpleTranslationEntries = { + "vouchers": "Vouchers", + "eggVoucher": "Egg Voucher", + "eggVoucherPlus": "Egg Voucher Plus", + "eggVoucherPremium": "Egg Voucher Premium", + "eggVoucherGold": "Egg Voucher Gold", + "locked": "Locked", + "defeatTrainer": "Defeat {{trainerName}}" +} as const; \ No newline at end of file diff --git a/src/locales/es/ability-trigger.ts b/src/locales/es/ability-trigger.ts index 88900741218..cd6def4c628 100644 --- a/src/locales/es/ability-trigger.ts +++ b/src/locales/es/ability-trigger.ts @@ -2,4 +2,5 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n"; export const abilityTriggers: SimpleTranslationEntries = { 'blockRecoilDamage' : `{{pokemonName}}'s {{abilityName}}\nprotected it from recoil!`, -} as const; \ No newline at end of file + 'badDreams': `{{pokemonName}} Está atormentado!` +} as const; diff --git a/src/locales/es/config.ts b/src/locales/es/config.ts index 92349028899..2d0a8a536b2 100644 --- a/src/locales/es/config.ts +++ b/src/locales/es/config.ts @@ -20,6 +20,7 @@ import { tutorial } from "./tutorial"; import { weather } from "./weather"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { berry } from "./berry"; +import { voucher } from "./voucher"; export const esConfig = { ability: ability, @@ -46,4 +47,5 @@ export const esConfig = { weather: weather, battleMessageUiHandler: battleMessageUiHandler, berry: berry, -} \ No newline at end of file + voucher: voucher, +} diff --git a/src/locales/es/trainers.ts b/src/locales/es/trainers.ts index 916ef8fb838..e0af5aac1c4 100644 --- a/src/locales/es/trainers.ts +++ b/src/locales/es/trainers.ts @@ -68,8 +68,9 @@ export const trainerClasses: SimpleTranslationEntries = { "officer": "Officer", "parasol_lady": "Parasol Lady", "pilot": "Pilot", - "pokefan": "Poké Fan", - "pokefan_family": "Poké Fan Family", + "pokéfan": "Poké Fan", + "pokéfan_female": "Poké Fan", + "pokéfan_family": "Poké Fan Family", "preschooler": "Preschooler", "preschooler_female": "Preschooler", "preschoolers": "Preschoolers", diff --git a/src/locales/es/voucher.ts b/src/locales/es/voucher.ts new file mode 100644 index 00000000000..7af569e88cb --- /dev/null +++ b/src/locales/es/voucher.ts @@ -0,0 +1,11 @@ +import { SimpleTranslationEntries } from "#app/plugins/i18n"; + +export const voucher: SimpleTranslationEntries = { + "vouchers": "Vouchers", + "eggVoucher": "Egg Voucher", + "eggVoucherPlus": "Egg Voucher Plus", + "eggVoucherPremium": "Egg Voucher Premium", + "eggVoucherGold": "Egg Voucher Gold", + "locked": "Locked", + "defeatTrainer": "Defeat {{trainerName}}" +} as const; \ No newline at end of file diff --git a/src/locales/fr/ability-trigger.ts b/src/locales/fr/ability-trigger.ts index f668ee5e8ab..b1bbaa5e353 100644 --- a/src/locales/fr/ability-trigger.ts +++ b/src/locales/fr/ability-trigger.ts @@ -2,4 +2,5 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n"; export const abilityTriggers: SimpleTranslationEntries = { 'blockRecoilDamage' : `{{abilityName}}\nde {{pokemonName}} le protège du contrecoup !`, + 'badDreams': `{{pokemonName}} a le sommeil agité !` } as const; diff --git a/src/locales/fr/config.ts b/src/locales/fr/config.ts index ecec8de6cb0..a6bdfe5cd59 100644 --- a/src/locales/fr/config.ts +++ b/src/locales/fr/config.ts @@ -20,6 +20,7 @@ import { tutorial } from "./tutorial"; import { weather } from "./weather"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { berry } from "./berry"; +import { voucher } from "./voucher"; export const frConfig = { ability: ability, @@ -46,4 +47,5 @@ export const frConfig = { weather: weather, battleMessageUiHandler: battleMessageUiHandler, berry: berry, -} \ No newline at end of file + voucher: voucher, +} diff --git a/src/locales/fr/modifier-type.ts b/src/locales/fr/modifier-type.ts index f8697c04259..4c3725117a2 100644 --- a/src/locales/fr/modifier-type.ts +++ b/src/locales/fr/modifier-type.ts @@ -52,16 +52,16 @@ export const modifierType: ModifierTypeTranslationEntries = { description: "Double les chances de tomber sur un combat double pendant {{battleCount}} combats", }, "TempBattleStatBoosterModifierType": { - description: "Augmente d’1 cran {{tempBattleStatName}} pour toute l’équipe pendant 5 combats", + description: "Augmente d’un cran {{tempBattleStatName}} pour toute l’équipe pendant 5 combats", }, "AttackTypeBoosterModifierType": { description: "Augmente de 20% la puissance des capacités de type {{moveType}} d’un Pokémon", }, "PokemonLevelIncrementModifierType": { - description: "Fait monter un Pokémon d’1 niveau", + description: "Fait monter un Pokémon d’un niveau", }, "AllPokemonLevelIncrementModifierType": { - description: "Fait monter toute l’équipe d’1 niveau", + description: "Fait monter toute l’équipe d’un niveau", }, "PokemonBaseStatBoosterModifierType": { description: "Augmente de 10% {{statName}} de base de son porteur. Plus les IV sont hauts, plus il peut en porter.", @@ -214,13 +214,13 @@ export const modifierType: ModifierTypeTranslationEntries = { "SHINY_CHARM": { name: "Charme Chroma", description: "Augmente énormément les chances de rencontrer un Pokémon sauvage chromatique" }, "ABILITY_CHARM": { name: "Charme Talent", description: "Augmente énormément les chances de rencontrer un Pokémon sauvage avec un Talent Caché" }, - "IV_SCANNER": { name: "Scanner d’IV", description: "Scanne les IV d’un Pokémon sauvage. 2 IV sont révélés par Scanner. Les meilleurs sont montrés en 1er." }, + "IV_SCANNER": { name: "Scanner d’IV", description: "Révèle la qualité de deux IV d’un Pokémon sauvage par scanner possédé. Les meilleurs IV sont révélés en priorité." }, "DNA_SPLICERS": { name: "Pointeau ADN" }, "MINI_BLACK_HOLE": { name: "Mini Trou Noir" }, - "GOLDEN_POKEBALL": { name: "Poké Ball Dorée", description: "Ajoute 1 choix d’objet à la fin de chaque combat" }, + "GOLDEN_POKEBALL": { name: "Poké Ball Dorée", description: "Ajoute un choix d’objet à la fin de chaque combat" }, "ENEMY_DAMAGE_BOOSTER": { name: "Jeton Dégâts", description: "Augmente les dégâts de 5%" }, "ENEMY_DAMAGE_REDUCTION": { name: "Jeton Protection", description: "Diminue les dégâts reçus de 2,5%" }, diff --git a/src/locales/fr/trainers.ts b/src/locales/fr/trainers.ts index 3d22b00f89e..bfc77b5a8cf 100644 --- a/src/locales/fr/trainers.ts +++ b/src/locales/fr/trainers.ts @@ -68,8 +68,9 @@ export const trainerClasses: SimpleTranslationEntries = { "officer": "Policier", "parasol_lady": "Sœur Parasol", "pilot": "Pilote", - "pokefan": "Poké Fan", - "pokefan_family": "Couple de Pokéfans", + "pokéfan": "Poké Fan", + "pokéfan_female": "Poké Fan", + "pokéfan_family": "Couple de Pokéfans", "preschooler": "Petit", "preschooler_female": "Petite", "preschoolers": "Petits", @@ -77,6 +78,7 @@ export const trainerClasses: SimpleTranslationEntries = { "psychic_female": "Kinésiste", "psychics": "Kinésistes", "pokémon_ranger": "Pokémon Ranger", + "pokémon_ranger_female": "Pokémon Ranger", "pokémon_rangers": "Pokémon Rangers", "ranger": "Ranger", "restaurant_staff": "Serveurs", diff --git a/src/locales/fr/voucher.ts b/src/locales/fr/voucher.ts new file mode 100644 index 00000000000..a432cfed53f --- /dev/null +++ b/src/locales/fr/voucher.ts @@ -0,0 +1,11 @@ +import { SimpleTranslationEntries } from "#app/plugins/i18n"; + +export const voucher: SimpleTranslationEntries = { + "vouchers": "Coupons", + "eggVoucher": "Coupon Œuf", + "eggVoucherPlus": "Coupon Œuf +", + "eggVoucherPremium": "Coupon Œuf Premium", + "eggVoucherGold": "Coupon Œuf Or", + "locked": "Verrouillé", + "defeatTrainer": "Vaincre {{trainerName}}" +} as const; \ No newline at end of file diff --git a/src/locales/it/ability-trigger.ts b/src/locales/it/ability-trigger.ts index de41e087236..3f7d09c9221 100644 --- a/src/locales/it/ability-trigger.ts +++ b/src/locales/it/ability-trigger.ts @@ -2,4 +2,5 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n"; export const abilityTriggers: SimpleTranslationEntries = { 'blockRecoilDamage' : `{{abilityName}} di {{pokemonName}}\nl'ha protetto dal contraccolpo!`, + 'badDreams': `{{pokemonName}} è tormentato!`, } as const; \ No newline at end of file diff --git a/src/locales/it/config.ts b/src/locales/it/config.ts index a9c80dc673d..807d136040c 100644 --- a/src/locales/it/config.ts +++ b/src/locales/it/config.ts @@ -20,6 +20,7 @@ import { tutorial } from "./tutorial"; import { weather } from "./weather"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { berry } from "./berry"; +import { voucher } from "./voucher"; export const itConfig = { ability: ability, @@ -46,4 +47,5 @@ export const itConfig = { weather: weather, battleMessageUiHandler: battleMessageUiHandler, berry: berry, -} \ No newline at end of file + voucher: voucher, +} diff --git a/src/locales/it/trainers.ts b/src/locales/it/trainers.ts index 24c40acdac4..6fcd157a0f1 100644 --- a/src/locales/it/trainers.ts +++ b/src/locales/it/trainers.ts @@ -68,8 +68,9 @@ export const trainerClasses: SimpleTranslationEntries = { "officer": "Officer", "parasol_lady": "Parasol Lady", "pilot": "Pilot", - "pokefan": "Poké Fan", - "pokefan_family": "Poké Fan Family", + "pokéfan": "Poké Fan", + "pokéfan_female": "Poké Fan", + "pokéfan_family": "Poké Fan Family", "preschooler": "Preschooler", "preschooler_female": "Preschooler", "preschoolers": "Preschoolers", @@ -77,6 +78,7 @@ export const trainerClasses: SimpleTranslationEntries = { "psychic_female": "Psychic", "psychics": "Psychics", "pokémon_ranger": "Pokémon Ranger", + "pokémon_ranger_female": "Pokémon Ranger", "pokémon_rangers": "Pokémon Ranger", "ranger": "Ranger", "restaurant_staff": "Restaurant Staff", diff --git a/src/locales/it/voucher.ts b/src/locales/it/voucher.ts new file mode 100644 index 00000000000..7af569e88cb --- /dev/null +++ b/src/locales/it/voucher.ts @@ -0,0 +1,11 @@ +import { SimpleTranslationEntries } from "#app/plugins/i18n"; + +export const voucher: SimpleTranslationEntries = { + "vouchers": "Vouchers", + "eggVoucher": "Egg Voucher", + "eggVoucherPlus": "Egg Voucher Plus", + "eggVoucherPremium": "Egg Voucher Premium", + "eggVoucherGold": "Egg Voucher Gold", + "locked": "Locked", + "defeatTrainer": "Defeat {{trainerName}}" +} as const; \ No newline at end of file diff --git a/src/locales/pt_BR/config.ts b/src/locales/pt_BR/config.ts index 457fc4c125a..a9244f5e9db 100644 --- a/src/locales/pt_BR/config.ts +++ b/src/locales/pt_BR/config.ts @@ -19,6 +19,7 @@ import { titles, trainerClasses, trainerNames } from "./trainers"; import { tutorial } from "./tutorial"; import { weather } from "./weather"; import { berry } from "./berry"; +import { voucher } from "./voucher"; export const ptBrConfig = { @@ -45,4 +46,5 @@ export const ptBrConfig = { weather: weather, modifierType: modifierType, berry: berry, + voucher: voucher, } diff --git a/src/locales/pt_BR/trainers.ts b/src/locales/pt_BR/trainers.ts index bc79f98b41b..96dc7d1934e 100644 --- a/src/locales/pt_BR/trainers.ts +++ b/src/locales/pt_BR/trainers.ts @@ -68,8 +68,9 @@ export const trainerClasses: SimpleTranslationEntries = { "officer": "Policial", "parasol_lady": "Moça de Sombrinha", "pilot": "Piloto", - "poké_fan": "Pokefã", - "poké_fan_family": "Família Pokefã", + "pokéfan": "Pokefã", + "pokéfan_female": "Pokéfã", + "pokéfan_family": "Família Pokefã", "preschooler": "Menino do Prezinho", "preschooler_female": "Menina do Prezinho", "preschoolers": "Alunos do Prezinho", @@ -77,6 +78,7 @@ export const trainerClasses: SimpleTranslationEntries = { "psychic_female": "Médium", "psychics": "Médiuns", "pokémon_ranger": "Guarda Pokémon", + "pokémon_ranger_female": "Guarda Pokémon", "pokémon_rangers": "Guardas Pokémon", "ranger": "Guarda", "restaurant_staff": "Equipe do Restaurante", diff --git a/src/locales/pt_BR/voucher.ts b/src/locales/pt_BR/voucher.ts new file mode 100644 index 00000000000..7af569e88cb --- /dev/null +++ b/src/locales/pt_BR/voucher.ts @@ -0,0 +1,11 @@ +import { SimpleTranslationEntries } from "#app/plugins/i18n"; + +export const voucher: SimpleTranslationEntries = { + "vouchers": "Vouchers", + "eggVoucher": "Egg Voucher", + "eggVoucherPlus": "Egg Voucher Plus", + "eggVoucherPremium": "Egg Voucher Premium", + "eggVoucherGold": "Egg Voucher Gold", + "locked": "Locked", + "defeatTrainer": "Defeat {{trainerName}}" +} as const; \ No newline at end of file diff --git a/src/locales/zh_CN/ability-trigger.ts b/src/locales/zh_CN/ability-trigger.ts index 85152b1bccc..d40fad7a10a 100644 --- a/src/locales/zh_CN/ability-trigger.ts +++ b/src/locales/zh_CN/ability-trigger.ts @@ -2,4 +2,5 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n"; export const abilityTriggers: SimpleTranslationEntries = { 'blockRecoilDamage' : `{{pokemonName}} 的 {{abilityName}}\n抵消了反作用力!`, + 'badDreams': `{{pokemonName}} 被折磨着!` } as const; \ No newline at end of file diff --git a/src/locales/zh_CN/config.ts b/src/locales/zh_CN/config.ts index 10fe2bc884e..2a01460b855 100644 --- a/src/locales/zh_CN/config.ts +++ b/src/locales/zh_CN/config.ts @@ -2,7 +2,7 @@ import { ability } from "./ability"; import { abilityTriggers } from "./ability-trigger"; import { battle } from "./battle"; import { commandUiHandler } from "./command-ui-handler"; -// import { egg } from "./egg"; +import { egg } from "./egg"; import { fightUiHandler } from "./fight-ui-handler"; import { growth } from "./growth"; import { menu } from "./menu"; @@ -20,6 +20,7 @@ import { tutorial } from "./tutorial"; import { weather } from "./weather"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { berry } from "./berry"; +import { voucher } from "./voucher"; export const zhCnConfig = { @@ -27,7 +28,7 @@ export const zhCnConfig = { abilityTriggers: abilityTriggers, battle: battle, commandUiHandler: commandUiHandler, - // egg: egg, + egg: egg, fightUiHandler: fightUiHandler, growth: growth, menu: menu, @@ -47,4 +48,5 @@ export const zhCnConfig = { weather: weather, battleMessageUiHandler: battleMessageUiHandler, berry: berry, -} \ No newline at end of file + voucher: voucher, +} diff --git a/src/locales/zh_CN/egg.ts b/src/locales/zh_CN/egg.ts new file mode 100644 index 00000000000..99916ab0778 --- /dev/null +++ b/src/locales/zh_CN/egg.ts @@ -0,0 +1,21 @@ +import { SimpleTranslationEntries } from "#app/plugins/i18n"; + +export const egg: SimpleTranslationEntries = { + "egg": "蛋", + "greatTier": "稀有", + "ultraTier": "史诗", + "masterTier": "传说", + "defaultTier": "普通", + "hatchWavesMessageSoon": "里面传来声音!\n似乎快要孵化了!", + "hatchWavesMessageClose": "有时好像会动一下。\n就快孵化了吧?", + "hatchWavesMessageNotClose": "会孵化出什么呢?\n看来还需要很长时\n间才能孵化。", + "hatchWavesMessageLongTime": "这个蛋需要很长时间\n才能孵化。", + "gachaTypeLegendary": "传说概率上升", + "gachaTypeMove": "稀有概率上升", + "gachaTypeShiny": "闪光概率上升", + "selectMachine": "选择一个机器。", + "notEnoughVouchers": "你没有足够的兑换券!", + "tooManyEggs": "你的蛋太多啦!", + "pull": "次", + "pulls": "次" +} as const; diff --git a/src/locales/zh_CN/trainers.ts b/src/locales/zh_CN/trainers.ts index 2dd10d49ef4..7fa16d87fad 100644 --- a/src/locales/zh_CN/trainers.ts +++ b/src/locales/zh_CN/trainers.ts @@ -68,8 +68,9 @@ export const trainerClasses: SimpleTranslationEntries = { "officer": "警察", "parasol_lady": "阳伞姐姐", "pilot": "飞行员", - "poké_fan": "发烧友俱乐部", - "poké_fan_family": "同好夫妇", + "pokéfan": "发烧友俱乐部", + "pokéfan_female": "发烧友俱乐部", + "pokéfan_family": "同好夫妇", "preschooler": "幼儿园小朋友", "preschooler_female": "幼儿园小朋友", "preschoolers": "幼儿园小朋友组合", @@ -77,6 +78,7 @@ export const trainerClasses: SimpleTranslationEntries = { "psychic_female": "超能力者", "psychics": "超能力者组合", "pokémon_ranger": "宝可梦巡护员", + "pokémon_ranger_female": "宝可梦巡护员", "pokémon_rangers": "宝可梦巡护员组合", "ranger": "巡护员", "restaurant_staff": "服务生组合", diff --git a/src/locales/zh_CN/voucher.ts b/src/locales/zh_CN/voucher.ts new file mode 100644 index 00000000000..7d0f8d2b2dd --- /dev/null +++ b/src/locales/zh_CN/voucher.ts @@ -0,0 +1,11 @@ +import { SimpleTranslationEntries } from "#app/plugins/i18n"; + +export const voucher: SimpleTranslationEntries = { + "vouchers": "兑换券", + "eggVoucher": "初级扭蛋券", + "eggVoucherPlus": "中级扭蛋券", + "eggVoucherPremium": "高级扭蛋券", + "eggVoucherGold": "黄金扭蛋券", + "locked": "锁定", + "defeatTrainer": "你打败了{{trainerName}}" +} as const; \ No newline at end of file diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 0736831a01e..75f54296a20 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1423,7 +1423,7 @@ export class ExpBalanceModifier extends PersistentModifier { } getMaxStackCount(scene: BattleScene): integer { - return 5; + return 4; } } diff --git a/src/phases.ts b/src/phases.ts index f70fe9e857a..b861c82f0e8 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2,7 +2,7 @@ import BattleScene, { AnySound, bypassLogin, startingWave } from "./battle-scene import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon"; import * as Utils from './utils'; import { Moves } from "./data/enums/moves"; -import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr, RechargeAttr, PreMoveMessageAttr, HealStatusEffectAttr, IgnoreOpponentStatChangesAttr, NoEffectAttr, BypassRedirectAttr, FixedDamageAttr, PostVictoryStatChangeAttr, OneHitKOAccuracyAttr, ForceSwitchOutAttr, VariableTargetAttr, SacrificialAttr } from "./data/move"; +import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr, RechargeAttr, PreMoveMessageAttr, HealStatusEffectAttr, IgnoreOpponentStatChangesAttr, NoEffectAttr, BypassRedirectAttr, FixedDamageAttr, PostVictoryStatChangeAttr, OneHitKOAccuracyAttr, ForceSwitchOutAttr, VariableTargetAttr, SacrificialAttr, IncrementMovePriorityAttr } from "./data/move"; import { Mode } from './ui/ui'; import { Command } from "./ui/command-ui-handler"; import { Stat } from "./data/pokemon-stat"; @@ -366,10 +366,14 @@ export class TitlePhase extends Phase { this.scene.pushPhase(new SummonPhase(this.scene, 0, true, true)); if (this.scene.currentBattle.double && availablePartyMembers > 1) this.scene.pushPhase(new SummonPhase(this.scene, 1, true, true)); - if (this.scene.currentBattle.waveIndex > 1 && this.scene.currentBattle.battleType !== BattleType.TRAINER) { - this.scene.pushPhase(new CheckSwitchPhase(this.scene, 0, this.scene.currentBattle.double)); - if (this.scene.currentBattle.double && availablePartyMembers > 1) - this.scene.pushPhase(new CheckSwitchPhase(this.scene, 1, this.scene.currentBattle.double)); + + if (this.scene.currentBattle.battleType !== BattleType.TRAINER && (this.scene.currentBattle.waveIndex > 1 || !this.scene.gameMode.isDaily)) { + const minPartySize = this.scene.currentBattle.double ? 2 : 1; + if (availablePartyMembers > minPartySize) { + this.scene.pushPhase(new CheckSwitchPhase(this.scene, 0, this.scene.currentBattle.double)); + if (this.scene.currentBattle.double) + this.scene.pushPhase(new CheckSwitchPhase(this.scene, 1, this.scene.currentBattle.double)); + } } } @@ -906,7 +910,7 @@ export class EncounterPhase extends BattlePhase { const showDialogueAndSummon = () => { let message: string; this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex); - this.scene.ui.showDialogue(message, trainer.getName(), null, () => { + this.scene.ui.showDialogue(message, trainer.getName(TrainerSlot.NONE,true), null, () => { this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doSummon())); }); }; @@ -955,10 +959,13 @@ export class EncounterPhase extends BattlePhase { this.scene.pushPhase(new ToggleDoublePositionPhase(this.scene, false)); } - if (this.scene.currentBattle.waveIndex > startingWave && this.scene.currentBattle.battleType !== BattleType.TRAINER) { - this.scene.pushPhase(new CheckSwitchPhase(this.scene, 0, this.scene.currentBattle.double)); - if (this.scene.currentBattle.double && availablePartyMembers.length > 1) - this.scene.pushPhase(new CheckSwitchPhase(this.scene, 1, this.scene.currentBattle.double)); + if (this.scene.currentBattle.battleType !== BattleType.TRAINER && (this.scene.currentBattle.waveIndex > 1 || !this.scene.gameMode.isDaily)) { + const minPartySize = this.scene.currentBattle.double ? 2 : 1; + if (availablePartyMembers.length > minPartySize) { + this.scene.pushPhase(new CheckSwitchPhase(this.scene, 0, this.scene.currentBattle.double)); + if (this.scene.currentBattle.double) + this.scene.pushPhase(new CheckSwitchPhase(this.scene, 1, this.scene.currentBattle.double)); + } } } @@ -1809,7 +1816,7 @@ export class CommandPhase extends FieldPhase { const trapped = new Utils.BooleanHolder(false); const batonPass = isSwitch && args[0] as boolean; if (!batonPass) - enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped)); + enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped, playerPokemon)); if (batonPass || (!trapTag && !trapped.value)) { this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch ? { command: Command.POKEMON, cursor: cursor, args: args } @@ -1914,7 +1921,7 @@ export class EnemyCommandPhase extends FieldPhase { const trapTag = enemyPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag; const trapped = new Utils.BooleanHolder(false); - opponents.forEach(playerPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, playerPokemon, trapped)); + opponents.forEach(playerPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, playerPokemon, trapped, enemyPokemon)); if (!trapTag && !trapped.value) { const partyMemberScores = trainer.getPartyMemberMatchupScores(enemyPokemon.trainerSlot, true); @@ -2012,6 +2019,9 @@ export class TurnStartPhase extends FieldPhase { const aPriority = new Utils.IntegerHolder(aMove.priority); const bPriority = new Utils.IntegerHolder(bMove.priority); + applyMoveAttrs(IncrementMovePriorityAttr,this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a),null,aMove,aPriority); + applyMoveAttrs(IncrementMovePriorityAttr,this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b),null,bMove,bPriority); + applyAbAttrs(IncrementMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a), null, aMove, aPriority); applyAbAttrs(IncrementMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b), null, bMove, bPriority); @@ -3045,7 +3055,8 @@ export class PostTurnStatusEffectPhase extends PokemonPhase { break; } if (damage) { - this.scene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage)); + // Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ... + this.scene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage, false, true)); pokemon.updateInfo(); } new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => this.end()); @@ -3840,6 +3851,10 @@ export class SwitchPhase extends BattlePhase { if (this.isModal && !this.scene.getParty().filter(p => !p.isFainted() && !p.isActive(true)).length) return super.end(); + // Check if there is any space still in field + if (this.isModal && this.scene.getPlayerField().filter(p => !p.isFainted() && p.isActive(true)).length >= this.scene.currentBattle.getBattlerCount()) + return super.end(); + // Override field index to 0 in case of double battle where 2/3 remaining party members fainted at once const fieldIndex = this.scene.currentBattle.getBattlerCount() === 1 || this.scene.getParty().filter(p => !p.isFainted()).length > 1 ? this.fieldIndex : 0; @@ -4349,6 +4364,7 @@ export class AttemptCapturePhase extends PokemonPhase { scale: 1 }); + this.scene.currentBattle.lastUsedPokeball = this.pokeballType; this.removePb(); this.end(); } @@ -4407,6 +4423,7 @@ export class AttemptCapturePhase extends PokemonPhase { if (this.scene.getParty().length === 6) { const promptRelease = () => { this.scene.ui.showText(`Your party is full.\nRelease a Pokémon to make room for ${pokemon.name}?`, null, () => { + this.scene.pokemonInfoContainer.makeRoomForConfirmUi(); this.scene.ui.setMode(Mode.CONFIRM, () => { this.scene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, this.fieldIndex, (slotIndex: integer, _option: PartyOption) => { this.scene.ui.setMode(Mode.MESSAGE).then(() => { diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index 790a3c729c8..7bb3535ba4a 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -93,8 +93,9 @@ export function initI18n(): void { i18next.use(LanguageDetector).init({ lng: lang, + nonExplicitSupportedLngs: true, fallbackLng: 'en', - supportedLngs: ['en', 'es', 'fr', 'it', 'de', 'zh_CN','pt_BR'], + supportedLngs: ['en', 'es', 'fr', 'it', 'de', 'zh','pt'], debug: true, interpolation: { escapeValue: false, @@ -153,6 +154,7 @@ declare module 'i18next' { modifierType: ModifierTypeTranslationEntries; battleMessageUiHandler: SimpleTranslationEntries; berry: BerryTranslationEntries; + voucher: SimpleTranslationEntries; }; } } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 2691e79be61..8b09fe8b910 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -867,8 +867,11 @@ export class GameData { const ret: PersistentModifierData[] = []; if (v === null) v = []; - for (let md of v) + for (let md of v) { + if(md?.className === 'ExpBalanceModifier') // Temporarily limit EXP Balance until it gets reworked + md.stackCount = Math.min(md.stackCount, 4); ret.push(new PersistentModifierData(md, player)); + } return ret; } diff --git a/src/system/settings.ts b/src/system/settings.ts index 41a66dace96..2673994db2b 100644 --- a/src/system/settings.ts +++ b/src/system/settings.ts @@ -184,11 +184,17 @@ export function setSetting(scene: BattleScene, setting: Setting, value: integer) scene.ui.revertMode(); (scene.ui.getHandler() as SettingsUiHandler).setOptionCursor(Object.values(Setting).indexOf(Setting.Language), 0, true); }; - const changeLocaleHandler = (locale: string) => { - i18next.changeLanguage(locale); - localStorage.setItem('prLang', locale); - cancelHandler(); - scene.reset(true, false, true); + const changeLocaleHandler = (locale: string): boolean => { + try { + i18next.changeLanguage(locale); + localStorage.setItem('prLang', locale); + cancelHandler(); + scene.reset(true, false, true); + return true; + } catch (error) { + console.error('Error changing locale:', error); + return false; + } }; scene.ui.setOverlayMode(Mode.OPTION_SELECT, { options: [ diff --git a/src/system/voucher.ts b/src/system/voucher.ts index 276e74eeb0d..507c14b5bfe 100644 --- a/src/system/voucher.ts +++ b/src/system/voucher.ts @@ -2,6 +2,7 @@ import BattleScene from "../battle-scene"; import { TrainerType } from "../data/enums/trainer-type"; import { ModifierTier } from "../modifier/modifier-tier"; import { Achv, AchvTier, achvs } from "./achv"; +import i18next from '../plugins/i18n'; export enum VoucherType { REGULAR, @@ -52,13 +53,13 @@ export class Voucher { export function getVoucherTypeName(voucherType: VoucherType): string { switch (voucherType) { case VoucherType.REGULAR: - return 'Egg Voucher'; + return i18next.t("voucher:eggVoucher"); case VoucherType.PLUS: - return 'Egg Voucher Plus'; + return i18next.t("voucher:eggVoucherPlus"); case VoucherType.PREMIUM: - return 'Egg Voucher Premium'; + return i18next.t("voucher:eggVoucherPremium"); case VoucherType.GOLDEN: - return 'Egg Voucher Gold'; + return i18next.t("voucher:eggVoucherGold"); } } @@ -75,9 +76,8 @@ export function getVoucherTypeIcon(voucherType: VoucherType): string { } } - export interface Vouchers { - [key: string]: Voucher + [key: string]: Voucher; } export const vouchers: Vouchers = {}; @@ -87,6 +87,8 @@ const voucherAchvs: Achv[] = [ achvs.CLASSIC_VICTORY ]; { (function() { import('../data/trainer-config').then(tc => { + const trainerConfigs = tc.trainerConfigs; + for (let achv of voucherAchvs) { const voucherType = achv.score >= 150 ? VoucherType.GOLDEN @@ -98,7 +100,6 @@ const voucherAchvs: Achv[] = [ achvs.CLASSIC_VICTORY ]; vouchers[achv.id] = new Voucher(voucherType, achv.description); } - const trainerConfigs = tc.trainerConfigs; const bossTrainerTypes = Object.keys(trainerConfigs) .filter(tt => trainerConfigs[tt].isBoss && trainerConfigs[tt].getDerivedType() !== TrainerType.RIVAL); @@ -107,12 +108,17 @@ const voucherAchvs: Achv[] = [ achvs.CLASSIC_VICTORY ]; ? VoucherType.PLUS : VoucherType.PREMIUM; const key = TrainerType[trainerType]; - vouchers[key] = new Voucher(voucherType, `Defeat ${trainerConfigs[trainerType].name}`); + const trainerName = trainerConfigs[trainerType].name; + vouchers[key] = new Voucher( + voucherType, + i18next.t("voucher:defeatTrainer", { trainerName }) + ); } const voucherKeys = Object.keys(vouchers); - for (let k of voucherKeys) + for (let k of voucherKeys) { vouchers[k].id = k; + } }); })(); -} \ No newline at end of file +} diff --git a/src/test/pokemonSprite.test.ts b/src/test/pokemonSprite.test.ts new file mode 100644 index 00000000000..2ea66878f36 --- /dev/null +++ b/src/test/pokemonSprite.test.ts @@ -0,0 +1,225 @@ +import {beforeAll, describe, expect, it} from "vitest"; +import _masterlist from '../../public/images/pokemon/variant/_masterlist.json'; +import fs from 'fs'; +import path from 'path'; +import {getAppRootDir} from "#app/test/testUtils"; + +const deepCopy = (data) => { + return JSON.parse(JSON.stringify(data)); +} + + +describe("check if every variant's sprite are correctly set", () => { + let masterlist; + let expVariant; + let femaleVariant; + let backVariant; + let rootDir; + + beforeAll(() => { + rootDir = `${getAppRootDir()}${path.sep}public${path.sep}images${path.sep}pokemon${path.sep}variant${path.sep}` + masterlist = deepCopy(_masterlist); + expVariant = masterlist.exp; + femaleVariant = masterlist.female; + backVariant = masterlist.back; + delete masterlist.exp + delete masterlist.female + delete masterlist.back + }); + + it('data should not be undefined', () => { + expect(masterlist).not.toBeUndefined(); + expect(expVariant).not.toBeUndefined(); + expect(femaleVariant).not.toBeUndefined(); + expect(backVariant).not.toBeUndefined(); + }); + + function getMissingMasterlist(mlist, dirpath, excludes = []) { + const errors = []; + const trimmedDirpath = `variant${path.sep}${dirpath.split(rootDir)[1]}`; + if (fs.existsSync(dirpath)) { + const files = fs.readdirSync(dirpath); + for (const filename of files) { + const filePath = `${dirpath}${filename}` + const trimmedFilePath = `${trimmedDirpath}${filename}` + const ext = filename.split('.')[1]; + const name = filename.split('.')[0]; + if (excludes.includes(name)) continue; + if (name.includes('_')) { + const id = name.split('_')[0]; + const variant = name.split('_')[1]; + if (ext !== 'json') { + if (mlist.hasOwnProperty(id)) { + const urlJsonFile = `${dirpath}${id}.json`; + const trimmedUrlJsonFilepath = `${trimmedDirpath}${id}.json`; + const jsonFileExists = fs.existsSync(urlJsonFile); + if (mlist[id].includes(1)) { + const msg = `[${name}] MISSING JSON ${trimmedUrlJsonFilepath}`; + if (!jsonFileExists && !errors.includes(msg)) errors.push(msg); + } + } + if (!mlist.hasOwnProperty(id)) errors.push(`[${id}] missing key ${id} in masterlist for ${trimmedFilePath}`); + else if (mlist[id][parseInt(variant, 10) - 1] !== 2) { + const urlJsonFile = `${dirpath}${name}.json`; + const trimmedUrlJsonFilepath = `${trimmedDirpath}${name}.json`; + const jsonFileExists = fs.existsSync(urlJsonFile); + if (mlist[id].includes(1)) { + const msg = `[${id}] MISSING JSON ${trimmedUrlJsonFilepath}`; + if (!jsonFileExists && !errors.includes(msg)) errors.push(msg); + } + errors.push(`[${id}] [${mlist[id]}] - the value should be 2 for the index ${parseInt(variant, 10) - 1} - ${trimmedFilePath}`); + } + } + } else if (!mlist.hasOwnProperty(name)) errors.push(`named - missing key ${name} in masterlist for ${trimmedFilePath}`);else { + const raw = fs.readFileSync(filePath, {encoding: 'utf8', flag: 'r'}); + const data = JSON.parse(raw); + for (const key of Object.keys(data)) { + if (mlist[name][key] !== 1) errors.push(`[${name}] [${mlist[name]}] - the value should be 1 for the index ${key} - ${trimmedFilePath}`); + } + } + } + } + return errors; + } + + function getMissingFiles(keys, dirPath) { + const errors = []; + for (const key of Object.keys(keys)) { + const row = keys[key]; + for (const [index, elm] of row.entries()) { + let url; + if (elm === 0) continue + else if (elm === 1) { + url = `${key}.json` + let filePath = `${dirPath}${url}`; + const raw = fs.readFileSync(filePath, {encoding: 'utf8', flag: 'r'}); + const data = JSON.parse(raw); + if (!data.hasOwnProperty(index)) { + errors.push(`index: ${index} - ${filePath}`); + } + } else if (elm === 2) { + url = `${key}_${parseInt(index, 10) + 1}.png`; + let filePath = `${dirPath}${url}`; + if (!fs.existsSync(filePath)) { + errors.push(filePath) + } + + url = `${key}_${parseInt(index, 10) + 1}.json`; + filePath = `${dirPath}${url}`; + if (!fs.existsSync(filePath)) { + errors.push(filePath) + } + } + } + } + return errors; + } + + // chech presence of every files listed in masterlist + + it('check root variant files', () => { + const dirPath = rootDir; + const errors = getMissingFiles(masterlist, dirPath); + if (errors.length) console.log('errors', errors); + expect(errors.length).toBe(0); + }); + + it('check female variant files', () => { + const dirPath = `${rootDir}female${path.sep}`; + const errors = getMissingFiles(femaleVariant, dirPath); + if (errors.length) console.log('errors', errors); + expect(errors.length).toBe(0); + }); + + it('check back female variant files', () => { + const dirPath = `${rootDir}back${path.sep}female${path.sep}`; + const errors = getMissingFiles(backVariant.female, dirPath); + if (errors.length) console.log('errors', errors); + expect(errors.length).toBe(0); + }); + + it('check back male back variant files', () => { + const dirPath = `${rootDir}back${path.sep}`; + let backMaleVariant = deepCopy(backVariant); + delete backMaleVariant.female; + const errors = getMissingFiles(backMaleVariant, dirPath); + if (errors.length) console.log('errors', errors); + expect(errors.length).toBe(0); + }); + + it('check exp back variant files', () => { + const dirPath = `${rootDir}exp${path.sep}back${path.sep}`; + const errors = getMissingFiles(expVariant.back, dirPath); + if (errors.length) console.log('errors', errors); + expect(errors.length).toBe(0); + }); + + it('check exp female variant files', () => { + const dirPath = `${rootDir}exp${path.sep}female${path.sep}`; + const errors = getMissingFiles(expVariant.female, dirPath); + if (errors.length) console.log('errors', errors); + expect(errors.length).toBe(0); + }); + + it('check exp male variant files', () => { + const dirPath = `${rootDir}exp${path.sep}`; + let expMaleVariant = deepCopy(expVariant); + delete expMaleVariant.female; + delete expMaleVariant.back; + const errors = getMissingFiles(expMaleVariant, dirPath); + if (errors.length) console.log('errors', errors); + expect(errors.length).toBe(0); + }); + + // check over every file if it's correctly set in the masterlist + + it('look over every file in variant female and check if present in masterlist', () => { + const dirPath = `${rootDir}female${path.sep}`; + const errors = getMissingMasterlist(femaleVariant, dirPath); + if (errors.length) console.log('errors for ', dirPath, errors); + expect(errors.length).toBe(0); + }); + + it('look over every file in variant back female and check if present in masterlist', () => { + const dirPath = `${rootDir}back${path.sep}female${path.sep}`; + const errors = getMissingMasterlist(backVariant.female, dirPath); + if (errors.length) console.log('errors for ', dirPath, errors); + expect(errors.length).toBe(0); + }); + + it('look over every file in variant back male and check if present in masterlist', () => { + const dirPath = `${rootDir}back${path.sep}`; + let backMaleVariant = deepCopy(backVariant); + const errors = getMissingMasterlist(backMaleVariant, dirPath, ['female']); + if (errors.length) console.log('errors for ', dirPath, errors); + expect(errors.length).toBe(0); + }); + + it('look over every file in variant exp back and check if present in masterlist', () => { + const dirPath = `${rootDir}exp${path.sep}back${path.sep}`; + const errors = getMissingMasterlist(expVariant.back, dirPath); + if (errors.length) console.log('errors for ', dirPath, errors); + expect(errors.length).toBe(0); + }); + + it('look over every file in variant exp female and check if present in masterlist', () => { + const dirPath = `${rootDir}exp${path.sep}female${path.sep}`; + const errors = getMissingMasterlist(expVariant.female, dirPath); + if (errors.length) console.log('errors for ', dirPath, errors); + expect(errors.length).toBe(0); + }); + + it('look over every file in variant exp male and check if present in masterlist', () => { + const dirPath = `${rootDir}exp${path.sep}`; + const errors = getMissingMasterlist(expVariant, dirPath, ['back', 'female']); + if (errors.length) console.log('errors for ', dirPath, errors); + expect(errors.length).toBe(0); + }); + + it('look over every file in variant root and check if present in masterlist', () => { + const dirPath = `${rootDir}`; + const errors = getMissingMasterlist(masterlist, dirPath, ['back', 'female', 'exp', 'icons']); + if (errors.length) console.log('errors for ', dirPath, errors); + expect(errors.length).toBe(0); + }); +}); \ No newline at end of file diff --git a/src/test/testUtils.ts b/src/test/testUtils.ts new file mode 100644 index 00000000000..6d01afdb1bc --- /dev/null +++ b/src/test/testUtils.ts @@ -0,0 +1,10 @@ +const fs = require('fs') +const path = require('path') + +export function getAppRootDir () { + let currentDir = __dirname + while(!fs.existsSync(path.join(currentDir, 'package.json'))) { + currentDir = path.join(currentDir, '..') + } + return currentDir +} \ No newline at end of file diff --git a/src/ui/confirm-ui-handler.ts b/src/ui/confirm-ui-handler.ts index bac980db99e..a9b959a9950 100644 --- a/src/ui/confirm-ui-handler.ts +++ b/src/ui/confirm-ui-handler.ts @@ -5,6 +5,9 @@ import i18next from "i18next"; import {Button} from "../enums/buttons"; export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler { + + public static readonly windowWidth: integer = 48; + private switchCheck: boolean; private switchCheckCursor: integer; @@ -13,7 +16,7 @@ export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler { } getWindowWidth(): integer { - return 48; + return ConfirmUiHandler.windowWidth; } show(args: any[]): boolean { diff --git a/src/ui/daily-run-scoreboard.ts b/src/ui/daily-run-scoreboard.ts index 8b258b3a702..139a6d60cd7 100644 --- a/src/ui/daily-run-scoreboard.ts +++ b/src/ui/daily-run-scoreboard.ts @@ -30,13 +30,34 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container { private pageCount: integer; private page: integer; private category: ScoreboardCategory; + + private _isUpdating: boolean; constructor(scene: BattleScene, x: number, y: number) { super(scene, x, y); + this._isUpdating = false; this.setup(); } + /** + * Sets the updating state and updates button states accordingly. + * If value is true (updating), disables the buttons; if false, enables the buttons. + * @param {boolean} value - The new updating state. + */ + set isUpdating(value) { + this._isUpdating = value; + this.setButtonsState(!value); + } + + /** + * Gets the current updating state. + * @returns {boolean} - The current updating state. + */ + get isUpdating() { + return this._isUpdating; + } + setup() { const titleWindow = addWindow(this.scene, 0, 0, 114, 18, false, false, null, null, WindowVariant.THIN); this.add(titleWindow); @@ -140,7 +161,24 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container { }); } + /** + * Updates the scoreboard rankings based on the selected category and page. + * + * If the update process is already ongoing, the method exits early. Otherwise, it begins the update process by clearing + * the current rankings and showing a loading label. If the category changes, the page is reset to 1. + * + * The method fetches the total page count if necessary, followed by fetching the rankings for the specified category + * and page. It updates the UI with the fetched rankings or shows an appropriate message if no rankings are found. + * + * @param {ScoreboardCategory} [category=this.category] - The category to fetch rankings for. Defaults to the current category. + * @param {number} [page=this.page] - The page number to fetch. Defaults to the current page. + */ update(category: ScoreboardCategory = this.category, page: integer = this.page) { + if (this.isUpdating) { + return; + } + + this.isUpdating = true; this.rankingsContainer.removeAll(true); this.loadingLabel.setText(i18next.t('menu:loading')); @@ -150,7 +188,7 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container { this.page = page = 1; Utils.executeIf(category !== this.category || this.pageCount === undefined, - () => Utils.apiFetch(`daily/rankingpagecount?category=${category}`).then(response => response.json()).then(count => this.pageCount = count) + () => Utils.apiFetch(`daily/rankingpagecount?category=${category}`).then(response => response.json()).then(count => this.pageCount = count) ).then(() => { Utils.apiFetch(`daily/rankings?category=${category}&page=${page}`) .then(response => response.json()) @@ -158,16 +196,40 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container { this.page = page; this.category = category; this.titleLabel.setText(`${i18next.t(`menu:${ScoreboardCategory[category].toLowerCase()}Rankings`)}`); - this.prevPageButton.setAlpha(page > 1 ? 1 : 0.5); - this.nextPageButton.setAlpha(page < this.pageCount ? 1 : 0.5); this.pageNumberLabel.setText(page.toString()); if (jsonResponse) { this.loadingLabel.setVisible(false); this.updateRankings(jsonResponse); } else this.loadingLabel.setText(i18next.t('menu:noRankings')); + }).finally(() => { + this.isUpdating = false; }); - }).catch(err => { console.error("Failed to load daily rankings:\n", err) }); + }).catch(err => { + console.error("Failed to load daily rankings:\n", err) + }) + } + + /** + * Sets the state of the navigation buttons. + * @param {boolean} [enabled=true] - Whether the buttons should be enabled or disabled. + */ + setButtonsState(enabled: boolean = true) { + const buttons = [ + { button: this.prevPageButton, alphaValue: enabled ? (this.page > 1 ? 1 : 0.5) : 0.5 }, + { button: this.nextPageButton, alphaValue: enabled ? (this.page < this.pageCount ? 1 : 0.5) : 0.5 }, + { button: this.nextCategoryButton, alphaValue: enabled ? 1 : 0.5 }, + { button: this.prevCategoryButton, alphaValue: enabled ? 1 : 0.5 } + ]; + + buttons.forEach(({ button, alphaValue }) => { + if (enabled) { + button.setInteractive(); + } else { + button.disableInteractive(); + } + button.setAlpha(alphaValue); + }); } } diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 8b497655a17..253ed8b4b72 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -24,6 +24,7 @@ export enum PartyUiMode { SWITCH, FAINT_SWITCH, POST_BATTLE_SWITCH, + REVIVAL_BLESSING, MODIFIER, MOVE_MODIFIER, TM_MODIFIER, @@ -37,6 +38,7 @@ export enum PartyOption { CANCEL = -1, SEND_OUT, PASS_BATON, + REVIVE, APPLY, TEACH, TRANSFER, @@ -103,6 +105,12 @@ export default class PartyUiHandler extends MessageUiHandler { return null; }; + public static FilterFainted = (pokemon: PlayerPokemon) => { + if(!pokemon.isFainted()) + return `${pokemon.name} still has energy\nto battle!`; + return null; + } + private static FilterAllMoves = (_pokemonMove: PokemonMove) => null; public static FilterItemMaxStacks = (pokemon: PlayerPokemon, modifier: PokemonHeldItemModifier) => { @@ -361,7 +369,7 @@ export default class PartyUiHandler extends MessageUiHandler { if (this.cursor < 6) { this.showOptions(); ui.playSelect(); - } else if (this.partyUiMode === PartyUiMode.FAINT_SWITCH) + } else if (this.partyUiMode === PartyUiMode.FAINT_SWITCH || this.partyUiMode === PartyUiMode.REVIVAL_BLESSING) ui.playError(); else return this.processInput(Button.CANCEL); @@ -370,7 +378,7 @@ export default class PartyUiHandler extends MessageUiHandler { if ((this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER || this.partyUiMode === PartyUiMode.SPLICE) && this.transferMode) { this.clearTransfer(); ui.playSelect(); - } else if (this.partyUiMode !== PartyUiMode.FAINT_SWITCH) { + } else if (this.partyUiMode !== PartyUiMode.FAINT_SWITCH && this.partyUiMode !== PartyUiMode.REVIVAL_BLESSING) { if (this.selectCallback) { const selectCallback = this.selectCallback; this.selectCallback = null; @@ -580,6 +588,9 @@ export default class PartyUiHandler extends MessageUiHandler { this.options.push(PartyOption.FORM_CHANGE_ITEM + i); } break; + case PartyUiMode.REVIVAL_BLESSING: + this.options.push(PartyOption.REVIVE); + break; case PartyUiMode.MODIFIER: this.options.push(PartyOption.APPLY); break; diff --git a/src/ui/pokemon-info-container.ts b/src/ui/pokemon-info-container.ts index 572a28f10c8..14d7ec35d1b 100644 --- a/src/ui/pokemon-info-container.ts +++ b/src/ui/pokemon-info-container.ts @@ -9,8 +9,11 @@ import { getNatureName } from "../data/nature"; import * as Utils from "../utils"; import { Type } from "../data/type"; import { getVariantTint } from "#app/data/variant"; +import ConfirmUiHandler from "./confirm-ui-handler"; export default class PokemonInfoContainer extends Phaser.GameObjects.Container { + private readonly infoWindowWidth = 104; + private pokemonGenderLabelText: Phaser.GameObjects.Text; private pokemonGenderText: Phaser.GameObjects.Text; private pokemonAbilityLabelText: Phaser.GameObjects.Text; @@ -37,7 +40,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { } setup(): void { - const infoBg = addWindow(this.scene, 0, 0, 104, 132); + const infoBg = addWindow(this.scene, 0, 0, this.infoWindowWidth, 132); infoBg.setOrigin(0.5, 0.5); this.pokemonMovesContainer = this.scene.add.container(6, 14); @@ -172,7 +175,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { targets: this, duration: Utils.fixedInt(Math.floor(750 / speedMultiplier)), ease: 'Cubic.easeInOut', - x: this.initialX - 104, + x: this.initialX - this.infoWindowWidth, onComplete: () => { resolve(); } @@ -201,6 +204,20 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { }); } + makeRoomForConfirmUi(speedMultiplier: number = 1): Promise { + return new Promise(resolve => { + this.scene.tweens.add({ + targets: this, + duration: Utils.fixedInt(Math.floor(150 / speedMultiplier)), + ease: 'Cubic.easeInOut', + x: this.initialX - this.infoWindowWidth - ConfirmUiHandler.windowWidth, + onComplete: () => { + resolve(); + } + }); + }); + } + hide(speedMultiplier: number = 1): Promise { return new Promise(resolve => { if (!this.shown) diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index d969cf65225..ac962cb7d26 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -42,48 +42,65 @@ export interface Starter { pokerus: boolean; } +interface LanguageSetting { + starterInfoTextSize: string, + instructionTextSize: string, + starterInfoXPos?: integer, + starterInfoYOffset?: integer +} + +const languageSettings: { [key: string]: LanguageSetting } = { + "en":{ + starterInfoTextSize: '56px', + instructionTextSize: '42px', + }, + "de":{ + starterInfoTextSize: '56px', + instructionTextSize: '35px', + }, + "es":{ + starterInfoTextSize: '56px', + instructionTextSize: '35px', + }, + "it":{ + starterInfoTextSize: '56px', + instructionTextSize: '38px', + }, + "fr":{ + starterInfoTextSize: '54px', + instructionTextSize: '42px', + }, + "zh":{ + starterInfoTextSize: '40px', + instructionTextSize: '42px', + starterInfoYOffset: 2 + }, + "pt":{ + starterInfoTextSize: '47px', + instructionTextSize: '38px', + starterInfoXPos: 32, + }, +} + +const starterCandyCosts: { passive: integer, costReduction: [integer, integer] }[] = [ + { passive: 50, costReduction: [30, 75] }, // 1 + { passive: 45, costReduction: [25, 60] }, // 2 + { passive: 40, costReduction: [20, 50] }, // 3 + { passive: 30, costReduction: [15, 40] }, // 4 + { passive: 25, costReduction: [12, 35] }, // 5 + { passive: 20, costReduction: [10, 30] }, // 6 + { passive: 15, costReduction: [8, 20] }, // 7 + { passive: 10, costReduction: [5, 15] }, // 8 + { passive: 10, costReduction: [3, 10] }, // 9 + { passive: 10, costReduction: [3, 10] }, // 10 +] + function getPassiveCandyCount(baseValue: integer): integer { - switch (baseValue) { - case 1: - return 50; - case 2: - return 45; - case 3: - return 40; - case 4: - return 30; - case 5: - return 25; - case 6: - return 20; - case 7: - return 15; - default: - return 10; - } + return starterCandyCosts[baseValue - 1].passive; } function getValueReductionCandyCounts(baseValue: integer): [integer, integer] { - switch (baseValue) { - case 1: - return [ 30, 75]; - case 2: - return [ 25, 60 ]; - case 3: - return [ 20, 50 ]; - case 4: - return [ 15, 40 ]; - case 5: - return [ 12, 35 ]; - case 6: - return [ 10, 30 ]; - case 7: - return [ 8, 20 ]; - case 8: - return [ 5, 15 ]; - default: - return [ 3, 10 ]; - } + return starterCandyCosts[baseValue - 1].costReduction; } const gens = [ @@ -201,6 +218,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler { setup() { const ui = this.getUi(); + const currentLanguage = i18next.language; + const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage.includes(lang)); + const textSettings = languageSettings[langSettingKey]; this.starterSelectContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6); this.starterSelectContainer.setVisible(false); @@ -257,54 +277,38 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonUncaughtText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonUncaughtText); - let starterInfoXPosition = 31; // Only text + // The position should be set per language - const currentLanguage = i18next.language; - switch (currentLanguage) { - case 'pt_BR': - starterInfoXPosition = 32; - break; - default: - starterInfoXPosition = 31; - break - } + let starterInfoXPos = textSettings?.starterInfoXPos || 31; + let starterInfoYOffset = textSettings?.starterInfoYOffset || 0; - let starterInfoTextSize = '56px'; // Labels and text // The font size should be set per language - // currentLanguage is already defined - switch (currentLanguage) { - case 'pt_BR': - starterInfoTextSize = '47px'; - break; - default: - starterInfoTextSize = '56px'; - break - } + let starterInfoTextSize = textSettings?.starterInfoTextSize || 56; - this.pokemonAbilityLabelText = addTextObject(this.scene, 6, 127, i18next.t("starterSelectUiHandler:ability"), TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); + this.pokemonAbilityLabelText = addTextObject(this.scene, 6, 127 + starterInfoYOffset, i18next.t("starterSelectUiHandler:ability"), TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); this.pokemonAbilityLabelText.setOrigin(0, 0); this.pokemonAbilityLabelText.setVisible(false); this.starterSelectContainer.add(this.pokemonAbilityLabelText); - this.pokemonAbilityText = addTextObject(this.scene, starterInfoXPosition, 127, '', TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); + this.pokemonAbilityText = addTextObject(this.scene, starterInfoXPos, 127 + starterInfoYOffset, '', TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); this.pokemonAbilityText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonAbilityText); - this.pokemonPassiveLabelText = addTextObject(this.scene, 6, 136, i18next.t("starterSelectUiHandler:passive"), TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); + this.pokemonPassiveLabelText = addTextObject(this.scene, 6, 136 + starterInfoYOffset, i18next.t("starterSelectUiHandler:passive"), TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); this.pokemonPassiveLabelText.setOrigin(0, 0); this.pokemonPassiveLabelText.setVisible(false); this.starterSelectContainer.add(this.pokemonPassiveLabelText); - this.pokemonPassiveText = addTextObject(this.scene, starterInfoXPosition, 136, '', TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); + this.pokemonPassiveText = addTextObject(this.scene, starterInfoXPos, 136 + starterInfoYOffset, '', TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); this.pokemonPassiveText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonPassiveText); - this.pokemonNatureLabelText = addTextObject(this.scene, 6, 145, i18next.t("starterSelectUiHandler:nature"), TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); + this.pokemonNatureLabelText = addTextObject(this.scene, 6, 145 + starterInfoYOffset, i18next.t("starterSelectUiHandler:nature"), TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); this.pokemonNatureLabelText.setOrigin(0, 0); this.pokemonNatureLabelText.setVisible(false); this.starterSelectContainer.add(this.pokemonNatureLabelText); - this.pokemonNatureText = addBBCodeTextObject(this.scene, starterInfoXPosition, 145, '', TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); + this.pokemonNatureText = addBBCodeTextObject(this.scene, starterInfoXPos, 145 + starterInfoYOffset, '', TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }); this.pokemonNatureText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonNatureText); @@ -610,36 +614,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.starterSelectContainer.add(this.pokemonEggMovesContainer); - - - let instructionTextSize = '42px'; // The font size should be set per language - // currentLanguage is already defined in the previous code block - switch (currentLanguage) { - case 'de': - instructionTextSize = '35px'; - break; - case 'en': - instructionTextSize = '42px'; - break; - case 'es': - instructionTextSize = '35px'; - break; - case 'fr': - instructionTextSize = '42px'; - break; - case 'it': - instructionTextSize = '38px'; - break; - case 'pt_BR': - instructionTextSize = '38px'; - break; - case 'zh_CN': - instructionTextSize = '42px'; - break; - - } - + let instructionTextSize = textSettings.instructionTextSize; this.instructionsText = addTextObject(this.scene, 4, 156, '', TextStyle.PARTY, { fontSize: instructionTextSize }); this.starterSelectContainer.add(this.instructionsText); diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index 943679cae39..0655880d271 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -494,20 +494,16 @@ export default class SummaryUiHandler extends UiHandler { } break; case Button.LEFT: - if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) - break; if (this.cursor) success = this.setCursor(this.cursor - 1); break; case Button.RIGHT: - if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) { - this.setCursor(Page.MOVES); - this.moveSelect = true; - success = true; - break; - } - if (this.cursor < pages.length - 1) + if (this.cursor < pages.length - 1) { success = this.setCursor(this.cursor + 1); + if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE && this.cursor === Page.MOVES) { + this.moveSelect = true; + } + } break; } } @@ -614,13 +610,7 @@ export default class SummaryUiHandler extends UiHandler { onComplete: () => { if (forward){ this.populatePageContainer(this.summaryPageContainer); - if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) { - this.moveCursorObj = null; - this.extraMoveRowContainer.setVisible(true); - this.setCursor(0, true); - this.showMoveEffect(); - } - else if (this.cursor===Page.MOVES) { + if (this.cursor===Page.MOVES) { this.moveCursorObj = null; this.showMoveSelect(); this.showMoveEffect(); diff --git a/src/ui/text.ts b/src/ui/text.ts index d7ecd3b2526..31c76c72956 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -4,6 +4,7 @@ import { ModifierTier } from "../modifier/modifier-tier"; import { EggTier } from "../data/enums/egg-type"; import BattleScene from "../battle-scene"; import { UiTheme } from "../enums/ui-theme"; +import i18next from "i18next"; export enum TextStyle { MESSAGE, @@ -28,12 +29,31 @@ export enum TextStyle { MOVE_INFO_CONTENT }; +interface LanguageSetting { + summaryFontSize?: string, + battleInfoFontSize?: string, + partyFontSize?: string, + tooltipContentFontSize?: string, + moveInfoFontSize?: string, + textScale?: number +} + +const languageSettings: { [key: string]: LanguageSetting } = { + "en":{}, + "de":{}, + "es":{}, + "it":{}, + "fr":{}, + "zh_CN":{}, + "pt_BR":{}, +} + export function addTextObject(scene: Phaser.Scene, x: number, y: number, content: string, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): Phaser.GameObjects.Text { - const [ styleOptions, shadowColor, shadowSize ] = getTextStyleOptions(style, (scene as BattleScene).uiTheme, extraStyleOptions); + const [ styleOptions, shadowColor, shadowXpos, shadowYpos ] = getTextStyleOptions(style, (scene as BattleScene).uiTheme, extraStyleOptions); const ret = scene.add.text(x, y, content, styleOptions); ret.setScale(0.1666666667); - ret.setShadow(shadowSize, shadowSize, shadowColor); + ret.setShadow(shadowXpos, shadowYpos, shadowColor); if (!(styleOptions as Phaser.Types.GameObjects.Text.TextStyle).lineSpacing) ret.setLineSpacing(5); @@ -41,12 +61,12 @@ export function addTextObject(scene: Phaser.Scene, x: number, y: number, content } export function addBBCodeTextObject(scene: Phaser.Scene, x: number, y: number, content: string, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): BBCodeText { - const [ styleOptions, shadowColor, shadowSize ] = getTextStyleOptions(style, (scene as BattleScene).uiTheme, extraStyleOptions); + const [ styleOptions, shadowColor, shadowXpos, shadowYpos ] = getTextStyleOptions(style, (scene as BattleScene).uiTheme, extraStyleOptions); const ret = new BBCodeText(scene, x, y, content, styleOptions as BBCodeText.TextStyle); scene.add.existing(ret); ret.setScale(0.1666666667); - ret.setShadow(shadowSize, shadowSize, shadowColor); + ret.setShadow(shadowXpos, shadowYpos, shadowColor); if (!(styleOptions as BBCodeText.TextStyle).lineSpacing) ret.setLineSpacing(10); @@ -64,8 +84,10 @@ export function addTextInputObject(scene: Phaser.Scene, x: number, y: number, wi } function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): [ Phaser.Types.GameObjects.Text.TextStyle | InputText.IConfig, string, integer ] { + const lang = i18next.language; let shadowColor: string; - let shadowSize = 6; + let shadowXpos = 4; + let shadowYpos = 5; let styleOptions: Phaser.Types.GameObjects.Text.TextStyle = { fontFamily: 'emerald', @@ -90,26 +112,29 @@ function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraStyleOptio case TextStyle.MESSAGE: case TextStyle.SETTINGS_LABEL: case TextStyle.SETTINGS_SELECTED: - styleOptions.fontSize = '96px'; + styleOptions.fontSize = languageSettings[lang]?.summaryFontSize || '96px'; break; case TextStyle.BATTLE_INFO: case TextStyle.MONEY: case TextStyle.TOOLTIP_TITLE: - styleOptions.fontSize = '72px'; - shadowSize = 4.5; + styleOptions.fontSize = languageSettings[lang]?.battleInfoFontSize || '72px'; + shadowXpos = 3.5; + shadowYpos = 3.5; break; case TextStyle.PARTY: case TextStyle.PARTY_RED: + styleOptions.fontSize = languageSettings[lang]?.partyFontSize || '66px'; styleOptions.fontFamily = 'pkmnems'; - styleOptions.fontSize = '66px'; break; case TextStyle.TOOLTIP_CONTENT: - styleOptions.fontSize = '64px'; - shadowSize = 4; + styleOptions.fontSize = languageSettings[lang]?.tooltipContentFontSize || '64px'; + shadowXpos = 3; + shadowYpos = 3; break; case TextStyle.MOVE_INFO_CONTENT: - styleOptions.fontSize = '56px'; - shadowSize = 3; + styleOptions.fontSize = languageSettings[lang]?.moveInfoFontSize || '56px'; + shadowXpos = 3; + shadowYpos = 3; break; } @@ -118,12 +143,12 @@ function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraStyleOptio if (extraStyleOptions) { if (extraStyleOptions.fontSize) { const sizeRatio = parseInt(extraStyleOptions.fontSize.toString().slice(0, -2)) / parseInt(styleOptions.fontSize.toString().slice(0, -2)); - shadowSize *= sizeRatio; + shadowXpos *= sizeRatio; } styleOptions = Object.assign(styleOptions, extraStyleOptions); } - return [ styleOptions, shadowColor, shadowSize ]; + return [ styleOptions, shadowColor, shadowXpos, shadowYpos ]; } export function getBBCodeFrag(content: string, textStyle: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string { diff --git a/src/ui/vouchers-ui-handler.ts b/src/ui/vouchers-ui-handler.ts index e28e211ee53..55f3ac224aa 100644 --- a/src/ui/vouchers-ui-handler.ts +++ b/src/ui/vouchers-ui-handler.ts @@ -5,6 +5,7 @@ import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import { addWindow } from "./ui-theme"; import {Button} from "../enums/buttons"; +import i18next from '../plugins/i18n'; const itemRows = 4; const itemCols = 17; @@ -40,7 +41,7 @@ export default class VouchersUiHandler extends MessageUiHandler { const headerBg = addWindow(this.scene, 0, 0, (this.scene.game.canvas.width / 6) - 2, 24); headerBg.setOrigin(0, 0); - const headerText = addTextObject(this.scene, 0, 0, 'Vouchers', TextStyle.SETTINGS_LABEL); + const headerText = addTextObject(this.scene, 0, 0, i18next.t("voucher:vouchers"), TextStyle.SETTINGS_LABEL); headerText.setOrigin(0, 0); headerText.setPositionRelative(headerBg, 8, 4); @@ -127,7 +128,7 @@ export default class VouchersUiHandler extends MessageUiHandler { this.titleText.setText(getVoucherTypeName(voucher.voucherType)); this.showText(voucher.description); - this.unlockText.setText(unlocked ? new Date(voucherUnlocks[voucher.id]).toLocaleDateString() : 'Locked'); + this.unlockText.setText(unlocked ? new Date(voucherUnlocks[voucher.id]).toLocaleDateString() : i18next.t("voucher:locked")); } processInput(button: Button): boolean { @@ -246,4 +247,4 @@ export default class VouchersUiHandler extends MessageUiHandler { this.cursorObj.destroy(); this.cursorObj = null; } -} \ No newline at end of file +}