diff --git a/global.d.ts b/global.d.ts new file mode 100644 index 00000000000..f4dfa7d4cb2 --- /dev/null +++ b/global.d.ts @@ -0,0 +1,14 @@ +import type { SetupServerApi } from "msw/node"; + +export {}; + +declare global { + /** + * Only used in testing. + * Can technically be undefined/null but for ease of use we are going to assume it is always defined. + * Used to load i18n files exclusively. + * + * To set up your own server in a test see `game_data.test.ts` + */ + var i18nServer: SetupServerApi; +} diff --git a/public/images/pokemon/275.png b/public/images/pokemon/275.png index 07a6fe725ad..61f98ed1655 100644 Binary files a/public/images/pokemon/275.png and b/public/images/pokemon/275.png differ diff --git a/public/images/pokemon/6706.png b/public/images/pokemon/6706.png index 44f627acd10..e967b550871 100644 Binary files a/public/images/pokemon/6706.png and b/public/images/pokemon/6706.png differ diff --git a/public/images/pokemon/back/6706.png b/public/images/pokemon/back/6706.png index e7620078a5b..82c64e98535 100644 Binary files a/public/images/pokemon/back/6706.png and b/public/images/pokemon/back/6706.png differ diff --git a/public/images/pokemon/exp/6706.png b/public/images/pokemon/exp/6706.png index ea745dfb04b..77b10183ec7 100644 Binary files a/public/images/pokemon/exp/6706.png and b/public/images/pokemon/exp/6706.png differ diff --git a/public/images/pokemon/exp/705.png b/public/images/pokemon/exp/705.png index 3413bcd9fa3..670e8be5d51 100644 Binary files a/public/images/pokemon/exp/705.png and b/public/images/pokemon/exp/705.png differ diff --git a/public/images/pokemon/exp/back/6706.png b/public/images/pokemon/exp/back/6706.png index 4bda5b08d10..7842bbbffad 100644 Binary files a/public/images/pokemon/exp/back/6706.png and b/public/images/pokemon/exp/back/6706.png differ diff --git a/public/images/pokemon/female/275.png b/public/images/pokemon/female/275.png index 7f251793a15..c3c358716b2 100644 Binary files a/public/images/pokemon/female/275.png and b/public/images/pokemon/female/275.png differ diff --git a/public/images/pokemon/shiny/275.png b/public/images/pokemon/shiny/275.png index fee57d64804..de8271cdbd2 100644 Binary files a/public/images/pokemon/shiny/275.png and b/public/images/pokemon/shiny/275.png differ diff --git a/public/images/pokemon/shiny/female/275.png b/public/images/pokemon/shiny/female/275.png index c8f9a90b721..a0571493279 100644 Binary files a/public/images/pokemon/shiny/female/275.png and b/public/images/pokemon/shiny/female/275.png differ diff --git a/public/images/pokemon/variant/6706.json b/public/images/pokemon/variant/6706.json new file mode 100644 index 00000000000..2b9ead28219 --- /dev/null +++ b/public/images/pokemon/variant/6706.json @@ -0,0 +1,40 @@ +{ + "1": { + "566678": "0e6296", + "e0e4f4": "513981", + "625287": "4e4094", + "536273": "1f1233", + "988b98": "b24c86", + "9170b9": "8b69c3", + "c4cce1": "3b235c", + "e6d3e9": "f1a4c5", + "bfacc1": "da75a5", + "515f70": "197497", + "c5cee3": "63cee1", + "8e96aa": "301848", + "8b93a6": "3aa8c4", + "80737f": "8a2166", + "4b454f": "6f1357", + "36404c": "0c5474", + "b791f2": "c7a1e5" + }, + "2": { + "566678": "8e480b", + "e0e4f4": "176463", + "625287": "274159", + "536273": "02262c", + "988b98": "2a6563", + "9170b9": "2f667c", + "c4cce1": "0d484a", + "e6d3e9": "9cead8", + "bfacc1": "5db6a9", + "515f70": "a34205", + "c5cee3": "f7af58", + "8e96aa": "073338", + "8b93a6": "d27e26", + "80737f": "2b736f", + "4b454f": "194f51", + "36404c": "842401", + "b791f2": "4a9699" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/6706_2.json b/public/images/pokemon/variant/6706_2.json deleted file mode 100644 index f6bbf20116e..00000000000 --- a/public/images/pokemon/variant/6706_2.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "textures": [ - { - "image": "6706_2.png", - "format": "RGBA8888", - "size": { - "w": 82, - "h": 82 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 82, - "h": 72 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 82, - "h": 72 - }, - "frame": { - "x": 0, - "y": 0, - "w": 82, - "h": 72 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:02eb46aa66ac70df612e129b7801a85c:a77cca14b23f4f3aece64d1a82449a0f:d60cc2e5ae2bd18de8ee3ab0649593ee$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/6706_2.png b/public/images/pokemon/variant/6706_2.png deleted file mode 100644 index 7cf1495d1a3..00000000000 Binary files a/public/images/pokemon/variant/6706_2.png and /dev/null differ diff --git a/public/images/pokemon/variant/6706_3.json b/public/images/pokemon/variant/6706_3.json deleted file mode 100644 index 615ca90e004..00000000000 --- a/public/images/pokemon/variant/6706_3.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "textures": [ - { - "image": "6706_3.png", - "format": "RGBA8888", - "size": { - "w": 82, - "h": 82 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 82, - "h": 72 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 82, - "h": 72 - }, - "frame": { - "x": 0, - "y": 0, - "w": 82, - "h": 72 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:02eb46aa66ac70df612e129b7801a85c:a77cca14b23f4f3aece64d1a82449a0f:d60cc2e5ae2bd18de8ee3ab0649593ee$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/6706_3.png b/public/images/pokemon/variant/6706_3.png deleted file mode 100644 index 001cab641f1..00000000000 Binary files a/public/images/pokemon/variant/6706_3.png and /dev/null differ diff --git a/public/images/pokemon/variant/_masterlist.json b/public/images/pokemon/variant/_masterlist.json index 4aabcac72a4..27856b4c065 100644 --- a/public/images/pokemon/variant/_masterlist.json +++ b/public/images/pokemon/variant/_masterlist.json @@ -3731,8 +3731,8 @@ ], "6706": [ 0, - 2, - 2 + 1, + 1 ], "6713": [ 0, @@ -7774,8 +7774,8 @@ ], "6706": [ 0, - 2, - 2 + 1, + 1 ], "6713": [ 0, @@ -8523,8 +8523,8 @@ ], "705": [ 0, - 2, - 2 + 1, + 1 ], "706": [ 0, @@ -9598,8 +9598,8 @@ ], "6706": [ 0, - 2, - 2 + 1, + 1 ], "female": {}, "back": { @@ -11135,8 +11135,8 @@ ], "6706": [ 0, - 2, - 2 + 1, + 1 ], "6713": [ 0, diff --git a/public/images/pokemon/variant/back/6706.json b/public/images/pokemon/variant/back/6706.json new file mode 100644 index 00000000000..2de5352e936 --- /dev/null +++ b/public/images/pokemon/variant/back/6706.json @@ -0,0 +1,38 @@ +{ + "1": { + "566678": "197497", + "8e96aa": "3b235c", + "929aad": "3aa8c4", + "625287": "4e4094", + "536273": "301848", + "988b98": "b24c86", + "36404c": "0c5474", + "c4cce1": "513981", + "e6d3e9": "f1a4c5", + "bfacc1": "d074a0", + "546475": "0e6296", + "c5cee3": "63cee1", + "80737f": "8a2166", + "4b454f": "6f1357", + "9170b9": "8b69c3", + "b791f2": "c7a1e5" + }, + "2": { + "566678": "a34205", + "8e96aa": "073338", + "929aad": "d27e26", + "625287": "0e3f47", + "536273": "042329", + "988b98": "2b736f", + "36404c": "842401", + "c4cce1": "0d484a", + "e6d3e9": "9cead8", + "bfacc1": "5db6a9", + "546475": "8e480b", + "c5cee3": "f7af58", + "80737f": "194f51", + "4b454f": "274159", + "9170b9": "2f667c", + "b791f2": "4a9699" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/back/6706_2.json b/public/images/pokemon/variant/back/6706_2.json deleted file mode 100644 index dccfbce60c5..00000000000 --- a/public/images/pokemon/variant/back/6706_2.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "textures": [ - { - "image": "6706_2.png", - "format": "RGBA8888", - "size": { - "w": 79, - "h": 79 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 79, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 79, - "h": 73 - }, - "frame": { - "x": 0, - "y": 0, - "w": 79, - "h": 73 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:64f7e6dfa489012922487e45ba53d557:4d24652b372939abe499497c4b6647b0:d60cc2e5ae2bd18de8ee3ab0649593ee$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/back/6706_2.png b/public/images/pokemon/variant/back/6706_2.png deleted file mode 100644 index e52f1af4ba5..00000000000 Binary files a/public/images/pokemon/variant/back/6706_2.png and /dev/null differ diff --git a/public/images/pokemon/variant/back/6706_3.json b/public/images/pokemon/variant/back/6706_3.json deleted file mode 100644 index 9a97ce27059..00000000000 --- a/public/images/pokemon/variant/back/6706_3.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "textures": [ - { - "image": "6706_3.png", - "format": "RGBA8888", - "size": { - "w": 79, - "h": 79 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 79, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 79, - "h": 73 - }, - "frame": { - "x": 0, - "y": 0, - "w": 79, - "h": 73 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:64f7e6dfa489012922487e45ba53d557:4d24652b372939abe499497c4b6647b0:d60cc2e5ae2bd18de8ee3ab0649593ee$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/back/6706_3.png b/public/images/pokemon/variant/back/6706_3.png deleted file mode 100644 index fb780d01499..00000000000 Binary files a/public/images/pokemon/variant/back/6706_3.png and /dev/null differ diff --git a/public/images/pokemon/variant/exp/6706.json b/public/images/pokemon/variant/exp/6706.json new file mode 100644 index 00000000000..2a5f8e112ee --- /dev/null +++ b/public/images/pokemon/variant/exp/6706.json @@ -0,0 +1,40 @@ +{ + "1": { + "566678": "0e6296", + "e0e4f4": "513981", + "625287": "4e4094", + "536273": "1f1233", + "988b98": "b24c86", + "36404c": "0c5474", + "c4cce1": "3b235c", + "e6d3e9": "f1a4c5", + "bfacc1": "da75a5", + "515f70": "197497", + "c5cee3": "63cee1", + "b791f2": "c7a1e5", + "8b93a6": "3aa8c4", + "80737f": "8a2166", + "4b454f": "6f1357", + "9170b9": "8b69c3", + "8e96aa": "301848" + }, + "2": { + "566678": "8e480b", + "e0e4f4": "176463", + "625287": "274159", + "536273": "02262c", + "988b98": "2a6563", + "36404c": "842401", + "c4cce1": "0d484a", + "e6d3e9": "9cead8", + "bfacc1": "5db6a9", + "515f70": "a34205", + "c5cee3": "f7af58", + "b791f2": "4a9699", + "8b93a6": "d27e26", + "80737f": "2b736f", + "4b454f": "194f51", + "9170b9": "2f667c", + "8e96aa": "073338" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/6706_2.json b/public/images/pokemon/variant/exp/6706_2.json deleted file mode 100644 index cb2ddfb1a12..00000000000 --- a/public/images/pokemon/variant/exp/6706_2.json +++ /dev/null @@ -1,2015 +0,0 @@ -{ - "textures": [ - { - "image": "6706_2.png", - "format": "RGBA8888", - "size": { - "w": 508, - "h": 508 - }, - "scale": 1, - "frames": [ - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 26, - "w": 59, - "h": 61 - }, - "frame": { - "x": 0, - "y": 0, - "w": 59, - "h": 61 - } - }, - { - "filename": "0073.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 26, - "y": 25, - "w": 56, - "h": 63 - }, - "frame": { - "x": 59, - "y": 0, - "w": 56, - "h": 63 - } - }, - { - "filename": "0075.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 26, - "y": 25, - "w": 56, - "h": 63 - }, - "frame": { - "x": 59, - "y": 0, - "w": 56, - "h": 63 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 115, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0084.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 115, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 29, - "y": 21, - "w": 54, - "h": 65 - }, - "frame": { - "x": 168, - "y": 0, - "w": 54, - "h": 65 - } - }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 29, - "y": 21, - "w": 54, - "h": 65 - }, - "frame": { - "x": 168, - "y": 0, - "w": 54, - "h": 65 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 222, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0082.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 222, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 55, - "h": 66 - }, - "frame": { - "x": 275, - "y": 0, - "w": 55, - "h": 66 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 55, - "h": 66 - }, - "frame": { - "x": 275, - "y": 0, - "w": 55, - "h": 66 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 23, - "w": 54, - "h": 66 - }, - "frame": { - "x": 330, - "y": 0, - "w": 54, - "h": 66 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 23, - "w": 54, - "h": 66 - }, - "frame": { - "x": 330, - "y": 0, - "w": 54, - "h": 66 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 54, - "h": 67 - }, - "frame": { - "x": 384, - "y": 0, - "w": 54, - "h": 67 - } - }, - { - "filename": "0085.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 54, - "h": 67 - }, - "frame": { - "x": 384, - "y": 0, - "w": 54, - "h": 67 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 21, - "w": 52, - "h": 68 - }, - "frame": { - "x": 438, - "y": 0, - "w": 52, - "h": 68 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 21, - "w": 52, - "h": 68 - }, - "frame": { - "x": 438, - "y": 0, - "w": 52, - "h": 68 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 68 - }, - "frame": { - "x": 0, - "y": 61, - "w": 53, - "h": 68 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 68 - }, - "frame": { - "x": 0, - "y": 61, - "w": 53, - "h": 68 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 18, - "y": 22, - "w": 52, - "h": 68 - }, - "frame": { - "x": 53, - "y": 63, - "w": 52, - "h": 68 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 18, - "y": 22, - "w": 52, - "h": 68 - }, - "frame": { - "x": 53, - "y": 63, - "w": 52, - "h": 68 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 19, - "y": 21, - "w": 55, - "h": 69 - }, - "frame": { - "x": 105, - "y": 65, - "w": 55, - "h": 69 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 19, - "y": 21, - "w": 55, - "h": 69 - }, - "frame": { - "x": 105, - "y": 65, - "w": 55, - "h": 69 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 160, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 160, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 16, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 213, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 16, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 213, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 15, - "y": 21, - "w": 52, - "h": 70 - }, - "frame": { - "x": 266, - "y": 66, - "w": 52, - "h": 70 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 15, - "y": 21, - "w": 52, - "h": 70 - }, - "frame": { - "x": 266, - "y": 66, - "w": 52, - "h": 70 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 71 - }, - "frame": { - "x": 318, - "y": 67, - "w": 87, - "h": 71 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 71 - }, - "frame": { - "x": 318, - "y": 67, - "w": 87, - "h": 71 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 71 - }, - "frame": { - "x": 318, - "y": 67, - "w": 87, - "h": 71 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 18, - "w": 87, - "h": 71 - }, - "frame": { - "x": 405, - "y": 68, - "w": 87, - "h": 71 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 19, - "w": 86, - "h": 72 - }, - "frame": { - "x": 0, - "y": 131, - "w": 86, - "h": 72 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 72 - }, - "frame": { - "x": 86, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 86, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 86, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 72 - }, - "frame": { - "x": 173, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 173, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 173, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 260, - "y": 138, - "w": 87, - "h": 72 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 260, - "y": 138, - "w": 87, - "h": 72 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 260, - "y": 138, - "w": 87, - "h": 72 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 347, - "y": 139, - "w": 87, - "h": 72 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 347, - "y": 139, - "w": 87, - "h": 72 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 347, - "y": 139, - "w": 87, - "h": 72 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 74, - "h": 80 - }, - "frame": { - "x": 434, - "y": 139, - "w": 74, - "h": 80 - } - }, - { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 74, - "h": 80 - }, - "frame": { - "x": 434, - "y": 139, - "w": 74, - "h": 80 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 72 - }, - "frame": { - "x": 0, - "y": 203, - "w": 85, - "h": 72 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 83, - "h": 72 - }, - "frame": { - "x": 85, - "y": 206, - "w": 83, - "h": 72 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 18, - "w": 84, - "h": 73 - }, - "frame": { - "x": 168, - "y": 206, - "w": 84, - "h": 73 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 73 - }, - "frame": { - "x": 252, - "y": 210, - "w": 85, - "h": 73 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 337, - "y": 211, - "w": 86, - "h": 73 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 337, - "y": 211, - "w": 86, - "h": 73 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 337, - "y": 211, - "w": 86, - "h": 73 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 73 - }, - "frame": { - "x": 423, - "y": 219, - "w": 85, - "h": 73 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 73 - }, - "frame": { - "x": 423, - "y": 219, - "w": 85, - "h": 73 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 16, - "w": 85, - "h": 74 - }, - "frame": { - "x": 0, - "y": 275, - "w": 85, - "h": 74 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 16, - "w": 85, - "h": 74 - }, - "frame": { - "x": 0, - "y": 275, - "w": 85, - "h": 74 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 16, - "w": 85, - "h": 74 - }, - "frame": { - "x": 0, - "y": 275, - "w": 85, - "h": 74 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 85, - "y": 279, - "w": 86, - "h": 73 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 85, - "y": 279, - "w": 86, - "h": 73 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 85, - "y": 279, - "w": 86, - "h": 73 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 4, - "w": 81, - "h": 74 - }, - "frame": { - "x": 171, - "y": 279, - "w": 81, - "h": 74 - } - }, - { - "filename": "0095.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 4, - "w": 81, - "h": 74 - }, - "frame": { - "x": 171, - "y": 279, - "w": 81, - "h": 74 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 16, - "w": 84, - "h": 74 - }, - "frame": { - "x": 252, - "y": 283, - "w": 84, - "h": 74 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 16, - "w": 84, - "h": 74 - }, - "frame": { - "x": 252, - "y": 283, - "w": 84, - "h": 74 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 336, - "y": 284, - "w": 86, - "h": 73 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 336, - "y": 284, - "w": 86, - "h": 73 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 422, - "y": 292, - "w": 80, - "h": 74 - } - }, - { - "filename": "0094.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 422, - "y": 292, - "w": 80, - "h": 74 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 79, - "h": 74 - }, - "frame": { - "x": 0, - "y": 349, - "w": 79, - "h": 74 - } - }, - { - "filename": "0093.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 79, - "h": 74 - }, - "frame": { - "x": 0, - "y": 349, - "w": 79, - "h": 74 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 79, - "y": 352, - "w": 80, - "h": 74 - } - }, - { - "filename": "0092.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 79, - "y": 352, - "w": 80, - "h": 74 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 83, - "h": 75 - }, - "frame": { - "x": 159, - "y": 353, - "w": 83, - "h": 75 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 83, - "h": 75 - }, - "frame": { - "x": 159, - "y": 353, - "w": 83, - "h": 75 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 83, - "h": 75 - }, - "frame": { - "x": 159, - "y": 353, - "w": 83, - "h": 75 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 242, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 242, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 242, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 323, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 323, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 323, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 82, - "h": 75 - }, - "frame": { - "x": 404, - "y": 366, - "w": 82, - "h": 75 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 82, - "h": 75 - }, - "frame": { - "x": 404, - "y": 366, - "w": 82, - "h": 75 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 82, - "h": 75 - }, - "frame": { - "x": 404, - "y": 366, - "w": 82, - "h": 75 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 79, - "h": 75 - }, - "frame": { - "x": 0, - "y": 423, - "w": 79, - "h": 75 - } - }, - { - "filename": "0091.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 79, - "h": 75 - }, - "frame": { - "x": 0, - "y": 423, - "w": 79, - "h": 75 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 78, - "h": 75 - }, - "frame": { - "x": 79, - "y": 426, - "w": 78, - "h": 75 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 78, - "h": 75 - }, - "frame": { - "x": 79, - "y": 426, - "w": 78, - "h": 75 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 85, - "h": 75 - }, - "frame": { - "x": 157, - "y": 428, - "w": 85, - "h": 75 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 14, - "w": 79, - "h": 76 - }, - "frame": { - "x": 242, - "y": 432, - "w": 79, - "h": 76 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 14, - "w": 79, - "h": 76 - }, - "frame": { - "x": 242, - "y": 432, - "w": 79, - "h": 76 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 14, - "w": 79, - "h": 76 - }, - "frame": { - "x": 242, - "y": 432, - "w": 79, - "h": 76 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:62a4a665074efb5def1545546995dc5b:de2788ebeab6b42f331926f332da5125:d60cc2e5ae2bd18de8ee3ab0649593ee$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/6706_2.png b/public/images/pokemon/variant/exp/6706_2.png deleted file mode 100644 index 7e7dfa8e05a..00000000000 Binary files a/public/images/pokemon/variant/exp/6706_2.png and /dev/null differ diff --git a/public/images/pokemon/variant/exp/6706_3.json b/public/images/pokemon/variant/exp/6706_3.json deleted file mode 100644 index 8c9b16b80ab..00000000000 --- a/public/images/pokemon/variant/exp/6706_3.json +++ /dev/null @@ -1,2015 +0,0 @@ -{ - "textures": [ - { - "image": "6706_3.png", - "format": "RGBA8888", - "size": { - "w": 508, - "h": 508 - }, - "scale": 1, - "frames": [ - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 26, - "w": 59, - "h": 61 - }, - "frame": { - "x": 0, - "y": 0, - "w": 59, - "h": 61 - } - }, - { - "filename": "0073.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 26, - "y": 25, - "w": 56, - "h": 63 - }, - "frame": { - "x": 59, - "y": 0, - "w": 56, - "h": 63 - } - }, - { - "filename": "0075.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 26, - "y": 25, - "w": 56, - "h": 63 - }, - "frame": { - "x": 59, - "y": 0, - "w": 56, - "h": 63 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 115, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0084.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 115, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 29, - "y": 21, - "w": 54, - "h": 65 - }, - "frame": { - "x": 168, - "y": 0, - "w": 54, - "h": 65 - } - }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 29, - "y": 21, - "w": 54, - "h": 65 - }, - "frame": { - "x": 168, - "y": 0, - "w": 54, - "h": 65 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 222, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0082.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 222, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 55, - "h": 66 - }, - "frame": { - "x": 275, - "y": 0, - "w": 55, - "h": 66 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 55, - "h": 66 - }, - "frame": { - "x": 275, - "y": 0, - "w": 55, - "h": 66 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 23, - "w": 54, - "h": 66 - }, - "frame": { - "x": 330, - "y": 0, - "w": 54, - "h": 66 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 23, - "w": 54, - "h": 66 - }, - "frame": { - "x": 330, - "y": 0, - "w": 54, - "h": 66 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 54, - "h": 67 - }, - "frame": { - "x": 384, - "y": 0, - "w": 54, - "h": 67 - } - }, - { - "filename": "0085.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 54, - "h": 67 - }, - "frame": { - "x": 384, - "y": 0, - "w": 54, - "h": 67 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 21, - "w": 52, - "h": 68 - }, - "frame": { - "x": 438, - "y": 0, - "w": 52, - "h": 68 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 21, - "w": 52, - "h": 68 - }, - "frame": { - "x": 438, - "y": 0, - "w": 52, - "h": 68 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 68 - }, - "frame": { - "x": 0, - "y": 61, - "w": 53, - "h": 68 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 68 - }, - "frame": { - "x": 0, - "y": 61, - "w": 53, - "h": 68 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 18, - "y": 22, - "w": 52, - "h": 68 - }, - "frame": { - "x": 53, - "y": 63, - "w": 52, - "h": 68 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 18, - "y": 22, - "w": 52, - "h": 68 - }, - "frame": { - "x": 53, - "y": 63, - "w": 52, - "h": 68 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 19, - "y": 21, - "w": 55, - "h": 69 - }, - "frame": { - "x": 105, - "y": 65, - "w": 55, - "h": 69 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 19, - "y": 21, - "w": 55, - "h": 69 - }, - "frame": { - "x": 105, - "y": 65, - "w": 55, - "h": 69 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 160, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 160, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 16, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 213, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 16, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 213, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 15, - "y": 21, - "w": 52, - "h": 70 - }, - "frame": { - "x": 266, - "y": 66, - "w": 52, - "h": 70 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 15, - "y": 21, - "w": 52, - "h": 70 - }, - "frame": { - "x": 266, - "y": 66, - "w": 52, - "h": 70 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 71 - }, - "frame": { - "x": 318, - "y": 67, - "w": 87, - "h": 71 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 71 - }, - "frame": { - "x": 318, - "y": 67, - "w": 87, - "h": 71 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 71 - }, - "frame": { - "x": 318, - "y": 67, - "w": 87, - "h": 71 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 18, - "w": 87, - "h": 71 - }, - "frame": { - "x": 405, - "y": 68, - "w": 87, - "h": 71 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 19, - "w": 86, - "h": 72 - }, - "frame": { - "x": 0, - "y": 131, - "w": 86, - "h": 72 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 72 - }, - "frame": { - "x": 86, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 86, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 86, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 72 - }, - "frame": { - "x": 173, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 173, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 173, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 260, - "y": 138, - "w": 87, - "h": 72 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 260, - "y": 138, - "w": 87, - "h": 72 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 260, - "y": 138, - "w": 87, - "h": 72 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 347, - "y": 139, - "w": 87, - "h": 72 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 347, - "y": 139, - "w": 87, - "h": 72 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 347, - "y": 139, - "w": 87, - "h": 72 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 74, - "h": 80 - }, - "frame": { - "x": 434, - "y": 139, - "w": 74, - "h": 80 - } - }, - { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 74, - "h": 80 - }, - "frame": { - "x": 434, - "y": 139, - "w": 74, - "h": 80 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 72 - }, - "frame": { - "x": 0, - "y": 203, - "w": 85, - "h": 72 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 83, - "h": 72 - }, - "frame": { - "x": 85, - "y": 206, - "w": 83, - "h": 72 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 18, - "w": 84, - "h": 73 - }, - "frame": { - "x": 168, - "y": 206, - "w": 84, - "h": 73 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 73 - }, - "frame": { - "x": 252, - "y": 210, - "w": 85, - "h": 73 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 337, - "y": 211, - "w": 86, - "h": 73 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 337, - "y": 211, - "w": 86, - "h": 73 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 337, - "y": 211, - "w": 86, - "h": 73 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 73 - }, - "frame": { - "x": 423, - "y": 219, - "w": 85, - "h": 73 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 73 - }, - "frame": { - "x": 423, - "y": 219, - "w": 85, - "h": 73 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 16, - "w": 85, - "h": 74 - }, - "frame": { - "x": 0, - "y": 275, - "w": 85, - "h": 74 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 16, - "w": 85, - "h": 74 - }, - "frame": { - "x": 0, - "y": 275, - "w": 85, - "h": 74 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 16, - "w": 85, - "h": 74 - }, - "frame": { - "x": 0, - "y": 275, - "w": 85, - "h": 74 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 85, - "y": 279, - "w": 86, - "h": 73 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 85, - "y": 279, - "w": 86, - "h": 73 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 85, - "y": 279, - "w": 86, - "h": 73 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 4, - "w": 81, - "h": 74 - }, - "frame": { - "x": 171, - "y": 279, - "w": 81, - "h": 74 - } - }, - { - "filename": "0095.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 4, - "w": 81, - "h": 74 - }, - "frame": { - "x": 171, - "y": 279, - "w": 81, - "h": 74 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 16, - "w": 84, - "h": 74 - }, - "frame": { - "x": 252, - "y": 283, - "w": 84, - "h": 74 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 16, - "w": 84, - "h": 74 - }, - "frame": { - "x": 252, - "y": 283, - "w": 84, - "h": 74 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 336, - "y": 284, - "w": 86, - "h": 73 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 336, - "y": 284, - "w": 86, - "h": 73 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 422, - "y": 292, - "w": 80, - "h": 74 - } - }, - { - "filename": "0094.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 422, - "y": 292, - "w": 80, - "h": 74 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 79, - "h": 74 - }, - "frame": { - "x": 0, - "y": 349, - "w": 79, - "h": 74 - } - }, - { - "filename": "0093.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 79, - "h": 74 - }, - "frame": { - "x": 0, - "y": 349, - "w": 79, - "h": 74 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 79, - "y": 352, - "w": 80, - "h": 74 - } - }, - { - "filename": "0092.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 79, - "y": 352, - "w": 80, - "h": 74 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 83, - "h": 75 - }, - "frame": { - "x": 159, - "y": 353, - "w": 83, - "h": 75 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 83, - "h": 75 - }, - "frame": { - "x": 159, - "y": 353, - "w": 83, - "h": 75 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 83, - "h": 75 - }, - "frame": { - "x": 159, - "y": 353, - "w": 83, - "h": 75 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 242, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 242, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 242, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 323, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 323, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 323, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 82, - "h": 75 - }, - "frame": { - "x": 404, - "y": 366, - "w": 82, - "h": 75 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 82, - "h": 75 - }, - "frame": { - "x": 404, - "y": 366, - "w": 82, - "h": 75 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 82, - "h": 75 - }, - "frame": { - "x": 404, - "y": 366, - "w": 82, - "h": 75 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 79, - "h": 75 - }, - "frame": { - "x": 0, - "y": 423, - "w": 79, - "h": 75 - } - }, - { - "filename": "0091.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 79, - "h": 75 - }, - "frame": { - "x": 0, - "y": 423, - "w": 79, - "h": 75 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 78, - "h": 75 - }, - "frame": { - "x": 79, - "y": 426, - "w": 78, - "h": 75 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 78, - "h": 75 - }, - "frame": { - "x": 79, - "y": 426, - "w": 78, - "h": 75 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 85, - "h": 75 - }, - "frame": { - "x": 157, - "y": 428, - "w": 85, - "h": 75 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 14, - "w": 79, - "h": 76 - }, - "frame": { - "x": 242, - "y": 432, - "w": 79, - "h": 76 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 14, - "w": 79, - "h": 76 - }, - "frame": { - "x": 242, - "y": 432, - "w": 79, - "h": 76 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 14, - "w": 79, - "h": 76 - }, - "frame": { - "x": 242, - "y": 432, - "w": 79, - "h": 76 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:62a4a665074efb5def1545546995dc5b:de2788ebeab6b42f331926f332da5125:d60cc2e5ae2bd18de8ee3ab0649593ee$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/6706_3.png b/public/images/pokemon/variant/exp/6706_3.png deleted file mode 100644 index 3ad44f4bbf5..00000000000 Binary files a/public/images/pokemon/variant/exp/6706_3.png and /dev/null differ diff --git a/public/images/pokemon/variant/exp/705.json b/public/images/pokemon/variant/exp/705.json new file mode 100644 index 00000000000..a29b8f124dc --- /dev/null +++ b/public/images/pokemon/variant/exp/705.json @@ -0,0 +1,33 @@ +{ + "1": { + "101010":"101010", + "4d454d":"8a2166", + "807380":"b93f84", + "bfacbf":"e56ca6", + "f2daf2":"fbb3d2", + "665980":"4e4094", + "8f7db3":"8b69c3", + "b8a1e5":"c7a1e5", + "4d993d":"aa6a00", + "66cc52":"ffd047", + "4e9c3e":"0c5474", + "67cf53":"3aa8c4", + "b6f2aa":"63cee1" + }, + "2": { + "101010":"101010", + "4d454d":"194f51", + "807380":"2b736f", + "bfacbf":"5db6a9", + "f2daf2":"9cead8", + "665980":"274159", + "8f7db3":"2f667c", + "b8a1e5":"4a9699", + "4d993d":"007d61", + "66cc52":"49ffbf", + "4e9c3e":"842401", + "67cf53":"a34205", + "b6f2aa":"d27e26" + } +} + diff --git a/public/images/pokemon/variant/exp/705_2.json b/public/images/pokemon/variant/exp/705_2.json deleted file mode 100644 index bf9fd104c5d..00000000000 --- a/public/images/pokemon/variant/exp/705_2.json +++ /dev/null @@ -1,272 +0,0 @@ -{ - "textures": [ - { - "image": "705_2.png", - "format": "RGBA8888", - "size": { - "w": 154, - "h": 154 - }, - "scale": 1, - "frames": [ - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 46, - "h": 58 - }, - "frame": { - "x": 0, - "y": 0, - "w": 46, - "h": 58 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 46, - "h": 58 - }, - "frame": { - "x": 0, - "y": 0, - "w": 46, - "h": 58 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 45, - "h": 58 - }, - "frame": { - "x": 46, - "y": 0, - "w": 45, - "h": 58 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 45, - "h": 58 - }, - "frame": { - "x": 46, - "y": 0, - "w": 45, - "h": 58 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 45, - "h": 58 - }, - "frame": { - "x": 91, - "y": 0, - "w": 45, - "h": 58 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 42, - "h": 58 - }, - "frame": { - "x": 0, - "y": 58, - "w": 42, - "h": 58 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 42, - "h": 58 - }, - "frame": { - "x": 0, - "y": 58, - "w": 42, - "h": 58 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 41, - "h": 58 - }, - "frame": { - "x": 42, - "y": 58, - "w": 41, - "h": 58 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 41, - "h": 58 - }, - "frame": { - "x": 42, - "y": 58, - "w": 41, - "h": 58 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 36, - "h": 58 - }, - "frame": { - "x": 83, - "y": 58, - "w": 36, - "h": 58 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 36, - "h": 58 - }, - "frame": { - "x": 83, - "y": 58, - "w": 36, - "h": 58 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 35, - "h": 58 - }, - "frame": { - "x": 119, - "y": 58, - "w": 35, - "h": 58 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:4bf155254b23c88780e7eee282256589:82bb727988054c3064e203b6908ff464:6b57e983626c7fc9144ab67f30c66814$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/705_2.png b/public/images/pokemon/variant/exp/705_2.png deleted file mode 100644 index 8256ebc7fdb..00000000000 Binary files a/public/images/pokemon/variant/exp/705_2.png and /dev/null differ diff --git a/public/images/pokemon/variant/exp/705_3.json b/public/images/pokemon/variant/exp/705_3.json deleted file mode 100644 index 199d7bc9c3e..00000000000 --- a/public/images/pokemon/variant/exp/705_3.json +++ /dev/null @@ -1,272 +0,0 @@ -{ - "textures": [ - { - "image": "705_3.png", - "format": "RGBA8888", - "size": { - "w": 154, - "h": 154 - }, - "scale": 1, - "frames": [ - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 46, - "h": 58 - }, - "frame": { - "x": 0, - "y": 0, - "w": 46, - "h": 58 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 46, - "h": 58 - }, - "frame": { - "x": 0, - "y": 0, - "w": 46, - "h": 58 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 45, - "h": 58 - }, - "frame": { - "x": 46, - "y": 0, - "w": 45, - "h": 58 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 45, - "h": 58 - }, - "frame": { - "x": 46, - "y": 0, - "w": 45, - "h": 58 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 45, - "h": 58 - }, - "frame": { - "x": 91, - "y": 0, - "w": 45, - "h": 58 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 42, - "h": 58 - }, - "frame": { - "x": 0, - "y": 58, - "w": 42, - "h": 58 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 42, - "h": 58 - }, - "frame": { - "x": 0, - "y": 58, - "w": 42, - "h": 58 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 41, - "h": 58 - }, - "frame": { - "x": 42, - "y": 58, - "w": 41, - "h": 58 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 41, - "h": 58 - }, - "frame": { - "x": 42, - "y": 58, - "w": 41, - "h": 58 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 36, - "h": 58 - }, - "frame": { - "x": 83, - "y": 58, - "w": 36, - "h": 58 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 36, - "h": 58 - }, - "frame": { - "x": 83, - "y": 58, - "w": 36, - "h": 58 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 35, - "h": 58 - }, - "frame": { - "x": 119, - "y": 58, - "w": 35, - "h": 58 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:4bf155254b23c88780e7eee282256589:82bb727988054c3064e203b6908ff464:6b57e983626c7fc9144ab67f30c66814$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/705_3.png b/public/images/pokemon/variant/exp/705_3.png deleted file mode 100644 index 66b43956bdf..00000000000 Binary files a/public/images/pokemon/variant/exp/705_3.png and /dev/null differ diff --git a/public/images/pokemon/variant/exp/back/6706.json b/public/images/pokemon/variant/exp/back/6706.json new file mode 100644 index 00000000000..2de5352e936 --- /dev/null +++ b/public/images/pokemon/variant/exp/back/6706.json @@ -0,0 +1,38 @@ +{ + "1": { + "566678": "197497", + "8e96aa": "3b235c", + "929aad": "3aa8c4", + "625287": "4e4094", + "536273": "301848", + "988b98": "b24c86", + "36404c": "0c5474", + "c4cce1": "513981", + "e6d3e9": "f1a4c5", + "bfacc1": "d074a0", + "546475": "0e6296", + "c5cee3": "63cee1", + "80737f": "8a2166", + "4b454f": "6f1357", + "9170b9": "8b69c3", + "b791f2": "c7a1e5" + }, + "2": { + "566678": "a34205", + "8e96aa": "073338", + "929aad": "d27e26", + "625287": "0e3f47", + "536273": "042329", + "988b98": "2b736f", + "36404c": "842401", + "c4cce1": "0d484a", + "e6d3e9": "9cead8", + "bfacc1": "5db6a9", + "546475": "8e480b", + "c5cee3": "f7af58", + "80737f": "194f51", + "4b454f": "274159", + "9170b9": "2f667c", + "b791f2": "4a9699" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/back/6706_2.json b/public/images/pokemon/variant/exp/back/6706_2.json deleted file mode 100644 index 5c916aeb664..00000000000 --- a/public/images/pokemon/variant/exp/back/6706_2.json +++ /dev/null @@ -1,776 +0,0 @@ -{ - "textures": [ - { - "image": "6706_2.png", - "format": "RGBA8888", - "size": { - "w": 358, - "h": 358 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 4, - "w": 84, - "h": 69 - }, - "frame": { - "x": 0, - "y": 0, - "w": 84, - "h": 69 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 4, - "w": 84, - "h": 69 - }, - "frame": { - "x": 0, - "y": 0, - "w": 84, - "h": 69 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 1, - "w": 83, - "h": 72 - }, - "frame": { - "x": 84, - "y": 0, - "w": 83, - "h": 72 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 1, - "w": 83, - "h": 72 - }, - "frame": { - "x": 84, - "y": 0, - "w": 83, - "h": 72 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 1, - "w": 83, - "h": 72 - }, - "frame": { - "x": 0, - "y": 69, - "w": 83, - "h": 72 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 167, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 167, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 250, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 250, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 167, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 167, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 83, - "y": 72, - "w": 82, - "h": 73 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 83, - "y": 72, - "w": 82, - "h": 73 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 0, - "y": 141, - "w": 82, - "h": 73 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 0, - "y": 141, - "w": 82, - "h": 73 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 0, - "y": 141, - "w": 82, - "h": 73 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 249, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 249, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 0, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 0, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 0, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 0, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 81, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 81, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 81, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 81, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 165, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 165, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 247, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 247, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 162, - "y": 215, - "w": 80, - "h": 73 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 162, - "y": 215, - "w": 80, - "h": 73 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 3, - "w": 81, - "h": 70 - }, - "frame": { - "x": 162, - "y": 288, - "w": 81, - "h": 70 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 3, - "w": 81, - "h": 70 - }, - "frame": { - "x": 162, - "y": 288, - "w": 81, - "h": 70 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 242, - "y": 215, - "w": 80, - "h": 73 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 242, - "y": 215, - "w": 80, - "h": 73 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:5d65e2c5a6a97b7c7014a175ce3592af:3255e87f637a475d82734fc7d93baf71:d60cc2e5ae2bd18de8ee3ab0649593ee$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/back/6706_2.png b/public/images/pokemon/variant/exp/back/6706_2.png deleted file mode 100644 index cb793478420..00000000000 Binary files a/public/images/pokemon/variant/exp/back/6706_2.png and /dev/null differ diff --git a/public/images/pokemon/variant/exp/back/6706_3.json b/public/images/pokemon/variant/exp/back/6706_3.json deleted file mode 100644 index 3bb1dc426b2..00000000000 --- a/public/images/pokemon/variant/exp/back/6706_3.json +++ /dev/null @@ -1,776 +0,0 @@ -{ - "textures": [ - { - "image": "6706_3.png", - "format": "RGBA8888", - "size": { - "w": 358, - "h": 358 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 4, - "w": 84, - "h": 69 - }, - "frame": { - "x": 0, - "y": 0, - "w": 84, - "h": 69 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 4, - "w": 84, - "h": 69 - }, - "frame": { - "x": 0, - "y": 0, - "w": 84, - "h": 69 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 1, - "w": 83, - "h": 72 - }, - "frame": { - "x": 84, - "y": 0, - "w": 83, - "h": 72 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 1, - "w": 83, - "h": 72 - }, - "frame": { - "x": 84, - "y": 0, - "w": 83, - "h": 72 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 1, - "w": 83, - "h": 72 - }, - "frame": { - "x": 0, - "y": 69, - "w": 83, - "h": 72 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 167, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 167, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 250, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 250, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 167, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 167, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 83, - "y": 72, - "w": 82, - "h": 73 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 83, - "y": 72, - "w": 82, - "h": 73 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 0, - "y": 141, - "w": 82, - "h": 73 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 0, - "y": 141, - "w": 82, - "h": 73 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 0, - "y": 141, - "w": 82, - "h": 73 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 249, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 249, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 0, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 0, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 0, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 0, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 81, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 81, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 81, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 81, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 165, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 165, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 247, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 247, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 162, - "y": 215, - "w": 80, - "h": 73 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 162, - "y": 215, - "w": 80, - "h": 73 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 3, - "w": 81, - "h": 70 - }, - "frame": { - "x": 162, - "y": 288, - "w": 81, - "h": 70 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 3, - "w": 81, - "h": 70 - }, - "frame": { - "x": 162, - "y": 288, - "w": 81, - "h": 70 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 242, - "y": 215, - "w": 80, - "h": 73 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 242, - "y": 215, - "w": 80, - "h": 73 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:5d65e2c5a6a97b7c7014a175ce3592af:3255e87f637a475d82734fc7d93baf71:d60cc2e5ae2bd18de8ee3ab0649593ee$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/back/6706_3.png b/public/images/pokemon/variant/exp/back/6706_3.png deleted file mode 100644 index 6390c20799f..00000000000 Binary files a/public/images/pokemon/variant/exp/back/6706_3.png and /dev/null differ diff --git a/src/battle-scene.ts b/src/battle-scene.ts index cc6934f20d1..40e3971b7fc 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -2676,7 +2676,7 @@ export default class BattleScene extends SceneBase { } /** - * Removes all modifiers from enemy of PersistentModifier type + * Removes all modifiers from enemy pokemon of {@linkcode PersistentModifier} type */ clearEnemyModifiers(): void { const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PersistentModifier); @@ -2687,10 +2687,11 @@ export default class BattleScene extends SceneBase { } /** - * Removes all modifiers from enemy of PokemonHeldItemModifier type + * Removes all modifiers from enemy pokemon of {@linkcode PokemonHeldItemModifier} type + * @param pokemon - If specified, only removes held items from that {@linkcode Pokemon} */ - clearEnemyHeldItemModifiers(): void { - const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier); + clearEnemyHeldItemModifiers(pokemon?: Pokemon): void { + const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier && (!pokemon || m.getPokemon(this) === pokemon)); for (const m of modifiersToRemove) { this.enemyModifiers.splice(this.enemyModifiers.indexOf(m), 1); } @@ -3161,13 +3162,17 @@ export default class BattleScene extends SceneBase { /** * Loads or generates a mystery encounter * @param encounterType used to load session encounter when restarting game, etc. + * @param canBypass optional boolean to indicate that the request is coming from a function that needs to access a Mystery Encounter outside of gameplay requirements * @returns */ - getMysteryEncounter(encounterType?: MysteryEncounterType): MysteryEncounter { + getMysteryEncounter(encounterType?: MysteryEncounterType, canBypass?: boolean): MysteryEncounter { // Loading override or session encounter let encounter: MysteryEncounter | null; if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)) { encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE]; + } else if (canBypass) { + encounter = allMysteryEncounters[encounterType ?? -1]; + return encounter; } else { encounter = !isNullOrUndefined(encounterType) ? allMysteryEncounters[encounterType] : null; } diff --git a/src/data/ability.ts b/src/data/ability.ts index 43d02da1733..6a391818866 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -634,15 +634,15 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr { * Examples include: Absorb, Draining Kiss, Bitter Blade, etc. * Also displays a message to show this ability was activated. * @param pokemon {@linkcode Pokemon} with this ability - * @param passive N/A + * @param _passive N/A * @param attacker {@linkcode Pokemon} that is attacking this Pokemon * @param move {@linkcode PokemonMove} that is being used - * @param hitResult N/A - * @args N/A + * @param _hitResult N/A + * @param _args N/A * @returns true if healing should be reversed on a healing move, false otherwise. */ - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (move.hasAttr(HitHealAttr)) { + 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) { pokemon.scene.queueMessage(i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) })); } @@ -669,8 +669,8 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr { this.allOthers = allOthers; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (this.condition(pokemon, attacker, move)) { + 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; } @@ -707,13 +707,13 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { this.selfTarget = selfTarget; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - const hpGateFlat: integer = Math.ceil(pokemon.getMaxHp() * this.hpGate); + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _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; - if (this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + damageReceived) > hpGateFlat)) { - if (!simulated ) { + if (this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + damageReceived) > hpGateFlat) && !move.hitsSubstitute(attacker, pokemon)) { + if (!simulated) { pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.stages)); } return true; @@ -734,8 +734,8 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr { this.tagType = tagType; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (this.condition(pokemon, attacker, move)) { + 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 = pokemon.scene.arena.getTag(this.tagType) as ArenaTrapTag; if (!pokemon.scene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers) { if (!simulated) { @@ -758,8 +758,8 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { this.tagType = tagType; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (this.condition(pokemon, attacker, move)) { + 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); pokemon.scene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name })); @@ -771,8 +771,8 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { } export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr { - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (hitResult < HitResult.NO_EFFECT) { + 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; } @@ -787,7 +787,7 @@ export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr { return false; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:postDefendTypeChange", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -805,8 +805,8 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { this.terrainType = terrainType; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (hitResult < HitResult.NO_EFFECT) { + 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 pokemon.scene.arena.terrain?.terrainType !== (this.terrainType || undefined); } else { @@ -829,8 +829,9 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { this.effects = effects; } - 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)) { + 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); @@ -869,8 +870,8 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { this.turnCount = turnCount; } - 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) { + 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 { @@ -893,7 +894,11 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr { this.stages = stages; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + if (move.hitsSubstitute(attacker, pokemon)) { + return false; + } + if (!simulated) { pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); } @@ -901,7 +906,7 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr { return true; } - getCondition(): AbAttrCondition { + override getCondition(): AbAttrCondition { return (pokemon: Pokemon) => pokemon.turnData.attacksReceived.length !== 0 && pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1].critical; } } @@ -915,8 +920,9 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { this.damageRatio = damageRatio; } - 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)) { + 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; @@ -925,7 +931,7 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { return false; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:postDefendContactDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName @@ -948,8 +954,8 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { this.turns = turns; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) { + 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 (pokemon.getTag(BattlerTagType.PERISH_SONG) || attacker.getTag(BattlerTagType.PERISH_SONG)) { return false; } else { @@ -963,24 +969,24 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { return false; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:perishBody", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName }); } } export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { private weatherType: WeatherType; - protected condition: PokemonDefendCondition | null; + protected condition?: PokemonDefendCondition; constructor(weatherType: WeatherType, condition?: PokemonDefendCondition) { super(); this.weatherType = weatherType; - this.condition = condition ?? null; + this.condition = condition; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (this.condition !== null && !this.condition(pokemon, attacker, move)) { + 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 (!pokemon.scene.arena.weather?.isImmutable()) { @@ -999,8 +1005,9 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { super(); } - 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)) { + 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 tempAbilityId = attacker.getAbility().id; attacker.summonData.ability = pokemon.getAbility().id; @@ -1012,7 +1019,7 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { return false; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + override getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:postDefendAbilitySwap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }); } } @@ -1025,8 +1032,9 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { this.ability = ability; } - 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)) { + 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.summonData.ability = this.ability; } @@ -1037,7 +1045,7 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { return false; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:postDefendAbilityGive", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName @@ -1056,8 +1064,8 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { this.chance = chance; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (attacker.getTag(BattlerTagType.DISABLED) === null) { + 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; @@ -1724,17 +1732,17 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { } export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { - private condition: PokemonDefendCondition | null; + private condition?: PokemonDefendCondition; constructor(condition?: PokemonDefendCondition) { super(); - this.condition = condition ?? null; + this.condition = condition; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): Promise { return new Promise(resolve => { - if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) { + 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)]; @@ -4476,7 +4484,7 @@ export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageC export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { private multiplier: number; private tagType: BattlerTagType; - private recoilDamageFunc: ((pokemon: Pokemon) => number) | undefined; + private recoilDamageFunc?: ((pokemon: Pokemon) => number); private triggerMessageFunc: (pokemon: Pokemon, abilityName: string) => string; constructor(condition: PokemonDefendCondition, multiplier: number, tagType: BattlerTagType, triggerMessageFunc: (pokemon: Pokemon, abilityName: string) => string, recoilDamageFunc?: (pokemon: Pokemon) => number) { @@ -4492,16 +4500,16 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { * Applies the pre-defense ability to the Pokémon. * Removes the appropriate `BattlerTagType` when hit by an attack and is in its defense form. * - * @param {Pokemon} pokemon The Pokémon with the ability. - * @param {boolean} passive n/a - * @param {Pokemon} attacker The attacking Pokémon. - * @param {PokemonMove} move The move being used. - * @param {Utils.BooleanHolder} cancelled n/a - * @param {any[]} args Additional arguments. - * @returns {boolean} Whether the immunity was applied. + * @param pokemon The Pokémon with the ability. + * @param _passive n/a + * @param attacker The attacking Pokémon. + * @param move The move being used. + * @param _cancelled n/a + * @param args Additional arguments. + * @returns `true` if the immunity was applied. */ - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.condition(pokemon, attacker, move)) { + 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); @@ -4517,12 +4525,12 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { /** * Gets the message triggered when the Pokémon avoids damage using the form-changing ability. - * @param {Pokemon} pokemon The Pokémon with the ability. - * @param {string} abilityName The name of the ability. - * @param {...any} args n/a - * @returns {string} The trigger message. + * @param pokemon The Pokémon with the ability. + * @param abilityName The name of the ability. + * @param _args n/a + * @returns The trigger message. */ - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return this.triggerMessageFunc(pokemon, abilityName); } } @@ -5503,7 +5511,8 @@ export function initAbilities() { .attr(NoFusionAbilityAbAttr) // Add BattlerTagType.DISGUISE if the pokemon is in its disguised form .conditionalAttr(pokemon => pokemon.formIndex === 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.DISGUISE, 0, false) - .attr(FormBlockDamageAbAttr, (target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getMoveEffectiveness(user, move) > 0, 0, BattlerTagType.DISGUISE, + .attr(FormBlockDamageAbAttr, + (target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getMoveEffectiveness(user, move) > 0, 0, BattlerTagType.DISGUISE, (pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }), (pokemon) => Utils.toDmgValue(pokemon.getMaxHp() / 8)) .attr(PostBattleInitFormChangeAbAttr, () => 0) @@ -5665,7 +5674,8 @@ export function initAbilities() { .conditionalAttr(getWeatherCondition(WeatherType.HAIL, WeatherType.SNOW), PostSummonAddBattlerTagAbAttr, BattlerTagType.ICE_FACE, 0) // When weather changes to HAIL or SNOW while pokemon is fielded, add BattlerTagType.ICE_FACE .attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.ICE_FACE, 0, WeatherType.HAIL, WeatherType.SNOW) - .attr(FormBlockDamageAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE), 0, BattlerTagType.ICE_FACE, + .attr(FormBlockDamageAbAttr, + (target, user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE), 0, BattlerTagType.ICE_FACE, (pokemon, abilityName) => i18next.t("abilityTriggers:iceFaceAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName })) .attr(PostBattleInitFormChangeAbAttr, () => 0) .bypassFaint() diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 6407e139a71..11d28ab7e25 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -28,20 +28,13 @@ export enum ArenaTagSide { } export abstract class ArenaTag { - public tagType: ArenaTagType; - public turnCount: integer; - public sourceMove?: Moves; - public sourceId?: integer; - public side: ArenaTagSide; - - - constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId?: integer, side: ArenaTagSide = ArenaTagSide.BOTH) { - this.tagType = tagType; - this.turnCount = turnCount; - this.sourceMove = sourceMove; - this.sourceId = sourceId; - this.side = side; - } + constructor( + public tagType: ArenaTagType, + public turnCount: number, + public sourceMove?: Moves, + public sourceId?: number, + public side: ArenaTagSide = ArenaTagSide.BOTH + ) {} apply(arena: Arena, args: any[]): boolean { return true; @@ -66,6 +59,18 @@ export abstract class ArenaTag { ? allMoves[this.sourceMove].name : null; } + + /** + * When given a arena tag or json representing one, load the data for it. + * This is meant to be inherited from by any arena tag with custom attributes + * @param {ArenaTag | any} source An arena tag + */ + loadTag(source : ArenaTag | any) : void { + this.turnCount = source.turnCount; + this.sourceMove = source.sourceMove; + this.sourceId = source.sourceId; + this.side = source.side; + } } /** @@ -73,7 +78,7 @@ export abstract class ArenaTag { * Prevents Pokémon on the opposing side from lowering the stats of the Pokémon in the Mist. */ export class MistTag extends ArenaTag { - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.MIST, turnCount, Moves.MIST, sourceId, side); } @@ -117,7 +122,7 @@ export class WeakenMoveScreenTag extends ArenaTag { * @param side - The side (player or enemy) the tag affects. * @param weakenedCategories - The categories of moves that are weakened by this tag. */ - constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide, weakenedCategories: MoveCategory[]) { + constructor(tagType: ArenaTagType, turnCount: number, sourceMove: Moves, sourceId: number, side: ArenaTagSide, weakenedCategories: MoveCategory[]) { super(tagType, turnCount, sourceMove, sourceId, side); this.weakenedCategories = weakenedCategories; @@ -148,7 +153,7 @@ export class WeakenMoveScreenTag extends ArenaTag { * Used by {@linkcode Moves.REFLECT} */ class ReflectTag extends WeakenMoveScreenTag { - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.REFLECT, turnCount, Moves.REFLECT, sourceId, side, [ MoveCategory.PHYSICAL ]); } @@ -164,7 +169,7 @@ class ReflectTag extends WeakenMoveScreenTag { * Used by {@linkcode Moves.LIGHT_SCREEN} */ class LightScreenTag extends WeakenMoveScreenTag { - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.LIGHT_SCREEN, turnCount, Moves.LIGHT_SCREEN, sourceId, side, [ MoveCategory.SPECIAL ]); } @@ -180,7 +185,7 @@ class LightScreenTag extends WeakenMoveScreenTag { * Used by {@linkcode Moves.AURORA_VEIL} */ class AuroraVeilTag extends WeakenMoveScreenTag { - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.AURORA_VEIL, turnCount, Moves.AURORA_VEIL, sourceId, side, [ MoveCategory.SPECIAL, MoveCategory.PHYSICAL ]); } @@ -203,7 +208,7 @@ export class ConditionalProtectTag extends ArenaTag { /** Does this apply to all moves, including those that ignore other forms of protection? */ protected ignoresBypass: boolean; - constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: integer, side: ArenaTagSide, condition: ProtectConditionFunc, ignoresBypass: boolean = false) { + constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: number, side: ArenaTagSide, condition: ProtectConditionFunc, ignoresBypass: boolean = false) { super(tagType, 1, sourceMove, sourceId, side); this.protectConditionFunc = condition; @@ -265,7 +270,7 @@ export class ConditionalProtectTag extends ArenaTag { */ const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => { const move = allMoves[moveId]; - const priority = new Utils.IntegerHolder(move.priority); + const priority = new Utils.NumberHolder(move.priority); const effectPhase = arena.scene.getCurrentPhase(); if (effectPhase instanceof MoveEffectPhase) { @@ -281,7 +286,7 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => { * Condition: The incoming move has increased priority. */ class QuickGuardTag extends ConditionalProtectTag { - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.QUICK_GUARD, Moves.QUICK_GUARD, sourceId, side, QuickGuardConditionFunc); } } @@ -312,7 +317,7 @@ const WideGuardConditionFunc: ProtectConditionFunc = (arena, moveId) : boolean = * can be an ally or enemy. */ class WideGuardTag extends ConditionalProtectTag { - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.WIDE_GUARD, Moves.WIDE_GUARD, sourceId, side, WideGuardConditionFunc); } } @@ -334,7 +339,7 @@ const MatBlockConditionFunc: ProtectConditionFunc = (arena, moveId) : boolean => * Condition: The incoming move is a Physical or Special attack move. */ class MatBlockTag extends ConditionalProtectTag { - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.MAT_BLOCK, Moves.MAT_BLOCK, sourceId, side, MatBlockConditionFunc); } @@ -372,7 +377,7 @@ const CraftyShieldConditionFunc: ProtectConditionFunc = (arena, moveId) => { * not target all Pokemon or sides of the field. */ class CraftyShieldTag extends ConditionalProtectTag { - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.CRAFTY_SHIELD, Moves.CRAFTY_SHIELD, sourceId, side, CraftyShieldConditionFunc, true); } } @@ -384,12 +389,12 @@ class CraftyShieldTag extends ConditionalProtectTag { export class NoCritTag extends ArenaTag { /** * Constructor method for the NoCritTag class - * @param turnCount `integer` the number of turns this effect lasts + * @param turnCount `number` the number of turns this effect lasts * @param sourceMove {@linkcode Moves} the move that created this effect - * @param sourceId `integer` the ID of the {@linkcode Pokemon} that created this effect + * @param sourceId `number` the ID of the {@linkcode Pokemon} that created this effect * @param side {@linkcode ArenaTagSide} the side to which this effect belongs */ - constructor(turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceMove: Moves, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.NO_CRIT, turnCount, sourceMove, sourceId, side); } @@ -419,7 +424,7 @@ class WishTag extends ArenaTag { private triggerMessage: string; private healHp: number; - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.WISH, turnCount, Moves.WISH, sourceId, side); } @@ -460,7 +465,7 @@ export class WeakenMoveTypeTag extends ArenaTag { * @param sourceMove - The move that created the tag. * @param sourceId - The ID of the source of the tag. */ - constructor(tagType: ArenaTagType, turnCount: integer, type: Type, sourceMove: Moves, sourceId: integer) { + constructor(tagType: ArenaTagType, turnCount: number, type: Type, sourceMove: Moves, sourceId: number) { super(tagType, turnCount, sourceMove, sourceId); this.weakenedType = type; @@ -481,7 +486,7 @@ export class WeakenMoveTypeTag extends ArenaTag { * Weakens Electric type moves for a set amount of turns, usually 5. */ class MudSportTag extends WeakenMoveTypeTag { - constructor(turnCount: integer, sourceId: integer) { + constructor(turnCount: number, sourceId: number) { super(ArenaTagType.MUD_SPORT, turnCount, Type.ELECTRIC, Moves.MUD_SPORT, sourceId); } @@ -499,7 +504,7 @@ class MudSportTag extends WeakenMoveTypeTag { * Weakens Fire type moves for a set amount of turns, usually 5. */ class WaterSportTag extends WeakenMoveTypeTag { - constructor(turnCount: integer, sourceId: integer) { + constructor(turnCount: number, sourceId: number) { super(ArenaTagType.WATER_SPORT, turnCount, Type.FIRE, Moves.WATER_SPORT, sourceId); } @@ -550,8 +555,8 @@ export class IonDelugeTag extends ArenaTag { * Abstract class to implement arena traps. */ export class ArenaTrapTag extends ArenaTag { - public layers: integer; - public maxLayers: integer; + public layers: number; + public maxLayers: number; /** * Creates a new instance of the ArenaTrapTag class. @@ -562,7 +567,7 @@ export class ArenaTrapTag extends ArenaTag { * @param side - The side (player or enemy) the tag affects. * @param maxLayers - The maximum amount of layers this tag can have. */ - constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: integer, side: ArenaTagSide, maxLayers: integer) { + constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: number, side: ArenaTagSide, maxLayers: number) { super(tagType, 0, sourceMove, sourceId, side); this.layers = 1; @@ -593,6 +598,12 @@ export class ArenaTrapTag extends ArenaTag { getMatchupScoreMultiplier(pokemon: Pokemon): number { return pokemon.isGrounded() ? 1 : Phaser.Math.Linear(0, 1 / Math.pow(2, this.layers), Math.min(pokemon.getHpRatio(), 0.5) * 2); } + + loadTag(source: any): void { + super.loadTag(source); + this.layers = source.layers; + this.maxLayers = source.maxLayers; + } } /** @@ -601,7 +612,7 @@ export class ArenaTrapTag extends ArenaTag { * in damage for 1, 2, or 3 layers of Spikes respectively if they are summoned into this trap. */ class SpikesTag extends ArenaTrapTag { - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.SPIKES, Moves.SPIKES, sourceId, side, 3); } @@ -645,7 +656,7 @@ class SpikesTag extends ArenaTrapTag { class ToxicSpikesTag extends ArenaTrapTag { private neutralized: boolean; - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.TOXIC_SPIKES, Moves.TOXIC_SPIKES, sourceId, side, 2); this.neutralized = false; } @@ -703,7 +714,7 @@ class ToxicSpikesTag extends ArenaTrapTag { class DelayedAttackTag extends ArenaTag { public targetIndex: BattlerIndex; - constructor(tagType: ArenaTagType, sourceMove: Moves | undefined, sourceId: integer, targetIndex: BattlerIndex) { + constructor(tagType: ArenaTagType, sourceMove: Moves | undefined, sourceId: number, targetIndex: BattlerIndex) { super(tagType, 3, sourceMove, sourceId); this.targetIndex = targetIndex; @@ -728,7 +739,7 @@ class DelayedAttackTag extends ArenaTag { * who is summoned into the trap, based on the Rock type's type effectiveness. */ class StealthRockTag extends ArenaTrapTag { - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.STEALTH_ROCK, Moves.STEALTH_ROCK, sourceId, side, 1); } @@ -804,7 +815,7 @@ class StealthRockTag extends ArenaTrapTag { * to any Pokémon who is summoned into this trap. */ class StickyWebTag extends ArenaTrapTag { - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.STICKY_WEB, Moves.STICKY_WEB, sourceId, side, 1); } @@ -838,7 +849,7 @@ class StickyWebTag extends ArenaTrapTag { * also reversing the turn order for all Pokémon on the field as well. */ export class TrickRoomTag extends ArenaTag { - constructor(turnCount: integer, sourceId: integer) { + constructor(turnCount: number, sourceId: number) { super(ArenaTagType.TRICK_ROOM, turnCount, Moves.TRICK_ROOM, sourceId); } @@ -866,7 +877,7 @@ export class TrickRoomTag extends ArenaTag { * {@linkcode Abilities.LEVITATE} for the duration of the arena tag, usually 5 turns. */ export class GravityTag extends ArenaTag { - constructor(turnCount: integer) { + constructor(turnCount: number) { super(ArenaTagType.GRAVITY, turnCount, Moves.GRAVITY); } @@ -874,7 +885,8 @@ export class GravityTag extends ArenaTag { arena.scene.queueMessage(i18next.t("arenaTag:gravityOnAdd")); arena.scene.getField(true).forEach((pokemon) => { if (pokemon !== null) { - pokemon.removeTag(BattlerTagType.MAGNET_RISEN); + pokemon.removeTag(BattlerTagType.FLOATING); + pokemon.removeTag(BattlerTagType.TELEKINESIS); } }); } @@ -890,7 +902,7 @@ export class GravityTag extends ArenaTag { * Applies this arena tag for 4 turns (including the turn the move was used). */ class TailwindTag extends ArenaTag { - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.TAILWIND, turnCount, Moves.TAILWIND, sourceId, side); } @@ -928,7 +940,7 @@ class TailwindTag extends ArenaTag { * Doubles the prize money from trainers and money moves like {@linkcode Moves.PAY_DAY} and {@linkcode Moves.MAKE_IT_RAIN}. */ class HappyHourTag extends ArenaTag { - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.HAPPY_HOUR, turnCount, Moves.HAPPY_HOUR, sourceId, side); } @@ -942,7 +954,7 @@ class HappyHourTag extends ArenaTag { } class SafeguardTag extends ArenaTag { - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.SAFEGUARD, turnCount, Moves.SAFEGUARD, sourceId, side); } @@ -955,6 +967,11 @@ class SafeguardTag extends ArenaTag { } } +class NoneTag extends ArenaTag { + constructor() { + super(ArenaTagType.NONE, 0); + } +} /** * This arena tag facilitates the application of the move Imprison * Imprison remains in effect as long as the source Pokemon is active and present on the field. @@ -1102,7 +1119,8 @@ class GrassWaterPledgeTag extends ArenaTag { } } -export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null { +// TODO: swap `sourceMove` and `sourceId` and make `sourceMove` an optional parameter +export function getArenaTag(tagType: ArenaTagType, turnCount: number, sourceMove: Moves | undefined, sourceId: number, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null { switch (tagType) { case ArenaTagType.MIST: return new MistTag(turnCount, sourceId, side); @@ -1163,3 +1181,16 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov return null; } } + +/** + * When given a battler tag or json representing one, creates an actual ArenaTag object with the same data. + * @param {ArenaTag | any} source An arena tag + * @return {ArenaTag} The valid arena tag + */ +export function loadArenaTag(source: ArenaTag | any): ArenaTag { + const tag = getArenaTag(source.tagType, source.turnCount, source.sourceMove, source.sourceId, source.targetIndex, source.side) + ?? new NoneTag(); + tag.loadTag(source); + return tag; +} + diff --git a/src/data/balance/species-egg-tiers.ts b/src/data/balance/species-egg-tiers.ts new file mode 100644 index 00000000000..cd266dfcf54 --- /dev/null +++ b/src/data/balance/species-egg-tiers.ts @@ -0,0 +1,603 @@ +import { Species } from "#enums/species"; +import { EggTier } from "#enums/egg-type"; + +/** + * Map of all starters and their respective {@linkcode EggTier}, which determines the type of egg the starter hatches from. + */ +export const speciesEggTiers = { + [Species.BULBASAUR]: EggTier.COMMON, + [Species.CHARMANDER]: EggTier.COMMON, + [Species.SQUIRTLE]: EggTier.COMMON, + [Species.CATERPIE]: EggTier.COMMON, + [Species.WEEDLE]: EggTier.COMMON, + [Species.PIDGEY]: EggTier.COMMON, + [Species.RATTATA]: EggTier.COMMON, + [Species.SPEAROW]: EggTier.COMMON, + [Species.EKANS]: EggTier.COMMON, + [Species.PIKACHU]: EggTier.COMMON, + [Species.SANDSHREW]: EggTier.COMMON, + [Species.NIDORAN_F]: EggTier.COMMON, + [Species.NIDORAN_M]: EggTier.COMMON, + [Species.CLEFAIRY]: EggTier.COMMON, + [Species.VULPIX]: EggTier.COMMON, + [Species.JIGGLYPUFF]: EggTier.COMMON, + [Species.ZUBAT]: EggTier.COMMON, + [Species.ODDISH]: EggTier.COMMON, + [Species.PARAS]: EggTier.COMMON, + [Species.VENONAT]: EggTier.COMMON, + [Species.DIGLETT]: EggTier.COMMON, + [Species.MEOWTH]: EggTier.COMMON, + [Species.PSYDUCK]: EggTier.COMMON, + [Species.MANKEY]: EggTier.RARE, + [Species.GROWLITHE]: EggTier.RARE, + [Species.POLIWAG]: EggTier.COMMON, + [Species.ABRA]: EggTier.RARE, + [Species.MACHOP]: EggTier.COMMON, + [Species.BELLSPROUT]: EggTier.COMMON, + [Species.TENTACOOL]: EggTier.COMMON, + [Species.GEODUDE]: EggTier.COMMON, + [Species.PONYTA]: EggTier.COMMON, + [Species.SLOWPOKE]: EggTier.COMMON, + [Species.MAGNEMITE]: EggTier.RARE, + [Species.FARFETCHD]: EggTier.COMMON, + [Species.DODUO]: EggTier.COMMON, + [Species.SEEL]: EggTier.COMMON, + [Species.GRIMER]: EggTier.COMMON, + [Species.SHELLDER]: EggTier.RARE, + [Species.GASTLY]: EggTier.RARE, + [Species.ONIX]: EggTier.COMMON, + [Species.DROWZEE]: EggTier.COMMON, + [Species.KRABBY]: EggTier.COMMON, + [Species.VOLTORB]: EggTier.COMMON, + [Species.EXEGGCUTE]: EggTier.COMMON, + [Species.CUBONE]: EggTier.COMMON, + [Species.HITMONLEE]: EggTier.RARE, + [Species.HITMONCHAN]: EggTier.RARE, + [Species.LICKITUNG]: EggTier.COMMON, + [Species.KOFFING]: EggTier.COMMON, + [Species.RHYHORN]: EggTier.COMMON, + [Species.CHANSEY]: EggTier.COMMON, + [Species.TANGELA]: EggTier.COMMON, + [Species.KANGASKHAN]: EggTier.RARE, + [Species.HORSEA]: EggTier.COMMON, + [Species.GOLDEEN]: EggTier.COMMON, + [Species.STARYU]: EggTier.COMMON, + [Species.MR_MIME]: EggTier.COMMON, + [Species.SCYTHER]: EggTier.RARE, + [Species.JYNX]: EggTier.RARE, + [Species.ELECTABUZZ]: EggTier.RARE, + [Species.MAGMAR]: EggTier.RARE, + [Species.PINSIR]: EggTier.RARE, + [Species.TAUROS]: EggTier.RARE, + [Species.MAGIKARP]: EggTier.RARE, + [Species.LAPRAS]: EggTier.RARE, + [Species.DITTO]: EggTier.COMMON, + [Species.EEVEE]: EggTier.COMMON, + [Species.PORYGON]: EggTier.RARE, + [Species.OMANYTE]: EggTier.COMMON, + [Species.KABUTO]: EggTier.COMMON, + [Species.AERODACTYL]: EggTier.RARE, + [Species.SNORLAX]: EggTier.RARE, + [Species.ARTICUNO]: EggTier.EPIC, + [Species.ZAPDOS]: EggTier.EPIC, + [Species.MOLTRES]: EggTier.EPIC, + [Species.DRATINI]: EggTier.RARE, + [Species.MEWTWO]: EggTier.LEGENDARY, + [Species.MEW]: EggTier.EPIC, + + [Species.CHIKORITA]: EggTier.COMMON, + [Species.CYNDAQUIL]: EggTier.COMMON, + [Species.TOTODILE]: EggTier.COMMON, + [Species.SENTRET]: EggTier.COMMON, + [Species.HOOTHOOT]: EggTier.COMMON, + [Species.LEDYBA]: EggTier.COMMON, + [Species.SPINARAK]: EggTier.COMMON, + [Species.CHINCHOU]: EggTier.COMMON, + [Species.PICHU]: EggTier.COMMON, + [Species.CLEFFA]: EggTier.COMMON, + [Species.IGGLYBUFF]: EggTier.COMMON, + [Species.TOGEPI]: EggTier.COMMON, + [Species.NATU]: EggTier.COMMON, + [Species.MAREEP]: EggTier.COMMON, + [Species.MARILL]: EggTier.RARE, + [Species.SUDOWOODO]: EggTier.COMMON, + [Species.HOPPIP]: EggTier.COMMON, + [Species.AIPOM]: EggTier.COMMON, + [Species.SUNKERN]: EggTier.COMMON, + [Species.YANMA]: EggTier.COMMON, + [Species.WOOPER]: EggTier.COMMON, + [Species.MURKROW]: EggTier.COMMON, + [Species.MISDREAVUS]: EggTier.COMMON, + [Species.UNOWN]: EggTier.COMMON, + [Species.WOBBUFFET]: EggTier.COMMON, + [Species.GIRAFARIG]: EggTier.COMMON, + [Species.PINECO]: EggTier.COMMON, + [Species.DUNSPARCE]: EggTier.COMMON, + [Species.GLIGAR]: EggTier.COMMON, + [Species.SNUBBULL]: EggTier.COMMON, + [Species.QWILFISH]: EggTier.COMMON, + [Species.SHUCKLE]: EggTier.COMMON, + [Species.HERACROSS]: EggTier.RARE, + [Species.SNEASEL]: EggTier.RARE, + [Species.TEDDIURSA]: EggTier.RARE, + [Species.SLUGMA]: EggTier.COMMON, + [Species.SWINUB]: EggTier.COMMON, + [Species.CORSOLA]: EggTier.COMMON, + [Species.REMORAID]: EggTier.COMMON, + [Species.DELIBIRD]: EggTier.COMMON, + [Species.MANTINE]: EggTier.COMMON, + [Species.SKARMORY]: EggTier.RARE, + [Species.HOUNDOUR]: EggTier.COMMON, + [Species.PHANPY]: EggTier.COMMON, + [Species.STANTLER]: EggTier.COMMON, + [Species.SMEARGLE]: EggTier.COMMON, + [Species.TYROGUE]: EggTier.COMMON, + [Species.SMOOCHUM]: EggTier.COMMON, + [Species.ELEKID]: EggTier.COMMON, + [Species.MAGBY]: EggTier.COMMON, + [Species.MILTANK]: EggTier.RARE, + [Species.RAIKOU]: EggTier.EPIC, + [Species.ENTEI]: EggTier.EPIC, + [Species.SUICUNE]: EggTier.EPIC, + [Species.LARVITAR]: EggTier.RARE, + [Species.LUGIA]: EggTier.LEGENDARY, + [Species.HO_OH]: EggTier.LEGENDARY, + [Species.CELEBI]: EggTier.EPIC, + + [Species.TREECKO]: EggTier.COMMON, + [Species.TORCHIC]: EggTier.RARE, + [Species.MUDKIP]: EggTier.COMMON, + [Species.POOCHYENA]: EggTier.COMMON, + [Species.ZIGZAGOON]: EggTier.COMMON, + [Species.WURMPLE]: EggTier.COMMON, + [Species.LOTAD]: EggTier.COMMON, + [Species.SEEDOT]: EggTier.COMMON, + [Species.TAILLOW]: EggTier.COMMON, + [Species.WINGULL]: EggTier.COMMON, + [Species.RALTS]: EggTier.COMMON, + [Species.SURSKIT]: EggTier.COMMON, + [Species.SHROOMISH]: EggTier.COMMON, + [Species.SLAKOTH]: EggTier.RARE, + [Species.NINCADA]: EggTier.RARE, + [Species.WHISMUR]: EggTier.COMMON, + [Species.MAKUHITA]: EggTier.COMMON, + [Species.AZURILL]: EggTier.RARE, + [Species.NOSEPASS]: EggTier.COMMON, + [Species.SKITTY]: EggTier.COMMON, + [Species.SABLEYE]: EggTier.COMMON, + [Species.MAWILE]: EggTier.COMMON, + [Species.ARON]: EggTier.COMMON, + [Species.MEDITITE]: EggTier.COMMON, + [Species.ELECTRIKE]: EggTier.COMMON, + [Species.PLUSLE]: EggTier.COMMON, + [Species.MINUN]: EggTier.COMMON, + [Species.VOLBEAT]: EggTier.COMMON, + [Species.ILLUMISE]: EggTier.COMMON, + [Species.ROSELIA]: EggTier.COMMON, + [Species.GULPIN]: EggTier.COMMON, + [Species.CARVANHA]: EggTier.COMMON, + [Species.WAILMER]: EggTier.COMMON, + [Species.NUMEL]: EggTier.COMMON, + [Species.TORKOAL]: EggTier.COMMON, + [Species.SPOINK]: EggTier.COMMON, + [Species.SPINDA]: EggTier.COMMON, + [Species.TRAPINCH]: EggTier.COMMON, + [Species.CACNEA]: EggTier.COMMON, + [Species.SWABLU]: EggTier.COMMON, + [Species.ZANGOOSE]: EggTier.RARE, + [Species.SEVIPER]: EggTier.COMMON, + [Species.LUNATONE]: EggTier.COMMON, + [Species.SOLROCK]: EggTier.COMMON, + [Species.BARBOACH]: EggTier.COMMON, + [Species.CORPHISH]: EggTier.COMMON, + [Species.BALTOY]: EggTier.COMMON, + [Species.LILEEP]: EggTier.COMMON, + [Species.ANORITH]: EggTier.COMMON, + [Species.FEEBAS]: EggTier.RARE, + [Species.CASTFORM]: EggTier.COMMON, + [Species.KECLEON]: EggTier.COMMON, + [Species.SHUPPET]: EggTier.COMMON, + [Species.DUSKULL]: EggTier.COMMON, + [Species.TROPIUS]: EggTier.COMMON, + [Species.CHIMECHO]: EggTier.COMMON, + [Species.ABSOL]: EggTier.RARE, + [Species.WYNAUT]: EggTier.COMMON, + [Species.SNORUNT]: EggTier.COMMON, + [Species.SPHEAL]: EggTier.COMMON, + [Species.CLAMPERL]: EggTier.COMMON, + [Species.RELICANTH]: EggTier.COMMON, + [Species.LUVDISC]: EggTier.COMMON, + [Species.BAGON]: EggTier.RARE, + [Species.BELDUM]: EggTier.RARE, + [Species.REGIROCK]: EggTier.EPIC, + [Species.REGICE]: EggTier.EPIC, + [Species.REGISTEEL]: EggTier.EPIC, + [Species.LATIAS]: EggTier.EPIC, + [Species.LATIOS]: EggTier.EPIC, + [Species.KYOGRE]: EggTier.LEGENDARY, + [Species.GROUDON]: EggTier.LEGENDARY, + [Species.RAYQUAZA]: EggTier.LEGENDARY, + [Species.JIRACHI]: EggTier.EPIC, + [Species.DEOXYS]: EggTier.EPIC, + + [Species.TURTWIG]: EggTier.COMMON, + [Species.CHIMCHAR]: EggTier.COMMON, + [Species.PIPLUP]: EggTier.COMMON, + [Species.STARLY]: EggTier.COMMON, + [Species.BIDOOF]: EggTier.COMMON, + [Species.KRICKETOT]: EggTier.COMMON, + [Species.SHINX]: EggTier.COMMON, + [Species.BUDEW]: EggTier.COMMON, + [Species.CRANIDOS]: EggTier.COMMON, + [Species.SHIELDON]: EggTier.COMMON, + [Species.BURMY]: EggTier.COMMON, + [Species.COMBEE]: EggTier.COMMON, + [Species.PACHIRISU]: EggTier.COMMON, + [Species.BUIZEL]: EggTier.COMMON, + [Species.CHERUBI]: EggTier.COMMON, + [Species.SHELLOS]: EggTier.COMMON, + [Species.DRIFLOON]: EggTier.COMMON, + [Species.BUNEARY]: EggTier.COMMON, + [Species.GLAMEOW]: EggTier.COMMON, + [Species.CHINGLING]: EggTier.COMMON, + [Species.STUNKY]: EggTier.COMMON, + [Species.BRONZOR]: EggTier.COMMON, + [Species.BONSLY]: EggTier.COMMON, + [Species.MIME_JR]: EggTier.COMMON, + [Species.HAPPINY]: EggTier.COMMON, + [Species.CHATOT]: EggTier.COMMON, + [Species.SPIRITOMB]: EggTier.RARE, + [Species.GIBLE]: EggTier.RARE, + [Species.MUNCHLAX]: EggTier.RARE, + [Species.RIOLU]: EggTier.COMMON, + [Species.HIPPOPOTAS]: EggTier.COMMON, + [Species.SKORUPI]: EggTier.COMMON, + [Species.CROAGUNK]: EggTier.COMMON, + [Species.CARNIVINE]: EggTier.COMMON, + [Species.FINNEON]: EggTier.COMMON, + [Species.MANTYKE]: EggTier.COMMON, + [Species.SNOVER]: EggTier.COMMON, + [Species.ROTOM]: EggTier.RARE, + [Species.UXIE]: EggTier.EPIC, + [Species.MESPRIT]: EggTier.EPIC, + [Species.AZELF]: EggTier.EPIC, + [Species.DIALGA]: EggTier.LEGENDARY, + [Species.PALKIA]: EggTier.LEGENDARY, + [Species.HEATRAN]: EggTier.EPIC, + [Species.REGIGIGAS]: EggTier.EPIC, + [Species.GIRATINA]: EggTier.LEGENDARY, + [Species.CRESSELIA]: EggTier.EPIC, + [Species.PHIONE]: EggTier.RARE, + [Species.MANAPHY]: EggTier.EPIC, + [Species.DARKRAI]: EggTier.EPIC, + [Species.SHAYMIN]: EggTier.EPIC, + [Species.ARCEUS]: EggTier.LEGENDARY, + + [Species.VICTINI]: EggTier.EPIC, + [Species.SNIVY]: EggTier.COMMON, + [Species.TEPIG]: EggTier.COMMON, + [Species.OSHAWOTT]: EggTier.COMMON, + [Species.PATRAT]: EggTier.COMMON, + [Species.LILLIPUP]: EggTier.COMMON, + [Species.PURRLOIN]: EggTier.COMMON, + [Species.PANSAGE]: EggTier.COMMON, + [Species.PANSEAR]: EggTier.COMMON, + [Species.PANPOUR]: EggTier.COMMON, + [Species.MUNNA]: EggTier.COMMON, + [Species.PIDOVE]: EggTier.COMMON, + [Species.BLITZLE]: EggTier.COMMON, + [Species.ROGGENROLA]: EggTier.COMMON, + [Species.WOOBAT]: EggTier.COMMON, + [Species.DRILBUR]: EggTier.RARE, + [Species.AUDINO]: EggTier.COMMON, + [Species.TIMBURR]: EggTier.RARE, + [Species.TYMPOLE]: EggTier.COMMON, + [Species.THROH]: EggTier.RARE, + [Species.SAWK]: EggTier.RARE, + [Species.SEWADDLE]: EggTier.COMMON, + [Species.VENIPEDE]: EggTier.COMMON, + [Species.COTTONEE]: EggTier.COMMON, + [Species.PETILIL]: EggTier.COMMON, + [Species.BASCULIN]: EggTier.RARE, + [Species.SANDILE]: EggTier.RARE, + [Species.DARUMAKA]: EggTier.RARE, + [Species.MARACTUS]: EggTier.COMMON, + [Species.DWEBBLE]: EggTier.COMMON, + [Species.SCRAGGY]: EggTier.COMMON, + [Species.SIGILYPH]: EggTier.RARE, + [Species.YAMASK]: EggTier.COMMON, + [Species.TIRTOUGA]: EggTier.COMMON, + [Species.ARCHEN]: EggTier.COMMON, + [Species.TRUBBISH]: EggTier.COMMON, + [Species.ZORUA]: EggTier.COMMON, + [Species.MINCCINO]: EggTier.COMMON, + [Species.GOTHITA]: EggTier.COMMON, + [Species.SOLOSIS]: EggTier.COMMON, + [Species.DUCKLETT]: EggTier.COMMON, + [Species.VANILLITE]: EggTier.COMMON, + [Species.DEERLING]: EggTier.COMMON, + [Species.EMOLGA]: EggTier.COMMON, + [Species.KARRABLAST]: EggTier.COMMON, + [Species.FOONGUS]: EggTier.COMMON, + [Species.FRILLISH]: EggTier.COMMON, + [Species.ALOMOMOLA]: EggTier.RARE, + [Species.JOLTIK]: EggTier.COMMON, + [Species.FERROSEED]: EggTier.COMMON, + [Species.KLINK]: EggTier.COMMON, + [Species.TYNAMO]: EggTier.COMMON, + [Species.ELGYEM]: EggTier.COMMON, + [Species.LITWICK]: EggTier.COMMON, + [Species.AXEW]: EggTier.RARE, + [Species.CUBCHOO]: EggTier.COMMON, + [Species.CRYOGONAL]: EggTier.RARE, + [Species.SHELMET]: EggTier.COMMON, + [Species.STUNFISK]: EggTier.COMMON, + [Species.MIENFOO]: EggTier.COMMON, + [Species.DRUDDIGON]: EggTier.RARE, + [Species.GOLETT]: EggTier.COMMON, + [Species.PAWNIARD]: EggTier.RARE, + [Species.BOUFFALANT]: EggTier.RARE, + [Species.RUFFLET]: EggTier.COMMON, + [Species.VULLABY]: EggTier.COMMON, + [Species.HEATMOR]: EggTier.COMMON, + [Species.DURANT]: EggTier.RARE, + [Species.DEINO]: EggTier.RARE, + [Species.LARVESTA]: EggTier.RARE, + [Species.COBALION]: EggTier.EPIC, + [Species.TERRAKION]: EggTier.EPIC, + [Species.VIRIZION]: EggTier.EPIC, + [Species.TORNADUS]: EggTier.EPIC, + [Species.THUNDURUS]: EggTier.EPIC, + [Species.RESHIRAM]: EggTier.LEGENDARY, + [Species.ZEKROM]: EggTier.LEGENDARY, + [Species.LANDORUS]: EggTier.EPIC, + [Species.KYUREM]: EggTier.LEGENDARY, + [Species.KELDEO]: EggTier.EPIC, + [Species.MELOETTA]: EggTier.EPIC, + [Species.GENESECT]: EggTier.EPIC, + + [Species.CHESPIN]: EggTier.COMMON, + [Species.FENNEKIN]: EggTier.COMMON, + [Species.FROAKIE]: EggTier.RARE, + [Species.BUNNELBY]: EggTier.COMMON, + [Species.FLETCHLING]: EggTier.COMMON, + [Species.SCATTERBUG]: EggTier.COMMON, + [Species.LITLEO]: EggTier.COMMON, + [Species.FLABEBE]: EggTier.COMMON, + [Species.SKIDDO]: EggTier.COMMON, + [Species.PANCHAM]: EggTier.COMMON, + [Species.FURFROU]: EggTier.COMMON, + [Species.ESPURR]: EggTier.COMMON, + [Species.HONEDGE]: EggTier.RARE, + [Species.SPRITZEE]: EggTier.COMMON, + [Species.SWIRLIX]: EggTier.COMMON, + [Species.INKAY]: EggTier.COMMON, + [Species.BINACLE]: EggTier.COMMON, + [Species.SKRELP]: EggTier.COMMON, + [Species.CLAUNCHER]: EggTier.COMMON, + [Species.HELIOPTILE]: EggTier.COMMON, + [Species.TYRUNT]: EggTier.COMMON, + [Species.AMAURA]: EggTier.COMMON, + [Species.HAWLUCHA]: EggTier.RARE, + [Species.DEDENNE]: EggTier.COMMON, + [Species.CARBINK]: EggTier.COMMON, + [Species.GOOMY]: EggTier.RARE, + [Species.KLEFKI]: EggTier.COMMON, + [Species.PHANTUMP]: EggTier.COMMON, + [Species.PUMPKABOO]: EggTier.COMMON, + [Species.BERGMITE]: EggTier.COMMON, + [Species.NOIBAT]: EggTier.COMMON, + [Species.XERNEAS]: EggTier.LEGENDARY, + [Species.YVELTAL]: EggTier.LEGENDARY, + [Species.ZYGARDE]: EggTier.LEGENDARY, + [Species.DIANCIE]: EggTier.EPIC, + [Species.HOOPA]: EggTier.EPIC, + [Species.VOLCANION]: EggTier.EPIC, + [Species.ETERNAL_FLOETTE]: EggTier.RARE, + + [Species.ROWLET]: EggTier.COMMON, + [Species.LITTEN]: EggTier.COMMON, + [Species.POPPLIO]: EggTier.RARE, + [Species.PIKIPEK]: EggTier.COMMON, + [Species.YUNGOOS]: EggTier.COMMON, + [Species.GRUBBIN]: EggTier.COMMON, + [Species.CRABRAWLER]: EggTier.COMMON, + [Species.ORICORIO]: EggTier.COMMON, + [Species.CUTIEFLY]: EggTier.COMMON, + [Species.ROCKRUFF]: EggTier.COMMON, + [Species.WISHIWASHI]: EggTier.COMMON, + [Species.MAREANIE]: EggTier.COMMON, + [Species.MUDBRAY]: EggTier.COMMON, + [Species.DEWPIDER]: EggTier.COMMON, + [Species.FOMANTIS]: EggTier.COMMON, + [Species.MORELULL]: EggTier.COMMON, + [Species.SALANDIT]: EggTier.COMMON, + [Species.STUFFUL]: EggTier.COMMON, + [Species.BOUNSWEET]: EggTier.COMMON, + [Species.COMFEY]: EggTier.RARE, + [Species.ORANGURU]: EggTier.RARE, + [Species.PASSIMIAN]: EggTier.RARE, + [Species.WIMPOD]: EggTier.COMMON, + [Species.SANDYGAST]: EggTier.COMMON, + [Species.PYUKUMUKU]: EggTier.COMMON, + [Species.TYPE_NULL]: EggTier.RARE, + [Species.MINIOR]: EggTier.RARE, + [Species.KOMALA]: EggTier.COMMON, + [Species.TURTONATOR]: EggTier.RARE, + [Species.TOGEDEMARU]: EggTier.COMMON, + [Species.MIMIKYU]: EggTier.RARE, + [Species.BRUXISH]: EggTier.RARE, + [Species.DRAMPA]: EggTier.RARE, + [Species.DHELMISE]: EggTier.RARE, + [Species.JANGMO_O]: EggTier.RARE, + [Species.TAPU_KOKO]: EggTier.EPIC, + [Species.TAPU_LELE]: EggTier.EPIC, + [Species.TAPU_BULU]: EggTier.EPIC, + [Species.TAPU_FINI]: EggTier.EPIC, + [Species.COSMOG]: EggTier.EPIC, + [Species.NIHILEGO]: EggTier.EPIC, + [Species.BUZZWOLE]: EggTier.EPIC, + [Species.PHEROMOSA]: EggTier.EPIC, + [Species.XURKITREE]: EggTier.EPIC, + [Species.CELESTEELA]: EggTier.EPIC, + [Species.KARTANA]: EggTier.EPIC, + [Species.GUZZLORD]: EggTier.EPIC, + [Species.NECROZMA]: EggTier.LEGENDARY, + [Species.MAGEARNA]: EggTier.EPIC, + [Species.MARSHADOW]: EggTier.EPIC, + [Species.POIPOLE]: EggTier.EPIC, + [Species.STAKATAKA]: EggTier.EPIC, + [Species.BLACEPHALON]: EggTier.EPIC, + [Species.ZERAORA]: EggTier.EPIC, + [Species.MELTAN]: EggTier.EPIC, + [Species.ALOLA_RATTATA]: EggTier.COMMON, + [Species.ALOLA_SANDSHREW]: EggTier.COMMON, + [Species.ALOLA_VULPIX]: EggTier.COMMON, + [Species.ALOLA_DIGLETT]: EggTier.COMMON, + [Species.ALOLA_MEOWTH]: EggTier.COMMON, + [Species.ALOLA_GEODUDE]: EggTier.COMMON, + [Species.ALOLA_GRIMER]: EggTier.COMMON, + + [Species.GROOKEY]: EggTier.COMMON, + [Species.SCORBUNNY]: EggTier.RARE, + [Species.SOBBLE]: EggTier.COMMON, + [Species.SKWOVET]: EggTier.COMMON, + [Species.ROOKIDEE]: EggTier.COMMON, + [Species.BLIPBUG]: EggTier.COMMON, + [Species.NICKIT]: EggTier.COMMON, + [Species.GOSSIFLEUR]: EggTier.COMMON, + [Species.WOOLOO]: EggTier.COMMON, + [Species.CHEWTLE]: EggTier.COMMON, + [Species.YAMPER]: EggTier.COMMON, + [Species.ROLYCOLY]: EggTier.COMMON, + [Species.APPLIN]: EggTier.COMMON, + [Species.SILICOBRA]: EggTier.COMMON, + [Species.CRAMORANT]: EggTier.COMMON, + [Species.ARROKUDA]: EggTier.COMMON, + [Species.TOXEL]: EggTier.COMMON, + [Species.SIZZLIPEDE]: EggTier.COMMON, + [Species.CLOBBOPUS]: EggTier.COMMON, + [Species.SINISTEA]: EggTier.COMMON, + [Species.HATENNA]: EggTier.COMMON, + [Species.IMPIDIMP]: EggTier.COMMON, + [Species.MILCERY]: EggTier.COMMON, + [Species.FALINKS]: EggTier.RARE, + [Species.PINCURCHIN]: EggTier.COMMON, + [Species.SNOM]: EggTier.COMMON, + [Species.STONJOURNER]: EggTier.COMMON, + [Species.EISCUE]: EggTier.COMMON, + [Species.INDEEDEE]: EggTier.RARE, + [Species.MORPEKO]: EggTier.COMMON, + [Species.CUFANT]: EggTier.COMMON, + [Species.DRACOZOLT]: EggTier.RARE, + [Species.ARCTOZOLT]: EggTier.RARE, + [Species.DRACOVISH]: EggTier.RARE, + [Species.ARCTOVISH]: EggTier.RARE, + [Species.DURALUDON]: EggTier.RARE, + [Species.DREEPY]: EggTier.RARE, + [Species.ZACIAN]: EggTier.LEGENDARY, + [Species.ZAMAZENTA]: EggTier.LEGENDARY, + [Species.ETERNATUS]: EggTier.COMMON, + [Species.KUBFU]: EggTier.EPIC, + [Species.ZARUDE]: EggTier.EPIC, + [Species.REGIELEKI]: EggTier.EPIC, + [Species.REGIDRAGO]: EggTier.EPIC, + [Species.GLASTRIER]: EggTier.EPIC, + [Species.SPECTRIER]: EggTier.EPIC, + [Species.CALYREX]: EggTier.LEGENDARY, + [Species.GALAR_MEOWTH]: EggTier.COMMON, + [Species.GALAR_PONYTA]: EggTier.COMMON, + [Species.GALAR_SLOWPOKE]: EggTier.COMMON, + [Species.GALAR_FARFETCHD]: EggTier.COMMON, + [Species.GALAR_CORSOLA]: EggTier.COMMON, + [Species.GALAR_ZIGZAGOON]: EggTier.COMMON, + [Species.GALAR_DARUMAKA]: EggTier.RARE, + [Species.GALAR_YAMASK]: EggTier.COMMON, + [Species.GALAR_STUNFISK]: EggTier.COMMON, + [Species.GALAR_MR_MIME]: EggTier.COMMON, + [Species.GALAR_ARTICUNO]: EggTier.EPIC, + [Species.GALAR_ZAPDOS]: EggTier.EPIC, + [Species.GALAR_MOLTRES]: EggTier.EPIC, + [Species.HISUI_GROWLITHE]: EggTier.RARE, + [Species.HISUI_VOLTORB]: EggTier.COMMON, + [Species.HISUI_QWILFISH]: EggTier.RARE, + [Species.HISUI_SNEASEL]: EggTier.RARE, + [Species.HISUI_ZORUA]: EggTier.COMMON, + [Species.ENAMORUS]: EggTier.EPIC, + + [Species.SPRIGATITO]: EggTier.RARE, + [Species.FUECOCO]: EggTier.RARE, + [Species.QUAXLY]: EggTier.RARE, + [Species.LECHONK]: EggTier.COMMON, + [Species.TAROUNTULA]: EggTier.COMMON, + [Species.NYMBLE]: EggTier.COMMON, + [Species.PAWMI]: EggTier.COMMON, + [Species.TANDEMAUS]: EggTier.RARE, + [Species.FIDOUGH]: EggTier.COMMON, + [Species.SMOLIV]: EggTier.COMMON, + [Species.SQUAWKABILLY]: EggTier.COMMON, + [Species.NACLI]: EggTier.RARE, + [Species.CHARCADET]: EggTier.RARE, + [Species.TADBULB]: EggTier.COMMON, + [Species.WATTREL]: EggTier.COMMON, + [Species.MASCHIFF]: EggTier.COMMON, + [Species.SHROODLE]: EggTier.COMMON, + [Species.BRAMBLIN]: EggTier.COMMON, + [Species.TOEDSCOOL]: EggTier.COMMON, + [Species.KLAWF]: EggTier.COMMON, + [Species.CAPSAKID]: EggTier.COMMON, + [Species.RELLOR]: EggTier.COMMON, + [Species.FLITTLE]: EggTier.COMMON, + [Species.TINKATINK]: EggTier.RARE, + [Species.WIGLETT]: EggTier.COMMON, + [Species.BOMBIRDIER]: EggTier.COMMON, + [Species.FINIZEN]: EggTier.COMMON, + [Species.VAROOM]: EggTier.RARE, + [Species.CYCLIZAR]: EggTier.RARE, + [Species.ORTHWORM]: EggTier.RARE, + [Species.GLIMMET]: EggTier.RARE, + [Species.GREAVARD]: EggTier.COMMON, + [Species.FLAMIGO]: EggTier.RARE, + [Species.CETODDLE]: EggTier.COMMON, + [Species.VELUZA]: EggTier.RARE, + [Species.DONDOZO]: EggTier.RARE, + [Species.TATSUGIRI]: EggTier.RARE, + [Species.GREAT_TUSK]: EggTier.EPIC, + [Species.SCREAM_TAIL]: EggTier.EPIC, + [Species.BRUTE_BONNET]: EggTier.EPIC, + [Species.FLUTTER_MANE]: EggTier.EPIC, + [Species.SLITHER_WING]: EggTier.EPIC, + [Species.SANDY_SHOCKS]: EggTier.EPIC, + [Species.IRON_TREADS]: EggTier.EPIC, + [Species.IRON_BUNDLE]: EggTier.EPIC, + [Species.IRON_HANDS]: EggTier.EPIC, + [Species.IRON_JUGULIS]: EggTier.EPIC, + [Species.IRON_MOTH]: EggTier.EPIC, + [Species.IRON_THORNS]: EggTier.EPIC, + [Species.FRIGIBAX]: EggTier.RARE, + [Species.GIMMIGHOUL]: EggTier.RARE, + [Species.WO_CHIEN]: EggTier.EPIC, + [Species.CHIEN_PAO]: EggTier.EPIC, + [Species.TING_LU]: EggTier.EPIC, + [Species.CHI_YU]: EggTier.EPIC, + [Species.ROARING_MOON]: EggTier.EPIC, + [Species.IRON_VALIANT]: EggTier.EPIC, + [Species.KORAIDON]: EggTier.LEGENDARY, + [Species.MIRAIDON]: EggTier.LEGENDARY, + [Species.WALKING_WAKE]: EggTier.EPIC, + [Species.IRON_LEAVES]: EggTier.EPIC, + [Species.POLTCHAGEIST]: EggTier.RARE, + [Species.OKIDOGI]: EggTier.EPIC, + [Species.MUNKIDORI]: EggTier.EPIC, + [Species.FEZANDIPITI]: EggTier.EPIC, + [Species.OGERPON]: EggTier.EPIC, + [Species.GOUGING_FIRE]: EggTier.EPIC, + [Species.RAGING_BOLT]: EggTier.EPIC, + [Species.IRON_BOULDER]: EggTier.EPIC, + [Species.IRON_CROWN]: EggTier.EPIC, + [Species.TERAPAGOS]: EggTier.LEGENDARY, + [Species.PECHARUNT]: EggTier.EPIC, + [Species.PALDEA_TAUROS]: EggTier.RARE, + [Species.PALDEA_WOOPER]: EggTier.COMMON, + [Species.BLOODMOON_URSALUNA]: EggTier.EPIC, +}; diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index a54a8c5f519..18f03ada941 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1376,7 +1376,7 @@ export class ContactStatStageChangeProtectedTag extends DamageProtectedTag { const effectPhase = pokemon.scene.getCurrentPhase(); if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) { const attacker = effectPhase.getPokemon(); - pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), true, [ this.stat ], this.levels)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), false, [ this.stat ], this.levels)); } } @@ -1713,7 +1713,12 @@ export class TypeImmuneTag extends BattlerTag { } } -export class MagnetRisenTag extends TypeImmuneTag { +/** + * Battler Tag that lifts the affected Pokemon into the air and provides immunity to Ground type moves. + * @see {@link https://bulbapedia.bulbagarden.net/wiki/Magnet_Rise_(move) | Moves.MAGNET_RISE} + * @see {@link https://bulbapedia.bulbagarden.net/wiki/Telekinesis_(move) | Moves.TELEKINESIS} + */ +export class FloatingTag extends TypeImmuneTag { constructor(tagType: BattlerTagType, sourceMove: Moves) { super(tagType, sourceMove, Type.GROUND, 5); } @@ -1721,13 +1726,17 @@ export class MagnetRisenTag extends TypeImmuneTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + if (this.sourceMove === Moves.MAGNET_RISE) { + pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + } + } onRemove(pokemon: Pokemon): void { super.onRemove(pokemon); - - pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + if (this.sourceMove === Moves.MAGNET_RISE) { + pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + } } } @@ -2139,6 +2148,10 @@ export class GulpMissileTag extends BattlerTag { return false; } + if (moveEffectPhase.move.getMove().hitsSubstitute(attacker, pokemon)) { + return true; + } + const cancelled = new Utils.BooleanHolder(false); applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled); @@ -2636,16 +2649,16 @@ export class ImprisonTag extends MoveRestrictionBattlerTag { /** * Battler Tag that applies the effects of Syrup Bomb to the target Pokemon. * For three turns, starting from the turn of hit, at the end of each turn, the target Pokemon's speed will decrease by 1. - * The tag can also expire by taking the target Pokemon off the field. + * The tag can also expire by taking the target Pokemon off the field, or the Pokemon that originally used the move. */ export class SyrupBombTag extends BattlerTag { - constructor() { - super(BattlerTagType.SYRUP_BOMB, BattlerTagLapseType.TURN_END, 3, Moves.SYRUP_BOMB); + constructor(sourceId: number) { + super(BattlerTagType.SYRUP_BOMB, BattlerTagLapseType.TURN_END, 3, Moves.SYRUP_BOMB, sourceId); } /** * Adds the Syrup Bomb battler tag to the target Pokemon. - * @param {Pokemon} pokemon the target Pokemon + * @param pokemon - The target {@linkcode Pokemon} */ override onAdd(pokemon: Pokemon) { super.onAdd(pokemon); @@ -2654,15 +2667,16 @@ export class SyrupBombTag extends BattlerTag { /** * Applies the single-stage speed down to the target Pokemon and decrements the tag's turn count - * @param {Pokemon} pokemon the target Pokemon - * @param {BattlerTagLapseType} _lapseType - * @returns `true` if the turnCount is still greater than 0 | `false` if the turnCount is 0 or the target Pokemon has been removed from the field + * @param pokemon - The target {@linkcode Pokemon} + * @param _lapseType - N/A + * @returns `true` if the `turnCount` is still greater than `0`; `false` if the `turnCount` is `0` or the target or source Pokemon has been removed from the field */ override lapse(pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean { - if (!pokemon.isActive(true)) { + if (this.sourceId && !pokemon.scene.getPokemonById(this.sourceId)?.isActive(true)) { return false; } - pokemon.scene.queueMessage(i18next.t("battlerTags:syrupBombLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); // Custom message in lieu of an animation in mainline + // Custom message in lieu of an animation in mainline + pokemon.scene.queueMessage(i18next.t("battlerTags:syrupBombLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); pokemon.scene.unshiftPhase(new StatStageChangePhase( pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.SPD ], -1, true, false, true @@ -2671,14 +2685,26 @@ export class SyrupBombTag extends BattlerTag { } } +/** + * Telekinesis raises the target into the air for three turns and causes all moves used against the target (aside from OHKO moves) to hit the target unless the target is in a semi-invulnerable state from Fly/Dig. + * The first effect is provided by {@linkcode FloatingTag}, the accuracy-bypass effect is provided by TelekinesisTag + * The effects of Telekinesis can be baton passed to a teammate. Unlike the mainline games, Telekinesis can be baton-passed to Mega Gengar. + * @see {@link https://bulbapedia.bulbagarden.net/wiki/Telekinesis_(move) | Moves.TELEKINESIS} + */ +export class TelekinesisTag extends BattlerTag { + constructor(sourceMove: Moves) { + super(BattlerTagType.TELEKINESIS, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.AFTER_MOVE ], 3, sourceMove, undefined, true); + } + + override onAdd(pokemon: Pokemon) { + pokemon.scene.queueMessage(i18next.t("battlerTags:telekinesisOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + } +} + /** * Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID. - * - * @param {BattlerTagType} tagType the type of the {@linkcode BattlerTagType}. - * @param turnCount the turn count. - * @param {Moves} sourceMove the source {@linkcode Moves}. - * @param sourceId the source ID. - * @returns {BattlerTag} the corresponding {@linkcode BattlerTag} object. + * @param sourceId - The ID of the pokemon adding the tag + * @returns The corresponding {@linkcode BattlerTag} object. */ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, sourceMove: Moves, sourceId: number): BattlerTag { switch (tagType) { @@ -2801,8 +2827,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new CursedTag(sourceId); case BattlerTagType.CHARGED: return new TypeBoostTag(tagType, sourceMove, Type.ELECTRIC, 2, true); - case BattlerTagType.MAGNET_RISEN: - return new MagnetRisenTag(tagType, sourceMove); + case BattlerTagType.FLOATING: + return new FloatingTag(tagType, sourceMove); case BattlerTagType.MINIMIZED: return new MinimizeTag(); case BattlerTagType.DESTINY_BOND: @@ -2847,7 +2873,9 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source case BattlerTagType.IMPRISON: return new ImprisonTag(sourceId); case BattlerTagType.SYRUP_BOMB: - return new SyrupBombTag(); + return new SyrupBombTag(sourceId); + case BattlerTagType.TELEKINESIS: + return new TelekinesisTag(sourceMove); case BattlerTagType.NONE: default: return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); diff --git a/src/data/egg.ts b/src/data/egg.ts index 5fffe4fcece..c475fc729e6 100644 --- a/src/data/egg.ts +++ b/src/data/egg.ts @@ -11,6 +11,7 @@ import { EggTier } from "#enums/egg-type"; import { Species } from "#enums/species"; import { EggSourceType } from "#enums/egg-source-types"; import { MANAPHY_EGG_MANAPHY_RATE, SAME_SPECIES_EGG_HA_RATE, GACHA_EGG_HA_RATE, GACHA_DEFAULT_RARE_EGGMOVE_RATE, SAME_SPECIES_EGG_RARE_EGGMOVE_RATE, GACHA_MOVE_UP_RARE_EGGMOVE_RATE, GACHA_DEFAULT_SHINY_RATE, GACHA_SHINY_UP_SHINY_RATE, SAME_SPECIES_EGG_SHINY_RATE, EGG_PITY_LEGENDARY_THRESHOLD, EGG_PITY_EPIC_THRESHOLD, EGG_PITY_RARE_THRESHOLD, SHINY_VARIANT_CHANCE, SHINY_EPIC_CHANCE, GACHA_DEFAULT_COMMON_EGG_THRESHOLD, GACHA_DEFAULT_RARE_EGG_THRESHOLD, GACHA_DEFAULT_EPIC_EGG_THRESHOLD, GACHA_LEGENDARY_UP_THRESHOLD_OFFSET, HATCH_WAVES_MANAPHY_EGG, HATCH_WAVES_COMMON_EGG, HATCH_WAVES_RARE_EGG, HATCH_WAVES_EPIC_EGG, HATCH_WAVES_LEGENDARY_EGG } from "#app/data/balance/rates"; +import { speciesEggTiers } from "#app/data/balance/species-egg-tiers"; export const EGG_SEED = 1073741824; @@ -160,7 +161,7 @@ export class Egg { // Override egg tier and hatchwaves if species was given if (eggOptions?.species) { - this._tier = this.getEggTierFromSpeciesStarterValue(); + this._tier = this.getEggTier(); this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves(); } // If species has no variant, set variantTier to common. This needs to @@ -261,11 +262,11 @@ export class Egg { return "Manaphy"; } switch (this.tier) { - case EggTier.GREAT: + case EggTier.RARE: return i18next.t("egg:greatTier"); - case EggTier.ULTRA: + case EggTier.EPIC: return i18next.t("egg:ultraTier"); - case EggTier.MASTER: + case EggTier.LEGENDARY: return i18next.t("egg:masterTier"); default: return i18next.t("egg:defaultTier"); @@ -336,9 +337,9 @@ export class Egg { switch (eggTier ?? this._tier) { case EggTier.COMMON: return HATCH_WAVES_COMMON_EGG; - case EggTier.GREAT: + case EggTier.RARE: return HATCH_WAVES_RARE_EGG; - case EggTier.ULTRA: + case EggTier.EPIC: return HATCH_WAVES_EPIC_EGG; } return HATCH_WAVES_LEGENDARY_EGG; @@ -347,7 +348,7 @@ export class Egg { private rollEggTier(): EggTier { const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0; const tierValue = Utils.randInt(256); - return tierValue >= GACHA_DEFAULT_COMMON_EGG_THRESHOLD + tierValueOffset ? EggTier.COMMON : tierValue >= GACHA_DEFAULT_RARE_EGG_THRESHOLD + tierValueOffset ? EggTier.GREAT : tierValue >= GACHA_DEFAULT_EPIC_EGG_THRESHOLD + tierValueOffset ? EggTier.ULTRA : EggTier.MASTER; + return tierValue >= GACHA_DEFAULT_COMMON_EGG_THRESHOLD + tierValueOffset ? EggTier.COMMON : tierValue >= GACHA_DEFAULT_RARE_EGG_THRESHOLD + tierValueOffset ? EggTier.RARE : tierValue >= GACHA_DEFAULT_EPIC_EGG_THRESHOLD + tierValueOffset ? EggTier.EPIC : EggTier.LEGENDARY; } private rollSpecies(scene: BattleScene): Species | null { @@ -367,7 +368,7 @@ export class Egg { */ const rand = (Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE) !== 1); return rand ? Species.PHIONE : Species.MANAPHY; - } else if (this.tier === EggTier.MASTER + } else if (this.tier === EggTier.LEGENDARY && this._sourceType === EggSourceType.GACHA_LEGENDARY) { if (!Utils.randSeedInt(2)) { return getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp); @@ -378,15 +379,15 @@ export class Egg { let maxStarterValue: integer; switch (this.tier) { - case EggTier.GREAT: + case EggTier.RARE: minStarterValue = 4; maxStarterValue = 5; break; - case EggTier.ULTRA: + case EggTier.EPIC: minStarterValue = 6; maxStarterValue = 7; break; - case EggTier.MASTER: + case EggTier.LEGENDARY: minStarterValue = 8; maxStarterValue = 9; break; @@ -398,8 +399,8 @@ export class Egg { const ignoredSpecies = [ Species.PHIONE, Species.MANAPHY, Species.ETERNATUS ]; - let speciesPool = Object.keys(speciesStarterCosts) - .filter(s => speciesStarterCosts[s] >= minStarterValue && speciesStarterCosts[s] <= maxStarterValue) + let speciesPool = Object.keys(speciesEggTiers) + .filter(s => speciesEggTiers[s] === this.tier) .map(s => parseInt(s) as Species) .filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1); @@ -430,7 +431,9 @@ export class Egg { let totalWeight = 0; const speciesWeights : number[] = []; for (const speciesId of speciesPool) { - let weight = Math.floor((((maxStarterValue - speciesStarterCosts[speciesId]) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100); + // Accounts for species that have starter costs outside of the normal range for their EggTier + const speciesCostClamped = Phaser.Math.Clamp(speciesStarterCosts[speciesId], minStarterValue, maxStarterValue); + let weight = Math.floor((((maxStarterValue - speciesCostClamped) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100); const species = getPokemonSpecies(speciesId); if (species.isRegional()) { weight = Math.floor(weight / 2); @@ -498,16 +501,16 @@ export class Egg { private checkForPityTierOverrides(scene: BattleScene): void { const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0; - scene.gameData.eggPity[EggTier.GREAT] += 1; - scene.gameData.eggPity[EggTier.ULTRA] += 1; - scene.gameData.eggPity[EggTier.MASTER] += 1 + tierValueOffset; + scene.gameData.eggPity[EggTier.RARE] += 1; + scene.gameData.eggPity[EggTier.EPIC] += 1; + scene.gameData.eggPity[EggTier.LEGENDARY] += 1 + tierValueOffset; // These numbers are roughly the 80% mark. That is, 80% of the time you'll get an egg before this gets triggered. - if (scene.gameData.eggPity[EggTier.MASTER] >= EGG_PITY_LEGENDARY_THRESHOLD && this._tier === EggTier.COMMON) { - this._tier = EggTier.MASTER; - } else if (scene.gameData.eggPity[EggTier.ULTRA] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) { - this._tier = EggTier.ULTRA; - } else if (scene.gameData.eggPity[EggTier.GREAT] >= EGG_PITY_RARE_THRESHOLD && this._tier === EggTier.COMMON) { - this._tier = EggTier.GREAT; + if (scene.gameData.eggPity[EggTier.LEGENDARY] >= EGG_PITY_LEGENDARY_THRESHOLD && this._tier === EggTier.COMMON) { + this._tier = EggTier.LEGENDARY; + } else if (scene.gameData.eggPity[EggTier.EPIC] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) { + this._tier = EggTier.EPIC; + } else if (scene.gameData.eggPity[EggTier.RARE] >= EGG_PITY_RARE_THRESHOLD && this._tier === EggTier.COMMON) { + this._tier = EggTier.RARE; } scene.gameData.eggPity[this._tier] = 0; } @@ -516,38 +519,24 @@ export class Egg { scene.gameData.gameStats.eggsPulled++; if (this.isManaphyEgg()) { scene.gameData.gameStats.manaphyEggsPulled++; - this._hatchWaves = this.getEggTierDefaultHatchWaves(EggTier.ULTRA); + this._hatchWaves = this.getEggTierDefaultHatchWaves(EggTier.EPIC); return; } switch (this.tier) { - case EggTier.GREAT: + case EggTier.RARE: scene.gameData.gameStats.rareEggsPulled++; break; - case EggTier.ULTRA: + case EggTier.EPIC: scene.gameData.gameStats.epicEggsPulled++; break; - case EggTier.MASTER: + case EggTier.LEGENDARY: scene.gameData.gameStats.legendaryEggsPulled++; break; } } - private getEggTierFromSpeciesStarterValue(): EggTier { - const speciesStartValue = speciesStarterCosts[this.species]; - if (speciesStartValue >= 1 && speciesStartValue <= 3) { - return EggTier.COMMON; - } - if (speciesStartValue >= 4 && speciesStartValue <= 5) { - return EggTier.GREAT; - } - if (speciesStartValue >= 6 && speciesStartValue <= 7) { - return EggTier.ULTRA; - } - if (speciesStartValue >= 8) { - return EggTier.MASTER; - } - - return EggTier.COMMON; + private getEggTier(): EggTier { + return speciesEggTiers[this.species]; } //// @@ -556,8 +545,8 @@ export class Egg { } export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timestamp: number): Species { - const legendarySpecies = Object.entries(speciesStarterCosts) - .filter(s => s[1] >= 8 && s[1] <= 9) + const legendarySpecies = Object.entries(speciesEggTiers) + .filter(s => s[1] === EggTier.LEGENDARY) .map(s => parseInt(s[0])) .filter(s => getPokemonSpecies(s).isObtainable()); @@ -579,17 +568,9 @@ export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timesta /** * Check for a given species EggTier Value - * @param species - Species for wich we will check the egg tier it belongs to + * @param pokemonSpecies - Species for wich we will check the egg tier it belongs to * @returns The egg tier of a given pokemon species */ export function getEggTierForSpecies(pokemonSpecies :PokemonSpecies): EggTier { - const speciesBaseValue = speciesStarterCosts[pokemonSpecies.getRootSpeciesId()]; - if (speciesBaseValue <= 3) { - return EggTier.COMMON; - } else if (speciesBaseValue <= 5) { - return EggTier.GREAT; - } else if (speciesBaseValue <= 7) { - return EggTier.ULTRA; - } - return EggTier.MASTER; + return speciesEggTiers[pokemonSpecies.getRootSpeciesId()]; } diff --git a/src/data/move.ts b/src/data/move.ts index 08c00829b48..bd3706545f9 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1938,12 +1938,21 @@ export class IncrementMovePriorityAttr extends MoveAttr { * @see {@linkcode apply} */ export class MultiHitAttr extends MoveAttr { + /** This move's intrinsic multi-hit type. It should never be modified. */ + private readonly intrinsicMultiHitType: MultiHitType; + /** This move's current multi-hit type. It may be temporarily modified by abilities (e.g., Battle Bond). */ private multiHitType: MultiHitType; constructor(multiHitType?: MultiHitType) { super(); - this.multiHitType = multiHitType !== undefined ? multiHitType : MultiHitType._2_TO_5; + this.intrinsicMultiHitType = multiHitType !== undefined ? multiHitType : MultiHitType._2_TO_5; + this.multiHitType = this.intrinsicMultiHitType; + } + + // Currently used by `battle_bond.test.ts` + getMultiHitType(): MultiHitType { + return this.multiHitType; } /** @@ -1957,7 +1966,7 @@ export class MultiHitAttr extends MoveAttr { * @returns True */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const hitType = new Utils.NumberHolder(this.multiHitType); + const hitType = new Utils.NumberHolder(this.intrinsicMultiHitType); applyMoveAttrs(ChangeMultiHitTypeAttr, user, target, move, hitType); this.multiHitType = hitType.value; @@ -4107,11 +4116,11 @@ export class StatusCategoryOnAllyAttr extends VariableMoveCategoryAttr { * @param user {@linkcode Pokemon} using the move * @param target {@linkcode Pokemon} target of the move * @param move {@linkcode Move} with this attribute - * @param args [0] {@linkcode Utils.IntegerHolder} The category of the move + * @param args [0] {@linkcode Utils.NumberHolder} The category of the move * @returns true if the function succeeds */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const category = (args[0] as Utils.IntegerHolder); + const category = (args[0] as Utils.NumberHolder); if (user.getAlly() === target) { category.value = MoveCategory.STATUS; @@ -4547,18 +4556,19 @@ export class WaterSuperEffectTypeMultiplierAttr extends VariableMoveTypeMultipli export class IceNoEffectTypeAttr extends VariableMoveTypeMultiplierAttr { /** * Checks to see if the Target is Ice-Type or not. If so, the move will have no effect. - * @param {Pokemon} user N/A - * @param {Pokemon} target Pokemon that is being checked whether Ice-Type or not. - * @param {Move} move N/A - * @param {any[]} args Sets to false if the target is Ice-Type, so it should do no damage/no effect. - * @returns {boolean} Returns true if move is successful, false if Ice-Type. + * @param user n/a + * @param target The {@linkcode Pokemon} targeted by the move + * @param move n/a + * @param args `[0]` a {@linkcode Utils.NumberHolder | NumberHolder} containing a type effectiveness multiplier + * @returns `true` if this Ice-type immunity applies; `false` otherwise */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + const multiplier = args[0] as Utils.NumberHolder; if (target.isOfType(Type.ICE)) { - (args[0] as Utils.BooleanHolder).value = false; - return false; + multiplier.value = 0; + return true; } - return true; + return false; } } @@ -4835,14 +4845,14 @@ export class GulpMissileTagAttr extends MoveEffectAttr { /** * Adds BattlerTagType from GulpMissileTag based on the Pokemon's HP ratio. - * @param {Pokemon} user The Pokemon using the move. - * @param {Pokemon} target The Pokemon being targeted by the move. - * @param {Move} move The move being used. - * @param {any[]} args Additional arguments, if any. + * @param user The Pokemon using the move. + * @param _target N/A + * @param move The move being used. + * @param _args N/A * @returns Whether the BattlerTag is applied. */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise { - if (!super.apply(user, target, move, args)) { + apply(user: Pokemon, _target: Pokemon, move: Move, _args: any[]): boolean { + if (!super.apply(user, _target, move, _args)) { return false; } @@ -7833,7 +7843,9 @@ export function initMoves() { .attr(RandomMovesetMoveAttr, true) .ignoresVirtual(), new SelfStatusMove(Moves.INGRAIN, Type.GRASS, -1, 20, -1, 0, 3) - .attr(AddBattlerTagAttr, BattlerTagType.INGRAIN, true, true), + .attr(AddBattlerTagAttr, BattlerTagType.INGRAIN, true, true) + .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, true, true) + .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLOATING ], true), new AttackMove(Moves.SUPERPOWER, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 3) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], -1, true), new SelfStatusMove(Moves.MAGIC_COAT, Type.PSYCHIC, -1, 15, -1, 4, 3) @@ -8167,8 +8179,8 @@ export function initMoves() { new SelfStatusMove(Moves.AQUA_RING, Type.WATER, -1, 20, -1, 0, 4) .attr(AddBattlerTagAttr, BattlerTagType.AQUA_RING, true, true), new SelfStatusMove(Moves.MAGNET_RISE, Type.ELECTRIC, -1, 10, -1, 0, 4) - .attr(AddBattlerTagAttr, BattlerTagType.MAGNET_RISEN, true, true) - .condition((user, target, move) => !user.scene.arena.getTag(ArenaTagType.GRAVITY) && [ BattlerTagType.MAGNET_RISEN, BattlerTagType.IGNORE_FLYING, BattlerTagType.INGRAIN ].every((tag) => !user.getTag(tag))), + .attr(AddBattlerTagAttr, BattlerTagType.FLOATING, true, true) + .condition((user, target, move) => !user.scene.arena.getTag(ArenaTagType.GRAVITY) && [ BattlerTagType.FLOATING, BattlerTagType.IGNORE_FLYING, BattlerTagType.INGRAIN ].every((tag) => !user.getTag(tag))), new AttackMove(Moves.FLARE_BLITZ, Type.FIRE, MoveCategory.PHYSICAL, 120, 100, 15, 10, 0, 4) .attr(RecoilAttr, false, 0.33) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) @@ -8393,7 +8405,11 @@ export function initMoves() { .attr(AddBattlerTagAttr, BattlerTagType.CENTER_OF_ATTENTION, true), new StatusMove(Moves.TELEKINESIS, Type.PSYCHIC, -1, 15, -1, 0, 5) .condition(failOnGravityCondition) - .unimplemented(), + .condition((_user, target, _move) => ![ Species.DIGLETT, Species.DUGTRIO, Species.ALOLA_DIGLETT, Species.ALOLA_DUGTRIO, Species.SANDYGAST, Species.PALOSSAND, Species.WIGLETT, Species.WUGTRIO ].includes(target.species.speciesId)) + .condition((_user, target, _move) => !(target.species.speciesId === Species.GENGAR && target.getFormKey() === "mega")) + .condition((_user, target, _move) => Utils.isNullOrUndefined(target.getTag(BattlerTagType.INGRAIN)) && Utils.isNullOrUndefined(target.getTag(BattlerTagType.IGNORE_FLYING))) + .attr(AddBattlerTagAttr, BattlerTagType.TELEKINESIS, false, true, 3) + .attr(AddBattlerTagAttr, BattlerTagType.FLOATING, false, true, 3), new StatusMove(Moves.MAGIC_ROOM, Type.PSYCHIC, -1, 10, -1, 0, 5) .ignoresProtect() .target(MoveTarget.BOTH_SIDES) @@ -8401,7 +8417,7 @@ export function initMoves() { new AttackMove(Moves.SMACK_DOWN, Type.ROCK, MoveCategory.PHYSICAL, 50, 100, 15, 100, 0, 5) .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true) .attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED) - .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.MAGNET_RISEN ]) + .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.FLOATING, BattlerTagType.TELEKINESIS ]) .attr(HitsTagAttr, BattlerTagType.FLYING) .makesContact(false), new AttackMove(Moves.STORM_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 5) @@ -8834,9 +8850,9 @@ export function initMoves() { .attr(NeutralDamageAgainstFlyingTypeMultiplierAttr) .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true) .attr(HitsTagAttr, BattlerTagType.FLYING) - .attr(HitsTagAttr, BattlerTagType.MAGNET_RISEN) + .attr(HitsTagAttr, BattlerTagType.FLOATING) .attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED) - .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.MAGNET_RISEN ]) + .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.FLOATING, BattlerTagType.TELEKINESIS ]) .makesContact(false) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.THOUSAND_WAVES, Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 6) diff --git a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts index 13e187179d4..4f3420f5194 100644 --- a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts +++ b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts @@ -128,6 +128,7 @@ export const ATrainersTestEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) @@ -149,7 +150,7 @@ export const ATrainersTestEncounter: MysteryEncounter = pulled: false, sourceType: EggSourceType.EVENT, eggDescriptor: encounter.misc.trainerEggDescription, - tier: EggTier.ULTRA + tier: EggTier.EPIC }; encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`)); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SACRED_ASH ], guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ULTRA ], fillRemaining: true }, [ eggOptions ]); @@ -171,7 +172,7 @@ export const ATrainersTestEncounter: MysteryEncounter = pulled: false, sourceType: EggSourceType.EVENT, eggDescriptor: encounter.misc.trainerEggDescription, - tier: EggTier.GREAT + tier: EggTier.RARE }; encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.rare`)); setEncounterRewards(scene, { fillRemaining: false, rerollMultiplier: -1 }, [ eggOptions ]); diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index c98947a3f93..70b2d50fe99 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -166,6 +166,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = text: `${namespace}:intro`, } ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts index e445a8f481d..ab892ae00f2 100644 --- a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts +++ b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts @@ -64,6 +64,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = speaker: `${namespace}:speaker`, }, ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index 3e5d75727b1..095f8a8473b 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -110,6 +110,7 @@ export const BerriesAboundEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts index 20c0569c725..b5d47cf6912 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -276,6 +276,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index 6c028d4619a..be52ab42c9d 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -148,6 +148,7 @@ export const ClowningAroundEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index cb07bf06a81..0f784739777 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -102,6 +102,7 @@ export const DancingLessonsEncounter: MysteryEncounter = text: `${namespace}:intro`, } ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index fc8c8088d58..5ad6630386f 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -117,6 +117,7 @@ export const DarkDealEncounter: MysteryEncounter = .withSceneWaveRangeRequirement(30, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1]) .withScenePartySizeRequirement(2, 6, true) // Must have at least 2 pokemon in party .withCatchAllowed(true) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts index a11dc8cbe72..5686d0f6ce5 100644 --- a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts +++ b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts @@ -84,6 +84,7 @@ export const DelibirdyEncounter: MysteryEncounter = text: `${namespace}:intro`, } ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts index 1505768f968..10034d19263 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -51,6 +51,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = }, ]) .withAutoHideIntroVisuals(false) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index a75e5ef6a77..bf5fb28163b 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -52,6 +52,7 @@ export const FieldTripEncounter: MysteryEncounter = }, ]) .withAutoHideIntroVisuals(false) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 9e7652e24ea..d44e7bae596 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -122,6 +122,7 @@ export const FieryFalloutEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index a04521839fe..380662ca817 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -120,6 +120,7 @@ export const FightOrFlightEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts index 2b103e0a293..549faa01fa1 100644 --- a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts +++ b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts @@ -76,6 +76,7 @@ export const FunAndGamesEncounter: MysteryEncounter = text: `${namespace}:intro_dialogue`, }, ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts index 7b929ea5e7b..bafc1901e5e 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -96,6 +96,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = text: `${namespace}:intro`, } ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 6ca131543b4..8fd46982dc1 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -50,6 +50,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index 08536f44245..fb25976ebd8 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -125,6 +125,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 2b44f6ee33d..1eb1c4cb13e 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -61,6 +61,7 @@ export const MysteriousChestEncounter: MysteryEncounter = text: `${namespace}:intro`, } ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/part-timer-encounter.ts b/src/data/mystery-encounters/encounters/part-timer-encounter.ts index 2f41aa96677..17a3a366569 100644 --- a/src/data/mystery-encounters/encounters/part-timer-encounter.ts +++ b/src/data/mystery-encounters/encounters/part-timer-encounter.ts @@ -69,6 +69,7 @@ export const PartTimerEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index d029460e617..c6b04b7aca6 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -54,6 +54,7 @@ export const SafariZoneEncounter: MysteryEncounter = text: `${namespace}:intro`, }, ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts index 89cb572962c..c70048ade07 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -62,6 +62,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = speaker: `${namespace}:speaker`, }, ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index d0b4cc13301..3a4bf465a78 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -88,6 +88,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts index 63ab178c52a..01e241f63d4 100644 --- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts +++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts @@ -58,6 +58,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter = text: `${namespace}:intro`, } ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts index aca46f1598b..0ac82243862 100644 --- a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts @@ -196,6 +196,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) @@ -493,7 +494,7 @@ function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number) pulled: false, sourceType: EggSourceType.EVENT, eggDescriptor: eggDescription, - tier: EggTier.GREAT + tier: EggTier.RARE }); } } diff --git a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts index 2720653e654..95f359547e4 100644 --- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts @@ -53,6 +53,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = speaker: `${namespace}:speaker`, }, ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts index d1d5b484129..7ee57d36027 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -117,6 +117,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts index ad4f2dd8498..c7cb23fe6f8 100644 --- a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts @@ -94,6 +94,7 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index ff993f339cb..10bb956636b 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -52,6 +52,7 @@ export const TrainingSessionEncounter: MysteryEncounter = text: `${namespace}:intro`, } ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts index be2cd796386..c2a0426bceb 100644 --- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts +++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts @@ -54,6 +54,7 @@ export const TrashToTreasureEncounter: MysteryEncounter = text: `${namespace}:intro`, }, ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index 51c1d5f963f..13594f273d9 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -125,6 +125,7 @@ export const UncommonBreedEncounter: MysteryEncounter = scene.time.delayedCall(500, () => scene.playSound("battle_anims/PRSFX- Spotlight2")); return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index 17f33c27645..6e2f8352480 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -125,6 +125,7 @@ export const WeirdDreamEncounter: MysteryEncounter = text: `${namespace}:intro_dialogue`, }, ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index aabb0a3311a..7e175957e21 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -190,7 +190,7 @@ export default class MysteryEncounter implements IMysteryEncounter { secondaryPokemon?: PlayerPokemon[]; // #region Post-construct / Auto-populated params - + localizationKey: string; /** * Dialogue object containing all the dialogue, messages, tooltips, etc. for an encounter */ @@ -264,6 +264,7 @@ export default class MysteryEncounter implements IMysteryEncounter { Object.assign(this, encounter); } this.encounterTier = this.encounterTier ?? MysteryEncounterTier.COMMON; + this.localizationKey = this.localizationKey ?? ""; this.dialogue = this.dialogue ?? {}; this.spriteConfigs = this.spriteConfigs ? [ ...this.spriteConfigs ] : []; // Default max is 1 for ROGUE encounters, 2 for others @@ -528,6 +529,7 @@ export class MysteryEncounterBuilder implements Partial { options: [MysteryEncounterOption, MysteryEncounterOption, ...MysteryEncounterOption[]]; enemyPartyConfigs: EnemyPartyConfig[] = []; + localizationKey: string = ""; dialogue: MysteryEncounterDialogue = {}; requirements: EncounterSceneRequirement[] = []; primaryPokemonRequirements: EncounterPokemonRequirement[] = []; @@ -632,6 +634,16 @@ export class MysteryEncounterBuilder implements Partial { return this.withIntroSpriteConfigs(spriteConfigs).withIntroDialogue(dialogue); } + /** + * Sets the localization key used by the encounter + * @param localizationKey the string used as the key + * @returns `this` + */ + setLocalizationKey(localizationKey: string): this { + this.localizationKey = localizationKey; + return this; + } + /** * OPTIONAL */ diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index 43c849a78e0..2efae9ad359 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -54,7 +54,7 @@ export enum BattlerTagType { CURSED = "CURSED", CHARGED = "CHARGED", ROOSTED = "ROOSTED", - MAGNET_RISEN = "MAGNET_RISEN", + FLOATING = "FLOATING", MINIMIZED = "MINIMIZED", DESTINY_BOND = "DESTINY_BOND", CENTER_OF_ATTENTION = "CENTER_OF_ATTENTION", @@ -86,4 +86,5 @@ export enum BattlerTagType { IMPRISON = "IMPRISON", SYRUP_BOMB = "SYRUP_BOMB", ELECTRIFIED = "ELECTRIFIED", + TELEKINESIS = "TELEKINESIS" } diff --git a/src/enums/egg-type.ts b/src/enums/egg-type.ts index d8d0facb020..901e60b3c76 100644 --- a/src/enums/egg-type.ts +++ b/src/enums/egg-type.ts @@ -1,6 +1,6 @@ export enum EggTier { COMMON, - GREAT, - ULTRA, - MASTER + RARE, + EPIC, + LEGENDARY } diff --git a/src/enums/stat.ts b/src/enums/stat.ts index a12d53e8559..6b3f7dc6d79 100644 --- a/src/enums/stat.ts +++ b/src/enums/stat.ts @@ -50,7 +50,7 @@ export function getStatStageChangeDescriptionKey(stages: number, isIncrease: boo return isIncrease ? "battle:statRose" : "battle:statFell"; } else if (stages === 2) { return isIncrease ? "battle:statSharplyRose" : "battle:statHarshlyFell"; - } else if (stages <= 6) { + } else if (stages > 2 && stages <= 6) { return isIncrease ? "battle:statRoseDrastically" : "battle:statSeverelyFell"; } return isIncrease ? "battle:statWontGoAnyHigher" : "battle:statWontGoAnyLower"; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 241524df1b9..c495e0833cd 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -983,7 +983,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.applyModifier(PokemonIncrementingStatModifier, this.isPlayer(), this, s, statHolder); } - statHolder.value = Utils.clampInt(statHolder.value, 1, Number.MAX_SAFE_INTEGER); + statHolder.value = Phaser.Math.Clamp(statHolder.value, 1, Number.MAX_SAFE_INTEGER); this.setStat(s, statHolder.value); } @@ -1417,10 +1417,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns {boolean} Whether the ability is present and active */ hasAbility(ability: Abilities, canApply: boolean = true, ignoreOverride?: boolean): boolean { - if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).id === ability) { + if (this.getAbility(ignoreOverride).id === ability && (!canApply || this.canApplyAbility())) { return true; } - if (this.hasPassive() && (!canApply || this.canApplyAbility(true)) && this.getPassiveAbility().id === ability) { + if (this.getPassiveAbility().id === ability && this.hasPassive() && (!canApply || this.canApplyAbility(true))) { return true; } return false; @@ -1488,7 +1488,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } isGrounded(): boolean { - return !!this.getTag(GroundedTag) || (!this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE) && !this.getTag(BattlerTagType.MAGNET_RISEN) && !this.getTag(SemiInvulnerableTag)); + return !!this.getTag(GroundedTag) || (!this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE) && !this.getTag(BattlerTagType.FLOATING) && !this.getTag(SemiInvulnerableTag)); } /** @@ -2684,7 +2684,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ apply(source: Pokemon, move: Move): HitResult { const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; - if (move.category === MoveCategory.STATUS) { + const moveCategory = new Utils.NumberHolder(move.category); + applyMoveAttrs(VariableMoveCategoryAttr, source, this, move, moveCategory); + if (moveCategory.value === MoveCategory.STATUS) { const cancelled = new Utils.BooleanHolder(false); const typeMultiplier = this.getMoveEffectiveness(source, move, false, false, cancelled); diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index b658d3b5277..dd8c82357a7 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -3084,11 +3084,12 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier { * Steals an item from a set of target Pokemon. * This prioritizes high-tier held items when selecting the item to steal. * @param pokemon The {@linkcode Pokemon} holding this item + * @param target The {@linkcode Pokemon} to steal from (optional) * @param _args N/A * @returns `true` if an item was stolen; false otherwise. */ - override apply(pokemon: Pokemon, ..._args: unknown[]): boolean { - const opponents = this.getTargets(pokemon); + override apply(pokemon: Pokemon, target?: Pokemon, ..._args: unknown[]): boolean { + const opponents = this.getTargets(pokemon, target); if (!opponents.length) { return false; @@ -3187,7 +3188,7 @@ export class TurnHeldItemTransferModifier extends HeldItemTransferModifier { * @see {@linkcode HeldItemTransferModifier} */ export class ContactHeldItemTransferChanceModifier extends HeldItemTransferModifier { - private chance: number; + public readonly chance: number; constructor(type: ModifierType, pokemonId: number, chancePercent: number, stackCount?: number) { super(type, pokemonId, stackCount); @@ -3634,7 +3635,7 @@ export function overrideHeldItems(scene: BattleScene, pokemon: Pokemon, isPlayer } if (!isPlayer) { - scene.clearEnemyHeldItemModifiers(); + scene.clearEnemyHeldItemModifiers(pokemon); } heldItemsOverride.forEach(item => { diff --git a/src/phases/check-status-effect-phase.ts b/src/phases/check-status-effect-phase.ts new file mode 100644 index 00000000000..44918b54966 --- /dev/null +++ b/src/phases/check-status-effect-phase.ts @@ -0,0 +1,23 @@ +import { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase"; +import { Phase } from "#app/phase"; +import { BattlerIndex } from "#app/battle"; +import BattleScene from "#app/battle-scene"; + +export class CheckStatusEffectPhase extends Phase { + private order : BattlerIndex[]; + constructor(scene : BattleScene, order : BattlerIndex[]) { + super(scene); + this.scene = scene; + this.order = order; + } + + start() { + const field = this.scene.getField(); + for (const o of this.order) { + if (field[o].status && field[o].status.isPostTurn()) { + this.scene.unshiftPhase(new PostTurnStatusEffectPhase(this.scene, o)); + } + } + this.end(); + } +} diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index b2d429a4313..581cd5ff017 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -4,7 +4,7 @@ import { applyPreAttackAbAttrs, AddSecondStrikeAbAttr, IgnoreMoveEffectsAbAttr, import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag"; import { MoveAnim } from "#app/data/battle-anims"; import { BattlerTagLapseType, DamageProtectedTag, ProtectedTag, SemiInvulnerableTag, SubstituteTag } from "#app/data/battler-tags"; -import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, MoveEffectTrigger, ChargeAttr, MoveCategory, NoEffectAttr, HitsTagAttr, ToxicAccuracyAttr } from "#app/data/move"; +import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, OneHitKOAttr, MoveEffectTrigger, ChargeAttr, MoveCategory, NoEffectAttr, HitsTagAttr, ToxicAccuracyAttr } from "#app/data/move"; import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { Moves } from "#app/enums/moves"; @@ -404,6 +404,10 @@ export class MoveEffectPhase extends PokemonPhase { return true; } + if (target.getTag(BattlerTagType.TELEKINESIS) && !target.getTag(SemiInvulnerableTag) && !this.move.getMove().hasAttr(OneHitKOAttr)) { + return true; + } + const semiInvulnerableTag = target.getTag(SemiInvulnerableTag); if (semiInvulnerableTag && !this.move.getMove().getAttrs(HitsTagAttr).some(hta => hta.tagType === semiInvulnerableTag.tagType) diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 10cc062ea3b..94093188571 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -8,10 +8,6 @@ import { SpeciesFormChangePreMoveTrigger } from "#app/data/pokemon-forms"; import { getStatusEffectActivationText, getStatusEffectHealText } from "#app/data/status-effect"; import { Type } from "#app/data/type"; import { getTerrainBlockMessage } from "#app/data/weather"; -import { Abilities } from "#app/enums/abilities"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { Moves } from "#app/enums/moves"; -import { StatusEffect } from "#app/enums/status-effect"; import { MoveUsedEvent } from "#app/events/battle-scene"; import Pokemon, { MoveResult, PokemonMove, TurnMove } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; @@ -20,7 +16,11 @@ import { CommonAnimPhase } from "#app/phases/common-anim-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; -import * as Utils from "#app/utils"; +import { BooleanHolder, NumberHolder } from "#app/utils"; +import { Abilities } from "#enums/abilities"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { Moves } from "#enums/moves"; +import { StatusEffect } from "#enums/status-effect"; import i18next from "i18next"; export class MovePhase extends BattlePhase { @@ -89,7 +89,7 @@ export class MovePhase extends BattlePhase { this.cancelled = true; } - public start() { + public start(): void { super.start(); console.log(Moves[this.move.moveId]); @@ -140,7 +140,7 @@ export class MovePhase extends BattlePhase { } /** Check for cancellation edge cases - no targets remaining, or {@linkcode Moves.NONE} is in the queue */ - protected resolveFinalPreMoveCancellationChecks() { + protected resolveFinalPreMoveCancellationChecks(): void { const targets = this.getActiveTargetPokemon(); const moveQueue = this.pokemon.getMoveQueue(); @@ -150,14 +150,14 @@ export class MovePhase extends BattlePhase { } } - public getActiveTargetPokemon() { + public getActiveTargetPokemon(): Pokemon[] { return this.scene.getField(true).filter(p => this.targets.includes(p.getBattlerIndex())); } /** * Handles {@link StatusEffect.SLEEP Sleep}/{@link StatusEffect.PARALYSIS Paralysis}/{@link StatusEffect.FREEZE Freeze} rolls and side effects. */ - protected resolvePreMoveStatusEffects() { + protected resolvePreMoveStatusEffects(): void { if (!this.followUp && this.pokemon.status && !this.pokemon.status.isPostTurn()) { this.pokemon.status.incrementTurn(); let activated = false; @@ -198,7 +198,7 @@ export class MovePhase extends BattlePhase { * Lapse {@linkcode BattlerTagLapseType.PRE_MOVE PRE_MOVE} tags that trigger before a move is used, regardless of whether or not it failed. * Also lapse {@linkcode BattlerTagLapseType.MOVE MOVE} tags if the move should be successful. */ - protected lapsePreMoveAndMoveTags() { + protected lapsePreMoveAndMoveTags(): void { this.pokemon.lapseTags(BattlerTagLapseType.PRE_MOVE); // TODO: does this intentionally happen before the no targets/Moves.NONE on queue cancellation case is checked? @@ -207,7 +207,7 @@ export class MovePhase extends BattlePhase { } } - protected useMove() { + protected useMove(): void { const targets = this.getActiveTargetPokemon(); const moveQueue = this.pokemon.getMoveQueue(); @@ -217,7 +217,8 @@ export class MovePhase extends BattlePhase { this.showMoveText(); // TODO: Clean up implementation of two-turn moves. - if (moveQueue.length > 0) { // Using .shift here clears out two turn moves once they've been used + if (moveQueue.length > 0) { + // Using .shift here clears out two turn moves once they've been used this.ignorePp = moveQueue.shift()?.ignorePP ?? false; } @@ -226,7 +227,7 @@ export class MovePhase extends BattlePhase { const ppUsed = 1 + this.getPpIncreaseFromPressure(targets); this.move.usePp(ppUsed); - this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed)); + this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed)); } // Update the battle's "last move" pointer, unless we're currently mimicking a move. @@ -275,7 +276,7 @@ export class MovePhase extends BattlePhase { this.pokemon.pushMoveHistory({ move: this.move.moveId, targets: this.targets, result: MoveResult.FAIL, virtual: this.move.virtual }); let failedText: string | undefined; - const failureMessage = move.getFailedText(this.pokemon, targets[0], move, new Utils.BooleanHolder(false)); + const failureMessage = move.getFailedText(this.pokemon, targets[0], move, new BooleanHolder(false)); if (failureMessage) { failedText = failureMessage; @@ -299,7 +300,7 @@ export class MovePhase extends BattlePhase { * Queues a {@linkcode MoveEndPhase} if the move wasn't a {@linkcode followUp} and {@linkcode canMove()} returns `true`, * then ends the phase. */ - public end() { + public end(): void { if (!this.followUp && this.canMove()) { this.scene.unshiftPhase(new MoveEndPhase(this.scene, this.pokemon.getBattlerIndex())); } @@ -313,7 +314,7 @@ export class MovePhase extends BattlePhase { * * TODO: This hardcodes the PP increase at 1 per opponent, rather than deferring to the ability. */ - public getPpIncreaseFromPressure(targets: Pokemon[]) { + public getPpIncreaseFromPressure(targets: Pokemon[]): number { const foesWithPressure = this.pokemon.getOpponents().filter(o => targets.includes(o) && o.isActive(true) && o.hasAbilityWithAttr(IncreasePpAbAttr)); return foesWithPressure.length; } @@ -323,10 +324,10 @@ export class MovePhase extends BattlePhase { * - Move redirection abilities, effects, etc. * - Counterattacks, which pass a special value into the `targets` constructor param (`[`{@linkcode BattlerIndex.ATTACKER}`]`). */ - protected resolveRedirectTarget() { + protected resolveRedirectTarget(): void { if (this.targets.length === 1) { const currentTarget = this.targets[0]; - const redirectTarget = new Utils.NumberHolder(currentTarget); + const redirectTarget = new NumberHolder(currentTarget); // check move redirection abilities of every pokemon *except* the user. this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, false, this.move.moveId, redirectTarget)); @@ -372,7 +373,7 @@ export class MovePhase extends BattlePhase { * If there is no last attacker, or they are no longer on the field, a message is displayed and the * move is marked for failure. */ - protected resolveCounterAttackTarget() { + protected resolveCounterAttackTarget(): void { if (this.targets.length === 1 && this.targets[0] === BattlerIndex.ATTACKER) { if (this.pokemon.turnData.attacksReceived.length) { this.targets[0] = this.pokemon.turnData.attacksReceived[0].sourceBattlerIndex; @@ -411,7 +412,7 @@ export class MovePhase extends BattlePhase { * * TODO: handle charge moves more gracefully */ - protected handlePreMoveFailures() { + protected handlePreMoveFailures(): void { if (this.cancelled || this.failed) { if (this.failed) { const ppUsed = this.ignorePp ? 0 : 1; diff --git a/src/phases/obtain-status-effect-phase.ts b/src/phases/obtain-status-effect-phase.ts index bf38c432394..c396fa7ba59 100644 --- a/src/phases/obtain-status-effect-phase.ts +++ b/src/phases/obtain-status-effect-phase.ts @@ -6,7 +6,6 @@ import { StatusEffect } from "#app/enums/status-effect"; import Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { PokemonPhase } from "./pokemon-phase"; -import { PostTurnStatusEffectPhase } from "./post-turn-status-effect-phase"; export class ObtainStatusEffectPhase extends PokemonPhase { private statusEffect?: StatusEffect | undefined; @@ -33,9 +32,6 @@ export class ObtainStatusEffectPhase extends PokemonPhase { pokemon.updateInfo(true); new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect! - 1), pokemon).play(this.scene, false, () => { this.scene.queueMessage(getStatusEffectObtainText(this.statusEffect, getPokemonNameWithAffix(pokemon), this.sourceText ?? undefined)); - if (pokemon.status?.isPostTurn()) { - this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, this.battlerIndex)); - } this.end(); }); return; diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index 115e4f640a2..58683cf8ec8 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -196,7 +196,7 @@ export class TitlePhase extends Phase { this.scene.gameMode = getGameMode(GameModes.DAILY); this.scene.setSeed(seed); - this.scene.resetSeed(1); + this.scene.resetSeed(0); this.scene.money = this.scene.gameMode.getStartingMoney(); diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index 95d55986185..627cee4b06a 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -13,10 +13,10 @@ import { BerryPhase } from "./berry-phase"; import { FieldPhase } from "./field-phase"; import { MoveHeaderPhase } from "./move-header-phase"; import { MovePhase } from "./move-phase"; -import { PostTurnStatusEffectPhase } from "./post-turn-status-effect-phase"; import { SwitchSummonPhase } from "./switch-summon-phase"; import { TurnEndPhase } from "./turn-end-phase"; import { WeatherEffectPhase } from "./weather-effect-phase"; +import { CheckStatusEffectPhase } from "#app/phases/check-status-effect-phase"; import { BattlerIndex } from "#app/battle"; import { TrickRoomTag } from "#app/data/arena-tag"; import { SwitchType } from "#enums/switch-type"; @@ -158,7 +158,7 @@ export class TurnStartPhase extends FieldPhase { if (!queuedMove) { continue; } - const move = pokemon.getMoveset().find(m => m?.moveId === queuedMove.move) || new PokemonMove(queuedMove.move); + const move = pokemon.getMoveset().find(m => m?.moveId === queuedMove.move && m?.ppUsed < m?.getMovePp()) || new PokemonMove(queuedMove.move); if (move.getMove().hasAttr(MoveHeaderAttr)) { this.scene.unshiftPhase(new MoveHeaderPhase(this.scene, pokemon, move)); } @@ -206,11 +206,8 @@ export class TurnStartPhase extends FieldPhase { this.scene.pushPhase(new WeatherEffectPhase(this.scene)); - for (const o of moveOrder) { - if (field[o].status && field[o].status.isPostTurn()) { - this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, o)); - } - } + /** Add a new phase to check who should be taking status damage */ + this.scene.pushPhase(new CheckStatusEffectPhase(this.scene, moveOrder)); this.scene.pushPhase(new BerryPhase(this.scene)); this.scene.pushPhase(new TurnEndPhase(this.scene)); diff --git a/src/system/arena-data.ts b/src/system/arena-data.ts index 5b907805372..ba37de0ed0e 100644 --- a/src/system/arena-data.ts +++ b/src/system/arena-data.ts @@ -1,5 +1,5 @@ import { Arena } from "../field/arena"; -import { ArenaTag } from "../data/arena-tag"; +import { ArenaTag, loadArenaTag } from "../data/arena-tag"; import { Biome } from "#enums/biome"; import { Weather } from "../data/weather"; import { Terrain } from "#app/data/terrain"; @@ -15,6 +15,10 @@ export default class ArenaData { this.biome = sourceArena ? sourceArena.biomeType : source.biome; this.weather = sourceArena ? sourceArena.weather : source.weather ? new Weather(source.weather.weatherType, source.weather.turnsLeft) : null; this.terrain = sourceArena ? sourceArena.terrain : source.terrain ? new Terrain(source.terrain.terrainType, source.terrain.turnsLeft) : null; - this.tags = sourceArena ? sourceArena.tags : []; + this.tags = []; + + if (source.tags) { + this.tags = source.tags.map(t => loadArenaTag(t)); + } } } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 0d2f35ae728..b162962fac6 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -31,7 +31,7 @@ import { TrainerVariant } from "#app/field/trainer"; import { Variant } from "#app/data/variant"; import { setSettingGamepad, SettingGamepad, settingGamepadDefaults } from "#app/system/settings/settings-gamepad"; import { setSettingKeyboard, SettingKeyboard } from "#app/system/settings/settings-keyboard"; -import { TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; +import { TagAddedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; import * as Modifier from "#app/modifier/modifier"; import { StatusEffect } from "#app/data/status-effect"; import ChallengeData from "#app/system/challenge-data"; @@ -50,6 +50,7 @@ import { applySessionDataPatches, applySettingsDataPatches, applySystemDataPatch import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PokerogueApiClearSessionData } from "#app/@types/pokerogue-api"; +import { ArenaTrapTag } from "#app/data/arena-tag"; export const defaultStarterSpecies: Species[] = [ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, @@ -1085,8 +1086,18 @@ export class GameData { scene.arena.terrain = sessionData.arena.terrain; scene.arena.eventTarget.dispatchEvent(new TerrainChangedEvent(TerrainType.NONE, scene.arena.terrain?.terrainType!, scene.arena.terrain?.turnsLeft!)); // TODO: is this bang correct? - // TODO - //scene.arena.tags = sessionData.arena.tags; + + scene.arena.tags = sessionData.arena.tags; + if (scene.arena.tags) { + for (const tag of scene.arena.tags) { + if (tag instanceof ArenaTrapTag) { + const { tagType, side, turnCount, layers, maxLayers } = tag as ArenaTrapTag; + scene.arena.eventTarget.dispatchEvent(new TagAddedEvent(tagType, side, turnCount, layers, maxLayers)); + } else { + scene.arena.eventTarget.dispatchEvent(new TagAddedEvent(tag.tagType, tag.side, tag.turnCount)); + } + } + } for (const modifierData of sessionData.modifiers) { const modifier = modifierData.toModifier(scene, Modifier[modifierData.className]); diff --git a/src/test/abilities/ability_timing.test.ts b/src/test/abilities/ability_timing.test.ts index 1472f9eb429..e3264c2c1a8 100644 --- a/src/test/abilities/ability_timing.test.ts +++ b/src/test/abilities/ability_timing.test.ts @@ -1,13 +1,13 @@ import { BattleStyle } from "#app/enums/battle-style"; import { CommandPhase } from "#app/phases/command-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase"; -import i18next, { initI18n } from "#app/plugins/i18n"; +import i18next from "#app/plugins/i18n"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Ability Timing", () => { @@ -32,11 +32,10 @@ describe("Ability Timing", () => { .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.INTIMIDATE) .ability(Abilities.BALL_FETCH); + vi.spyOn(i18next, "t"); }); it("should trigger after switch check", async () => { - initI18n(); - i18next.changeLanguage("en"); game.settings.battleStyle = BattleStyle.SWITCH; await game.classicMode.runToSummon([ Species.EEVEE, Species.FEEBAS ]); @@ -46,7 +45,6 @@ describe("Ability Timing", () => { }, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)); await game.phaseInterceptor.to("MessagePhase"); - const message = game.textInterceptor.getLatestMessage(); - expect(message).toContain("battle:statFell"); + expect(i18next.t).toHaveBeenCalledWith("battle:statFell", expect.objectContaining({ count: 1 })); }, 5000); }); diff --git a/src/test/abilities/battle_bond.test.ts b/src/test/abilities/battle_bond.test.ts index c7dffeb150a..283fb0d0f14 100644 --- a/src/test/abilities/battle_bond.test.ts +++ b/src/test/abilities/battle_bond.test.ts @@ -1,17 +1,19 @@ +import { allMoves, MultiHitAttr, MultiHitType } from "#app/data/move"; import { Status, StatusEffect } from "#app/data/status-effect"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Abilities - BATTLE BOND", () => { let phaserGame: Phaser.Game; let game: GameManager; + const baseForm = 1; + const ashForm = 2; + beforeAll(() => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, @@ -24,40 +26,68 @@ describe("Abilities - BATTLE BOND", () => { beforeEach(() => { game = new GameManager(phaserGame); - const moveToUse = Moves.SPLASH; - game.override.battleType("single"); - game.override.ability(Abilities.BATTLE_BOND); - game.override.moveset([ moveToUse ]); - game.override.enemyMoveset([ Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE ]); + game.override.battleType("single") + .startingWave(4) // Leads to arena reset on Wave 5 trainer battle + .ability(Abilities.BATTLE_BOND) + .starterForms({ [Species.GRENINJA]: ashForm, }) + .moveset([ Moves.SPLASH, Moves.WATER_SHURIKEN ]) + .enemySpecies(Species.BULBASAUR) + .enemyMoveset(Moves.SPLASH) + .startingLevel(100) // Avoid levelling up + .enemyLevel(1000); // Avoid opponent dying before `doKillOpponents()` }); - test( - "check if fainted pokemon switches to base form on arena reset", - async () => { - const baseForm = 1; - const ashForm = 2; - game.override.startingWave(4); - game.override.starterForms({ - [Species.GRENINJA]: ashForm, - }); + it("check if fainted pokemon switches to base form on arena reset", async () => { + await game.classicMode.startBattle([ Species.MAGIKARP, Species.GRENINJA ]); - await game.startBattle([ Species.MAGIKARP, Species.GRENINJA ]); + const greninja = game.scene.getParty()[1]; + expect(greninja.formIndex).toBe(ashForm); - const greninja = game.scene.getParty().find((p) => p.species.speciesId === Species.GRENINJA); - expect(greninja).toBeDefined(); - expect(greninja!.formIndex).toBe(ashForm); + greninja.hp = 0; + greninja.status = new Status(StatusEffect.FAINT); + expect(greninja.isFainted()).toBe(true); - greninja!.hp = 0; - greninja!.status = new Status(StatusEffect.FAINT); - expect(greninja!.isFainted()).toBe(true); + game.move.select(Moves.SPLASH); + await game.doKillOpponents(); + await game.phaseInterceptor.to("TurnEndPhase"); + game.doSelectModifier(); + await game.phaseInterceptor.to("QuietFormChangePhase"); - game.move.select(Moves.SPLASH); - await game.doKillOpponents(); - await game.phaseInterceptor.to(TurnEndPhase); - game.doSelectModifier(); - await game.phaseInterceptor.to(QuietFormChangePhase); + expect(greninja.formIndex).toBe(baseForm); + }); - expect(greninja!.formIndex).toBe(baseForm); - }, - ); + it("should not keep buffing Water Shuriken after Greninja switches to base form", async () => { + await game.classicMode.startBattle([ Species.GRENINJA ]); + + const waterShuriken = allMoves[Moves.WATER_SHURIKEN]; + vi.spyOn(waterShuriken, "calculateBattlePower"); + + let actualMultiHitType: MultiHitType | null = null; + const multiHitAttr = waterShuriken.getAttrs(MultiHitAttr)[0]; + vi.spyOn(multiHitAttr, "getHitCount").mockImplementation(() => { + actualMultiHitType = multiHitAttr.getMultiHitType(); + return 3; + }); + + // Wave 4: Use Water Shuriken in Ash form + let expectedBattlePower = 20; + let expectedMultiHitType = MultiHitType._3; + + game.move.select(Moves.WATER_SHURIKEN); + await game.phaseInterceptor.to("BerryPhase", false); + expect(waterShuriken.calculateBattlePower).toHaveLastReturnedWith(expectedBattlePower); + expect(actualMultiHitType).toBe(expectedMultiHitType); + + await game.doKillOpponents(); + await game.toNextWave(); + + // Wave 5: Use Water Shuriken in base form + expectedBattlePower = 15; + expectedMultiHitType = MultiHitType._2_TO_5; + + game.move.select(Moves.WATER_SHURIKEN); + await game.phaseInterceptor.to("BerryPhase", false); + expect(waterShuriken.calculateBattlePower).toHaveLastReturnedWith(expectedBattlePower); + expect(actualMultiHitType).toBe(expectedMultiHitType); + }); }); diff --git a/src/test/abilities/disguise.test.ts b/src/test/abilities/disguise.test.ts index a295dd61443..0241aa4b9ea 100644 --- a/src/test/abilities/disguise.test.ts +++ b/src/test/abilities/disguise.test.ts @@ -1,8 +1,9 @@ +import { BattlerIndex } from "#app/battle"; +import { StatusEffect } from "#app/data/status-effect"; import { toDmgValue } from "#app/utils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { StatusEffect } from "#app/data/status-effect"; import { Stat } from "#enums/stat"; import GameManager from "#test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -222,4 +223,17 @@ describe("Abilities - Disguise", () => { expect(mimikyu.formIndex).toBe(bustedForm); expect(mimikyu.hp).toBe(maxHp - disguiseDamage); }); + + it("doesn't trigger if user is behind a substitute", async () => { + game.override + .enemyMoveset(Moves.SUBSTITUTE) + .moveset(Moves.POWER_TRIP); + await game.classicMode.startBattle(); + + game.move.select(Moves.POWER_TRIP); + await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); + await game.toNextTurn(); + + expect(game.scene.getEnemyPokemon()!.formIndex).toBe(disguisedForm); + }); }); diff --git a/src/test/abilities/gulp_missile.test.ts b/src/test/abilities/gulp_missile.test.ts index 1ca208996b5..01b68d0c89d 100644 --- a/src/test/abilities/gulp_missile.test.ts +++ b/src/test/abilities/gulp_missile.test.ts @@ -1,13 +1,14 @@ -import { BattlerTagType } from "#enums/battler-tag-type"; -import { StatusEffect } from "#enums/status-effect"; +import { BattlerIndex } from "#app/battle"; import Pokemon from "#app/field/pokemon"; -import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import { Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { Stat } from "#enums/stat"; describe("Abilities - Gulp Missile", () => { let phaserGame: Phaser.Game; @@ -40,8 +41,9 @@ describe("Abilities - Gulp Missile", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override + .disableCrits() .battleType("single") - .moveset([ Moves.SURF, Moves.DIVE, Moves.SPLASH ]) + .moveset([ Moves.SURF, Moves.DIVE, Moves.SPLASH, Moves.SUBSTITUTE ]) .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) @@ -234,6 +236,25 @@ describe("Abilities - Gulp Missile", () => { expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.DEF)).toBe(-1); }); + it("doesn't trigger if user is behind a substitute", async () => { + game.override + .enemyAbility(Abilities.STURDY) + .enemyMoveset([ Moves.SPLASH, Moves.POWER_TRIP ]); + await game.classicMode.startBattle([ Species.CRAMORANT ]); + + game.move.select(Moves.SURF); + await game.forceEnemyMove(Moves.SPLASH); + await game.toNextTurn(); + + expect(game.scene.getPlayerPokemon()!.formIndex).toBe(GULPING_FORM); + + game.move.select(Moves.SUBSTITUTE); + await game.forceEnemyMove(Moves.POWER_TRIP); + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await game.toNextTurn(); + + expect(game.scene.getPlayerPokemon()!.formIndex).toBe(GULPING_FORM); + }); it("cannot be suppressed", async () => { game.override.enemyMoveset(Moves.GASTRO_ACID); diff --git a/src/test/abilities/ice_face.test.ts b/src/test/abilities/ice_face.test.ts index 723d5e8d855..1c7f7bd6093 100644 --- a/src/test/abilities/ice_face.test.ts +++ b/src/test/abilities/ice_face.test.ts @@ -1,3 +1,4 @@ +import { BattlerIndex } from "#app/battle"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; @@ -36,7 +37,7 @@ describe("Abilities - Ice Face", () => { }); it("takes no damage from physical move and transforms to Noice", async () => { - await game.startBattle([ Species.HITMONLEE ]); + await game.classicMode.startBattle([ Species.HITMONLEE ]); game.move.select(Moves.TACKLE); @@ -52,7 +53,7 @@ describe("Abilities - Ice Face", () => { it("takes no damage from the first hit of multihit physical move and transforms to Noice", async () => { game.override.moveset([ Moves.SURGING_STRIKES ]); game.override.enemyLevel(1); - await game.startBattle([ Species.HITMONLEE ]); + await game.classicMode.startBattle([ Species.HITMONLEE ]); game.move.select(Moves.SURGING_STRIKES); @@ -78,7 +79,7 @@ describe("Abilities - Ice Face", () => { }); it("takes damage from special moves", async () => { - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); game.move.select(Moves.ICE_BEAM); @@ -92,7 +93,7 @@ describe("Abilities - Ice Face", () => { }); it("takes effects from status moves", async () => { - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); game.move.select(Moves.TOXIC_THREAD); @@ -108,7 +109,7 @@ describe("Abilities - Ice Face", () => { game.override.moveset([ Moves.QUICK_ATTACK ]); game.override.enemyMoveset([ Moves.HAIL, Moves.HAIL, Moves.HAIL, Moves.HAIL ]); - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); game.move.select(Moves.QUICK_ATTACK); @@ -130,7 +131,7 @@ describe("Abilities - Ice Face", () => { game.override.enemyMoveset([ Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE ]); game.override.moveset([ Moves.SNOWSCAPE ]); - await game.startBattle([ Species.EISCUE, Species.NINJASK ]); + await game.classicMode.startBattle([ Species.EISCUE, Species.NINJASK ]); game.move.select(Moves.SNOWSCAPE); @@ -157,7 +158,7 @@ describe("Abilities - Ice Face", () => { game.override.enemySpecies(Species.SHUCKLE); game.override.enemyMoveset([ Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE ]); - await game.startBattle([ Species.EISCUE ]); + await game.classicMode.startBattle([ Species.EISCUE ]); game.move.select(Moves.HAIL); const eiscue = game.scene.getPlayerPokemon()!; @@ -176,7 +177,7 @@ describe("Abilities - Ice Face", () => { it("persists form change when switched out", async () => { game.override.enemyMoveset([ Moves.QUICK_ATTACK, Moves.QUICK_ATTACK, Moves.QUICK_ATTACK, Moves.QUICK_ATTACK ]); - await game.startBattle([ Species.EISCUE, Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.EISCUE, Species.MAGIKARP ]); game.move.select(Moves.ICE_BEAM); @@ -205,7 +206,7 @@ describe("Abilities - Ice Face", () => { [Species.EISCUE]: noiceForm, }); - await game.startBattle([ Species.EISCUE ]); + await game.classicMode.startBattle([ Species.EISCUE ]); const eiscue = game.scene.getPlayerPokemon()!; @@ -222,10 +223,23 @@ describe("Abilities - Ice Face", () => { expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); }); + it("doesn't trigger if user is behind a substitute", async () => { + game.override + .enemyMoveset(Moves.SUBSTITUTE) + .moveset(Moves.POWER_TRIP); + await game.classicMode.startBattle(); + + game.move.select(Moves.POWER_TRIP); + await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); + await game.toNextTurn(); + + expect(game.scene.getEnemyPokemon()!.formIndex).toBe(icefaceForm); + }); + it("cannot be suppressed", async () => { game.override.moveset([ Moves.GASTRO_ACID ]); - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); game.move.select(Moves.GASTRO_ACID); @@ -241,7 +255,7 @@ describe("Abilities - Ice Face", () => { it("cannot be swapped with another ability", async () => { game.override.moveset([ Moves.SKILL_SWAP ]); - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); game.move.select(Moves.SKILL_SWAP); @@ -257,7 +271,7 @@ describe("Abilities - Ice Face", () => { it("cannot be copied", async () => { game.override.ability(Abilities.TRACE); - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); game.move.select(Moves.SIMPLE_BEAM); diff --git a/src/test/abilities/sheer_force.test.ts b/src/test/abilities/sheer_force.test.ts index a3add0a9964..a2600476d6d 100644 --- a/src/test/abilities/sheer_force.test.ts +++ b/src/test/abilities/sheer_force.test.ts @@ -9,6 +9,7 @@ import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { allMoves } from "#app/data/move"; describe("Abilities - Sheer Force", () => { @@ -174,5 +175,31 @@ describe("Abilities - Sheer Force", () => { }, 20000); + it("Two Pokemon with abilities disabled by Sheer Force hitting each other should not cause a crash", async () => { + const moveToUse = Moves.CRUNCH; + game.override.enemyAbility(Abilities.COLOR_CHANGE) + .ability(Abilities.COLOR_CHANGE) + .moveset(moveToUse) + .enemyMoveset(moveToUse); + + await game.classicMode.startBattle([ + Species.PIDGEOT + ]); + + const pidgeot = game.scene.getParty()[0]; + const onix = game.scene.getEnemyParty()[0]; + + pidgeot.stats[Stat.DEF] = 10000; + onix.stats[Stat.DEF] = 10000; + + game.move.select(moveToUse); + await game.toNextTurn(); + + // Check that both Pokemon's Color Change activated + const expectedTypes = [ allMoves[moveToUse].type ]; + expect(pidgeot.getTypes()).toStrictEqual(expectedTypes); + expect(onix.getTypes()).toStrictEqual(expectedTypes); + }); + //TODO King's Rock Interaction Unit Test }); diff --git a/src/test/eggs/egg.test.ts b/src/test/eggs/egg.test.ts index cf53cca5af8..6f57af63e6b 100644 --- a/src/test/eggs/egg.test.ts +++ b/src/test/eggs/egg.test.ts @@ -55,7 +55,7 @@ describe("Egg Generation Tests", () => { let gachaSpeciesCount = 0; for (let i = 0; i < EGG_HATCH_COUNT; i++) { - const result = new Egg({ scene, timestamp, sourceType: EggSourceType.GACHA_LEGENDARY, tier: EggTier.MASTER }).generatePlayerPokemon(scene).species.speciesId; + const result = new Egg({ scene, timestamp, sourceType: EggSourceType.GACHA_LEGENDARY, tier: EggTier.LEGENDARY }).generatePlayerPokemon(scene).species.speciesId; if (result === expectedSpecies) { gachaSpeciesCount++; } @@ -82,7 +82,7 @@ describe("Egg Generation Tests", () => { }); it("should return an rare tier egg", () => { const scene = game.scene; - const expectedTier = EggTier.GREAT; + const expectedTier = EggTier.RARE; const result = new Egg({ scene, tier: expectedTier }).tier; @@ -90,7 +90,7 @@ describe("Egg Generation Tests", () => { }); it("should return an epic tier egg", () => { const scene = game.scene; - const expectedTier = EggTier.ULTRA; + const expectedTier = EggTier.EPIC; const result = new Egg({ scene, tier: expectedTier }).tier; @@ -98,7 +98,7 @@ describe("Egg Generation Tests", () => { }); it("should return an legendary tier egg", () => { const scene = game.scene; - const expectedTier = EggTier.MASTER; + const expectedTier = EggTier.LEGENDARY; const result = new Egg({ scene, tier: expectedTier }).tier; @@ -200,7 +200,7 @@ describe("Egg Generation Tests", () => { const scene = game.scene; const expectedEggTier = EggTier.COMMON; - const result = new Egg({ scene, tier: EggTier.MASTER, species: Species.BULBASAUR }).tier; + const result = new Egg({ scene, tier: EggTier.LEGENDARY, species: Species.BULBASAUR }).tier; expect(result).toBe(expectedEggTier); }); @@ -208,7 +208,7 @@ describe("Egg Generation Tests", () => { const scene = game.scene; const expectedHatchWaves = 10; - const result = new Egg({ scene, tier: EggTier.MASTER, species: Species.BULBASAUR }).hatchWaves; + const result = new Egg({ scene, tier: EggTier.LEGENDARY, species: Species.BULBASAUR }).hatchWaves; expect(result).toBe(expectedHatchWaves); }); @@ -229,7 +229,7 @@ describe("Egg Generation Tests", () => { const result = new EggData(legacyEgg).toEgg(); - expect(result.tier).toBe(EggTier.GREAT); + expect(result.tier).toBe(EggTier.RARE); expect(result.id).toBe(legacyEgg.id); expect(result.timestamp).toBe(legacyEgg.timestamp); expect(result.hatchWaves).toBe(legacyEgg.hatchWaves); @@ -241,9 +241,9 @@ describe("Egg Generation Tests", () => { new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true, tier: EggTier.COMMON }); - expect(scene.gameData.eggPity[EggTier.GREAT]).toBe(startPityValues[EggTier.GREAT] + 1); - expect(scene.gameData.eggPity[EggTier.ULTRA]).toBe(startPityValues[EggTier.ULTRA] + 1); - expect(scene.gameData.eggPity[EggTier.MASTER]).toBe(startPityValues[EggTier.MASTER] + 1); + expect(scene.gameData.eggPity[EggTier.RARE]).toBe(startPityValues[EggTier.RARE] + 1); + expect(scene.gameData.eggPity[EggTier.EPIC]).toBe(startPityValues[EggTier.EPIC] + 1); + expect(scene.gameData.eggPity[EggTier.LEGENDARY]).toBe(startPityValues[EggTier.LEGENDARY] + 1); }); it("should increase legendary egg pity by two", () => { const scene = game.scene; @@ -251,9 +251,9 @@ describe("Egg Generation Tests", () => { new Egg({ scene, sourceType: EggSourceType.GACHA_LEGENDARY, pulled: true, tier: EggTier.COMMON }); - expect(scene.gameData.eggPity[EggTier.GREAT]).toBe(startPityValues[EggTier.GREAT] + 1); - expect(scene.gameData.eggPity[EggTier.ULTRA]).toBe(startPityValues[EggTier.ULTRA] + 1); - expect(scene.gameData.eggPity[EggTier.MASTER]).toBe(startPityValues[EggTier.MASTER] + 2); + expect(scene.gameData.eggPity[EggTier.RARE]).toBe(startPityValues[EggTier.RARE] + 1); + expect(scene.gameData.eggPity[EggTier.EPIC]).toBe(startPityValues[EggTier.EPIC] + 1); + expect(scene.gameData.eggPity[EggTier.LEGENDARY]).toBe(startPityValues[EggTier.LEGENDARY] + 2); }); it("should not increase manaphy egg count if bulbasaurs are pulled", () => { const scene = game.scene; @@ -277,7 +277,7 @@ describe("Egg Generation Tests", () => { const scene = game.scene; const startingRareEggsPulled = scene.gameData.gameStats.rareEggsPulled; - new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true, tier: EggTier.GREAT }); + new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true, tier: EggTier.RARE }); expect(scene.gameData.gameStats.rareEggsPulled).toBe(startingRareEggsPulled + 1); }); @@ -285,7 +285,7 @@ describe("Egg Generation Tests", () => { const scene = game.scene; const startingEpicEggsPulled = scene.gameData.gameStats.epicEggsPulled; - new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true, tier: EggTier.ULTRA }); + new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true, tier: EggTier.EPIC }); expect(scene.gameData.gameStats.epicEggsPulled).toBe(startingEpicEggsPulled + 1); }); @@ -293,7 +293,7 @@ describe("Egg Generation Tests", () => { const scene = game.scene; const startingLegendaryEggsPulled = scene.gameData.gameStats.legendaryEggsPulled; - new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true, tier: EggTier.MASTER }); + new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true, tier: EggTier.LEGENDARY }); expect(scene.gameData.gameStats.legendaryEggsPulled).toBe(startingLegendaryEggsPulled + 1); }); @@ -301,8 +301,8 @@ describe("Egg Generation Tests", () => { vi.spyOn(Utils, "randInt").mockReturnValue(1); const scene = game.scene; - const expectedTier1 = EggTier.MASTER; - const expectedTier2 = EggTier.ULTRA; + const expectedTier1 = EggTier.LEGENDARY; + const expectedTier2 = EggTier.EPIC; const result1 = new Egg({ scene, sourceType: EggSourceType.GACHA_LEGENDARY, pulled: true }).tier; const result2 = new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true }).tier; diff --git a/src/test/items/grip_claw.test.ts b/src/test/items/grip_claw.test.ts index 9d44a9e4672..2909549af87 100644 --- a/src/test/items/grip_claw.test.ts +++ b/src/test/items/grip_claw.test.ts @@ -1,16 +1,14 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/move"; -import { Abilities } from "#app/enums/abilities"; -import { BerryType } from "#app/enums/berry-type"; -import { Moves } from "#app/enums/moves"; -import { Species } from "#app/enums/species"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; +import Pokemon from "#app/field/pokemon"; +import { ContactHeldItemTransferChanceModifier } from "#app/modifier/modifier"; +import { Abilities } from "#enums/abilities"; +import { BerryType } from "#enums/berry-type"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -// 20 seconds - describe("Items - Grip Claw", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -30,39 +28,85 @@ describe("Items - Grip Claw", () => { game.override .battleType("double") - .moveset([ Moves.POPULATION_BOMB, Moves.SPLASH ]) + .moveset([ Moves.TACKLE, Moves.SPLASH, Moves.ATTRACT ]) .startingHeldItems([ - { name: "GRIP_CLAW", count: 5 }, // TODO: Find a way to mock the steal chance of grip claw - { name: "MULTI_LENS", count: 3 }, + { name: "GRIP_CLAW", count: 1 }, ]) .enemySpecies(Species.SNORLAX) - .ability(Abilities.KLUTZ) + .enemyAbility(Abilities.UNNERVE) + .ability(Abilities.UNNERVE) .enemyMoveset(Moves.SPLASH) .enemyHeldItems([ { name: "BERRY", type: BerryType.SITRUS, count: 2 }, { name: "BERRY", type: BerryType.LUM, count: 2 }, ]) - .startingLevel(100) .enemyLevel(100); - vi.spyOn(allMoves[Moves.POPULATION_BOMB], "accuracy", "get").mockReturnValue(100); }); - it( - "should only steal items from the attack target", - async () => { - await game.startBattle([ Species.PANSEAR, Species.ROWLET ]); + it("should steal items on contact and only from the attack target", async () => { + await game.classicMode.startBattle([ Species.FEEBAS, Species.MILOTIC ]); - const enemyPokemon = game.scene.getEnemyField(); + const [ playerPokemon, ] = game.scene.getPlayerField(); - const enemyHeldItemCt = enemyPokemon.map(p => p.getHeldItems.length); + const gripClaw = playerPokemon.getHeldItems()[0] as ContactHeldItemTransferChanceModifier; + vi.spyOn(gripClaw, "chance", "get").mockReturnValue(100); - game.move.select(Moves.POPULATION_BOMB, 0, BattlerIndex.ENEMY); - game.move.select(Moves.SPLASH, 1); + const enemyPokemon = game.scene.getEnemyField(); - await game.phaseInterceptor.to(MoveEndPhase, false); + const playerHeldItemCount = getHeldItemCount(playerPokemon); + const enemy1HeldItemCount = getHeldItemCount(enemyPokemon[0]); + const enemy2HeldItemCount = getHeldItemCount(enemyPokemon[1]); + expect(enemy2HeldItemCount).toBeGreaterThan(0); - expect(enemyPokemon[1].getHeldItems.length).toBe(enemyHeldItemCt[1]); - } - ); + game.move.select(Moves.TACKLE, 0, BattlerIndex.ENEMY_2); + game.move.select(Moves.SPLASH, 1); + + await game.phaseInterceptor.to("BerryPhase", false); + + const playerHeldItemCountAfter = getHeldItemCount(playerPokemon); + const enemy1HeldItemCountsAfter = getHeldItemCount(enemyPokemon[0]); + const enemy2HeldItemCountsAfter = getHeldItemCount(enemyPokemon[1]); + + expect(playerHeldItemCountAfter).toBe(playerHeldItemCount + 1); + expect(enemy1HeldItemCountsAfter).toBe(enemy1HeldItemCount); + expect(enemy2HeldItemCountsAfter).toBe(enemy2HeldItemCount - 1); + }); + + it("should not steal items when using a targetted, non attack move", async () => { + await game.classicMode.startBattle([ Species.FEEBAS, Species.MILOTIC ]); + + const [ playerPokemon, ] = game.scene.getPlayerField(); + + const gripClaw = playerPokemon.getHeldItems()[0] as ContactHeldItemTransferChanceModifier; + vi.spyOn(gripClaw, "chance", "get").mockReturnValue(100); + + const enemyPokemon = game.scene.getEnemyField(); + + const playerHeldItemCount = getHeldItemCount(playerPokemon); + const enemy1HeldItemCount = getHeldItemCount(enemyPokemon[0]); + const enemy2HeldItemCount = getHeldItemCount(enemyPokemon[1]); + expect(enemy2HeldItemCount).toBeGreaterThan(0); + + game.move.select(Moves.ATTRACT, 0, BattlerIndex.ENEMY_2); + game.move.select(Moves.SPLASH, 1); + + await game.phaseInterceptor.to("BerryPhase", false); + + const playerHeldItemCountAfter = getHeldItemCount(playerPokemon); + const enemy1HeldItemCountsAfter = getHeldItemCount(enemyPokemon[0]); + const enemy2HeldItemCountsAfter = getHeldItemCount(enemyPokemon[1]); + + expect(playerHeldItemCountAfter).toBe(playerHeldItemCount); + expect(enemy1HeldItemCountsAfter).toBe(enemy1HeldItemCount); + expect(enemy2HeldItemCountsAfter).toBe(enemy2HeldItemCount); + }); }); + +/* + * Gets the total number of items a Pokemon holds + */ +function getHeldItemCount(pokemon: Pokemon) { + return pokemon.getHeldItems().reduce((currentTotal, item) => currentTotal + item.getStackCount(), 0); +} + diff --git a/src/test/items/toxic_orb.test.ts b/src/test/items/toxic_orb.test.ts index 35d6e77b209..63c7b6245f5 100644 --- a/src/test/items/toxic_orb.test.ts +++ b/src/test/items/toxic_orb.test.ts @@ -1,15 +1,13 @@ import { StatusEffect } from "#app/data/status-effect"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { MessagePhase } from "#app/phases/message-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import i18next, { initI18n } from "#app/plugins/i18n"; +import i18next from "#app/plugins/i18n"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +const TIMEOUT = 20 * 1000; describe("Items - Toxic orb", () => { let phaserGame: Phaser.Game; @@ -27,41 +25,36 @@ describe("Items - Toxic orb", () => { beforeEach(() => { game = new GameManager(phaserGame); - const moveToUse = Moves.GROWTH; - const oppMoveToUse = Moves.TACKLE; - game.override.battleType("single"); - game.override.enemySpecies(Species.RATTATA); - game.override.ability(Abilities.INSOMNIA); - game.override.enemyAbility(Abilities.INSOMNIA); - game.override.startingLevel(2000); - game.override.moveset([ moveToUse ]); - game.override.enemyMoveset([ oppMoveToUse, oppMoveToUse, oppMoveToUse, oppMoveToUse ]); - game.override.startingHeldItems([{ - name: "TOXIC_ORB", - }]); + game.override + .battleType("single") + .enemySpecies(Species.RATTATA) + .ability(Abilities.BALL_FETCH) + .enemyAbility(Abilities.BALL_FETCH) + .moveset([ Moves.SPLASH ]) + .enemyMoveset(Moves.SPLASH) + .startingHeldItems([{ + name: "TOXIC_ORB", + }]); + + vi.spyOn(i18next, "t"); }); - it("TOXIC ORB", async () => { - initI18n(); - i18next.changeLanguage("en"); - const moveToUse = Moves.GROWTH; - await game.startBattle([ - Species.MIGHTYENA, - Species.MIGHTYENA, - ]); - expect(game.scene.modifiers[0].type.id).toBe("TOXIC_ORB"); + it("badly poisons the holder", async () => { + await game.classicMode.startBattle([ Species.MIGHTYENA ]); - game.move.select(moveToUse); + const player = game.scene.getPlayerField()[0]; - // will run the 13 phase from enemyCommandPhase to TurnEndPhase - await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase); + game.move.select(Moves.SPLASH); + + await game.phaseInterceptor.to("TurnEndPhase"); // Toxic orb should trigger here - await game.phaseInterceptor.run(MessagePhase); - const message = game.textInterceptor.getLatestMessage(); - expect(message).toContain("statusEffect:toxic.obtainSource"); - await game.phaseInterceptor.run(MessagePhase); - const message2 = game.textInterceptor.getLatestMessage(); - expect(message2).toBe("statusEffect:toxic.activation"); - expect(game.scene.getParty()[0].status!.effect).toBe(StatusEffect.TOXIC); - }, 20000); + await game.phaseInterceptor.run("MessagePhase"); + expect(i18next.t).toHaveBeenCalledWith("statusEffect:toxic.obtainSource", expect.anything()); + + await game.toNextTurn(); + + expect(player.status?.effect).toBe(StatusEffect.TOXIC); + // Damage should not have ticked yet. + expect(player.status?.turnCount).toBe(0); + }, TIMEOUT); }); diff --git a/src/test/moves/obstruct.test.ts b/src/test/moves/obstruct.test.ts index fbb5437b43a..1649c199e32 100644 --- a/src/test/moves/obstruct.test.ts +++ b/src/test/moves/obstruct.test.ts @@ -1,6 +1,7 @@ -import { Moves } from "#app/enums/moves"; -import { Stat } from "#app/enums/stat"; import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { Stat } from "#enums/stat"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -22,13 +23,15 @@ describe("Moves - Obstruct", () => { game = new GameManager(phaserGame); game.override .battleType("single") + .enemySpecies(Species.MAGIKARP) + .enemyMoveset(Moves.TACKLE) .enemyAbility(Abilities.BALL_FETCH) .ability(Abilities.BALL_FETCH) - .moveset([ Moves.OBSTRUCT ]); + .moveset([ Moves.OBSTRUCT ]) + .starterSpecies(Species.FEEBAS); }); it("protects from contact damaging moves and lowers the opponent's defense by 2 stages", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.ICE_PUNCH)); await game.classicMode.startBattle(); game.move.select(Moves.OBSTRUCT); @@ -42,7 +45,6 @@ describe("Moves - Obstruct", () => { }); it("bypasses accuracy checks when applying protection and defense reduction", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.ICE_PUNCH)); await game.classicMode.startBattle(); game.move.select(Moves.OBSTRUCT); @@ -59,7 +61,7 @@ describe("Moves - Obstruct", () => { ); it("protects from non-contact damaging moves and doesn't lower the opponent's defense by 2 stages", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.WATER_GUN)); + game.override.enemyMoveset(Moves.WATER_GUN); await game.classicMode.startBattle(); game.move.select(Moves.OBSTRUCT); @@ -73,7 +75,7 @@ describe("Moves - Obstruct", () => { }); it("doesn't protect from status moves", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.GROWL)); + game.override.enemyMoveset(Moves.GROWL); await game.classicMode.startBattle(); game.move.select(Moves.OBSTRUCT); @@ -83,4 +85,14 @@ describe("Moves - Obstruct", () => { expect(player.getStatStage(Stat.ATK)).toBe(-1); }); + + it("doesn't reduce the stats of an opponent with Clear Body/etc", async () => { + game.override.enemyAbility(Abilities.CLEAR_BODY); + await game.classicMode.startBattle(); + + game.move.select(Moves.OBSTRUCT); + await game.phaseInterceptor.to("BerryPhase"); + + expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.DEF)).toBe(0); + }); }); diff --git a/src/test/moves/sketch.test.ts b/src/test/moves/sketch.test.ts new file mode 100644 index 00000000000..2e3eb97a76c --- /dev/null +++ b/src/test/moves/sketch.test.ts @@ -0,0 +1,53 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { MoveResult } from "#app/field/pokemon"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Moves - Sketch", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .ability(Abilities.BALL_FETCH) + .battleType("single") + .disableCrits() + .enemySpecies(Species.SHUCKLE) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("Sketch should not fail even if a previous Sketch failed to retrieve a valid move and ran out of PP", async () => { + game.override.moveset([ Moves.SKETCH, Moves.SKETCH ]); + + await game.classicMode.startBattle([ Species.REGIELEKI ]); + const playerPokemon = game.scene.getPlayerPokemon(); + + game.move.select(Moves.SKETCH); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(playerPokemon?.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + const moveSlot0 = playerPokemon?.getMoveset()[0]; + expect(moveSlot0?.moveId).toBe(Moves.SKETCH); + expect(moveSlot0?.getPpRatio()).toBe(0); + + await game.toNextTurn(); + game.move.select(Moves.SKETCH); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(playerPokemon?.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + // Can't verify if the player Pokemon's moveset was successfully changed because of overrides. + }); +}); diff --git a/src/test/moves/syrup_bomb.test.ts b/src/test/moves/syrup_bomb.test.ts index 7f914e45cc6..ea2f8b6bab3 100644 --- a/src/test/moves/syrup_bomb.test.ts +++ b/src/test/moves/syrup_bomb.test.ts @@ -1,4 +1,3 @@ -import { allMoves } from "#app/data/move"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { Abilities } from "#enums/abilities"; @@ -7,7 +6,7 @@ import { Stat } from "#enums/stat"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { BattlerIndex } from "#app/battle"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - SYRUP BOMB", () => { let phaserGame: Phaser.Game; @@ -26,20 +25,21 @@ describe("Moves - SYRUP BOMB", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .starterSpecies(Species.MAGIKARP) + .battleType("single") .enemySpecies(Species.SNORLAX) + .enemyAbility(Abilities.BALL_FETCH) + .ability(Abilities.BALL_FETCH) .startingLevel(30) .enemyLevel(100) .moveset([ Moves.SYRUP_BOMB, Moves.SPLASH ]) .enemyMoveset(Moves.SPLASH); - vi.spyOn(allMoves[Moves.SYRUP_BOMB], "accuracy", "get").mockReturnValue(100); }); //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/syrup_bomb_(move) it("decreases the target Pokemon's speed stat once per turn for 3 turns", async () => { - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); const targetPokemon = game.scene.getEnemyPokemon()!; expect(targetPokemon.getStatStage(Stat.SPD)).toBe(0); @@ -66,7 +66,7 @@ describe("Moves - SYRUP BOMB", () => { it("does not affect Pokemon with the ability Bulletproof", async () => { game.override.enemyAbility(Abilities.BULLETPROOF); - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); const targetPokemon = game.scene.getEnemyPokemon()!; @@ -79,4 +79,18 @@ describe("Moves - SYRUP BOMB", () => { expect(targetPokemon.getStatStage(Stat.SPD)).toBe(0); } ); + + it("stops lowering the target's speed if the user leaves the field", async () => { + await game.classicMode.startBattle([ Species.FEEBAS, Species.MILOTIC ]); + + game.move.select(Moves.SYRUP_BOMB); + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await game.move.forceHit(); + await game.toNextTurn(); + + game.doSwitchPokemon(1); + await game.toNextTurn(); + + expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.SPD)).toBe(-1); + }); }); diff --git a/src/test/moves/telekinesis.test.ts b/src/test/moves/telekinesis.test.ts new file mode 100644 index 00000000000..76c0d001f00 --- /dev/null +++ b/src/test/moves/telekinesis.test.ts @@ -0,0 +1,124 @@ +import { BattlerTagType } from "#enums/battler-tag-type"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { MoveResult } from "#app/field/pokemon"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; + +describe("Moves - Telekinesis", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.TELEKINESIS, Moves.TACKLE, Moves.MUD_SHOT, Moves.SMACK_DOWN ]) + .battleType("single") + .enemySpecies(Species.SNORLAX) + .enemyLevel(60) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset([ Moves.SPLASH ]); + }); + + it("Telekinesis makes the affected vulnerable to most attacking moves regardless of accuracy", async () => { + await game.classicMode.startBattle([ Species.MAGIKARP ]); + + const enemyOpponent = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.TELEKINESIS); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined(); + + await game.toNextTurn(); + vi.spyOn(allMoves[Moves.TACKLE], "accuracy", "get").mockReturnValue(0); + game.move.select(Moves.TACKLE); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.isFullHp()).toBe(false); + }); + + it("Telekinesis makes the affected airborne and immune to most Ground-moves", async () => { + await game.classicMode.startBattle([ Species.MAGIKARP ]); + + const enemyOpponent = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.TELEKINESIS); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined(); + + await game.toNextTurn(); + vi.spyOn(allMoves[Moves.MUD_SHOT], "accuracy", "get").mockReturnValue(100); + game.move.select(Moves.MUD_SHOT); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.isFullHp()).toBe(true); + }); + + it("Telekinesis can still affect Pokemon that have been transformed into invalid Pokemon", async () => { + game.override.enemyMoveset(Moves.TRANSFORM); + await game.classicMode.startBattle([ Species.DIGLETT ]); + + const enemyOpponent = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.TELEKINESIS); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined(); + expect(enemyOpponent.summonData.speciesForm?.speciesId).toBe(Species.DIGLETT); + }); + + it("Moves like Smack Down and 1000 Arrows remove all effects of Telekinesis from the target Pokemon", async () => { + await game.classicMode.startBattle([ Species.MAGIKARP ]); + + const enemyOpponent = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.TELEKINESIS); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined(); + + await game.toNextTurn(); + game.move.select(Moves.SMACK_DOWN); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeUndefined(); + expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeUndefined(); + }); + + it("Ingrain will remove the floating effect of Telekinesis, but not the 100% hit", async () => { + game.override.enemyMoveset([ Moves.SPLASH, Moves.INGRAIN ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyOpponent = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.TELEKINESIS); + await game.forceEnemyMove(Moves.SPLASH); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined(); + + await game.toNextTurn(); + vi.spyOn(allMoves[Moves.MUD_SHOT], "accuracy", "get").mockReturnValue(0); + game.move.select(Moves.MUD_SHOT); + await game.forceEnemyMove(Moves.INGRAIN); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.INGRAIN)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeUndefined(); + expect(playerPokemon.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + }); +}); diff --git a/src/test/moves/thousand_arrows.test.ts b/src/test/moves/thousand_arrows.test.ts index 112be476955..976b4352ee4 100644 --- a/src/test/moves/thousand_arrows.test.ts +++ b/src/test/moves/thousand_arrows.test.ts @@ -85,13 +85,13 @@ describe("Moves - Thousand Arrows", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; - enemyPokemon.addTag(BattlerTagType.MAGNET_RISEN, undefined, Moves.MAGNET_RISE); + enemyPokemon.addTag(BattlerTagType.FLOATING, undefined, Moves.MAGNET_RISE); game.move.select(Moves.THOUSAND_ARROWS); await game.phaseInterceptor.to(BerryPhase, false); - expect(enemyPokemon.getTag(BattlerTagType.MAGNET_RISEN)).toBeUndefined(); + expect(enemyPokemon.getTag(BattlerTagType.FLOATING)).toBeUndefined(); expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); } diff --git a/src/test/moves/toxic_spikes.test.ts b/src/test/moves/toxic_spikes.test.ts new file mode 100644 index 00000000000..bac1ccdccd8 --- /dev/null +++ b/src/test/moves/toxic_spikes.test.ts @@ -0,0 +1,136 @@ +import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; +import { StatusEffect } from "#app/data/status-effect"; +import { decrypt, encrypt, GameData, SessionSaveData } from "#app/system/game-data"; +import { Abilities } from "#enums/abilities"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Toxic Spikes", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .startingWave(5) + .enemySpecies(Species.RATTATA) + .enemyAbility(Abilities.BALL_FETCH) + .ability(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .moveset([ Moves.TOXIC_SPIKES, Moves.SPLASH, Moves.ROAR ]); + }); + + it("should not affect the opponent if they do not switch", async() => { + await game.classicMode.runToSummon([ Species.MIGHTYENA, Species.POOCHYENA ]); + + const enemy = game.scene.getEnemyField()[0]; + + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + game.move.select(Moves.SPLASH); + await game.phaseInterceptor.to("TurnEndPhase"); + game.doSwitchPokemon(1); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(enemy.hp).toBe(enemy.getMaxHp()); + expect(enemy.status?.effect).toBeUndefined(); + }, TIMEOUT); + + it("should poison the opponent if they switch into 1 layer", async() => { + await game.classicMode.runToSummon([ Species.MIGHTYENA ]); + + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + game.move.select(Moves.ROAR); + await game.phaseInterceptor.to("TurnEndPhase"); + + const enemy = game.scene.getEnemyField()[0]; + + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); + expect(enemy.status?.effect).toBe(StatusEffect.POISON); + }, TIMEOUT); + + it("should badly poison the opponent if they switch into 2 layers", async() => { + await game.classicMode.runToSummon([ Species.MIGHTYENA ]); + + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + game.move.select(Moves.ROAR); + await game.phaseInterceptor.to("TurnEndPhase"); + + const enemy = game.scene.getEnemyField()[0]; + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); + expect(enemy.status?.effect).toBe(StatusEffect.TOXIC); + }, TIMEOUT); + + it("should be removed if a grounded poison pokemon switches in", async() => { + game.override.enemySpecies(Species.GRIMER); + await game.classicMode.runToSummon([ Species.MIGHTYENA ]); + + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + game.move.select(Moves.ROAR); + await game.phaseInterceptor.to("TurnEndPhase"); + + const enemy = game.scene.getEnemyField()[0]; + expect(enemy.hp).toBe(enemy.getMaxHp()); + expect(enemy.status?.effect).toBeUndefined(); + + expect(game.scene.arena.tags.length).toBe(0); + }, TIMEOUT); + + it("shouldn't create multiple layers per use in doubles", async() => { + await game.classicMode.runToSummon([ Species.MIGHTYENA, Species.POOCHYENA ]); + + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + + const arenaTags = (game.scene.arena.getTagOnSide(ArenaTagType.TOXIC_SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag); + expect(arenaTags.tagType).toBe(ArenaTagType.TOXIC_SPIKES); + expect(arenaTags.layers).toBe(1); + }, TIMEOUT); + + it("should persist through reload", async() => { + game.override.startingWave(1); + const scene = game.scene; + const gameData = new GameData(scene); + + await game.classicMode.runToSummon([ Species.MIGHTYENA ]); + + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + game.move.select(Moves.SPLASH); + await game.doKillOpponents(); + await game.phaseInterceptor.to("BattleEndPhase"); + await game.toNextWave(); + + const sessionData : SessionSaveData = gameData["getSessionSaveData"](game.scene); + localStorage.setItem("sessionTestData", encrypt(JSON.stringify(sessionData), true)); + const recoveredData : SessionSaveData = gameData.parseSessionData(decrypt(localStorage.getItem("sessionTestData")!, true)); + gameData.loadSession(game.scene, 0, recoveredData); + + expect(sessionData.arena.tags).toEqual(recoveredData.arena.tags); + localStorage.removeItem("sessionTestData"); + }, TIMEOUT); +}); diff --git a/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts b/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts index f24800eaa71..7d783958422 100644 --- a/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts @@ -16,6 +16,7 @@ import { EggTier } from "#enums/egg-type"; import { CommandPhase } from "#app/phases/command-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import { PartyHealPhase } from "#app/phases/party-heal-phase"; +import i18next from "i18next"; const namespace = "mysteryEncounters/aTrainersTest"; const defaultParty = [ Species.LAPRAS, Species.GENGAR, Species.ABRA ]; @@ -106,7 +107,8 @@ describe("A Trainer's Test - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(scene.currentBattle.trainer).toBeDefined(); - expect([ "trainerNames:buck", "trainerNames:cheryl", "trainerNames:marley", "trainerNames:mira", "trainerNames:riley" ].includes(scene.currentBattle.trainer!.config.name)).toBeTruthy(); + expect([ i18next.t("trainerNames:buck"), i18next.t("trainerNames:cheryl"), i18next.t("trainerNames:marley"), i18next.t("trainerNames:mira"), i18next.t("trainerNames:riley") ] + .map(name => name.toLowerCase()).includes(scene.currentBattle.trainer!.config.name)).toBeTruthy(); expect(enemyField[0]).toBeDefined(); }); @@ -126,7 +128,7 @@ describe("A Trainer's Test - Mystery Encounter", () => { expect(eggsAfter).toBeDefined(); expect(eggsBeforeLength + 1).toBe(eggsAfter.length); const eggTier = eggsAfter[eggsAfter.length - 1].tier; - expect(eggTier === EggTier.ULTRA || eggTier === EggTier.MASTER).toBeTruthy(); + expect(eggTier === EggTier.EPIC || eggTier === EggTier.LEGENDARY).toBeTruthy(); }); }); @@ -174,7 +176,7 @@ describe("A Trainer's Test - Mystery Encounter", () => { expect(eggsAfter).toBeDefined(); expect(eggsBeforeLength + 1).toBe(eggsAfter.length); const eggTier = eggsAfter[eggsAfter.length - 1].tier; - expect(eggTier).toBe(EggTier.GREAT); + expect(eggTier).toBe(EggTier.RARE); }); it("should leave encounter without battle", async () => { diff --git a/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts b/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts index 99a835cb6ae..a72a9fbb5a3 100644 --- a/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts @@ -16,6 +16,7 @@ import { Moves } from "#enums/moves"; import { CommandPhase } from "#app/phases/command-phase"; import { MovePhase } from "#app/phases/move-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import i18next from "i18next"; const namespace = "mysteryEncounters/absoluteAvarice"; const defaultParty = [ Species.LAPRAS, Species.GENGAR, Species.ABRA ]; @@ -146,7 +147,7 @@ describe("Absolute Avarice - Mystery Encounter", () => { const pokemonId = partyPokemon.id; const pokemonItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).pokemonId === pokemonId, true) as PokemonHeldItemModifier[]; - const revSeed = pokemonItems.find(i => i.type.name === "modifierType:ModifierType.REVIVER_SEED.name"); + const revSeed = pokemonItems.find(i => i.type.name === i18next.t("modifierType:ModifierType.REVIVER_SEED.name")); expect(revSeed).toBeDefined; expect(revSeed?.stackCount).toBe(1); } diff --git a/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts b/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts index 77d5a842b47..9883b4332b9 100644 --- a/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts @@ -17,6 +17,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Moves } from "#enums/moves"; import { ShinyRateBoosterModifier } from "#app/modifier/modifier"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import i18next from "i18next"; const namespace = "mysteryEncounters/anOfferYouCantRefuse"; /** Gyarados for Indimidate */ @@ -93,8 +94,8 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => { expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.strongestPokemon).toBeDefined(); expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.price).toBeDefined(); - expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.option2PrimaryAbility).toBe("ability:intimidate.name"); - expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.moveOrAbility).toBe("ability:intimidate.name"); + expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.option2PrimaryAbility).toBe(i18next.t("ability:intimidate.name")); + expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.moveOrAbility).toBe(i18next.t("ability:intimidate.name")); expect(AnOfferYouCantRefuseEncounter.misc.pokemon instanceof PlayerPokemon).toBeTruthy(); expect(AnOfferYouCantRefuseEncounter.misc?.price?.toString()).toBe(AnOfferYouCantRefuseEncounter.dialogueTokens?.price); expect(onInitResult).toBe(true); diff --git a/src/test/mystery-encounter/encounters/field-trip-encounter.test.ts b/src/test/mystery-encounter/encounters/field-trip-encounter.test.ts index 232bad3c2b8..a6f925274c3 100644 --- a/src/test/mystery-encounter/encounters/field-trip-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/field-trip-encounter.test.ts @@ -103,11 +103,11 @@ describe("Field Trip - Mystery Encounter", () => { expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; expect(modifierSelectHandler.options.length).toEqual(5); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_attack"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_defense"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_speed"); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe("modifierType:ModifierType.DIRE_HIT.name"); - expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe("modifierType:ModifierType.RARER_CANDY.name"); + expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_attack")); + expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_defense")); + expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_speed")); + expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe(i18next.t("modifierType:ModifierType.DIRE_HIT.name")); + expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe(i18next.t("modifierType:ModifierType.RARER_CANDY.name")); }); it("should leave encounter without battle", async () => { @@ -150,11 +150,11 @@ describe("Field Trip - Mystery Encounter", () => { expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; expect(modifierSelectHandler.options.length).toEqual(5); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_sp_atk"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_sp_def"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_speed"); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe("modifierType:ModifierType.DIRE_HIT.name"); - expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe("modifierType:ModifierType.RARER_CANDY.name"); + expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_sp_atk")); + expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_sp_def")); + expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_speed")); + expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe(i18next.t("modifierType:ModifierType.DIRE_HIT.name")); + expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe(i18next.t("modifierType:ModifierType.RARER_CANDY.name")); }); it("should leave encounter without battle", async () => { @@ -198,12 +198,12 @@ describe("Field Trip - Mystery Encounter", () => { expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; expect(modifierSelectHandler.options.length).toEqual(5); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_accuracy"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_speed"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe("modifierType:ModifierType.AddPokeballModifierType.name"); - expect(i18next.t).toHaveBeenCalledWith("modifierType:ModifierType.AddPokeballModifierType.name", expect.objectContaining({ modifierCount: 5 })); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe("modifierType:ModifierType.IV_SCANNER.name"); - expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe("modifierType:ModifierType.RARER_CANDY.name"); + expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_accuracy")); + expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_speed")); + expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe(i18next.t("modifierType:ModifierType.AddPokeballModifierType.name", { modifierCount: 5, pokeballName: i18next.t("pokeball:greatBall") })); + expect(i18next.t).toHaveBeenCalledWith(("modifierType:ModifierType.AddPokeballModifierType.name"), expect.objectContaining({ modifierCount: 5 })); + expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe(i18next.t("modifierType:ModifierType.IV_SCANNER.name")); + expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe(i18next.t("modifierType:ModifierType.RARER_CANDY.name")); }); it("should leave encounter without battle", async () => { diff --git a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts index 3d2533c0817..a4f303d121f 100644 --- a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts @@ -22,6 +22,7 @@ import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils"; import { CommandPhase } from "#app/phases/command-phase"; import { MovePhase } from "#app/phases/move-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import i18next from "i18next"; const namespace = "mysteryEncounters/fieryFallout"; /** Arcanine and Ninetails for 2 Fire types. Lapras, Gengar, Abra for burnable mon. */ @@ -205,7 +206,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { const burnablePokemon = party.filter((pkm) => pkm.isAllowedInBattle() && !pkm.getTypes().includes(Type.FIRE)); const notBurnablePokemon = party.filter((pkm) => !pkm.isAllowedInBattle() || pkm.getTypes().includes(Type.FIRE)); - expect(scene.currentBattle.mysteryEncounter?.dialogueTokens["burnedPokemon"]).toBe("pokemon:gengar"); + expect(scene.currentBattle.mysteryEncounter?.dialogueTokens["burnedPokemon"]).toBe(i18next.t("pokemon:gengar")); burnablePokemon.forEach((pkm) => { expect(pkm.hp, `${pkm.name} should have received 20% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.2)); }); diff --git a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts index 456c18c572d..dec14d46cc8 100644 --- a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -14,6 +14,7 @@ import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils"; import BattleScene from "#app/battle-scene"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { PartyExpPhase } from "#app/phases/party-exp-phase"; +import i18next from "i18next"; const namespace = "mysteryEncounters/lostAtSea"; @@ -86,8 +87,8 @@ describe("Lost at Sea - Mystery Encounter", () => { const onInitResult = onInit!(scene); expect(LostAtSeaEncounter.dialogueTokens?.damagePercentage).toBe("25"); - expect(LostAtSeaEncounter.dialogueTokens?.option1RequiredMove).toBe("move:surf.name"); - expect(LostAtSeaEncounter.dialogueTokens?.option2RequiredMove).toBe("move:fly.name"); + expect(LostAtSeaEncounter.dialogueTokens?.option1RequiredMove).toBe(i18next.t("move:surf.name")); + expect(LostAtSeaEncounter.dialogueTokens?.option2RequiredMove).toBe(i18next.t("move:fly.name")); expect(onInitResult).toBe(true); }); diff --git a/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts b/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts index 2411752baa7..02375d83b98 100644 --- a/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts @@ -1,21 +1,22 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import { Biome } from "#app/enums/biome"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; -import { Species } from "#app/enums/species"; -import GameManager from "#app/test/utils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption, skipBattleRunMysteryEncounterRewardsPhase } from "#test/mystery-encounter/encounter-test-utils"; import BattleScene from "#app/battle-scene"; +import { TeleportingHijinksEncounter } from "#app/data/mystery-encounters/encounters/teleporting-hijinks-encounter"; +import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import { Abilities } from "#enums/abilities"; +import { Biome } from "#enums/biome"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Species } from "#enums/species"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; +import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import GameManager from "#test/utils/gameManager"; +import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; +import { Mode } from "#app/ui/ui"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption, skipBattleRunMysteryEncounterRewardsPhase } from "#test/mystery-encounter/encounter-test-utils"; import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { CommandPhase } from "#app/phases/command-phase"; -import { TeleportingHijinksEncounter } from "#app/data/mystery-encounters/encounters/teleporting-hijinks-encounter"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { Mode } from "#app/ui/ui"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { Abilities } from "#app/enums/abilities"; +import i18next from "i18next"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/teleportingHijinks"; const defaultParty = [ Species.LAPRAS, Species.GENGAR, Species.ABRA ]; @@ -300,8 +301,8 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; - expect(modifierSelectHandler.options.some(opt => opt.modifierTypeOption.type.name === "modifierType:AttackTypeBoosterItem.metal_coat")).toBe(true); - expect(modifierSelectHandler.options.some(opt => opt.modifierTypeOption.type.name === "modifierType:AttackTypeBoosterItem.magnet")).toBe(true); + expect(modifierSelectHandler.options.some(opt => opt.modifierTypeOption.type.name === i18next.t("modifierType:AttackTypeBoosterItem.metal_coat"))).toBe(true); + expect(modifierSelectHandler.options.some(opt => opt.modifierTypeOption.type.name === i18next.t("modifierType:AttackTypeBoosterItem.magnet"))).toBe(true); }); }); }); diff --git a/src/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts b/src/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts index 7e445ac1fe2..bbb4f249feb 100644 --- a/src/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts @@ -155,7 +155,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => { expect(eggsAfter).toBeDefined(); expect(eggsBeforeLength + commonEggs + rareEggs).toBe(eggsAfter.length); expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs); - expect(eggsAfter.filter(egg => egg.tier === EggTier.GREAT).length).toBe(rareEggs); + expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs); game.phaseInterceptor.superEndPhase(); await game.phaseInterceptor.to(PostMysteryEncounterPhase); @@ -213,7 +213,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => { expect(eggsAfter).toBeDefined(); expect(eggsBeforeLength + commonEggs + rareEggs).toBe(eggsAfter.length); expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs); - expect(eggsAfter.filter(egg => egg.tier === EggTier.GREAT).length).toBe(rareEggs); + expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs); game.phaseInterceptor.superEndPhase(); await game.phaseInterceptor.to(PostMysteryEncounterPhase); @@ -271,7 +271,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => { expect(eggsAfter).toBeDefined(); expect(eggsBeforeLength + commonEggs + rareEggs).toBe(eggsAfter.length); expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs); - expect(eggsAfter.filter(egg => egg.tier === EggTier.GREAT).length).toBe(rareEggs); + expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs); game.phaseInterceptor.superEndPhase(); await game.phaseInterceptor.to(PostMysteryEncounterPhase); diff --git a/src/test/phases/mystery-encounter-phase.test.ts b/src/test/phases/mystery-encounter-phase.test.ts index 4468045756b..32e31ce1c94 100644 --- a/src/test/phases/mystery-encounter-phase.test.ts +++ b/src/test/phases/mystery-encounter-phase.test.ts @@ -9,6 +9,7 @@ import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import MessageUiHandler from "#app/ui/message-ui-handler"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import i18next from "i18next"; describe("Mystery Encounter Phases", () => { let phaserGame: Phaser.Game; @@ -78,9 +79,9 @@ describe("Mystery Encounter Phases", () => { expect(ui.getMode()).toBe(Mode.MESSAGE); expect(ui.showDialogue).toHaveBeenCalledTimes(1); expect(ui.showText).toHaveBeenCalledTimes(2); - expect(ui.showDialogue).toHaveBeenCalledWith("battle:mysteryEncounterAppeared", "???", null, expect.any(Function)); - expect(ui.showText).toHaveBeenCalledWith("mysteryEncounters/mysteriousChallengers:intro", null, expect.any(Function), 750, true); - expect(ui.showText).toHaveBeenCalledWith("mysteryEncounters/mysteriousChallengers:option.selected", null, expect.any(Function), 300, true); + expect(ui.showDialogue).toHaveBeenCalledWith(i18next.t("battle:mysteryEncounterAppeared"), "???", null, expect.any(Function)); + expect(ui.showText).toHaveBeenCalledWith(i18next.t("mysteryEncounters/mysteriousChallengers:intro"), null, expect.any(Function), 750, true); + expect(ui.showText).toHaveBeenCalledWith(i18next.t("mysteryEncounters/mysteriousChallengers:option.selected"), null, expect.any(Function), 300, true); }); }); diff --git a/src/test/system/game_data.test.ts b/src/test/system/game_data.test.ts index 5eb4dea3910..fcb7e9067a3 100644 --- a/src/test/system/game_data.test.ts +++ b/src/test/system/game_data.test.ts @@ -11,13 +11,15 @@ import * as account from "../../account"; const apiBase = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8001"; -export const server = setupServer(); +/** We need a custom server. For some reasons I can't extend the listeners of {@linkcode global.i18nServer} with {@linkcode global.i18nServer.use} */ +const server = setupServer(); describe("System - Game Data", () => { let phaserGame: Phaser.Game; let game: GameManager; beforeAll(() => { + global.i18nServer.close(); server.listen(); phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, @@ -26,6 +28,7 @@ describe("System - Game Data", () => { afterAll(() => { server.close(); + global.i18nServer.listen(); }); beforeEach(() => { diff --git a/src/test/ui/starter-select.test.ts b/src/test/ui/starter-select.test.ts index dd0761be392..94370ca1b74 100644 --- a/src/test/ui/starter-select.test.ts +++ b/src/test/ui/starter-select.test.ts @@ -14,6 +14,7 @@ import { Abilities } from "#enums/abilities"; import { Button } from "#enums/buttons"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; +import i18next from "i18next"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -66,11 +67,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { @@ -127,11 +128,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { @@ -191,11 +192,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { @@ -254,11 +255,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { @@ -315,11 +316,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { @@ -376,11 +377,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { @@ -436,11 +437,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { @@ -496,11 +497,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); let starterSelectUiHandler: StarterSelectUiHandler; @@ -561,11 +562,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); let starterSelectUiHandler: StarterSelectUiHandler | undefined; diff --git a/src/test/ui/type-hints.test.ts b/src/test/ui/type-hints.test.ts index 450f43f1263..2977262dda7 100644 --- a/src/test/ui/type-hints.test.ts +++ b/src/test/ui/type-hints.test.ts @@ -7,7 +7,8 @@ import { Mode } from "#app/ui/ui"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import MockText from "../utils/mocks/mocksContainer/mockText"; +import MockText from "#test/utils/mocks/mocksContainer/mockText"; +import i18next from "i18next"; describe("UI - Type Hints", () => { let phaserGame: Phaser.Game; @@ -53,7 +54,7 @@ describe("UI - Type Hints", () => { const movesContainer = ui.getByName(FightUiHandler.MOVES_CONTAINER_NAME); const dragonClawText = movesContainer .getAll() - .find((text) => text.text === "move:dragonClaw.name")! as unknown as MockText; + .find((text) => text.text === i18next.t("move:dragonClaw.name"))! as unknown as MockText; expect.soft(dragonClawText.color).toBe("#929292"); ui.getHandler().processInput(Button.ACTION); @@ -78,7 +79,7 @@ describe("UI - Type Hints", () => { const movesContainer = ui.getByName(FightUiHandler.MOVES_CONTAINER_NAME); const growlText = movesContainer .getAll() - .find((text) => text.text === "move:growl.name")! as unknown as MockText; + .find((text) => text.text === i18next.t("move:growl.name"))! as unknown as MockText; expect.soft(growlText.color).toBe(undefined); ui.getHandler().processInput(Button.ACTION); diff --git a/src/test/utils/gameManagerUtils.ts b/src/test/utils/gameManagerUtils.ts index 700d93082d8..543ee9627fe 100644 --- a/src/test/utils/gameManagerUtils.ts +++ b/src/test/utils/gameManagerUtils.ts @@ -86,7 +86,7 @@ export function waitUntil(truth) { export function getMovePosition(scene: BattleScene, pokemonIndex: 0 | 1, move: Moves) { const playerPokemon = scene.getPlayerField()[pokemonIndex]; const moveSet = playerPokemon.getMoveset(); - const index = moveSet.findIndex((m) => m?.moveId === move); + const index = moveSet.findIndex((m) => m?.moveId === move && m?.ppUsed < m?.getMovePp()); console.log(`Move position for ${Moves[move]} (=${move}):`, index); return index; } diff --git a/src/test/utils/phaseInterceptor.ts b/src/test/utils/phaseInterceptor.ts index d108c4cb2ea..ec9309e2405 100644 --- a/src/test/utils/phaseInterceptor.ts +++ b/src/test/utils/phaseInterceptor.ts @@ -53,6 +53,7 @@ import { } from "#app/phases/mystery-encounter-phases"; import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; import { PartyExpPhase } from "#app/phases/party-exp-phase"; +import { ExpPhase } from "#app/phases/exp-phase"; export interface PromptHandler { phaseTarget?: string; @@ -61,7 +62,114 @@ export interface PromptHandler { expireFn?: () => void; awaitingActionInput?: boolean; } -import { ExpPhase } from "#app/phases/exp-phase"; + +type PhaseClass = + | typeof LoginPhase + | typeof TitlePhase + | typeof SelectGenderPhase + | typeof EncounterPhase + | typeof NewBiomeEncounterPhase + | typeof SelectStarterPhase + | typeof PostSummonPhase + | typeof SummonPhase + | typeof ToggleDoublePositionPhase + | typeof CheckSwitchPhase + | typeof ShowAbilityPhase + | typeof MessagePhase + | typeof TurnInitPhase + | typeof CommandPhase + | typeof EnemyCommandPhase + | typeof TurnStartPhase + | typeof MovePhase + | typeof MoveEffectPhase + | typeof DamagePhase + | typeof FaintPhase + | typeof BerryPhase + | typeof TurnEndPhase + | typeof BattleEndPhase + | typeof EggLapsePhase + | typeof SelectModifierPhase + | typeof NextEncounterPhase + | typeof NewBattlePhase + | typeof VictoryPhase + | typeof LearnMovePhase + | typeof MoveEndPhase + | typeof StatStageChangePhase + | typeof ShinySparklePhase + | typeof SelectTargetPhase + | typeof UnavailablePhase + | typeof QuietFormChangePhase + | typeof SwitchPhase + | typeof SwitchSummonPhase + | typeof PartyHealPhase + | typeof EvolutionPhase + | typeof EndEvolutionPhase + | typeof LevelCapPhase + | typeof AttemptRunPhase + | typeof SelectBiomePhase + | typeof MysteryEncounterPhase + | typeof MysteryEncounterOptionSelectedPhase + | typeof MysteryEncounterBattlePhase + | typeof MysteryEncounterRewardsPhase + | typeof PostMysteryEncounterPhase + | typeof ModifierRewardPhase + | typeof PartyExpPhase + | typeof ExpPhase; + +type PhaseString = + | "LoginPhase" + | "TitlePhase" + | "SelectGenderPhase" + | "EncounterPhase" + | "NewBiomeEncounterPhase" + | "SelectStarterPhase" + | "PostSummonPhase" + | "SummonPhase" + | "ToggleDoublePositionPhase" + | "CheckSwitchPhase" + | "ShowAbilityPhase" + | "MessagePhase" + | "TurnInitPhase" + | "CommandPhase" + | "EnemyCommandPhase" + | "TurnStartPhase" + | "MovePhase" + | "MoveEffectPhase" + | "DamagePhase" + | "FaintPhase" + | "BerryPhase" + | "TurnEndPhase" + | "BattleEndPhase" + | "EggLapsePhase" + | "SelectModifierPhase" + | "NextEncounterPhase" + | "NewBattlePhase" + | "VictoryPhase" + | "LearnMovePhase" + | "MoveEndPhase" + | "StatStageChangePhase" + | "ShinySparklePhase" + | "SelectTargetPhase" + | "UnavailablePhase" + | "QuietFormChangePhase" + | "SwitchPhase" + | "SwitchSummonPhase" + | "PartyHealPhase" + | "EvolutionPhase" + | "EndEvolutionPhase" + | "LevelCapPhase" + | "AttemptRunPhase" + | "SelectBiomePhase" + | "MysteryEncounterPhase" + | "MysteryEncounterOptionSelectedPhase" + | "MysteryEncounterBattlePhase" + | "MysteryEncounterRewardsPhase" + | "PostMysteryEncounterPhase" + | "ModifierRewardPhase" + | "PartyExpPhase" + | "ExpPhase"; + +type PhaseInterceptorPhase = PhaseClass | PhaseString; export default class PhaseInterceptor { public scene; @@ -172,7 +280,7 @@ export default class PhaseInterceptor { * @param phaseFrom - The phase to start from. * @returns The instance of the PhaseInterceptor. */ - runFrom(phaseFrom) { + runFrom(phaseFrom: PhaseInterceptorPhase): PhaseInterceptor { this.phaseFrom = phaseFrom; return this; } @@ -180,9 +288,10 @@ export default class PhaseInterceptor { /** * Method to transition to a target phase. * @param phaseTo - The phase to transition to. + * @param runTarget - Whether or not to run the target phase. * @returns A promise that resolves when the transition is complete. */ - async to(phaseTo, runTarget: boolean = true): Promise { + async to(phaseTo: PhaseInterceptorPhase, runTarget: boolean = true): Promise { return new Promise(async (resolve, reject) => { ErrorInterceptor.getInstance().add(this); if (this.phaseFrom) { @@ -219,7 +328,7 @@ export default class PhaseInterceptor { * @param skipFn - Optional skip function. * @returns A promise that resolves when the phase is run. */ - run(phaseTarget, skipFn?): Promise { + run(phaseTarget: PhaseInterceptorPhase, skipFn?: (className: PhaseClass) => boolean): Promise { const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name; this.scene.moveAnimations = null; // Mandatory to avoid crash return new Promise(async (resolve, reject) => { @@ -253,7 +362,7 @@ export default class PhaseInterceptor { }); } - whenAboutToRun(phaseTarget, skipFn?): Promise { + whenAboutToRun(phaseTarget: PhaseInterceptorPhase, skipFn?: (className: PhaseClass) => boolean): Promise { const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name; this.scene.moveAnimations = null; // Mandatory to avoid crash return new Promise(async (resolve, reject) => { @@ -311,7 +420,7 @@ export default class PhaseInterceptor { * Method to start a phase and log it. * @param phase - The phase to start. */ - startPhase(phase) { + startPhase(phase: PhaseClass) { this.log.push(phase.name); const instance = this.scene.getCurrentPhase(); this.onHold.push({ @@ -340,9 +449,10 @@ export default class PhaseInterceptor { /** * m2m to set mode. - * @param phase - The phase to start. + * @param mode - The {@linkcode Mode} to set. + * @param args - Additional arguments to pass to the original method. */ - setMode(mode: Mode, ...args: any[]): Promise { + setMode(mode: Mode, ...args: unknown[]): Promise { const currentPhase = this.scene.getCurrentPhase(); const instance = this.scene.ui; console.log("setMode", `${Mode[mode]} (=${mode})`, args); diff --git a/src/test/vitest.setup.ts b/src/test/vitest.setup.ts index 0d67d6787c4..8438f607db2 100644 --- a/src/test/vitest.setup.ts +++ b/src/test/vitest.setup.ts @@ -4,16 +4,17 @@ import { initLoggedInUser } from "#app/account"; import { initAbilities } from "#app/data/ability"; import { initBiomes } from "#app/data/balance/biomes"; import { initEggMoves } from "#app/data/balance/egg-moves"; +import { initPokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { initMoves } from "#app/data/move"; import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters"; -import { initPokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { initPokemonForms } from "#app/data/pokemon-forms"; import { initSpecies } from "#app/data/pokemon-species"; import { initAchievements } from "#app/system/achv"; import { initVouchers } from "#app/system/voucher"; import { initStatsKeys } from "#app/ui/game-stats-ui-handler"; -import { beforeAll, vi } from "vitest"; +import { afterAll, beforeAll, vi } from "vitest"; +/** Set the timezone to UTC for tests. */ process.env.TZ = "UTC"; /** Mock the override import to always return default values, ignoring any custom overrides. */ @@ -26,26 +27,36 @@ vi.mock("#app/overrides", async (importOriginal) => { } satisfies typeof import("#app/overrides"); }); -vi.mock("i18next", () => ({ - default: { - use: () => {}, - t: (key: string) => key, - changeLanguage: () => Promise.resolve(), - init: () => Promise.resolve(), - resolvedLanguage: "en", - exists: () => true, - getDataByLanguage:() => ({ - en: { - keys: [ "foo" ] - }, - }), - services: { - formatter: { - add: () => {}, +/** + * This is a hacky way to mock the i18n backend requests (with the help of {@link https://mswjs.io/ | msw}). + * The reason to put it inside of a mock is to elevate it. + * This is necessary because how our code is structured. + * Do NOT try to put any of this code into external functions, it won't work as it's elevated during runtime. + */ +vi.mock("i18next", async (importOriginal) => { + console.log("Mocking i18next"); + const { setupServer } = await import("msw/node"); + const { http, HttpResponse } = await import("msw"); + + global.i18nServer = setupServer( + http.get("/locales/en/*", async (req) => { + const filename = req.params[0]; + + try { + const json = await import(`../../public/locales/en/${req.params[0]}`); + console.log("Loaded locale", filename); + return HttpResponse.json(json); + } catch (err) { + console.log(`Failed to load locale ${filename}!`, err); + return HttpResponse.json({}); } - }, - }, -})); + }) + ); + global.i18nServer.listen({ onUnhandledRequest: "error" }); + console.log("i18n MSW server listening!"); + + return await importOriginal(); +}); initVouchers(); initAchievements(); @@ -70,3 +81,8 @@ beforeAll(() => { }, }); }); + +afterAll(() => { + global.i18nServer.close(); + console.log("Closing i18n MSW server!"); +}); diff --git a/src/ui/achvs-ui-handler.ts b/src/ui/achvs-ui-handler.ts index f90732d1fae..4e0e2feea81 100644 --- a/src/ui/achvs-ui-handler.ts +++ b/src/ui/achvs-ui-handler.ts @@ -40,7 +40,9 @@ export default class AchvsUiHandler extends MessageUiHandler { private iconsBg: Phaser.GameObjects.NineSlice; private icons: Phaser.GameObjects.Sprite[]; + private titleBg: Phaser.GameObjects.NineSlice; private titleText: Phaser.GameObjects.Text; + private scoreContainer: Phaser.GameObjects.Container; private scoreText: Phaser.GameObjects.Text; private unlockText: Phaser.GameObjects.Text; @@ -114,29 +116,31 @@ export default class AchvsUiHandler extends MessageUiHandler { const titleBg = addWindow(this.scene, 0, this.headerBg.height + this.iconsBg.height, 174, 24); titleBg.setOrigin(0, 0); + this.titleBg = titleBg; this.titleText = addTextObject(this.scene, 0, 0, "", TextStyle.WINDOW); const textSize = languageSettings[i18next.language]?.TextSize ?? this.titleText.style.fontSize; this.titleText.setFontSize(textSize); - this.titleText.setOrigin(0, 0); const titleBgCenterX = titleBg.x + titleBg.width / 2; const titleBgCenterY = titleBg.y + titleBg.height / 2; this.titleText.setOrigin(0.5, 0.5); this.titleText.setPosition(titleBgCenterX, titleBgCenterY); - const scoreBg = addWindow(this.scene, titleBg.x + titleBg.width, titleBg.y, 46, 24); + this.scoreContainer = this.scene.add.container(titleBg.x + titleBg.width, titleBg.y); + const scoreBg = addWindow(this.scene, 0, 0, 46, 24); scoreBg.setOrigin(0, 0); + this.scoreContainer.add(scoreBg); - this.scoreText = addTextObject(this.scene, 0, 0, "", TextStyle.WINDOW); - this.scoreText.setOrigin(0, 0); - this.scoreText.setPositionRelative(scoreBg, 8, 4); + this.scoreText = addTextObject(this.scene, scoreBg.width / 2, scoreBg.height / 2, "", TextStyle.WINDOW); + this.scoreText.setOrigin(0.5, 0.5); + this.scoreContainer.add(this.scoreText); - const unlockBg = addWindow(this.scene, scoreBg.x + scoreBg.width, scoreBg.y, 98, 24); + const unlockBg = addWindow(this.scene, this.scoreContainer.x + scoreBg.width, titleBg.y, 98, 24); unlockBg.setOrigin(0, 0); this.unlockText = addTextObject(this.scene, 0, 0, "", TextStyle.WINDOW); - this.unlockText.setOrigin(0, 0); - this.unlockText.setPositionRelative(unlockBg, 8, 4); + this.unlockText.setOrigin(0.5, 0.5); + this.unlockText.setPositionRelative(unlockBg, unlockBg.width / 2, unlockBg.height / 2); const descriptionBg = addWindow(this.scene, 0, titleBg.y + titleBg.height, (this.scene.game.canvas.width / 6) - 2, 42); descriptionBg.setOrigin(0, 0); @@ -157,8 +161,7 @@ export default class AchvsUiHandler extends MessageUiHandler { this.mainContainer.add(this.iconsContainer); this.mainContainer.add(titleBg); this.mainContainer.add(this.titleText); - this.mainContainer.add(scoreBg); - this.mainContainer.add(this.scoreText); + this.mainContainer.add(this.scoreContainer); this.mainContainer.add(unlockBg); this.mainContainer.add(this.unlockText); this.mainContainer.add(descriptionBg); @@ -167,8 +170,6 @@ export default class AchvsUiHandler extends MessageUiHandler { ui.add(this.mainContainer); this.currentPage = Page.ACHIEVEMENTS; - this.setCursor(0); - this.setScrollCursor(0); this.mainContainer.setVisible(false); } @@ -316,9 +317,19 @@ export default class AchvsUiHandler extends MessageUiHandler { if (update || pageChange) { switch (this.currentPage) { case Page.ACHIEVEMENTS: + if (pageChange) { + this.titleBg.width = 174; + this.titleText.x = this.titleBg.width / 2; + this.scoreContainer.setVisible(true); + } this.showAchv(achvs[Object.keys(achvs)[cursor + this.scrollCursor * this.COLS]]); break; case Page.VOUCHERS: + if (pageChange) { + this.titleBg.width = 220; + this.titleText.x = this.titleBg.width / 2; + this.scoreContainer.setVisible(false); + } this.showVoucher(vouchers[Object.keys(vouchers)[cursor + this.scrollCursor * this.COLS]]); break; } @@ -442,6 +453,7 @@ export default class AchvsUiHandler extends MessageUiHandler { this.currentPage = Page.ACHIEVEMENTS; this.mainContainer.setVisible(false); this.setScrollCursor(0); + this.setCursor(0, true); this.eraseCursor(); } diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index 79b51ba6c44..1d97998f491 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -593,7 +593,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { }; const updatePokemonHp = () => { - let duration = !instant ? Utils.clampInt(Math.abs((this.lastHp) - pokemon.hp) * 5, 250, 5000) : 0; + let duration = !instant ? Phaser.Math.Clamp(Math.abs((this.lastHp) - pokemon.hp) * 5, 250, 5000) : 0; const speed = (this.scene as BattleScene).hpBarSpeed; if (speed) { duration = speed >= 3 ? 0 : duration / Math.pow(2, speed); diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index 366f1604740..3aa009b1b31 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -471,9 +471,9 @@ export default class EggGachaUiHandler extends MessageUiHandler { getGuaranteedEggTierFromPullCount(pullCount: number): EggTier { switch (pullCount) { case 10: - return EggTier.GREAT; + return EggTier.RARE; case 25: - return EggTier.ULTRA; + return EggTier.EPIC; default: return EggTier.COMMON; } @@ -516,7 +516,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { const eggText = addTextObject(this.scene, 0, 14, egg.getEggDescriptor(), TextStyle.PARTY, { align: "center" }); eggText.setOrigin(0.5, 0); - eggText.setTint(getEggTierTextTint(!egg.isManaphyEgg() ? egg.tier : EggTier.ULTRA)); + eggText.setTint(getEggTierTextTint(!egg.isManaphyEgg() ? egg.tier : EggTier.EPIC)); ret.add(eggText); this.eggGachaSummaryContainer.addAt(ret, 0); diff --git a/src/ui/run-history-ui-handler.ts b/src/ui/run-history-ui-handler.ts index f4de9b21963..20de7fd832c 100644 --- a/src/ui/run-history-ui-handler.ts +++ b/src/ui/run-history-ui-handler.ts @@ -12,6 +12,7 @@ import { BattleType } from "../battle"; import { RunEntry } from "../system/game-data"; import { PlayerGender } from "#enums/player-gender"; import { TrainerVariant } from "../field/trainer"; +import { RunDisplayMode } from "#app/ui/run-info-ui-handler"; export type RunSelectCallback = (cursor: number) => void; @@ -104,7 +105,7 @@ export default class RunHistoryUiHandler extends MessageUiHandler { if (button === Button.ACTION) { const cursor = this.cursor + this.scrollCursor; if (this.runs[cursor]) { - this.scene.ui.setOverlayMode(Mode.RUN_INFO, this.runs[cursor].entryData, true); + this.scene.ui.setOverlayMode(Mode.RUN_INFO, this.runs[cursor].entryData, RunDisplayMode.RUN_HISTORY, true); } else { return false; } diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index d5f04f90e5b..39927f8e071 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -5,6 +5,7 @@ import { SessionSaveData } from "../system/game-data"; import { TextStyle, addTextObject, addBBCodeTextObject, getTextColor } from "./text"; import { Mode } from "./ui"; import { addWindow } from "./ui-theme"; +import { getPokeballAtlasKey } from "#app/data/pokeball"; import * as Utils from "../utils"; import PokemonData from "../system/pokemon-data"; import i18next from "i18next"; @@ -22,6 +23,8 @@ import * as Modifier from "../modifier/modifier"; import { Species } from "#enums/species"; import { PlayerGender } from "#enums/player-gender"; import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; +import { getBiomeName } from "#app/data/balance/biomes"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; /** * RunInfoUiMode indicates possible overlays of RunInfoUiHandler. @@ -34,6 +37,11 @@ enum RunInfoUiMode { ENDING_ART } +export enum RunDisplayMode { + RUN_HISTORY, + SESSION_PREVIEW +} + /** * Some variables are protected because this UI class will most likely be extended in the future to display more information. * These variables will most likely be shared across 'classes' aka pages. @@ -41,6 +49,7 @@ enum RunInfoUiMode { * For now, I leave as is. */ export default class RunInfoUiHandler extends UiHandler { + protected runDisplayMode: RunDisplayMode; protected runInfo: SessionSaveData; protected isVictory: boolean; protected pageMode: RunInfoUiMode; @@ -66,6 +75,7 @@ export default class RunInfoUiHandler extends UiHandler { // The import of the modifiersModule is loaded here to sidestep async/await issues. this.modifiersModule = Modifier; this.runContainer.setVisible(false); + this.scene.loadImage("encounter_exclaim", "mystery-encounters"); } /** @@ -87,9 +97,15 @@ export default class RunInfoUiHandler extends UiHandler { this.runContainer.add(gameStatsBg); const run = args[0]; + this.runDisplayMode = args[1]; + if (this.runDisplayMode === RunDisplayMode.RUN_HISTORY) { + this.runInfo = this.scene.gameData.parseSessionData(JSON.stringify(run.entry)); + this.isVictory = run.isVictory ?? false; + } else if (this.runDisplayMode === RunDisplayMode.SESSION_PREVIEW) { + this.runInfo = args[0]; + } // Assigning information necessary for the UI's creation - this.runInfo = this.scene.gameData.parseSessionData(JSON.stringify(run.entry)); - this.isVictory = run.isVictory; + this.pageMode = RunInfoUiMode.MAIN; // Creates Header and adds to this.runContainer @@ -102,7 +118,11 @@ export default class RunInfoUiHandler extends UiHandler { const runResultWindow = addWindow(this.scene, 0, 0, this.statsBgWidth - 11, 65); runResultWindow.setOrigin(0, 0); this.runResultContainer.add(runResultWindow); - this.parseRunResult(); + if (this.runDisplayMode === RunDisplayMode.RUN_HISTORY) { + this.parseRunResult(); + } else if (this.runDisplayMode === RunDisplayMode.SESSION_PREVIEW) { + this.parseRunStatus(); + } // Creates Run Info Container this.runInfoContainer = this.scene.add.container(0, 89); @@ -226,6 +246,66 @@ export default class RunInfoUiHandler extends UiHandler { this.runContainer.add(this.runResultContainer); } + /** + * This function is used when the Run Info UI is used to preview a Session. + * It edits {@linkcode runResultContainer}, but most importantly - does not display the negative results of a Mystery Encounter or any details of a trainer's party. + * Trainer Parties are replaced with their sprites, names, and their party size. + * Mystery Encounters contain sprites associated with MEs + the title of the specific ME. + */ + private parseRunStatus() { + const runStatusText = addTextObject(this.scene, 6, 5, `${i18next.t("saveSlotSelectUiHandler:wave")} ${this.runInfo.waveIndex} - ${getBiomeName(this.runInfo.arena.biome)}`, TextStyle.WINDOW, { fontSize : "65px", lineSpacing: 0.1 }); + + const enemyContainer = this.scene.add.container(0, 0); + this.runResultContainer.add(enemyContainer); + if (this.runInfo.battleType === BattleType.WILD) { + if (this.runInfo.enemyParty.length === 1) { + this.parseWildSingleDefeat(enemyContainer); + } else if (this.runInfo.enemyParty.length === 2) { + this.parseWildDoubleDefeat(enemyContainer); + } + } else if (this.runInfo.battleType === BattleType.TRAINER) { + this.showTrainerSprites(enemyContainer); + const row_limit = 3; + this.runInfo.enemyParty.forEach((p, i) => { + const pokeball = this.scene.add.sprite(0, 0, "pb"); + pokeball.setFrame(getPokeballAtlasKey(p.pokeball)); + pokeball.setScale(0.5); + pokeball.setPosition(52 + ((i % row_limit) * 8), (i <= 2) ? 18 : 25); + enemyContainer.add(pokeball); + }); + const trainerObj = this.runInfo.trainer.toTrainer(this.scene); + const RIVAL_TRAINER_ID_THRESHOLD = 375; + let trainerName = ""; + if (this.runInfo.trainer.trainerType >= RIVAL_TRAINER_ID_THRESHOLD) { + trainerName = (trainerObj.variant === TrainerVariant.FEMALE) ? i18next.t("trainerNames:rival_female") : i18next.t("trainerNames:rival"); + } else { + trainerName = trainerObj.getName(0, true); + } + const boxString = i18next.t(trainerObj.variant !== TrainerVariant.DOUBLE ? "battle:trainerAppeared" : "battle:trainerAppearedDouble", { trainerName: trainerName }).replace(/\n/g, " "); + const descContainer = this.scene.add.container(0, 0); + const textBox = addTextObject(this.scene, 0, 0, boxString, TextStyle.WINDOW, { fontSize : "35px", wordWrap: { width: 200 }}); + descContainer.add(textBox); + descContainer.setPosition(52, 29); + this.runResultContainer.add(descContainer); + } else if (this.runInfo.battleType === BattleType.MYSTERY_ENCOUNTER) { + const encounterExclaim = this.scene.add.sprite(0, 0, "encounter_exclaim"); + encounterExclaim.setPosition(34, 26); + encounterExclaim.setScale(0.65); + const subSprite = this.scene.add.sprite(56, -106, "pkmn__sub"); + subSprite.setScale(0.65); + subSprite.setPosition(34, 46); + const mysteryEncounterTitle = i18next.t(this.scene.getMysteryEncounter(this.runInfo.mysteryEncounterType as MysteryEncounterType, true).localizationKey + ":title"); + const descContainer = this.scene.add.container(0, 0); + const textBox = addTextObject(this.scene, 0, 0, mysteryEncounterTitle, TextStyle.WINDOW, { fontSize : "45px", wordWrap: { width: 160 }}); + descContainer.add(textBox); + descContainer.setPosition(47, 37); + this.runResultContainer.add([ encounterExclaim, subSprite, descContainer ]); + } + + this.runResultContainer.add(runStatusText); + this.runContainer.add(this.runResultContainer); + } + /** * This function is called to edit an enemyContainer to represent a loss from a defeat by a wild single Pokemon battle. * @param enemyContainer - container holding enemy visual and level information @@ -278,40 +358,58 @@ export default class RunInfoUiHandler extends UiHandler { } /** - * This edits a container to represent a loss from a defeat by a trainer battle. - * @param enemyContainer - container holding enemy visuals and level information - * The trainers are placed to the left of their party. - * Depending on the trainer icon, there may be overlap between the edges of the box or their party. (Capes...) - * - * Party Pokemon have their icons, terastalization status, and level shown. + * This loads the enemy sprites, positions, and scales them according to the current display mode of the RunInfo UI and then adds them to the container parameter. + * Used by {@linkcode parseRunStatus} and {@linkcode parseTrainerDefeat} + * @param enemyContainer a Phaser Container that should hold enemy sprites */ - private parseTrainerDefeat(enemyContainer: Phaser.GameObjects.Container) { + private showTrainerSprites(enemyContainer: Phaser.GameObjects.Container) { // Creating the trainer sprite and adding it to enemyContainer const tObj = this.runInfo.trainer.toTrainer(this.scene); - // Loads trainer assets on demand, as they are not loaded by default in the scene tObj.config.loadAssets(this.scene, this.runInfo.trainer.variant).then(() => { const tObjSpriteKey = tObj.config.getSpriteKey(this.runInfo.trainer.variant === TrainerVariant.FEMALE, false); const tObjSprite = this.scene.add.sprite(0, 5, tObjSpriteKey); - if (this.runInfo.trainer.variant === TrainerVariant.DOUBLE) { + if (this.runInfo.trainer.variant === TrainerVariant.DOUBLE && !tObj.config.doubleOnly) { const doubleContainer = this.scene.add.container(5, 8); tObjSprite.setPosition(-3, -3); const tObjPartnerSpriteKey = tObj.config.getSpriteKey(true, true); const tObjPartnerSprite = this.scene.add.sprite(5, -3, tObjPartnerSpriteKey); // Double Trainers have smaller sprites than Single Trainers - tObjPartnerSprite.setScale(0.20); - tObjSprite.setScale(0.20); - doubleContainer.add(tObjSprite); - doubleContainer.add(tObjPartnerSprite); - doubleContainer.setPosition(12, 38); + if (this.runDisplayMode === RunDisplayMode.RUN_HISTORY) { + tObjPartnerSprite.setScale(0.20); + tObjSprite.setScale(0.20); + doubleContainer.add(tObjSprite); + doubleContainer.add(tObjPartnerSprite); + doubleContainer.setPosition(12, 38); + } else { + tObjSprite.setScale(0.55); + tObjSprite.setPosition(-9, -3); + tObjPartnerSprite.setScale(0.55); + doubleContainer.add([ tObjSprite, tObjPartnerSprite ]); + doubleContainer.setPosition(28, 40); + } enemyContainer.add(doubleContainer); } else { - tObjSprite.setScale(0.35, 0.35); - tObjSprite.setPosition(12, 28); + const scale = (this.runDisplayMode === RunDisplayMode.RUN_HISTORY) ? 0.35 : 0.65; + const position = (this.runDisplayMode === RunDisplayMode.RUN_HISTORY) ? [ 12, 28 ] : [ 32, 36 ]; + tObjSprite.setScale(scale, scale); + tObjSprite.setPosition(position[0], position[1]); enemyContainer.add(tObjSprite); } }); + } + /** + * This edits a container to represent a loss from a defeat by a trainer battle. + * The trainers are placed to the left of their party. + * Depending on the trainer icon, there may be overlap between the edges of the box or their party. (Capes...) + * + * Party Pokemon have their icons, terastalization status, and level shown. + * @param enemyContainer - container holding enemy visuals and level information + */ + private parseTrainerDefeat(enemyContainer: Phaser.GameObjects.Container) { + // Loads and adds trainer sprites to the UI + this.showTrainerSprites(enemyContainer); // Determining which Terastallize Modifier belongs to which Pokemon // Creates a dictionary {PokemonId: TeraShardType} const teraPokemon = {}; diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index 89b20322a68..2e664db8d43 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -10,8 +10,10 @@ import MessageUiHandler from "./message-ui-handler"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import { addWindow } from "./ui-theme"; +import { RunDisplayMode } from "#app/ui/run-info-ui-handler"; -const sessionSlotCount = 5; +const SESSION_SLOTS_COUNT = 5; +const SLOTS_ON_SCREEN = 3; export enum SaveSlotUiMode { LOAD, @@ -33,7 +35,7 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { private scrollCursor: integer = 0; - private cursorObj: Phaser.GameObjects.NineSlice | null; + private cursorObj: Phaser.GameObjects.Container | null; private sessionSlotsContainerInitialY: number; @@ -84,9 +86,9 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { this.saveSlotSelectContainer.setVisible(true); this.populateSessionSlots(); + this.setScrollCursor(0); this.setCursor(0); - return true; } @@ -147,21 +149,28 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { success = true; } } else { + const cursorPosition = this.cursor + this.scrollCursor; switch (button) { case Button.UP: if (this.cursor) { - success = this.setCursor(this.cursor - 1); + // Check to prevent cursor from accessing a negative index + success = (this.cursor === 0) ? this.setCursor(this.cursor) : this.setCursor(this.cursor - 1, cursorPosition); } else if (this.scrollCursor) { - success = this.setScrollCursor(this.scrollCursor - 1); + success = this.setScrollCursor(this.scrollCursor - 1, cursorPosition); } break; case Button.DOWN: - if (this.cursor < 2) { - success = this.setCursor(this.cursor + 1); - } else if (this.scrollCursor < sessionSlotCount - 3) { - success = this.setScrollCursor(this.scrollCursor + 1); + if (this.cursor < (SLOTS_ON_SCREEN - 1)) { + success = this.setCursor(this.cursor + 1, cursorPosition); + } else if (this.scrollCursor < SESSION_SLOTS_COUNT - SLOTS_ON_SCREEN) { + success = this.setScrollCursor(this.scrollCursor + 1, cursorPosition); } break; + case Button.RIGHT: + if (this.sessionSlots[cursorPosition].hasData && this.sessionSlots[cursorPosition].saveData) { + this.scene.ui.setOverlayMode(Mode.RUN_INFO, this.sessionSlots[cursorPosition].saveData, RunDisplayMode.SESSION_PREVIEW); + success = true; + } } } @@ -175,12 +184,18 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { } populateSessionSlots() { - for (let s = 0; s < sessionSlotCount; s++) { + for (let s = 0; s < SESSION_SLOTS_COUNT; s++) { const sessionSlot = new SessionSlot(this.scene, s); - sessionSlot.load(); this.scene.add.existing(sessionSlot); this.sessionSlotsContainer.add(sessionSlot); this.sessionSlots.push(sessionSlot); + sessionSlot.load().then((success) => { + // If the cursor was moved to this slot while the session was loading + // call setCursor again to shift the slot position and show the arrow for save preview + if (success && (this.cursor + this.scrollCursor) === s) { + this.setCursor(s); + } + }); } } @@ -198,25 +213,79 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { this.saveSlotSelectMessageBoxContainer.setVisible(!!text?.length); } - setCursor(cursor: integer): boolean { + /** + * Move the cursor to a new position and update the view accordingly + * @param cursor the new cursor position, between `0` and `SLOTS_ON_SCREEN - 1` + * @param prevSlotIndex index of the previous session occupied by the cursor, between `0` and `SESSION_SLOTS_COUNT - 1` - optional + * @returns `true` if the cursor position has changed | `false` if it has not + */ + override setCursor(cursor: integer, prevSlotIndex?: integer): boolean { const changed = super.setCursor(cursor); if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight_thick", undefined, 296, 44, 6, 6, 6, 6); - this.cursorObj.setOrigin(0, 0); + this.cursorObj = this.scene.add.container(0, 0); + const cursorBox = this.scene.add.nineslice(0, 0, "select_cursor_highlight_thick", undefined, 296, 44, 6, 6, 6, 6); + const rightArrow = this.scene.add.image(0, 0, "cursor"); + rightArrow.setPosition(160, 0); + rightArrow.setName("rightArrow"); + this.cursorObj.add([ cursorBox, rightArrow ]); this.sessionSlotsContainer.add(this.cursorObj); } - this.cursorObj.setPosition(4, 4 + (cursor + this.scrollCursor) * 56); + const cursorPosition = cursor + this.scrollCursor; + const cursorIncrement = cursorPosition * 56; + if (this.sessionSlots[cursorPosition] && this.cursorObj) { + const hasData = this.sessionSlots[cursorPosition].hasData; + // If the session slot lacks session data, it does not move from its default, central position. + // Only session slots with session data will move leftwards and have a visible arrow. + if (!hasData) { + this.cursorObj.setPosition(151, 26 + cursorIncrement); + this.sessionSlots[cursorPosition].setPosition(0, cursorIncrement); + } else { + this.cursorObj.setPosition(145, 26 + cursorIncrement); + this.sessionSlots[cursorPosition].setPosition(-6, cursorIncrement); + } + this.setArrowVisibility(hasData); + } + if (!Utils.isNullOrUndefined(prevSlotIndex)) { + this.revertSessionSlot(prevSlotIndex); + } return changed; } - setScrollCursor(scrollCursor: integer): boolean { + /** + * Helper function that resets the given session slot to its default central position + */ + revertSessionSlot(slotIndex: integer): void { + const sessionSlot = this.sessionSlots[slotIndex]; + if (sessionSlot) { + sessionSlot.setPosition(0, slotIndex * 56); + } + } + + /** + * Helper function that checks if the session slot involved holds data or not + * @param hasData `true` if session slot contains data | 'false' if not + */ + setArrowVisibility(hasData: boolean): void { + if (this.cursorObj) { + const rightArrow = this.cursorObj?.getByName("rightArrow") as Phaser.GameObjects.Image; + rightArrow.setVisible(hasData); + } + } + + /** + * Move the scrolling cursor to a new position and update the view accordingly + * @param scrollCursor the new cursor position, between `0` and `SESSION_SLOTS_COUNT - SLOTS_ON_SCREEN` + * @param prevSlotIndex index of the previous slot occupied by the cursor, between `0` and `SESSION_SLOTS_COUNT-1` - optional + * @returns `true` if the cursor position has changed | `false` if it has not + */ + setScrollCursor(scrollCursor: integer, prevSlotIndex?: integer): boolean { const changed = scrollCursor !== this.scrollCursor; if (changed) { this.scrollCursor = scrollCursor; - this.setCursor(this.cursor); + this.setCursor(this.cursor, prevSlotIndex); this.scene.tweens.add({ targets: this.sessionSlotsContainer, y: this.sessionSlotsContainerInitialY - 56 * scrollCursor, @@ -231,6 +300,7 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { clear() { super.clear(); this.saveSlotSelectContainer.setVisible(false); + this.setScrollCursor(0); this.eraseCursor(); this.saveSlotSelectCallback = null; this.clearSessionSlots(); @@ -254,6 +324,8 @@ class SessionSlot extends Phaser.GameObjects.Container { public hasData: boolean; private loadingLabel: Phaser.GameObjects.Text; + public saveData: SessionSaveData; + constructor(scene: BattleScene, slotId: integer) { super(scene, 0, slotId * 56); @@ -330,6 +402,10 @@ class SessionSlot extends Phaser.GameObjects.Container { load(): Promise { return new Promise(resolve => { this.scene.gameData.getSession(this.slotId).then(async sessionData => { + // Ignore the results if the view was exited + if (!this.active) { + return; + } if (!sessionData) { this.hasData = false; this.loadingLabel.setText(i18next.t("saveSlotSelectUiHandler:empty")); @@ -337,6 +413,7 @@ class SessionSlot extends Phaser.GameObjects.Container { return; } this.hasData = true; + this.saveData = sessionData; await this.setupWithData(sessionData); resolve(true); }); diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 5bbe765947e..2be89052bc1 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -2963,8 +2963,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.natureCursor = -1; if (this.activeTooltip === "CANDY") { - const { currentFriendship, friendshipCap } = this.getFriendship(this.lastSpecies.speciesId); - this.scene.ui.editTooltip("", `${currentFriendship}/${friendshipCap}`); + if (this.lastSpecies) { + const { currentFriendship, friendshipCap } = this.getFriendship(this.lastSpecies.speciesId); + this.scene.ui.editTooltip("", `${currentFriendship}/${friendshipCap}`); + } else { + this.scene.ui.hideTooltip(); + } } if (species?.forms?.find(f => f.formKey === "female")) { @@ -3655,6 +3659,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler { StarterPrefs.save(this.starterPreferences); this.cursor = -1; this.hideInstructions(); + this.activeTooltip = undefined; + this.scene.ui.hideTooltip(); + this.starterSelectContainer.setVisible(false); this.blockInput = false; diff --git a/src/ui/text.ts b/src/ui/text.ts index e6e1978118b..22dd3f4cd6a 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -356,11 +356,11 @@ export function getEggTierTextTint(tier: EggTier): integer { switch (tier) { case EggTier.COMMON: return getModifierTierTextTint(ModifierTier.COMMON); - case EggTier.GREAT: + case EggTier.RARE: return getModifierTierTextTint(ModifierTier.GREAT); - case EggTier.ULTRA: + case EggTier.EPIC: return getModifierTierTextTint(ModifierTier.ULTRA); - case EggTier.MASTER: + case EggTier.LEGENDARY: return getModifierTierTextTint(ModifierTier.MASTER); } } diff --git a/src/utils.ts b/src/utils.ts index 9cc95b00826..c2ee7100909 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -38,10 +38,6 @@ export function shiftCharCodes(str: string, shiftCount: integer) { return newStr; } -export function clampInt(value: integer, min: integer, max: integer): integer { - return Math.min(Math.max(value, min), max); -} - export function randGauss(stdev: number, mean: number = 0): number { if (!stdev) { return 0;