diff --git a/public/images/pokemon/icons/7/791-radiant-sun.png b/public/images/pokemon/icons/7/791-radiant-sun.png deleted file mode 100644 index ac53b40b0e6..00000000000 Binary files a/public/images/pokemon/icons/7/791-radiant-sun.png and /dev/null differ diff --git a/public/images/pokemon/icons/7/791s-radiant-sun.png b/public/images/pokemon/icons/7/791s-radiant-sun.png deleted file mode 100644 index d6b33d6618f..00000000000 Binary files a/public/images/pokemon/icons/7/791s-radiant-sun.png and /dev/null differ diff --git a/public/images/pokemon/icons/7/792-full-moon.png b/public/images/pokemon/icons/7/792-full-moon.png deleted file mode 100644 index 6e77f626e94..00000000000 Binary files a/public/images/pokemon/icons/7/792-full-moon.png and /dev/null differ diff --git a/public/images/pokemon/icons/7/792s-full-moon.png b/public/images/pokemon/icons/7/792s-full-moon.png deleted file mode 100644 index e65ad88fd78..00000000000 Binary files a/public/images/pokemon/icons/7/792s-full-moon.png and /dev/null differ diff --git a/public/images/pokemon_icons_7.json b/public/images/pokemon_icons_7.json index d5faa4e8b02..03eeba88a70 100644 --- a/public/images/pokemon_icons_7.json +++ b/public/images/pokemon_icons_7.json @@ -4,8 +4,8 @@ "image": "pokemon_icons_7.png", "format": "RGBA8888", "size": { - "w": 250, - "h": 672 + "w": 502, + "h": 325 }, "scale": 1, "frames": [ @@ -345,132 +345,6 @@ "h": 29 } }, - { - "filename": "795", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 28, - "h": 30 - }, - "frame": { - "x": 0, - "y": 322, - "w": 28, - "h": 30 - } - }, - { - "filename": "795s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 28, - "h": 30 - }, - "frame": { - "x": 0, - "y": 352, - "w": 28, - "h": 30 - } - }, - { - "filename": "800", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 28, - "h": 30 - }, - "frame": { - "x": 0, - "y": 382, - "w": 28, - "h": 30 - } - }, - { - "filename": "800s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 28, - "h": 30 - }, - "frame": { - "x": 0, - "y": 412, - "w": 28, - "h": 30 - } - }, - { - "filename": "805", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 28, - "h": 30 - }, - "frame": { - "x": 0, - "y": 442, - "w": 28, - "h": 30 - } - }, - { - "filename": "805s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 28, - "h": 30 - }, - "frame": { - "x": 0, - "y": 472, - "w": 28, - "h": 30 - } - }, { "filename": "809-gigantamax", "rotated": false, @@ -513,27 +387,6 @@ "h": 28 } }, - { - "filename": "791-radiant-sun", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 2, - "w": 29, - "h": 28 - }, - "frame": { - "x": 142, - "y": 0, - "w": 29, - "h": 28 - } - }, { "filename": "791", "rotated": false, @@ -549,28 +402,7 @@ "h": 28 }, "frame": { - "x": 138, - "y": 28, - "w": 29, - "h": 28 - } - }, - { - "filename": "791s-radiant-sun", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 2, - "w": 29, - "h": 28 - }, - "frame": { - "x": 171, + "x": 142, "y": 0, "w": 29, "h": 28 @@ -591,138 +423,12 @@ "h": 28 }, "frame": { - "x": 167, + "x": 138, "y": 28, "w": 29, "h": 28 } }, - { - "filename": "773-bug", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 0, - "y": 502, - "w": 26, - "h": 30 - } - }, - { - "filename": "773-dark", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 0, - "y": 532, - "w": 26, - "h": 30 - } - }, - { - "filename": "773-dragon", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 0, - "y": 562, - "w": 26, - "h": 30 - } - }, - { - "filename": "773-electric", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 0, - "y": 592, - "w": 26, - "h": 30 - } - }, - { - "filename": "773-fairy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 0, - "y": 622, - "w": 26, - "h": 30 - } - }, - { - "filename": "789", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 6, - "w": 23, - "h": 20 - }, - "frame": { - "x": 0, - "y": 652, - "w": 23, - "h": 20 - } - }, { "filename": "784", "rotated": false, @@ -737,6 +443,27 @@ "w": 29, "h": 27 }, + "frame": { + "x": 171, + "y": 0, + "w": 29, + "h": 27 + } + }, + { + "filename": "784s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 3, + "w": 29, + "h": 27 + }, "frame": { "x": 200, "y": 0, @@ -745,7 +472,7 @@ } }, { - "filename": "2103", + "filename": "730", "rotated": false, "trimmed": true, "sourceSize": { @@ -753,20 +480,83 @@ "h": 30 }, "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 21, - "h": 30 + "x": 6, + "y": 2, + "w": 28, + "h": 27 }, "frame": { "x": 229, "y": 0, - "w": 21, - "h": 30 + "w": 28, + "h": 27 } }, { - "filename": "773-fighting", + "filename": "730s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 6, + "y": 2, + "w": 28, + "h": 27 + }, + "frame": { + "x": 257, + "y": 0, + "w": 28, + "h": 27 + } + }, + { + "filename": "740", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 32, + "h": 23 + }, + "frame": { + "x": 285, + "y": 0, + "w": 32, + "h": 23 + } + }, + { + "filename": "740s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 32, + "h": 23 + }, + "frame": { + "x": 317, + "y": 0, + "w": 32, + "h": 23 + } + }, + { + "filename": "795", "rotated": false, "trimmed": true, "sourceSize": { @@ -776,18 +566,18 @@ "spriteSourceSize": { "x": 7, "y": 0, - "w": 26, + "w": 28, "h": 30 }, "frame": { "x": 34, "y": 85, - "w": 26, + "w": 28, "h": 30 } }, { - "filename": "773-fire", + "filename": "795s", "rotated": false, "trimmed": true, "sourceSize": { @@ -797,18 +587,18 @@ "spriteSourceSize": { "x": 7, "y": 0, - "w": 26, + "w": 28, "h": 30 }, "frame": { "x": 34, "y": 115, - "w": 26, + "w": 28, "h": 30 } }, { - "filename": "773-flying", + "filename": "800", "rotated": false, "trimmed": true, "sourceSize": { @@ -818,18 +608,18 @@ "spriteSourceSize": { "x": 7, "y": 0, - "w": 26, + "w": 28, "h": 30 }, "frame": { "x": 32, "y": 145, - "w": 26, + "w": 28, "h": 30 } }, { - "filename": "773-ghost", + "filename": "800s", "rotated": false, "trimmed": true, "sourceSize": { @@ -839,18 +629,18 @@ "spriteSourceSize": { "x": 7, "y": 0, - "w": 26, + "w": 28, "h": 30 }, "frame": { "x": 32, "y": 175, - "w": 26, + "w": 28, "h": 30 } }, { - "filename": "773-grass", + "filename": "805", "rotated": false, "trimmed": true, "sourceSize": { @@ -858,20 +648,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 7, + "x": 6, "y": 0, - "w": 26, + "w": 28, "h": 30 }, "frame": { "x": 31, "y": 205, - "w": 26, + "w": 28, "h": 30 } }, { - "filename": "773-ground", + "filename": "805s", "rotated": false, "trimmed": true, "sourceSize": { @@ -879,20 +669,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 7, + "x": 6, "y": 0, - "w": 26, + "w": 28, "h": 30 }, "frame": { "x": 31, "y": 235, - "w": 26, + "w": 28, "h": 30 } }, { - "filename": "773-ice", + "filename": "773-bug", "rotated": false, "trimmed": true, "sourceSize": { @@ -913,7 +703,7 @@ } }, { - "filename": "730", + "filename": "773-dark", "rotated": false, "trimmed": true, "sourceSize": { @@ -921,20 +711,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 6, - "y": 2, - "w": 28, - "h": 27 + "x": 7, + "y": 0, + "w": 26, + "h": 30 }, "frame": { "x": 31, "y": 295, - "w": 28, - "h": 27 + "w": 26, + "h": 30 } }, { - "filename": "773-poison", + "filename": "2038", "rotated": false, "trimmed": true, "sourceSize": { @@ -942,20 +732,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 7, + "x": 8, + "y": 4, + "w": 30, + "h": 25 + }, + "frame": { + "x": 349, "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 28, - "y": 322, - "w": 26, - "h": 30 + "w": 30, + "h": 25 } }, { - "filename": "773-psychic", + "filename": "2038s", "rotated": false, "trimmed": true, "sourceSize": { @@ -963,230 +753,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 7, + "x": 8, + "y": 4, + "w": 30, + "h": 25 + }, + "frame": { + "x": 379, "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 28, - "y": 352, - "w": 26, - "h": 30 + "w": 30, + "h": 25 } }, { - "filename": "773-rock", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 28, - "y": 382, - "w": 26, - "h": 30 - } - }, - { - "filename": "773-steel", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 28, - "y": 412, - "w": 26, - "h": 30 - } - }, - { - "filename": "773-water", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 28, - "y": 442, - "w": 26, - "h": 30 - } - }, - { - "filename": "773", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 28, - "y": 472, - "w": 26, - "h": 30 - } - }, - { - "filename": "773s-bug", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 26, - "y": 502, - "w": 26, - "h": 30 - } - }, - { - "filename": "773s-dark", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 26, - "y": 532, - "w": 26, - "h": 30 - } - }, - { - "filename": "773s-dragon", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 26, - "y": 562, - "w": 26, - "h": 30 - } - }, - { - "filename": "773s-electric", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 26, - "y": 592, - "w": 26, - "h": 30 - } - }, - { - "filename": "773s-fairy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 26, - "h": 30 - }, - "frame": { - "x": 26, - "y": 622, - "w": 26, - "h": 30 - } - }, - { - "filename": "789s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 6, - "w": 23, - "h": 20 - }, - "frame": { - "x": 23, - "y": 652, - "w": 23, - "h": 20 - } - }, - { - "filename": "784s", + "filename": "785", "rotated": false, "trimmed": true, "sourceSize": { @@ -1196,18 +776,18 @@ "spriteSourceSize": { "x": 7, "y": 3, - "w": 29, - "h": 27 + "w": 28, + "h": 26 }, "frame": { - "x": 70, - "y": 57, - "w": 29, - "h": 27 + "x": 409, + "y": 0, + "w": 28, + "h": 26 } }, { - "filename": "747", + "filename": "785s", "rotated": false, "trimmed": true, "sourceSize": { @@ -1215,16 +795,226 @@ "h": 30 }, "spriteSourceSize": { - "x": 10, - "y": 7, - "w": 22, - "h": 20 + "x": 7, + "y": 3, + "w": 28, + "h": 26 }, "frame": { - "x": 46, - "y": 652, - "w": 22, - "h": 20 + "x": 437, + "y": 0, + "w": 28, + "h": 26 + } + }, + { + "filename": "792", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 6, + "y": 1, + "w": 28, + "h": 28 + }, + "frame": { + "x": 465, + "y": 0, + "w": 28, + "h": 28 + } + }, + { + "filename": "792s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 6, + "y": 1, + "w": 28, + "h": 28 + }, + "frame": { + "x": 70, + "y": 57, + "w": 28, + "h": 28 + } + }, + { + "filename": "773-dragon", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 62, + "y": 85, + "w": 26, + "h": 30 + } + }, + { + "filename": "773-electric", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 62, + "y": 115, + "w": 26, + "h": 30 + } + }, + { + "filename": "773-fairy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 60, + "y": 145, + "w": 26, + "h": 30 + } + }, + { + "filename": "773-fighting", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 60, + "y": 175, + "w": 26, + "h": 30 + } + }, + { + "filename": "773-fire", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 59, + "y": 205, + "w": 26, + "h": 30 + } + }, + { + "filename": "773-flying", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 59, + "y": 235, + "w": 26, + "h": 30 + } + }, + { + "filename": "773-ghost", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 57, + "y": 265, + "w": 26, + "h": 30 + } + }, + { + "filename": "773-grass", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 57, + "y": 295, + "w": 26, + "h": 30 } }, { @@ -1242,7 +1032,7 @@ "h": 30 }, "frame": { - "x": 60, + "x": 88, "y": 85, "w": 25, "h": 30 @@ -1263,12 +1053,306 @@ "h": 30 }, "frame": { - "x": 60, + "x": 88, "y": 115, "w": 25, "h": 30 } }, + { + "filename": "773-ground", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 86, + "y": 145, + "w": 26, + "h": 30 + } + }, + { + "filename": "773-ice", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 86, + "y": 175, + "w": 26, + "h": 30 + } + }, + { + "filename": "773-poison", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 85, + "y": 205, + "w": 26, + "h": 30 + } + }, + { + "filename": "773-psychic", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 85, + "y": 235, + "w": 26, + "h": 30 + } + }, + { + "filename": "773-rock", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 83, + "y": 265, + "w": 26, + "h": 30 + } + }, + { + "filename": "773-steel", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 83, + "y": 295, + "w": 26, + "h": 30 + } + }, + { + "filename": "772", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 2, + "w": 25, + "h": 28 + }, + "frame": { + "x": 98, + "y": 57, + "w": 25, + "h": 28 + } + }, + { + "filename": "773-water", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 123, + "y": 56, + "w": 26, + "h": 30 + } + }, + { + "filename": "773", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 149, + "y": 56, + "w": 26, + "h": 30 + } + }, + { + "filename": "773s-bug", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 113, + "y": 86, + "w": 26, + "h": 30 + } + }, + { + "filename": "773s-dark", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 139, + "y": 86, + "w": 26, + "h": 30 + } + }, + { + "filename": "773s-dragon", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 113, + "y": 116, + "w": 26, + "h": 30 + } + }, + { + "filename": "773s-electric", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 139, + "y": 116, + "w": 26, + "h": 30 + } + }, + { + "filename": "773s-fairy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 26, + "h": 30 + }, + "frame": { + "x": 112, + "y": 146, + "w": 26, + "h": 30 + } + }, { "filename": "773s-fighting", "rotated": false, @@ -1284,8 +1368,8 @@ "h": 30 }, "frame": { - "x": 58, - "y": 145, + "x": 138, + "y": 146, "w": 26, "h": 30 } @@ -1305,8 +1389,8 @@ "h": 30 }, "frame": { - "x": 58, - "y": 175, + "x": 112, + "y": 176, "w": 26, "h": 30 } @@ -1326,8 +1410,8 @@ "h": 30 }, "frame": { - "x": 57, - "y": 205, + "x": 138, + "y": 176, "w": 26, "h": 30 } @@ -1347,8 +1431,8 @@ "h": 30 }, "frame": { - "x": 57, - "y": 235, + "x": 111, + "y": 206, "w": 26, "h": 30 } @@ -1368,33 +1452,12 @@ "h": 30 }, "frame": { - "x": 57, - "y": 265, + "x": 137, + "y": 206, "w": 26, "h": 30 } }, - { - "filename": "730s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 2, - "w": 28, - "h": 27 - }, - "frame": { - "x": 59, - "y": 295, - "w": 28, - "h": 27 - } - }, { "filename": "773s-ground", "rotated": false, @@ -1410,8 +1473,8 @@ "h": 30 }, "frame": { - "x": 54, - "y": 322, + "x": 111, + "y": 236, "w": 26, "h": 30 } @@ -1431,8 +1494,8 @@ "h": 30 }, "frame": { - "x": 54, - "y": 352, + "x": 137, + "y": 236, "w": 26, "h": 30 } @@ -1452,12 +1515,33 @@ "h": 30 }, "frame": { - "x": 54, - "y": 382, + "x": 109, + "y": 266, "w": 26, "h": 30 } }, + { + "filename": "794", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 1, + "w": 25, + "h": 29 + }, + "frame": { + "x": 109, + "y": 296, + "w": 25, + "h": 29 + } + }, { "filename": "773s-psychic", "rotated": false, @@ -1473,12 +1557,54 @@ "h": 30 }, "frame": { - "x": 54, - "y": 412, + "x": 135, + "y": 266, "w": 26, "h": 30 } }, + { + "filename": "794s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 1, + "w": 25, + "h": 29 + }, + "frame": { + "x": 134, + "y": 296, + "w": 25, + "h": 29 + } + }, + { + "filename": "796", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 1, + "w": 18, + "h": 29 + }, + "frame": { + "x": 159, + "y": 296, + "w": 18, + "h": 29 + } + }, { "filename": "773s-rock", "rotated": false, @@ -1494,12 +1620,54 @@ "h": 30 }, "frame": { - "x": 54, - "y": 442, + "x": 161, + "y": 266, "w": 26, "h": 30 } }, + { + "filename": "796s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 1, + "w": 18, + "h": 29 + }, + "frame": { + "x": 177, + "y": 296, + "w": 18, + "h": 29 + } + }, + { + "filename": "772s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 2, + "w": 25, + "h": 28 + }, + "frame": { + "x": 167, + "y": 28, + "w": 25, + "h": 28 + } + }, { "filename": "773s-steel", "rotated": false, @@ -1515,8 +1683,8 @@ "h": 30 }, "frame": { - "x": 54, - "y": 472, + "x": 192, + "y": 27, "w": 26, "h": 30 } @@ -1536,8 +1704,8 @@ "h": 30 }, "frame": { - "x": 52, - "y": 502, + "x": 218, + "y": 27, "w": 26, "h": 30 } @@ -1557,98 +1725,14 @@ "h": 30 }, "frame": { - "x": 52, - "y": 532, + "x": 244, + "y": 27, "w": 26, "h": 30 } }, { - "filename": "792-full-moon", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 1, - "w": 28, - "h": 28 - }, - "frame": { - "x": 52, - "y": 562, - "w": 28, - "h": 28 - } - }, - { - "filename": "792", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 1, - "w": 28, - "h": 28 - }, - "frame": { - "x": 52, - "y": 590, - "w": 28, - "h": 28 - } - }, - { - "filename": "792s-full-moon", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 1, - "w": 28, - "h": 28 - }, - "frame": { - "x": 52, - "y": 618, - "w": 28, - "h": 28 - } - }, - { - "filename": "785", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 3, - "w": 28, - "h": 26 - }, - "frame": { - "x": 68, - "y": 646, - "w": 28, - "h": 26 - } - }, - { - "filename": "2103s", + "filename": "2103", "rotated": false, "trimmed": true, "sourceSize": { @@ -1662,264 +1746,12 @@ "h": 30 }, "frame": { - "x": 85, - "y": 84, + "x": 270, + "y": 27, "w": 21, "h": 30 } }, - { - "filename": "794", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 1, - "w": 25, - "h": 29 - }, - "frame": { - "x": 85, - "y": 114, - "w": 25, - "h": 29 - } - }, - { - "filename": "783", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 3, - "w": 21, - "h": 27 - }, - "frame": { - "x": 99, - "y": 57, - "w": 21, - "h": 27 - } - }, - { - "filename": "792s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 1, - "w": 28, - "h": 28 - }, - "frame": { - "x": 120, - "y": 56, - "w": 28, - "h": 28 - } - }, - { - "filename": "740", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 32, - "h": 23 - }, - "frame": { - "x": 106, - "y": 84, - "w": 32, - "h": 23 - } - }, - { - "filename": "740s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 32, - "h": 23 - }, - "frame": { - "x": 148, - "y": 56, - "w": 32, - "h": 23 - } - }, - { - "filename": "785s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 3, - "w": 28, - "h": 26 - }, - "frame": { - "x": 110, - "y": 107, - "w": 28, - "h": 26 - } - }, - { - "filename": "724", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 4, - "w": 19, - "h": 25 - }, - "frame": { - "x": 180, - "y": 56, - "w": 19, - "h": 25 - } - }, - { - "filename": "794s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 1, - "w": 25, - "h": 29 - }, - "frame": { - "x": 138, - "y": 84, - "w": 25, - "h": 29 - } - }, - { - "filename": "725", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 7, - "w": 25, - "h": 21 - }, - "frame": { - "x": 138, - "y": 113, - "w": 25, - "h": 21 - } - }, - { - "filename": "746-school", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 28, - "h": 22 - }, - "frame": { - "x": 110, - "y": 133, - "w": 28, - "h": 22 - } - }, - { - "filename": "725s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 7, - "w": 25, - "h": 21 - }, - "frame": { - "x": 138, - "y": 134, - "w": 25, - "h": 21 - } - }, - { - "filename": "734", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 10, - "w": 25, - "h": 16 - }, - "frame": { - "x": 85, - "y": 143, - "w": 25, - "h": 16 - } - }, { "filename": "2026", "rotated": false, @@ -1935,75 +1767,12 @@ "h": 28 }, "frame": { - "x": 84, - "y": 159, + "x": 291, + "y": 23, "w": 26, "h": 28 } }, - { - "filename": "2038", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 30, - "h": 25 - }, - "frame": { - "x": 110, - "y": 155, - "w": 30, - "h": 25 - } - }, - { - "filename": "745-dusk", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 25, - "h": 25 - }, - "frame": { - "x": 140, - "y": 155, - "w": 25, - "h": 25 - } - }, - { - "filename": "746s-school", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 28, - "h": 22 - }, - "frame": { - "x": 84, - "y": 187, - "w": 28, - "h": 22 - } - }, { "filename": "2026s", "rotated": false, @@ -2019,14 +1788,14 @@ "h": 28 }, "frame": { - "x": 83, - "y": 209, + "x": 317, + "y": 23, "w": 26, "h": 28 } }, { - "filename": "772", + "filename": "2075", "rotated": false, "trimmed": true, "sourceSize": { @@ -2034,62 +1803,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 8, - "y": 2, - "w": 25, - "h": 28 - }, - "frame": { - "x": 83, - "y": 237, - "w": 25, - "h": 28 - } - }, - { - "filename": "772s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 2, - "w": 25, - "h": 28 - }, - "frame": { - "x": 83, - "y": 265, - "w": 25, - "h": 28 - } - }, - { - "filename": "2038s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, + "x": 7, "y": 4, - "w": 30, - "h": 25 + "w": 29, + "h": 24 }, "frame": { - "x": 112, - "y": 180, - "w": 30, - "h": 25 + "x": 343, + "y": 25, + "w": 29, + "h": 24 } }, { - "filename": "745s-dusk", + "filename": "2075s", "rotated": false, "trimmed": true, "sourceSize": { @@ -2097,20 +1824,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 8, + "x": 7, "y": 4, - "w": 25, - "h": 25 + "w": 29, + "h": 24 }, "frame": { - "x": 142, - "y": 180, - "w": 25, - "h": 25 + "x": 372, + "y": 25, + "w": 29, + "h": 24 } }, { - "filename": "783s", + "filename": "746-school", "rotated": false, "trimmed": true, "sourceSize": { @@ -2118,16 +1845,37 @@ "h": 30 }, "spriteSourceSize": { - "x": 9, - "y": 3, - "w": 21, - "h": 27 + "x": 7, + "y": 5, + "w": 28, + "h": 22 }, "frame": { - "x": 87, - "y": 293, - "w": 21, - "h": 27 + "x": 401, + "y": 26, + "w": 28, + "h": 22 + } + }, + { + "filename": "746s-school", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 5, + "w": 28, + "h": 22 + }, + "frame": { + "x": 429, + "y": 26, + "w": 28, + "h": 22 } }, { @@ -2145,54 +1893,12 @@ "h": 24 }, "frame": { - "x": 163, - "y": 79, + "x": 175, + "y": 56, "w": 17, "h": 24 } }, - { - "filename": "796", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 1, - "w": 18, - "h": 29 - }, - "frame": { - "x": 163, - "y": 103, - "w": 18, - "h": 29 - } - }, - { - "filename": "745", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 6, - "w": 25, - "h": 23 - }, - "frame": { - "x": 163, - "y": 132, - "w": 25, - "h": 23 - } - }, { "filename": "748", "rotated": false, @@ -2208,8 +1914,8 @@ "h": 25 }, "frame": { - "x": 165, - "y": 155, + "x": 192, + "y": 57, "w": 26, "h": 25 } @@ -2229,264 +1935,12 @@ "h": 25 }, "frame": { - "x": 167, - "y": 180, + "x": 218, + "y": 57, "w": 26, "h": 25 } }, - { - "filename": "732", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 6, - "w": 19, - "h": 22 - }, - "frame": { - "x": 180, - "y": 81, - "w": 19, - "h": 22 - } - }, - { - "filename": "796s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 1, - "w": 18, - "h": 29 - }, - "frame": { - "x": 181, - "y": 103, - "w": 18, - "h": 29 - } - }, - { - "filename": "741-sensu", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 5, - "w": 17, - "h": 23 - }, - "frame": { - "x": 188, - "y": 132, - "w": 17, - "h": 23 - } - }, - { - "filename": "724s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 4, - "w": 19, - "h": 25 - }, - "frame": { - "x": 191, - "y": 155, - "w": 19, - "h": 25 - } - }, - { - "filename": "766", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 4, - "w": 24, - "h": 26 - }, - "frame": { - "x": 193, - "y": 180, - "w": 24, - "h": 26 - } - }, - { - "filename": "801-original", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 2, - "w": 20, - "h": 28 - }, - "frame": { - "x": 80, - "y": 322, - "w": 20, - "h": 28 - } - }, - { - "filename": "801", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 2, - "w": 20, - "h": 28 - }, - "frame": { - "x": 80, - "y": 350, - "w": 20, - "h": 28 - } - }, - { - "filename": "801s-original", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 2, - "w": 20, - "h": 28 - }, - "frame": { - "x": 80, - "y": 378, - "w": 20, - "h": 28 - } - }, - { - "filename": "801s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 2, - "w": 20, - "h": 28 - }, - "frame": { - "x": 80, - "y": 406, - "w": 20, - "h": 28 - } - }, - { - "filename": "766s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 4, - "w": 24, - "h": 26 - }, - "frame": { - "x": 80, - "y": 434, - "w": 24, - "h": 26 - } - }, - { - "filename": "776", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 23, - "h": 26 - }, - "frame": { - "x": 80, - "y": 460, - "w": 23, - "h": 26 - } - }, - { - "filename": "726", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 23, - "h": 24 - }, - "frame": { - "x": 80, - "y": 486, - "w": 23, - "h": 24 - } - }, { "filename": "2089", "rotated": false, @@ -2502,12 +1956,33 @@ "h": 25 }, "frame": { - "x": 78, - "y": 510, + "x": 244, + "y": 57, "w": 26, "h": 25 } }, + { + "filename": "745-dusk", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 25, + "h": 25 + }, + "frame": { + "x": 270, + "y": 57, + "w": 25, + "h": 25 + } + }, { "filename": "2089s", "rotated": false, @@ -2523,14 +1998,14 @@ "h": 25 }, "frame": { - "x": 78, - "y": 535, + "x": 295, + "y": 51, "w": 26, "h": 25 } }, { - "filename": "776s", + "filename": "745s-dusk", "rotated": false, "trimmed": true, "sourceSize": { @@ -2540,123 +2015,123 @@ "spriteSourceSize": { "x": 8, "y": 4, - "w": 23, - "h": 26 + "w": 25, + "h": 25 }, "frame": { - "x": 80, - "y": 560, - "w": 23, - "h": 26 - } - }, - { - "filename": "787", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 2, - "w": 22, - "h": 26 - }, - "frame": { - "x": 80, - "y": 586, - "w": 22, - "h": 26 - } - }, - { - "filename": "787s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 2, - "w": 22, - "h": 26 - }, - "frame": { - "x": 80, - "y": 612, - "w": 22, - "h": 26 - } - }, - { - "filename": "2075", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 29, - "h": 24 - }, - "frame": { - "x": 200, - "y": 27, - "w": 29, - "h": 24 - } - }, - { - "filename": "745-midnight", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 4, - "w": 21, - "h": 24 - }, - "frame": { - "x": 229, - "y": 30, - "w": 21, - "h": 24 - } - }, - { - "filename": "2075s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 29, - "h": 24 - }, - "frame": { - "x": 199, + "x": 321, "y": 51, - "w": 29, + "w": 25, + "h": 25 + } + }, + { + "filename": "766", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 4, + "w": 24, + "h": 26 + }, + "frame": { + "x": 346, + "y": 49, + "w": 24, + "h": 26 + } + }, + { + "filename": "766s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 4, + "w": 24, + "h": 26 + }, + "frame": { + "x": 370, + "y": 49, + "w": 24, + "h": 26 + } + }, + { + "filename": "765", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 28, + "h": 21 + }, + "frame": { + "x": 457, + "y": 28, + "w": 28, + "h": 21 + } + }, + { + "filename": "760s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 4, + "w": 17, + "h": 24 + }, + "frame": { + "x": 485, + "y": 28, + "w": 17, "h": 24 } }, { - "filename": "788", + "filename": "2103s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 0, + "w": 21, + "h": 30 + }, + "frame": { + "x": 165, + "y": 86, + "w": 21, + "h": 30 + } + }, + { + "filename": "783", "rotated": false, "trimmed": true, "sourceSize": { @@ -2665,15 +2140,15 @@ }, "spriteSourceSize": { "x": 9, - "y": 2, - "w": 22, - "h": 26 + "y": 3, + "w": 21, + "h": 27 }, "frame": { - "x": 228, - "y": 54, - "w": 22, - "h": 26 + "x": 165, + "y": 116, + "w": 21, + "h": 27 } }, { @@ -2691,8 +2166,8 @@ "h": 24 }, "frame": { - "x": 199, - "y": 75, + "x": 186, + "y": 82, "w": 25, "h": 24 } @@ -2712,12 +2187,33 @@ "h": 24 }, "frame": { - "x": 199, - "y": 99, + "x": 211, + "y": 82, "w": 25, "h": 24 } }, + { + "filename": "765s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 28, + "h": 21 + }, + "frame": { + "x": 186, + "y": 106, + "w": 28, + "h": 21 + } + }, { "filename": "2028", "rotated": false, @@ -2733,12 +2229,432 @@ "h": 24 }, "frame": { - "x": 224, - "y": 80, + "x": 236, + "y": 82, "w": 25, "h": 24 } }, + { + "filename": "2028s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 25, + "h": 24 + }, + "frame": { + "x": 261, + "y": 82, + "w": 25, + "h": 24 + } + }, + { + "filename": "725", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 7, + "w": 25, + "h": 21 + }, + "frame": { + "x": 214, + "y": 106, + "w": 25, + "h": 21 + } + }, + { + "filename": "725s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 7, + "w": 25, + "h": 21 + }, + "frame": { + "x": 239, + "y": 106, + "w": 25, + "h": 21 + } + }, + { + "filename": "726", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 23, + "h": 24 + }, + "frame": { + "x": 264, + "y": 106, + "w": 23, + "h": 24 + } + }, + { + "filename": "780", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 27, + "h": 19 + }, + "frame": { + "x": 186, + "y": 127, + "w": 27, + "h": 19 + } + }, + { + "filename": "780s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 27, + "h": 19 + }, + "frame": { + "x": 213, + "y": 127, + "w": 27, + "h": 19 + } + }, + { + "filename": "758", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 6, + "w": 24, + "h": 22 + }, + "frame": { + "x": 240, + "y": 127, + "w": 24, + "h": 22 + } + }, + { + "filename": "726s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 23, + "h": 24 + }, + "frame": { + "x": 264, + "y": 130, + "w": 23, + "h": 24 + } + }, + { + "filename": "727", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 4, + "w": 23, + "h": 24 + }, + "frame": { + "x": 286, + "y": 82, + "w": 23, + "h": 24 + } + }, + { + "filename": "776", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 23, + "h": 26 + }, + "frame": { + "x": 287, + "y": 106, + "w": 23, + "h": 26 + } + }, + { + "filename": "727s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 4, + "w": 23, + "h": 24 + }, + "frame": { + "x": 287, + "y": 132, + "w": 23, + "h": 24 + } + }, + { + "filename": "776s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 23, + "h": 26 + }, + "frame": { + "x": 309, + "y": 76, + "w": 23, + "h": 26 + } + }, + { + "filename": "783s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 3, + "w": 21, + "h": 27 + }, + "frame": { + "x": 310, + "y": 102, + "w": 21, + "h": 27 + } + }, + { + "filename": "787", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 2, + "w": 22, + "h": 26 + }, + "frame": { + "x": 310, + "y": 129, + "w": 22, + "h": 26 + } + }, + { + "filename": "787s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 2, + "w": 22, + "h": 26 + }, + "frame": { + "x": 332, + "y": 76, + "w": 22, + "h": 26 + } + }, + { + "filename": "788", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 2, + "w": 22, + "h": 26 + }, + "frame": { + "x": 331, + "y": 102, + "w": 22, + "h": 26 + } + }, + { + "filename": "745", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 6, + "w": 25, + "h": 23 + }, + "frame": { + "x": 354, + "y": 75, + "w": 25, + "h": 23 + } + }, + { + "filename": "788s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 2, + "w": 22, + "h": 26 + }, + "frame": { + "x": 332, + "y": 128, + "w": 22, + "h": 26 + } + }, + { + "filename": "724", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 4, + "w": 19, + "h": 25 + }, + "frame": { + "x": 379, + "y": 75, + "w": 19, + "h": 25 + } + }, + { + "filename": "724s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 4, + "w": 19, + "h": 25 + }, + "frame": { + "x": 394, + "y": 49, + "w": 19, + "h": 25 + } + }, { "filename": "745s", "rotated": false, @@ -2754,8 +2670,8 @@ "h": 23 }, "frame": { - "x": 224, - "y": 104, + "x": 413, + "y": 48, "w": 25, "h": 23 } @@ -2775,327 +2691,12 @@ "h": 25 }, "frame": { - "x": 205, - "y": 123, + "x": 438, + "y": 48, "w": 19, "h": 25 } }, - { - "filename": "2020", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 6, - "w": 25, - "h": 22 - }, - "frame": { - "x": 224, - "y": 127, - "w": 25, - "h": 22 - } - }, - { - "filename": "765", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 28, - "h": 21 - }, - "frame": { - "x": 112, - "y": 205, - "w": 28, - "h": 21 - } - }, - { - "filename": "765s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 28, - "h": 21 - }, - "frame": { - "x": 140, - "y": 205, - "w": 28, - "h": 21 - } - }, - { - "filename": "2020s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 6, - "w": 25, - "h": 22 - }, - "frame": { - "x": 168, - "y": 205, - "w": 25, - "h": 22 - } - }, - { - "filename": "758", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 6, - "w": 24, - "h": 22 - }, - "frame": { - "x": 193, - "y": 206, - "w": 24, - "h": 22 - } - }, - { - "filename": "780", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 27, - "h": 19 - }, - "frame": { - "x": 109, - "y": 226, - "w": 27, - "h": 19 - } - }, - { - "filename": "2028s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 25, - "h": 24 - }, - "frame": { - "x": 108, - "y": 245, - "w": 25, - "h": 24 - } - }, - { - "filename": "788s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 2, - "w": 22, - "h": 26 - }, - "frame": { - "x": 108, - "y": 269, - "w": 22, - "h": 26 - } - }, - { - "filename": "803", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 3, - "w": 23, - "h": 25 - }, - "frame": { - "x": 108, - "y": 295, - "w": 23, - "h": 25 - } - }, - { - "filename": "780s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 27, - "h": 19 - }, - "frame": { - "x": 136, - "y": 226, - "w": 27, - "h": 19 - } - }, - { - "filename": "726s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 23, - "h": 24 - }, - "frame": { - "x": 133, - "y": 245, - "w": 23, - "h": 24 - } - }, - { - "filename": "803s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 3, - "w": 23, - "h": 25 - }, - "frame": { - "x": 130, - "y": 269, - "w": 23, - "h": 25 - } - }, - { - "filename": "806", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 3, - "w": 22, - "h": 25 - }, - "frame": { - "x": 131, - "y": 294, - "w": 22, - "h": 25 - } - }, - { - "filename": "727", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 4, - "w": 23, - "h": 24 - }, - "frame": { - "x": 100, - "y": 320, - "w": 23, - "h": 24 - } - }, - { - "filename": "727s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 4, - "w": 23, - "h": 24 - }, - "frame": { - "x": 100, - "y": 344, - "w": 23, - "h": 24 - } - }, { "filename": "729", "rotated": false, @@ -3111,14 +2712,14 @@ "h": 24 }, "frame": { - "x": 100, - "y": 368, + "x": 457, + "y": 49, "w": 23, "h": 24 } }, { - "filename": "729s", + "filename": "806", "rotated": false, "trimmed": true, "sourceSize": { @@ -3126,20 +2727,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 11, - "y": 5, - "w": 23, - "h": 24 + "x": 9, + "y": 3, + "w": 22, + "h": 25 }, "frame": { - "x": 100, - "y": 392, - "w": 23, - "h": 24 + "x": 480, + "y": 52, + "w": 22, + "h": 25 } }, { - "filename": "752", + "filename": "801-original", "rotated": false, "trimmed": true, "sourceSize": { @@ -3148,15 +2749,57 @@ }, "spriteSourceSize": { "x": 10, - "y": 8, - "w": 23, - "h": 18 + "y": 2, + "w": 20, + "h": 28 }, "frame": { - "x": 100, - "y": 416, + "x": 398, + "y": 74, + "w": 20, + "h": 28 + } + }, + { + "filename": "801", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 2, + "w": 20, + "h": 28 + }, + "frame": { + "x": 418, + "y": 71, + "w": 20, + "h": 28 + } + }, + { + "filename": "803", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 3, "w": 23, - "h": 18 + "h": 25 + }, + "frame": { + "x": 438, + "y": 73, + "w": 23, + "h": 25 } }, { @@ -3174,138 +2817,12 @@ "h": 25 }, "frame": { - "x": 104, - "y": 434, + "x": 461, + "y": 73, "w": 19, "h": 25 } }, - { - "filename": "758s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 6, - "w": 24, - "h": 22 - }, - "frame": { - "x": 163, - "y": 227, - "w": 24, - "h": 22 - } - }, - { - "filename": "768", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 24, - "h": 23 - }, - "frame": { - "x": 187, - "y": 228, - "w": 24, - "h": 23 - } - }, - { - "filename": "768s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 24, - "h": 23 - }, - "frame": { - "x": 156, - "y": 249, - "w": 24, - "h": 23 - } - }, - { - "filename": "770", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 4, - "w": 23, - "h": 24 - }, - "frame": { - "x": 153, - "y": 272, - "w": 23, - "h": 24 - } - }, - { - "filename": "770s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 4, - "w": 23, - "h": 24 - }, - "frame": { - "x": 153, - "y": 296, - "w": 23, - "h": 24 - } - }, - { - "filename": "2053", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 24, - "h": 22 - }, - "frame": { - "x": 180, - "y": 251, - "w": 24, - "h": 22 - } - }, { "filename": "806s", "rotated": false, @@ -3321,14 +2838,14 @@ "h": 25 }, "frame": { - "x": 176, - "y": 273, + "x": 480, + "y": 77, "w": 22, "h": 25 } }, { - "filename": "756", + "filename": "734", "rotated": false, "trimmed": true, "sourceSize": { @@ -3337,19 +2854,124 @@ }, "spriteSourceSize": { "x": 9, + "y": 10, + "w": 25, + "h": 16 + }, + "frame": { + "x": 354, + "y": 98, + "w": 25, + "h": 16 + } + }, + { + "filename": "767", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 11, + "w": 24, + "h": 14 + }, + "frame": { + "x": 353, + "y": 114, + "w": 24, + "h": 14 + } + }, + { + "filename": "803s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 3, + "w": 23, + "h": 25 + }, + "frame": { + "x": 354, + "y": 128, + "w": 23, + "h": 25 + } + }, + { + "filename": "732", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 6, + "w": 19, + "h": 22 + }, + "frame": { + "x": 379, + "y": 100, + "w": 19, + "h": 22 + } + }, + { + "filename": "801s-original", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 2, + "w": 20, + "h": 28 + }, + "frame": { + "x": 377, + "y": 122, + "w": 20, + "h": 28 + } + }, + { + "filename": "729s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, "y": 5, - "w": 22, + "w": 23, "h": 24 }, "frame": { - "x": 176, - "y": 298, - "w": 22, + "x": 398, + "y": 102, + "w": 23, "h": 24 } }, { - "filename": "786", + "filename": "768", "rotated": false, "trimmed": true, "sourceSize": { @@ -3357,20 +2979,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 11, - "y": 3, - "w": 20, - "h": 25 + "x": 8, + "y": 5, + "w": 24, + "h": 23 }, "frame": { - "x": 123, - "y": 320, - "w": 20, - "h": 25 + "x": 397, + "y": 126, + "w": 24, + "h": 23 } }, { - "filename": "786s", + "filename": "801s", "rotated": false, "trimmed": true, "sourceSize": { @@ -3378,16 +3000,37 @@ "h": 30 }, "spriteSourceSize": { - "x": 11, - "y": 3, + "x": 10, + "y": 2, "w": 20, - "h": 25 + "h": 28 }, "frame": { - "x": 123, - "y": 345, + "x": 421, + "y": 99, "w": 20, - "h": 25 + "h": 28 + } + }, + { + "filename": "768s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 24, + "h": 23 + }, + "frame": { + "x": 441, + "y": 98, + "w": 24, + "h": 23 } }, { @@ -3405,14 +3048,14 @@ "h": 24 }, "frame": { - "x": 123, - "y": 370, + "x": 421, + "y": 127, "w": 20, "h": 24 } }, { - "filename": "735s", + "filename": "770", "rotated": false, "trimmed": true, "sourceSize": { @@ -3420,20 +3063,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 11, + "x": 9, "y": 4, - "w": 20, + "w": 23, "h": 24 }, "frame": { - "x": 123, - "y": 394, - "w": 20, + "x": 441, + "y": 121, + "w": 23, "h": 24 } }, { - "filename": "745s-midnight", + "filename": "2050", "rotated": false, "trimmed": true, "sourceSize": { @@ -3441,15 +3084,120 @@ "h": 30 }, "spriteSourceSize": { - "x": 10, - "y": 4, - "w": 21, + "x": 13, + "y": 10, + "w": 15, + "h": 18 + }, + "frame": { + "x": 465, + "y": 98, + "w": 15, + "h": 18 + } + }, + { + "filename": "756", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 5, + "w": 22, "h": 24 }, "frame": { - "x": 123, - "y": 418, - "w": 21, + "x": 480, + "y": 102, + "w": 22, + "h": 24 + } + }, + { + "filename": "2050s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 10, + "w": 15, + "h": 18 + }, + "frame": { + "x": 465, + "y": 116, + "w": 15, + "h": 18 + } + }, + { + "filename": "756s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 5, + "w": 22, + "h": 24 + }, + "frame": { + "x": 480, + "y": 126, + "w": 22, + "h": 24 + } + }, + { + "filename": "761", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 8, + "w": 16, + "h": 19 + }, + "frame": { + "x": 464, + "y": 134, + "w": 16, + "h": 19 + } + }, + { + "filename": "802-zenith", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 4, + "w": 22, + "h": 24 + }, + "frame": { + "x": 480, + "y": 150, + "w": 22, "h": 24 } }, @@ -3468,14 +3216,14 @@ "h": 21 }, "frame": { - "x": 123, - "y": 442, + "x": 441, + "y": 145, "w": 23, "h": 21 } }, { - "filename": "2051", + "filename": "761s", "rotated": false, "trimmed": true, "sourceSize": { @@ -3483,20 +3231,41 @@ "h": 30 }, "spriteSourceSize": { - "x": 9, + "x": 13, + "y": 8, + "w": 16, + "h": 19 + }, + "frame": { + "x": 464, + "y": 153, + "w": 16, + "h": 19 + } + }, + { + "filename": "745-midnight", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, "y": 4, - "w": 23, + "w": 21, "h": 24 }, "frame": { - "x": 143, - "y": 320, - "w": 23, + "x": 165, + "y": 143, + "w": 21, "h": 24 } }, { - "filename": "2051s", + "filename": "2020", "rotated": false, "trimmed": true, "sourceSize": { @@ -3504,20 +3273,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 9, - "y": 4, - "w": 23, - "h": 24 + "x": 8, + "y": 6, + "w": 25, + "h": 22 }, "frame": { - "x": 143, - "y": 344, - "w": 23, - "h": 24 + "x": 186, + "y": 146, + "w": 25, + "h": 22 } }, { - "filename": "756s", + "filename": "2020s", "rotated": false, "trimmed": true, "sourceSize": { @@ -3525,20 +3294,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 9, - "y": 5, - "w": 22, - "h": 24 + "x": 8, + "y": 6, + "w": 25, + "h": 22 }, "frame": { - "x": 143, - "y": 368, - "w": 22, - "h": 24 + "x": 211, + "y": 146, + "w": 25, + "h": 22 } }, { - "filename": "802-zenith", + "filename": "802", "rotated": false, "trimmed": true, "sourceSize": { @@ -3552,54 +3321,12 @@ "h": 24 }, "frame": { - "x": 143, - "y": 392, + "x": 164, + "y": 167, "w": 22, "h": 24 } }, - { - "filename": "763", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 4, - "w": 21, - "h": 24 - }, - "frame": { - "x": 144, - "y": 416, - "w": 21, - "h": 24 - } - }, - { - "filename": "749", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 5, - "w": 21, - "h": 23 - }, - "frame": { - "x": 146, - "y": 440, - "w": 21, - "h": 23 - } - }, { "filename": "2105", "rotated": false, @@ -3615,8 +3342,8 @@ "h": 23 }, "frame": { - "x": 166, - "y": 322, + "x": 186, + "y": 168, "w": 24, "h": 23 } @@ -3636,14 +3363,14 @@ "h": 23 }, "frame": { - "x": 166, - "y": 345, + "x": 210, + "y": 168, "w": 24, "h": 23 } }, { - "filename": "802", + "filename": "734s", "rotated": false, "trimmed": true, "sourceSize": { @@ -3651,18 +3378,186 @@ "h": 30 }, "spriteSourceSize": { - "x": 13, + "x": 9, + "y": 10, + "w": 25, + "h": 16 + }, + "frame": { + "x": 164, + "y": 191, + "w": 25, + "h": 16 + } + }, + { + "filename": "770s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, "y": 4, - "w": 22, + "w": 23, "h": 24 }, "frame": { - "x": 165, - "y": 368, - "w": 22, + "x": 163, + "y": 207, + "w": 23, "h": 24 } }, + { + "filename": "2051", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 4, + "w": 23, + "h": 24 + }, + "frame": { + "x": 163, + "y": 231, + "w": 23, + "h": 24 + } + }, + { + "filename": "758s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 6, + "w": 24, + "h": 22 + }, + "frame": { + "x": 189, + "y": 191, + "w": 24, + "h": 22 + } + }, + { + "filename": "2051s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, + "y": 4, + "w": 23, + "h": 24 + }, + "frame": { + "x": 186, + "y": 213, + "w": 23, + "h": 24 + } + }, + { + "filename": "745s-midnight", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 4, + "w": 21, + "h": 24 + }, + "frame": { + "x": 213, + "y": 191, + "w": 21, + "h": 24 + } + }, + { + "filename": "2053", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 24, + "h": 22 + }, + "frame": { + "x": 209, + "y": 215, + "w": 24, + "h": 22 + } + }, + { + "filename": "2053s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 24, + "h": 22 + }, + "frame": { + "x": 186, + "y": 237, + "w": 24, + "h": 22 + } + }, + { + "filename": "769", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 8, + "y": 6, + "w": 23, + "h": 22 + }, + "frame": { + "x": 210, + "y": 237, + "w": 23, + "h": 22 + } + }, { "filename": "802s-zenith", "rotated": false, @@ -3678,8 +3573,8 @@ "h": 24 }, "frame": { - "x": 165, - "y": 392, + "x": 187, + "y": 259, "w": 22, "h": 24 } @@ -3699,14 +3594,14 @@ "h": 24 }, "frame": { - "x": 165, - "y": 416, + "x": 209, + "y": 259, "w": 22, "h": 24 } }, { - "filename": "749s", + "filename": "746", "rotated": false, "trimmed": true, "sourceSize": { @@ -3714,83 +3609,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 10, - "y": 5, - "w": 21, - "h": 23 - }, - "frame": { - "x": 167, - "y": 440, - "w": 21, - "h": 23 - } - }, - { - "filename": "755", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 14, - "y": 4, - "w": 13, - "h": 24 - }, - "frame": { - "x": 190, - "y": 322, - "w": 13, - "h": 24 - } - }, - { - "filename": "723", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 5, - "w": 17, - "h": 22 - }, - "frame": { - "x": 190, - "y": 346, - "w": 17, - "h": 22 - } - }, - { - "filename": "763s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 4, - "w": 21, - "h": 24 + "x": 12, + "y": 11, + "w": 18, + "h": 13 }, "frame": { "x": 187, - "y": 368, - "w": 21, - "h": 24 + "y": 283, + "w": 18, + "h": 13 } }, { - "filename": "793", + "filename": "733s", "rotated": false, "trimmed": true, "sourceSize": { @@ -3798,121 +3630,16 @@ "h": 30 }, "spriteSourceSize": { - "x": 10, - "y": 4, - "w": 21, - "h": 24 + "x": 8, + "y": 7, + "w": 23, + "h": 21 }, "frame": { - "x": 187, - "y": 392, - "w": 21, - "h": 24 - } - }, - { - "filename": "793s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 4, - "w": 21, - "h": 24 - }, - "frame": { - "x": 187, - "y": 416, - "w": 21, - "h": 24 - } - }, - { - "filename": "782", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 5, - "w": 22, - "h": 23 - }, - "frame": { - "x": 188, - "y": 440, - "w": 22, - "h": 23 - } - }, - { - "filename": "755s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 14, - "y": 4, - "w": 13, - "h": 24 - }, - "frame": { - "x": 210, - "y": 148, - "w": 13, - "h": 24 - } - }, - { - "filename": "2053s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 24, - "h": 22 - }, - "frame": { - "x": 223, + "x": 236, "y": 149, - "w": 24, - "h": 22 - } - }, - { - "filename": "760s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 4, - "w": 17, - "h": 24 - }, - "frame": { - "x": 198, - "y": 273, - "w": 17, - "h": 24 + "w": 23, + "h": 21 } }, { @@ -3930,14 +3657,14 @@ "h": 24 }, "frame": { - "x": 198, - "y": 297, + "x": 234, + "y": 170, "w": 22, "h": 24 } }, { - "filename": "723s", + "filename": "738", "rotated": false, "trimmed": true, "sourceSize": { @@ -3945,16 +3672,16 @@ "h": 30 }, "spriteSourceSize": { - "x": 11, - "y": 5, - "w": 17, - "h": 22 + "x": 9, + "y": 6, + "w": 22, + "h": 21 }, "frame": { - "x": 204, - "y": 251, - "w": 17, - "h": 22 + "x": 234, + "y": 194, + "w": 22, + "h": 21 } }, { @@ -3972,14 +3699,14 @@ "h": 24 }, "frame": { - "x": 203, - "y": 321, + "x": 233, + "y": 215, "w": 22, "h": 24 } }, { - "filename": "762", + "filename": "738s", "rotated": false, "trimmed": true, "sourceSize": { @@ -3987,20 +3714,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 12, - "y": 4, - "w": 18, - "h": 23 + "x": 9, + "y": 6, + "w": 22, + "h": 21 }, "frame": { - "x": 207, - "y": 345, - "w": 18, - "h": 23 + "x": 233, + "y": 239, + "w": 22, + "h": 21 } }, { - "filename": "741s-sensu", + "filename": "769s", "rotated": false, "trimmed": true, "sourceSize": { @@ -4008,36 +3735,162 @@ "h": 30 }, "spriteSourceSize": { - "x": 13, + "x": 8, + "y": 6, + "w": 23, + "h": 22 + }, + "frame": { + "x": 231, + "y": 260, + "w": 23, + "h": 22 + } + }, + { + "filename": "767s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 11, + "w": 24, + "h": 14 + }, + "frame": { + "x": 205, + "y": 283, + "w": 24, + "h": 14 + } + }, + { + "filename": "763", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 4, + "w": 21, + "h": 24 + }, + "frame": { + "x": 195, + "y": 297, + "w": 21, + "h": 24 + } + }, + { + "filename": "735s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 4, + "w": 20, + "h": 24 + }, + "frame": { + "x": 216, + "y": 297, + "w": 20, + "h": 24 + } + }, + { + "filename": "752", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 23, + "h": 18 + }, + "frame": { + "x": 259, + "y": 154, + "w": 23, + "h": 18 + } + }, + { + "filename": "763s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 4, + "w": 21, + "h": 24 + }, + "frame": { + "x": 256, + "y": 172, + "w": 21, + "h": 24 + } + }, + { + "filename": "747", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 22, + "h": 20 + }, + "frame": { + "x": 256, + "y": 196, + "w": 22, + "h": 20 + } + }, + { + "filename": "782", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 9, "y": 5, - "w": 17, + "w": 22, "h": 23 }, "frame": { - "x": 208, - "y": 368, - "w": 17, - "h": 23 - } - }, - { - "filename": "762s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 4, - "w": 18, - "h": 23 - }, - "frame": { - "x": 208, - "y": 391, - "w": 18, + "x": 255, + "y": 216, + "w": 22, "h": 23 } }, @@ -4056,14 +3909,14 @@ "h": 23 }, "frame": { - "x": 208, - "y": 414, + "x": 255, + "y": 239, "w": 22, "h": 23 } }, { - "filename": "2076", + "filename": "789", "rotated": false, "trimmed": true, "sourceSize": { @@ -4071,16 +3924,37 @@ "h": 30 }, "spriteSourceSize": { - "x": 11, - "y": 5, - "w": 21, - "h": 23 + "x": 10, + "y": 6, + "w": 23, + "h": 20 }, "frame": { - "x": 210, - "y": 437, - "w": 21, - "h": 23 + "x": 254, + "y": 262, + "w": 23, + "h": 20 + } + }, + { + "filename": "752s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 23, + "h": 18 + }, + "frame": { + "x": 282, + "y": 156, + "w": 23, + "h": 18 } }, { @@ -4098,71 +3972,8 @@ "h": 22 }, "frame": { - "x": 230, - "y": 171, - "w": 20, - "h": 22 - } - }, - { - "filename": "743s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 5, - "w": 20, - "h": 22 - }, - "frame": { - "x": 230, - "y": 193, - "w": 20, - "h": 22 - } - }, - { - "filename": "754", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 6, - "w": 20, - "h": 22 - }, - "frame": { - "x": 230, - "y": 215, - "w": 20, - "h": 22 - } - }, - { - "filename": "754s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 6, - "w": 20, - "h": 22 - }, - "frame": { - "x": 230, - "y": 237, + "x": 277, + "y": 174, "w": 20, "h": 22 } @@ -4182,12 +3993,54 @@ "h": 22 }, "frame": { - "x": 211, - "y": 228, + "x": 278, + "y": 196, "w": 19, "h": 22 } }, + { + "filename": "786", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 3, + "w": 20, + "h": 25 + }, + "frame": { + "x": 277, + "y": 218, + "w": 20, + "h": 25 + } + }, + { + "filename": "786s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 3, + "w": 20, + "h": 25 + }, + "frame": { + "x": 277, + "y": 243, + "w": 20, + "h": 25 + } + }, { "filename": "739", "rotated": false, @@ -4203,14 +4056,14 @@ "h": 21 }, "frame": { - "x": 230, - "y": 259, + "x": 277, + "y": 268, "w": 20, "h": 21 } }, { - "filename": "739s", + "filename": "755", "rotated": false, "trimmed": true, "sourceSize": { @@ -4218,20 +4071,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 11, - "y": 6, - "w": 20, - "h": 21 + "x": 14, + "y": 4, + "w": 13, + "h": 24 }, "frame": { - "x": 230, - "y": 280, - "w": 20, - "h": 21 + "x": 297, + "y": 174, + "w": 13, + "h": 24 } }, { - "filename": "2050", + "filename": "755s", "rotated": false, "trimmed": true, "sourceSize": { @@ -4239,20 +4092,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 13, - "y": 10, - "w": 15, - "h": 18 + "x": 14, + "y": 4, + "w": 13, + "h": 24 }, "frame": { - "x": 215, - "y": 273, - "w": 15, - "h": 18 + "x": 297, + "y": 198, + "w": 13, + "h": 24 } }, { - "filename": "744", + "filename": "793", "rotated": false, "trimmed": true, "sourceSize": { @@ -4260,20 +4113,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 11, - "y": 6, - "w": 19, - "h": 22 + "x": 10, + "y": 4, + "w": 21, + "h": 24 }, "frame": { - "x": 231, - "y": 301, - "w": 19, - "h": 22 + "x": 297, + "y": 222, + "w": 21, + "h": 24 } }, { - "filename": "734s", + "filename": "793s", "rotated": false, "trimmed": true, "sourceSize": { @@ -4281,20 +4134,20 @@ "h": 30 }, "spriteSourceSize": { - "x": 9, - "y": 10, - "w": 25, - "h": 16 + "x": 10, + "y": 4, + "w": 21, + "h": 24 }, "frame": { - "x": 225, - "y": 323, - "w": 25, - "h": 16 + "x": 297, + "y": 246, + "w": 21, + "h": 24 } }, { - "filename": "769", + "filename": "747s", "rotated": false, "trimmed": true, "sourceSize": { @@ -4302,37 +4155,16 @@ "h": 30 }, "spriteSourceSize": { - "x": 8, - "y": 6, - "w": 23, - "h": 22 + "x": 10, + "y": 7, + "w": 22, + "h": 20 }, "frame": { - "x": 225, - "y": 339, - "w": 23, - "h": 22 - } - }, - { - "filename": "769s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 6, - "w": 23, - "h": 22 - }, - "frame": { - "x": 225, - "y": 361, - "w": 23, - "h": 22 + "x": 297, + "y": 270, + "w": 22, + "h": 20 } }, { @@ -4350,12 +4182,516 @@ "h": 15 }, "frame": { - "x": 226, - "y": 383, + "x": 231, + "y": 282, "w": 24, "h": 15 } }, + { + "filename": "2037", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 22, + "h": 21 + }, + "frame": { + "x": 255, + "y": 282, + "w": 22, + "h": 21 + } + }, + { + "filename": "744", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 6, + "w": 19, + "h": 22 + }, + "frame": { + "x": 236, + "y": 297, + "w": 19, + "h": 22 + } + }, + { + "filename": "743s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 5, + "w": 20, + "h": 22 + }, + "frame": { + "x": 255, + "y": 303, + "w": 20, + "h": 22 + } + }, + { + "filename": "754", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 6, + "w": 20, + "h": 22 + }, + "frame": { + "x": 275, + "y": 303, + "w": 20, + "h": 22 + } + }, + { + "filename": "746s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 18, + "h": 13 + }, + "frame": { + "x": 277, + "y": 290, + "w": 18, + "h": 13 + } + }, + { + "filename": "749", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 5, + "w": 21, + "h": 23 + }, + "frame": { + "x": 295, + "y": 290, + "w": 21, + "h": 23 + } + }, + { + "filename": "722", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 8, + "w": 17, + "h": 18 + }, + "frame": { + "x": 305, + "y": 156, + "w": 17, + "h": 18 + } + }, + { + "filename": "741-sensu", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 5, + "w": 17, + "h": 23 + }, + "frame": { + "x": 310, + "y": 174, + "w": 17, + "h": 23 + } + }, + { + "filename": "741s-sensu", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 5, + "w": 17, + "h": 23 + }, + "frame": { + "x": 310, + "y": 197, + "w": 17, + "h": 23 + } + }, + { + "filename": "737", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 8, + "w": 18, + "h": 19 + }, + "frame": { + "x": 322, + "y": 155, + "w": 18, + "h": 19 + } + }, + { + "filename": "749s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 5, + "w": 21, + "h": 23 + }, + "frame": { + "x": 327, + "y": 174, + "w": 21, + "h": 23 + } + }, + { + "filename": "2076", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 5, + "w": 21, + "h": 23 + }, + "frame": { + "x": 327, + "y": 197, + "w": 21, + "h": 23 + } + }, + { + "filename": "2076s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 5, + "w": 21, + "h": 23 + }, + "frame": { + "x": 318, + "y": 220, + "w": 21, + "h": 23 + } + }, + { + "filename": "754s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 6, + "w": 20, + "h": 22 + }, + "frame": { + "x": 318, + "y": 243, + "w": 20, + "h": 22 + } + }, + { + "filename": "728", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 8, + "w": 17, + "h": 20 + }, + "frame": { + "x": 340, + "y": 154, + "w": 17, + "h": 20 + } + }, + { + "filename": "739s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 6, + "w": 20, + "h": 21 + }, + "frame": { + "x": 357, + "y": 153, + "w": 20, + "h": 21 + } + }, + { + "filename": "789s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 6, + "w": 23, + "h": 20 + }, + "frame": { + "x": 348, + "y": 174, + "w": 23, + "h": 20 + } + }, + { + "filename": "2037s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 22, + "h": 21 + }, + "frame": { + "x": 348, + "y": 194, + "w": 22, + "h": 21 + } + }, + { + "filename": "744s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 6, + "w": 19, + "h": 22 + }, + "frame": { + "x": 377, + "y": 150, + "w": 19, + "h": 22 + } + }, + { + "filename": "762", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 4, + "w": 18, + "h": 23 + }, + "frame": { + "x": 319, + "y": 265, + "w": 18, + "h": 23 + } + }, + { + "filename": "762s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 4, + "w": 18, + "h": 23 + }, + "frame": { + "x": 339, + "y": 220, + "w": 18, + "h": 23 + } + }, + { + "filename": "741-pau", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 14, + "y": 6, + "w": 18, + "h": 22 + }, + "frame": { + "x": 338, + "y": 243, + "w": 18, + "h": 22 + } + }, + { + "filename": "741s-pau", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 14, + "y": 6, + "w": 18, + "h": 22 + }, + "frame": { + "x": 337, + "y": 265, + "w": 18, + "h": 22 + } + }, + { + "filename": "723", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 5, + "w": 17, + "h": 22 + }, + "frame": { + "x": 357, + "y": 215, + "w": 17, + "h": 22 + } + }, { "filename": "2074s", "rotated": false, @@ -4371,8 +4707,8 @@ "h": 15 }, "frame": { - "x": 226, - "y": 398, + "x": 397, + "y": 149, "w": 24, "h": 15 } @@ -4392,54 +4728,12 @@ "h": 20 }, "frame": { - "x": 230, - "y": 413, + "x": 421, + "y": 151, "w": 20, "h": 20 } }, - { - "filename": "744s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 6, - "w": 19, - "h": 22 - }, - "frame": { - "x": 231, - "y": 433, - "w": 19, - "h": 22 - } - }, - { - "filename": "742", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 5, - "w": 19, - "h": 20 - }, - "frame": { - "x": 231, - "y": 455, - "w": 19, - "h": 20 - } - }, { "filename": "775", "rotated": false, @@ -4455,14 +4749,14 @@ "h": 20 }, "frame": { - "x": 210, - "y": 460, + "x": 396, + "y": 164, "w": 21, "h": 20 } }, { - "filename": "742s", + "filename": "742", "rotated": false, "trimmed": true, "sourceSize": { @@ -4476,180 +4770,12 @@ "h": 20 }, "frame": { - "x": 231, - "y": 475, + "x": 377, + "y": 172, "w": 19, "h": 20 } }, - { - "filename": "733s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 8, - "y": 7, - "w": 23, - "h": 21 - }, - "frame": { - "x": 103, - "y": 463, - "w": 23, - "h": 21 - } - }, - { - "filename": "2076s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 5, - "w": 21, - "h": 23 - }, - "frame": { - "x": 103, - "y": 484, - "w": 21, - "h": 23 - } - }, - { - "filename": "738", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 126, - "y": 463, - "w": 22, - "h": 21 - } - }, - { - "filename": "738s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 9, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 124, - "y": 484, - "w": 22, - "h": 21 - } - }, - { - "filename": "2037", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 7, - "w": 22, - "h": 21 - }, - "frame": { - "x": 148, - "y": 463, - "w": 22, - "h": 21 - } - }, - { - "filename": "2037s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 7, - "w": 22, - "h": 21 - }, - "frame": { - "x": 146, - "y": 484, - "w": 22, - "h": 21 - } - }, - { - "filename": "747s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 7, - "w": 22, - "h": 20 - }, - "frame": { - "x": 170, - "y": 463, - "w": 22, - "h": 20 - } - }, - { - "filename": "741-pau", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 14, - "y": 6, - "w": 18, - "h": 22 - }, - "frame": { - "x": 192, - "y": 463, - "w": 18, - "h": 22 - } - }, { "filename": "775s", "rotated": false, @@ -4665,495 +4791,12 @@ "h": 20 }, "frame": { - "x": 210, - "y": 480, + "x": 417, + "y": 171, "w": 21, "h": 20 } }, - { - "filename": "757", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 9, - "w": 19, - "h": 18 - }, - "frame": { - "x": 231, - "y": 495, - "w": 19, - "h": 18 - } - }, - { - "filename": "2019", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 5, - "w": 21, - "h": 21 - }, - "frame": { - "x": 104, - "y": 507, - "w": 21, - "h": 21 - } - }, - { - "filename": "2019s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 5, - "w": 21, - "h": 21 - }, - "frame": { - "x": 104, - "y": 528, - "w": 21, - "h": 21 - } - }, - { - "filename": "2052", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 7, - "w": 21, - "h": 21 - }, - "frame": { - "x": 125, - "y": 505, - "w": 21, - "h": 21 - } - }, - { - "filename": "2052s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 11, - "y": 7, - "w": 21, - "h": 21 - }, - "frame": { - "x": 125, - "y": 526, - "w": 21, - "h": 21 - } - }, - { - "filename": "779", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 7, - "w": 21, - "h": 20 - }, - "frame": { - "x": 146, - "y": 505, - "w": 21, - "h": 20 - } - }, - { - "filename": "779s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 7, - "w": 21, - "h": 20 - }, - "frame": { - "x": 146, - "y": 525, - "w": 21, - "h": 20 - } - }, - { - "filename": "752s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 8, - "w": 23, - "h": 18 - }, - "frame": { - "x": 104, - "y": 549, - "w": 23, - "h": 18 - } - }, - { - "filename": "790", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 7, - "w": 21, - "h": 19 - }, - "frame": { - "x": 103, - "y": 567, - "w": 21, - "h": 19 - } - }, - { - "filename": "741s-pau", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 14, - "y": 6, - "w": 18, - "h": 22 - }, - "frame": { - "x": 102, - "y": 586, - "w": 18, - "h": 22 - } - }, - { - "filename": "731", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 6, - "w": 17, - "h": 22 - }, - "frame": { - "x": 102, - "y": 608, - "w": 17, - "h": 22 - } - }, - { - "filename": "741s-pompom", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 7, - "w": 20, - "h": 20 - }, - "frame": { - "x": 127, - "y": 547, - "w": 20, - "h": 20 - } - }, - { - "filename": "790s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 7, - "w": 21, - "h": 19 - }, - "frame": { - "x": 124, - "y": 567, - "w": 21, - "h": 19 - } - }, - { - "filename": "731s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 6, - "w": 17, - "h": 22 - }, - "frame": { - "x": 120, - "y": 586, - "w": 17, - "h": 22 - } - }, - { - "filename": "778-disguised", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 5, - "w": 17, - "h": 22 - }, - "frame": { - "x": 119, - "y": 608, - "w": 17, - "h": 22 - } - }, - { - "filename": "778s-disguised", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 5, - "w": 17, - "h": 22 - }, - "frame": { - "x": 147, - "y": 545, - "w": 17, - "h": 22 - } - }, - { - "filename": "737", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 8, - "w": 18, - "h": 19 - }, - "frame": { - "x": 145, - "y": 567, - "w": 18, - "h": 19 - } - }, - { - "filename": "2027", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 10, - "w": 21, - "h": 18 - }, - "frame": { - "x": 137, - "y": 586, - "w": 21, - "h": 18 - } - }, - { - "filename": "767", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 11, - "w": 24, - "h": 14 - }, - "frame": { - "x": 102, - "y": 630, - "w": 24, - "h": 14 - } - }, - { - "filename": "2027s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 10, - "w": 21, - "h": 18 - }, - "frame": { - "x": 96, - "y": 644, - "w": 21, - "h": 18 - } - }, - { - "filename": "722", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 8, - "w": 17, - "h": 18 - }, - "frame": { - "x": 117, - "y": 644, - "w": 17, - "h": 18 - } - }, - { - "filename": "767s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 10, - "y": 11, - "w": 24, - "h": 14 - }, - "frame": { - "x": 126, - "y": 630, - "w": 24, - "h": 14 - } - }, - { - "filename": "722s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 8, - "w": 17, - "h": 18 - }, - "frame": { - "x": 134, - "y": 644, - "w": 17, - "h": 18 - } - }, { "filename": "778-busted", "rotated": false, @@ -5169,14 +4812,14 @@ "h": 16 }, "frame": { - "x": 137, - "y": 604, + "x": 396, + "y": 184, "w": 21, "h": 16 } }, { - "filename": "728", + "filename": "778s-busted", "rotated": false, "trimmed": true, "sourceSize": { @@ -5184,18 +4827,60 @@ "h": 30 }, "spriteSourceSize": { - "x": 13, - "y": 8, - "w": 17, + "x": 9, + "y": 12, + "w": 21, + "h": 16 + }, + "frame": { + "x": 417, + "y": 191, + "w": 21, + "h": 16 + } + }, + { + "filename": "779", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 21, "h": 20 }, "frame": { - "x": 158, - "y": 586, - "w": 17, + "x": 441, + "y": 166, + "w": 21, "h": 20 } }, + { + "filename": "2019", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 5, + "w": 21, + "h": 21 + }, + "frame": { + "x": 438, + "y": 186, + "w": 21, + "h": 21 + } + }, { "filename": "737s", "rotated": false, @@ -5211,14 +4896,14 @@ "h": 19 }, "frame": { - "x": 163, - "y": 567, + "x": 462, + "y": 172, "w": 18, "h": 19 } }, { - "filename": "728s", + "filename": "779s", "rotated": false, "trimmed": true, "sourceSize": { @@ -5226,15 +4911,225 @@ "h": 30 }, "spriteSourceSize": { - "x": 13, - "y": 8, - "w": 17, + "x": 10, + "y": 7, + "w": 21, "h": 20 }, "frame": { - "x": 158, - "y": 606, + "x": 480, + "y": 174, + "w": 21, + "h": 20 + } + }, + { + "filename": "790", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 21, + "h": 19 + }, + "frame": { + "x": 459, + "y": 191, + "w": 21, + "h": 19 + } + }, + { + "filename": "790s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 21, + "h": 19 + }, + "frame": { + "x": 480, + "y": 194, + "w": 21, + "h": 19 + } + }, + { + "filename": "2019s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 5, + "w": 21, + "h": 21 + }, + "frame": { + "x": 370, + "y": 194, + "w": 21, + "h": 21 + } + }, + { + "filename": "723s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 5, "w": 17, + "h": 22 + }, + "frame": { + "x": 374, + "y": 215, + "w": 17, + "h": 22 + } + }, + { + "filename": "2052", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 21, + "h": 21 + }, + "frame": { + "x": 391, + "y": 200, + "w": 21, + "h": 21 + } + }, + { + "filename": "2027", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 10, + "w": 21, + "h": 18 + }, + "frame": { + "x": 391, + "y": 221, + "w": 21, + "h": 18 + } + }, + { + "filename": "2052s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 21, + "h": 21 + }, + "frame": { + "x": 412, + "y": 207, + "w": 21, + "h": 21 + } + }, + { + "filename": "741s-pompom", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 20, + "h": 20 + }, + "frame": { + "x": 433, + "y": 207, + "w": 20, + "h": 20 + } + }, + { + "filename": "2027s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 10, + "y": 10, + "w": 21, + "h": 18 + }, + "frame": { + "x": 412, + "y": 228, + "w": 21, + "h": 18 + } + }, + { + "filename": "742s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 5, + "w": 19, + "h": 20 + }, + "frame": { + "x": 433, + "y": 227, + "w": 19, "h": 20 } }, @@ -5253,8 +5148,8 @@ "h": 18 }, "frame": { - "x": 150, - "y": 626, + "x": 453, + "y": 210, "w": 21, "h": 18 } @@ -5274,14 +5169,14 @@ "h": 18 }, "frame": { - "x": 151, - "y": 644, + "x": 452, + "y": 228, "w": 21, "h": 18 } }, { - "filename": "751", + "filename": "757", "rotated": false, "trimmed": true, "sourceSize": { @@ -5289,37 +5184,16 @@ "h": 30 }, "spriteSourceSize": { - "x": 13, - "y": 8, - "w": 14, - "h": 20 + "x": 11, + "y": 9, + "w": 19, + "h": 18 }, "frame": { - "x": 175, - "y": 586, - "w": 14, - "h": 20 - } - }, - { - "filename": "751s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 8, - "w": 14, - "h": 20 - }, - "frame": { - "x": 175, - "y": 606, - "w": 14, - "h": 20 + "x": 474, + "y": 213, + "w": 19, + "h": 18 } }, { @@ -5337,12 +5211,138 @@ "h": 18 }, "frame": { - "x": 171, - "y": 626, + "x": 473, + "y": 231, "w": 19, "h": 18 } }, + { + "filename": "731", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 6, + "w": 17, + "h": 22 + }, + "frame": { + "x": 316, + "y": 290, + "w": 17, + "h": 22 + } + }, + { + "filename": "771", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 11, + "w": 18, + "h": 13 + }, + "frame": { + "x": 316, + "y": 312, + "w": 18, + "h": 13 + } + }, + { + "filename": "731s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 6, + "w": 17, + "h": 22 + }, + "frame": { + "x": 333, + "y": 288, + "w": 17, + "h": 22 + } + }, + { + "filename": "736", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 12, + "w": 17, + "h": 13 + }, + "frame": { + "x": 334, + "y": 310, + "w": 17, + "h": 13 + } + }, + { + "filename": "778-disguised", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 5, + "w": 17, + "h": 22 + }, + "frame": { + "x": 350, + "y": 287, + "w": 17, + "h": 22 + } + }, + { + "filename": "778s-disguised", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 5, + "w": 17, + "h": 22 + }, + "frame": { + "x": 355, + "y": 265, + "w": 17, + "h": 22 + } + }, { "filename": "741", "rotated": false, @@ -5358,14 +5358,14 @@ "h": 19 }, "frame": { - "x": 172, - "y": 644, + "x": 356, + "y": 246, "w": 17, "h": 19 } }, { - "filename": "778s-busted", + "filename": "728s", "rotated": false, "trimmed": true, "sourceSize": { @@ -5373,16 +5373,79 @@ "h": 30 }, "spriteSourceSize": { - "x": 9, - "y": 12, - "w": 21, - "h": 16 + "x": 13, + "y": 8, + "w": 17, + "h": 20 }, "frame": { - "x": 189, - "y": 485, - "w": 21, - "h": 16 + "x": 373, + "y": 237, + "w": 17, + "h": 20 + } + }, + { + "filename": "722s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 8, + "w": 17, + "h": 18 + }, + "frame": { + "x": 390, + "y": 239, + "w": 17, + "h": 18 + } + }, + { + "filename": "736s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 12, + "w": 17, + "h": 13 + }, + "frame": { + "x": 351, + "y": 309, + "w": 17, + "h": 13 + } + }, + { + "filename": "771s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 11, + "w": 18, + "h": 13 + }, + "frame": { + "x": 373, + "y": 257, + "w": 18, + "h": 13 } }, { @@ -5400,54 +5463,12 @@ "h": 19 }, "frame": { - "x": 164, - "y": 545, + "x": 372, + "y": 270, "w": 17, "h": 19 } }, - { - "filename": "753", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 14, - "y": 8, - "w": 14, - "h": 19 - }, - "frame": { - "x": 168, - "y": 484, - "w": 14, - "h": 19 - } - }, - { - "filename": "753s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 14, - "y": 8, - "w": 14, - "h": 19 - }, - "frame": { - "x": 189, - "y": 644, - "w": 14, - "h": 19 - } - }, { "filename": "759", "rotated": false, @@ -5463,12 +5484,33 @@ "h": 19 }, "frame": { - "x": 167, - "y": 505, + "x": 367, + "y": 289, "w": 17, "h": 19 } }, + { + "filename": "808", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 17, + "h": 17 + }, + "frame": { + "x": 368, + "y": 308, + "w": 17, + "h": 17 + } + }, { "filename": "759s", "rotated": false, @@ -5484,117 +5526,12 @@ "h": 19 }, "frame": { - "x": 167, - "y": 524, + "x": 391, + "y": 257, "w": 17, "h": 19 } }, - { - "filename": "777", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 8, - "w": 17, - "h": 19 - }, - "frame": { - "x": 184, - "y": 501, - "w": 17, - "h": 19 - } - }, - { - "filename": "777s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 8, - "w": 17, - "h": 19 - }, - "frame": { - "x": 184, - "y": 520, - "w": 17, - "h": 19 - } - }, - { - "filename": "761", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 8, - "w": 16, - "h": 19 - }, - "frame": { - "x": 201, - "y": 501, - "w": 16, - "h": 19 - } - }, - { - "filename": "761s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 8, - "w": 16, - "h": 19 - }, - "frame": { - "x": 201, - "y": 520, - "w": 16, - "h": 19 - } - }, - { - "filename": "746", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 11, - "w": 18, - "h": 13 - }, - "frame": { - "x": 184, - "y": 539, - "w": 18, - "h": 13 - } - }, { "filename": "774-blue", "rotated": false, @@ -5610,14 +5547,14 @@ "h": 18 }, "frame": { - "x": 181, - "y": 552, + "x": 389, + "y": 276, "w": 17, "h": 18 } }, { - "filename": "736", + "filename": "777", "rotated": false, "trimmed": true, "sourceSize": { @@ -5625,37 +5562,16 @@ "h": 30 }, "spriteSourceSize": { - "x": 12, - "y": 12, + "x": 13, + "y": 8, "w": 17, - "h": 13 + "h": 19 }, "frame": { - "x": 181, - "y": 570, + "x": 408, + "y": 246, "w": 17, - "h": 13 - } - }, - { - "filename": "736s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 12, - "w": 17, - "h": 13 - }, - "frame": { - "x": 202, - "y": 539, - "w": 17, - "h": 13 + "h": 19 } }, { @@ -5673,14 +5589,14 @@ "h": 18 }, "frame": { - "x": 198, - "y": 552, + "x": 408, + "y": 265, "w": 17, "h": 18 } }, { - "filename": "746s", + "filename": "777s", "rotated": false, "trimmed": true, "sourceSize": { @@ -5688,16 +5604,16 @@ "h": 30 }, "spriteSourceSize": { - "x": 12, - "y": 11, - "w": 18, - "h": 13 + "x": 13, + "y": 8, + "w": 17, + "h": 19 }, "frame": { - "x": 198, - "y": 570, - "w": 18, - "h": 13 + "x": 425, + "y": 247, + "w": 17, + "h": 19 } }, { @@ -5715,12 +5631,33 @@ "h": 18 }, "frame": { - "x": 189, - "y": 583, + "x": 425, + "y": 266, "w": 17, "h": 18 } }, + { + "filename": "751", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 8, + "w": 14, + "h": 20 + }, + "frame": { + "x": 442, + "y": 247, + "w": 14, + "h": 20 + } + }, { "filename": "774-orange", "rotated": false, @@ -5736,8 +5673,8 @@ "h": 18 }, "frame": { - "x": 189, - "y": 601, + "x": 456, + "y": 246, "w": 17, "h": 18 } @@ -5757,12 +5694,33 @@ "h": 18 }, "frame": { - "x": 206, - "y": 583, + "x": 473, + "y": 249, "w": 17, "h": 18 } }, + { + "filename": "751s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 13, + "y": 8, + "w": 14, + "h": 20 + }, + "frame": { + "x": 442, + "y": 267, + "w": 14, + "h": 20 + } + }, { "filename": "774-violet", "rotated": false, @@ -5778,8 +5736,8 @@ "h": 18 }, "frame": { - "x": 206, - "y": 601, + "x": 456, + "y": 264, "w": 17, "h": 18 } @@ -5799,8 +5757,8 @@ "h": 18 }, "frame": { - "x": 190, - "y": 619, + "x": 473, + "y": 267, "w": 17, "h": 18 } @@ -5820,8 +5778,8 @@ "h": 18 }, "frame": { - "x": 207, - "y": 619, + "x": 406, + "y": 283, "w": 17, "h": 18 } @@ -5841,33 +5799,12 @@ "h": 18 }, "frame": { - "x": 203, - "y": 637, + "x": 423, + "y": 284, "w": 17, "h": 18 } }, - { - "filename": "808", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 11, - "w": 17, - "h": 17 - }, - "frame": { - "x": 203, - "y": 655, - "w": 17, - "h": 17 - } - }, { "filename": "774s-green", "rotated": false, @@ -5883,14 +5820,14 @@ "h": 18 }, "frame": { - "x": 215, - "y": 552, + "x": 440, + "y": 287, "w": 17, "h": 18 } }, { - "filename": "771", + "filename": "753", "rotated": false, "trimmed": true, "sourceSize": { @@ -5898,58 +5835,16 @@ "h": 30 }, "spriteSourceSize": { - "x": 13, - "y": 11, - "w": 18, - "h": 13 + "x": 14, + "y": 8, + "w": 14, + "h": 19 }, "frame": { - "x": 216, - "y": 570, - "w": 18, - "h": 13 - } - }, - { - "filename": "771s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 11, - "w": 18, - "h": 13 - }, - "frame": { - "x": 232, - "y": 513, - "w": 18, - "h": 13 - } - }, - { - "filename": "2050s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 13, - "y": 10, - "w": 15, - "h": 18 - }, - "frame": { - "x": 217, - "y": 513, - "w": 15, - "h": 18 + "x": 457, + "y": 282, + "w": 14, + "h": 19 } }, { @@ -5967,12 +5862,33 @@ "h": 18 }, "frame": { - "x": 223, - "y": 583, + "x": 471, + "y": 285, "w": 17, "h": 18 } }, + { + "filename": "753s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 14, + "y": 8, + "w": 14, + "h": 19 + }, + "frame": { + "x": 488, + "y": 285, + "w": 14, + "h": 19 + } + }, { "filename": "774s-orange", "rotated": false, @@ -5988,8 +5904,8 @@ "h": 18 }, "frame": { - "x": 223, - "y": 601, + "x": 385, + "y": 294, "w": 17, "h": 18 } @@ -6009,8 +5925,8 @@ "h": 18 }, "frame": { - "x": 224, - "y": 619, + "x": 402, + "y": 301, "w": 17, "h": 18 } @@ -6030,33 +5946,12 @@ "h": 18 }, "frame": { - "x": 220, - "y": 637, + "x": 419, + "y": 302, "w": 17, "h": 18 } }, - { - "filename": "808s", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 30 - }, - "spriteSourceSize": { - "x": 12, - "y": 11, - "w": 17, - "h": 17 - }, - "frame": { - "x": 220, - "y": 655, - "w": 17, - "h": 17 - } - }, { "filename": "774s-yellow", "rotated": false, @@ -6072,8 +5967,8 @@ "h": 18 }, "frame": { - "x": 219, - "y": 531, + "x": 436, + "y": 305, "w": 17, "h": 18 } @@ -6093,11 +5988,32 @@ "h": 18 }, "frame": { - "x": 232, - "y": 549, + "x": 453, + "y": 305, "w": 17, "h": 18 } + }, + { + "filename": "808s", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 40, + "h": 30 + }, + "spriteSourceSize": { + "x": 12, + "y": 11, + "w": 17, + "h": 17 + }, + "frame": { + "x": 470, + "y": 303, + "w": 17, + "h": 17 + } } ] } @@ -6105,6 +6021,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:be37a6c6f866e26c7e4deba1514c5a68:cb56f7bd9b13cc1604b5814449582c05:2e7c5873ead8fd8fce82a0b3fcc86b42$" + "smartupdate": "$TexturePacker:SmartUpdate:5861cb99c3db0943f1ccef3b0e1b4683:26656a692718930d92be5b6aba391ced:2e7c5873ead8fd8fce82a0b3fcc86b42$" } } diff --git a/public/images/pokemon_icons_7.png b/public/images/pokemon_icons_7.png index 8c8311b1d0b..5e6421360fd 100644 Binary files a/public/images/pokemon_icons_7.png and b/public/images/pokemon_icons_7.png differ diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 3048c22d9d8..af128519a76 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -168,6 +168,8 @@ import { BattlerTagType } from "#enums/battler-tag-type"; import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters"; import { StatusEffect } from "#enums/status-effect"; import { initGlobalScene } from "#app/global-scene"; +import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; +import { HideAbilityPhase } from "#app/phases/hide-ability-phase"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; @@ -2923,6 +2925,21 @@ export default class BattleScene extends SceneBase { } } + /** + * Queues an ability bar flyout phase + * @param pokemon The pokemon who has the ability + * @param passive Whether the ability is a passive + * @param show Whether to show or hide the bar + */ + public queueAbilityDisplay(pokemon: Pokemon, passive: boolean, show: boolean): void { + this.unshiftPhase( + show + ? new ShowAbilityPhase(pokemon.getBattlerIndex(), passive) + : new HideAbilityPhase(pokemon.getBattlerIndex(), passive), + ); + this.clearPhaseQueueSplice(); + } + /** * Moves everything from nextCommandPhaseQueue to phaseQueue (keeping order) */ @@ -3152,6 +3169,41 @@ export default class BattleScene extends SceneBase { return false; } + canTransferHeldItemModifier(itemModifier: PokemonHeldItemModifier, target: Pokemon, transferQuantity = 1): boolean { + const mod = itemModifier.clone() as PokemonHeldItemModifier; + const source = mod.pokemonId ? mod.getPokemon() : null; + const cancelled = new Utils.BooleanHolder(false); + + if (source && source.isPlayer() !== target.isPlayer()) { + applyAbAttrs(BlockItemTheftAbAttr, source, cancelled); + } + + if (cancelled.value) { + return false; + } + + const matchingModifier = this.findModifier( + m => m instanceof PokemonHeldItemModifier && m.matchType(mod) && m.pokemonId === target.id, + target.isPlayer(), + ) as PokemonHeldItemModifier; + + if (matchingModifier) { + const maxStackCount = matchingModifier.getMaxStackCount(); + if (matchingModifier.stackCount >= maxStackCount) { + return false; + } + const countTaken = Math.min(transferQuantity, mod.stackCount, maxStackCount - matchingModifier.stackCount); + mod.stackCount -= countTaken; + } else { + const countTaken = Math.min(transferQuantity, mod.stackCount); + mod.stackCount -= countTaken; + } + + const removeOld = mod.stackCount === 0; + + return !removeOld || !source || this.hasModifier(itemModifier, !source.isPlayer()); + } + removePartyMemberModifiers(partyMemberIndex: number): Promise { return new Promise(resolve => { const pokemonId = this.getPlayerParty()[partyMemberIndex].id; @@ -3309,6 +3361,11 @@ export default class BattleScene extends SceneBase { }); } + hasModifier(modifier: PersistentModifier, enemy = false): boolean { + const modifiers = !enemy ? this.modifiers : this.enemyModifiers; + return modifiers.indexOf(modifier) > -1; + } + /** * Removes a currently owned item. If the item is stacked, the entire item stack * gets removed. This function does NOT apply in-battle effects, such as Unburden. diff --git a/src/data/ability.ts b/src/data/ability.ts index 6e9f9f0e252..5cdac37beee 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -35,7 +35,6 @@ import { Species } from "#enums/species"; import { Stat, type BattleStat, type EffectiveStat, BATTLE_STATS, EFFECTIVE_STATS, getStatKey } from "#app/enums/stat"; import { MovePhase } from "#app/phases/move-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; -import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { globalScene } from "#app/global-scene"; import { SwitchType } from "#app/enums/switch-type"; @@ -149,7 +148,8 @@ export class Ability implements Localizable { } } -type AbAttrApplyFunc = (attr: TAttr, passive: boolean) => boolean; +type AbAttrApplyFunc = (attr: TAttr, passive: boolean) => void; +type AbAttrSuccessFunc = (attr: TAttr, passive: boolean) => boolean; type AbAttrCondition = (pokemon: Pokemon) => boolean; // TODO: Can this be improved? @@ -165,9 +165,15 @@ export abstract class AbAttr { this.showAbility = showAbility; } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { - return false; - } + /** + * Applies ability effects without checking conditions + * @param pokemon - The pokemon to apply this ability to + * @param passive - Whether or not the ability is a passive + * @param simulated - Whether the call is simulated + * @param args - Extra args passed to the function. Handled by child classes. + * @see {@linkcode canApply} + */ + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder | null, args: any[]): void {} getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null { return null; @@ -181,13 +187,28 @@ export abstract class AbAttr { this.extraCondition = condition; return this; } + + /** + * Returns a boolean describing whether the ability can be applied under current conditions + * @param pokemon - The pokemon to apply this ability to + * @param passive - Whether or not the ability is a passive + * @param simulated - Whether the call is simulated + * @param args - Extra args passed to the function. Handled by child classes. + * @returns `true` if the ability can be applied, `false` otherwise + * @see {@linkcode apply} + */ + canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return true; + } } export class BlockRecoilDamageAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - cancelled.value = true; + constructor() { + super(false); + } - return true; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + cancelled.value = true; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]) { @@ -198,7 +219,7 @@ export class BlockRecoilDamageAttr extends AbAttr { /** * Attribute for abilities that increase the chance of a double battle * occurring. - * @see apply + * @see {@linkcode apply} */ export class DoubleBattleChanceAbAttr extends AbAttr { constructor() { @@ -208,40 +229,39 @@ export class DoubleBattleChanceAbAttr extends AbAttr { /** * Increases the chance of a double battle occurring * @param args [0] {@linkcode Utils.NumberHolder} for double battle chance - * @returns true if the ability was applied */ - apply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: Utils.BooleanHolder, args: any[]): void { const doubleBattleChance = args[0] as Utils.NumberHolder; // This is divided because the chance is generated as a number from 0 to doubleBattleChance.value using Utils.randSeedInt // A double battle will initiate if the generated number is 0 doubleBattleChance.value = doubleBattleChance.value / 4; - - return true; } } export class PostBattleInitAbAttr extends AbAttr { - applyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return false; + canApplyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return true; } + + applyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} } export class PostBattleInitFormChangeAbAttr extends PostBattleInitAbAttr { private formFunc: (p: Pokemon) => number; constructor(formFunc: ((p: Pokemon) => number)) { - super(true); + super(false); this.formFunc = formFunc; } - applyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const formIndex = this.formFunc(pokemon); - if (formIndex !== pokemon.formIndex && !simulated) { - return globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); - } + return formIndex !== pokemon.formIndex && !simulated; + } - return false; + override applyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } } @@ -256,7 +276,7 @@ export class PostTeraFormChangeStatChangeAbAttr extends AbAttr { this.stages = stages; } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder | null, args: any[]): void { const statStageChangePhases: StatStageChangePhase[] = []; if (!simulated) { @@ -266,8 +286,6 @@ export class PostTeraFormChangeStatChangeAbAttr extends AbAttr { globalScene.unshiftPhase(statStageChangePhase); } } - - return true; } } @@ -286,11 +304,14 @@ export class ClearWeatherAbAttr extends AbAttr { this.weather = weather; } - apply(pokemon: Pokemon, passive: boolean, simulated:boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + public override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return globalScene.arena.canSetWeather(WeatherType.NONE); + } + + public override apply(pokemon: Pokemon, passive: boolean, simulated:boolean, cancelled: Utils.BooleanHolder, args: any[]): void { if (!simulated) { globalScene.arena.trySetWeather(WeatherType.NONE, true); } - return true; } } @@ -309,17 +330,31 @@ export class ClearTerrainAbAttr extends AbAttr { this.terrain = terrain; } - apply(pokemon: Pokemon, passive: boolean, simulated:boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + public override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return globalScene.arena.canSetTerrain(TerrainType.NONE); + } + + public override apply(pokemon: Pokemon, passive: boolean, simulated:boolean, cancelled: Utils.BooleanHolder, args: any[]): void { if (!simulated) { globalScene.arena.trySetTerrain(TerrainType.NONE, true, true); } - return true; } } type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: Move) => boolean; export class PreDefendAbAttr extends AbAttr { + canApplyPreDefend( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move | null, + cancelled: Utils.BooleanHolder | null, + args: any[]): boolean { + return true; + } + applyPreDefend( pokemon: Pokemon, passive: boolean, @@ -328,28 +363,26 @@ export class PreDefendAbAttr extends AbAttr { move: Move | null, cancelled: Utils.BooleanHolder | null, args: any[], - ): boolean { - return false; - } + ): void {} } export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr { - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (pokemon.isFullHp() - && pokemon.getMaxHp() > 1 //Checks if pokemon has wonder_guard (which forces 1hp) - && (args[0] as Utils.NumberHolder).value >= pokemon.hp) { //Damage >= hp - return simulated || pokemon.addTag(BattlerTagType.STURDY, 1); - } + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + return pokemon.isFullHp() + && pokemon.getMaxHp() > 1 //Checks if pokemon has wonder_guard (which forces 1hp) + && (args[0] as Utils.NumberHolder).value >= pokemon.hp; //Damage >= hp + } - return false; + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): void { + if (!simulated) { + pokemon.addTag(BattlerTagType.STURDY, 1); + } } } export class BlockItemTheftAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { cancelled.value = true; - - return true; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]) { @@ -361,13 +394,16 @@ export class BlockItemTheftAbAttr extends AbAttr { } export class StabBoostAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if ((args[0] as Utils.NumberHolder).value > 1) { - (args[0] as Utils.NumberHolder).value += 0.5; - return true; - } + constructor() { + super(false); + } - return false; + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return (args[0] as Utils.NumberHolder).value > 1; + } + + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + (args[0] as Utils.NumberHolder).value += 0.5; } } @@ -375,21 +411,19 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr { protected condition: PokemonDefendCondition; private damageMultiplier: number; - constructor(condition: PokemonDefendCondition, damageMultiplier: number) { - super(); + constructor(condition: PokemonDefendCondition, damageMultiplier: number, showAbility: boolean = false) { + super(showAbility); this.condition = condition; this.damageMultiplier = damageMultiplier; } - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.condition(pokemon, attacker, move)) { - (args[0] as Utils.NumberHolder).value = Utils.toDmgValue((args[0] as Utils.NumberHolder).value * this.damageMultiplier); + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + return this.condition(pokemon, attacker, move); + } - return true; - } - - return false; + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): void { + (args[0] as Utils.NumberHolder).value = Utils.toDmgValue((args[0] as Utils.NumberHolder).value * this.damageMultiplier); } } @@ -410,16 +444,15 @@ export class AlliedFieldDamageReductionAbAttr extends PreDefendAbAttr { * @param args * - `[0]` {@linkcode Utils.NumberHolder} - The damage being dealt */ - override applyPreDefend(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _attacker: Pokemon, _move: Move, _cancelled: Utils.BooleanHolder, args: any[]): boolean { + override applyPreDefend(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _attacker: Pokemon, _move: Move, _cancelled: Utils.BooleanHolder, args: any[]): void { const damage = args[0] as Utils.NumberHolder; damage.value = Utils.toDmgValue(damage.value * this.damageMultiplier); - return true; } } export class ReceivedTypeDamageMultiplierAbAttr extends ReceivedMoveDamageMultiplierAbAttr { constructor(moveType: PokemonType, damageMultiplier: number) { - super((target, user, move) => user.getMoveType(move) === moveType, damageMultiplier); + super((target, user, move) => user.getMoveType(move) === moveType, damageMultiplier, false); } } @@ -434,12 +467,16 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { private condition: AbAttrCondition | null; constructor(immuneType: PokemonType | null, condition?: AbAttrCondition) { - super(); + super(true); this.immuneType = immuneType; this.condition = condition ?? null; } + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + return ![ MoveTarget.BOTH_SIDES, MoveTarget.ENEMY_SIDE, MoveTarget.USER_SIDE ].includes(move.moveTarget) && attacker !== pokemon && attacker.getMoveType(move) === this.immuneType; + } + /** * Applies immunity if this ability grants immunity to the type of the given move. * @param pokemon {@linkcode Pokemon} The defending Pokemon. @@ -450,16 +487,8 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { * @param args [0] {@linkcode Utils.NumberHolder} gets set to 0 if move is immuned by an ability. * @param args [1] - Whether the move is simulated. */ - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - // Field moves should ignore immunity - if ([ MoveTarget.BOTH_SIDES, MoveTarget.ENEMY_SIDE, MoveTarget.USER_SIDE ].includes(move.moveTarget)) { - return false; - } - if (attacker !== pokemon && attacker.getMoveType(move) === this.immuneType) { - (args[0] as Utils.NumberHolder).value = 0; - return true; - } - return false; + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): void { + (args[0] as Utils.NumberHolder).value = 0; } getImmuneType(): PokemonType | null { @@ -476,17 +505,19 @@ export class AttackTypeImmunityAbAttr extends TypeImmunityAbAttr { super(immuneType, condition); } + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + return move.category !== MoveCategory.STATUS && !move.hasAttr(NeutralDamageAgainstFlyingTypeMultiplierAttr) + && super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + } + /** * Applies immunity if the move used is not a status move. * Type immunity abilities that do not give additional benefits (HP recovery, stat boosts, etc) are not immune to status moves of the type * Example: Levitate */ - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): void { // this is a hacky way to fix the Levitate/Thousand Arrows interaction, but it works for now... - if (move.category !== MoveCategory.STATUS && !move.hasAttr(NeutralDamageAgainstFlyingTypeMultiplierAttr)) { - return super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); - } - return false; + super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); } } @@ -495,20 +526,18 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr { super(immuneType); } - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const ret = super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + return super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + } - if (ret) { - if (!pokemon.isFullHp() && !simulated) { - const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), - Utils.toDmgValue(pokemon.getMaxHp() / 4), i18next.t("abilityTriggers:typeImmunityHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); - cancelled.value = true; // Suppresses "No Effect" message - } - return true; + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): void { + super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + if (!pokemon.isFullHp() && !simulated) { + const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; + globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), + Utils.toDmgValue(pokemon.getMaxHp() / 4), i18next.t("abilityTriggers:typeImmunityHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + cancelled.value = true; // Suppresses "No Effect" message } - - return false; } } @@ -523,17 +552,16 @@ class TypeImmunityStatStageChangeAbAttr extends TypeImmunityAbAttr { this.stages = stages; } - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const ret = super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + return super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + } - if (ret) { - cancelled.value = true; // Suppresses "No Effect" message - if (!simulated) { - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); - } + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): void { + super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + cancelled.value = true; // Suppresses "No Effect" message + if (!simulated) { + globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); } - - return ret; } } @@ -548,17 +576,16 @@ class TypeImmunityAddBattlerTagAbAttr extends TypeImmunityAbAttr { this.turnCount = turnCount; } - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const ret = super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + return super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + } - if (ret) { - cancelled.value = true; // Suppresses "No Effect" message - if (!simulated) { - pokemon.addTag(this.tagType, this.turnCount, undefined, pokemon.id); - } + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): void { + super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + cancelled.value = true; // Suppresses "No Effect" message + if (!simulated) { + pokemon.addTag(this.tagType, this.turnCount, undefined, pokemon.id); } - - return ret; } } @@ -567,18 +594,16 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { super(null, condition); } - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { const modifierValue = args.length > 0 ? (args[0] as Utils.NumberHolder).value : pokemon.getAttackTypeEffectiveness(attacker.getMoveType(move), attacker, undefined, undefined, move); + return move instanceof AttackMove && modifierValue < 2; + } - if (move instanceof AttackMove && modifierValue < 2) { - cancelled.value = true; // Suppresses "No Effect" message - (args[0] as Utils.NumberHolder).value = 0; - return true; - } - - return false; + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): void { + cancelled.value = true; // Suppresses "No Effect" message + (args[0] as Utils.NumberHolder).value = 0; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { @@ -595,6 +620,12 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { * @extends PreDefendAbAttr */ export class FullHpResistTypeAbAttr extends PreDefendAbAttr { + + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + const typeMultiplier = args[0]; + return (typeMultiplier && typeMultiplier instanceof Utils.NumberHolder) && !(move && move.hasAttr(FixedDamageAttr)) && pokemon.isFullHp() && typeMultiplier.value > 0.5; + } + /** * Reduces a type multiplier to 0.5 if the source is at full HP. * @param pokemon {@linkcode Pokemon} the Pokemon with this ability @@ -604,7 +635,6 @@ export class FullHpResistTypeAbAttr extends PreDefendAbAttr { * @param move {@linkcode Move} the move being used on the source * @param cancelled n/a * @param args `[0]` a container for the move's current type effectiveness multiplier - * @returns `true` if the move's effectiveness is reduced; `false` otherwise */ override applyPreDefend( pokemon: Pokemon, @@ -613,23 +643,10 @@ export class FullHpResistTypeAbAttr extends PreDefendAbAttr { attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, - args: any[], - ): boolean { + args: any[]): void { const typeMultiplier = args[0]; - if (!(typeMultiplier && typeMultiplier instanceof Utils.NumberHolder)) { - return false; - } - - if (move && move.hasAttr(FixedDamageAttr)) { - return false; - } - - if (pokemon.isFullHp() && typeMultiplier.value > 0.5) { - typeMultiplier.value = 0.5; - pokemon.turnData.moveEffectiveness = 0.5; - return true; - } - return false; + typeMultiplier.value = 0.5; + pokemon.turnData.moveEffectiveness = 0.5; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { @@ -640,6 +657,17 @@ export class FullHpResistTypeAbAttr extends PreDefendAbAttr { } export class PostDefendAbAttr extends AbAttr { + canApplyPostDefend( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + hitResult: HitResult | null, + args: any[]): boolean { + return true; + } + applyPostDefend( pokemon: Pokemon, passive: boolean, @@ -648,37 +676,39 @@ export class PostDefendAbAttr extends AbAttr { move: Move, hitResult: HitResult | null, args: any[], - ): boolean { - return false; - } + ): void {} } export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY) { - return false; - } - if (move.getPriority(attacker) > 0 && !move.isMultiTarget()) { - cancelled.value = true; - return true; - } + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + return !(move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY) && move.getPriority(attacker) > 0 && !move.isMultiTarget(); + } - return false; + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): void { + cancelled.value = true; } } export class PostStatStageChangeAbAttr extends AbAttr { + canApplyPostStatStageChange( + pokemon: Pokemon, + simulated: boolean, + statsChanged: BattleStat[], + stagesChanged: number, + selfTarget: boolean, + args: any[]): boolean { + return true; + } + applyPostStatStageChange( pokemon: Pokemon, simulated: boolean, statsChanged: BattleStat[], - stagesChanged: integer, + stagesChanged: number, selfTarget: boolean, args: any[], - ): boolean { - return false; - } + ): void {} } export class MoveImmunityAbAttr extends PreDefendAbAttr { @@ -690,13 +720,12 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr { this.immuneCondition = immuneCondition; } - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.immuneCondition(pokemon, attacker, move)) { - cancelled.value = true; - return true; - } + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + return this.immuneCondition(pokemon, attacker, move); + } - return false; + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): void { + cancelled.value = true; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { @@ -711,14 +740,19 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr { * @extends PreDefendAbAttr */ export class WonderSkinAbAttr extends PreDefendAbAttr { - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const moveAccuracy = args[0] as Utils.NumberHolder; - if (move.category === MoveCategory.STATUS && moveAccuracy.value >= 50) { - moveAccuracy.value = 50; - return true; - } - return false; + constructor() { + super(false); + } + + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + const moveAccuracy = args[0] as Utils.NumberHolder; + return move.category === MoveCategory.STATUS && moveAccuracy.value >= 50; + } + + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): void { + const moveAccuracy = args[0] as Utils.NumberHolder; + moveAccuracy.value = 50; } } @@ -732,13 +766,13 @@ export class MoveImmunityStatStageChangeAbAttr extends MoveImmunityAbAttr { this.stages = stages; } - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const ret = super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); - if (ret && !simulated) { - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); - } + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + return !simulated && super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + } - return ret; + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): void { + super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); } } /** @@ -747,6 +781,11 @@ export class MoveImmunityStatStageChangeAbAttr extends MoveImmunityAbAttr { * @see {@linkcode applyPostDefend} */ export class ReverseDrainAbAttr extends PostDefendAbAttr { + + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + return move.hasAttr(HitHealAttr) && !move.hitsSubstitute(attacker, pokemon); + } + /** * Determines if a damage and draining move was used to check if this ability should stop the healing. * Examples include: Absorb, Draining Kiss, Bitter Blade, etc. @@ -757,16 +796,11 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr { * @param move {@linkcode PokemonMove} that is being used * @param _hitResult N/A * @param _args N/A - * @returns true if healing should be reversed on a healing move, false otherwise. */ - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (move.hasAttr(HitHealAttr) && !move.hitsSubstitute(attacker, pokemon)) { - if (!simulated) { - globalScene.queueMessage(i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) })); - } - return true; + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + if (!simulated) { + globalScene.queueMessage(i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) })); } - return false; } } @@ -787,24 +821,23 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr { this.allOthers = allOthers; } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) { - if (simulated) { - return true; - } + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + return this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon); + } - if (this.allOthers) { - const otherPokemon = pokemon.getAlly() ? pokemon.getOpponents().concat([ pokemon.getAlly() ]) : pokemon.getOpponents(); - for (const other of otherPokemon) { - globalScene.unshiftPhase(new StatStageChangePhase((other).getBattlerIndex(), false, [ this.stat ], this.stages)); - } - return true; - } - globalScene.unshiftPhase(new StatStageChangePhase((this.selfTarget ? pokemon : attacker).getBattlerIndex(), this.selfTarget, [ this.stat ], this.stages)); - return true; + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + if (simulated) { + return; } - return false; + if (this.allOthers) { + const otherPokemon = pokemon.getAlly() ? pokemon.getOpponents().concat([ pokemon.getAlly() ]) : pokemon.getOpponents(); + for (const other of otherPokemon) { + globalScene.unshiftPhase(new StatStageChangePhase((other).getBattlerIndex(), false, [ this.stat ], this.stages)); + } + } else { + globalScene.unshiftPhase(new StatStageChangePhase((this.selfTarget ? pokemon : attacker).getBattlerIndex(), this.selfTarget, [ this.stat ], this.stages)); + } } } @@ -825,19 +858,17 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { this.selfTarget = selfTarget; } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { const hpGateFlat: number = Math.ceil(pokemon.getMaxHp() * this.hpGate); const lastAttackReceived = pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1]; const damageReceived = lastAttackReceived?.damage || 0; + return this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + damageReceived) > hpGateFlat) && !move.hitsSubstitute(attacker, pokemon); + } - if (this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + damageReceived) > hpGateFlat) && !move.hitsSubstitute(attacker, pokemon)) { - if (!simulated) { - globalScene.unshiftPhase(new StatStageChangePhase((this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.stages)); - } - return true; + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + if (!simulated) { + globalScene.unshiftPhase(new StatStageChangePhase((this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.stages)); } - - return false; } } @@ -852,17 +883,16 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr { this.tagType = tagType; } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) { - const tag = globalScene.arena.getTag(this.tagType) as ArenaTrapTag; - if (!globalScene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers) { - if (!simulated) { - globalScene.arena.addTag(this.tagType, 0, undefined, pokemon.id, pokemon.isPlayer() ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER); - } - return true; - } + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + const tag = globalScene.arena.getTag(this.tagType) as ArenaTrapTag; + return (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) + && (!globalScene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers); + } + + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + if (!simulated) { + globalScene.arena.addTag(this.tagType, 0, undefined, pokemon.id, pokemon.isPlayer() ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER); } - return false; } } @@ -876,40 +906,37 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { this.tagType = tagType; } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) { - if (!pokemon.getTag(this.tagType) && !simulated) { - pokemon.addTag(this.tagType, undefined, undefined, pokemon.id); - globalScene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name })); - } - return true; + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + return this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon); + } + + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + if (!pokemon.getTag(this.tagType) && !simulated) { + pokemon.addTag(this.tagType, undefined, undefined, pokemon.id); + globalScene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name })); } - return false; } } export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr { - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): boolean { - if (hitResult < HitResult.NO_EFFECT && !move.hitsSubstitute(attacker, pokemon)) { - if (simulated) { - return true; - } - const type = attacker.getMoveType(move); - const pokemonTypes = pokemon.getTypes(true); - if (pokemonTypes.length !== 1 || pokemonTypes[0] !== type) { - pokemon.summonData.types = [ type ]; - return true; - } - } + private type: PokemonType; - return false; + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + this.type = attacker.getMoveType(move); + const pokemonTypes = pokemon.getTypes(true); + return hitResult < HitResult.NO_EFFECT && !move.hitsSubstitute(attacker, pokemon) && (simulated || pokemonTypes.length !== 1 || pokemonTypes[0] !== this.type); + } + + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): void { + const type = attacker.getMoveType(move); + pokemon.summonData.types = [ type ]; } override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:postDefendTypeChange", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, - typeName: i18next.t(`pokemonInfo:Type.${PokemonType[pokemon.getTypes(true)[0]]}`) + typeName: i18next.t(`pokemonInfo:Type.${PokemonType[this.type]}`) }); } } @@ -923,16 +950,14 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { this.terrainType = terrainType; } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): boolean { - if (hitResult < HitResult.NO_EFFECT && !move.hitsSubstitute(attacker, pokemon)) { - if (simulated) { - return globalScene.arena.terrain?.terrainType !== (this.terrainType || undefined); - } else { - return globalScene.arena.trySetTerrain(this.terrainType, true); - } - } + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + return hitResult < HitResult.NO_EFFECT && !move.hitsSubstitute(attacker, pokemon) && globalScene.arena.canSetTerrain(this.terrainType); + } - return false; + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): void { + if (!simulated) { + globalScene.arena.trySetTerrain(this.terrainType, true); + } } } @@ -941,24 +966,22 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { private effects: StatusEffect[]; constructor(chance: number, ...effects: StatusEffect[]) { - super(); + super(true); this.chance = chance; this.effects = effects; } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status - && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !move.hitsSubstitute(attacker, pokemon)) { - const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; - if (simulated) { - return attacker.canSetStatus(effect, true, false, pokemon); - } else { - return attacker.trySetStatus(effect, true, pokemon); - } - } + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; + return move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status + && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !move.hitsSubstitute(attacker, pokemon) + && attacker.canSetStatus(effect, true, false, pokemon); + } - return false; + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; + attacker.trySetStatus(effect, true, pokemon); } } @@ -967,11 +990,13 @@ export class EffectSporeAbAttr extends PostDefendContactApplyStatusEffectAbAttr super(10, StatusEffect.POISON, StatusEffect.PARALYSIS, StatusEffect.SLEEP); } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (attacker.hasAbility(Abilities.OVERCOAT) || attacker.isOfType(PokemonType.GRASS)) { - return false; - } - return super.applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args); + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + return !(attacker.hasAbility(Abilities.OVERCOAT) || attacker.isOfType(PokemonType.GRASS)) + && super.canApplyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args); + } + + override applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): void { + super.applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args); } } @@ -988,16 +1013,15 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { this.turnCount = turnCount; } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance && !move.hitsSubstitute(attacker, pokemon)) { - if (simulated) { - return attacker.canAddTag(this.tagType); - } else { - return attacker.addTag(this.tagType, this.turnCount, move.id, attacker.id); - } - } + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + return move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance + && !move.hitsSubstitute(attacker, pokemon) && attacker.canAddTag(this.tagType); + } - return false; + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + if (!simulated) { + attacker.addTag(this.tagType, this.turnCount, move.id, attacker.id); + } } } @@ -1012,16 +1036,14 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr { this.stages = stages; } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (move.hitsSubstitute(attacker, pokemon)) { - return false; - } + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + return !move.hitsSubstitute(attacker, pokemon); + } + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { if (!simulated) { globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); } - - return true; } override getCondition(): AbAttrCondition { @@ -1038,15 +1060,14 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { this.damageRatio = damageRatio; } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (!simulated && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) - && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && !move.hitsSubstitute(attacker, pokemon)) { - attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); - attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); - return true; - } + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + return !simulated && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) + && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && !move.hitsSubstitute(attacker, pokemon); + } - return false; + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); + attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); } override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { @@ -1072,19 +1093,16 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { this.turns = turns; } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !move.hitsSubstitute(attacker, pokemon)) { - if (attacker.getTag(BattlerTagType.PERISH_SONG)) { - return false; - } else { - if (!simulated) { - attacker.addTag(BattlerTagType.PERISH_SONG, this.turns); - pokemon.addTag(BattlerTagType.PERISH_SONG, this.turns); - } - return true; - } + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + return (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !move.hitsSubstitute(attacker, pokemon)) + && !attacker.getTag(BattlerTagType.PERISH_SONG); + } + + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + if (!simulated) { + attacker.addTag(BattlerTagType.PERISH_SONG, this.turns); + pokemon.addTag(BattlerTagType.PERISH_SONG, this.turns); } - return false; } override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { @@ -1103,18 +1121,15 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { this.condition = condition; } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (this.condition && !this.condition(pokemon, attacker, move) || move.hitsSubstitute(attacker, pokemon)) { - return false; - } - if (!globalScene.arena.weather?.isImmutable()) { - if (simulated) { - return globalScene.arena.weather?.weatherType !== this.weatherType; - } - return globalScene.arena.trySetWeather(this.weatherType, true); - } + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + return (!(this.condition && !this.condition(pokemon, attacker, move) || move.hitsSubstitute(attacker, pokemon)) + && !globalScene.arena.weather?.isImmutable() && globalScene.arena.canSetWeather(this.weatherType)); + } - return false; + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + if (!simulated) { + globalScene.arena.trySetWeather(this.weatherType, true); + } } } @@ -1123,18 +1138,17 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { super(); } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) - && !attacker.getAbility().hasAttr(UnswappableAbilityAbAttr) && !move.hitsSubstitute(attacker, pokemon)) { - if (!simulated) { - const tempAbility = attacker.getAbility(); - attacker.setTempAbility(pokemon.getAbility()); - pokemon.setTempAbility(tempAbility); - } - return true; - } + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + return move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) + && !attacker.getAbility().hasAttr(UnswappableAbilityAbAttr) && !move.hitsSubstitute(attacker, pokemon); + } - return false; + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, args: any[]): void { + if (!simulated) { + const tempAbility = attacker.getAbility(); + attacker.setTempAbility(pokemon.getAbility()); + pokemon.setTempAbility(tempAbility); + } } override getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { @@ -1150,17 +1164,15 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { this.ability = ability; } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) - && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr) && !move.hitsSubstitute(attacker, pokemon)) { - if (!simulated) { - attacker.setTempAbility(allAbilities[this.ability]); - } + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + return move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) + && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr) && !move.hitsSubstitute(attacker, pokemon); + } - return true; + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + if (!simulated) { + attacker.setTempAbility(allAbilities[this.ability]); } - - return false; } override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { @@ -1182,20 +1194,17 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { this.chance = chance; } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (attacker.getTag(BattlerTagType.DISABLED) === null && !move.hitsSubstitute(attacker, pokemon)) { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) { - if (simulated) { - return true; - } + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + return attacker.getTag(BattlerTagType.DISABLED) === null && !move.hitsSubstitute(attacker, pokemon) + && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance); + } - this.attacker = attacker; - this.move = move; - this.attacker.addTag(BattlerTagType.DISABLED, 4, 0, pokemon.id); - return true; - } + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + if (!simulated) { + this.attacker = attacker; + this.move = move; + this.attacker.addTag(BattlerTagType.DISABLED, 4, 0, pokemon.id); } - return false; } } @@ -1212,19 +1221,28 @@ export class PostStatStageChangeStatStageChangeAbAttr extends PostStatStageChang this.stages = stages; } - applyPostStatStageChange(pokemon: Pokemon, simulated: boolean, statStagesChanged: BattleStat[], stagesChanged: number, selfTarget: boolean, args: any[]): boolean { - if (this.condition(pokemon, statStagesChanged, stagesChanged) && !selfTarget) { - if (!simulated) { - globalScene.unshiftPhase(new StatStageChangePhase((pokemon).getBattlerIndex(), true, this.statsToChange, this.stages)); - } - return true; - } + override canApplyPostStatStageChange(pokemon: Pokemon, simulated: boolean, statStagesChanged: BattleStat[], stagesChanged: integer, selfTarget: boolean, args: any[]): boolean { + return this.condition(pokemon, statStagesChanged, stagesChanged) && !selfTarget; + } - return false; + override applyPostStatStageChange(pokemon: Pokemon, simulated: boolean, statStagesChanged: BattleStat[], stagesChanged: number, selfTarget: boolean, args: any[]): void { + if (!simulated) { + globalScene.unshiftPhase(new StatStageChangePhase((pokemon).getBattlerIndex(), true, this.statsToChange, this.stages)); + } } } export class PreAttackAbAttr extends AbAttr { + canApplyPreAttack( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + defender: Pokemon | null, + move: Move, + args: any[]): boolean { + return true; + } + applyPreAttack( pokemon: Pokemon, passive: boolean, @@ -1232,9 +1250,7 @@ export class PreAttackAbAttr extends AbAttr { defender: Pokemon | null, move: Move, args: any[], - ): boolean { - return false; - } + ): void {} } /** @@ -1246,26 +1262,22 @@ export class MoveEffectChanceMultiplierAbAttr extends AbAttr { private chanceMultiplier: number; constructor(chanceMultiplier: number) { - super(true); + super(false); this.chanceMultiplier = chanceMultiplier; } + + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + const exceptMoves = [ Moves.ORDER_UP, Moves.ELECTRO_SHOT ]; + return !((args[0] as Utils.NumberHolder).value <= 0 || exceptMoves.includes((args[1] as Move).id)); + } + /** * @param args [0]: {@linkcode Utils.NumberHolder} Move additional effect chance. Has to be higher than or equal to 0. * [1]: {@linkcode Moves } Move used by the ability user. */ - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - // Disable showAbility during getTargetBenefitScore - this.showAbility = args[4]; - - const exceptMoves = [ Moves.ORDER_UP, Moves.ELECTRO_SHOT ]; - if ((args[0] as Utils.NumberHolder).value <= 0 || exceptMoves.includes((args[1] as Move).id)) { - return false; - } - + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { (args[0] as Utils.NumberHolder).value *= this.chanceMultiplier; (args[0] as Utils.NumberHolder).value = Math.min((args[0] as Utils.NumberHolder).value, 100); - return true; - } } @@ -1275,25 +1287,22 @@ export class MoveEffectChanceMultiplierAbAttr extends AbAttr { * @see {@linkcode applyPreDefend} */ export class IgnoreMoveEffectsAbAttr extends PreDefendAbAttr { + + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + return (args[0] as Utils.NumberHolder).value > 0; + } + /** * @param args [0]: {@linkcode Utils.NumberHolder} Move additional effect chance. */ - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - - if ((args[0] as Utils.NumberHolder).value <= 0) { - return false; - } - + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): void { (args[0] as Utils.NumberHolder).value = 0; - return true; - } } export class VariableMovePowerAbAttr extends PreAttackAbAttr { - applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { - //const power = args[0] as Utils.NumberHolder; - return false; + override canApplyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + return true; } } @@ -1304,9 +1313,8 @@ export class FieldPreventExplosiveMovesAbAttr extends AbAttr { simulated: boolean, cancelled: Utils.BooleanHolder, args: any[], - ): boolean { + ): void { cancelled.value = true; - return true; } } @@ -1330,6 +1338,11 @@ export class FieldMultiplyStatAbAttr extends AbAttr { this.canStack = canStack; } + canApplyFieldStat(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, args: any[]): boolean { + return this.canStack || !hasApplied.value + && this.stat === stat && checkedPokemon.getAbilityAttrs(FieldMultiplyStatAbAttr).every(attr => (attr as FieldMultiplyStatAbAttr).stat !== stat); + } + /** * applyFieldStat: Tries to multiply a Pokemon's Stat * @param pokemon {@linkcode Pokemon} the Pokemon using this ability @@ -1339,19 +1352,10 @@ export class FieldMultiplyStatAbAttr extends AbAttr { * @param checkedPokemon {@linkcode Pokemon} the Pokemon this ability is targeting * @param hasApplied {@linkcode Utils.BooleanHolder} whether or not another multiplier has been applied to this stat * @param args {any[]} unused - * @returns true if this changed the checked stat, false otherwise. */ - applyFieldStat(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, args: any[]): boolean { - if (!this.canStack && hasApplied.value) { - return false; - } - - if (this.stat === stat && checkedPokemon.getAbilityAttrs(FieldMultiplyStatAbAttr).every(attr => (attr as FieldMultiplyStatAbAttr).stat !== stat)) { - statValue.value *= this.multiplier; - hasApplied.value = true; - return true; - } - return false; + applyFieldStat(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, args: any[]): void { + statValue.value *= this.multiplier; + hasApplied.value = true; } } @@ -1362,22 +1366,21 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr { private powerMultiplier: number, private condition?: PokemonAttackCondition ) { - super(true); + super(false); + } + + override canApplyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean { + return (this.condition && this.condition(pokemon, defender, move)) ?? false; } // TODO: Decouple this into two attributes (type change / power boost) - applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { - if (this.condition && this.condition(pokemon, defender, move)) { - if (args[0] && args[0] instanceof Utils.NumberHolder) { - args[0].value = this.newType; - } - if (args[1] && args[1] instanceof Utils.NumberHolder) { - args[1].value *= this.powerMultiplier; - } - return true; + override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void { + if (args[0] && args[0] instanceof Utils.NumberHolder) { + args[0].value = this.newType; + } + if (args[1] && args[1] instanceof Utils.NumberHolder) { + args[1].value *= this.powerMultiplier; } - - return false; } } @@ -1389,37 +1392,37 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { super(true); } - applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { - if ( - !pokemon.isTerastallized && - move.id !== Moves.STRUGGLE && - /** - * Skip moves that call other moves because these moves generate a following move that will trigger this ability attribute - * @see {@link https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_call_other_moves} - */ - !move.findAttr((attr) => - attr instanceof RandomMovesetMoveAttr || - attr instanceof RandomMoveAttr || - attr instanceof NaturePowerAttr || - attr instanceof CopyMoveAttr - ) - ) { + override canApplyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean { + if (!pokemon.isTerastallized && + move.id !== Moves.STRUGGLE && + /** + * Skip moves that call other moves because these moves generate a following move that will trigger this ability attribute + * @see {@link https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_call_other_moves} + */ + !move.findAttr((attr) => + attr instanceof RandomMovesetMoveAttr || + attr instanceof RandomMoveAttr || + attr instanceof NaturePowerAttr || + attr instanceof CopyMoveAttr)) { const moveType = pokemon.getMoveType(move); - if (pokemon.getTypes().some((t) => t !== moveType)) { - if (!simulated) { - this.moveType = moveType; - pokemon.summonData.types = [ moveType ]; - pokemon.updateInfo(); - } - + this.moveType = moveType; return true; } } - return false; } + override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void { + const moveType = pokemon.getMoveType(move); + + if (!simulated) { + this.moveType = moveType; + pokemon.summonData.types = [ moveType ]; + pokemon.updateInfo(); + } + } + getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { return i18next.t("abilityTriggers:pokemonTypeChange", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), @@ -1441,6 +1444,10 @@ export class AddSecondStrikeAbAttr extends PreAttackAbAttr { this.damageMultiplier = damageMultiplier; } + override canApplyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean { + return move.canBeMultiStrikeEnhanced(pokemon, true); + } + /** * If conditions are met, this doubles the move's hit count (via args[1]) * or multiplies the damage of secondary strikes (via args[2]) @@ -1451,24 +1458,17 @@ export class AddSecondStrikeAbAttr extends PreAttackAbAttr { * @param args Additional arguments: * - `[0]` the number of strikes this move currently has ({@linkcode Utils.NumberHolder}) * - `[1]` the damage multiplier for the current strike ({@linkcode Utils.NumberHolder}) - * @returns */ - applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void { const hitCount = args[0] as Utils.NumberHolder; const multiplier = args[1] as Utils.NumberHolder; - - if (move.canBeMultiStrikeEnhanced(pokemon, true)) { - this.showAbility = !!hitCount?.value; - if (hitCount?.value) { - hitCount.value += 1; - } - - if (multiplier?.value && pokemon.turnData.hitsLeft === 1) { - multiplier.value = this.damageMultiplier; - } - return true; + if (hitCount?.value) { + hitCount.value += 1; + } + + if (multiplier?.value && pokemon.turnData.hitsLeft === 1) { + multiplier.value = this.damageMultiplier; } - return false; } } @@ -1483,11 +1483,15 @@ export class DamageBoostAbAttr extends PreAttackAbAttr { private condition: PokemonAttackCondition; constructor(damageMultiplier: number, condition: PokemonAttackCondition) { - super(true); + super(false); this.damageMultiplier = damageMultiplier; this.condition = condition; } + override canApplyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean { + return this.condition(pokemon, defender, move); + } + /** * * @param pokemon the attacker pokemon @@ -1495,16 +1499,10 @@ export class DamageBoostAbAttr extends PreAttackAbAttr { * @param defender the target pokemon * @param move the move used by the attacker pokemon * @param args Utils.NumberHolder as damage - * @returns true if the function succeeds */ - applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { - if (this.condition(pokemon, defender, move)) { - const power = args[0] as Utils.NumberHolder; - power.value = Utils.toDmgValue(power.value * this.damageMultiplier); - return true; - } - - return false; + override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void { + const power = args[0] as Utils.NumberHolder; + power.value = Utils.toDmgValue(power.value * this.damageMultiplier); } } @@ -1512,26 +1510,24 @@ export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr { private condition: PokemonAttackCondition; private powerMultiplier: number; - constructor(condition: PokemonAttackCondition, powerMultiplier: number, showAbility = true) { + constructor(condition: PokemonAttackCondition, powerMultiplier: number, showAbility: boolean = false) { super(showAbility); this.condition = condition; this.powerMultiplier = powerMultiplier; } - applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { - if (this.condition(pokemon, defender, move)) { - (args[0] as Utils.NumberHolder).value *= this.powerMultiplier; + override canApplyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean { + return this.condition(pokemon, defender, move); + } - return true; - } - - return false; + override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void { + (args[0] as Utils.NumberHolder).value *= this.powerMultiplier; } } export class MoveTypePowerBoostAbAttr extends MovePowerBoostAbAttr { constructor(boostedType: PokemonType, powerMultiplier?: number) { - super((pokemon, defender, move) => pokemon?.getMoveType(move) === boostedType, powerMultiplier || 1.5); + super((pokemon, defender, move) => pokemon?.getMoveType(move) === boostedType, powerMultiplier || 1.5, false); } } @@ -1562,17 +1558,13 @@ export class VariableMovePowerBoostAbAttr extends VariableMovePowerAbAttr { this.mult = mult; } - /** - * @override - */ - applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move, args: any[]): boolean { - const multiplier = this.mult(pokemon, defender, move); - if (multiplier !== 1) { - (args[0] as Utils.NumberHolder).value *= multiplier; - return true; - } + override canApplyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + return this.mult(pokemon, defender, move) !== 1; + } - return false; + override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void { + const multiplier = this.mult(pokemon, defender, move); + (args[0] as Utils.NumberHolder).value *= multiplier; } } @@ -1581,6 +1573,7 @@ export class VariableMovePowerBoostAbAttr extends VariableMovePowerAbAttr { * @extends AbAttr */ export class FieldMovePowerBoostAbAttr extends AbAttr { + // TODO: Refactor this class? It extends from base AbAttr but has preAttack methods and gets called directly instead of going through applyAbAttrsInternal private condition: PokemonAttackCondition; private powerMultiplier: number; @@ -1594,14 +1587,14 @@ export class FieldMovePowerBoostAbAttr extends AbAttr { this.powerMultiplier = powerMultiplier; } - applyPreAttack(pokemon: Pokemon | null, passive: boolean | null, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean { + canApplyPreAttack(pokemon: Pokemon | null, passive: boolean | null, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean { + return true; // logic for this attr is handled in move.ts instead of normally + } + + applyPreAttack(pokemon: Pokemon | null, passive: boolean | null, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): void { if (this.condition(pokemon, defender, move)) { (args[0] as Utils.NumberHolder).value *= this.powerMultiplier; - - return true; } - - return false; } } @@ -1658,21 +1651,25 @@ export class StatMultiplierAbAttr extends AbAttr { this.condition = condition ?? null; } + canApplyStatStage( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + stat: BattleStat, + statValue: Utils.NumberHolder, + args: any[]): boolean { + const move = (args[0] as Move); + return stat === this.stat && (!this.condition || this.condition(pokemon, null, move)); + } + applyStatStage( pokemon: Pokemon, _passive: boolean, simulated: boolean, stat: BattleStat, statValue: Utils.NumberHolder, - args: any[], - ): boolean { - const move = args[0] as Move; - if (stat === this.stat && (!this.condition || this.condition(pokemon, null, move))) { - statValue.value *= this.multiplier; - return true; - } - - return false; + args: any[]): void { + statValue.value *= this.multiplier; } } @@ -1687,10 +1684,23 @@ export class PostAttackAbAttr extends AbAttr { } /** - * Please override {@link applyPostAttackAfterMoveTypeCheck} instead of this method. By default, this method checks that the move used is a damaging attack before + * By default, this method checks that the move used is a damaging attack before * applying the effect of any inherited class. This can be changed by providing a different {@link attackCondition} to the constructor. See {@link ConfusionOnStatusEffectAbAttr} * for an example of an effect that does not require a damaging move. */ + canApplyPostAttack( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + defender: Pokemon, + move: Move, + hitResult: HitResult | null, + args: any[]): boolean { + // When attackRequired is true, we require the move to be an attack move and to deal damage before checking secondary requirements. + // If attackRequired is false, we always defer to the secondary requirements. + return this.attackCondition(pokemon, defender, move); + } + applyPostAttack( pokemon: Pokemon, passive: boolean, @@ -1698,31 +1708,7 @@ export class PostAttackAbAttr extends AbAttr { defender: Pokemon, move: Move, hitResult: HitResult | null, - args: any[], - ): boolean { - // When attackRequired is true, we require the move to be an attack move and to deal damage before checking secondary requirements. - // If attackRequired is false, we always defer to the secondary requirements. - if (this.attackCondition(pokemon, defender, move)) { - return this.applyPostAttackAfterMoveTypeCheck(pokemon, passive, simulated, defender, move, hitResult, args); - } else { - return false; - } - } - - /** - * This method is only called after {@link applyPostAttack} has already been applied. Use this for handling checks specific to the ability in question. - */ - applyPostAttackAfterMoveTypeCheck( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - defender: Pokemon, - move: Move, - hitResult: HitResult | null, - args: any[], - ): boolean { - return false; - } + args: any[]): void {} } /** @@ -1734,6 +1720,18 @@ export class GorillaTacticsAbAttr extends PostAttackAbAttr { super((user, target, move) => true, false); } + override canApplyPostAttack( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + defender: Pokemon, + move: Move, + hitResult: HitResult | null, + args: any[]): boolean { + return super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args) + && simulated || !pokemon.getTag(BattlerTagType.GORILLA_TACTICS); + } + /** * * @param {Pokemon} pokemon the {@linkcode Pokemon} with this ability @@ -1743,32 +1741,24 @@ export class GorillaTacticsAbAttr extends PostAttackAbAttr { * @param move n/a * @param hitResult n/a * @param args n/a - * @returns `true` if the ability is applied */ - override applyPostAttackAfterMoveTypeCheck( + override applyPostAttack( pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, - args: any[], - ): boolean { - if (simulated) { - return simulated; + args: any[]): void { + if (!simulated) { + pokemon.addTag(BattlerTagType.GORILLA_TACTICS); } - - if (pokemon.getTag(BattlerTagType.GORILLA_TACTICS)) { - return false; - } - - pokemon.addTag(BattlerTagType.GORILLA_TACTICS); - return true; } } export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { private stealCondition: PokemonAttackCondition | null; + private stolenItem?: PokemonHeldItemModifier; constructor(stealCondition?: PokemonAttackCondition) { super(); @@ -1776,7 +1766,34 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { this.stealCondition = stealCondition ?? null; } - override applyPostAttackAfterMoveTypeCheck( + override canApplyPostAttack( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + defender: Pokemon, + move: Move, + hitResult: HitResult, + args: any[]): boolean { + if ( + super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args) && + !simulated && + hitResult < HitResult.NO_EFFECT && + (!this.stealCondition || this.stealCondition(pokemon, defender, move)) + ) { + const heldItems = this.getTargetHeldItems(defender).filter((i) => i.isTransferable); + if (heldItems.length) { + // Ensure that the stolen item in testing is the same as when the effect is applied + this.stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; + if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) { + return true; + } + } + } + this.stolenItem = undefined; + return false; + } + + override applyPostAttack( pokemon: Pokemon, passive: boolean, simulated: boolean, @@ -1784,28 +1801,21 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { move: Move, hitResult: HitResult, args: any[], - ): boolean { - if ( - !simulated && - hitResult < HitResult.NO_EFFECT && - (!this.stealCondition || this.stealCondition(pokemon, defender, move)) - ) { - const heldItems = this.getTargetHeldItems(defender).filter((i) => i.isTransferable); - if (heldItems.length) { - const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; - if (globalScene.tryTransferHeldItemModifier(stolenItem, pokemon, false)) { - globalScene.queueMessage( - i18next.t("abilityTriggers:postAttackStealHeldItem", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - defenderName: defender.name, - stolenItemType: stolenItem.type.name, - }), - ); - return true; - } - } + ): void { + const heldItems = this.getTargetHeldItems(defender).filter((i) => i.isTransferable); + if (!this.stolenItem) { + this.stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; } - return false; + if (globalScene.tryTransferHeldItemModifier(this.stolenItem, pokemon, false)) { + globalScene.queueMessage( + i18next.t("abilityTriggers:postAttackStealHeldItem", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + defenderName: defender.name, + stolenItemType: this.stolenItem.type.name, + }), + ); + } + this.stolenItem = undefined; } getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] { @@ -1827,18 +1837,23 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr { this.effects = effects; } - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (pokemon !== attacker && move.hitsSubstitute(attacker, pokemon)) { - return false; - } - - /**Status inflicted by abilities post attacking are also considered additional effects.*/ - if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && !simulated && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) { + override canApplyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + if ( + super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) + && !(pokemon !== attacker && move.hitsSubstitute(attacker, pokemon)) + && (simulated || !attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker + && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) + ) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; - return attacker.trySetStatus(effect, true, pokemon); + return simulated || attacker.canSetStatus(effect, true, false, pokemon); } - return simulated; + return false; + } + + applyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): void { + const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; + attacker.trySetStatus(effect, true, pokemon); } } @@ -1855,26 +1870,32 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { constructor(contactRequired: boolean, chance: (user: Pokemon, target: Pokemon, move: Move) => number, ...effects: BattlerTagType[]) { - super(); + super(undefined, false); this.contactRequired = contactRequired; this.chance = chance; this.effects = effects; } - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + override canApplyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { /**Battler tags inflicted by abilities post attacking are also considered additional effects.*/ - if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status) { - const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; - return simulated || attacker.addTag(effect); - } + return super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) && + !attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && + (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && + pokemon.randSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status; + } - return false; + override applyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): void { + if (!simulated) { + const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; + attacker.addTag(effect); + } } } export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { private condition?: PokemonDefendCondition; + private stolenItem?: PokemonHeldItemModifier; constructor(condition?: PokemonDefendCondition) { super(); @@ -1882,6 +1903,24 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { this.condition = condition; } + override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + if ( + !simulated && + hitResult < HitResult.NO_EFFECT && + (!this.condition || this.condition(pokemon, attacker, move)) && + !move.hitsSubstitute(attacker, pokemon) + ) { + const heldItems = this.getTargetHeldItems(attacker).filter((i) => i.isTransferable); + if (heldItems.length) { + this.stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; + if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) { + return true; + } + } + } + return false; + } + override applyPostDefend( pokemon: Pokemon, _passive: boolean, @@ -1890,29 +1929,22 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { move: Move, hitResult: HitResult, _args: any[], - ): boolean { - if ( - !simulated && - hitResult < HitResult.NO_EFFECT && - (!this.condition || this.condition(pokemon, attacker, move)) && - !move.hitsSubstitute(attacker, pokemon) - ) { - const heldItems = this.getTargetHeldItems(attacker).filter((i) => i.isTransferable); - if (heldItems.length) { - const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; - if (globalScene.tryTransferHeldItemModifier(stolenItem, pokemon, false)) { - globalScene.queueMessage( - i18next.t("abilityTriggers:postDefendStealHeldItem", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - attackerName: attacker.name, - stolenItemType: stolenItem.type.name, - }), - ); - return true; - } - } + ): void { + + const heldItems = this.getTargetHeldItems(attacker).filter((i) => i.isTransferable); + if (!this.stolenItem) { + this.stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; } - return false; + if (globalScene.tryTransferHeldItemModifier(this.stolenItem, pokemon, false)) { + globalScene.queueMessage( + i18next.t("abilityTriggers:postDefendStealHeldItem", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + attackerName: attacker.name, + stolenItemType: this.stolenItem.type.name, + }), + ); + } + this.stolenItem = undefined; } getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] { @@ -1926,6 +1958,16 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { * @see {@linkcode applyPostSetStatus()}. */ export class PostSetStatusAbAttr extends AbAttr { + canApplyPostSetStatus( + pokemon: Pokemon, + sourcePokemon: Pokemon | null = null, + passive: boolean, + effect: StatusEffect, + simulated: boolean, + rgs: any[]): boolean { + return true; + } + /** * Does nothing after a status condition is set. * @param pokemon {@linkcode Pokemon} that status condition was set on. @@ -1933,7 +1975,6 @@ export class PostSetStatusAbAttr extends AbAttr { * @param passive Whether this ability is a passive. * @param effect {@linkcode StatusEffect} that was set. * @param args Set of unique arguments needed by this attribute. - * @returns `true` if application of the ability succeeds. */ applyPostSetStatus( pokemon: Pokemon, @@ -1942,9 +1983,7 @@ export class PostSetStatusAbAttr extends AbAttr { effect: StatusEffect, simulated: boolean, args: any[], - ): boolean { - return false; - } + ): void {} } /** @@ -1953,17 +1992,7 @@ export class PostSetStatusAbAttr extends AbAttr { * ability attribute. For Synchronize ability. */ export class SynchronizeStatusAbAttr extends PostSetStatusAbAttr { - /** - * If the `StatusEffect` that was set is Burn, Paralysis, Poison, or Toxic, and the status - * was set by a source Pokemon, set the source Pokemon's status to the same `StatusEffect`. - * @param pokemon {@linkcode Pokemon} that status condition was set on. - * @param sourcePokemon {@linkcode Pokemon} that that set the status condition. Is null if status was not set by a Pokemon. - * @param passive Whether this ability is a passive. - * @param effect {@linkcode StatusEffect} that was set. - * @param args Set of unique arguments needed by this attribute. - * @returns `true` if application of the ability succeeds. - */ - override applyPostSetStatus(pokemon: Pokemon, sourcePokemon: Pokemon | null = null, passive: boolean, effect: StatusEffect, simulated: boolean, args: any[]): boolean { + override canApplyPostSetStatus(pokemon: Pokemon, sourcePokemon: (Pokemon | null) | undefined, passive: boolean, effect: StatusEffect, simulated: boolean, args: any[]): boolean { /** Synchronizable statuses */ const syncStatuses = new Set([ StatusEffect.BURN, @@ -1972,21 +2001,32 @@ export class SynchronizeStatusAbAttr extends PostSetStatusAbAttr { StatusEffect.TOXIC ]); - if (sourcePokemon && syncStatuses.has(effect)) { - if (!simulated) { - sourcePokemon.trySetStatus(effect, true, pokemon); - } - return true; - } + // synchronize does not need to check canSetStatus because the ability shows even if it fails to set the status + return ((sourcePokemon ?? false) && syncStatuses.has(effect)); + } - return false; + /** + * If the `StatusEffect` that was set is Burn, Paralysis, Poison, or Toxic, and the status + * was set by a source Pokemon, set the source Pokemon's status to the same `StatusEffect`. + * @param pokemon {@linkcode Pokemon} that status condition was set on. + * @param sourcePokemon {@linkcode Pokemon} that that set the status condition. Is null if status was not set by a Pokemon. + * @param passive Whether this ability is a passive. + * @param effect {@linkcode StatusEffect} that was set. + * @param args Set of unique arguments needed by this attribute. + */ + override applyPostSetStatus(pokemon: Pokemon, sourcePokemon: Pokemon | null = null, passive: boolean, effect: StatusEffect, simulated: boolean, args: any[]): void { + if (!simulated && sourcePokemon) { + sourcePokemon.trySetStatus(effect, true, pokemon); + } } } export class PostVictoryAbAttr extends AbAttr { - applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return false; + canApplyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return true; } + + applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} } class PostVictoryStatStageChangeAbAttr extends PostVictoryAbAttr { @@ -2000,12 +2040,11 @@ class PostVictoryStatStageChangeAbAttr extends PostVictoryAbAttr { this.stages = stages; } - applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; if (!simulated) { globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ stat ], this.stages)); } - return true; } } @@ -2018,23 +2057,24 @@ export class PostVictoryFormChangeAbAttr extends PostVictoryAbAttr { this.formFunc = formFunc; } - applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const formIndex = this.formFunc(pokemon); - if (formIndex !== pokemon.formIndex) { - if (!simulated) { - globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); - } - return true; - } + return formIndex !== pokemon.formIndex; + } - return false; + override applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + if (!simulated) { + globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); + } } } export class PostKnockOutAbAttr extends AbAttr { - applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean { - return false; + canApplyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean { + return true; } + + applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): void {} } export class PostKnockOutStatStageChangeAbAttr extends PostKnockOutAbAttr { @@ -2048,12 +2088,11 @@ export class PostKnockOutStatStageChangeAbAttr extends PostKnockOutAbAttr { this.stages = stages; } - applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean { + override applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): void { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; if (!simulated) { globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ stat ], this.stages)); } - return true; } } @@ -2062,16 +2101,15 @@ export class CopyFaintedAllyAbilityAbAttr extends PostKnockOutAbAttr { super(); } - applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean { - if (pokemon.isPlayer() === knockedOut.isPlayer() && !knockedOut.getAbility().hasAttr(UncopiableAbilityAbAttr)) { - if (!simulated) { - globalScene.queueMessage(i18next.t("abilityTriggers:copyFaintedAllyAbility", { pokemonNameWithAffix: getPokemonNameWithAffix(knockedOut), abilityName: allAbilities[knockedOut.getAbility().id].name })); - pokemon.setTempAbility(knockedOut.getAbility()); - } - return true; - } + override canApplyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean { + return pokemon.isPlayer() === knockedOut.isPlayer() && !knockedOut.getAbility().hasAttr(UncopiableAbilityAbAttr); + } - return false; + override applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): void { + if (!simulated) { + pokemon.setTempAbility(knockedOut.getAbility()); + globalScene.queueMessage(i18next.t("abilityTriggers:copyFaintedAllyAbility", { pokemonNameWithAffix: getPokemonNameWithAffix(knockedOut), abilityName: allAbilities[knockedOut.getAbility().id].name })); + } } } @@ -2088,6 +2126,10 @@ export class IgnoreOpponentStatStagesAbAttr extends AbAttr { this.stats = stats ?? BATTLE_STATS; } + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return this.stats.includes(args[0]); + } + /** * Modifies a BooleanHolder and returns the result to see if a stat is ignored or not * @param _pokemon n/a @@ -2095,14 +2137,9 @@ export class IgnoreOpponentStatStagesAbAttr extends AbAttr { * @param simulated n/a * @param _cancelled n/a * @param args A BooleanHolder that represents whether or not to ignore a stat's stat changes - * @returns true if the stat is ignored, false otherwise */ - apply(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _cancelled: Utils.BooleanHolder, args: any[]) { - if (this.stats.includes(args[0])) { - (args[1] as Utils.BooleanHolder).value = true; - return true; - } - return false; + override apply(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _cancelled: Utils.BooleanHolder, args: any[]): void { + (args[1] as Utils.BooleanHolder).value = true; } } @@ -2111,9 +2148,8 @@ export class IntimidateImmunityAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { cancelled.value = true; - return true; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { @@ -2136,12 +2172,11 @@ export class PostIntimidateStatStageChangeAbAttr extends AbAttr { this.overwrites = !!overwrites; } - apply(pokemon: Pokemon, passive: boolean, simulated:boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated:boolean, cancelled: Utils.BooleanHolder, args: any[]): void { if (!simulated) { globalScene.pushPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), false, this.stats, this.stages)); } cancelled.value = this.overwrites; - return true; } } @@ -2165,16 +2200,17 @@ export class PostSummonAbAttr extends AbAttr { return this.activateOnGain; } + canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return true; + } + /** * Applies ability post summon (after switching in) * @param pokemon {@linkcode Pokemon} with this ability * @param passive Whether this ability is a passive * @param args Set of unique arguments needed by this attribute - * @returns true if application of the ability succeeds */ - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return false; - } + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} } /** @@ -2192,13 +2228,16 @@ export class PostSummonRemoveArenaTagAbAttr extends PostSummonAbAttr { this.arenaTags = arenaTags; } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return globalScene.arena.tags.some(tag => this.arenaTags.includes(tag.tagType)); + } + + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (!simulated) { for (const arenaTag of this.arenaTags) { globalScene.arena.removeTag(arenaTag); } } - return true; } } @@ -2213,20 +2252,19 @@ export class PostSummonAddArenaTagAbAttr extends PostSummonAbAttr { private sourceId: number; - constructor(tagType: ArenaTagType, turnCount: number, side?: ArenaTagSide, quiet?: boolean) { - super(false); + constructor(showAbility: boolean, tagType: ArenaTagType, turnCount: number, side?: ArenaTagSide, quiet?: boolean) { + super(showAbility); this.tagType = tagType; this.turnCount = turnCount; this.side = side; this.quiet = quiet; } - public override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + public override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { this.sourceId = pokemon.id; if (!simulated) { globalScene.arena.addTag(this.tagType, this.turnCount, undefined, this.sourceId, this.side, this.quiet); } - return true; } } @@ -2239,12 +2277,10 @@ export class PostSummonMessageAbAttr extends PostSummonAbAttr { this.messageFunc = messageFunc; } - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (!simulated) { globalScene.queueMessage(this.messageFunc(pokemon)); } - - return true; } } @@ -2258,12 +2294,10 @@ export class PostSummonUnnamedMessageAbAttr extends PostSummonAbAttr { this.message = message; } - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (!simulated) { globalScene.queueMessage(this.message); } - - return true; } } @@ -2278,11 +2312,13 @@ export class PostSummonAddBattlerTagAbAttr extends PostSummonAbAttr { this.turnCount = turnCount; } - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - if (simulated) { - return pokemon.canAddTag(this.tagType); - } else { - return pokemon.addTag(this.tagType, this.turnCount); + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return pokemon.canAddTag(this.tagType); + } + + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + if (!simulated) { + pokemon.addTag(this.tagType, this.turnCount); } } } @@ -2294,7 +2330,7 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { private intimidate: boolean; constructor(stats: BattleStat[], stages: number, selfTarget?: boolean, intimidate?: boolean) { - super(false); + super(true); this.stats = stats; this.stages = stages; @@ -2302,33 +2338,31 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { this.intimidate = !!intimidate; } - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (simulated) { - return true; + return; } - queueShowAbility(pokemon, passive); // TODO: Better solution than manually showing the ability here if (this.selfTarget) { // we unshift the StatStageChangePhase to put it right after the showAbility and not at the end of the // phase list (which could be after CommandPhase for example) globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages)); - return true; - } - for (const opponent of pokemon.getOpponents()) { - const cancelled = new Utils.BooleanHolder(false); - if (this.intimidate) { - applyAbAttrs(IntimidateImmunityAbAttr, opponent, cancelled, simulated); - applyAbAttrs(PostIntimidateStatStageChangeAbAttr, opponent, cancelled, simulated); + } else { + for (const opponent of pokemon.getOpponents()) { + const cancelled = new Utils.BooleanHolder(false); + if (this.intimidate) { + applyAbAttrs(IntimidateImmunityAbAttr, opponent, cancelled, simulated); + applyAbAttrs(PostIntimidateStatStageChangeAbAttr, opponent, cancelled, simulated); - if (opponent.getTag(BattlerTagType.SUBSTITUTE)) { - cancelled.value = true; + if (opponent.getTag(BattlerTagType.SUBSTITUTE)) { + cancelled.value = true; + } + } + if (!cancelled.value) { + globalScene.unshiftPhase(new StatStageChangePhase(opponent.getBattlerIndex(), false, this.stats, this.stages)); } } - if (!cancelled.value) { - globalScene.unshiftPhase(new StatStageChangePhase(opponent.getBattlerIndex(), false, this.stats, this.stages)); - } } - return true; } } @@ -2343,18 +2377,16 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { this.showAnim = showAnim; } - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return pokemon.getAlly()?.isActive(true); + } + + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { const target = pokemon.getAlly(); - if (target?.isActive(true)) { - if (!simulated) { - globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), - Utils.toDmgValue(pokemon.getMaxHp() / this.healRatio), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim)); - } - - return true; + if (!simulated) { + globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), + Utils.toDmgValue(pokemon.getMaxHp() / this.healRatio), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim)); } - - return false; } } @@ -2371,21 +2403,19 @@ export class PostSummonClearAllyStatStagesAbAttr extends PostSummonAbAttr { super(); } - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const target = pokemon.getAlly(); - if (target?.isActive(true)) { - if (!simulated) { - for (const s of BATTLE_STATS) { - target.setStatStage(s, 0); - } + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return pokemon.getAlly()?.isActive(true); + } - globalScene.queueMessage(i18next.t("abilityTriggers:postSummonClearAllyStats", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + const target = pokemon.getAlly(); + if (!simulated) { + for (const s of BATTLE_STATS) { + target.setStatStage(s, 0); } - return true; + globalScene.queueMessage(i18next.t("abilityTriggers:postSummonClearAllyStats", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); } - - return false; } } @@ -2402,15 +2432,7 @@ export class DownloadAbAttr extends PostSummonAbAttr { private enemyCountTally: number; private stats: BattleStat[]; - /** - * Checks to see if it is the opening turn (starting a new game), if so, Download won't work. This is because Download takes into account - * vitamins and items, so it needs to use the Stat and the stat alone. - * @param {Pokemon} pokemon Pokemon that is using the move, as well as seeing the opposing pokemon. - * @param {boolean} passive N/A - * @param {any[]} args N/A - * @returns Returns true if ability is used successful, false if not. - */ - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { this.enemyDef = 0; this.enemySpDef = 0; this.enemyCountTally = 0; @@ -2422,21 +2444,26 @@ export class DownloadAbAttr extends PostSummonAbAttr { } this.enemyDef = Math.round(this.enemyDef / this.enemyCountTally); this.enemySpDef = Math.round(this.enemySpDef / this.enemyCountTally); + return this.enemyDef > 0 && this.enemySpDef > 0; + } + /** + * Checks to see if it is the opening turn (starting a new game), if so, Download won't work. This is because Download takes into account + * vitamins and items, so it needs to use the Stat and the stat alone. + * @param {Pokemon} pokemon Pokemon that is using the move, as well as seeing the opposing pokemon. + * @param {boolean} passive N/A + * @param {any[]} args N/A + */ + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (this.enemyDef < this.enemySpDef) { this.stats = [ Stat.ATK ]; } else { this.stats = [ Stat.SPATK ]; } - if (this.enemyDef > 0 && this.enemySpDef > 0) { // only activate if there's actually an enemy to download from - if (!simulated) { - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), false, this.stats, 1)); - } - return true; + if (!simulated) { + globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), false, this.stats, 1)); } - - return false; } } @@ -2449,18 +2476,17 @@ export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr { this.weatherType = weatherType; } - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - if ((this.weatherType === WeatherType.HEAVY_RAIN || + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + const weatherReplaceable = (this.weatherType === WeatherType.HEAVY_RAIN || this.weatherType === WeatherType.HARSH_SUN || - this.weatherType === WeatherType.STRONG_WINDS) || !globalScene.arena.weather?.isImmutable()) { - if (simulated) { - return globalScene.arena.weather?.weatherType !== this.weatherType; - } else { - return globalScene.arena.trySetWeather(this.weatherType, true); - } - } + this.weatherType === WeatherType.STRONG_WINDS) || !globalScene.arena.weather?.isImmutable(); + return weatherReplaceable && globalScene.arena.canSetWeather(this.weatherType); + } - return false; + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + if (!simulated) { + globalScene.arena.trySetWeather(this.weatherType, true); + } } } @@ -2473,11 +2499,13 @@ export class PostSummonTerrainChangeAbAttr extends PostSummonAbAttr { this.terrainType = terrainType; } - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - if (simulated) { - return globalScene.arena.terrain?.terrainType !== this.terrainType; - } else { - return globalScene.arena.trySetTerrain(this.terrainType, true); + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return globalScene.arena.canSetTerrain(this.terrainType); + } + + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + if (!simulated) { + globalScene.arena.trySetTerrain(this.terrainType, true); } } } @@ -2491,13 +2519,14 @@ export class PostSummonFormChangeAbAttr extends PostSummonAbAttr { this.formFunc = formFunc; } - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const formIndex = this.formFunc(pokemon); - if (formIndex !== pokemon.formIndex) { - return simulated || globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); - } + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return this.formFunc(pokemon) !== pokemon.formIndex; + } - return false; + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + if (!simulated) { + globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); + } } } @@ -2506,7 +2535,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { private target: Pokemon; private targetAbilityName: string; - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const targets = pokemon.getOpponents(); if (!targets.length) { return false; @@ -2527,15 +2556,17 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { return false; } + this.target = target!; + this.targetAbilityName = allAbilities[target!.getAbility().id].name; + return true; + } + + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (!simulated) { - this.target = target!; - this.targetAbilityName = allAbilities[target!.getAbility().id].name; - pokemon.setTempAbility(target!.getAbility()); - setAbilityRevealed(target!); + pokemon.setTempAbility(this.target!.getAbility()); + setAbilityRevealed(this.target!); pokemon.updateInfo(); } - - return true; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { @@ -2562,22 +2593,22 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt this.statusEffect = statusEffect; } + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + const party = pokemon instanceof PlayerPokemon ? globalScene.getPlayerField() : globalScene.getEnemyField(); + return party.filter(p => p.isAllowedInBattle()).length > 0; + } + /** * Removes supplied status effect from the user's field when user of the ability is summoned. * * @param pokemon - The Pokémon that triggered the ability. * @param passive - n/a * @param args - n/a - * @returns A boolean that resolves to a boolean indicating the result of the ability application. */ - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { const party = pokemon instanceof PlayerPokemon ? globalScene.getPlayerField() : globalScene.getEnemyField(); const allowedParty = party.filter(p => p.isAllowedInBattle()); - if (allowedParty.length < 1) { - return false; - } - if (!simulated) { for (const pokemon of allowedParty) { if (pokemon.status && this.statusEffect.includes(pokemon.status.effect)) { @@ -2587,14 +2618,13 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt } } } - return true; } } /** Attempt to copy the stat changes on an ally pokemon */ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { if (!globalScene.currentBattle.double) { return false; } @@ -2604,14 +2634,17 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { return false; } + return true; + } + + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + const ally = pokemon.getAlly(); if (!simulated) { for (const s of BATTLE_STATS) { pokemon.setStatStage(s, ally.getStatStage(s)); } pokemon.updateInfo(); } - - return true; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { @@ -2630,12 +2663,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { super(true, false); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): boolean { - const targets = pokemon.getOpponents(); - if (simulated || !targets.length) { - return simulated; - } - + private getTarget(targets: Pokemon[]): Pokemon { let target: Pokemon; if (targets.length > 1) { globalScene.executeWithSeedOffset(() => { @@ -2655,21 +2683,28 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { } target = target!; + return target; + } + + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + const targets = pokemon.getOpponents(); + if (simulated || !targets.length) { + return simulated; + } + // transforming from or into fusion pokemon causes various problems (including crashes and save corruption) - if (target.fusionSpecies || pokemon.fusionSpecies) { + if (this.getTarget(targets).fusionSpecies || pokemon.fusionSpecies) { return false; } + return true; + } + + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + const target = this.getTarget(pokemon.getOpponents()); + globalScene.unshiftPhase(new PokemonTransformPhase(pokemon.getBattlerIndex(), target.getBattlerIndex(), true)); - globalScene.queueMessage( - i18next.t("abilityTriggers:postSummonTransform", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - targetName: target.name, - }), - ); - - return true; } } @@ -2679,25 +2714,20 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { * @extends PostSummonAbAttr */ export class PostSummonWeatherSuppressedFormChangeAbAttr extends PostSummonAbAttr { + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return getPokemonWithWeatherBasedForms().length > 0; + } + /** * Triggers {@linkcode Arena.triggerWeatherBasedFormChangesToNormal | triggerWeatherBasedFormChangesToNormal} * @param {Pokemon} pokemon the Pokemon with this ability * @param passive n/a * @param args n/a - * @returns whether a Pokemon was reverted to its normal form */ - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]) { - const pokemonToTransform = getPokemonWithWeatherBasedForms(); - - if (pokemonToTransform.length < 1) { - return false; - } - + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (!simulated) { globalScene.arena.triggerWeatherBasedFormChangesToNormal(); } - - return true; } } @@ -2715,6 +2745,12 @@ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr { this.ability = ability; } + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + const isCastformWithForecast = (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST); + const isCherrimWithFlowerGift = (pokemon.species.speciesId === Species.CHERRIM && this.ability === Abilities.FLOWER_GIFT); + return isCastformWithForecast || isCherrimWithFlowerGift; + } + /** * Calls the {@linkcode BattleScene.triggerPokemonFormChange | triggerPokemonFormChange} for both * {@linkcode SpeciesFormChange.SpeciesFormChangeWeatherTrigger | SpeciesFormChangeWeatherTrigger} and @@ -2723,23 +2759,13 @@ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr { * @param {Pokemon} pokemon the Pokemon with this ability * @param passive n/a * @param args n/a - * @returns whether the form change was triggered */ - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const isCastformWithForecast = (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST); - const isCherrimWithFlowerGift = (pokemon.species.speciesId === Species.CHERRIM && this.ability === Abilities.FLOWER_GIFT); - - if (isCastformWithForecast || isCherrimWithFlowerGift) { - if (simulated) { - return simulated; - } - + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeWeatherTrigger); globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeRevertWeatherFormTrigger); - queueShowAbility(pokemon, passive); - return true; + globalScene.queueAbilityDisplay(pokemon, passive, true); } - return false; } } @@ -2754,53 +2780,55 @@ export class CommanderAbAttr extends AbAttr { super(true); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: null, args: any[]): boolean { - // TODO: Should this work with X + Dondozo fusions? - if (globalScene.currentBattle?.double && pokemon.getAlly()?.species.speciesId === Species.DONDOZO) { - // If the ally Dondozo is fainted or was previously "commanded" by - // another Pokemon, this effect cannot apply. - if (pokemon.getAlly().isFainted() || pokemon.getAlly().getTag(BattlerTagType.COMMANDED)) { - return false; - } + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + // If the ally Dondozo is fainted or was previously "commanded" by + // another Pokemon, this effect cannot apply. - if (!simulated) { - // Lapse the source's semi-invulnerable tags (to avoid visual inconsistencies) - pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); - // Play an animation of the source jumping into the ally Dondozo's mouth - globalScene.triggerPokemonBattleAnim(pokemon, PokemonAnimType.COMMANDER_APPLY); - // Apply boosts from this effect to the ally Dondozo - pokemon.getAlly().addTag(BattlerTagType.COMMANDED, 0, Moves.NONE, pokemon.id); - // Cancel the source Pokemon's next move (if a move is queued) - globalScene.tryRemovePhase((phase) => phase instanceof MovePhase && phase.pokemon === pokemon); - } - return true; + // TODO: Should this work with X + Dondozo fusions? + return !(pokemon.getAlly().isFainted() || pokemon.getAlly().getTag(BattlerTagType.COMMANDED)) + && globalScene.currentBattle?.double && pokemon.getAlly()?.species.speciesId === Species.DONDOZO; + } + + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: null, args: any[]): void { + if (!simulated) { + // Lapse the source's semi-invulnerable tags (to avoid visual inconsistencies) + pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); + // Play an animation of the source jumping into the ally Dondozo's mouth + globalScene.triggerPokemonBattleAnim(pokemon, PokemonAnimType.COMMANDER_APPLY); + // Apply boosts from this effect to the ally Dondozo + pokemon.getAlly().addTag(BattlerTagType.COMMANDED, 0, Moves.NONE, pokemon.id); + // Cancel the source Pokemon's next move (if a move is queued) + globalScene.tryRemovePhase((phase) => phase instanceof MovePhase && phase.pokemon === pokemon); } - return false; } } export class PreSwitchOutAbAttr extends AbAttr { - constructor() { - super(true); + constructor(showAbility: boolean = true) { + super(showAbility); } - applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return false; + canApplyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return true; } + + applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} } export class PreSwitchOutResetStatusAbAttr extends PreSwitchOutAbAttr { - override applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - if (pokemon.status) { - if (!simulated) { - pokemon.resetStatus(); - pokemon.updateInfo(); - } + constructor() { + super(false); + } - return true; + override canApplyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return !Utils.isNullOrUndefined(pokemon.status); + } + + override applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + if (!simulated) { + pokemon.resetStatus(); + pokemon.updateInfo(); } - - return false; } } @@ -2869,18 +2897,16 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { } export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr { - override applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - if (!pokemon.isFullHp()) { - if (!simulated) { - const healAmount = Utils.toDmgValue(pokemon.getMaxHp() * 0.33); - pokemon.heal(healAmount); - pokemon.updateInfo(); - } + override canApplyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return !pokemon.isFullHp(); + } - return true; + override applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + if (!simulated) { + const healAmount = Utils.toDmgValue(pokemon.getMaxHp() * 0.33); + pokemon.heal(healAmount); + pokemon.updateInfo(); } - - return false; } } @@ -2898,80 +2924,73 @@ export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr { this.formFunc = formFunc; } + override canApplyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return this.formFunc(pokemon) !== pokemon.formIndex; + } + /** * On switch out, trigger the form change to the one defined in the ability * @param pokemon The pokemon switching out and changing form {@linkcode Pokemon} * @param passive N/A * @param args N/A - * @returns true if the form change was successful */ - override applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const formIndex = this.formFunc(pokemon); - if (formIndex !== pokemon.formIndex) { - if (!simulated) { - globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); - } - return true; + override applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + if (!simulated) { + globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } - - return false; } } export class PreLeaveFieldAbAttr extends AbAttr { - applyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return false; + canApplyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return true; } + + applyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} } /** * Clears Desolate Land/Primordial Sea/Delta Stream upon the Pokemon switching out. */ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr { - /** - * @param pokemon The {@linkcode Pokemon} with the ability - * @param passive N/A - * @param args N/A - * @returns Returns `true` if the weather clears, otherwise `false`. - */ - applyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const weatherType = globalScene.arena.weather?.weatherType; - let turnOffWeather = false; + override canApplyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + const weatherType = globalScene.arena.weather?.weatherType; // Clear weather only if user's ability matches the weather and no other pokemon has the ability. switch (weatherType) { case (WeatherType.HARSH_SUN): if (pokemon.hasAbility(Abilities.DESOLATE_LAND) && globalScene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(Abilities.DESOLATE_LAND)).length === 0) { - turnOffWeather = true; + return true; } break; case (WeatherType.HEAVY_RAIN): if (pokemon.hasAbility(Abilities.PRIMORDIAL_SEA) && globalScene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(Abilities.PRIMORDIAL_SEA)).length === 0) { - turnOffWeather = true; + return true; } break; case (WeatherType.STRONG_WINDS): if (pokemon.hasAbility(Abilities.DELTA_STREAM) && globalScene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(Abilities.DELTA_STREAM)).length === 0) { - turnOffWeather = true; + return true; } break; } - - if (simulated) { - return turnOffWeather; - } - - if (turnOffWeather) { - globalScene.arena.trySetWeather(WeatherType.NONE, false); - return true; - } - return false; } + + /** + * @param pokemon The {@linkcode Pokemon} with the ability + * @param passive N/A + * @param args N/A + */ + override applyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + if (!simulated) { + globalScene.arena.trySetWeather(WeatherType.NONE, false); + } + } } /** @@ -2982,19 +3001,27 @@ export class PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr extends PreLeaveFi super(false); } - public override applyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - if (!simulated) { - const suppressTag = globalScene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag; - if (suppressTag) { - suppressTag.onSourceLeave(globalScene.arena); - return true; - } - } - return simulated; + public override canApplyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return !!globalScene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS); + } + + public override applyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + const suppressTag = globalScene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag; + suppressTag.onSourceLeave(globalScene.arena); } } export class PreStatStageChangeAbAttr extends AbAttr { + canApplyPreStatStageChange( + pokemon: Pokemon | null, + passive: boolean, + simulated: boolean, + stat: BattleStat, + cancelled: Utils.BooleanHolder, + args: any[]): boolean { + return true; + } + applyPreStatStageChange( pokemon: Pokemon | null, passive: boolean, @@ -3002,9 +3029,7 @@ export class PreStatStageChangeAbAttr extends AbAttr { stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[], - ): boolean { - return false; - } + ): void {} } /** @@ -3023,9 +3048,8 @@ export class ReflectStatStageChangeAbAttr extends PreStatStageChangeAbAttr { * @param stat the {@linkcode BattleStat} being affected * @param cancelled The {@linkcode Utils.BooleanHolder} that will be set to true due to reflection * @param args - * @returns true because it reflects any stat being lowered */ - applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): void { const attacker: Pokemon = args[0]; const stages = args[1]; this.reflectedStat = stat; @@ -3033,7 +3057,6 @@ export class ReflectStatStageChangeAbAttr extends PreStatStageChangeAbAttr { globalScene.unshiftPhase(new StatStageChangePhase(attacker.getBattlerIndex(), false, [ stat ], stages, true, false, true, null, true)); } cancelled.value = true; - return true; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { @@ -3058,6 +3081,10 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr { this.protectedStat = protectedStat; } + override canApplyPreStatStageChange(pokemon: Pokemon | null, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { + return Utils.isNullOrUndefined(this.protectedStat) || stat === this.protectedStat; + } + /** * Apply the {@linkcode ProtectedStatAbAttr} to an interaction * @param _pokemon @@ -3066,15 +3093,9 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr { * @param stat the {@linkcode BattleStat} being affected * @param cancelled The {@linkcode Utils.BooleanHolder} that will be set to true if the stat is protected * @param _args - * @returns true if the stat is protected, false otherwise */ - applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, _args: any[]): boolean { - if (Utils.isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) { - cancelled.value = true; - return true; - } - - return false; + override applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, _args: any[]): void { + cancelled.value = true; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { @@ -3102,6 +3123,13 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr { super((user, target, move) => true); this.effects = effects; } + + override canApplyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + return super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args) + && this.effects.indexOf(args[0]) > -1 && !defender.isFainted() && defender.canAddTag(BattlerTagType.CONFUSED); + } + + /** * Applies confusion to the target pokemon. * @param pokemon {@link Pokemon} attacking @@ -3110,21 +3138,25 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr { * @param move {@link Move} used to apply status effect and confusion * @param hitResult N/A * @param args [0] {@linkcode StatusEffect} applied by move - * @returns true if defender is confused */ - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (this.effects.indexOf(args[0]) > -1 && !defender.isFainted()) { - if (simulated) { - return defender.canAddTag(BattlerTagType.CONFUSED); - } else { - return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedIntRange(2, 5), move.id, defender.id); - } + override applyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): void { + if (!simulated) { + defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedIntRange(2, 5), move.id, defender.id); } - return false; } } export class PreSetStatusAbAttr extends AbAttr { + canApplyPreSetStatus( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + effect: StatusEffect | undefined, + cancelled: Utils.BooleanHolder, + args: any[]): boolean { + return true; + } + applyPreSetStatus( pokemon: Pokemon, passive: boolean, @@ -3132,9 +3164,7 @@ export class PreSetStatusAbAttr extends AbAttr { effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, args: any[], - ): boolean { - return false; - } + ): void {} } /** @@ -3152,6 +3182,10 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { this.immuneEffects = immuneEffects; } + override canApplyPreSetStatus(pokemon: Pokemon, passive: boolean, simulated: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean { + return effect !== StatusEffect.FAINT && this.immuneEffects.length < 1 || this.immuneEffects.includes(effect); + } + /** * Applies immunity to supplied status effects. * @@ -3160,15 +3194,9 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { * @param effect - The status effect being applied. * @param cancelled - A holder for a boolean value indicating if the status application was cancelled. * @param args - n/a - * @returns A boolean indicating the result of the status application. */ - applyPreSetStatus(pokemon: Pokemon, passive: boolean, simulated: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (effect !== StatusEffect.FAINT && this.immuneEffects.length < 1 || this.immuneEffects.includes(effect)) { - cancelled.value = true; - return true; - } - - return false; + override applyPreSetStatus(pokemon: Pokemon, passive: boolean, simulated: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): void { + cancelled.value = true; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { @@ -3198,7 +3226,7 @@ export class StatusEffectImmunityAbAttr extends PreSetStatusEffectImmunityAbAttr export class UserFieldStatusEffectImmunityAbAttr extends PreSetStatusEffectImmunityAbAttr { } export class PreApplyBattlerTagAbAttr extends AbAttr { - applyPreApplyBattlerTag( + canApplyPreApplyBattlerTag( pokemon: Pokemon, passive: boolean, simulated: boolean, @@ -3206,8 +3234,17 @@ export class PreApplyBattlerTagAbAttr extends AbAttr { cancelled: Utils.BooleanHolder, args: any[], ): boolean { - return false; + return true; } + + applyPreApplyBattlerTag( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + tag: BattlerTag, + cancelled: Utils.BooleanHolder, + args: any[], + ): void {} } /** @@ -3218,21 +3255,19 @@ export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr { private battlerTag: BattlerTag; constructor(immuneTagTypes: BattlerTagType | BattlerTagType[]) { - super(); + super(true); this.immuneTagTypes = Array.isArray(immuneTagTypes) ? immuneTagTypes : [ immuneTagTypes ]; } - applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, simulated: boolean, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.immuneTagTypes.includes(tag.tagType)) { - cancelled.value = true; - if (!simulated) { - this.battlerTag = tag; - } - return true; - } + override canApplyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, simulated: boolean, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean { + this.battlerTag = tag; - return false; + return this.immuneTagTypes.includes(tag.tagType); + } + + override applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, simulated: boolean, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): void { + cancelled.value = true; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { @@ -3257,16 +3292,21 @@ export class BattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr { export class UserFieldBattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr { } export class BlockCritAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + constructor() { + super(false); + } + + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { (args[0] as Utils.BooleanHolder).value = true; - return true; } } export class BonusCritAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + constructor() { + super(false); + } + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { (args[0] as Utils.BooleanHolder).value = true; - return true; } } @@ -3274,19 +3314,19 @@ export class MultCritAbAttr extends AbAttr { public multAmount: number; constructor(multAmount: number) { - super(true); + super(false); this.multAmount = multAmount; } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const critMult = args[0] as Utils.NumberHolder; - if (critMult.value > 1) { - critMult.value *= this.multAmount; - return true; - } + return critMult.value > 1; + } - return false; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + const critMult = args[0] as Utils.NumberHolder; + critMult.value *= this.multAmount; } } @@ -3299,33 +3339,35 @@ export class ConditionalCritAbAttr extends AbAttr { private condition: PokemonAttackCondition; constructor(condition: PokemonAttackCondition, checkUser?: boolean) { - super(); + super(false); this.condition = condition; } + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + const target = (args[1] as Pokemon); + const move = (args[2] as Move); + return this.condition(pokemon, target, move); + } + /** * @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, simulated: 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; - } - + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { (args[0] as Utils.BooleanHolder).value = true; - return true; } } export class BlockNonDirectDamageAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + constructor() { + super(false); + } + + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { cancelled.value = true; - return true; } } @@ -3344,26 +3386,27 @@ export class BlockStatusDamageAbAttr extends AbAttr { this.effects = effects; } + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (pokemon.status && this.effects.includes(pokemon.status.effect)) { + return true; + } + return false; + } + /** * @param {Pokemon} pokemon The pokemon with the ability * @param {boolean} passive N/A * @param {Utils.BooleanHolder} cancelled Whether to cancel the status damage * @param {any[]} args N/A - * @returns Returns true if status damage is blocked */ - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (pokemon.status && this.effects.includes(pokemon.status.effect)) { - cancelled.value = true; - return true; - } - return false; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + cancelled.value = true; } } export class BlockOneHitKOAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { cancelled.value = true; - return true; } } @@ -3381,25 +3424,34 @@ export class ChangeMovePriorityAbAttr extends AbAttr { * @param {number} changeAmount the amount of priority added or subtracted */ constructor(moveFunc: (pokemon: Pokemon, move: Move) => boolean, changeAmount: number) { - super(true); + super(false); this.moveFunc = moveFunc; this.changeAmount = changeAmount; } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (!this.moveFunc(pokemon, args[0] as Move)) { - return false; - } + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return this.moveFunc(pokemon, args[0] as Move); + } + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { (args[1] as Utils.NumberHolder).value += this.changeAmount; - return true; } } export class IgnoreContactAbAttr extends AbAttr { } export class PreWeatherEffectAbAttr extends AbAttr { + canApplyPreWeatherEffect( + pokemon: Pokemon, + passive: Boolean, + simulated: boolean, + weather: Weather | null, + cancelled: Utils.BooleanHolder, + args: any[]): boolean { + return true; + } + applyPreWeatherEffect( pokemon: Pokemon, passive: boolean, @@ -3407,9 +3459,7 @@ export class PreWeatherEffectAbAttr extends AbAttr { weather: Weather | null, cancelled: Utils.BooleanHolder, args: any[], - ): boolean { - return false; - } + ): void {} } export class PreWeatherDamageAbAttr extends PreWeatherEffectAbAttr { } @@ -3418,17 +3468,17 @@ export class BlockWeatherDamageAttr extends PreWeatherDamageAbAttr { private weatherTypes: WeatherType[]; constructor(...weatherTypes: WeatherType[]) { - super(); + super(false); this.weatherTypes = weatherTypes; } - applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (!this.weatherTypes.length || this.weatherTypes.indexOf(weather?.weatherType) > -1) { - cancelled.value = true; - } + override canApplyPreWeatherEffect(pokemon: Pokemon, passive: Boolean, simulated: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { + return !this.weatherTypes.length || this.weatherTypes.indexOf(weather?.weatherType) > -1; + } - return true; + override applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): void { + cancelled.value = true; } } @@ -3436,18 +3486,17 @@ export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr { public affectsImmutable: boolean; constructor(affectsImmutable?: boolean) { - super(); + super(true); this.affectsImmutable = !!affectsImmutable; } - applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.affectsImmutable || weather.isImmutable()) { - cancelled.value = true; - return true; - } + override canApplyPreWeatherEffect(pokemon: Pokemon, passive: Boolean, simulated: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { + return this.affectsImmutable || weather.isImmutable(); + } - return false; + override applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): void { + cancelled.value = true; } } @@ -3552,7 +3601,7 @@ export class ForewarnAbAttr extends PostSummonAbAttr { super(true); } - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { let maxPowerSeen = 0; let maxMove = ""; let movePower = 0; @@ -3579,7 +3628,6 @@ export class ForewarnAbAttr extends PostSummonAbAttr { if (!simulated) { globalScene.queueMessage(i18next.t("abilityTriggers:forewarn", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: maxMove })); } - return true; } } @@ -3588,21 +3636,22 @@ export class FriskAbAttr extends PostSummonAbAttr { super(true); } - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (!simulated) { for (const opponent of pokemon.getOpponents()) { globalScene.queueMessage(i18next.t("abilityTriggers:frisk", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), opponentName: opponent.name, opponentAbilityName: opponent.getAbility().name })); setAbilityRevealed(opponent); } } - return true; } } export class PostWeatherChangeAbAttr extends AbAttr { - applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { - return false; + canApplyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { + return true; } + + applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): void {} } /** @@ -3621,6 +3670,13 @@ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr { this.formRevertingWeathers = formRevertingWeathers; } + override canApplyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { + const isCastformWithForecast = (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST); + const isCherrimWithFlowerGift = (pokemon.species.speciesId === Species.CHERRIM && this.ability === Abilities.FLOWER_GIFT); + + return isCastformWithForecast || isCherrimWithFlowerGift; + } + /** * Calls {@linkcode Arena.triggerWeatherBasedFormChangesToNormal | triggerWeatherBasedFormChangesToNormal} when the * weather changed to form-reverting weather, otherwise calls {@linkcode Arena.triggerWeatherBasedFormChanges | triggerWeatherBasedFormChanges} @@ -3628,27 +3684,19 @@ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr { * @param passive n/a * @param weather n/a * @param args n/a - * @returns whether the form change was triggered */ - applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { - const isCastformWithForecast = (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST); - const isCherrimWithFlowerGift = (pokemon.species.speciesId === Species.CHERRIM && this.ability === Abilities.FLOWER_GIFT); - - if (isCastformWithForecast || isCherrimWithFlowerGift) { - if (simulated) { - return simulated; - } - - const weatherType = globalScene.arena.weather?.weatherType; - - if (weatherType && this.formRevertingWeathers.includes(weatherType)) { - globalScene.arena.triggerWeatherBasedFormChangesToNormal(); - } else { - globalScene.arena.triggerWeatherBasedFormChanges(); - } - return true; + override applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): void { + if (simulated) { + return; + } + + const weatherType = globalScene.arena.weather?.weatherType; + + if (weatherType && this.formRevertingWeathers.includes(weatherType)) { + globalScene.arena.triggerWeatherBasedFormChangesToNormal(); + } else { + globalScene.arena.triggerWeatherBasedFormChanges(); } - return false; } } @@ -3665,16 +3713,13 @@ export class PostWeatherChangeAddBattlerTagAttr extends PostWeatherChangeAbAttr this.weatherTypes = weatherTypes; } - applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { - console.log(this.weatherTypes.find(w => weather === w), WeatherType[weather]); - if (!this.weatherTypes.find(w => weather === w)) { - return false; - } + override canApplyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { + return !!this.weatherTypes.find(w => weather === w) && pokemon.canAddTag(this.tagType); + } - if (simulated) { - return pokemon.canAddTag(this.tagType); - } else { - return pokemon.addTag(this.tagType, this.turnCount); + override applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): void { + if (!simulated) { + pokemon.addTag(this.tagType, this.turnCount); } } } @@ -3688,15 +3733,22 @@ export class PostWeatherLapseAbAttr extends AbAttr { this.weatherTypes = weatherTypes; } + canApplyPostWeatherLapse( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + weather: Weather | null, + args: any[]): boolean { + return true; + } + applyPostWeatherLapse( pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather | null, args: any[], - ): boolean { - return false; - } + ): void {} getCondition(): AbAttrCondition { return getWeatherCondition(...this.weatherTypes); @@ -3712,17 +3764,16 @@ export class PostWeatherLapseHealAbAttr extends PostWeatherLapseAbAttr { this.healFactor = healFactor; } - applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, args: any[]): boolean { - if (!pokemon.isFullHp()) { - const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - if (!simulated) { - globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), - Utils.toDmgValue(pokemon.getMaxHp() / (16 / this.healFactor)), i18next.t("abilityTriggers:postWeatherLapseHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); - } - return true; - } + override canApplyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather | null, args: any[]): boolean { + return !pokemon.isFullHp(); + } - return false; + override applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, args: any[]): void { + const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; + if (!simulated) { + globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), + Utils.toDmgValue(pokemon.getMaxHp() / (16 / this.healFactor)), i18next.t("abilityTriggers:postWeatherLapseHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + } } } @@ -3735,25 +3786,25 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr { this.damageFactor = damageFactor; } - applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, args: any[]): boolean { - if (pokemon.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { - return false; - } + override canApplyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather | null, args: any[]): boolean { + return !pokemon.hasAbilityWithAttr(BlockNonDirectDamageAbAttr); + } + override applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, args: any[]): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; globalScene.queueMessage(i18next.t("abilityTriggers:postWeatherLapseDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName })); pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / (16 / this.damageFactor)), HitResult.OTHER); } - - return true; } } export class PostTerrainChangeAbAttr extends AbAttr { - applyPostTerrainChange(pokemon: Pokemon, passive: boolean, simulated: boolean, terrain: TerrainType, args: any[]): boolean { - return false; + canApplyPostTerrainChange(pokemon: Pokemon, passive: boolean, simulated: boolean, terrain: TerrainType, args: any[]): boolean { + return true; } + + applyPostTerrainChange(pokemon: Pokemon, passive: boolean, simulated: boolean, terrain: TerrainType, args: any[]): void {} } export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr { @@ -3769,15 +3820,13 @@ export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr this.terrainTypes = terrainTypes; } - applyPostTerrainChange(pokemon: Pokemon, passive: boolean, simulated: boolean, terrain: TerrainType, args: any[]): boolean { - if (!this.terrainTypes.find(t => t === terrain)) { - return false; - } + override canApplyPostTerrainChange(pokemon: Pokemon, passive: boolean, simulated: boolean, terrain: TerrainType, args: any[]): boolean { + return !!this.terrainTypes.find(t => t === terrain) && pokemon.canAddTag(this.tagType); + } - if (simulated) { - return pokemon.canAddTag(this.tagType); - } else { - return pokemon.addTag(this.tagType, this.turnCount); + override applyPostTerrainChange(pokemon: Pokemon, passive: boolean, simulated: boolean, terrain: TerrainType, args: any[]): void { + if (!simulated) { + pokemon.addTag(this.tagType, this.turnCount); } } } @@ -3790,9 +3839,11 @@ function getTerrainCondition(...terrainTypes: TerrainType[]): AbAttrCondition { } export class PostTurnAbAttr extends AbAttr { - applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return false; + canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return true; } + + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} } /** @@ -3810,24 +3861,21 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { this.effects = effects; } + override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return (pokemon.status !== null) && this.effects.includes(pokemon.status.effect) && !pokemon.isFullHp(); + } + /** * @param {Pokemon} pokemon The pokemon with the ability that will receive the healing * @param {Boolean} passive N/A * @param {any[]} args N/A - * @returns Returns true if healed from status, false if not */ - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - if (pokemon.status && this.effects.includes(pokemon.status.effect)) { - if (!pokemon.isFullHp()) { - if (!simulated) { - const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), - Utils.toDmgValue(pokemon.getMaxHp() / 8), i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), true)); - } - return true; - } + override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + if (!simulated) { + const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; + globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), + Utils.toDmgValue(pokemon.getMaxHp() / 8), i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), true)); } - return false; } } @@ -3844,23 +3892,21 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { this.allyTarget = allyTarget; } - applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { if (this.allyTarget) { this.target = pokemon.getAlly(); } else { this.target = pokemon; } - if (this.target?.status) { - if (!simulated) { - globalScene.queueMessage(getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target))); - this.target.resetStatus(false); - this.target.updateInfo(); - } + return !Utils.isNullOrUndefined(this.target.status); + } - return true; + override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + if (!simulated && this.target.status) { + globalScene.queueMessage(getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target))); + this.target.resetStatus(false); + this.target.updateInfo(); } - - return false; } } @@ -3881,18 +3927,14 @@ export class PostTurnLootAbAttr extends PostTurnAbAttr { super(); } - applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const pass = Phaser.Math.RND.realInRange(0, 1); + override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { // Clamp procChance to [0, 1]. Skip if didn't proc (less than pass) - if (Math.max(Math.min(this.procChance(pokemon), 1), 0) < pass) { - return false; - } + const pass = Phaser.Math.RND.realInRange(0, 1); + return !(Math.max(Math.min(this.procChance(pokemon), 1), 0) < pass) && this.itemType === "EATEN_BERRIES" && !!pokemon.battleData.berriesEaten; + } - if (this.itemType === "EATEN_BERRIES") { - return this.createEatenBerry(pokemon, simulated); - } else { - return false; - } + override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + this.createEatenBerry(pokemon, simulated); } /** @@ -3953,13 +3995,12 @@ export class MoodyAbAttr extends PostTurnAbAttr { * @param passive N/A * @param simulated true if applying in a simulated call. * @param args N/A - * @returns true * * Any stat stages at +6 or -6 are excluded from being increased or decreased, respectively * If the pokemon already has all stat stages raised to 6, it will only decrease one stat stage by 1 * If the pokemon already has all stat stages lowered to -6, it will only increase one stat stage by 2 */ - applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { const canRaise = EFFECTIVE_STATS.filter(s => pokemon.getStatStage(s) < 6); let canLower = EFFECTIVE_STATS.filter(s => pokemon.getStatStage(s) > -6); @@ -3974,8 +4015,6 @@ export class MoodyAbAttr extends PostTurnAbAttr { globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ loweredStat ], -1)); } } - - return true; } } @@ -3985,31 +4024,26 @@ export class SpeedBoostAbAttr extends PostTurnAbAttr { super(true); } - applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - if (!simulated) { - if (!pokemon.turnData.switchedInThisTurn && !pokemon.turnData.failedRunAway) { - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.SPD ], 1)); - } else { - return false; - } - } - return true; + override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return simulated || (!pokemon.turnData.switchedInThisTurn && !pokemon.turnData.failedRunAway); + } + + override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.SPD ], 1)); } } export class PostTurnHealAbAttr extends PostTurnAbAttr { - applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - if (!pokemon.isFullHp()) { - if (!simulated) { - const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), - Utils.toDmgValue(pokemon.getMaxHp() / 16), i18next.t("abilityTriggers:postTurnHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); - } + override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return !pokemon.isFullHp(); + } - return true; + override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + if (!simulated) { + const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; + globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), + Utils.toDmgValue(pokemon.getMaxHp() / 16), i18next.t("abilityTriggers:postTurnHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); } - - return false; } } @@ -4022,17 +4056,14 @@ export class PostTurnFormChangeAbAttr extends PostTurnAbAttr { this.formFunc = formFunc; } - applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const formIndex = this.formFunc(pokemon); - if (formIndex !== pokemon.formIndex) { - if (!simulated) { - globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); - } + override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return this.formFunc(pokemon) !== pokemon.formIndex; + } - return true; + override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + if (!simulated) { + globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } - - return false; } } @@ -4041,28 +4072,25 @@ export class PostTurnFormChangeAbAttr extends PostTurnAbAttr { * Attribute used for abilities (Bad Dreams) that damages the opponents for being asleep */ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { - + override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return pokemon.getOpponents().some(opp => (opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(Abilities.COMATOSE)) && !opp.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && !opp.switchOutStatus); + } /** * Deals damage to all sleeping opponents equal to 1/8 of their max hp (min 1) * @param pokemon Pokemon that has this ability * @param passive N/A * @param simulated `true` if applying in a simulated call. * @param args N/A - * @returns `true` if any opponents are sleeping */ - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - let hadEffect = false; + override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { for (const opp of pokemon.getOpponents()) { if ((opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(Abilities.COMATOSE)) && !opp.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && !opp.switchOutStatus) { if (!simulated) { opp.damageAndUpdate(Utils.toDmgValue(opp.getMaxHp() / 8), HitResult.OTHER); globalScene.queueMessage(i18next.t("abilityTriggers:badDreams", { pokemonName: getPokemonNameWithAffix(opp) })); } - hadEffect = true; } - } - return hadEffect; } } @@ -4075,25 +4103,22 @@ export class FetchBallAbAttr extends PostTurnAbAttr { constructor() { super(); } + + override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return !simulated && globalScene.currentBattle.lastUsedPokeball !== null && !!pokemon.isPlayer; + } + /** * 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, simulated: boolean, args: any[]): boolean { - if (simulated) { - return false; - } + override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { const lastUsed = globalScene.currentBattle.lastUsedPokeball; - if (lastUsed !== null && !!pokemon.isPlayer) { - globalScene.pokeballCounts[lastUsed]++; - globalScene.currentBattle.lastUsedPokeball = null; - globalScene.queueMessage(i18next.t("abilityTriggers:fetchBall", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokeballName: getPokeballName(lastUsed) })); - return true; - } - return false; + globalScene.pokeballCounts[lastUsed!]++; + globalScene.currentBattle.lastUsedPokeball = null; + globalScene.queueMessage(i18next.t("abilityTriggers:fetchBall", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokeballName: getPokeballName(lastUsed!) })); } } @@ -4108,16 +4133,14 @@ export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr { this.weatherType = weatherType; } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (!globalScene.arena.weather?.isImmutable()) { - if (simulated) { - return globalScene.arena.weather?.weatherType !== this.weatherType; - } else { - return globalScene.arena.trySetWeather(this.weatherType, true); - } - } + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return ((globalScene.arena.weather?.isImmutable() ?? false) && globalScene.arena.canSetWeather(this.weatherType)); + } - return false; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + if (!simulated) { + globalScene.arena.trySetWeather(this.weatherType, true); + } } } @@ -4130,11 +4153,13 @@ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr { this.terrainType = terrainType; } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (simulated) { - return globalScene.arena.terrain?.terrainType !== this.terrainType; - } else { - return globalScene.arena.trySetTerrain(this.terrainType, true); + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return globalScene.arena.canSetTerrain(this.terrainType); + } + + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + if (!simulated) { + globalScene.arena.trySetTerrain(this.terrainType, true); } } } @@ -4144,6 +4169,16 @@ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr { * @extends AbAttr */ export class PostMoveUsedAbAttr extends AbAttr { + canApplyPostMoveUsed( + pokemon: Pokemon, + move: PokemonMove, + source: Pokemon, + targets: BattlerIndex[], + simulated: boolean, + args: any[]): boolean { + return true; + } + applyPostMoveUsed( pokemon: Pokemon, move: PokemonMove, @@ -4151,9 +4186,7 @@ export class PostMoveUsedAbAttr extends AbAttr { targets: BattlerIndex[], simulated: boolean, args: any[], - ): boolean { - return false; - } + ): void {} } /** @@ -4161,6 +4194,15 @@ export class PostMoveUsedAbAttr extends AbAttr { * @extends PostMoveUsedAbAttr */ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { + override canApplyPostMoveUsed(dancer: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], simulated: boolean, args: any[]): boolean { + // List of tags that prevent the Dancer from replicating the move + const forbiddenTags = [ BattlerTagType.FLYING, BattlerTagType.UNDERWATER, + BattlerTagType.UNDERGROUND, BattlerTagType.HIDDEN ]; + // The move to replicate cannot come from the Dancer + return source.getBattlerIndex() !== dancer.getBattlerIndex() + && !dancer.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType)); + } + /** * Resolves the Dancer ability by replicating the move used by the source of the dance * either on the source itself or on the target of the dance @@ -4169,8 +4211,6 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { * @param source {@linkcode Pokemon} that used the dancing move * @param targets {@linkcode BattlerIndex}Targets of the dancing move * @param args N/A - * - * @return true if the Dancer ability was resolved */ override applyPostMoveUsed( dancer: Pokemon, @@ -4178,27 +4218,17 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { source: Pokemon, targets: BattlerIndex[], simulated: boolean, - args: any[], - ): boolean { - // List of tags that prevent the Dancer from replicating the move - const forbiddenTags = [ BattlerTagType.FLYING, BattlerTagType.UNDERWATER, - BattlerTagType.UNDERGROUND, BattlerTagType.HIDDEN ]; - // The move to replicate cannot come from the Dancer - if (source.getBattlerIndex() !== dancer.getBattlerIndex() - && !dancer.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType))) { - if (!simulated) { - // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance - if (move.getMove() instanceof AttackMove || move.getMove() instanceof StatusMove) { - const target = this.getTarget(dancer, source, targets); - globalScene.unshiftPhase(new MovePhase(dancer, target, move, true, true)); - } else if (move.getMove() instanceof SelfStatusMove) { - // If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself - globalScene.unshiftPhase(new MovePhase(dancer, [ dancer.getBattlerIndex() ], move, true, true)); - } + args: any[]): void { + if (!simulated) { + // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance + if (move.getMove() instanceof AttackMove || move.getMove() instanceof StatusMove) { + const target = this.getTarget(dancer, source, targets); + globalScene.unshiftPhase(new MovePhase(dancer, target, move, true, true)); + } else if (move.getMove() instanceof SelfStatusMove) { + // If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself + globalScene.unshiftPhase(new MovePhase(dancer, [ dancer.getBattlerIndex() ], move, true, true)); } - return true; } - return false; } /** @@ -4221,9 +4251,11 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { * @extends AbAttr */ export class PostItemLostAbAttr extends AbAttr { - applyPostItemLost(pokemon: Pokemon, simulated: boolean, args: any[]): boolean { - return false; + canApplyPostItemLost(pokemon: Pokemon, simulated: boolean, args: any[]): boolean { + return true; } + + applyPostItemLost(pokemon: Pokemon, simulated: boolean, args: any[]): void {} } /** @@ -4233,21 +4265,21 @@ export class PostItemLostAbAttr extends AbAttr { export class PostItemLostApplyBattlerTagAbAttr extends PostItemLostAbAttr { private tagType: BattlerTagType; constructor(tagType: BattlerTagType) { - super(true); + super(false); this.tagType = tagType; } + + override canApplyPostItemLost(pokemon: Pokemon, simulated: boolean, args: any[]): boolean { + return !pokemon.getTag(this.tagType) && !simulated; + } + /** * Adds the last used Pokeball back into the player's inventory * @param pokemon {@linkcode Pokemon} with this ability * @param args N/A - * @returns true if BattlerTag was applied */ - override applyPostItemLost(pokemon: Pokemon, simulated: boolean, args: any[]): boolean { - if (!pokemon.getTag(this.tagType) && !simulated) { - pokemon.addTag(this.tagType); - return true; - } - return false; + override applyPostItemLost(pokemon: Pokemon, simulated: boolean, args: any[]): void { + pokemon.addTag(this.tagType); } } @@ -4255,15 +4287,13 @@ export class StatStageChangeMultiplierAbAttr extends AbAttr { private multiplier: number; constructor(multiplier: number) { - super(true); + super(false); this.multiplier = multiplier; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { (args[0] as Utils.NumberHolder).value *= this.multiplier; - - return true; } } @@ -4274,11 +4304,10 @@ export class StatStageChangeCopyAbAttr extends AbAttr { simulated: boolean, cancelled: Utils.BooleanHolder, args: any[], - ): boolean { + ): void { if (!simulated) { globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, (args[0] as BattleStat[]), (args[1] as number), true, false, false)); } - return true; } } @@ -4287,10 +4316,8 @@ export class BypassBurnDamageReductionAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { cancelled.value = true; - - return true; } } @@ -4309,28 +4336,21 @@ export class ReduceBurnDamageAbAttr extends AbAttr { * @param passive N/A * @param cancelled N/A * @param args `[0]` {@linkcode Utils.NumberHolder} The damage value being modified - * @returns `true` */ - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { (args[0] as Utils.NumberHolder).value = Utils.toDmgValue((args[0] as Utils.NumberHolder).value * this.multiplier); - - return true; } } export class DoubleBerryEffectAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { (args[0] as Utils.NumberHolder).value *= 2; - - return true; } } export class PreventBerryUseAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { cancelled.value = true; - - return true; } } @@ -4350,7 +4370,7 @@ export class HealFromBerryUseAbAttr extends AbAttr { this.healPercent = Math.max(Math.min(healPercent, 1), 0); } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, ...args: [Utils.BooleanHolder, any[]]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, ...args: [Utils.BooleanHolder, any[]]): void { const { name: abilityName } = passive ? pokemon.getPassiveAbility() : pokemon.getAbility(); if (!simulated) { globalScene.unshiftPhase( @@ -4362,15 +4382,12 @@ export class HealFromBerryUseAbAttr extends AbAttr { ) ); } - return true; } } export class RunSuccessAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { (args[0] as Utils.NumberHolder).value = 256; - - return true; } } @@ -4390,6 +4407,16 @@ export class CheckTrappedAbAttr extends AbAttr { this.arenaTrapCondition = condition; } + canApplyCheckTrapped( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + trapped: Utils.BooleanHolder, + otherPokemon: Pokemon, + args: any[]): boolean { + return true; + } + applyCheckTrapped( pokemon: Pokemon, passive: boolean, @@ -4397,9 +4424,7 @@ export class CheckTrappedAbAttr extends AbAttr { trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[], - ): boolean { - return false; - } + ): void {} } /** @@ -4409,6 +4434,12 @@ export class CheckTrappedAbAttr extends AbAttr { * @see {@linkcode applyCheckTrapped} */ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { + override canApplyCheckTrapped(pokemon: Pokemon, passive: boolean, simulated: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean { + return this.arenaTrapCondition(pokemon, otherPokemon) + && !(otherPokemon.getTypes(true).includes(PokemonType.GHOST) || (otherPokemon.getTypes(true).includes(PokemonType.STELLAR) && otherPokemon.getTypes().includes(PokemonType.GHOST))) + && !otherPokemon.hasAbility(Abilities.RUN_AWAY); + } + /** * Checks if enemy Pokemon is trapped by an Arena Trap-esque ability * If the enemy is a Ghost type, it is not trapped @@ -4420,22 +4451,9 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { * @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, simulated: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean { - if (this.arenaTrapCondition(pokemon, otherPokemon)) { - if (otherPokemon.getTypes(true).includes(PokemonType.GHOST) || (otherPokemon.getTypes(true).includes(PokemonType.STELLAR) && otherPokemon.getTypes().includes(PokemonType.GHOST))) { - trapped.value = false; - return false; - } else if (otherPokemon.hasAbility(Abilities.RUN_AWAY)) { - trapped.value = false; - return false; - } - trapped.value = true; - return true; - } - trapped.value = false; - return false; + override applyCheckTrapped(pokemon: Pokemon, passive: boolean, simulated: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): void { + trapped.value = true; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { @@ -4444,10 +4462,12 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { } export class MaxMultiHitAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = 0; + constructor() { + super(false); + } - return true; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + (args[0] as Utils.NumberHolder).value = 0; } } @@ -4456,36 +4476,48 @@ export class PostBattleAbAttr extends AbAttr { super(true); } - applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return false; + canApplyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return true; } + + applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} } export class PostBattleLootAbAttr extends PostBattleAbAttr { - /** - * @param args - `[0]`: boolean for if the battle ended in a victory - * @returns `true` if successful - */ - applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + private randItem?: PokemonHeldItemModifier; + + override canApplyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const postBattleLoot = globalScene.currentBattle.postBattleLoot; if (!simulated && postBattleLoot.length && args[0]) { - const randItem = Utils.randSeedItem(postBattleLoot); - //@ts-ignore - TODO see below - if (globalScene.tryTransferHeldItemModifier(randItem, pokemon, true, 1, true, undefined, false)) { - postBattleLoot.splice(postBattleLoot.indexOf(randItem), 1); - globalScene.queueMessage(i18next.t("abilityTriggers:postBattleLoot", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), itemName: randItem.type.name })); - return true; - } + this.randItem = Utils.randSeedItem(postBattleLoot); + return globalScene.canTransferHeldItemModifier(this.randItem, pokemon, 1); + } + return false; + } + + /** + * @param args - `[0]`: boolean for if the battle ended in a victory + */ + override applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + const postBattleLoot = globalScene.currentBattle.postBattleLoot; + if (!this.randItem) { + this.randItem = Utils.randSeedItem(postBattleLoot); } - return false; + if (globalScene.tryTransferHeldItemModifier(this.randItem, pokemon, true, 1, true, undefined, false)) { + postBattleLoot.splice(postBattleLoot.indexOf(this.randItem), 1); + globalScene.queueMessage(i18next.t("abilityTriggers:postBattleLoot", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), itemName: this.randItem.type.name })); + } + this.randItem = undefined; } } export class PostFaintAbAttr extends AbAttr { - applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { - return false; + canApplyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { + return true; } + + applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): void {} } /** @@ -4494,6 +4526,10 @@ export class PostFaintAbAttr extends AbAttr { * @extends PostFaintAbAttr */ export class PostFaintUnsuppressedWeatherFormChangeAbAttr extends PostFaintAbAttr { + override canApplyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { + return getPokemonWithWeatherBasedForms().length > 0; + } + /** * Triggers {@linkcode Arena.triggerWeatherBasedFormChanges | triggerWeatherBasedFormChanges} * when the user of the ability faints @@ -4503,20 +4539,11 @@ export class PostFaintUnsuppressedWeatherFormChangeAbAttr extends PostFaintAbAtt * @param move n/a * @param hitResult n/a * @param args n/a - * @returns whether the form change was triggered */ - applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - const pokemonToTransform = getPokemonWithWeatherBasedForms(); - - if (pokemonToTransform.length < 1) { - return false; - } - + override applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): void { if (!simulated) { globalScene.arena.triggerWeatherBasedFormChanges(); } - - return true; } } @@ -4524,26 +4551,27 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { private damageRatio: number; constructor(damageRatio: number) { - super(); + super(true); this.damageRatio = damageRatio; } - applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { - if (move !== undefined && attacker !== undefined && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) { //If the mon didn't die to indirect damage - const cancelled = new Utils.BooleanHolder(false); - globalScene.getField(true).map(p => applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled, simulated)); - if (cancelled.value || attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { - return false; - } - if (!simulated) { - attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); - attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); - } - return true; + override canApplyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { + const diedToDirectDamage = move !== undefined && attacker !== undefined && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon); + const cancelled = new Utils.BooleanHolder(false); + globalScene.getField(true).map(p => applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled, simulated)); + if (!diedToDirectDamage || cancelled.value || attacker!.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { + return false; } - return false; + return true; + } + + override applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): void { + if (!simulated) { + attacker!.damageAndUpdate(Utils.toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); + attacker!.turnData.damageTaken += Utils.toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)); + } } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { @@ -4559,13 +4587,12 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr { super (); } - applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { + override applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): void { if (move !== undefined && attacker !== undefined && !simulated) { //If the mon didn't die to indirect damage const damage = pokemon.turnData.attacksReceived[0].damage; attacker.damageAndUpdate((damage), HitResult.OTHER); attacker.turnData.damageTaken += damage; } - return true; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { @@ -4584,17 +4611,20 @@ export class RedirectMoveAbAttr extends AbAttr { * - `[1]` - The target's battler index (before redirection) * - `[2]` - The Pokemon that used the move being redirected */ - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.canRedirect(args[0] as Moves, args[2] as Pokemon)) { - const target = args[1] as Utils.NumberHolder; - const newTarget = pokemon.getBattlerIndex(); - if (target.value !== newTarget) { - target.value = newTarget; - return true; - } - } - return false; + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (!this.canRedirect(args[0] as Moves, args[2] as Pokemon)) { + return false; + } + const target = args[1] as Utils.NumberHolder; + const newTarget = pokemon.getBattlerIndex(); + return target.value !== newTarget; + } + + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + const target = args[1] as Utils.NumberHolder; + const newTarget = pokemon.getBattlerIndex(); + target.value = newTarget; } canRedirect(moveId: Moves, user: Pokemon): boolean { @@ -4627,28 +4657,23 @@ export class ReduceStatusEffectDurationAbAttr extends AbAttr { private statusEffect: StatusEffect; constructor(statusEffect: StatusEffect) { - super(true); + super(false); this.statusEffect = statusEffect; } + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return args[1] instanceof Utils.NumberHolder && args[0] === this.statusEffect; + } + /** * Reduces the number of sleep turns remaining by an extra 1 when applied * @param args - The args passed to the `AbAttr`: * - `[0]` - The {@linkcode StatusEffect} of the Pokemon * - `[1]` - The number of turns remaining until the status is healed - * @returns `true` if the ability was applied */ - apply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (!(args[1] instanceof Utils.NumberHolder)) { - return false; - } - if (args[0] === this.statusEffect) { - args[1].value -= 1; - return true; - } - - return false; + override apply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: Utils.BooleanHolder, args: any[]): void { + args[1].value -= 1; } } @@ -4671,20 +4696,18 @@ export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr { this.stages = stages; } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { if (!simulated) { globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages)); } - return true; } } export class IncreasePpAbAttr extends AbAttr { } export class ForceSwitchOutImmunityAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { cancelled.value = true; - return true; } } @@ -4693,15 +4716,13 @@ export class ReduceBerryUseThresholdAbAttr extends AbAttr { super(); } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const hpRatio = pokemon.getHpRatio(); + return args[0].value < hpRatio; + } - if (args[0].value < hpRatio) { - args[0].value *= 2; - return args[0].value >= hpRatio; - } - - return false; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + args[0].value *= 2; } } @@ -4713,15 +4734,13 @@ export class WeightMultiplierAbAttr extends AbAttr { private multiplier: number; constructor(multiplier: number) { - super(); + super(false); this.multiplier = multiplier; } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { (args[0] as Utils.NumberHolder).value *= this.multiplier; - - return true; } } @@ -4730,10 +4749,8 @@ export class SyncEncounterNatureAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { (args[0] as Pokemon).setNature(pokemon.getNature()); - - return true; } } @@ -4746,12 +4763,12 @@ export class MoveAbilityBypassAbAttr extends AbAttr { this.moveIgnoreFunc = moveIgnoreFunc || ((pokemon, move) => true); } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.moveIgnoreFunc(pokemon, (args[0] as Move))) { - cancelled.value = true; - return true; - } - return false; + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return this.moveIgnoreFunc(pokemon, (args[0] as Move)); + } + + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + cancelled.value = true; } } @@ -4765,6 +4782,14 @@ export class IgnoreProtectOnContactAbAttr extends AbAttr { } * Allows the source's moves to bypass the effects of opposing Light Screen, Reflect, Aurora Veil, Safeguard, Mist, and Substitute. */ export class InfiltratorAbAttr extends AbAttr { + constructor() { + super(false); + } + + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return args[0] instanceof Utils.BooleanHolder; + } + /** * Sets a flag to bypass screens, Substitute, Safeguard, and Mist * @param pokemon n/a @@ -4772,15 +4797,10 @@ export class InfiltratorAbAttr extends AbAttr { * @param simulated n/a * @param cancelled n/a * @param args `[0]` a {@linkcode Utils.BooleanHolder | BooleanHolder} containing the flag - * @returns `true` if the bypass flag was successfully set; `false` otherwise. */ - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: null, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: null, args: any[]): void { const bypassed = args[0]; - if (args[0] instanceof Utils.BooleanHolder) { - bypassed.value = true; - return true; - } - return false; + bypassed.value = true; } } @@ -4826,17 +4846,17 @@ export class IgnoreTypeImmunityAbAttr extends AbAttr { private allowedMoveTypes: PokemonType[]; constructor(defenderType: PokemonType, allowedMoveTypes: PokemonType[]) { - super(true); + super(false); this.defenderType = defenderType; this.allowedMoveTypes = allowedMoveTypes; } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.defenderType === (args[1] as PokemonType) && this.allowedMoveTypes.includes(args[0] as PokemonType)) { - cancelled.value = true; - return true; - } - return false; + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return this.defenderType === (args[1] as PokemonType) && this.allowedMoveTypes.includes(args[0] as PokemonType); + } + + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + cancelled.value = true; } } @@ -4848,19 +4868,18 @@ export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr { private defenderType: PokemonType[]; constructor(statusEffect: StatusEffect[], defenderType: PokemonType[]) { - super(true); + super(false); this.statusEffect = statusEffect; this.defenderType = defenderType; } - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.statusEffect.includes(args[0] as StatusEffect) && this.defenderType.includes(args[1] as PokemonType)) { - cancelled.value = true; - return true; - } + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return this.statusEffect.includes(args[0] as StatusEffect) && this.defenderType.includes(args[1] as PokemonType); + } - return false; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + cancelled.value = true; } } @@ -4875,18 +4894,17 @@ export class MoneyAbAttr extends PostBattleAbAttr { super(); } + override canApplyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return !simulated && args[0]; + } + /** * @param pokemon {@linkcode Pokemon} that is the user of this ability. * @param passive N/A * @param args - `[0]`: boolean for if the battle ended in a victory - * @returns `true` if successful */ - applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - if (!simulated && args[0]) { - globalScene.currentBattle.moneyScattered += globalScene.getWaveMoneyAmount(0.2); - return true; - } - return false; + override applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + globalScene.currentBattle.moneyScattered += globalScene.getWaveMoneyAmount(0.2); } } @@ -4914,6 +4932,12 @@ export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageC this.tagType = tagType; } + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + const side = pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + return (globalScene.arena.getTagOnSide(this.tagType, side) ?? false) + && super.canApplyPostSummon(pokemon, passive, simulated, args); + } + /** * Applies the post-summon stat change if the specified arena tag is present on pokemon's side. * This is used in Wind Rider ability. @@ -4921,15 +4945,9 @@ export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageC * @param {Pokemon} pokemon - The Pokémon being summoned. * @param {boolean} passive - Whether the effect is passive. * @param {any[]} args - Additional arguments. - * @returns {boolean} - Returns true if the stat change was applied, otherwise false. */ - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const side = pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; - - if (globalScene.arena.getTagOnSide(this.tagType, side)) { - return super.applyPostSummon(pokemon, passive, simulated, args); - } - return false; + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + super.applyPostSummon(pokemon, passive, simulated, args); } } @@ -4953,6 +4971,10 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { this.triggerMessageFunc = triggerMessageFunc; } + override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + return this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon); + } + /** * Applies the pre-defense ability to the Pokémon. * Removes the appropriate `BattlerTagType` when hit by an attack and is in its defense form. @@ -4963,21 +4985,15 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { * @param move The move being used. * @param _cancelled n/a * @param args Additional arguments. - * @returns `true` if the immunity was applied. */ - override applyPreDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) { - if (!simulated) { - (args[0] as Utils.NumberHolder).value = this.multiplier; - pokemon.removeTag(this.tagType); - if (this.recoilDamageFunc) { - pokemon.damageAndUpdate(this.recoilDamageFunc(pokemon), HitResult.OTHER, false, false, true, true); - } + override applyPreDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _cancelled: Utils.BooleanHolder, args: any[]): void { + if (!simulated) { + (args[0] as Utils.NumberHolder).value = this.multiplier; + pokemon.removeTag(this.tagType); + if (this.recoilDamageFunc) { + pokemon.damageAndUpdate(this.recoilDamageFunc(pokemon), HitResult.OTHER, false, false, true, true); } - return true; } - - return false; } /** @@ -5008,34 +5024,25 @@ export class BypassSpeedChanceAbAttr extends AbAttr { this.chance = chance; } + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + const bypassSpeed = args[0] as Utils.BooleanHolder; + const turnCommand = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; + const isCommandFight = turnCommand?.command === Command.FIGHT; + const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; + const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL; + return !simulated && !bypassSpeed.value && pokemon.randSeedInt(100) < this.chance && isCommandFight && isDamageMove; + } + /** * bypass move order in their priority bracket when pokemon choose damaging move * @param {Pokemon} pokemon {@linkcode Pokemon} the Pokemon applying this ability * @param {boolean} passive N/A * @param {Utils.BooleanHolder} cancelled N/A * @param {any[]} args [0] {@linkcode Utils.BooleanHolder} set to true when the ability activated - * @returns {boolean} - whether the ability was activated. */ - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (simulated) { - return false; - } + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { const bypassSpeed = args[0] as Utils.BooleanHolder; - - if (!bypassSpeed.value && pokemon.randSeedInt(100) < this.chance) { - const turnCommand = - globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; - const isCommandFight = turnCommand?.command === Command.FIGHT; - const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; - const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL; - - if (isCommandFight && isDamageMove) { - bypassSpeed.value = true; - return true; - } - } - - return false; + bypassSpeed.value = true; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { @@ -5058,23 +5065,22 @@ export class PreventBypassSpeedChanceAbAttr extends AbAttr { this.condition = condition; } + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + const turnCommand = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; + const isCommandFight = turnCommand?.command === Command.FIGHT; + const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; + return isCommandFight && this.condition(pokemon, move!); + } + /** * @argument {boolean} bypassSpeed - determines if a Pokemon is able to bypass speed at the moment * @argument {boolean} canCheckHeldItems - determines if a Pokemon has access to Quick Claw's effects or not */ - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { const bypassSpeed = args[0] as Utils.BooleanHolder; const canCheckHeldItems = args[1] as Utils.BooleanHolder; - - const turnCommand = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; - const isCommandFight = turnCommand?.command === Command.FIGHT; - const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; - if (isCommandFight && this.condition(pokemon, move!)) { - bypassSpeed.value = false; - canCheckHeldItems.value = false; - return false; - } - return true; + bypassSpeed.value = false; + canCheckHeldItems.value = false; } } @@ -5087,10 +5093,11 @@ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr { super(true); } - override apply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: Utils.BooleanHolder, _args: any[]): boolean { - if (pokemon.isTerastallized) { - return false; - } + override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return !pokemon.isTerastallized; + } + + override apply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: Utils.BooleanHolder, _args: any[]): void { const currentTerrain = globalScene.arena.getTerrainType(); const typeChange: PokemonType[] = this.determineTypeChange(pokemon, currentTerrain); if (typeChange.length !== 0) { @@ -5100,7 +5107,6 @@ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr { pokemon.summonData.types = typeChange; pokemon.updateInfo(); } - return true; } /** @@ -5133,15 +5139,16 @@ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr { return typeChange; } + override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + return globalScene.arena.getTerrainType() !== TerrainType.NONE && + this.canApply(pokemon, passive, simulated, args); + } + /** * Checks if the Pokemon should change types if summoned into an active terrain - * @returns `true` if there is an active terrain requiring a type change | `false` if not */ - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - if (globalScene.arena.getTerrainType() !== TerrainType.NONE) { - return this.apply(pokemon, passive, simulated, new Utils.BooleanHolder(false), []); - } - return false; + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + this.apply(pokemon, passive, simulated, new Utils.BooleanHolder(false), []); } override getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]) { @@ -5161,11 +5168,11 @@ function applySingleAbAttrs( passive: boolean, attrType: Constructor, applyFunc: AbAttrApplyFunc, + successFunc: AbAttrSuccessFunc, args: any[], gainedMidTurn = false, simulated = false, priorityCondition?: (p: number) => boolean, - showAbilityInstant = false, messages: string[] = [] ) { if (!pokemon?.canApplyAbility(passive) || (passive && (pokemon.getPassiveAbility().id === pokemon.getAbility().id))) { @@ -5182,33 +5189,39 @@ function applySingleAbAttrs( for (const attr of ability.getAttrs(attrType)) { const condition = attr.getCondition(); - if (condition && !condition(pokemon)) { + let abShown = false; + if (condition && !condition(pokemon) || !successFunc(attr, passive)) { continue; } globalScene.setPhaseQueueSplice(); - if (applyFunc(attr, passive)) { - if (pokemon.summonData && !pokemon.summonData.abilitiesApplied.includes(ability.id)) { - pokemon.summonData.abilitiesApplied.push(ability.id); - } - if (pokemon.battleData && !simulated && !pokemon.battleData.abilitiesApplied.includes(ability.id)) { - pokemon.battleData.abilitiesApplied.push(ability.id); - } - if (attr.showAbility && !simulated) { - if (showAbilityInstant) { - globalScene.abilityBar.showAbility(pokemon, passive); - } else { - queueShowAbility(pokemon, passive); - } - } - const message = attr.getTriggerMessage(pokemon, ability.name, args); - if (message) { - if (!simulated) { - globalScene.queueMessage(message); - } - } - messages.push(message!); + + if (attr.showAbility && !simulated) { + globalScene.queueAbilityDisplay(pokemon, passive, true); + abShown = true; } + const message = attr.getTriggerMessage(pokemon, ability.name, args); + if (message) { + if (!simulated) { + globalScene.queueMessage(message); + } + messages.push(message); + } + + applyFunc(attr, passive); + + if (abShown) { + globalScene.queueAbilityDisplay(pokemon, passive, false); + } + + if (pokemon.summonData && !pokemon.summonData.abilitiesApplied.includes(ability.id)) { + pokemon.summonData.abilitiesApplied.push(ability.id); + } + if (pokemon.battleData && !simulated && !pokemon.battleData.abilitiesApplied.includes(ability.id)) { + pokemon.battleData.abilitiesApplied.push(ability.id); + } + + globalScene.clearPhaseQueueSplice(); } } @@ -5352,6 +5365,16 @@ function calculateShellBellRecovery(pokemon: Pokemon): number { * @extends AbAttr */ export class PostDamageAbAttr extends AbAttr { + public canApplyPostDamage( + pokemon: Pokemon, + damage: number, + passive: boolean, + simulated: boolean, + args: any[], + source?: Pokemon): boolean { + return true; + } + public applyPostDamage( pokemon: Pokemon, damage: number, @@ -5359,9 +5382,7 @@ export class PostDamageAbAttr extends AbAttr { simulated: boolean, args: any[], source?: Pokemon, - ): boolean { - return false; - } + ): void {} } /** @@ -5383,27 +5404,13 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr { this.hpRatio = hpRatio; } - /** - * Applies the switch-out logic after the Pokémon takes damage. - * Checks various conditions based on the moves used by the Pokémon, the opponents' moves, and - * the Pokémon's health after damage to determine whether the switch-out should occur. - * - * @param pokemon The Pokémon that took damage. - * @param damage The amount of damage taken by the Pokémon. - * @param passive N/A - * @param simulated Whether the ability is being simulated. - * @param args N/A - * @param source The Pokemon that dealt damage - * @returns `true` if the switch-out logic was successfully applied - */ - public override applyPostDamage( + public override canApplyPostDamage( pokemon: Pokemon, damage: number, passive: boolean, simulated: boolean, args: any[], - source?: Pokemon, - ): boolean { + source?: Pokemon): boolean { const moveHistory = pokemon.getMoveHistory(); // Will not activate when the Pokémon's HP is lowered by cutting its own HP const fordbiddenAttackingMoves = [ Moves.BELLY_DRUM, Moves.SUBSTITUTE, Moves.CURSE, Moves.PAIN_SPLIT ]; @@ -5437,7 +5444,6 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr { } if (pokemon.hp + damage >= pokemon.getMaxHp() * this.hpRatio) { - // Activates if it falls below half and recovers back above half from a Shell Bell const shellBellHeal = calculateShellBellRecovery(pokemon); if (pokemon.hp - shellBellHeal < pokemon.getMaxHp() * this.hpRatio) { for (const opponent of pokemon.getOpponents()) { @@ -5445,16 +5451,27 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr { return false; } } - return this.helper.switchOutLogic(pokemon); - } else { - return false; + return true; } - } else { - return false; } + + return false; } - public getFailedText(user: Pokemon, target: Pokemon, move: Move, cancelled: Utils.BooleanHolder): string | null { - return this.helper.getFailedText(target); + + /** + * Applies the switch-out logic after the Pokémon takes damage. + * Checks various conditions based on the moves used by the Pokémon, the opponents' moves, and + * the Pokémon's health after damage to determine whether the switch-out should occur. + * + * @param pokemon The Pokémon that took damage. + * @param damage N/A + * @param passive N/A + * @param simulated Whether the ability is being simulated. + * @param args N/A + * @param source N/A + */ + public override applyPostDamage(pokemon: Pokemon, damage: number, passive: boolean, simulated: boolean, args: any[], source?: Pokemon): void { + this.helper.switchOutLogic(pokemon); } } @@ -5474,16 +5491,16 @@ function applyAbAttrsInternal( attrType: Constructor, pokemon: Pokemon | null, applyFunc: AbAttrApplyFunc, + successFunc: AbAttrSuccessFunc, args: any[], - showAbilityInstant = false, - simulated = false, + simulated: boolean = false, messages: string[] = [], gainedMidTurn = false, priorityCondition?: (p: number) => boolean ) { for (const passive of [ false, true ]) { if (pokemon) { - applySingleAbAttrs(pokemon, passive, attrType, applyFunc, args, gainedMidTurn, simulated, priorityCondition, showAbilityInstant, messages); + applySingleAbAttrs(pokemon, passive, attrType, applyFunc, successFunc, args, gainedMidTurn, simulated, priorityCondition, messages); globalScene.clearPhaseQueueSplice(); } } @@ -5500,8 +5517,8 @@ export function applyAbAttrs( attrType, pokemon, (attr, passive) => attr.apply(pokemon, passive, simulated, cancelled, args), + (attr, passive) => attr.canApply(pokemon, passive, simulated, args), args, - false, simulated, ); } @@ -5516,8 +5533,8 @@ export function applyPostBattleInitAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostBattleInit(pokemon, passive, simulated, args), + (attr, passive) => attr.canApplyPostBattleInit(pokemon, passive, simulated, args), args, - false, simulated, ); } @@ -5535,8 +5552,8 @@ export function applyPreDefendAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args), + (attr, passive) => attr.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args), args, - false, simulated, ); } @@ -5554,8 +5571,7 @@ export function applyPostDefendAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args), - args, - false, + (attr, passive) => attr.canApplyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args), args, simulated, ); } @@ -5573,8 +5589,8 @@ export function applyPostMoveUsedAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostMoveUsed(pokemon, move, source, targets, simulated, args), + (attr, passive) => attr.canApplyPostMoveUsed(pokemon, move, source, targets, simulated, args), args, - false, simulated, ); } @@ -5591,6 +5607,7 @@ export function applyStatMultiplierAbAttrs( attrType, pokemon, (attr, passive) => attr.applyStatStage(pokemon, passive, simulated, stat, statValue, args), + (attr, passive) => attr.canApplyStatStage(pokemon, passive, simulated, stat, statValue, args), args, ); } @@ -5606,8 +5623,7 @@ export function applyPostSetStatusAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args), - args, - false, + (attr, passive) => attr.canApplyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args), args, simulated, ); } @@ -5625,6 +5641,7 @@ export function applyPostDamageAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostDamage(pokemon, damage, passive, simulated, args, source), + (attr, passive) => attr.canApplyPostDamage(pokemon, damage, passive, simulated, args, source), args, ); } @@ -5652,9 +5669,8 @@ export function applyFieldStatMultiplierAbAttrs( applyAbAttrsInternal( attrType, pokemon, - (attr, passive) => - attr.applyFieldStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, hasApplied, args), - args, + (attr, passive) => attr.applyFieldStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, hasApplied, args), + (attr, passive) => attr.canApplyFieldStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, hasApplied, args), args, ); } @@ -5670,8 +5686,8 @@ export function applyPreAttackAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPreAttack(pokemon, passive, simulated, defender, move, args), + (attr, passive) => attr.canApplyPreAttack(pokemon, passive, simulated, defender, move, args), args, - false, simulated, ); } @@ -5689,8 +5705,7 @@ export function applyPostAttackAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args), - args, - false, + (attr, passive) => attr.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args), args, simulated, ); } @@ -5706,8 +5721,8 @@ export function applyPostKnockOutAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostKnockOut(pokemon, passive, simulated, knockedOut, args), + (attr, passive) => attr.canApplyPostKnockOut(pokemon, passive, simulated, knockedOut, args), args, - false, simulated, ); } @@ -5722,8 +5737,8 @@ export function applyPostVictoryAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostVictory(pokemon, passive, simulated, args), + (attr, passive) => attr.canApplyPostVictory(pokemon, passive, simulated, args), args, - false, simulated, ); } @@ -5739,8 +5754,8 @@ export function applyPostSummonAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args), + (attr, passive) => attr.canApplyPostSummon(pokemon, passive, simulated, args), args, - false, simulated, [], false, @@ -5758,8 +5773,8 @@ export function applyPreSwitchOutAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPreSwitchOut(pokemon, passive, simulated, args), + (attr, passive) => attr.canApplyPreSwitchOut(pokemon, passive, simulated, args), args, - true, simulated, ); } @@ -5775,8 +5790,8 @@ export function applyPreLeaveFieldAbAttrs( pokemon, (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, args), + (attr, passive) => attr.canApplyPreLeaveField(pokemon, passive, simulated, args), args, - true, simulated ); } @@ -5793,8 +5808,7 @@ export function applyPreStatStageChangeAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args), - args, - false, + (attr, passive) => attr.canApplyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args), args, simulated, ); } @@ -5812,8 +5826,7 @@ export function applyPostStatStageChangeAbAttrs( attrType, pokemon, (attr, _passive) => attr.applyPostStatStageChange(pokemon, simulated, stats, stages, selfTarget, args), - args, - false, + (attr, _passive) => attr.canApplyPostStatStageChange(pokemon, simulated, stats, stages, selfTarget, args), args, simulated, ); } @@ -5830,8 +5843,8 @@ export function applyPreSetStatusAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPreSetStatus(pokemon, passive, simulated, effect, cancelled, args), + (attr, passive) => attr.canApplyPreSetStatus(pokemon, passive, simulated, effect, cancelled, args), args, - false, simulated, ); } @@ -5848,8 +5861,7 @@ export function applyPreApplyBattlerTagAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args), - args, - false, + (attr, passive) => attr.canApplyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args), args, simulated, ); } @@ -5866,8 +5878,7 @@ export function applyPreWeatherEffectAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPreWeatherEffect(pokemon, passive, simulated, weather, cancelled, args), - args, - true, + (attr, passive) => attr.canApplyPreWeatherEffect(pokemon, passive, simulated, weather, cancelled, args), args, simulated, ); } @@ -5882,8 +5893,8 @@ export function applyPostTurnAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostTurn(pokemon, passive, simulated, args), + (attr, passive) => attr.canApplyPostTurn(pokemon, passive, simulated, args), args, - false, simulated, ); } @@ -5899,8 +5910,8 @@ export function applyPostWeatherChangeAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostWeatherChange(pokemon, passive, simulated, weather, args), + (attr, passive) => attr.canApplyPostWeatherChange(pokemon, passive, simulated, weather, args), args, - false, simulated, ); } @@ -5916,8 +5927,8 @@ export function applyPostWeatherLapseAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostWeatherLapse(pokemon, passive, simulated, weather, args), + (attr, passive) => attr.canApplyPostWeatherLapse(pokemon, passive, simulated, weather, args), args, - false, simulated, ); } @@ -5933,8 +5944,8 @@ export function applyPostTerrainChangeAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostTerrainChange(pokemon, passive, simulated, terrain, args), + (attr, passive) => attr.canApplyPostTerrainChange(pokemon, passive, simulated, terrain, args), args, - false, simulated, ); } @@ -5952,8 +5963,7 @@ export function applyCheckTrappedAbAttrs( attrType, pokemon, (attr, passive) => attr.applyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args), - args, - false, + (attr, passive) => attr.canApplyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args), args, simulated, messages, ); @@ -5969,8 +5979,8 @@ export function applyPostBattleAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostBattle(pokemon, passive, simulated, args), + (attr, passive) => attr.canApplyPostBattle(pokemon, passive, simulated, args), args, - false, simulated, ); } @@ -5988,8 +5998,8 @@ export function applyPostFaintAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args), + (attr, passive) => attr.canApplyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args), args, - false, simulated, ); } @@ -6004,6 +6014,7 @@ export function applyPostItemLostAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostItemLost(pokemon, simulated, args), + (attr, passive) => attr.canApplyPostItemLost(pokemon, simulated, args), args, ); } @@ -6013,20 +6024,36 @@ export function applyPostItemLostAbAttrs( * * Ignores passives as they don't change and shouldn't be reapplied when main abilities change */ -export function applyOnGainAbAttrs(pokemon: Pokemon, passive = false, simulated = false, ...args: any[]): void { - applySingleAbAttrs(pokemon, passive, PostSummonAbAttr, (attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args), args, true, simulated); +export function applyOnGainAbAttrs( + pokemon: Pokemon, + passive: boolean = false, + simulated: boolean = false, + ...args: any[]): void { + applySingleAbAttrs( + pokemon, + passive, + PostSummonAbAttr, + (attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args), + (attr, passive) => attr.canApplyPostSummon(pokemon, passive, simulated, args), + args, + true, + simulated, + ); } /** * Clears primal weather/neutralizing gas during the turn if {@linkcode pokemon}'s ability corresponds to one */ export function applyOnLoseAbAttrs(pokemon: Pokemon, passive = false, simulated = false, ...args: any[]): void { - applySingleAbAttrs(pokemon, passive, PreLeaveFieldAbAttr, (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [ ...args, true ]), args, true, simulated); -} - -function queueShowAbility(pokemon: Pokemon, passive: boolean): void { - globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.id, passive)); - globalScene.clearPhaseQueueSplice(); + applySingleAbAttrs( + pokemon, + passive, + PreLeaveFieldAbAttr, + (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [ ...args, true ]), + (attr, passive) => attr.canApplyPreLeaveField(pokemon, passive, simulated, [ ...args, true ]), + args, + true, + simulated); } /** @@ -6913,7 +6940,7 @@ export function initAbilities() { new Ability(Abilities.GORILLA_TACTICS, 8) .attr(GorillaTacticsAbAttr), new Ability(Abilities.NEUTRALIZING_GAS, 8, 2) - .attr(PostSummonAddArenaTagAbAttr, ArenaTagType.NEUTRALIZING_GAS, 0) + .attr(PostSummonAddArenaTagAbAttr, true, ArenaTagType.NEUTRALIZING_GAS, 0) .attr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) diff --git a/src/data/balance/signature-species.ts b/src/data/balance/signature-species.ts new file mode 100644 index 00000000000..a1b73af40cd --- /dev/null +++ b/src/data/balance/signature-species.ts @@ -0,0 +1,162 @@ +import { Species } from "#enums/species"; + +export type SignatureSpecies = { + [key in string]: (Species | Species[])[]; +}; + +/* + * The signature species for each Gym Leader, Elite Four member, and Champion. + * The key is the trainer type, and the value is an array of Species or Species arrays. + * This is in a separate const so it can be accessed from other places and not just the trainerConfigs + */ +export const signatureSpecies: SignatureSpecies = { + // Gym Leaders- Kanto + BROCK: [Species.GEODUDE, Species.ONIX], + MISTY: [Species.STARYU, Species.PSYDUCK], + LT_SURGE: [Species.VOLTORB, Species.PIKACHU, Species.ELECTABUZZ], + ERIKA: [Species.ODDISH, Species.BELLSPROUT, Species.TANGELA, Species.HOPPIP], + JANINE: [Species.VENONAT, Species.SPINARAK, Species.ZUBAT], + SABRINA: [Species.ABRA, Species.MR_MIME, Species.ESPEON], + BLAINE: [Species.GROWLITHE, Species.PONYTA, Species.MAGMAR], + GIOVANNI: [Species.SANDILE, Species.MURKROW, Species.NIDORAN_M, Species.NIDORAN_F], + // Gym Leaders- Johto + FALKNER: [Species.PIDGEY, Species.HOOTHOOT, Species.DODUO], + BUGSY: [Species.SCYTHER, Species.HERACROSS, Species.SHUCKLE, Species.PINSIR], + WHITNEY: [Species.JIGGLYPUFF, Species.MILTANK, Species.AIPOM, Species.GIRAFARIG], + MORTY: [Species.GASTLY, Species.MISDREAVUS, Species.SABLEYE], + CHUCK: [Species.POLIWRATH, Species.MANKEY], + JASMINE: [Species.MAGNEMITE, Species.STEELIX], + PRYCE: [Species.SEEL, Species.SWINUB], + CLAIR: [Species.DRATINI, Species.HORSEA, Species.GYARADOS], + // Gym Leaders- Hoenn + ROXANNE: [Species.GEODUDE, Species.NOSEPASS], + BRAWLY: [Species.MACHOP, Species.MAKUHITA], + WATTSON: [Species.MAGNEMITE, Species.VOLTORB, Species.ELECTRIKE], + FLANNERY: [Species.SLUGMA, Species.TORKOAL, Species.NUMEL], + NORMAN: [Species.SLAKOTH, Species.SPINDA, Species.ZIGZAGOON, Species.KECLEON], + WINONA: [Species.SWABLU, Species.WINGULL, Species.TROPIUS, Species.SKARMORY], + TATE: [Species.SOLROCK, Species.NATU, Species.CHIMECHO, Species.GALLADE], + LIZA: [Species.LUNATONE, Species.SPOINK, Species.BALTOY, Species.GARDEVOIR], + JUAN: [Species.HORSEA, Species.BARBOACH, Species.SPHEAL, Species.RELICANTH], + // Gym Leaders- Sinnoh + ROARK: [Species.CRANIDOS, Species.LARVITAR, Species.GEODUDE], + GARDENIA: [Species.ROSELIA, Species.TANGELA, Species.TURTWIG], + MAYLENE: [Species.LUCARIO, Species.MEDITITE, Species.CHIMCHAR], + CRASHER_WAKE: [Species.BUIZEL, Species.WOOPER, Species.PIPLUP, Species.MAGIKARP], + FANTINA: [Species.MISDREAVUS, Species.DRIFLOON, Species.SPIRITOMB], + BYRON: [Species.SHIELDON, Species.BRONZOR, Species.AGGRON], + CANDICE: [Species.SNEASEL, Species.SNOVER, Species.SNORUNT], + VOLKNER: [Species.SHINX, Species.CHINCHOU, Species.ROTOM], + // Gym Leaders- Unova + CILAN: [Species.PANSAGE, Species.FOONGUS, Species.PETILIL], + CHILI: [Species.PANSEAR, Species.DARUMAKA, Species.NUMEL], + CRESS: [Species.PANPOUR, Species.TYMPOLE, Species.SLOWPOKE], + CHEREN: [Species.LILLIPUP, Species.MINCCINO, Species.PIDOVE], + LENORA: [Species.PATRAT, Species.DEERLING, Species.AUDINO], + ROXIE: [Species.VENIPEDE, Species.TRUBBISH, Species.SKORUPI], + BURGH: [Species.SEWADDLE, Species.SHELMET, Species.KARRABLAST], + ELESA: [Species.EMOLGA, Species.BLITZLE, Species.JOLTIK], + CLAY: [Species.DRILBUR, Species.SANDILE, Species.GOLETT], + SKYLA: [Species.DUCKLETT, Species.WOOBAT, Species.RUFFLET], + BRYCEN: [Species.CRYOGONAL, Species.VANILLITE, Species.CUBCHOO], + DRAYDEN: [Species.DRUDDIGON, Species.AXEW, Species.DEINO], + MARLON: [Species.WAILMER, Species.FRILLISH, Species.TIRTOUGA], + // Gym Leaders- Kalos + VIOLA: [Species.SURSKIT, Species.SCATTERBUG], + GRANT: [Species.AMAURA, Species.TYRUNT], + KORRINA: [Species.HAWLUCHA, Species.LUCARIO, Species.MIENFOO], + RAMOS: [Species.SKIDDO, Species.HOPPIP, Species.BELLSPROUT], + CLEMONT: [Species.HELIOPTILE, Species.MAGNEMITE, Species.EMOLGA], + VALERIE: [Species.SYLVEON, Species.MAWILE, Species.MR_MIME], + OLYMPIA: [Species.ESPURR, Species.SIGILYPH, Species.SLOWKING], + WULFRIC: [Species.BERGMITE, Species.SNOVER, Species.CRYOGONAL], + // Gym Leaders- Galar + MILO: [Species.GOSSIFLEUR, Species.APPLIN, Species.BOUNSWEET], + NESSA: [Species.CHEWTLE, Species.ARROKUDA, Species.WIMPOD], + KABU: [Species.SIZZLIPEDE, Species.VULPIX, Species.TORKOAL], + BEA: [Species.GALAR_FARFETCHD, Species.MACHOP, Species.CLOBBOPUS], + ALLISTER: [Species.GALAR_YAMASK, Species.GALAR_CORSOLA, Species.GASTLY], + OPAL: [Species.MILCERY, Species.TOGETIC, Species.GALAR_WEEZING], + BEDE: [Species.HATENNA, Species.GALAR_PONYTA, Species.GARDEVOIR], + GORDIE: [Species.ROLYCOLY, Species.STONJOURNER, Species.BINACLE], + MELONY: [Species.SNOM, Species.GALAR_DARUMAKA, Species.GALAR_MR_MIME], + PIERS: [Species.GALAR_ZIGZAGOON, Species.SCRAGGY, Species.INKAY], + MARNIE: [Species.IMPIDIMP, Species.PURRLOIN, Species.MORPEKO], + RAIHAN: [Species.DURALUDON, Species.TURTONATOR, Species.GOOMY], + // Gym Leaders- Paldea; First slot is Tera + KATY: [Species.TEDDIURSA, Species.NYMBLE, Species.TAROUNTULA], // Tera Bug Teddiursa + BRASSIUS: [Species.SUDOWOODO, Species.BRAMBLIN, Species.SMOLIV], // Tera Grass Sudowoodo + IONO: [Species.MISDREAVUS, Species.TADBULB, Species.WATTREL], // Tera Ghost Misdreavus + KOFU: [Species.CRABRAWLER, Species.VELUZA, Species.WIGLETT, Species.WINGULL], // Tera Water Crabrawler + LARRY: [Species.STARLY, Species.DUNSPARCE, Species.LECHONK, Species.KOMALA], // Tera Normal Starly + RYME: [Species.TOXEL, Species.GREAVARD, Species.SHUPPET, Species.MIMIKYU], // Tera Ghost Toxel + TULIP: [Species.FLABEBE, Species.FLITTLE, Species.RALTS, Species.GIRAFARIG], // Tera Psychic Flabebe + GRUSHA: [Species.SWABLU, Species.CETODDLE, Species.CUBCHOO, Species.ALOLA_VULPIX], // Tera Ice Swablu + + // Elite Four- Kanto + LORELEI: [ + Species.JYNX, + [Species.SLOWBRO, Species.GALAR_SLOWBRO], + Species.LAPRAS, + [Species.CLOYSTER, Species.ALOLA_SANDSLASH], + ], + BRUNO: [Species.MACHAMP, Species.HITMONCHAN, Species.HITMONLEE, [Species.GOLEM, Species.ALOLA_GOLEM]], + AGATHA: [Species.GENGAR, [Species.ARBOK, Species.WEEZING], Species.CROBAT, Species.ALOLA_MAROWAK], + LANCE: [Species.DRAGONITE, Species.GYARADOS, Species.AERODACTYL, Species.ALOLA_EXEGGUTOR], + // Elite Four- Johto (Bruno included) + WILL: [Species.XATU, Species.JYNX, [Species.SLOWBRO, Species.SLOWKING], Species.EXEGGUTOR], + KOGA: [[Species.MUK, Species.WEEZING], [Species.VENOMOTH, Species.ARIADOS], Species.CROBAT, Species.TENTACRUEL], + KAREN: [Species.UMBREON, Species.HONCHKROW, Species.HOUNDOOM, Species.WEAVILE], + // Elite Four- Hoenn + SIDNEY: [ + [Species.SHIFTRY, Species.CACTURNE], + [Species.SHARPEDO, Species.CRAWDAUNT], + Species.ABSOL, + Species.MIGHTYENA, + ], + PHOEBE: [Species.SABLEYE, Species.DUSKNOIR, Species.BANETTE, [Species.DRIFBLIM, Species.MISMAGIUS]], + GLACIA: [Species.GLALIE, Species.WALREIN, Species.FROSLASS, Species.ABOMASNOW], + DRAKE: [Species.ALTARIA, Species.SALAMENCE, Species.FLYGON, Species.KINGDRA], + // Elite Four- Sinnoh + AARON: [[Species.SCIZOR, Species.KLEAVOR], Species.HERACROSS, [Species.VESPIQUEN, Species.YANMEGA], Species.DRAPION], + BERTHA: [Species.WHISCASH, Species.HIPPOWDON, Species.GLISCOR, Species.RHYPERIOR], + FLINT: [ + [Species.RAPIDASH, Species.FLAREON], + Species.MAGMORTAR, + [Species.STEELIX, Species.LOPUNNY], + Species.INFERNAPE, + ], // Tera Fire Steelix or Lopunny + LUCIAN: [Species.MR_MIME, Species.GALLADE, Species.BRONZONG, [Species.ALAKAZAM, Species.ESPEON]], + // Elite Four- Unova + SHAUNTAL: [Species.COFAGRIGUS, Species.CHANDELURE, Species.GOLURK, Species.JELLICENT], + MARSHAL: [Species.CONKELDURR, Species.MIENSHAO, Species.THROH, Species.SAWK], + GRIMSLEY: [Species.LIEPARD, Species.KINGAMBIT, Species.SCRAFTY, Species.KROOKODILE], + CAITLIN: [Species.MUSHARNA, Species.GOTHITELLE, Species.SIGILYPH, Species.REUNICLUS], + // Elite Four- Kalos + MALVA: [Species.PYROAR, Species.TORKOAL, Species.CHANDELURE, Species.TALONFLAME], + SIEBOLD: [Species.CLAWITZER, Species.GYARADOS, Species.BARBARACLE, Species.STARMIE], + WIKSTROM: [Species.KLEFKI, Species.PROBOPASS, Species.SCIZOR, Species.AEGISLASH], + DRASNA: [Species.DRAGALGE, Species.DRUDDIGON, Species.ALTARIA, Species.NOIVERN], + // Elite Four- Alola + HALA: [Species.HARIYAMA, Species.BEWEAR, Species.CRABOMINABLE, [Species.POLIWRATH, Species.ANNIHILAPE]], + MOLAYNE: [Species.KLEFKI, Species.MAGNEZONE, Species.METAGROSS, Species.ALOLA_DUGTRIO], + OLIVIA: [Species.RELICANTH, Species.CARBINK, Species.ALOLA_GOLEM, Species.LYCANROC], + ACEROLA: [[Species.BANETTE, Species.DRIFBLIM], Species.MIMIKYU, Species.DHELMISE, Species.PALOSSAND], + KAHILI: [[Species.BRAVIARY, Species.MANDIBUZZ], Species.HAWLUCHA, Species.ORICORIO, Species.TOUCANNON], + // Elite Four- Galar + MARNIE_ELITE: [Species.MORPEKO, Species.LIEPARD, [Species.TOXICROAK, Species.SCRAFTY], Species.GRIMMSNARL], + NESSA_ELITE: [Species.GOLISOPOD, [Species.QUAGSIRE, Species.PELIPPER], Species.TOXAPEX, Species.DREDNAW], + BEA_ELITE: [Species.HAWLUCHA, [Species.GRAPPLOCT, Species.SIRFETCHD], Species.FALINKS, Species.MACHAMP], + ALLISTER_ELITE: [Species.DUSKNOIR, [Species.POLTEAGEIST, Species.RUNERIGUS], Species.CURSOLA, Species.GENGAR], + RAIHAN_ELITE: [Species.GOODRA, [Species.TORKOAL, Species.TURTONATOR], Species.FLYGON, Species.ARCHALUDON], + // Elite Four- Paldea + RIKA: [Species.CLODSIRE, [Species.DUGTRIO, Species.DONPHAN], Species.CAMERUPT, Species.WHISCASH], // Tera Ground Clodsire + POPPY: [Species.TINKATON, Species.BRONZONG, Species.CORVIKNIGHT, Species.COPPERAJAH], // Tera Steel Tinkaton + LARRY_ELITE: [Species.FLAMIGO, Species.STARAPTOR, [Species.ALTARIA, Species.TROPIUS], Species.ORICORIO], // Tera Flying Flamigo; random Oricorio + HASSEL: [Species.BAXCALIBUR, [Species.FLAPPLE, Species.APPLETUN], Species.DRAGALGE, Species.NOIVERN], // Tera Dragon Baxcalibur + // Elite Four- BBL + CRISPIN: [Species.BLAZIKEN, Species.MAGMORTAR, [Species.CAMERUPT, Species.TALONFLAME], Species.ROTOM], // Tera Fire Blaziken; Heat Rotom + AMARYS: [Species.METAGROSS, Species.SCIZOR, Species.EMPOLEON, Species.SKARMORY], // Tera Steel Metagross + LACEY: [Species.EXCADRILL, Species.PRIMARINA, [Species.WHIMSICOTT, Species.ALCREMIE], Species.GRANBULL], // Tera Fairy Excadrill + DRAYTON: [Species.ARCHALUDON, Species.DRAGONITE, Species.HAXORUS, Species.SCEPTILE], // Tera Dragon Archaludon +}; diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index 1a3e463de3f..4636e68d6d6 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -912,24 +912,12 @@ export const pokemonFormChanges: PokemonFormChanges = { new SpeciesFormChange(Species.MIMIKYU, "disguised", "busted", new SpeciesFormChangeAbilityTrigger(), true), new SpeciesFormChange(Species.MIMIKYU, "busted", "disguised", new SpeciesFormChangeAbilityTrigger(), true) ], - [Species.SOLGALEO]: [ - new SpeciesFormChange(Species.SOLGALEO, "", "radiant-sun", new SpeciesFormChangePreMoveTrigger(Moves.SUNSTEEL_STRIKE), true), - new SpeciesFormChange(Species.SOLGALEO, "radiant-sun", "", new SpeciesFormChangeActiveTrigger(false), true) - ], - [Species.LUNALA]: [ - new SpeciesFormChange(Species.LUNALA, "", "full-moon", new SpeciesFormChangePreMoveTrigger(Moves.MOONGEIST_BEAM), true), - new SpeciesFormChange(Species.LUNALA, "full-moon", "", new SpeciesFormChangeActiveTrigger(false), true) - ], [Species.NECROZMA]: [ new SpeciesFormChange(Species.NECROZMA, "", "dawn-wings", new SpeciesFormChangeItemTrigger(FormChangeItem.N_LUNARIZER), false, getSpeciesDependentFormChangeCondition(Species.LUNALA)), new SpeciesFormChange(Species.NECROZMA, "", "dusk-mane", new SpeciesFormChangeItemTrigger(FormChangeItem.N_SOLARIZER), false, getSpeciesDependentFormChangeCondition(Species.SOLGALEO)), new SpeciesFormChange(Species.NECROZMA, "dawn-wings", "ultra", new SpeciesFormChangeItemTrigger(FormChangeItem.ULTRANECROZIUM_Z)), new SpeciesFormChange(Species.NECROZMA, "dusk-mane", "ultra", new SpeciesFormChangeItemTrigger(FormChangeItem.ULTRANECROZIUM_Z)) ], - [Species.MARSHADOW]: [ - new SpeciesFormChange(Species.MARSHADOW, "", "zenith", new SpeciesFormChangePreMoveTrigger(Moves.SPECTRAL_THIEF), true), - new SpeciesFormChange(Species.MARSHADOW, "zenith", "", new SpeciesFormChangeActiveTrigger(false), true) - ], [Species.MELMETAL]: [ new SpeciesFormChange(Species.MELMETAL, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS)) ], diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index fee2bc5c9da..1bf97209613 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -63,8 +63,6 @@ export const normalForm: Species[] = [ Species.FROGADIER, Species.GRENINJA, Species.ROCKRUFF, - Species.SOLGALEO, - Species.LUNALA, Species.NECROZMA, Species.MAGEARNA, Species.MARSHADOW, @@ -2678,14 +2676,8 @@ export function initSpecies() { new PokemonSpecies(Species.TAPU_FINI, 7, true, false, false, "Land Spirit Pokémon", PokemonType.WATER, PokemonType.FAIRY, 1.3, 21.2, Abilities.MISTY_SURGE, Abilities.NONE, Abilities.TELEPATHY, 570, 70, 75, 115, 95, 130, 85, 3, 50, 285, GrowthRate.SLOW, null, false), new PokemonSpecies(Species.COSMOG, 7, true, false, false, "Nebula Pokémon", PokemonType.PSYCHIC, null, 0.2, 0.1, Abilities.UNAWARE, Abilities.NONE, Abilities.NONE, 200, 43, 29, 31, 29, 31, 37, 45, 0, 40, GrowthRate.SLOW, null, false), new PokemonSpecies(Species.COSMOEM, 7, true, false, false, "Protostar Pokémon", PokemonType.PSYCHIC, null, 0.1, 999.9, Abilities.STURDY, Abilities.NONE, Abilities.NONE, 400, 43, 29, 131, 29, 131, 37, 45, 0, 140, GrowthRate.SLOW, null, false), - new PokemonSpecies(Species.SOLGALEO, 7, false, true, false, "Sunne Pokémon", PokemonType.PSYCHIC, PokemonType.STEEL, 3.4, 230, Abilities.FULL_METAL_BODY, Abilities.NONE, Abilities.NONE, 680, 137, 137, 107, 113, 89, 97, 45, 0, 340, GrowthRate.SLOW, null, false, true, - new PokemonForm("Normal", "", PokemonType.PSYCHIC, PokemonType.STEEL, 3.4, 230, Abilities.FULL_METAL_BODY, Abilities.NONE, Abilities.NONE, 680, 137, 137, 107, 113, 89, 97, 45, 0, 340, false, null, true), - new PokemonForm("Radiant Sun", "radiant-sun", PokemonType.PSYCHIC, PokemonType.STEEL, 3.4, 230, Abilities.FULL_METAL_BODY, Abilities.NONE, Abilities.NONE, 680, 137, 137, 107, 113, 89, 97, 45, 0, 340), - ), - new PokemonSpecies(Species.LUNALA, 7, false, true, false, "Moone Pokémon", PokemonType.PSYCHIC, PokemonType.GHOST, 4, 120, Abilities.SHADOW_SHIELD, Abilities.NONE, Abilities.NONE, 680, 137, 113, 89, 137, 107, 97, 45, 0, 340, GrowthRate.SLOW, null, false, true, - new PokemonForm("Normal", "", PokemonType.PSYCHIC, PokemonType.GHOST, 4, 120, Abilities.SHADOW_SHIELD, Abilities.NONE, Abilities.NONE, 680, 137, 113, 89, 137, 107, 97, 45, 0, 340, false, null, true), - new PokemonForm("Full Moon", "full-moon", PokemonType.PSYCHIC, PokemonType.GHOST, 4, 120, Abilities.SHADOW_SHIELD, Abilities.NONE, Abilities.NONE, 680, 137, 113, 89, 137, 107, 97, 45, 0, 340), - ), + new PokemonSpecies(Species.SOLGALEO, 7, false, true, false, "Sunne Pokémon", PokemonType.PSYCHIC, PokemonType.STEEL, 3.4, 230, Abilities.FULL_METAL_BODY, Abilities.NONE, Abilities.NONE, 680, 137, 137, 107, 113, 89, 97, 45, 0, 340, GrowthRate.SLOW, null, false), + new PokemonSpecies(Species.LUNALA, 7, false, true, false, "Moone Pokémon", PokemonType.PSYCHIC, PokemonType.GHOST, 4, 120, Abilities.SHADOW_SHIELD, Abilities.NONE, Abilities.NONE, 680, 137, 113, 89, 137, 107, 97, 45, 0, 340, GrowthRate.SLOW, null, false), new PokemonSpecies(Species.NIHILEGO, 7, true, false, false, "Parasite Pokémon", PokemonType.ROCK, PokemonType.POISON, 1.2, 55.5, Abilities.BEAST_BOOST, Abilities.NONE, Abilities.NONE, 570, 109, 53, 47, 127, 131, 103, 45, 0, 285, GrowthRate.SLOW, null, false), new PokemonSpecies(Species.BUZZWOLE, 7, true, false, false, "Swollen Pokémon", PokemonType.BUG, PokemonType.FIGHTING, 2.4, 333.6, Abilities.BEAST_BOOST, Abilities.NONE, Abilities.NONE, 570, 107, 139, 139, 53, 53, 79, 45, 0, 285, GrowthRate.SLOW, null, false), new PokemonSpecies(Species.PHEROMOSA, 7, true, false, false, "Lissome Pokémon", PokemonType.BUG, PokemonType.FIGHTING, 1.8, 25, Abilities.BEAST_BOOST, Abilities.NONE, Abilities.NONE, 570, 71, 137, 37, 137, 37, 151, 45, 0, 285, GrowthRate.SLOW, null, false), @@ -2705,7 +2697,7 @@ export function initSpecies() { ), new PokemonSpecies(Species.MARSHADOW, 7, false, false, true, "Gloomdweller Pokémon", PokemonType.FIGHTING, PokemonType.GHOST, 0.7, 22.2, Abilities.TECHNICIAN, Abilities.NONE, Abilities.NONE, 600, 90, 125, 80, 90, 90, 125, 3, 0, 300, GrowthRate.SLOW, null, false, true, new PokemonForm("Normal", "", PokemonType.FIGHTING, PokemonType.GHOST, 0.7, 22.2, Abilities.TECHNICIAN, Abilities.NONE, Abilities.NONE, 600, 90, 125, 80, 90, 90, 125, 3, 0, 300, false, null, true), - new PokemonForm("Zenith", "zenith", PokemonType.FIGHTING, PokemonType.GHOST, 0.7, 22.2, Abilities.TECHNICIAN, Abilities.NONE, Abilities.NONE, 600, 90, 125, 80, 90, 90, 125, 3, 0, 300), + new PokemonForm("Zenith", "zenith", PokemonType.FIGHTING, PokemonType.GHOST, 0.7, 22.2, Abilities.TECHNICIAN, Abilities.NONE, Abilities.NONE, 600, 90, 125, 80, 90, 90, 125, 3, 0, 300, false, null, false, true) ), new PokemonSpecies(Species.POIPOLE, 7, true, false, false, "Poison Pin Pokémon", PokemonType.POISON, null, 0.6, 1.8, Abilities.BEAST_BOOST, Abilities.NONE, Abilities.NONE, 420, 67, 73, 67, 73, 67, 73, 45, 0, 210, GrowthRate.SLOW, null, false), new PokemonSpecies(Species.NAGANADEL, 7, true, false, false, "Poison Pin Pokémon", PokemonType.POISON, PokemonType.DRAGON, 3.6, 150, Abilities.BEAST_BOOST, Abilities.NONE, Abilities.NONE, 540, 73, 73, 73, 127, 73, 121, 45, 0, 270, GrowthRate.SLOW, null, false), diff --git a/src/data/trainer-config.ts b/src/data/trainer-config.ts index 693338c145e..3884aa05b13 100644 --- a/src/data/trainer-config.ts +++ b/src/data/trainer-config.ts @@ -22,6 +22,7 @@ import { PartyMemberStrength } from "#enums/party-member-strength"; import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; import { Gender } from "#app/data/gender"; +import { signatureSpecies } from "./balance/signature-species"; /** Minimum BST for Pokemon generated onto the Elite Four's teams */ const ELITE_FOUR_MINIMUM_BST = 460; @@ -1744,148 +1745,6 @@ function getSpeciesFilterRandomPartyMemberFunc( }; } -type SignatureSpecies = { - [key in string]: (Species | Species[])[]; -}; - -/* - * The signature species for each Gym Leader, Elite Four member, and Champion. - * The key is the trainer type, and the value is an array of Species or Species arrays. - * This is in a separate const so it can be accessed from other places and not just the trainerConfigs - */ -export const signatureSpecies: SignatureSpecies = { - BROCK: [Species.GEODUDE, Species.ONIX], - MISTY: [Species.STARYU, Species.PSYDUCK], - LT_SURGE: [Species.VOLTORB, Species.PIKACHU, Species.ELECTABUZZ], - ERIKA: [Species.ODDISH, Species.BELLSPROUT, Species.TANGELA, Species.HOPPIP], - JANINE: [Species.VENONAT, Species.SPINARAK, Species.ZUBAT], - SABRINA: [Species.ABRA, Species.MR_MIME, Species.ESPEON], - BLAINE: [Species.GROWLITHE, Species.PONYTA, Species.MAGMAR], - GIOVANNI: [Species.SANDILE, Species.MURKROW, Species.NIDORAN_M, Species.NIDORAN_F], - FALKNER: [Species.PIDGEY, Species.HOOTHOOT, Species.DODUO], - BUGSY: [Species.SCYTHER, Species.HERACROSS, Species.SHUCKLE, Species.PINSIR], - WHITNEY: [Species.JIGGLYPUFF, Species.MILTANK, Species.AIPOM, Species.GIRAFARIG], - MORTY: [Species.GASTLY, Species.MISDREAVUS, Species.SABLEYE], - CHUCK: [Species.POLIWRATH, Species.MANKEY], - JASMINE: [Species.MAGNEMITE, Species.STEELIX], - PRYCE: [Species.SEEL, Species.SWINUB], - CLAIR: [Species.DRATINI, Species.HORSEA, Species.GYARADOS], - ROXANNE: [Species.GEODUDE, Species.NOSEPASS], - BRAWLY: [Species.MACHOP, Species.MAKUHITA], - WATTSON: [Species.MAGNEMITE, Species.VOLTORB, Species.ELECTRIKE], - FLANNERY: [Species.SLUGMA, Species.TORKOAL, Species.NUMEL], - NORMAN: [Species.SLAKOTH, Species.SPINDA, Species.ZIGZAGOON, Species.KECLEON], - WINONA: [Species.SWABLU, Species.WINGULL, Species.TROPIUS, Species.SKARMORY], - TATE: [Species.SOLROCK, Species.NATU, Species.CHIMECHO, Species.GALLADE], - LIZA: [Species.LUNATONE, Species.SPOINK, Species.BALTOY, Species.GARDEVOIR], - JUAN: [Species.HORSEA, Species.BARBOACH, Species.SPHEAL, Species.RELICANTH], - ROARK: [Species.CRANIDOS, Species.LARVITAR, Species.GEODUDE], - GARDENIA: [Species.ROSELIA, Species.TANGELA, Species.TURTWIG], - MAYLENE: [Species.LUCARIO, Species.MEDITITE, Species.CHIMCHAR], - CRASHER_WAKE: [Species.BUIZEL, Species.WOOPER, Species.PIPLUP, Species.MAGIKARP], - FANTINA: [Species.MISDREAVUS, Species.DRIFLOON, Species.SPIRITOMB], - BYRON: [Species.SHIELDON, Species.BRONZOR, Species.AGGRON], - CANDICE: [Species.SNEASEL, Species.SNOVER, Species.SNORUNT], - VOLKNER: [Species.SHINX, Species.CHINCHOU, Species.ROTOM], - CILAN: [Species.PANSAGE, Species.FOONGUS, Species.PETILIL], - CHILI: [Species.PANSEAR, Species.DARUMAKA, Species.NUMEL], - CRESS: [Species.PANPOUR, Species.TYMPOLE, Species.SLOWPOKE], - CHEREN: [Species.LILLIPUP, Species.MINCCINO, Species.PIDOVE], - LENORA: [Species.PATRAT, Species.DEERLING, Species.AUDINO], - ROXIE: [Species.VENIPEDE, Species.TRUBBISH, Species.SKORUPI], - BURGH: [Species.SEWADDLE, Species.SHELMET, Species.KARRABLAST], - ELESA: [Species.EMOLGA, Species.BLITZLE, Species.JOLTIK], - CLAY: [Species.DRILBUR, Species.SANDILE, Species.GOLETT], - SKYLA: [Species.DUCKLETT, Species.WOOBAT, Species.RUFFLET], - BRYCEN: [Species.CRYOGONAL, Species.VANILLITE, Species.CUBCHOO], - DRAYDEN: [Species.DRUDDIGON, Species.AXEW, Species.DEINO], - MARLON: [Species.WAILMER, Species.FRILLISH, Species.TIRTOUGA], - VIOLA: [Species.SURSKIT, Species.SCATTERBUG], - GRANT: [Species.AMAURA, Species.TYRUNT], - KORRINA: [Species.HAWLUCHA, Species.LUCARIO, Species.MIENFOO], - RAMOS: [Species.SKIDDO, Species.HOPPIP, Species.BELLSPROUT], - CLEMONT: [Species.HELIOPTILE, Species.MAGNEMITE, Species.EMOLGA], - VALERIE: [Species.SYLVEON, Species.MAWILE, Species.MR_MIME], - OLYMPIA: [Species.ESPURR, Species.SIGILYPH, Species.SLOWKING], - WULFRIC: [Species.BERGMITE, Species.SNOVER, Species.CRYOGONAL], - MILO: [Species.GOSSIFLEUR, Species.APPLIN, Species.BOUNSWEET], - NESSA: [Species.CHEWTLE, Species.ARROKUDA, Species.WIMPOD], - KABU: [Species.SIZZLIPEDE, Species.VULPIX, Species.TORKOAL], - BEA: [Species.GALAR_FARFETCHD, Species.MACHOP, Species.CLOBBOPUS], - ALLISTER: [Species.GALAR_YAMASK, Species.GALAR_CORSOLA, Species.GASTLY], - OPAL: [Species.MILCERY, Species.TOGETIC, Species.GALAR_WEEZING], - BEDE: [Species.HATENNA, Species.GALAR_PONYTA, Species.GARDEVOIR], - GORDIE: [Species.ROLYCOLY, Species.STONJOURNER, Species.BINACLE], - MELONY: [Species.SNOM, Species.GALAR_DARUMAKA, Species.GALAR_MR_MIME], - PIERS: [Species.GALAR_ZIGZAGOON, Species.SCRAGGY, Species.INKAY], - MARNIE: [Species.IMPIDIMP, Species.PURRLOIN, Species.MORPEKO], - RAIHAN: [Species.DURALUDON, Species.TURTONATOR, Species.GOOMY], - KATY: [Species.TEDDIURSA, Species.NYMBLE, Species.TAROUNTULA], // Tera Bug Teddiursa - BRASSIUS: [Species.SUDOWOODO, Species.BRAMBLIN, Species.SMOLIV], // Tera Grass Sudowoodo - IONO: [Species.MISDREAVUS, Species.TADBULB, Species.WATTREL], // Tera Ghost Misdreavus - KOFU: [Species.CRABRAWLER, Species.VELUZA, Species.WIGLETT, Species.WINGULL], // Tera Water Crabrawler - LARRY: [Species.STARLY, Species.DUNSPARCE, Species.LECHONK, Species.KOMALA], // Tera Normal Starly - RYME: [Species.TOXEL, Species.GREAVARD, Species.SHUPPET, Species.MIMIKYU], // Tera Ghost Toxel - TULIP: [Species.FLABEBE, Species.FLITTLE, Species.RALTS, Species.GIRAFARIG], // Tera Psychic Flabebe - GRUSHA: [Species.SWABLU, Species.CETODDLE, Species.CUBCHOO, Species.ALOLA_VULPIX], // Tera Ice Swablu - LORELEI: [ - Species.JYNX, - [Species.SLOWBRO, Species.GALAR_SLOWBRO], - Species.LAPRAS, - [Species.CLOYSTER, Species.ALOLA_SANDSLASH], - ], - BRUNO: [Species.MACHAMP, Species.HITMONCHAN, Species.HITMONLEE, [Species.GOLEM, Species.ALOLA_GOLEM]], - AGATHA: [Species.GENGAR, [Species.ARBOK, Species.WEEZING], Species.CROBAT, Species.ALOLA_MAROWAK], - LANCE: [Species.DRAGONITE, Species.GYARADOS, Species.AERODACTYL, Species.ALOLA_EXEGGUTOR], - WILL: [Species.XATU, Species.JYNX, [Species.SLOWBRO, Species.SLOWKING], Species.EXEGGUTOR], - KOGA: [[Species.MUK, Species.WEEZING], [Species.VENOMOTH, Species.ARIADOS], Species.CROBAT, Species.TENTACRUEL], - KAREN: [Species.UMBREON, Species.HONCHKROW, Species.HOUNDOOM, Species.WEAVILE], - SIDNEY: [ - [Species.SHIFTRY, Species.CACTURNE], - [Species.SHARPEDO, Species.CRAWDAUNT], - Species.ABSOL, - Species.MIGHTYENA, - ], - PHOEBE: [Species.SABLEYE, Species.DUSKNOIR, Species.BANETTE, [Species.DRIFBLIM, Species.MISMAGIUS]], - GLACIA: [Species.GLALIE, Species.WALREIN, Species.FROSLASS, Species.ABOMASNOW], - DRAKE: [Species.ALTARIA, Species.SALAMENCE, Species.FLYGON, Species.KINGDRA], - AARON: [[Species.SCIZOR, Species.KLEAVOR], Species.HERACROSS, [Species.VESPIQUEN, Species.YANMEGA], Species.DRAPION], - BERTHA: [Species.WHISCASH, Species.HIPPOWDON, Species.GLISCOR, Species.RHYPERIOR], - FLINT: [ - [Species.RAPIDASH, Species.FLAREON], - Species.MAGMORTAR, - [Species.STEELIX, Species.LOPUNNY], - Species.INFERNAPE, - ], // Tera Fire Steelix or Lopunny - LUCIAN: [Species.MR_MIME, Species.GALLADE, Species.BRONZONG, [Species.ALAKAZAM, Species.ESPEON]], - SHAUNTAL: [Species.COFAGRIGUS, Species.CHANDELURE, Species.GOLURK, Species.JELLICENT], - MARSHAL: [Species.CONKELDURR, Species.MIENSHAO, Species.THROH, Species.SAWK], - GRIMSLEY: [Species.LIEPARD, Species.KINGAMBIT, Species.SCRAFTY, Species.KROOKODILE], - CAITLIN: [Species.MUSHARNA, Species.GOTHITELLE, Species.SIGILYPH, Species.REUNICLUS], - MALVA: [Species.PYROAR, Species.TORKOAL, Species.CHANDELURE, Species.TALONFLAME], - SIEBOLD: [Species.CLAWITZER, Species.GYARADOS, Species.BARBARACLE, Species.STARMIE], - WIKSTROM: [Species.KLEFKI, Species.PROBOPASS, Species.SCIZOR, Species.AEGISLASH], - DRASNA: [Species.DRAGALGE, Species.DRUDDIGON, Species.ALTARIA, Species.NOIVERN], - HALA: [Species.HARIYAMA, Species.BEWEAR, Species.CRABOMINABLE, [Species.POLIWRATH, Species.ANNIHILAPE]], - MOLAYNE: [Species.KLEFKI, Species.MAGNEZONE, Species.METAGROSS, Species.ALOLA_DUGTRIO], - OLIVIA: [Species.RELICANTH, Species.CARBINK, Species.ALOLA_GOLEM, Species.LYCANROC], - ACEROLA: [[Species.BANETTE, Species.DRIFBLIM], Species.MIMIKYU, Species.DHELMISE, Species.PALOSSAND], - KAHILI: [[Species.BRAVIARY, Species.MANDIBUZZ], Species.HAWLUCHA, Species.ORICORIO, Species.TOUCANNON], - MARNIE_ELITE: [Species.MORPEKO, Species.LIEPARD, [Species.TOXICROAK, Species.SCRAFTY], Species.GRIMMSNARL], - NESSA_ELITE: [Species.GOLISOPOD, [Species.QUAGSIRE, Species.PELIPPER], Species.TOXAPEX, Species.DREDNAW], - BEA_ELITE: [Species.HAWLUCHA, [Species.GRAPPLOCT, Species.SIRFETCHD], Species.FALINKS, Species.MACHAMP], - ALLISTER_ELITE: [Species.DUSKNOIR, [Species.POLTEAGEIST, Species.RUNERIGUS], Species.CURSOLA, Species.GENGAR], - RAIHAN_ELITE: [Species.GOODRA, [Species.TORKOAL, Species.TURTONATOR], Species.FLYGON, Species.ARCHALUDON], - RIKA: [Species.CLODSIRE, [Species.DUGTRIO, Species.DONPHAN], Species.CAMERUPT, Species.WHISCASH], // Tera Ground Clodsire - POPPY: [Species.TINKATON, Species.BRONZONG, Species.CORVIKNIGHT, Species.COPPERAJAH], // Tera Steel Tinkaton - LARRY_ELITE: [Species.FLAMIGO, Species.STARAPTOR, [Species.ALTARIA, Species.TROPIUS], Species.ORICORIO], // Tera Flying Flamigo; random Oricorio - HASSEL: [Species.BAXCALIBUR, [Species.FLAPPLE, Species.APPLETUN], Species.DRAGALGE, Species.NOIVERN], // Tera Dragon Baxcalibur - CRISPIN: [Species.BLAZIKEN, Species.MAGMORTAR, [Species.CAMERUPT, Species.TALONFLAME], Species.ROTOM], // Tera Fire Blaziken; Heat Rotom - AMARYS: [Species.METAGROSS, Species.SCIZOR, Species.EMPOLEON, Species.SKARMORY], // Tera Steel Metagross - LACEY: [Species.EXCADRILL, Species.PRIMARINA, [Species.WHIMSICOTT, Species.ALCREMIE], Species.GRANBULL], // Tera Fairy Excadrill - DRAYTON: [Species.ARCHALUDON, Species.DRAGONITE, Species.HAXORUS, Species.SCEPTILE], // Tera Dragon Archaludon -}; - export const trainerConfigs: TrainerConfigs = { [TrainerType.UNKNOWN]: new TrainerConfig(0).setHasGenders(), [TrainerType.ACE_TRAINER]: new TrainerConfig(++t) diff --git a/src/field/arena.ts b/src/field/arena.ts index 2ed94e0e3e3..997b80b3237 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -303,6 +303,11 @@ export class Arena { return true; } + /** Returns weather or not the weather can be changed to {@linkcode weather} */ + canSetWeather(weather: WeatherType): boolean { + return !(this.weather?.weatherType === (weather || undefined)); + } + /** * Attempts to set a new weather to the battle * @param weather {@linkcode WeatherType} new {@linkcode WeatherType} to set @@ -314,7 +319,7 @@ export class Arena { return this.trySetWeatherOverride(Overrides.WEATHER_OVERRIDE); } - if (this.weather?.weatherType === (weather || undefined)) { + if (!this.canSetWeather(weather)) { return false; } @@ -388,8 +393,13 @@ export class Arena { }); } + /** Returns whether or not the terrain can be set to {@linkcode terrain} */ + canSetTerrain(terrain: TerrainType): boolean { + return !(this.terrain?.terrainType === (terrain || undefined)); + } + trySetTerrain(terrain: TerrainType, hasPokemonSource: boolean, ignoreAnim = false): boolean { - if (this.terrain?.terrainType === (terrain || undefined)) { + if (!this.canSetTerrain(terrain)) { return false; } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 058996ba093..ec0a60c0108 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -6920,10 +6920,10 @@ export class EnemyPokemon extends Pokemon { if ( speciesId in Overrides.OPP_FORM_OVERRIDES && - Overrides.OPP_FORM_OVERRIDES[speciesId] && + !isNullOrUndefined(Overrides.OPP_FORM_OVERRIDES[speciesId]) && this.species.forms[Overrides.OPP_FORM_OVERRIDES[speciesId]] ) { - this.formIndex = Overrides.OPP_FORM_OVERRIDES[speciesId] ?? 0; + this.formIndex = Overrides.OPP_FORM_OVERRIDES[speciesId]; } if (!dataSource) { diff --git a/src/field/trainer.ts b/src/field/trainer.ts index 4325b54a783..98919ebfa93 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -9,7 +9,6 @@ import { TrainerSlot, trainerConfigs, trainerPartyTemplates, - signatureSpecies, TeraAIMode, } from "#app/data/trainer-config"; import type { EnemyPokemon } from "#app/field/pokemon"; @@ -22,6 +21,7 @@ import i18next from "i18next"; import { PartyMemberStrength } from "#enums/party-member-strength"; import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; +import { signatureSpecies } from "#app/data/balance/signature-species"; export enum TrainerVariant { DEFAULT, diff --git a/src/loading-scene.ts b/src/loading-scene.ts index 1e98c175487..295dc318db4 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -79,6 +79,7 @@ export class LoadingScene extends SceneBase { this.loadImage("icon_owned", "ui"); this.loadImage("icon_egg_move", "ui"); this.loadImage("ability_bar_left", "ui"); + this.loadImage("ability_bar_right", "ui"); this.loadImage("bgm_bar", "ui"); this.loadImage("party_exp_bar", "ui"); this.loadImage("achv_bar", "ui"); diff --git a/src/overrides.ts b/src/overrides.ts index dc04f72049a..882a634ff90 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/consistent-type-imports */ import { type PokeballCounts } from "#app/battle-scene"; import { EvolutionItem } from "#app/data/balance/pokemon-evolutions"; import { Gender } from "#app/data/gender"; diff --git a/src/phase.ts b/src/phase.ts index 8da00d78b61..20cc7cc4063 100644 --- a/src/phase.ts +++ b/src/phase.ts @@ -1,11 +1,7 @@ import { globalScene } from "#app/global-scene"; export class Phase { - start() { - if (globalScene.abilityBar.shown) { - globalScene.abilityBar.resetAutoHideTimer(); - } - } + start() {} end() { globalScene.shiftPhase(); diff --git a/src/phases/hide-ability-phase.ts b/src/phases/hide-ability-phase.ts new file mode 100644 index 00000000000..0745b3f832a --- /dev/null +++ b/src/phases/hide-ability-phase.ts @@ -0,0 +1,27 @@ +import { globalScene } from "#app/global-scene"; +import type { BattlerIndex } from "#app/battle"; +import { PokemonPhase } from "./pokemon-phase"; + +export class HideAbilityPhase extends PokemonPhase { + private passive: boolean; + + constructor(battlerIndex: BattlerIndex, passive = false) { + super(battlerIndex); + + this.passive = passive; + } + + start() { + super.start(); + + const pokemon = this.getPokemon(); + + if (pokemon) { + globalScene.abilityBar.hide().then(() => { + this.end(); + }); + } else { + this.end(); + } + } +} diff --git a/src/phases/message-phase.ts b/src/phases/message-phase.ts index af4e63c6916..f671307d86a 100644 --- a/src/phases/message-phase.ts +++ b/src/phases/message-phase.ts @@ -61,12 +61,4 @@ export class MessagePhase extends Phase { ); } } - - end() { - if (globalScene.abilityBar.shown) { - globalScene.abilityBar.hide(); - } - - super.end(); - } } diff --git a/src/phases/pokemon-transform-phase.ts b/src/phases/pokemon-transform-phase.ts index 70b390dc6de..b33689321b5 100644 --- a/src/phases/pokemon-transform-phase.ts +++ b/src/phases/pokemon-transform-phase.ts @@ -5,6 +5,8 @@ import { EFFECTIVE_STATS, BATTLE_STATS } from "#enums/stat"; import { PokemonMove } from "#app/field/pokemon"; import { globalScene } from "#app/global-scene"; import { PokemonPhase } from "./pokemon-phase"; +import { getPokemonNameWithAffix } from "#app/messages"; +import i18next from "i18next"; /** * Transforms a Pokemon into another Pokemon on the field. @@ -62,6 +64,13 @@ export class PokemonTransformPhase extends PokemonPhase { globalScene.playSound("battle_anims/PRSFX- Transform"); } + globalScene.queueMessage( + i18next.t("abilityTriggers:postSummonTransform", { + pokemonNameWithAffix: getPokemonNameWithAffix(user), + targetName: target.name, + }), + ); + promises.push( user.loadAssets(false).then(() => { user.playAnim(); diff --git a/src/phases/select-starter-phase.ts b/src/phases/select-starter-phase.ts index 103174a4c83..0f4360b0af6 100644 --- a/src/phases/select-starter-phase.ts +++ b/src/phases/select-starter-phase.ts @@ -53,6 +53,7 @@ export class SelectStarterPhase extends Phase { let starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0)); if ( starter.species.speciesId in Overrides.STARTER_FORM_OVERRIDES && + !Utils.isNullOrUndefined(Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]) && starter.species.forms[Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!] ) { starterFormIndex = Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!; diff --git a/src/phases/show-ability-phase.ts b/src/phases/show-ability-phase.ts index 326b3a55ce3..1b3c6dde568 100644 --- a/src/phases/show-ability-phase.ts +++ b/src/phases/show-ability-phase.ts @@ -1,36 +1,60 @@ import { globalScene } from "#app/global-scene"; import type { BattlerIndex } from "#app/battle"; import { PokemonPhase } from "./pokemon-phase"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { HideAbilityPhase } from "#app/phases/hide-ability-phase"; export class ShowAbilityPhase extends PokemonPhase { private passive: boolean; + private pokemonName: string; + private abilityName: string; + private pokemonOnField: boolean; constructor(battlerIndex: BattlerIndex, passive = false) { super(battlerIndex); this.passive = passive; + + const pokemon = this.getPokemon(); + if (pokemon) { + // Set these now as the pokemon object may change before the queued phase is run + this.pokemonName = getPokemonNameWithAffix(pokemon); + this.abilityName = (passive ? this.getPokemon().getPassiveAbility() : this.getPokemon().getAbility()).name; + this.pokemonOnField = true; + } else { + this.pokemonOnField = false; + } } start() { super.start(); + if (!this.pokemonOnField || !this.getPokemon()) { + return this.end(); + } + + // If the bar is already out, hide it before showing the new one + if (globalScene.abilityBar.isVisible()) { + globalScene.unshiftPhase(new HideAbilityPhase(this.battlerIndex, this.passive)); + globalScene.unshiftPhase(new ShowAbilityPhase(this.battlerIndex, this.passive)); + return this.end(); + } + const pokemon = this.getPokemon(); - if (pokemon) { - if (!pokemon.isPlayer()) { - /** If its an enemy pokemon, list it as last enemy to use ability or move */ - globalScene.currentBattle.lastEnemyInvolved = pokemon.getBattlerIndex() % 2; - } else { - globalScene.currentBattle.lastPlayerInvolved = pokemon.getBattlerIndex() % 2; - } - - globalScene.abilityBar.showAbility(pokemon, this.passive); + if (!pokemon.isPlayer()) { + /** If its an enemy pokemon, list it as last enemy to use ability or move */ + globalScene.currentBattle.lastEnemyInvolved = pokemon.getBattlerIndex() % 2; + } else { + globalScene.currentBattle.lastPlayerInvolved = pokemon.getBattlerIndex() % 2; + } + globalScene.abilityBar.showAbility(this.pokemonName, this.abilityName, this.passive, this.player).then(() => { if (pokemon?.battleData) { pokemon.battleData.abilityRevealed = true; } - } - this.end(); + this.end(); + }); } } diff --git a/src/ui/ability-bar.ts b/src/ui/ability-bar.ts index 7fe9c0d8052..5481791de64 100644 --- a/src/ui/ability-bar.ts +++ b/src/ui/ability-bar.ts @@ -1,31 +1,33 @@ -import { getPokemonNameWithAffix } from "#app/messages"; import { globalScene } from "#app/global-scene"; -import type Pokemon from "../field/pokemon"; import { TextStyle, addTextObject } from "./text"; import i18next from "i18next"; -const hiddenX = -118; -const shownX = 0; +const barWidth = 118; +const screenLeft = 0; const baseY = -116; export default class AbilityBar extends Phaser.GameObjects.Container { - private bg: Phaser.GameObjects.Image; + private abilityBars: Phaser.GameObjects.Image[]; private abilityBarText: Phaser.GameObjects.Text; - - private tween: Phaser.Tweens.Tween | null; - private autoHideTimer: NodeJS.Timeout | null; - - public shown: boolean; + private player: boolean; + private screenRight: number; // hold screenRight in case size changes between show and hide + private shown: boolean; constructor() { - super(globalScene, hiddenX, baseY); + super(globalScene, barWidth, baseY); + this.abilityBars = []; + this.player = true; + this.shown = false; } setup(): void { - this.bg = globalScene.add.image(0, 0, "ability_bar_left"); - this.bg.setOrigin(0, 0); - - this.add(this.bg); + for (const key of ["ability_bar_right", "ability_bar_left"]) { + const bar = globalScene.add.image(0, 0, key); + bar.setOrigin(0, 0); + bar.setVisible(false); + this.add(bar); + this.abilityBars.push(bar); + } this.abilityBarText = addTextObject(15, 3, "", TextStyle.MESSAGE, { fontSize: "72px", @@ -33,72 +35,80 @@ export default class AbilityBar extends Phaser.GameObjects.Container { this.abilityBarText.setOrigin(0, 0); this.abilityBarText.setWordWrapWidth(600, true); this.add(this.abilityBarText); + this.bringToTop(this.abilityBarText); this.setVisible(false); - this.shown = false; + this.setX(-barWidth); // start hidden (right edge of bar at x=0) } - showAbility(pokemon: Pokemon, passive = false): void { - this.abilityBarText.setText( - `${i18next.t("fightUiHandler:abilityFlyInText", { pokemonName: getPokemonNameWithAffix(pokemon), passive: passive ? i18next.t("fightUiHandler:passive") : "", abilityName: !passive ? pokemon.getAbility().name : pokemon.getPassiveAbility().name })}`, - ); + public override setVisible(value: boolean): this { + this.abilityBars[+this.player].setVisible(value); + this.shown = value; + return this; + } - if (this.shown) { - return; + public async startTween(config: any, text?: string): Promise { + this.setVisible(true); + if (text) { + this.abilityBarText.setText(text); } + return new Promise(resolve => { + globalScene.tweens.add({ + ...config, + onComplete: () => { + if (config.onComplete) { + config.onComplete(); + } + resolve(); + }, + }); + }); + } + public async showAbility(pokemonName: string, abilityName: string, passive = false, player = true): Promise { + const text = `${i18next.t("fightUiHandler:abilityFlyInText", { pokemonName: pokemonName, passive: passive ? i18next.t("fightUiHandler:passive") : "", abilityName: abilityName })}`; + this.screenRight = globalScene.scaledCanvas.width; + if (player !== this.player) { + // Move the bar if it has changed from the player to enemy side (or vice versa) + this.setX(player ? -barWidth : this.screenRight); + this.player = player; + } globalScene.fieldUI.bringToTop(this); - this.y = baseY + (globalScene.currentBattle.double ? 14 : 0); - this.tween = globalScene.tweens.add({ - targets: this, - x: shownX, - duration: 500, - ease: "Sine.easeOut", - onComplete: () => { - this.tween = null; - this.resetAutoHideTimer(); - }, - }); + let y = baseY; + if (this.player) { + y += globalScene.currentBattle.double ? 14 : 0; + } else { + y -= globalScene.currentBattle.double ? 28 : 14; + } - this.setVisible(true); - this.shown = true; + this.setY(y); + + return this.startTween( + { + targets: this, + x: this.player ? screenLeft : this.screenRight - barWidth, + duration: 500, + ease: "Sine.easeOut", + hold: 1000, + }, + text, + ); } - hide(): void { - if (!this.shown) { - return; - } - - if (this.autoHideTimer) { - clearInterval(this.autoHideTimer); - } - - if (this.tween) { - this.tween.stop(); - } - - this.tween = globalScene.tweens.add({ + public async hide(): Promise { + return this.startTween({ targets: this, - x: -91, - duration: 500, + x: this.player ? -barWidth : this.screenRight, + duration: 200, ease: "Sine.easeIn", onComplete: () => { - this.tween = null; this.setVisible(false); }, }); - - this.shown = false; } - resetAutoHideTimer(): void { - if (this.autoHideTimer) { - clearInterval(this.autoHideTimer); - } - this.autoHideTimer = setTimeout(() => { - this.hide(); - this.autoHideTimer = null; - }, 2500); + public isVisible(): boolean { + return this.shown; } } diff --git a/test/moves/secret_power.test.ts b/test/moves/secret_power.test.ts index 28dc7efc434..37f1664251b 100644 --- a/test/moves/secret_power.test.ts +++ b/test/moves/secret_power.test.ts @@ -68,7 +68,7 @@ describe("Moves - Secret Power", () => { await game.classicMode.startBattle([Species.BLASTOISE, Species.CHARIZARD]); const sereneGraceAttr = allAbilities[Abilities.SERENE_GRACE].getAttrs(MoveEffectChanceMultiplierAbAttr)[0]; - vi.spyOn(sereneGraceAttr, "apply"); + vi.spyOn(sereneGraceAttr, "canApply"); game.move.select(Moves.WATER_PLEDGE, 0, BattlerIndex.ENEMY); game.move.select(Moves.FIRE_PLEDGE, 1, BattlerIndex.ENEMY_2); @@ -86,8 +86,8 @@ describe("Moves - Secret Power", () => { await game.phaseInterceptor.to("BerryPhase", false); - expect(sereneGraceAttr.apply).toHaveBeenCalledOnce(); - expect(sereneGraceAttr.apply).toHaveLastReturnedWith(true); + expect(sereneGraceAttr.canApply).toHaveBeenCalledOnce(); + expect(sereneGraceAttr.canApply).toHaveLastReturnedWith(true); expect(rainbowEffect.apply).toHaveBeenCalledTimes(0); });