diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000000..180c7af1240 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: patapancakes diff --git a/public/fonts/PokePT_Wansung.woff2 b/public/fonts/PokePT_Wansung.woff2 index 02b299ef7a0..15aab3eb4f4 100644 Binary files a/public/fonts/PokePT_Wansung.woff2 and b/public/fonts/PokePT_Wansung.woff2 differ diff --git a/public/images/events/august-variant-update.png b/public/images/events/august-variant-update.png index 58d29adbcfc..1c78aa2fe14 100644 Binary files a/public/images/events/august-variant-update.png and b/public/images/events/august-variant-update.png differ diff --git a/public/images/pokemon/867.png b/public/images/pokemon/867.png index bd07a7a4837..2fe8856d74d 100644 Binary files a/public/images/pokemon/867.png and b/public/images/pokemon/867.png differ diff --git a/public/images/pokemon/back/821.png b/public/images/pokemon/back/821.png index a102a0491de..24e0edd705d 100644 Binary files a/public/images/pokemon/back/821.png and b/public/images/pokemon/back/821.png differ diff --git a/public/images/pokemon/back/867.png b/public/images/pokemon/back/867.png index 925002d7483..ae631607ef2 100644 Binary files a/public/images/pokemon/back/867.png and b/public/images/pokemon/back/867.png differ diff --git a/public/images/pokemon/back/female/178.png b/public/images/pokemon/back/female/178.png index b6b1aa908d8..bf4e727088f 100644 Binary files a/public/images/pokemon/back/female/178.png and b/public/images/pokemon/back/female/178.png differ diff --git a/public/images/pokemon/back/female/457.png b/public/images/pokemon/back/female/457.png index 85268448129..04e1ae12ff4 100644 Binary files a/public/images/pokemon/back/female/457.png and b/public/images/pokemon/back/female/457.png differ diff --git a/public/images/pokemon/back/shiny/821.png b/public/images/pokemon/back/shiny/821.png index 200489518c4..6d29988ed9f 100644 Binary files a/public/images/pokemon/back/shiny/821.png and b/public/images/pokemon/back/shiny/821.png differ diff --git a/public/images/pokemon/back/shiny/867.png b/public/images/pokemon/back/shiny/867.png index fce3fe4e37e..accb87844f1 100644 Binary files a/public/images/pokemon/back/shiny/867.png and b/public/images/pokemon/back/shiny/867.png differ diff --git a/public/images/pokemon/back/shiny/female/178.png b/public/images/pokemon/back/shiny/female/178.png index e82027f2da8..7978ec22e7f 100644 Binary files a/public/images/pokemon/back/shiny/female/178.png and b/public/images/pokemon/back/shiny/female/178.png differ diff --git a/public/images/pokemon/exp/670-blue.json b/public/images/pokemon/exp/670-blue.json index 95b040e8302..ba237724cbc 100644 --- a/public/images/pokemon/exp/670-blue.json +++ b/public/images/pokemon/exp/670-blue.json @@ -4,135 +4,156 @@ "image": "670-blue.png", "format": "RGBA8888", "size": { - "w": 131, - "h": 131 + "w": 491, + "h": 491 }, "scale": 1, "frames": [ { - "filename": "0002.png", + "filename": "0101.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 4, + "y": 4, + "w": 60, + "h": 65 }, "frame": { "x": 0, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 65 } }, { - "filename": "0006.png", + "filename": "0083.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 3, + "y": 9, + "w": 60, + "h": 67 }, "frame": { - "x": 0, + "x": 60, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 67 } }, { - "filename": "0012.png", + "filename": "0102.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 4, + "y": 5, + "w": 60, + "h": 67 }, "frame": { - "x": 0, + "x": 120, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 67 } }, { - "filename": "0001.png", + "filename": "0082.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, + "x": 4, + "y": 10, + "w": 61, + "h": 68 + }, + "frame": { + "x": 180, + "y": 0, + "w": 61, + "h": 68 + } + }, + { + "filename": "0100.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, "y": 2, - "w": 40, - "h": 65 + "w": 60, + "h": 68 }, "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 65 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 2, - "w": 40, - "h": 65 - }, - "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 65 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, - "frame": { - "x": 40, + "x": 241, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 68 + } + }, + { + "filename": "0081.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 12, + "w": 62, + "h": 69 + }, + "frame": { + "x": 301, + "y": 0, + "w": 62, + "h": 69 + } + }, + { + "filename": "0084.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 56, + "h": 69 + }, + "frame": { + "x": 363, + "y": 0, + "w": 56, + "h": 69 } }, { @@ -140,62 +161,146 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 38, - "h": 65 + "x": 4, + "y": 11, + "w": 62, + "h": 70 }, "frame": { - "x": 40, - "y": 66, - "w": 38, - "h": 65 + "x": 419, + "y": 0, + "w": 62, + "h": 70 } }, { - "filename": "0010.png", + "filename": "0015.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 38, - "h": 65 + "x": 4, + "y": 11, + "w": 62, + "h": 70 }, "frame": { - "x": 40, - "y": 66, - "w": 38, - "h": 65 + "x": 419, + "y": 0, + "w": 62, + "h": 70 } }, { - "filename": "0003.png", + "filename": "0026.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 11, + "w": 62, + "h": 70 }, "frame": { - "x": 80, + "x": 419, "y": 0, - "w": 39, - "h": 66 + "w": 62, + "h": 70 + } + }, + { + "filename": "0037.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0048.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0059.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0070.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 } }, { @@ -203,41 +308,1259 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 8, + "w": 59, + "h": 70 }, "frame": { - "x": 80, - "y": 0, - "w": 39, - "h": 66 + "x": 0, + "y": 65, + "w": 59, + "h": 70 } }, { - "filename": "0009.png", + "filename": "0016.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0038.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0049.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0060.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0071.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0085.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 56, + "h": 70 + }, + "frame": { + "x": 59, + "y": 67, + "w": 56, + "h": 70 + } + }, + { + "filename": "0099.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 57, + "h": 70 + }, + "frame": { + "x": 115, + "y": 67, + "w": 57, + "h": 70 + } + }, + { + "filename": "0103.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 57, + "h": 70 + }, + "frame": { + "x": 172, + "y": 68, + "w": 57, + "h": 70 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0036.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0047.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0058.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0069.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0080.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 292, + "y": 69, + "w": 63, + "h": 71 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0046.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0057.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0068.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0079.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 418, + "y": 70, + "w": 63, + "h": 72 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0039.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0050.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0061.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0072.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 53, + "y": 137, + "w": 61, + "h": 73 + } + }, + { + "filename": "0098.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 54, + "h": 73 + }, + "frame": { + "x": 114, + "y": 137, + "w": 54, + "h": 73 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0045.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0056.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0067.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0078.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 229, + "y": 139, + "w": 61, + "h": 73 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0040.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0051.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0062.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0073.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0104.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 52, + "h": 74 + }, + "frame": { + "x": 339, + "y": 141, + "w": 52, + "h": 74 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0041.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0052.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0063.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0074.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0114.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 50, + "h": 75 + }, + "frame": { + "x": 438, + "y": 142, + "w": 50, + "h": 75 + } + }, + { + "filename": "0086.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 39, - "h": 66 + "y": 4, + "w": 51, + "h": 76 }, "frame": { - "x": 80, - "y": 0, - "w": 39, - "h": 66 + "x": 0, + "y": 208, + "w": 51, + "h": 76 + } + }, + { + "filename": "0119.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 11, + "w": 57, + "h": 75 + }, + "frame": { + "x": 51, + "y": 210, + "w": 57, + "h": 75 } }, { @@ -245,20 +1568,944 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0044.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0055.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0066.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0077.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0115.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 163, + "y": 211, + "w": 51, + "h": 76 + } + }, + { + "filename": "0116.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 214, + "y": 212, + "w": 51, + "h": 76 + } + }, + { + "filename": "0117.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 53, + "h": 76 + }, + "frame": { + "x": 265, + "y": 214, + "w": 53, + "h": 76 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0042.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0053.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0064.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0075.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0043.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0054.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0065.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0076.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0095.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 3, + "w": 51, + "h": 77 + }, + "frame": { + "x": 419, + "y": 217, + "w": 51, + "h": 77 + } + }, + { + "filename": "0113.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 9, + "w": 47, + "h": 77 + }, + "frame": { + "x": 0, + "y": 284, + "w": 47, + "h": 77 + } + }, + { + "filename": "0096.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 2, + "w": 54, + "h": 77 + }, + "frame": { + "x": 47, + "y": 285, + "w": 54, + "h": 77 + } + }, + { + "filename": "0097.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 55, + "h": 77 + }, + "frame": { + "x": 101, + "y": 286, + "w": 55, + "h": 77 + } + }, + { + "filename": "0118.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 53, + "h": 77 + }, + "frame": { + "x": 156, + "y": 287, + "w": 53, + "h": 77 + } + }, + { + "filename": "0094.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 50, + "h": 78 + }, + "frame": { + "x": 209, + "y": 288, + "w": 50, + "h": 78 + } + }, + { + "filename": "0105.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 0, + "w": 48, + "h": 78 + }, + "frame": { + "x": 259, + "y": 290, + "w": 48, + "h": 78 + } + }, + { + "filename": "0112.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 47, + "h": 78 + }, + "frame": { + "x": 307, + "y": 292, + "w": 47, + "h": 78 + } + }, + { + "filename": "0087.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 39, - "h": 66 + "y": 3, + "w": 45, + "h": 79 }, "frame": { - "x": 80, - "y": 0, - "w": 39, - "h": 66 + "x": 354, + "y": 294, + "w": 45, + "h": 79 + } + }, + { + "filename": "0093.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 49, + "h": 79 + }, + "frame": { + "x": 399, + "y": 294, + "w": 49, + "h": 79 + } + }, + { + "filename": "0088.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 4, + "w": 43, + "h": 82 + }, + "frame": { + "x": 448, + "y": 294, + "w": 43, + "h": 82 + } + }, + { + "filename": "0089.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 6, + "w": 44, + "h": 80 + }, + "frame": { + "x": 0, + "y": 361, + "w": 44, + "h": 80 + } + }, + { + "filename": "0106.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 2, + "w": 48, + "h": 79 + }, + "frame": { + "x": 44, + "y": 362, + "w": 48, + "h": 79 + } + }, + { + "filename": "0107.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 3, + "w": 48, + "h": 79 + }, + "frame": { + "x": 92, + "y": 363, + "w": 48, + "h": 79 + } + }, + { + "filename": "0110.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 48, + "h": 79 + }, + "frame": { + "x": 140, + "y": 364, + "w": 48, + "h": 79 + } + }, + { + "filename": "0111.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 48, + "h": 79 + }, + "frame": { + "x": 188, + "y": 366, + "w": 48, + "h": 79 + } + }, + { + "filename": "0090.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 46, + "h": 80 + }, + "frame": { + "x": 236, + "y": 368, + "w": 46, + "h": 80 + } + }, + { + "filename": "0091.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 47, + "h": 80 + }, + "frame": { + "x": 282, + "y": 370, + "w": 47, + "h": 80 + } + }, + { + "filename": "0108.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 4, + "w": 50, + "h": 80 + }, + "frame": { + "x": 329, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0109.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 50, + "h": 80 + }, + "frame": { + "x": 379, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0092.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 48, + "h": 81 + }, + "frame": { + "x": 429, + "y": 376, + "w": 48, + "h": 81 } } ] @@ -266,7 +2513,6 @@ ], "meta": { "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:6da8d1608a9c65309f0d1306470072d7:4ca05025221d73b733ddbeb890226940:f28e65cb64de91b1773f7b4998e488a5$" + "version": "3.0" } } diff --git a/public/images/pokemon/exp/670-orange.json b/public/images/pokemon/exp/670-orange.json index e43c9fb689e..ded85695da2 100644 --- a/public/images/pokemon/exp/670-orange.json +++ b/public/images/pokemon/exp/670-orange.json @@ -4,135 +4,156 @@ "image": "670-orange.png", "format": "RGBA8888", "size": { - "w": 131, - "h": 131 + "w": 491, + "h": 491 }, "scale": 1, "frames": [ { - "filename": "0002.png", + "filename": "0101.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 4, + "y": 4, + "w": 60, + "h": 65 }, "frame": { "x": 0, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 65 } }, { - "filename": "0006.png", + "filename": "0083.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 3, + "y": 9, + "w": 60, + "h": 67 }, "frame": { - "x": 0, + "x": 60, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 67 } }, { - "filename": "0012.png", + "filename": "0102.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 4, + "y": 5, + "w": 60, + "h": 67 }, "frame": { - "x": 0, + "x": 120, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 67 } }, { - "filename": "0001.png", + "filename": "0082.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, + "x": 4, + "y": 10, + "w": 61, + "h": 68 + }, + "frame": { + "x": 180, + "y": 0, + "w": 61, + "h": 68 + } + }, + { + "filename": "0100.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, "y": 2, - "w": 40, - "h": 65 + "w": 60, + "h": 68 }, "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 65 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 2, - "w": 40, - "h": 65 - }, - "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 65 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, - "frame": { - "x": 40, + "x": 241, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 68 + } + }, + { + "filename": "0081.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 12, + "w": 62, + "h": 69 + }, + "frame": { + "x": 301, + "y": 0, + "w": 62, + "h": 69 + } + }, + { + "filename": "0084.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 56, + "h": 69 + }, + "frame": { + "x": 363, + "y": 0, + "w": 56, + "h": 69 } }, { @@ -140,62 +161,146 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 38, - "h": 65 + "x": 4, + "y": 11, + "w": 62, + "h": 70 }, "frame": { - "x": 40, - "y": 66, - "w": 38, - "h": 65 + "x": 419, + "y": 0, + "w": 62, + "h": 70 } }, { - "filename": "0010.png", + "filename": "0015.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 38, - "h": 65 + "x": 4, + "y": 11, + "w": 62, + "h": 70 }, "frame": { - "x": 40, - "y": 66, - "w": 38, - "h": 65 + "x": 419, + "y": 0, + "w": 62, + "h": 70 } }, { - "filename": "0003.png", + "filename": "0026.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 11, + "w": 62, + "h": 70 }, "frame": { - "x": 80, + "x": 419, "y": 0, - "w": 39, - "h": 66 + "w": 62, + "h": 70 + } + }, + { + "filename": "0037.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0048.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0059.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0070.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 } }, { @@ -203,41 +308,1259 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 8, + "w": 59, + "h": 70 }, "frame": { - "x": 80, - "y": 0, - "w": 39, - "h": 66 + "x": 0, + "y": 65, + "w": 59, + "h": 70 } }, { - "filename": "0009.png", + "filename": "0016.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0038.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0049.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0060.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0071.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0085.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 56, + "h": 70 + }, + "frame": { + "x": 59, + "y": 67, + "w": 56, + "h": 70 + } + }, + { + "filename": "0099.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 57, + "h": 70 + }, + "frame": { + "x": 115, + "y": 67, + "w": 57, + "h": 70 + } + }, + { + "filename": "0103.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 57, + "h": 70 + }, + "frame": { + "x": 172, + "y": 68, + "w": 57, + "h": 70 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0036.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0047.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0058.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0069.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0080.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 292, + "y": 69, + "w": 63, + "h": 71 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0046.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0057.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0068.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0079.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 418, + "y": 70, + "w": 63, + "h": 72 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0039.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0050.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0061.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0072.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 53, + "y": 137, + "w": 61, + "h": 73 + } + }, + { + "filename": "0098.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 54, + "h": 73 + }, + "frame": { + "x": 114, + "y": 137, + "w": 54, + "h": 73 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0045.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0056.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0067.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0078.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 229, + "y": 139, + "w": 61, + "h": 73 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0040.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0051.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0062.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0073.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0104.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 52, + "h": 74 + }, + "frame": { + "x": 339, + "y": 141, + "w": 52, + "h": 74 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0041.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0052.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0063.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0074.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0114.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 50, + "h": 75 + }, + "frame": { + "x": 438, + "y": 142, + "w": 50, + "h": 75 + } + }, + { + "filename": "0086.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 39, - "h": 66 + "y": 4, + "w": 51, + "h": 76 }, "frame": { - "x": 80, - "y": 0, - "w": 39, - "h": 66 + "x": 0, + "y": 208, + "w": 51, + "h": 76 + } + }, + { + "filename": "0119.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 11, + "w": 57, + "h": 75 + }, + "frame": { + "x": 51, + "y": 210, + "w": 57, + "h": 75 } }, { @@ -245,20 +1568,944 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0044.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0055.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0066.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0077.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0115.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 163, + "y": 211, + "w": 51, + "h": 76 + } + }, + { + "filename": "0116.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 214, + "y": 212, + "w": 51, + "h": 76 + } + }, + { + "filename": "0117.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 53, + "h": 76 + }, + "frame": { + "x": 265, + "y": 214, + "w": 53, + "h": 76 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0042.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0053.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0064.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0075.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0043.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0054.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0065.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0076.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0095.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 3, + "w": 51, + "h": 77 + }, + "frame": { + "x": 419, + "y": 217, + "w": 51, + "h": 77 + } + }, + { + "filename": "0113.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 9, + "w": 47, + "h": 77 + }, + "frame": { + "x": 0, + "y": 284, + "w": 47, + "h": 77 + } + }, + { + "filename": "0096.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 2, + "w": 54, + "h": 77 + }, + "frame": { + "x": 47, + "y": 285, + "w": 54, + "h": 77 + } + }, + { + "filename": "0097.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 55, + "h": 77 + }, + "frame": { + "x": 101, + "y": 286, + "w": 55, + "h": 77 + } + }, + { + "filename": "0118.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 53, + "h": 77 + }, + "frame": { + "x": 156, + "y": 287, + "w": 53, + "h": 77 + } + }, + { + "filename": "0094.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 50, + "h": 78 + }, + "frame": { + "x": 209, + "y": 288, + "w": 50, + "h": 78 + } + }, + { + "filename": "0105.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 0, + "w": 48, + "h": 78 + }, + "frame": { + "x": 259, + "y": 290, + "w": 48, + "h": 78 + } + }, + { + "filename": "0112.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 47, + "h": 78 + }, + "frame": { + "x": 307, + "y": 292, + "w": 47, + "h": 78 + } + }, + { + "filename": "0087.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 39, - "h": 66 + "y": 3, + "w": 45, + "h": 79 }, "frame": { - "x": 80, - "y": 0, - "w": 39, - "h": 66 + "x": 354, + "y": 294, + "w": 45, + "h": 79 + } + }, + { + "filename": "0093.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 49, + "h": 79 + }, + "frame": { + "x": 399, + "y": 294, + "w": 49, + "h": 79 + } + }, + { + "filename": "0088.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 4, + "w": 43, + "h": 82 + }, + "frame": { + "x": 448, + "y": 294, + "w": 43, + "h": 82 + } + }, + { + "filename": "0089.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 6, + "w": 44, + "h": 80 + }, + "frame": { + "x": 0, + "y": 361, + "w": 44, + "h": 80 + } + }, + { + "filename": "0106.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 2, + "w": 48, + "h": 79 + }, + "frame": { + "x": 44, + "y": 362, + "w": 48, + "h": 79 + } + }, + { + "filename": "0107.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 3, + "w": 48, + "h": 79 + }, + "frame": { + "x": 92, + "y": 363, + "w": 48, + "h": 79 + } + }, + { + "filename": "0110.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 48, + "h": 79 + }, + "frame": { + "x": 140, + "y": 364, + "w": 48, + "h": 79 + } + }, + { + "filename": "0111.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 48, + "h": 79 + }, + "frame": { + "x": 188, + "y": 366, + "w": 48, + "h": 79 + } + }, + { + "filename": "0090.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 46, + "h": 80 + }, + "frame": { + "x": 236, + "y": 368, + "w": 46, + "h": 80 + } + }, + { + "filename": "0091.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 47, + "h": 80 + }, + "frame": { + "x": 282, + "y": 370, + "w": 47, + "h": 80 + } + }, + { + "filename": "0108.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 4, + "w": 50, + "h": 80 + }, + "frame": { + "x": 329, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0109.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 50, + "h": 80 + }, + "frame": { + "x": 379, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0092.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 48, + "h": 81 + }, + "frame": { + "x": 429, + "y": 376, + "w": 48, + "h": 81 } } ] @@ -266,7 +2513,6 @@ ], "meta": { "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:f5cb262b81e50f1216f7c96b87cee008:1e93586061e47c799fccb4b2e32678a7:a4ae1870548474db9c466580d9a207bb$" + "version": "3.0" } } diff --git a/public/images/pokemon/exp/670-red.json b/public/images/pokemon/exp/670-red.json index d5879f024af..098b0efca76 100644 --- a/public/images/pokemon/exp/670-red.json +++ b/public/images/pokemon/exp/670-red.json @@ -2513,7 +2513,6 @@ ], "meta": { "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:edb2df3a947401efb05329a2c96d5d73:f256d83ef4df17c17958acc6e0432ab0:bad05b37c157676604256a043511a6a2$" + "version": "3.0" } } diff --git a/public/images/pokemon/exp/670-white.json b/public/images/pokemon/exp/670-white.json index 3a30277b875..521ca042bf2 100644 --- a/public/images/pokemon/exp/670-white.json +++ b/public/images/pokemon/exp/670-white.json @@ -4,135 +4,156 @@ "image": "670-white.png", "format": "RGBA8888", "size": { - "w": 131, - "h": 131 + "w": 491, + "h": 491 }, "scale": 1, "frames": [ { - "filename": "0002.png", + "filename": "0101.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 4, + "y": 4, + "w": 60, + "h": 65 }, "frame": { "x": 0, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 65 } }, { - "filename": "0006.png", + "filename": "0083.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 3, + "y": 9, + "w": 60, + "h": 67 }, "frame": { - "x": 0, + "x": 60, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 67 } }, { - "filename": "0012.png", + "filename": "0102.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 4, + "y": 5, + "w": 60, + "h": 67 }, "frame": { - "x": 0, + "x": 120, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 67 } }, { - "filename": "0001.png", + "filename": "0082.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, + "x": 4, + "y": 10, + "w": 61, + "h": 68 + }, + "frame": { + "x": 180, + "y": 0, + "w": 61, + "h": 68 + } + }, + { + "filename": "0100.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, "y": 2, - "w": 40, - "h": 65 + "w": 60, + "h": 68 }, "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 65 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 2, - "w": 40, - "h": 65 - }, - "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 65 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, - "frame": { - "x": 40, + "x": 241, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 68 + } + }, + { + "filename": "0081.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 12, + "w": 62, + "h": 69 + }, + "frame": { + "x": 301, + "y": 0, + "w": 62, + "h": 69 + } + }, + { + "filename": "0084.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 56, + "h": 69 + }, + "frame": { + "x": 363, + "y": 0, + "w": 56, + "h": 69 } }, { @@ -140,62 +161,146 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 38, - "h": 65 + "x": 4, + "y": 11, + "w": 62, + "h": 70 }, "frame": { - "x": 40, - "y": 66, - "w": 38, - "h": 65 + "x": 419, + "y": 0, + "w": 62, + "h": 70 } }, { - "filename": "0010.png", + "filename": "0015.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 38, - "h": 65 + "x": 4, + "y": 11, + "w": 62, + "h": 70 }, "frame": { - "x": 40, - "y": 66, - "w": 38, - "h": 65 + "x": 419, + "y": 0, + "w": 62, + "h": 70 } }, { - "filename": "0003.png", + "filename": "0026.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 11, + "w": 62, + "h": 70 }, "frame": { - "x": 80, + "x": 419, "y": 0, - "w": 39, - "h": 66 + "w": 62, + "h": 70 + } + }, + { + "filename": "0037.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0048.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0059.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0070.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 } }, { @@ -203,41 +308,1259 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 8, + "w": 59, + "h": 70 }, "frame": { - "x": 80, - "y": 0, - "w": 39, - "h": 66 + "x": 0, + "y": 65, + "w": 59, + "h": 70 } }, { - "filename": "0009.png", + "filename": "0016.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0038.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0049.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0060.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0071.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0085.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 56, + "h": 70 + }, + "frame": { + "x": 59, + "y": 67, + "w": 56, + "h": 70 + } + }, + { + "filename": "0099.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 57, + "h": 70 + }, + "frame": { + "x": 115, + "y": 67, + "w": 57, + "h": 70 + } + }, + { + "filename": "0103.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 57, + "h": 70 + }, + "frame": { + "x": 172, + "y": 68, + "w": 57, + "h": 70 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0036.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0047.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0058.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0069.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0080.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 292, + "y": 69, + "w": 63, + "h": 71 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0046.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0057.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0068.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0079.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 418, + "y": 70, + "w": 63, + "h": 72 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0039.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0050.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0061.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0072.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 53, + "y": 137, + "w": 61, + "h": 73 + } + }, + { + "filename": "0098.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 54, + "h": 73 + }, + "frame": { + "x": 114, + "y": 137, + "w": 54, + "h": 73 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0045.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0056.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0067.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0078.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 229, + "y": 139, + "w": 61, + "h": 73 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0040.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0051.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0062.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0073.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0104.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 52, + "h": 74 + }, + "frame": { + "x": 339, + "y": 141, + "w": 52, + "h": 74 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0041.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0052.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0063.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0074.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0114.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 50, + "h": 75 + }, + "frame": { + "x": 438, + "y": 142, + "w": 50, + "h": 75 + } + }, + { + "filename": "0086.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 39, - "h": 66 + "y": 4, + "w": 51, + "h": 76 }, "frame": { - "x": 80, - "y": 0, - "w": 39, - "h": 66 + "x": 0, + "y": 208, + "w": 51, + "h": 76 + } + }, + { + "filename": "0119.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 11, + "w": 57, + "h": 75 + }, + "frame": { + "x": 51, + "y": 210, + "w": 57, + "h": 75 } }, { @@ -245,20 +1568,944 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0044.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0055.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0066.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0077.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0115.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 163, + "y": 211, + "w": 51, + "h": 76 + } + }, + { + "filename": "0116.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 214, + "y": 212, + "w": 51, + "h": 76 + } + }, + { + "filename": "0117.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 53, + "h": 76 + }, + "frame": { + "x": 265, + "y": 214, + "w": 53, + "h": 76 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0042.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0053.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0064.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0075.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0043.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0054.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0065.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0076.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0095.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 3, + "w": 51, + "h": 77 + }, + "frame": { + "x": 419, + "y": 217, + "w": 51, + "h": 77 + } + }, + { + "filename": "0113.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 9, + "w": 47, + "h": 77 + }, + "frame": { + "x": 0, + "y": 284, + "w": 47, + "h": 77 + } + }, + { + "filename": "0096.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 2, + "w": 54, + "h": 77 + }, + "frame": { + "x": 47, + "y": 285, + "w": 54, + "h": 77 + } + }, + { + "filename": "0097.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 55, + "h": 77 + }, + "frame": { + "x": 101, + "y": 286, + "w": 55, + "h": 77 + } + }, + { + "filename": "0118.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 53, + "h": 77 + }, + "frame": { + "x": 156, + "y": 287, + "w": 53, + "h": 77 + } + }, + { + "filename": "0094.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 50, + "h": 78 + }, + "frame": { + "x": 209, + "y": 288, + "w": 50, + "h": 78 + } + }, + { + "filename": "0105.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 0, + "w": 48, + "h": 78 + }, + "frame": { + "x": 259, + "y": 290, + "w": 48, + "h": 78 + } + }, + { + "filename": "0112.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 47, + "h": 78 + }, + "frame": { + "x": 307, + "y": 292, + "w": 47, + "h": 78 + } + }, + { + "filename": "0087.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 39, - "h": 66 + "y": 3, + "w": 45, + "h": 79 }, "frame": { - "x": 80, - "y": 0, - "w": 39, - "h": 66 + "x": 354, + "y": 294, + "w": 45, + "h": 79 + } + }, + { + "filename": "0093.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 49, + "h": 79 + }, + "frame": { + "x": 399, + "y": 294, + "w": 49, + "h": 79 + } + }, + { + "filename": "0088.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 4, + "w": 43, + "h": 82 + }, + "frame": { + "x": 448, + "y": 294, + "w": 43, + "h": 82 + } + }, + { + "filename": "0089.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 6, + "w": 44, + "h": 80 + }, + "frame": { + "x": 0, + "y": 361, + "w": 44, + "h": 80 + } + }, + { + "filename": "0106.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 2, + "w": 48, + "h": 79 + }, + "frame": { + "x": 44, + "y": 362, + "w": 48, + "h": 79 + } + }, + { + "filename": "0107.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 3, + "w": 48, + "h": 79 + }, + "frame": { + "x": 92, + "y": 363, + "w": 48, + "h": 79 + } + }, + { + "filename": "0110.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 48, + "h": 79 + }, + "frame": { + "x": 140, + "y": 364, + "w": 48, + "h": 79 + } + }, + { + "filename": "0111.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 48, + "h": 79 + }, + "frame": { + "x": 188, + "y": 366, + "w": 48, + "h": 79 + } + }, + { + "filename": "0090.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 46, + "h": 80 + }, + "frame": { + "x": 236, + "y": 368, + "w": 46, + "h": 80 + } + }, + { + "filename": "0091.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 47, + "h": 80 + }, + "frame": { + "x": 282, + "y": 370, + "w": 47, + "h": 80 + } + }, + { + "filename": "0108.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 4, + "w": 50, + "h": 80 + }, + "frame": { + "x": 329, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0109.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 50, + "h": 80 + }, + "frame": { + "x": 379, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0092.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 48, + "h": 81 + }, + "frame": { + "x": 429, + "y": 376, + "w": 48, + "h": 81 } } ] @@ -266,7 +2513,6 @@ ], "meta": { "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:c665c1ed8b3570782ceb8fe40f155b3a:43fe4cf13bd16f48a733e1533ed1dc70:3b5fc0bf297b00d7e2adc65825803c41$" + "version": "3.0" } } diff --git a/public/images/pokemon/exp/670-yellow.json b/public/images/pokemon/exp/670-yellow.json index 7b870101a5f..ffd722006d3 100644 --- a/public/images/pokemon/exp/670-yellow.json +++ b/public/images/pokemon/exp/670-yellow.json @@ -4,135 +4,156 @@ "image": "670-yellow.png", "format": "RGBA8888", "size": { - "w": 131, - "h": 131 + "w": 491, + "h": 491 }, "scale": 1, "frames": [ { - "filename": "0002.png", + "filename": "0101.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 4, + "y": 4, + "w": 60, + "h": 65 }, "frame": { "x": 0, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 65 } }, { - "filename": "0006.png", + "filename": "0083.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 3, + "y": 9, + "w": 60, + "h": 67 }, "frame": { - "x": 0, + "x": 60, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 67 } }, { - "filename": "0012.png", + "filename": "0102.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 4, + "y": 5, + "w": 60, + "h": 67 }, "frame": { - "x": 0, + "x": 120, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 67 } }, { - "filename": "0001.png", + "filename": "0082.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, + "x": 4, + "y": 10, + "w": 61, + "h": 68 + }, + "frame": { + "x": 180, + "y": 0, + "w": 61, + "h": 68 + } + }, + { + "filename": "0100.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, "y": 2, - "w": 40, - "h": 65 + "w": 60, + "h": 68 }, "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 65 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 2, - "w": 40, - "h": 65 - }, - "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 65 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, - "frame": { - "x": 40, + "x": 241, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 68 + } + }, + { + "filename": "0081.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 12, + "w": 62, + "h": 69 + }, + "frame": { + "x": 301, + "y": 0, + "w": 62, + "h": 69 + } + }, + { + "filename": "0084.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 56, + "h": 69 + }, + "frame": { + "x": 363, + "y": 0, + "w": 56, + "h": 69 } }, { @@ -140,62 +161,146 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 38, - "h": 65 + "x": 4, + "y": 11, + "w": 62, + "h": 70 }, "frame": { - "x": 40, - "y": 66, - "w": 38, - "h": 65 + "x": 419, + "y": 0, + "w": 62, + "h": 70 } }, { - "filename": "0010.png", + "filename": "0015.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 38, - "h": 65 + "x": 4, + "y": 11, + "w": 62, + "h": 70 }, "frame": { - "x": 40, - "y": 66, - "w": 38, - "h": 65 + "x": 419, + "y": 0, + "w": 62, + "h": 70 } }, { - "filename": "0003.png", + "filename": "0026.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 11, + "w": 62, + "h": 70 }, "frame": { - "x": 80, + "x": 419, "y": 0, - "w": 39, - "h": 66 + "w": 62, + "h": 70 + } + }, + { + "filename": "0037.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0048.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0059.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0070.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 } }, { @@ -203,41 +308,1259 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 8, + "w": 59, + "h": 70 }, "frame": { - "x": 80, - "y": 0, - "w": 39, - "h": 66 + "x": 0, + "y": 65, + "w": 59, + "h": 70 } }, { - "filename": "0009.png", + "filename": "0016.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0038.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0049.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0060.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0071.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0085.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 56, + "h": 70 + }, + "frame": { + "x": 59, + "y": 67, + "w": 56, + "h": 70 + } + }, + { + "filename": "0099.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 57, + "h": 70 + }, + "frame": { + "x": 115, + "y": 67, + "w": 57, + "h": 70 + } + }, + { + "filename": "0103.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 57, + "h": 70 + }, + "frame": { + "x": 172, + "y": 68, + "w": 57, + "h": 70 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0036.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0047.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0058.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0069.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0080.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 292, + "y": 69, + "w": 63, + "h": 71 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0046.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0057.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0068.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0079.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 418, + "y": 70, + "w": 63, + "h": 72 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0039.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0050.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0061.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0072.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 53, + "y": 137, + "w": 61, + "h": 73 + } + }, + { + "filename": "0098.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 54, + "h": 73 + }, + "frame": { + "x": 114, + "y": 137, + "w": 54, + "h": 73 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0045.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0056.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0067.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0078.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 229, + "y": 139, + "w": 61, + "h": 73 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0040.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0051.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0062.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0073.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0104.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 52, + "h": 74 + }, + "frame": { + "x": 339, + "y": 141, + "w": 52, + "h": 74 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0041.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0052.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0063.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0074.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0114.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 50, + "h": 75 + }, + "frame": { + "x": 438, + "y": 142, + "w": 50, + "h": 75 + } + }, + { + "filename": "0086.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 39, - "h": 66 + "y": 4, + "w": 51, + "h": 76 }, "frame": { - "x": 80, - "y": 0, - "w": 39, - "h": 66 + "x": 0, + "y": 208, + "w": 51, + "h": 76 + } + }, + { + "filename": "0119.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 11, + "w": 57, + "h": 75 + }, + "frame": { + "x": 51, + "y": 210, + "w": 57, + "h": 75 } }, { @@ -245,20 +1568,944 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0044.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0055.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0066.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0077.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0115.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 163, + "y": 211, + "w": 51, + "h": 76 + } + }, + { + "filename": "0116.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 214, + "y": 212, + "w": 51, + "h": 76 + } + }, + { + "filename": "0117.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 53, + "h": 76 + }, + "frame": { + "x": 265, + "y": 214, + "w": 53, + "h": 76 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0042.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0053.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0064.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0075.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0043.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0054.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0065.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0076.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0095.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 3, + "w": 51, + "h": 77 + }, + "frame": { + "x": 419, + "y": 217, + "w": 51, + "h": 77 + } + }, + { + "filename": "0113.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 9, + "w": 47, + "h": 77 + }, + "frame": { + "x": 0, + "y": 284, + "w": 47, + "h": 77 + } + }, + { + "filename": "0096.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 2, + "w": 54, + "h": 77 + }, + "frame": { + "x": 47, + "y": 285, + "w": 54, + "h": 77 + } + }, + { + "filename": "0097.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 55, + "h": 77 + }, + "frame": { + "x": 101, + "y": 286, + "w": 55, + "h": 77 + } + }, + { + "filename": "0118.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 53, + "h": 77 + }, + "frame": { + "x": 156, + "y": 287, + "w": 53, + "h": 77 + } + }, + { + "filename": "0094.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 50, + "h": 78 + }, + "frame": { + "x": 209, + "y": 288, + "w": 50, + "h": 78 + } + }, + { + "filename": "0105.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 0, + "w": 48, + "h": 78 + }, + "frame": { + "x": 259, + "y": 290, + "w": 48, + "h": 78 + } + }, + { + "filename": "0112.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 47, + "h": 78 + }, + "frame": { + "x": 307, + "y": 292, + "w": 47, + "h": 78 + } + }, + { + "filename": "0087.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 39, - "h": 66 + "y": 3, + "w": 45, + "h": 79 }, "frame": { - "x": 80, - "y": 0, - "w": 39, - "h": 66 + "x": 354, + "y": 294, + "w": 45, + "h": 79 + } + }, + { + "filename": "0093.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 49, + "h": 79 + }, + "frame": { + "x": 399, + "y": 294, + "w": 49, + "h": 79 + } + }, + { + "filename": "0088.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 4, + "w": 43, + "h": 82 + }, + "frame": { + "x": 448, + "y": 294, + "w": 43, + "h": 82 + } + }, + { + "filename": "0089.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 6, + "w": 44, + "h": 80 + }, + "frame": { + "x": 0, + "y": 361, + "w": 44, + "h": 80 + } + }, + { + "filename": "0106.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 2, + "w": 48, + "h": 79 + }, + "frame": { + "x": 44, + "y": 362, + "w": 48, + "h": 79 + } + }, + { + "filename": "0107.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 3, + "w": 48, + "h": 79 + }, + "frame": { + "x": 92, + "y": 363, + "w": 48, + "h": 79 + } + }, + { + "filename": "0110.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 48, + "h": 79 + }, + "frame": { + "x": 140, + "y": 364, + "w": 48, + "h": 79 + } + }, + { + "filename": "0111.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 48, + "h": 79 + }, + "frame": { + "x": 188, + "y": 366, + "w": 48, + "h": 79 + } + }, + { + "filename": "0090.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 46, + "h": 80 + }, + "frame": { + "x": 236, + "y": 368, + "w": 46, + "h": 80 + } + }, + { + "filename": "0091.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 47, + "h": 80 + }, + "frame": { + "x": 282, + "y": 370, + "w": 47, + "h": 80 + } + }, + { + "filename": "0108.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 4, + "w": 50, + "h": 80 + }, + "frame": { + "x": 329, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0109.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 50, + "h": 80 + }, + "frame": { + "x": 379, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0092.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 48, + "h": 81 + }, + "frame": { + "x": 429, + "y": 376, + "w": 48, + "h": 81 } } ] @@ -266,7 +2513,6 @@ ], "meta": { "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:fa4d73da9ed13167c827701785bfb172:cffbc78a697506fc6a1560471e6521e9:e33b1f5cc51f17c070e89705329e1246$" + "version": "3.0" } } diff --git a/public/images/pokemon/exp/821.png b/public/images/pokemon/exp/821.png index e4a73a66140..8bb00063bcb 100644 Binary files a/public/images/pokemon/exp/821.png and b/public/images/pokemon/exp/821.png differ diff --git a/public/images/pokemon/exp/867.json b/public/images/pokemon/exp/867.json index 1a9c7572f43..52e0127509b 100644 --- a/public/images/pokemon/exp/867.json +++ b/public/images/pokemon/exp/867.json @@ -4,8 +4,8 @@ "image": "867.png", "format": "RGBA8888", "size": { - "w": 344, - "h": 344 + "w": 361, + "h": 361 }, "scale": 1, "frames": [ @@ -31,7 +31,7 @@ } }, { - "filename": "0020.png", + "filename": "0021.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -52,7 +52,7 @@ } }, { - "filename": "0035.png", + "filename": "0037.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -94,70 +94,7 @@ } }, { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 113, - "h": 56 - }, - "frame": { - "x": 114, - "y": 0, - "w": 113, - "h": 56 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 113, - "h": 56 - }, - "frame": { - "x": 114, - "y": 0, - "w": 113, - "h": 56 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 113, - "h": 56 - }, - "frame": { - "x": 114, - "y": 0, - "w": 113, - "h": 56 - } - }, - { - "filename": "0034.png", + "filename": "0020.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -199,7 +136,7 @@ } }, { - "filename": "0003.png", + "filename": "0006.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -207,20 +144,20 @@ "h": 66 }, "spriteSourceSize": { - "x": 4, + "x": 2, "y": 0, - "w": 107, + "w": 112, "h": 56 }, "frame": { "x": 227, "y": 0, - "w": 107, + "w": 112, "h": 56 } }, { - "filename": "0018.png", + "filename": "0022.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -228,20 +165,20 @@ "h": 66 }, "spriteSourceSize": { - "x": 4, + "x": 2, "y": 0, - "w": 107, + "w": 112, "h": 56 }, "frame": { "x": 227, "y": 0, - "w": 107, + "w": 112, "h": 56 } }, { - "filename": "0033.png", + "filename": "0038.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -249,62 +186,20 @@ "h": 66 }, "spriteSourceSize": { - "x": 4, + "x": 2, "y": 0, - "w": 107, + "w": 112, "h": 56 }, "frame": { "x": 227, "y": 0, - "w": 107, + "w": 112, "h": 56 } }, { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 103, - "h": 65 - }, - "frame": { - "x": 0, - "y": 56, - "w": 103, - "h": 65 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 103, - "h": 65 - }, - "frame": { - "x": 0, - "y": 56, - "w": 103, - "h": 65 - } - }, - { - "filename": "0026.png", + "filename": "0012.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -346,7 +241,7 @@ } }, { - "filename": "0041.png", + "filename": "0044.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -367,7 +262,7 @@ } }, { - "filename": "0043.png", + "filename": "0003.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -375,16 +270,58 @@ "h": 66 }, "spriteSourceSize": { - "x": 5, + "x": 4, "y": 0, - "w": 103, - "h": 65 + "w": 107, + "h": 56 }, "frame": { - "x": 0, + "x": 103, "y": 56, - "w": 103, - "h": 65 + "w": 107, + "h": 56 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 4, + "y": 0, + "w": 107, + "h": 56 + }, + "frame": { + "x": 103, + "y": 56, + "w": 107, + "h": 56 + } + }, + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 4, + "y": 0, + "w": 107, + "h": 56 + }, + "frame": { + "x": 103, + "y": 56, + "w": 107, + "h": 56 } }, { @@ -402,14 +339,14 @@ "h": 56 }, "frame": { - "x": 103, + "x": 210, "y": 56, "w": 107, "h": 56 } }, { - "filename": "0022.png", + "filename": "0023.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -423,14 +360,14 @@ "h": 56 }, "frame": { - "x": 103, + "x": 210, "y": 56, "w": 107, "h": 56 } }, { - "filename": "0037.png", + "filename": "0039.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -444,12 +381,54 @@ "h": 56 }, "frame": { - "x": 103, + "x": 210, "y": 56, "w": 107, "h": 56 } }, + { + "filename": "0053.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 40, + "y": 1, + "w": 44, + "h": 56 + }, + "frame": { + "x": 317, + "y": 56, + "w": 44, + "h": 56 + } + }, + { + "filename": "0064.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 40, + "y": 1, + "w": 44, + "h": 56 + }, + "frame": { + "x": 317, + "y": 56, + "w": 44, + "h": 56 + } + }, { "filename": "0002.png", "rotated": false, @@ -465,14 +444,14 @@ "h": 57 }, "frame": { - "x": 210, - "y": 56, + "x": 103, + "y": 112, "w": 102, "h": 57 } }, { - "filename": "0017.png", + "filename": "0018.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -486,14 +465,14 @@ "h": 57 }, "frame": { - "x": 210, - "y": 56, + "x": 103, + "y": 112, "w": 102, "h": 57 } }, { - "filename": "0032.png", + "filename": "0034.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -507,12 +486,138 @@ "h": 57 }, "frame": { - "x": 210, - "y": 56, + "x": 103, + "y": 112, "w": 102, "h": 57 } }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 5, + "y": 1, + "w": 101, + "h": 65 + }, + "frame": { + "x": 0, + "y": 121, + "w": 101, + "h": 65 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 5, + "y": 1, + "w": 101, + "h": 65 + }, + "frame": { + "x": 0, + "y": 121, + "w": 101, + "h": 65 + } + }, + { + "filename": "0045.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 5, + "y": 1, + "w": 101, + "h": 65 + }, + "frame": { + "x": 0, + "y": 121, + "w": 101, + "h": 65 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 101, + "h": 61 + }, + "frame": { + "x": 205, + "y": 112, + "w": 101, + "h": 61 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 101, + "h": 61 + }, + "frame": { + "x": 205, + "y": 112, + "w": 101, + "h": 61 + } + }, + { + "filename": "0043.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 101, + "h": 61 + }, + "frame": { + "x": 205, + "y": 112, + "w": 101, + "h": 61 + } + }, { "filename": "0008.png", "rotated": false, @@ -524,266 +629,14 @@ "spriteSourceSize": { "x": 6, "y": 0, - "w": 102, + "w": 101, "h": 57 }, - "frame": { - "x": 103, - "y": 112, - "w": 102, - "h": 57 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 102, - "h": 57 - }, - "frame": { - "x": 103, - "y": 112, - "w": 102, - "h": 57 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 102, - "h": 57 - }, - "frame": { - "x": 103, - "y": 112, - "w": 102, - "h": 57 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 5, - "y": 1, - "w": 101, - "h": 65 - }, - "frame": { - "x": 0, - "y": 121, - "w": 101, - "h": 65 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 5, - "y": 1, - "w": 101, - "h": 65 - }, - "frame": { - "x": 0, - "y": 121, - "w": 101, - "h": 65 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 5, - "y": 1, - "w": 101, - "h": 65 - }, - "frame": { - "x": 0, - "y": 121, - "w": 101, - "h": 65 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 101, - "h": 61 - }, - "frame": { - "x": 205, - "y": 113, - "w": 101, - "h": 61 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 101, - "h": 61 - }, - "frame": { - "x": 205, - "y": 113, - "w": 101, - "h": 61 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 101, - "h": 61 - }, - "frame": { - "x": 205, - "y": 113, - "w": 101, - "h": 61 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 101, - "h": 61 - }, - "frame": { - "x": 205, - "y": 113, - "w": 101, - "h": 61 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 101, - "h": 61 - }, - "frame": { - "x": 205, - "y": 113, - "w": 101, - "h": 61 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 101, - "h": 61 - }, - "frame": { - "x": 205, - "y": 113, - "w": 101, - "h": 61 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 8, - "y": 0, - "w": 99, - "h": 59 - }, "frame": { "x": 101, "y": 169, - "w": 99, - "h": 59 + "w": 101, + "h": 57 } }, { @@ -795,20 +648,20 @@ "h": 66 }, "spriteSourceSize": { - "x": 8, + "x": 6, "y": 0, - "w": 99, - "h": 59 + "w": 101, + "h": 57 }, "frame": { "x": 101, "y": 169, - "w": 99, - "h": 59 + "w": 101, + "h": 57 } }, { - "filename": "0039.png", + "filename": "0040.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -816,20 +669,20 @@ "h": 66 }, "spriteSourceSize": { - "x": 8, + "x": 6, "y": 0, - "w": 99, - "h": 59 + "w": 101, + "h": 57 }, "frame": { "x": 101, "y": 169, - "w": 99, - "h": 59 + "w": 101, + "h": 57 } }, { - "filename": "0015.png", + "filename": "0010.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -849,6 +702,195 @@ "h": 59 } }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 8, + "y": 0, + "w": 99, + "h": 59 + }, + "frame": { + "x": 0, + "y": 186, + "w": 99, + "h": 59 + } + }, + { + "filename": "0042.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 8, + "y": 0, + "w": 99, + "h": 59 + }, + "frame": { + "x": 0, + "y": 186, + "w": 99, + "h": 59 + } + }, + { + "filename": "0054.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 40, + "y": 0, + "w": 44, + "h": 54 + }, + "frame": { + "x": 306, + "y": 112, + "w": 44, + "h": 54 + } + }, + { + "filename": "0055.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 40, + "y": 3, + "w": 44, + "h": 54 + }, + "frame": { + "x": 306, + "y": 112, + "w": 44, + "h": 54 + } + }, + { + "filename": "0059.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 40, + "y": 3, + "w": 44, + "h": 54 + }, + "frame": { + "x": 306, + "y": 112, + "w": 44, + "h": 54 + } + }, + { + "filename": "0063.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 40, + "y": 3, + "w": 44, + "h": 54 + }, + "frame": { + "x": 306, + "y": 112, + "w": 44, + "h": 54 + } + }, + { + "filename": "0060.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 39, + "y": 3, + "w": 44, + "h": 54 + }, + "frame": { + "x": 306, + "y": 166, + "w": 44, + "h": 54 + } + }, + { + "filename": "0062.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 39, + "y": 3, + "w": 44, + "h": 54 + }, + "frame": { + "x": 306, + "y": 166, + "w": 44, + "h": 54 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 5, + "y": 0, + "w": 97, + "h": 65 + }, + "frame": { + "x": 202, + "y": 173, + "w": 97, + "h": 65 + } + }, { "filename": "0030.png", "rotated": false, @@ -858,20 +900,20 @@ "h": 66 }, "spriteSourceSize": { - "x": 8, + "x": 5, "y": 0, - "w": 99, - "h": 59 + "w": 97, + "h": 65 }, "frame": { - "x": 0, - "y": 186, - "w": 99, - "h": 59 + "x": 202, + "y": 173, + "w": 97, + "h": 65 } }, { - "filename": "0045.png", + "filename": "0046.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -879,16 +921,58 @@ "h": 66 }, "spriteSourceSize": { - "x": 8, + "x": 5, "y": 0, - "w": 99, - "h": 59 + "w": 97, + "h": 65 }, "frame": { - "x": 0, - "y": 186, - "w": 99, - "h": 59 + "x": 202, + "y": 173, + "w": 97, + "h": 65 + } + }, + { + "filename": "0051.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 29, + "y": 1, + "w": 58, + "h": 57 + }, + "frame": { + "x": 299, + "y": 220, + "w": 58, + "h": 57 + } + }, + { + "filename": "0066.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 29, + "y": 1, + "w": 58, + "h": 57 + }, + "frame": { + "x": 299, + "y": 220, + "w": 58, + "h": 57 } }, { @@ -906,8 +990,134 @@ "h": 59 }, "frame": { - "x": 0, - "y": 245, + "x": 99, + "y": 226, + "w": 95, + "h": 59 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 9, + "y": 1, + "w": 95, + "h": 59 + }, + "frame": { + "x": 99, + "y": 226, + "w": 95, + "h": 59 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 9, + "y": 1, + "w": 95, + "h": 59 + }, + "frame": { + "x": 99, + "y": 226, + "w": 95, + "h": 59 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 9, + "y": 1, + "w": 95, + "h": 59 + }, + "frame": { + "x": 99, + "y": 226, + "w": 95, + "h": 59 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 9, + "y": 1, + "w": 95, + "h": 59 + }, + "frame": { + "x": 99, + "y": 226, + "w": 95, + "h": 59 + } + }, + { + "filename": "0041.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 9, + "y": 1, + "w": 95, + "h": 59 + }, + "frame": { + "x": 99, + "y": 226, + "w": 95, + "h": 59 + } + }, + { + "filename": "0068.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 9, + "y": 1, + "w": 95, + "h": 59 + }, + "frame": { + "x": 99, + "y": 226, "w": 95, "h": 59 } @@ -921,146 +1131,20 @@ "h": 66 }, "spriteSourceSize": { - "x": 9, - "y": 1, - "w": 95, - "h": 59 - }, - "frame": { - "x": 0, - "y": 245, - "w": 95, - "h": 59 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 9, - "y": 1, - "w": 95, - "h": 59 - }, - "frame": { - "x": 0, - "y": 245, - "w": 95, - "h": 59 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 9, - "y": 1, - "w": 95, - "h": 59 - }, - "frame": { - "x": 0, - "y": 245, - "w": 95, - "h": 59 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 12, - "y": 1, - "w": 90, - "h": 59 - }, - "frame": { - "x": 95, - "y": 245, - "w": 90, - "h": 59 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 22, - "y": 1, - "w": 70, - "h": 59 - }, - "frame": { - "x": 185, - "y": 228, - "w": 70, - "h": 59 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 22, - "y": 1, - "w": 70, - "h": 59 - }, - "frame": { - "x": 185, - "y": 228, - "w": 70, - "h": 59 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 40, + "x": 8, "y": 0, - "w": 44, - "h": 54 + "w": 95, + "h": 59 }, "frame": { - "x": 200, - "y": 174, - "w": 44, - "h": 54 + "x": 0, + "y": 245, + "w": 95, + "h": 59 } }, { - "filename": "0052.png", + "filename": "0032.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -1068,100 +1152,16 @@ "h": 66 }, "spriteSourceSize": { - "x": 40, - "y": 3, - "w": 44, - "h": 54 + "x": 8, + "y": 0, + "w": 95, + "h": 59 }, "frame": { - "x": 200, - "y": 174, - "w": 44, - "h": 54 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 40, - "y": 3, - "w": 44, - "h": 54 - }, - "frame": { - "x": 200, - "y": 174, - "w": 44, - "h": 54 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 40, - "y": 3, - "w": 44, - "h": 54 - }, - "frame": { - "x": 200, - "y": 174, - "w": 44, - "h": 54 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 39, - "y": 3, - "w": 44, - "h": 54 - }, - "frame": { - "x": 244, - "y": 174, - "w": 44, - "h": 54 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 39, - "y": 3, - "w": 44, - "h": 54 - }, - "frame": { - "x": 244, - "y": 174, - "w": 44, - "h": 54 + "x": 0, + "y": 245, + "w": 95, + "h": 59 } }, { @@ -1173,41 +1173,20 @@ "h": 66 }, "spriteSourceSize": { - "x": 29, - "y": 1, - "w": 58, - "h": 57 + "x": 8, + "y": 0, + "w": 95, + "h": 59 }, "frame": { - "x": 185, - "y": 287, - "w": 58, - "h": 57 + "x": 0, + "y": 245, + "w": 95, + "h": 59 } }, { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 29, - "y": 1, - "w": 58, - "h": 57 - }, - "frame": { - "x": 185, - "y": 287, - "w": 58, - "h": 57 - } - }, - { - "filename": "0049.png", + "filename": "0052.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -1221,14 +1200,14 @@ "h": 56 }, "frame": { - "x": 288, - "y": 174, + "x": 0, + "y": 304, "w": 56, "h": 56 } }, { - "filename": "0062.png", + "filename": "0065.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -1242,56 +1221,14 @@ "h": 56 }, "frame": { - "x": 288, - "y": 174, + "x": 0, + "y": 304, "w": 56, "h": 56 } }, { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 40, - "y": 1, - "w": 44, - "h": 56 - }, - "frame": { - "x": 243, - "y": 287, - "w": 44, - "h": 56 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 40, - "y": 1, - "w": 44, - "h": 56 - }, - "frame": { - "x": 243, - "y": 287, - "w": 44, - "h": 56 - } - }, - { - "filename": "0053.png", + "filename": "0056.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -1305,29 +1242,8 @@ "h": 53 }, "frame": { - "x": 255, - "y": 230, - "w": 44, - "h": 53 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 114, - "h": 66 - }, - "spriteSourceSize": { - "x": 41, - "y": 4, - "w": 44, - "h": 53 - }, - "frame": { - "x": 255, - "y": 230, + "x": 56, + "y": 304, "w": 44, "h": 53 } @@ -1341,20 +1257,20 @@ "h": 66 }, "spriteSourceSize": { - "x": 38, + "x": 41, "y": 4, "w": 44, "h": 53 }, "frame": { - "x": 299, - "y": 230, + "x": 56, + "y": 304, "w": 44, "h": 53 } }, { - "filename": "0054.png", + "filename": "0015.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -1362,14 +1278,161 @@ "h": 66 }, "spriteSourceSize": { - "x": 42, + "x": 7, + "y": 0, + "w": 94, + "h": 61 + }, + "frame": { + "x": 194, + "y": 238, + "w": 94, + "h": 61 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 94, + "h": 61 + }, + "frame": { + "x": 194, + "y": 238, + "w": 94, + "h": 61 + } + }, + { + "filename": "0047.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 94, + "h": 61 + }, + "frame": { + "x": 194, + "y": 238, + "w": 94, + "h": 61 + } + }, + { + "filename": "0050.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 22, + "y": 1, + "w": 70, + "h": 59 + }, + "frame": { + "x": 288, + "y": 277, + "w": 70, + "h": 59 + } + }, + { + "filename": "0067.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 22, + "y": 1, + "w": 70, + "h": 59 + }, + "frame": { + "x": 288, + "y": 277, + "w": 70, + "h": 59 + } + }, + { + "filename": "0049.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 12, + "y": 1, + "w": 90, + "h": 59 + }, + "frame": { + "x": 100, + "y": 299, + "w": 90, + "h": 59 + } + }, + { + "filename": "0061.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 37, + "y": 4, + "w": 44, + "h": 53 + }, + "frame": { + "x": 190, + "y": 299, + "w": 44, + "h": 53 + } + }, + { + "filename": "0057.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 114, + "h": 66 + }, + "spriteSourceSize": { + "x": 43, "y": 5, "w": 44, "h": 52 }, "frame": { - "x": 287, - "y": 283, + "x": 234, + "y": 299, "w": 44, "h": 52 } @@ -1379,7 +1442,6 @@ ], "meta": { "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:b8ca75f7f37906e78ed633b32d037b74:92bc79a7ca35490600679451c06105fc:58bcd289dd222ce646aec14ff657c9fc$" + "version": "3.0" } } diff --git a/public/images/pokemon/exp/867.png b/public/images/pokemon/exp/867.png index 689a72694ef..4f8f67842bd 100644 Binary files a/public/images/pokemon/exp/867.png and b/public/images/pokemon/exp/867.png differ diff --git a/public/images/pokemon/exp/953.png b/public/images/pokemon/exp/953.png index fb4255a112b..8a6ca2e7eff 100644 Binary files a/public/images/pokemon/exp/953.png and b/public/images/pokemon/exp/953.png differ diff --git a/public/images/pokemon/exp/back/821.png b/public/images/pokemon/exp/back/821.png index 80d88dce2f9..13aa5c24e83 100644 Binary files a/public/images/pokemon/exp/back/821.png and b/public/images/pokemon/exp/back/821.png differ diff --git a/public/images/pokemon/exp/back/867.png b/public/images/pokemon/exp/back/867.png index 766ce3f39ed..b816f10a0de 100644 Binary files a/public/images/pokemon/exp/back/867.png and b/public/images/pokemon/exp/back/867.png differ diff --git a/public/images/pokemon/exp/back/shiny/821.png b/public/images/pokemon/exp/back/shiny/821.png index 426a1e81fea..d069d59ef72 100644 Binary files a/public/images/pokemon/exp/back/shiny/821.png and b/public/images/pokemon/exp/back/shiny/821.png differ diff --git a/public/images/pokemon/exp/back/shiny/867.png b/public/images/pokemon/exp/back/shiny/867.png index 7be80836150..9821217341e 100644 Binary files a/public/images/pokemon/exp/back/shiny/867.png and b/public/images/pokemon/exp/back/shiny/867.png differ diff --git a/public/images/pokemon/exp/shiny/670-blue.json b/public/images/pokemon/exp/shiny/670-blue.json index efead949c9b..ba237724cbc 100644 --- a/public/images/pokemon/exp/shiny/670-blue.json +++ b/public/images/pokemon/exp/shiny/670-blue.json @@ -4,219 +4,156 @@ "image": "670-blue.png", "format": "RGBA8888", "size": { - "w": 132, - "h": 132 + "w": 491, + "h": 491 }, "scale": 1, "frames": [ { - "filename": "0002.png", + "filename": "0101.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 4, + "y": 4, + "w": 60, + "h": 65 }, "frame": { "x": 0, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 65 } }, { - "filename": "0006.png", + "filename": "0083.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, - "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 66 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, + "x": 3, + "y": 9, + "w": 60, "h": 67 }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 66 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, - "frame": { - "x": 40, + "x": 60, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 67 } }, { - "filename": "0003.png", + "filename": "0102.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 5, + "w": 60, + "h": 67 }, "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 + "x": 120, + "y": 0, + "w": 60, + "h": 67 } }, { - "filename": "0005.png", + "filename": "0082.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 10, + "w": 61, + "h": 68 }, "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, + "x": 180, "y": 0, - "w": 39, - "h": 66 - }, - "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 + "w": 61, + "h": 68 } }, { - "filename": "0011.png", + "filename": "0100.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 - }, - "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, + "x": 5, "y": 2, - "w": 40, - "h": 65 + "w": 60, + "h": 68 }, "frame": { - "x": 79, - "y": 66, - "w": 40, - "h": 65 + "x": 241, + "y": 0, + "w": 60, + "h": 68 } }, { - "filename": "0007.png", + "filename": "0081.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 2, - "w": 40, - "h": 65 + "x": 4, + "y": 12, + "w": 62, + "h": 69 }, "frame": { - "x": 79, - "y": 66, - "w": 40, - "h": 65 + "x": 301, + "y": 0, + "w": 62, + "h": 69 + } + }, + { + "filename": "0084.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 56, + "h": 69 + }, + "frame": { + "x": 363, + "y": 0, + "w": 56, + "h": 69 } }, { @@ -224,20 +161,1763 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0037.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0048.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0059.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0070.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0038.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0049.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0060.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0071.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0085.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 56, + "h": 70 + }, + "frame": { + "x": 59, + "y": 67, + "w": 56, + "h": 70 + } + }, + { + "filename": "0099.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 57, + "h": 70 + }, + "frame": { + "x": 115, + "y": 67, + "w": 57, + "h": 70 + } + }, + { + "filename": "0103.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 57, + "h": 70 + }, + "frame": { + "x": 172, + "y": 68, + "w": 57, + "h": 70 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0036.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0047.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0058.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0069.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0080.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 292, + "y": 69, + "w": 63, + "h": 71 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0046.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0057.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0068.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0079.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 418, + "y": 70, + "w": 63, + "h": 72 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0039.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0050.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0061.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0072.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 53, + "y": 137, + "w": 61, + "h": 73 + } + }, + { + "filename": "0098.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 54, + "h": 73 + }, + "frame": { + "x": 114, + "y": 137, + "w": 54, + "h": 73 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0045.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0056.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0067.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0078.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 229, + "y": 139, + "w": 61, + "h": 73 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0040.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0051.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0062.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0073.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0104.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 52, + "h": 74 + }, + "frame": { + "x": 339, + "y": 141, + "w": 52, + "h": 74 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0041.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0052.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0063.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0074.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0114.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 50, + "h": 75 + }, + "frame": { + "x": 438, + "y": 142, + "w": 50, + "h": 75 + } + }, + { + "filename": "0086.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 38, - "h": 65 + "y": 4, + "w": 51, + "h": 76 }, "frame": { - "x": 80, - "y": 0, - "w": 38, - "h": 65 + "x": 0, + "y": 208, + "w": 51, + "h": 76 + } + }, + { + "filename": "0119.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 11, + "w": 57, + "h": 75 + }, + "frame": { + "x": 51, + "y": 210, + "w": 57, + "h": 75 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0044.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0055.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0066.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0077.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0115.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 163, + "y": 211, + "w": 51, + "h": 76 + } + }, + { + "filename": "0116.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 214, + "y": 212, + "w": 51, + "h": 76 + } + }, + { + "filename": "0117.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 53, + "h": 76 + }, + "frame": { + "x": 265, + "y": 214, + "w": 53, + "h": 76 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0042.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0053.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0064.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0075.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 } }, { @@ -245,20 +1925,587 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0043.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0054.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0065.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0076.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0095.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 3, + "w": 51, + "h": 77 + }, + "frame": { + "x": 419, + "y": 217, + "w": 51, + "h": 77 + } + }, + { + "filename": "0113.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 9, + "w": 47, + "h": 77 + }, + "frame": { + "x": 0, + "y": 284, + "w": 47, + "h": 77 + } + }, + { + "filename": "0096.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 2, + "w": 54, + "h": 77 + }, + "frame": { + "x": 47, + "y": 285, + "w": 54, + "h": 77 + } + }, + { + "filename": "0097.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 55, + "h": 77 + }, + "frame": { + "x": 101, + "y": 286, + "w": 55, + "h": 77 + } + }, + { + "filename": "0118.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 53, + "h": 77 + }, + "frame": { + "x": 156, + "y": 287, + "w": 53, + "h": 77 + } + }, + { + "filename": "0094.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 50, + "h": 78 + }, + "frame": { + "x": 209, + "y": 288, + "w": 50, + "h": 78 + } + }, + { + "filename": "0105.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 0, + "w": 48, + "h": 78 + }, + "frame": { + "x": 259, + "y": 290, + "w": 48, + "h": 78 + } + }, + { + "filename": "0112.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 47, + "h": 78 + }, + "frame": { + "x": 307, + "y": 292, + "w": 47, + "h": 78 + } + }, + { + "filename": "0087.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 38, - "h": 65 + "y": 3, + "w": 45, + "h": 79 }, "frame": { - "x": 80, - "y": 0, - "w": 38, - "h": 65 + "x": 354, + "y": 294, + "w": 45, + "h": 79 + } + }, + { + "filename": "0093.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 49, + "h": 79 + }, + "frame": { + "x": 399, + "y": 294, + "w": 49, + "h": 79 + } + }, + { + "filename": "0088.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 4, + "w": 43, + "h": 82 + }, + "frame": { + "x": 448, + "y": 294, + "w": 43, + "h": 82 + } + }, + { + "filename": "0089.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 6, + "w": 44, + "h": 80 + }, + "frame": { + "x": 0, + "y": 361, + "w": 44, + "h": 80 + } + }, + { + "filename": "0106.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 2, + "w": 48, + "h": 79 + }, + "frame": { + "x": 44, + "y": 362, + "w": 48, + "h": 79 + } + }, + { + "filename": "0107.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 3, + "w": 48, + "h": 79 + }, + "frame": { + "x": 92, + "y": 363, + "w": 48, + "h": 79 + } + }, + { + "filename": "0110.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 48, + "h": 79 + }, + "frame": { + "x": 140, + "y": 364, + "w": 48, + "h": 79 + } + }, + { + "filename": "0111.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 48, + "h": 79 + }, + "frame": { + "x": 188, + "y": 366, + "w": 48, + "h": 79 + } + }, + { + "filename": "0090.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 46, + "h": 80 + }, + "frame": { + "x": 236, + "y": 368, + "w": 46, + "h": 80 + } + }, + { + "filename": "0091.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 47, + "h": 80 + }, + "frame": { + "x": 282, + "y": 370, + "w": 47, + "h": 80 + } + }, + { + "filename": "0108.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 4, + "w": 50, + "h": 80 + }, + "frame": { + "x": 329, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0109.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 50, + "h": 80 + }, + "frame": { + "x": 379, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0092.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 48, + "h": 81 + }, + "frame": { + "x": 429, + "y": 376, + "w": 48, + "h": 81 } } ] @@ -266,7 +2513,6 @@ ], "meta": { "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:36694a5c0e14e2bcf35d63ad72dd1f6d:3a1ea08b2c0badd5e308726fb0dab395:f28e65cb64de91b1773f7b4998e488a5$" + "version": "3.0" } } diff --git a/public/images/pokemon/exp/shiny/670-orange.json b/public/images/pokemon/exp/shiny/670-orange.json index 0f6043e12c5..ded85695da2 100644 --- a/public/images/pokemon/exp/shiny/670-orange.json +++ b/public/images/pokemon/exp/shiny/670-orange.json @@ -4,219 +4,156 @@ "image": "670-orange.png", "format": "RGBA8888", "size": { - "w": 132, - "h": 132 + "w": 491, + "h": 491 }, "scale": 1, "frames": [ { - "filename": "0002.png", + "filename": "0101.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 4, + "y": 4, + "w": 60, + "h": 65 }, "frame": { "x": 0, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 65 } }, { - "filename": "0006.png", + "filename": "0083.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, - "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 66 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, + "x": 3, + "y": 9, + "w": 60, "h": 67 }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 66 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, - "frame": { - "x": 40, + "x": 60, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 67 } }, { - "filename": "0003.png", + "filename": "0102.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 5, + "w": 60, + "h": 67 }, "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 + "x": 120, + "y": 0, + "w": 60, + "h": 67 } }, { - "filename": "0005.png", + "filename": "0082.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 10, + "w": 61, + "h": 68 }, "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, + "x": 180, "y": 0, - "w": 39, - "h": 66 - }, - "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 + "w": 61, + "h": 68 } }, { - "filename": "0011.png", + "filename": "0100.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 - }, - "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, + "x": 5, "y": 2, - "w": 40, - "h": 65 + "w": 60, + "h": 68 }, "frame": { - "x": 79, - "y": 66, - "w": 40, - "h": 65 + "x": 241, + "y": 0, + "w": 60, + "h": 68 } }, { - "filename": "0007.png", + "filename": "0081.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 2, - "w": 40, - "h": 65 + "x": 4, + "y": 12, + "w": 62, + "h": 69 }, "frame": { - "x": 79, - "y": 66, - "w": 40, - "h": 65 + "x": 301, + "y": 0, + "w": 62, + "h": 69 + } + }, + { + "filename": "0084.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 56, + "h": 69 + }, + "frame": { + "x": 363, + "y": 0, + "w": 56, + "h": 69 } }, { @@ -224,20 +161,1763 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0037.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0048.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0059.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0070.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0038.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0049.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0060.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0071.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0085.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 56, + "h": 70 + }, + "frame": { + "x": 59, + "y": 67, + "w": 56, + "h": 70 + } + }, + { + "filename": "0099.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 57, + "h": 70 + }, + "frame": { + "x": 115, + "y": 67, + "w": 57, + "h": 70 + } + }, + { + "filename": "0103.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 57, + "h": 70 + }, + "frame": { + "x": 172, + "y": 68, + "w": 57, + "h": 70 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0036.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0047.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0058.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0069.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0080.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 292, + "y": 69, + "w": 63, + "h": 71 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0046.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0057.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0068.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0079.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 418, + "y": 70, + "w": 63, + "h": 72 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0039.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0050.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0061.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0072.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 53, + "y": 137, + "w": 61, + "h": 73 + } + }, + { + "filename": "0098.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 54, + "h": 73 + }, + "frame": { + "x": 114, + "y": 137, + "w": 54, + "h": 73 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0045.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0056.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0067.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0078.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 229, + "y": 139, + "w": 61, + "h": 73 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0040.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0051.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0062.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0073.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0104.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 52, + "h": 74 + }, + "frame": { + "x": 339, + "y": 141, + "w": 52, + "h": 74 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0041.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0052.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0063.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0074.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0114.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 50, + "h": 75 + }, + "frame": { + "x": 438, + "y": 142, + "w": 50, + "h": 75 + } + }, + { + "filename": "0086.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 38, - "h": 65 + "y": 4, + "w": 51, + "h": 76 }, "frame": { - "x": 80, - "y": 0, - "w": 38, - "h": 65 + "x": 0, + "y": 208, + "w": 51, + "h": 76 + } + }, + { + "filename": "0119.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 11, + "w": 57, + "h": 75 + }, + "frame": { + "x": 51, + "y": 210, + "w": 57, + "h": 75 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0044.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0055.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0066.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0077.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0115.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 163, + "y": 211, + "w": 51, + "h": 76 + } + }, + { + "filename": "0116.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 214, + "y": 212, + "w": 51, + "h": 76 + } + }, + { + "filename": "0117.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 53, + "h": 76 + }, + "frame": { + "x": 265, + "y": 214, + "w": 53, + "h": 76 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0042.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0053.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0064.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0075.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 } }, { @@ -245,20 +1925,587 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0043.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0054.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0065.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0076.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0095.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 3, + "w": 51, + "h": 77 + }, + "frame": { + "x": 419, + "y": 217, + "w": 51, + "h": 77 + } + }, + { + "filename": "0113.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 9, + "w": 47, + "h": 77 + }, + "frame": { + "x": 0, + "y": 284, + "w": 47, + "h": 77 + } + }, + { + "filename": "0096.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 2, + "w": 54, + "h": 77 + }, + "frame": { + "x": 47, + "y": 285, + "w": 54, + "h": 77 + } + }, + { + "filename": "0097.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 55, + "h": 77 + }, + "frame": { + "x": 101, + "y": 286, + "w": 55, + "h": 77 + } + }, + { + "filename": "0118.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 53, + "h": 77 + }, + "frame": { + "x": 156, + "y": 287, + "w": 53, + "h": 77 + } + }, + { + "filename": "0094.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 50, + "h": 78 + }, + "frame": { + "x": 209, + "y": 288, + "w": 50, + "h": 78 + } + }, + { + "filename": "0105.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 0, + "w": 48, + "h": 78 + }, + "frame": { + "x": 259, + "y": 290, + "w": 48, + "h": 78 + } + }, + { + "filename": "0112.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 47, + "h": 78 + }, + "frame": { + "x": 307, + "y": 292, + "w": 47, + "h": 78 + } + }, + { + "filename": "0087.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 38, - "h": 65 + "y": 3, + "w": 45, + "h": 79 }, "frame": { - "x": 80, - "y": 0, - "w": 38, - "h": 65 + "x": 354, + "y": 294, + "w": 45, + "h": 79 + } + }, + { + "filename": "0093.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 49, + "h": 79 + }, + "frame": { + "x": 399, + "y": 294, + "w": 49, + "h": 79 + } + }, + { + "filename": "0088.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 4, + "w": 43, + "h": 82 + }, + "frame": { + "x": 448, + "y": 294, + "w": 43, + "h": 82 + } + }, + { + "filename": "0089.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 6, + "w": 44, + "h": 80 + }, + "frame": { + "x": 0, + "y": 361, + "w": 44, + "h": 80 + } + }, + { + "filename": "0106.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 2, + "w": 48, + "h": 79 + }, + "frame": { + "x": 44, + "y": 362, + "w": 48, + "h": 79 + } + }, + { + "filename": "0107.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 3, + "w": 48, + "h": 79 + }, + "frame": { + "x": 92, + "y": 363, + "w": 48, + "h": 79 + } + }, + { + "filename": "0110.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 48, + "h": 79 + }, + "frame": { + "x": 140, + "y": 364, + "w": 48, + "h": 79 + } + }, + { + "filename": "0111.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 48, + "h": 79 + }, + "frame": { + "x": 188, + "y": 366, + "w": 48, + "h": 79 + } + }, + { + "filename": "0090.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 46, + "h": 80 + }, + "frame": { + "x": 236, + "y": 368, + "w": 46, + "h": 80 + } + }, + { + "filename": "0091.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 47, + "h": 80 + }, + "frame": { + "x": 282, + "y": 370, + "w": 47, + "h": 80 + } + }, + { + "filename": "0108.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 4, + "w": 50, + "h": 80 + }, + "frame": { + "x": 329, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0109.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 50, + "h": 80 + }, + "frame": { + "x": 379, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0092.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 48, + "h": 81 + }, + "frame": { + "x": 429, + "y": 376, + "w": 48, + "h": 81 } } ] @@ -266,7 +2513,6 @@ ], "meta": { "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:a63935896af8b0a45c2a96144ad240db:0991e6604fcdd7ce0b2b0f12f8c7d0fc:a4ae1870548474db9c466580d9a207bb$" + "version": "3.0" } } diff --git a/public/images/pokemon/exp/shiny/670-red.json b/public/images/pokemon/exp/shiny/670-red.json index 53390cee950..098b0efca76 100644 --- a/public/images/pokemon/exp/shiny/670-red.json +++ b/public/images/pokemon/exp/shiny/670-red.json @@ -4,517 +4,13 @@ "image": "670-red.png", "format": "RGBA8888", "size": { - "w": 512, - "h": 512 + "w": 491, + "h": 491 }, "scale": 1, "frames": [ { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 72 - }, - "frame": { - "x": 0, - "y": 0, - "w": 63, - "h": 72 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 72 - }, - "frame": { - "x": 0, - "y": 0, - "w": 63, - "h": 72 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 72 - }, - "frame": { - "x": 0, - "y": 0, - "w": 63, - "h": 72 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 72 - }, - "frame": { - "x": 0, - "y": 0, - "w": 63, - "h": 72 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 72 - }, - "frame": { - "x": 0, - "y": 0, - "w": 63, - "h": 72 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 72 - }, - "frame": { - "x": 0, - "y": 0, - "w": 63, - "h": 72 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 72 - }, - "frame": { - "x": 0, - "y": 0, - "w": 63, - "h": 72 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 72 - }, - "frame": { - "x": 0, - "y": 72, - "w": 63, - "h": 72 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 12, - "w": 61, - "h": 73 - }, - "frame": { - "x": 63, - "y": 0, - "w": 61, - "h": 73 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 71 - }, - "frame": { - "x": 0, - "y": 144, - "w": 63, - "h": 71 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 71 - }, - "frame": { - "x": 0, - "y": 144, - "w": 63, - "h": 71 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 71 - }, - "frame": { - "x": 0, - "y": 144, - "w": 63, - "h": 71 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 71 - }, - "frame": { - "x": 0, - "y": 144, - "w": 63, - "h": 71 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 71 - }, - "frame": { - "x": 0, - "y": 144, - "w": 63, - "h": 71 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 71 - }, - "frame": { - "x": 0, - "y": 144, - "w": 63, - "h": 71 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 71 - }, - "frame": { - "x": 0, - "y": 144, - "w": 63, - "h": 71 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 12, - "w": 61, - "h": 73 - }, - "frame": { - "x": 63, - "y": 73, - "w": 61, - "h": 73 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 12, - "w": 61, - "h": 73 - }, - "frame": { - "x": 63, - "y": 73, - "w": 61, - "h": 73 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 12, - "w": 61, - "h": 73 - }, - "frame": { - "x": 63, - "y": 73, - "w": 61, - "h": 73 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 12, - "w": 61, - "h": 73 - }, - "frame": { - "x": 63, - "y": 73, - "w": 61, - "h": 73 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 12, - "w": 61, - "h": 73 - }, - "frame": { - "x": 63, - "y": 73, - "w": 61, - "h": 73 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 12, - "w": 61, - "h": 73 - }, - "frame": { - "x": 63, - "y": 73, - "w": 61, - "h": 73 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 12, - "w": 61, - "h": 73 - }, - "frame": { - "x": 124, - "y": 0, - "w": 61, - "h": 73 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 63, - "h": 71 - }, - "frame": { - "x": 0, - "y": 215, - "w": 63, - "h": 71 - } - }, - { - "filename": "0004.png", + "filename": "0101.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -523,166 +19,19 @@ }, "spriteSourceSize": { "x": 4, - "y": 11, - "w": 62, - "h": 70 - }, - "frame": { - "x": 185, - "y": 0, - "w": 62, - "h": 70 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 62, - "h": 70 - }, - "frame": { - "x": 185, - "y": 0, - "w": 62, - "h": 70 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 62, - "h": 70 - }, - "frame": { - "x": 185, - "y": 0, - "w": 62, - "h": 70 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 62, - "h": 70 - }, - "frame": { - "x": 185, - "y": 0, - "w": 62, - "h": 70 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 62, - "h": 70 - }, - "frame": { - "x": 185, - "y": 0, - "w": 62, - "h": 70 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 62, - "h": 70 - }, - "frame": { - "x": 185, - "y": 0, - "w": 62, - "h": 70 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 62, - "h": 70 + "y": 4, + "w": 60, + "h": 65 }, "frame": { "x": 0, - "y": 286, - "w": 62, - "h": 70 - } - }, - { - "filename": "0097.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 7, "y": 0, - "w": 55, - "h": 77 - }, - "frame": { - "x": 63, - "y": 146, - "w": 55, - "h": 77 + "w": 60, + "h": 65 } }, { - "filename": "0119.png", + "filename": "0083.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -691,19 +40,19 @@ }, "spriteSourceSize": { "x": 3, - "y": 11, - "w": 57, - "h": 75 + "y": 9, + "w": 60, + "h": 67 }, "frame": { - "x": 0, - "y": 356, - "w": 57, - "h": 75 + "x": 60, + "y": 0, + "w": 60, + "h": 67 } }, { - "filename": "0092.png", + "filename": "0102.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -711,58 +60,16 @@ "h": 86 }, "spriteSourceSize": { - "x": 6, + "x": 4, "y": 5, - "w": 48, - "h": 81 + "w": 60, + "h": 67 }, "frame": { - "x": 0, - "y": 431, - "w": 48, - "h": 81 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 12, - "w": 62, - "h": 69 - }, - "frame": { - "x": 247, + "x": 120, "y": 0, - "w": 62, - "h": 69 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 59, - "h": 70 - }, - "frame": { - "x": 124, - "y": 73, - "w": 59, - "h": 70 + "w": 60, + "h": 67 } }, { @@ -780,7 +87,7 @@ "h": 68 }, "frame": { - "x": 309, + "x": 180, "y": 0, "w": 61, "h": 68 @@ -801,12 +108,222 @@ "h": 68 }, "frame": { - "x": 370, + "x": 241, "y": 0, "w": 60, "h": 68 } }, + { + "filename": "0081.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 12, + "w": 62, + "h": 69 + }, + "frame": { + "x": 301, + "y": 0, + "w": 62, + "h": 69 + } + }, + { + "filename": "0084.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 56, + "h": 69 + }, + "frame": { + "x": 363, + "y": 0, + "w": 56, + "h": 69 + } + }, + { + "filename": "0004.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0037.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0048.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0059.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0070.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, { "filename": "0016.png", "rotated": false, @@ -822,159 +339,12 @@ "h": 70 }, "frame": { - "x": 430, - "y": 0, + "x": 0, + "y": 65, "w": 59, "h": 70 } }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 9, - "w": 55, - "h": 76 - }, - "frame": { - "x": 63, - "y": 223, - "w": 55, - "h": 76 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 9, - "w": 55, - "h": 76 - }, - "frame": { - "x": 63, - "y": 223, - "w": 55, - "h": 76 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 9, - "w": 55, - "h": 76 - }, - "frame": { - "x": 63, - "y": 223, - "w": 55, - "h": 76 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 9, - "w": 55, - "h": 76 - }, - "frame": { - "x": 63, - "y": 223, - "w": 55, - "h": 76 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 9, - "w": 55, - "h": 76 - }, - "frame": { - "x": 63, - "y": 223, - "w": 55, - "h": 76 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 9, - "w": 55, - "h": 76 - }, - "frame": { - "x": 63, - "y": 223, - "w": 55, - "h": 76 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 9, - "w": 55, - "h": 76 - }, - "frame": { - "x": 63, - "y": 223, - "w": 55, - "h": 76 - } - }, { "filename": "0027.png", "rotated": false, @@ -990,8 +360,8 @@ "h": 70 }, "frame": { - "x": 62, - "y": 299, + "x": 0, + "y": 65, "w": 59, "h": 70 } @@ -1011,8 +381,8 @@ "h": 70 }, "frame": { - "x": 62, - "y": 299, + "x": 0, + "y": 65, "w": 59, "h": 70 } @@ -1032,8 +402,8 @@ "h": 70 }, "frame": { - "x": 62, - "y": 299, + "x": 0, + "y": 65, "w": 59, "h": 70 } @@ -1053,8 +423,8 @@ "h": 70 }, "frame": { - "x": 62, - "y": 299, + "x": 0, + "y": 65, "w": 59, "h": 70 } @@ -1074,138 +444,12 @@ "h": 70 }, "frame": { - "x": 62, - "y": 299, + "x": 0, + "y": 65, "w": 59, "h": 70 } }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 9, - "w": 60, - "h": 67 - }, - "frame": { - "x": 57, - "y": 369, - "w": 60, - "h": 67 - } - }, - { - "filename": "0117.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 10, - "w": 53, - "h": 76 - }, - "frame": { - "x": 48, - "y": 436, - "w": 53, - "h": 76 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 0, - "y": 4, - "w": 51, - "h": 76 - }, - "frame": { - "x": 101, - "y": 436, - "w": 51, - "h": 76 - } - }, - { - "filename": "0102.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 60, - "h": 67 - }, - "frame": { - "x": 117, - "y": 369, - "w": 60, - "h": 67 - } - }, - { - "filename": "0115.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 10, - "w": 51, - "h": 76 - }, - "frame": { - "x": 152, - "y": 436, - "w": 51, - "h": 76 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 0, - "y": 4, - "w": 43, - "h": 82 - }, - "frame": { - "x": 118, - "y": 146, - "w": 43, - "h": 82 - } - }, { "filename": "0085.png", "rotated": false, @@ -1221,8 +465,8 @@ "h": 70 }, "frame": { - "x": 118, - "y": 228, + "x": 59, + "y": 67, "w": 56, "h": 70 } @@ -1242,35 +486,14 @@ "h": 70 }, "frame": { - "x": 121, - "y": 298, + "x": 115, + "y": 67, "w": 57, "h": 70 } }, { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 0, - "y": 6, - "w": 44, - "h": 80 - }, - "frame": { - "x": 161, - "y": 143, - "w": 44, - "h": 80 - } - }, - { - "filename": "0008.png", + "filename": "0103.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -1279,19 +502,19 @@ }, "spriteSourceSize": { "x": 4, - "y": 2, - "w": 47, - "h": 75 + "y": 4, + "w": 57, + "h": 70 }, "frame": { - "x": 174, - "y": 223, - "w": 47, - "h": 75 + "x": 172, + "y": 68, + "w": 57, + "h": 70 } }, { - "filename": "0019.png", + "filename": "0003.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -1299,16 +522,331 @@ "h": 86 }, "spriteSourceSize": { - "x": 4, - "y": 2, - "w": 47, - "h": 75 + "x": 3, + "y": 13, + "w": 63, + "h": 71 }, "frame": { - "x": 174, - "y": 223, - "w": 47, - "h": 75 + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0036.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0047.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0058.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0069.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0080.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 292, + "y": 69, + "w": 63, + "h": 71 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0046.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0057.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0068.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0079.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 418, + "y": 70, + "w": 63, + "h": 72 } }, { @@ -1326,8 +864,8 @@ "h": 73 }, "frame": { - "x": 178, - "y": 298, + "x": 0, + "y": 135, "w": 53, "h": 73 } @@ -1347,8 +885,8 @@ "h": 73 }, "frame": { - "x": 178, - "y": 298, + "x": 0, + "y": 135, "w": 53, "h": 73 } @@ -1368,8 +906,8 @@ "h": 73 }, "frame": { - "x": 178, - "y": 298, + "x": 0, + "y": 135, "w": 53, "h": 73 } @@ -1389,8 +927,8 @@ "h": 73 }, "frame": { - "x": 178, - "y": 298, + "x": 0, + "y": 135, "w": 53, "h": 73 } @@ -1410,8 +948,8 @@ "h": 73 }, "frame": { - "x": 178, - "y": 298, + "x": 0, + "y": 135, "w": 53, "h": 73 } @@ -1431,8 +969,8 @@ "h": 73 }, "frame": { - "x": 178, - "y": 298, + "x": 0, + "y": 135, "w": 53, "h": 73 } @@ -1452,35 +990,14 @@ "h": 73 }, "frame": { - "x": 178, - "y": 298, + "x": 0, + "y": 135, "w": 53, "h": 73 } }, { - "filename": "0101.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 60, - "h": 65 - }, - "frame": { - "x": 177, - "y": 371, - "w": 60, - "h": 65 - } - }, - { - "filename": "0116.png", + "filename": "0001.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -1489,162 +1006,15 @@ }, "spriteSourceSize": { "x": 3, - "y": 10, - "w": 51, - "h": 76 + "y": 12, + "w": 61, + "h": 73 }, "frame": { - "x": 203, - "y": 436, - "w": 51, - "h": 76 - } - }, - { - "filename": "0103.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 57, - "h": 70 - }, - "frame": { - "x": 183, - "y": 73, - "w": 57, - "h": 70 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 1, - "y": 6, - "w": 46, - "h": 80 - }, - "frame": { - "x": 205, - "y": 143, - "w": 46, - "h": 80 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 2, - "w": 47, - "h": 75 - }, - "frame": { - "x": 221, - "y": 223, - "w": 47, - "h": 75 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 2, - "w": 47, - "h": 75 - }, - "frame": { - "x": 221, - "y": 223, - "w": 47, - "h": 75 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 2, - "w": 47, - "h": 75 - }, - "frame": { - "x": 221, - "y": 223, - "w": 47, - "h": 75 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 2, - "w": 47, - "h": 75 - }, - "frame": { - "x": 221, - "y": 223, - "w": 47, - "h": 75 - } - }, - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 4, - "y": 2, - "w": 47, - "h": 75 - }, - "frame": { - "x": 221, - "y": 223, - "w": 47, - "h": 75 + "x": 53, + "y": 137, + "w": 61, + "h": 73 } }, { @@ -1662,14 +1032,14 @@ "h": 73 }, "frame": { - "x": 231, - "y": 298, + "x": 114, + "y": 137, "w": 54, "h": 73 } }, { - "filename": "0084.png", + "filename": "0012.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -1678,19 +1048,19 @@ }, "spriteSourceSize": { "x": 3, - "y": 7, - "w": 56, - "h": 69 + "y": 12, + "w": 61, + "h": 73 }, "frame": { - "x": 240, - "y": 70, - "w": 56, - "h": 69 + "x": 168, + "y": 138, + "w": 61, + "h": 73 } }, { - "filename": "0091.png", + "filename": "0023.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -1699,36 +1069,120 @@ }, "spriteSourceSize": { "x": 3, - "y": 6, - "w": 47, - "h": 80 + "y": 12, + "w": 61, + "h": 73 }, "frame": { - "x": 251, + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0045.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0056.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0067.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0078.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 229, "y": 139, - "w": 47, - "h": 80 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 0, - "y": 3, - "w": 45, - "h": 79 - }, - "frame": { - "x": 268, - "y": 219, - "w": 45, - "h": 79 + "w": 61, + "h": 73 } }, { @@ -1746,8 +1200,8 @@ "h": 74 }, "frame": { - "x": 285, - "y": 298, + "x": 290, + "y": 140, "w": 49, "h": 74 } @@ -1767,8 +1221,8 @@ "h": 74 }, "frame": { - "x": 285, - "y": 298, + "x": 290, + "y": 140, "w": 49, "h": 74 } @@ -1788,8 +1242,8 @@ "h": 74 }, "frame": { - "x": 285, - "y": 298, + "x": 290, + "y": 140, "w": 49, "h": 74 } @@ -1809,8 +1263,8 @@ "h": 74 }, "frame": { - "x": 285, - "y": 298, + "x": 290, + "y": 140, "w": 49, "h": 74 } @@ -1830,8 +1284,8 @@ "h": 74 }, "frame": { - "x": 285, - "y": 298, + "x": 290, + "y": 140, "w": 49, "h": 74 } @@ -1851,8 +1305,8 @@ "h": 74 }, "frame": { - "x": 285, - "y": 298, + "x": 290, + "y": 140, "w": 49, "h": 74 } @@ -1872,33 +1326,12 @@ "h": 74 }, "frame": { - "x": 285, - "y": 298, + "x": 290, + "y": 140, "w": 49, "h": 74 } }, - { - "filename": "0096.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 54, - "h": 77 - }, - "frame": { - "x": 309, - "y": 68, - "w": 54, - "h": 77 - } - }, { "filename": "0104.png", "rotated": false, @@ -1914,14 +1347,14 @@ "h": 74 }, "frame": { - "x": 298, - "y": 145, + "x": 339, + "y": 141, "w": 52, "h": 74 } }, { - "filename": "0093.png", + "filename": "0008.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -1929,20 +1362,209 @@ "h": 86 }, "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 49, - "h": 79 + "x": 4, + "y": 2, + "w": 47, + "h": 75 }, "frame": { - "x": 313, - "y": 219, - "w": 49, - "h": 79 + "x": 391, + "y": 142, + "w": 47, + "h": 75 } }, { - "filename": "0118.png", + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0041.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0052.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0063.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0074.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0114.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 50, + "h": 75 + }, + "frame": { + "x": 438, + "y": 142, + "w": 50, + "h": 75 + } + }, + { + "filename": "0086.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 4, + "w": 51, + "h": 76 + }, + "frame": { + "x": 0, + "y": 208, + "w": 51, + "h": 76 + } + }, + { + "filename": "0119.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 11, + "w": 57, + "h": 75 + }, + "frame": { + "x": 51, + "y": 210, + "w": 57, + "h": 75 + } + }, + { + "filename": "0011.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -1952,161 +1574,203 @@ "spriteSourceSize": { "x": 3, "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0044.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0055.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0066.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0077.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0115.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 163, + "y": 211, + "w": 51, + "h": 76 + } + }, + { + "filename": "0116.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 214, + "y": 212, + "w": 51, + "h": 76 + } + }, + { + "filename": "0117.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, "w": 53, - "h": 77 + "h": 76 }, "frame": { - "x": 363, - "y": 68, + "x": 265, + "y": 214, "w": 53, - "h": 77 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 7, - "w": 52, - "h": 77 - }, - "frame": { - "x": 416, - "y": 70, - "w": 52, - "h": 77 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 7, - "w": 52, - "h": 77 - }, - "frame": { - "x": 416, - "y": 70, - "w": 52, - "h": 77 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 7, - "w": 52, - "h": 77 - }, - "frame": { - "x": 416, - "y": 70, - "w": 52, - "h": 77 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 7, - "w": 52, - "h": 77 - }, - "frame": { - "x": 416, - "y": 70, - "w": 52, - "h": 77 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 7, - "w": 52, - "h": 77 - }, - "frame": { - "x": 416, - "y": 70, - "w": 52, - "h": 77 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 7, - "w": 52, - "h": 77 - }, - "frame": { - "x": 416, - "y": 70, - "w": 52, - "h": 77 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 66, - "h": 86 - }, - "spriteSourceSize": { - "x": 3, - "y": 7, - "w": 52, - "h": 77 - }, - "frame": { - "x": 416, - "y": 70, - "w": 52, - "h": 77 + "h": 76 } }, { @@ -2124,8 +1788,8 @@ "h": 77 }, "frame": { - "x": 334, - "y": 298, + "x": 318, + "y": 215, "w": 49, "h": 77 } @@ -2145,8 +1809,8 @@ "h": 77 }, "frame": { - "x": 334, - "y": 298, + "x": 318, + "y": 215, "w": 49, "h": 77 } @@ -2166,8 +1830,8 @@ "h": 77 }, "frame": { - "x": 334, - "y": 298, + "x": 318, + "y": 215, "w": 49, "h": 77 } @@ -2187,8 +1851,8 @@ "h": 77 }, "frame": { - "x": 334, - "y": 298, + "x": 318, + "y": 215, "w": 49, "h": 77 } @@ -2208,8 +1872,8 @@ "h": 77 }, "frame": { - "x": 334, - "y": 298, + "x": 318, + "y": 215, "w": 49, "h": 77 } @@ -2229,8 +1893,8 @@ "h": 77 }, "frame": { - "x": 334, - "y": 298, + "x": 318, + "y": 215, "w": 49, "h": 77 } @@ -2250,14 +1914,14 @@ "h": 77 }, "frame": { - "x": 334, - "y": 298, + "x": 318, + "y": 215, "w": 49, "h": 77 } }, { - "filename": "0108.png", + "filename": "0010.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -2265,20 +1929,20 @@ "h": 86 }, "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 50, - "h": 80 + "x": 3, + "y": 7, + "w": 52, + "h": 77 }, "frame": { - "x": 362, - "y": 145, - "w": 50, - "h": 80 + "x": 367, + "y": 217, + "w": 52, + "h": 77 } }, { - "filename": "0094.png", + "filename": "0021.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -2286,20 +1950,20 @@ "h": 86 }, "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 50, - "h": 78 + "x": 3, + "y": 7, + "w": 52, + "h": 77 }, "frame": { - "x": 412, - "y": 147, - "w": 50, - "h": 78 + "x": 367, + "y": 217, + "w": 52, + "h": 77 } }, { - "filename": "0109.png", + "filename": "0032.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -2307,16 +1971,100 @@ "h": 86 }, "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 50, - "h": 80 + "x": 3, + "y": 7, + "w": 52, + "h": 77 }, "frame": { - "x": 462, - "y": 147, - "w": 50, - "h": 80 + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0043.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0054.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0065.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0076.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 } }, { @@ -2334,14 +2082,14 @@ "h": 77 }, "frame": { - "x": 383, - "y": 225, + "x": 419, + "y": 217, "w": 51, "h": 77 } }, { - "filename": "0114.png", + "filename": "0113.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -2350,15 +2098,99 @@ }, "spriteSourceSize": { "x": 4, - "y": 11, - "w": 50, - "h": 75 + "y": 9, + "w": 47, + "h": 77 }, "frame": { - "x": 383, - "y": 302, + "x": 0, + "y": 284, + "w": 47, + "h": 77 + } + }, + { + "filename": "0096.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 2, + "w": 54, + "h": 77 + }, + "frame": { + "x": 47, + "y": 285, + "w": 54, + "h": 77 + } + }, + { + "filename": "0097.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 55, + "h": 77 + }, + "frame": { + "x": 101, + "y": 286, + "w": 55, + "h": 77 + } + }, + { + "filename": "0118.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 53, + "h": 77 + }, + "frame": { + "x": 156, + "y": 287, + "w": 53, + "h": 77 + } + }, + { + "filename": "0094.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, "w": 50, - "h": 75 + "h": 78 + }, + "frame": { + "x": 209, + "y": 288, + "w": 50, + "h": 78 } }, { @@ -2376,12 +2208,117 @@ "h": 78 }, "frame": { - "x": 434, - "y": 227, + "x": 259, + "y": 290, "w": 48, "h": 78 } }, + { + "filename": "0112.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 47, + "h": 78 + }, + "frame": { + "x": 307, + "y": 292, + "w": 47, + "h": 78 + } + }, + { + "filename": "0087.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 3, + "w": 45, + "h": 79 + }, + "frame": { + "x": 354, + "y": 294, + "w": 45, + "h": 79 + } + }, + { + "filename": "0093.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 49, + "h": 79 + }, + "frame": { + "x": 399, + "y": 294, + "w": 49, + "h": 79 + } + }, + { + "filename": "0088.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 4, + "w": 43, + "h": 82 + }, + "frame": { + "x": 448, + "y": 294, + "w": 43, + "h": 82 + } + }, + { + "filename": "0089.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 6, + "w": 44, + "h": 80 + }, + "frame": { + "x": 0, + "y": 361, + "w": 44, + "h": 80 + } + }, { "filename": "0106.png", "rotated": false, @@ -2397,8 +2334,8 @@ "h": 79 }, "frame": { - "x": 433, - "y": 305, + "x": 44, + "y": 362, "w": 48, "h": 79 } @@ -2418,8 +2355,8 @@ "h": 79 }, "frame": { - "x": 254, - "y": 372, + "x": 92, + "y": 363, "w": 48, "h": 79 } @@ -2439,8 +2376,8 @@ "h": 79 }, "frame": { - "x": 302, - "y": 375, + "x": 140, + "y": 364, "w": 48, "h": 79 } @@ -2460,14 +2397,14 @@ "h": 79 }, "frame": { - "x": 350, - "y": 377, + "x": 188, + "y": 366, "w": 48, "h": 79 } }, { - "filename": "0112.png", + "filename": "0090.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -2475,20 +2412,20 @@ "h": 86 }, "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 47, - "h": 78 + "x": 1, + "y": 6, + "w": 46, + "h": 80 }, "frame": { - "x": 398, - "y": 384, - "w": 47, - "h": 78 + "x": 236, + "y": 368, + "w": 46, + "h": 80 } }, { - "filename": "0113.png", + "filename": "0091.png", "rotated": false, "trimmed": true, "sourceSize": { @@ -2496,16 +2433,79 @@ "h": 86 }, "spriteSourceSize": { - "x": 4, - "y": 9, + "x": 3, + "y": 6, "w": 47, - "h": 77 + "h": 80 }, "frame": { - "x": 445, - "y": 384, + "x": 282, + "y": 370, "w": 47, - "h": 77 + "h": 80 + } + }, + { + "filename": "0108.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 4, + "w": 50, + "h": 80 + }, + "frame": { + "x": 329, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0109.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 50, + "h": 80 + }, + "frame": { + "x": 379, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0092.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 48, + "h": 81 + }, + "frame": { + "x": 429, + "y": 376, + "w": 48, + "h": 81 } } ] @@ -2513,7 +2513,6 @@ ], "meta": { "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:9e48c848a41465f492d915f4dce7450d:4400a226a5397c6ca3d4bfb29ba2dc10:bad05b37c157676604256a043511a6a2$" + "version": "3.0" } } diff --git a/public/images/pokemon/exp/shiny/670-white.json b/public/images/pokemon/exp/shiny/670-white.json index abebccdf384..521ca042bf2 100644 --- a/public/images/pokemon/exp/shiny/670-white.json +++ b/public/images/pokemon/exp/shiny/670-white.json @@ -4,219 +4,156 @@ "image": "670-white.png", "format": "RGBA8888", "size": { - "w": 132, - "h": 132 + "w": 491, + "h": 491 }, "scale": 1, "frames": [ { - "filename": "0002.png", + "filename": "0101.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 4, + "y": 4, + "w": 60, + "h": 65 }, "frame": { "x": 0, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 65 } }, { - "filename": "0006.png", + "filename": "0083.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, - "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 66 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, + "x": 3, + "y": 9, + "w": 60, "h": 67 }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 66 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, - "frame": { - "x": 40, + "x": 60, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 67 } }, { - "filename": "0003.png", + "filename": "0102.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 5, + "w": 60, + "h": 67 }, "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 + "x": 120, + "y": 0, + "w": 60, + "h": 67 } }, { - "filename": "0005.png", + "filename": "0082.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 10, + "w": 61, + "h": 68 }, "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, + "x": 180, "y": 0, - "w": 39, - "h": 66 - }, - "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 + "w": 61, + "h": 68 } }, { - "filename": "0011.png", + "filename": "0100.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 - }, - "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, + "x": 5, "y": 2, - "w": 40, - "h": 65 + "w": 60, + "h": 68 }, "frame": { - "x": 79, - "y": 66, - "w": 40, - "h": 65 + "x": 241, + "y": 0, + "w": 60, + "h": 68 } }, { - "filename": "0007.png", + "filename": "0081.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 2, - "w": 40, - "h": 65 + "x": 4, + "y": 12, + "w": 62, + "h": 69 }, "frame": { - "x": 79, - "y": 66, - "w": 40, - "h": 65 + "x": 301, + "y": 0, + "w": 62, + "h": 69 + } + }, + { + "filename": "0084.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 56, + "h": 69 + }, + "frame": { + "x": 363, + "y": 0, + "w": 56, + "h": 69 } }, { @@ -224,20 +161,1763 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0037.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0048.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0059.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0070.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0038.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0049.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0060.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0071.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0085.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 56, + "h": 70 + }, + "frame": { + "x": 59, + "y": 67, + "w": 56, + "h": 70 + } + }, + { + "filename": "0099.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 57, + "h": 70 + }, + "frame": { + "x": 115, + "y": 67, + "w": 57, + "h": 70 + } + }, + { + "filename": "0103.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 57, + "h": 70 + }, + "frame": { + "x": 172, + "y": 68, + "w": 57, + "h": 70 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0036.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0047.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0058.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0069.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0080.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 292, + "y": 69, + "w": 63, + "h": 71 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0046.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0057.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0068.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0079.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 418, + "y": 70, + "w": 63, + "h": 72 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0039.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0050.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0061.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0072.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 53, + "y": 137, + "w": 61, + "h": 73 + } + }, + { + "filename": "0098.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 54, + "h": 73 + }, + "frame": { + "x": 114, + "y": 137, + "w": 54, + "h": 73 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0045.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0056.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0067.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0078.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 229, + "y": 139, + "w": 61, + "h": 73 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0040.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0051.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0062.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0073.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0104.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 52, + "h": 74 + }, + "frame": { + "x": 339, + "y": 141, + "w": 52, + "h": 74 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0041.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0052.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0063.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0074.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0114.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 50, + "h": 75 + }, + "frame": { + "x": 438, + "y": 142, + "w": 50, + "h": 75 + } + }, + { + "filename": "0086.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 38, - "h": 65 + "y": 4, + "w": 51, + "h": 76 }, "frame": { - "x": 80, - "y": 0, - "w": 38, - "h": 65 + "x": 0, + "y": 208, + "w": 51, + "h": 76 + } + }, + { + "filename": "0119.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 11, + "w": 57, + "h": 75 + }, + "frame": { + "x": 51, + "y": 210, + "w": 57, + "h": 75 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0044.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0055.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0066.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0077.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0115.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 163, + "y": 211, + "w": 51, + "h": 76 + } + }, + { + "filename": "0116.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 214, + "y": 212, + "w": 51, + "h": 76 + } + }, + { + "filename": "0117.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 53, + "h": 76 + }, + "frame": { + "x": 265, + "y": 214, + "w": 53, + "h": 76 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0042.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0053.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0064.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0075.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 } }, { @@ -245,20 +1925,587 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0043.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0054.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0065.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0076.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0095.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 3, + "w": 51, + "h": 77 + }, + "frame": { + "x": 419, + "y": 217, + "w": 51, + "h": 77 + } + }, + { + "filename": "0113.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 9, + "w": 47, + "h": 77 + }, + "frame": { + "x": 0, + "y": 284, + "w": 47, + "h": 77 + } + }, + { + "filename": "0096.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 2, + "w": 54, + "h": 77 + }, + "frame": { + "x": 47, + "y": 285, + "w": 54, + "h": 77 + } + }, + { + "filename": "0097.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 55, + "h": 77 + }, + "frame": { + "x": 101, + "y": 286, + "w": 55, + "h": 77 + } + }, + { + "filename": "0118.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 53, + "h": 77 + }, + "frame": { + "x": 156, + "y": 287, + "w": 53, + "h": 77 + } + }, + { + "filename": "0094.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 50, + "h": 78 + }, + "frame": { + "x": 209, + "y": 288, + "w": 50, + "h": 78 + } + }, + { + "filename": "0105.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 0, + "w": 48, + "h": 78 + }, + "frame": { + "x": 259, + "y": 290, + "w": 48, + "h": 78 + } + }, + { + "filename": "0112.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 47, + "h": 78 + }, + "frame": { + "x": 307, + "y": 292, + "w": 47, + "h": 78 + } + }, + { + "filename": "0087.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 38, - "h": 65 + "y": 3, + "w": 45, + "h": 79 }, "frame": { - "x": 80, - "y": 0, - "w": 38, - "h": 65 + "x": 354, + "y": 294, + "w": 45, + "h": 79 + } + }, + { + "filename": "0093.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 49, + "h": 79 + }, + "frame": { + "x": 399, + "y": 294, + "w": 49, + "h": 79 + } + }, + { + "filename": "0088.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 4, + "w": 43, + "h": 82 + }, + "frame": { + "x": 448, + "y": 294, + "w": 43, + "h": 82 + } + }, + { + "filename": "0089.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 6, + "w": 44, + "h": 80 + }, + "frame": { + "x": 0, + "y": 361, + "w": 44, + "h": 80 + } + }, + { + "filename": "0106.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 2, + "w": 48, + "h": 79 + }, + "frame": { + "x": 44, + "y": 362, + "w": 48, + "h": 79 + } + }, + { + "filename": "0107.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 3, + "w": 48, + "h": 79 + }, + "frame": { + "x": 92, + "y": 363, + "w": 48, + "h": 79 + } + }, + { + "filename": "0110.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 48, + "h": 79 + }, + "frame": { + "x": 140, + "y": 364, + "w": 48, + "h": 79 + } + }, + { + "filename": "0111.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 48, + "h": 79 + }, + "frame": { + "x": 188, + "y": 366, + "w": 48, + "h": 79 + } + }, + { + "filename": "0090.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 46, + "h": 80 + }, + "frame": { + "x": 236, + "y": 368, + "w": 46, + "h": 80 + } + }, + { + "filename": "0091.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 47, + "h": 80 + }, + "frame": { + "x": 282, + "y": 370, + "w": 47, + "h": 80 + } + }, + { + "filename": "0108.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 4, + "w": 50, + "h": 80 + }, + "frame": { + "x": 329, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0109.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 50, + "h": 80 + }, + "frame": { + "x": 379, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0092.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 48, + "h": 81 + }, + "frame": { + "x": 429, + "y": 376, + "w": 48, + "h": 81 } } ] @@ -266,7 +2513,6 @@ ], "meta": { "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:01dae0b687f736498b0c925683fd383c:e338b45598e34ba8ebac09b743db09de:3b5fc0bf297b00d7e2adc65825803c41$" + "version": "3.0" } } diff --git a/public/images/pokemon/exp/shiny/670-yellow.json b/public/images/pokemon/exp/shiny/670-yellow.json index 50b2d8ed3fc..ffd722006d3 100644 --- a/public/images/pokemon/exp/shiny/670-yellow.json +++ b/public/images/pokemon/exp/shiny/670-yellow.json @@ -4,219 +4,156 @@ "image": "670-yellow.png", "format": "RGBA8888", "size": { - "w": 132, - "h": 132 + "w": 491, + "h": 491 }, "scale": 1, "frames": [ { - "filename": "0002.png", + "filename": "0101.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 + "x": 4, + "y": 4, + "w": 60, + "h": 65 }, "frame": { "x": 0, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 65 } }, { - "filename": "0006.png", + "filename": "0083.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, - "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 66 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, + "x": 3, + "y": 9, + "w": 60, "h": 67 }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, "frame": { - "x": 0, - "y": 66, - "w": 40, - "h": 66 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 40, - "h": 66 - }, - "frame": { - "x": 40, + "x": 60, "y": 0, - "w": 40, - "h": 66 + "w": 60, + "h": 67 } }, { - "filename": "0003.png", + "filename": "0102.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 5, + "w": 60, + "h": 67 }, "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 + "x": 120, + "y": 0, + "w": 60, + "h": 67 } }, { - "filename": "0005.png", + "filename": "0082.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 + "x": 4, + "y": 10, + "w": 61, + "h": 68 }, "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, + "x": 180, "y": 0, - "w": 39, - "h": 66 - }, - "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 + "w": 61, + "h": 68 } }, { - "filename": "0011.png", + "filename": "0100.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 39, - "h": 66 - }, - "frame": { - "x": 40, - "y": 66, - "w": 39, - "h": 66 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 40, - "h": 67 - }, - "spriteSourceSize": { - "x": 0, + "x": 5, "y": 2, - "w": 40, - "h": 65 + "w": 60, + "h": 68 }, "frame": { - "x": 79, - "y": 66, - "w": 40, - "h": 65 + "x": 241, + "y": 0, + "w": 60, + "h": 68 } }, { - "filename": "0007.png", + "filename": "0081.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 }, "spriteSourceSize": { - "x": 0, - "y": 2, - "w": 40, - "h": 65 + "x": 4, + "y": 12, + "w": 62, + "h": 69 }, "frame": { - "x": 79, - "y": 66, - "w": 40, - "h": 65 + "x": 301, + "y": 0, + "w": 62, + "h": 69 + } + }, + { + "filename": "0084.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 56, + "h": 69 + }, + "frame": { + "x": 363, + "y": 0, + "w": 56, + "h": 69 } }, { @@ -224,20 +161,1763 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0037.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0048.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0059.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0070.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 62, + "h": 70 + }, + "frame": { + "x": 419, + "y": 0, + "w": 62, + "h": 70 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0038.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0049.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0060.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0071.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 59, + "h": 70 + }, + "frame": { + "x": 0, + "y": 65, + "w": 59, + "h": 70 + } + }, + { + "filename": "0085.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 56, + "h": 70 + }, + "frame": { + "x": 59, + "y": 67, + "w": 56, + "h": 70 + } + }, + { + "filename": "0099.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 57, + "h": 70 + }, + "frame": { + "x": 115, + "y": 67, + "w": 57, + "h": 70 + } + }, + { + "filename": "0103.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 57, + "h": 70 + }, + "frame": { + "x": 172, + "y": 68, + "w": 57, + "h": 70 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0036.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0047.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0058.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0069.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 229, + "y": 68, + "w": 63, + "h": 71 + } + }, + { + "filename": "0080.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 71 + }, + "frame": { + "x": 292, + "y": 69, + "w": 63, + "h": 71 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0046.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0057.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0068.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 355, + "y": 69, + "w": 63, + "h": 72 + } + }, + { + "filename": "0079.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 13, + "w": 63, + "h": 72 + }, + "frame": { + "x": 418, + "y": 70, + "w": 63, + "h": 72 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0039.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0050.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0061.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0072.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 53, + "h": 73 + }, + "frame": { + "x": 0, + "y": 135, + "w": 53, + "h": 73 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 53, + "y": 137, + "w": 61, + "h": 73 + } + }, + { + "filename": "0098.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 0, + "w": 54, + "h": 73 + }, + "frame": { + "x": 114, + "y": 137, + "w": 54, + "h": 73 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0045.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0056.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0067.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 168, + "y": 138, + "w": 61, + "h": 73 + } + }, + { + "filename": "0078.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 12, + "w": 61, + "h": 73 + }, + "frame": { + "x": 229, + "y": 139, + "w": 61, + "h": 73 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0040.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0051.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0062.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0073.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 49, + "h": 74 + }, + "frame": { + "x": 290, + "y": 140, + "w": 49, + "h": 74 + } + }, + { + "filename": "0104.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 1, + "w": 52, + "h": 74 + }, + "frame": { + "x": 339, + "y": 141, + "w": 52, + "h": 74 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0041.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0052.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0063.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0074.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 47, + "h": 75 + }, + "frame": { + "x": 391, + "y": 142, + "w": 47, + "h": 75 + } + }, + { + "filename": "0114.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 50, + "h": 75 + }, + "frame": { + "x": 438, + "y": 142, + "w": 50, + "h": 75 + } + }, + { + "filename": "0086.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 38, - "h": 65 + "y": 4, + "w": 51, + "h": 76 }, "frame": { - "x": 80, - "y": 0, - "w": 38, - "h": 65 + "x": 0, + "y": 208, + "w": 51, + "h": 76 + } + }, + { + "filename": "0119.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 11, + "w": 57, + "h": 75 + }, + "frame": { + "x": 51, + "y": 210, + "w": 57, + "h": 75 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0044.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0055.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0066.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0077.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 55, + "h": 76 + }, + "frame": { + "x": 108, + "y": 210, + "w": 55, + "h": 76 + } + }, + { + "filename": "0115.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 163, + "y": 211, + "w": 51, + "h": 76 + } + }, + { + "filename": "0116.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 51, + "h": 76 + }, + "frame": { + "x": 214, + "y": 212, + "w": 51, + "h": 76 + } + }, + { + "filename": "0117.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 10, + "w": 53, + "h": 76 + }, + "frame": { + "x": 265, + "y": 214, + "w": 53, + "h": 76 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0042.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0053.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0064.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 + } + }, + { + "filename": "0075.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 49, + "h": 77 + }, + "frame": { + "x": 318, + "y": 215, + "w": 49, + "h": 77 } }, { @@ -245,20 +1925,587 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 40, - "h": 67 + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0043.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0054.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0065.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0076.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 52, + "h": 77 + }, + "frame": { + "x": 367, + "y": 217, + "w": 52, + "h": 77 + } + }, + { + "filename": "0095.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 3, + "w": 51, + "h": 77 + }, + "frame": { + "x": 419, + "y": 217, + "w": 51, + "h": 77 + } + }, + { + "filename": "0113.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 9, + "w": 47, + "h": 77 + }, + "frame": { + "x": 0, + "y": 284, + "w": 47, + "h": 77 + } + }, + { + "filename": "0096.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 2, + "w": 54, + "h": 77 + }, + "frame": { + "x": 47, + "y": 285, + "w": 54, + "h": 77 + } + }, + { + "filename": "0097.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 0, + "w": 55, + "h": 77 + }, + "frame": { + "x": 101, + "y": 286, + "w": 55, + "h": 77 + } + }, + { + "filename": "0118.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 9, + "w": 53, + "h": 77 + }, + "frame": { + "x": 156, + "y": 287, + "w": 53, + "h": 77 + } + }, + { + "filename": "0094.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 50, + "h": 78 + }, + "frame": { + "x": 209, + "y": 288, + "w": 50, + "h": 78 + } + }, + { + "filename": "0105.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 0, + "w": 48, + "h": 78 + }, + "frame": { + "x": 259, + "y": 290, + "w": 48, + "h": 78 + } + }, + { + "filename": "0112.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 47, + "h": 78 + }, + "frame": { + "x": 307, + "y": 292, + "w": 47, + "h": 78 + } + }, + { + "filename": "0087.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 }, "spriteSourceSize": { "x": 0, - "y": 0, - "w": 38, - "h": 65 + "y": 3, + "w": 45, + "h": 79 }, "frame": { - "x": 80, - "y": 0, - "w": 38, - "h": 65 + "x": 354, + "y": 294, + "w": 45, + "h": 79 + } + }, + { + "filename": "0093.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 49, + "h": 79 + }, + "frame": { + "x": 399, + "y": 294, + "w": 49, + "h": 79 + } + }, + { + "filename": "0088.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 4, + "w": 43, + "h": 82 + }, + "frame": { + "x": 448, + "y": 294, + "w": 43, + "h": 82 + } + }, + { + "filename": "0089.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 0, + "y": 6, + "w": 44, + "h": 80 + }, + "frame": { + "x": 0, + "y": 361, + "w": 44, + "h": 80 + } + }, + { + "filename": "0106.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 2, + "w": 48, + "h": 79 + }, + "frame": { + "x": 44, + "y": 362, + "w": 48, + "h": 79 + } + }, + { + "filename": "0107.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 3, + "w": 48, + "h": 79 + }, + "frame": { + "x": 92, + "y": 363, + "w": 48, + "h": 79 + } + }, + { + "filename": "0110.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 48, + "h": 79 + }, + "frame": { + "x": 140, + "y": 364, + "w": 48, + "h": 79 + } + }, + { + "filename": "0111.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 7, + "w": 48, + "h": 79 + }, + "frame": { + "x": 188, + "y": 366, + "w": 48, + "h": 79 + } + }, + { + "filename": "0090.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 46, + "h": 80 + }, + "frame": { + "x": 236, + "y": 368, + "w": 46, + "h": 80 + } + }, + { + "filename": "0091.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 3, + "y": 6, + "w": 47, + "h": 80 + }, + "frame": { + "x": 282, + "y": 370, + "w": 47, + "h": 80 + } + }, + { + "filename": "0108.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 4, + "w": 50, + "h": 80 + }, + "frame": { + "x": 329, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0109.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 50, + "h": 80 + }, + "frame": { + "x": 379, + "y": 373, + "w": 50, + "h": 80 + } + }, + { + "filename": "0092.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 66, + "h": 86 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 48, + "h": 81 + }, + "frame": { + "x": 429, + "y": 376, + "w": 48, + "h": 81 } } ] @@ -266,7 +2513,6 @@ ], "meta": { "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:91de02bbe0d8b5422a0590701d05eada:8288efcc5ff6f1e55757cca59d77c9b3:e33b1f5cc51f17c070e89705329e1246$" + "version": "3.0" } } diff --git a/public/images/pokemon/exp/shiny/821.png b/public/images/pokemon/exp/shiny/821.png index 5c03c17c793..6f6cf7f2991 100644 Binary files a/public/images/pokemon/exp/shiny/821.png and b/public/images/pokemon/exp/shiny/821.png differ diff --git a/public/images/pokemon/female/178.png b/public/images/pokemon/female/178.png index 87815289b78..96323eeea94 100644 Binary files a/public/images/pokemon/female/178.png and b/public/images/pokemon/female/178.png differ diff --git a/public/images/pokemon/female/457.png b/public/images/pokemon/female/457.png index 14c6c698db5..2aec3e26a07 100644 Binary files a/public/images/pokemon/female/457.png and b/public/images/pokemon/female/457.png differ diff --git a/public/images/pokemon/icons/2/178.png b/public/images/pokemon/icons/2/178.png index 3b604631957..9f4b97ffc1c 100644 Binary files a/public/images/pokemon/icons/2/178.png and b/public/images/pokemon/icons/2/178.png differ diff --git a/public/images/pokemon/icons/2/178s.png b/public/images/pokemon/icons/2/178s.png index 8605b01c01e..55edd478de2 100644 Binary files a/public/images/pokemon/icons/2/178s.png and b/public/images/pokemon/icons/2/178s.png differ diff --git a/public/images/pokemon/icons/variant/2/177_2.png b/public/images/pokemon/icons/variant/2/177_2.png new file mode 100644 index 00000000000..676224d2827 Binary files /dev/null and b/public/images/pokemon/icons/variant/2/177_2.png differ diff --git a/public/images/pokemon/icons/variant/2/177_3.png b/public/images/pokemon/icons/variant/2/177_3.png new file mode 100644 index 00000000000..2952d8f4926 Binary files /dev/null and b/public/images/pokemon/icons/variant/2/177_3.png differ diff --git a/public/images/pokemon/icons/variant/2/178_2.png b/public/images/pokemon/icons/variant/2/178_2.png new file mode 100644 index 00000000000..060c25138e0 Binary files /dev/null and b/public/images/pokemon/icons/variant/2/178_2.png differ diff --git a/public/images/pokemon/icons/variant/2/178_3.png b/public/images/pokemon/icons/variant/2/178_3.png new file mode 100644 index 00000000000..34d38f8cf1f Binary files /dev/null and b/public/images/pokemon/icons/variant/2/178_3.png differ diff --git a/public/images/pokemon/shiny/867.png b/public/images/pokemon/shiny/867.png index a56e107a9c4..0443deeaf9c 100644 Binary files a/public/images/pokemon/shiny/867.png and b/public/images/pokemon/shiny/867.png differ diff --git a/public/images/pokemon/shiny/female/178.png b/public/images/pokemon/shiny/female/178.png index 7695b06e155..b0d983307e4 100644 Binary files a/public/images/pokemon/shiny/female/178.png and b/public/images/pokemon/shiny/female/178.png differ diff --git a/public/images/pokemon/variant/_masterlist.json b/public/images/pokemon/variant/_masterlist.json index 3c40a16e081..11578f2e084 100644 --- a/public/images/pokemon/variant/_masterlist.json +++ b/public/images/pokemon/variant/_masterlist.json @@ -3815,6 +3815,11 @@ 1, 1 ], + "178": [ + 0, + 2, + 2 + ], "185": [ 0, 1, @@ -7833,6 +7838,11 @@ 1, 1 ], + "178": [ + 0, + 2, + 2 + ], "185": [ 0, 1, diff --git a/public/images/pokemon/variant/back/177_2.png b/public/images/pokemon/variant/back/177_2.png new file mode 100644 index 00000000000..1c775f2ef3f Binary files /dev/null and b/public/images/pokemon/variant/back/177_2.png differ diff --git a/public/images/pokemon/variant/back/177_3.png b/public/images/pokemon/variant/back/177_3.png new file mode 100644 index 00000000000..04ec33986cd Binary files /dev/null and b/public/images/pokemon/variant/back/177_3.png differ diff --git a/public/images/pokemon/variant/back/female/178_2.json b/public/images/pokemon/variant/back/female/178_2.json new file mode 100644 index 00000000000..202894b474c --- /dev/null +++ b/public/images/pokemon/variant/back/female/178_2.json @@ -0,0 +1,2372 @@ +{ + "textures": [ + { + "image": "178_2.png", + "format": "RGBA8888", + "size": { + "w": 263, + "h": 263 + }, + "scale": 1, + "frames": [ + { + "filename": "0101.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 67, + "h": 58 + }, + "frame": { + "x": 0, + "y": 0, + "w": 67, + "h": 58 + } + }, + { + "filename": "0102.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 67, + "h": 58 + }, + "frame": { + "x": 0, + "y": 0, + "w": 67, + "h": 58 + } + }, + { + "filename": "0105.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 3, + "y": 0, + "w": 64, + "h": 58 + }, + "frame": { + "x": 0, + "y": 58, + "w": 64, + "h": 58 + } + }, + { + "filename": "0106.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 3, + "y": 0, + "w": 64, + "h": 58 + }, + "frame": { + "x": 0, + "y": 58, + "w": 64, + "h": 58 + } + }, + { + "filename": "0103.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 58, + "h": 57 + }, + "frame": { + "x": 67, + "y": 0, + "w": 58, + "h": 57 + } + }, + { + "filename": "0104.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 58, + "h": 57 + }, + "frame": { + "x": 67, + "y": 0, + "w": 58, + "h": 57 + } + }, + { + "filename": "0097.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 59, + "h": 54 + }, + "frame": { + "x": 0, + "y": 116, + "w": 59, + "h": 54 + } + }, + { + "filename": "0098.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 59, + "h": 54 + }, + "frame": { + "x": 0, + "y": 116, + "w": 59, + "h": 54 + } + }, + { + "filename": "0107.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 7, + "y": 2, + "w": 55, + "h": 57 + }, + "frame": { + "x": 125, + "y": 0, + "w": 55, + "h": 57 + } + }, + { + "filename": "0108.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 7, + "y": 2, + "w": 55, + "h": 57 + }, + "frame": { + "x": 125, + "y": 0, + "w": 55, + "h": 57 + } + }, + { + "filename": "0099.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 54, + "h": 57 + }, + "frame": { + "x": 0, + "y": 170, + "w": 54, + "h": 57 + } + }, + { + "filename": "0100.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 54, + "h": 57 + }, + "frame": { + "x": 0, + "y": 170, + "w": 54, + "h": 57 + } + }, + { + "filename": "0095.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 54, + "h": 52 + }, + "frame": { + "x": 180, + "y": 0, + "w": 54, + "h": 52 + } + }, + { + "filename": "0096.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 54, + "h": 52 + }, + "frame": { + "x": 180, + "y": 0, + "w": 54, + "h": 52 + } + }, + { + "filename": "0109.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 3, + "w": 48, + "h": 56 + }, + "frame": { + "x": 54, + "y": 170, + "w": 48, + "h": 56 + } + }, + { + "filename": "0110.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 3, + "w": 48, + "h": 56 + }, + "frame": { + "x": 54, + "y": 170, + "w": 48, + "h": 56 + } + }, + { + "filename": "0111.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 5, + "w": 48, + "h": 54 + }, + "frame": { + "x": 59, + "y": 116, + "w": 48, + "h": 54 + } + }, + { + "filename": "0112.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 5, + "w": 48, + "h": 54 + }, + "frame": { + "x": 59, + "y": 116, + "w": 48, + "h": 54 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0043.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0044.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0057.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0058.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0071.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0072.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0036.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0037.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0038.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0063.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0064.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0065.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0066.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0004.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0041.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0042.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0059.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0060.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0069.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0070.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0039.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0040.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0061.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0062.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0067.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0068.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0045.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0046.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0055.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0056.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0073.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0074.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0083.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0084.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0047.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0048.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0053.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0054.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0075.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0076.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0081.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0082.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0049.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0050.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0051.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0052.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0077.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0078.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0079.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0080.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0085.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 199, + "y": 156, + "w": 48, + "h": 52 + } + }, + { + "filename": "0086.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 199, + "y": 156, + "w": 48, + "h": 52 + } + }, + { + "filename": "0087.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 48, + "h": 51 + }, + "frame": { + "x": 150, + "y": 162, + "w": 48, + "h": 51 + } + }, + { + "filename": "0088.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 48, + "h": 51 + }, + "frame": { + "x": 150, + "y": 162, + "w": 48, + "h": 51 + } + }, + { + "filename": "0089.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 9, + "w": 48, + "h": 50 + }, + "frame": { + "x": 150, + "y": 213, + "w": 48, + "h": 50 + } + }, + { + "filename": "0090.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 9, + "w": 48, + "h": 50 + }, + "frame": { + "x": 150, + "y": 213, + "w": 48, + "h": 50 + } + }, + { + "filename": "0091.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 9, + "w": 48, + "h": 50 + }, + "frame": { + "x": 150, + "y": 213, + "w": 48, + "h": 50 + } + }, + { + "filename": "0092.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 9, + "w": 48, + "h": 50 + }, + "frame": { + "x": 150, + "y": 213, + "w": 48, + "h": 50 + } + }, + { + "filename": "0093.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 50, + "h": 51 + }, + "frame": { + "x": 198, + "y": 208, + "w": 50, + "h": 51 + } + }, + { + "filename": "0094.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 50, + "h": 51 + }, + "frame": { + "x": 198, + "y": 208, + "w": 50, + "h": 51 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:09a3b8263891ad99a615fcb08d56ef56:420667b66547b2d5cc8ddbc8c794dd00:319c95b9f5acf1139a5c6761349cd6ab$" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/back/female/178_2.png b/public/images/pokemon/variant/back/female/178_2.png new file mode 100644 index 00000000000..3bf2b443719 Binary files /dev/null and b/public/images/pokemon/variant/back/female/178_2.png differ diff --git a/public/images/pokemon/variant/back/female/178_3.json b/public/images/pokemon/variant/back/female/178_3.json new file mode 100644 index 00000000000..a1ba7af59d3 --- /dev/null +++ b/public/images/pokemon/variant/back/female/178_3.json @@ -0,0 +1,2372 @@ +{ + "textures": [ + { + "image": "178_3.png", + "format": "RGBA8888", + "size": { + "w": 263, + "h": 263 + }, + "scale": 1, + "frames": [ + { + "filename": "0101.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 67, + "h": 58 + }, + "frame": { + "x": 0, + "y": 0, + "w": 67, + "h": 58 + } + }, + { + "filename": "0102.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 67, + "h": 58 + }, + "frame": { + "x": 0, + "y": 0, + "w": 67, + "h": 58 + } + }, + { + "filename": "0105.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 3, + "y": 0, + "w": 64, + "h": 58 + }, + "frame": { + "x": 0, + "y": 58, + "w": 64, + "h": 58 + } + }, + { + "filename": "0106.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 3, + "y": 0, + "w": 64, + "h": 58 + }, + "frame": { + "x": 0, + "y": 58, + "w": 64, + "h": 58 + } + }, + { + "filename": "0103.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 58, + "h": 57 + }, + "frame": { + "x": 67, + "y": 0, + "w": 58, + "h": 57 + } + }, + { + "filename": "0104.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 58, + "h": 57 + }, + "frame": { + "x": 67, + "y": 0, + "w": 58, + "h": 57 + } + }, + { + "filename": "0097.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 59, + "h": 54 + }, + "frame": { + "x": 0, + "y": 116, + "w": 59, + "h": 54 + } + }, + { + "filename": "0098.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 59, + "h": 54 + }, + "frame": { + "x": 0, + "y": 116, + "w": 59, + "h": 54 + } + }, + { + "filename": "0107.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 7, + "y": 2, + "w": 55, + "h": 57 + }, + "frame": { + "x": 125, + "y": 0, + "w": 55, + "h": 57 + } + }, + { + "filename": "0108.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 7, + "y": 2, + "w": 55, + "h": 57 + }, + "frame": { + "x": 125, + "y": 0, + "w": 55, + "h": 57 + } + }, + { + "filename": "0099.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 54, + "h": 57 + }, + "frame": { + "x": 0, + "y": 170, + "w": 54, + "h": 57 + } + }, + { + "filename": "0100.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 4, + "y": 2, + "w": 54, + "h": 57 + }, + "frame": { + "x": 0, + "y": 170, + "w": 54, + "h": 57 + } + }, + { + "filename": "0095.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 54, + "h": 52 + }, + "frame": { + "x": 180, + "y": 0, + "w": 54, + "h": 52 + } + }, + { + "filename": "0096.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 54, + "h": 52 + }, + "frame": { + "x": 180, + "y": 0, + "w": 54, + "h": 52 + } + }, + { + "filename": "0109.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 3, + "w": 48, + "h": 56 + }, + "frame": { + "x": 54, + "y": 170, + "w": 48, + "h": 56 + } + }, + { + "filename": "0110.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 3, + "w": 48, + "h": 56 + }, + "frame": { + "x": 54, + "y": 170, + "w": 48, + "h": 56 + } + }, + { + "filename": "0111.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 5, + "w": 48, + "h": 54 + }, + "frame": { + "x": 59, + "y": 116, + "w": 48, + "h": 54 + } + }, + { + "filename": "0112.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 5, + "w": 48, + "h": 54 + }, + "frame": { + "x": 59, + "y": 116, + "w": 48, + "h": 54 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0043.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0044.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0057.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0058.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0071.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0072.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 102, + "y": 170, + "w": 48, + "h": 52 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0036.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0037.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0038.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0063.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0064.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0065.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0066.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 52, + "h": 52 + }, + "frame": { + "x": 180, + "y": 52, + "w": 52, + "h": 52 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0004.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0041.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0042.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0059.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0060.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0069.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0070.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 50, + "h": 52 + }, + "frame": { + "x": 64, + "y": 58, + "w": 50, + "h": 52 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0039.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0040.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0061.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0062.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0067.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0068.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 9, + "y": 7, + "w": 51, + "h": 52 + }, + "frame": { + "x": 114, + "y": 57, + "w": 51, + "h": 52 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0045.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0046.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0055.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0056.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0073.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0074.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0083.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0084.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 11, + "y": 7, + "w": 47, + "h": 52 + }, + "frame": { + "x": 107, + "y": 110, + "w": 47, + "h": 52 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0047.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0048.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0053.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0054.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0075.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0076.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0081.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0082.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 154, + "y": 109, + "w": 45, + "h": 52 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0049.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0050.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0051.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0052.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0077.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0078.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0079.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0080.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 13, + "y": 7, + "w": 45, + "h": 52 + }, + "frame": { + "x": 199, + "y": 104, + "w": 45, + "h": 52 + } + }, + { + "filename": "0085.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 199, + "y": 156, + "w": 48, + "h": 52 + } + }, + { + "filename": "0086.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 7, + "w": 48, + "h": 52 + }, + "frame": { + "x": 199, + "y": 156, + "w": 48, + "h": 52 + } + }, + { + "filename": "0087.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 48, + "h": 51 + }, + "frame": { + "x": 150, + "y": 162, + "w": 48, + "h": 51 + } + }, + { + "filename": "0088.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 48, + "h": 51 + }, + "frame": { + "x": 150, + "y": 162, + "w": 48, + "h": 51 + } + }, + { + "filename": "0089.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 9, + "w": 48, + "h": 50 + }, + "frame": { + "x": 150, + "y": 213, + "w": 48, + "h": 50 + } + }, + { + "filename": "0090.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 9, + "w": 48, + "h": 50 + }, + "frame": { + "x": 150, + "y": 213, + "w": 48, + "h": 50 + } + }, + { + "filename": "0091.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 9, + "w": 48, + "h": 50 + }, + "frame": { + "x": 150, + "y": 213, + "w": 48, + "h": 50 + } + }, + { + "filename": "0092.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 9, + "w": 48, + "h": 50 + }, + "frame": { + "x": 150, + "y": 213, + "w": 48, + "h": 50 + } + }, + { + "filename": "0093.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 50, + "h": 51 + }, + "frame": { + "x": 198, + "y": 208, + "w": 50, + "h": 51 + } + }, + { + "filename": "0094.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 67, + "h": 59 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 50, + "h": 51 + }, + "frame": { + "x": 198, + "y": 208, + "w": 50, + "h": 51 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:09a3b8263891ad99a615fcb08d56ef56:420667b66547b2d5cc8ddbc8c794dd00:319c95b9f5acf1139a5c6761349cd6ab$" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/back/female/178_3.png b/public/images/pokemon/variant/back/female/178_3.png new file mode 100644 index 00000000000..9533621c6d6 Binary files /dev/null and b/public/images/pokemon/variant/back/female/178_3.png differ diff --git a/public/images/pokemon/variant/exp/800-ultra.json b/public/images/pokemon/variant/exp/800-ultra.json index 53dd9b55df0..cab917ec271 100644 --- a/public/images/pokemon/variant/exp/800-ultra.json +++ b/public/images/pokemon/variant/exp/800-ultra.json @@ -1,21 +1,5 @@ { "1": { - "b0a080": "e552ec", - "f8f8e8": "ffe2ed", - "9b8259": "b021c5", - "e5e4c2": "ffb9f9", - "000000": "000000", - "bc9b4e": "900090", - "f8f8d0": "ff8ae9", - "e8e088": "ff49e7", - "d0b868": "d10cc7", - "7d673b": "510059", - "282828": "282828", - "f84040": "f84040", - "f88888": "1ae2e6", - "c81010": "00c2d2" - }, - "2": { "b0a080": "d96b23", "f8f8e8": "ffe1b8", "9b8259": "b43c06", @@ -30,5 +14,21 @@ "f84040": "f84040", "f88888": "f88888", "c81010": "c81010" + }, + "2": { + "b0a080": "e552ec", + "f8f8e8": "ffe2ed", + "9b8259": "b021c5", + "e5e4c2": "ffb9f9", + "000000": "000000", + "bc9b4e": "900090", + "f8f8d0": "ff8ae9", + "e8e088": "ff49e7", + "d0b868": "d10cc7", + "7d673b": "510059", + "282828": "282828", + "f84040": "f84040", + "f88888": "1ae2e6", + "c81010": "00c2d2" } } \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/953.json b/public/images/pokemon/variant/exp/953.json index df4262f3ed2..6f2885be607 100644 --- a/public/images/pokemon/variant/exp/953.json +++ b/public/images/pokemon/variant/exp/953.json @@ -2,79 +2,35 @@ "1": { "5a4d37": "1c1e76", "766348": "323aa5", - "9d8361": "4059bd", "c5b4aa": "d3e6e6", "37332b": "104139", "9e8360": "4059bd", - "f18725": "2e8c19", - "f18625": "2e8c19", - "bb6f2a": "2f7410", "0f0f0f": "0f0f0f", "575243": "18734a", - "b8702d": "2f7410", "777462": "199e46", - "585243": "18734a", - "4f4531": "b29c3e", - "4d4530": "b29c3e", - "aea56b": "f9fba2", - "afa667": "f9fba2", - "a28e85": "c1d8db", - "a28c8c": "c1d8db", - "b64c95": "dedb64", + "afa668": "f9fba2", + "a28c88": "c1d8db", "f2f2f2": "e8eab5", - "b05f8f": "dedb64", - "776348": "323aa5", + "b45492": "dedb64", "f28625": "2e8c19", - "bc6e28": "2f7410", - "bb6e27": "2f7410", - "4d4430": "b29c3e", - "b0a766": "f9fba2", - "a18f8d": "c1d8db", - "a28f86": "c1d8db", - "b74a94": "dedb64", - "9e8461": "4059bd", - "777362": "199e46", - "ba6d27": "2f7410", - "afa567": "f9fba2", - "a18f85": "c1d8db" + "4e4530": "b29c3e", + "ba6e29": "2f7410" }, "2": { "5a4d37": "333e5f", "766348": "8c9fbf", - "9d8361": "dbedec", "c5b4aa": "39cfbc", "37332b": "261031", "9e8360": "dbedec", - "f18725": "4baecd", - "f18625": "4baecd", - "bb6f2a": "4792bd", "0f0f0f": "0f0f0f", "575243": "5e2d72", - "b8702d": "4792bd", "777462": "8358a1", - "585243": "5e2d72", - "4f4531": "534b8c", - "4d4530": "534b8c", - "aea56b": "c9dbac", - "afa667": "c9dbac", - "a28e85": "52b0b0", - "a28c8c": "52b0b0", - "b64c95": "b56c3e", + "afa668": "c9dbac", + "a28e88": "52b0b0", "f2f2f2": "d9c951", - "b05f8f": "b56c3e", - "776348": "8c9fbf", + "b45492": "b56c3e", "f28625": "4baecd", - "bc6e28": "4792bd", - "bb6e27": "4792bd", - "4d4430": "534b8c", - "b0a766": "c9dbac", - "a18f8d": "52b0b0", - "a28f86": "52b0b0", - "b74a94": "b56c3e", - "9e8461": "dbedec", - "777362": "8358a1", - "ba6d27": "4792bd", - "afa567": "c9dbac", - "a18f85": "52b0b0" + "4e4530": "534b8c", + "ba6e29": "4792bd" } -} \ No newline at end of file +} diff --git a/public/images/pokemon/variant/female/178_2.json b/public/images/pokemon/variant/female/178_2.json new file mode 100644 index 00000000000..d2326f5acbf --- /dev/null +++ b/public/images/pokemon/variant/female/178_2.json @@ -0,0 +1,2372 @@ +{ + "textures": [ + { + "image": "178_2.png", + "format": "RGBA8888", + "size": { + "w": 224, + "h": 224 + }, + "scale": 1, + "frames": [ + { + "filename": "0101.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 45, + "h": 60 + }, + "frame": { + "x": 0, + "y": 0, + "w": 45, + "h": 60 + } + }, + { + "filename": "0102.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 45, + "h": 60 + }, + "frame": { + "x": 0, + "y": 0, + "w": 45, + "h": 60 + } + }, + { + "filename": "0103.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 0, + "w": 43, + "h": 60 + }, + "frame": { + "x": 45, + "y": 0, + "w": 43, + "h": 60 + } + }, + { + "filename": "0104.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 0, + "w": 43, + "h": 60 + }, + "frame": { + "x": 45, + "y": 0, + "w": 43, + "h": 60 + } + }, + { + "filename": "0105.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 43, + "h": 60 + }, + "frame": { + "x": 88, + "y": 0, + "w": 43, + "h": 60 + } + }, + { + "filename": "0106.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 43, + "h": 60 + }, + "frame": { + "x": 88, + "y": 0, + "w": 43, + "h": 60 + } + }, + { + "filename": "0099.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 1, + "w": 41, + "h": 59 + }, + "frame": { + "x": 131, + "y": 0, + "w": 41, + "h": 59 + } + }, + { + "filename": "0100.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 1, + "w": 41, + "h": 59 + }, + "frame": { + "x": 131, + "y": 0, + "w": 41, + "h": 59 + } + }, + { + "filename": "0107.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 39, + "h": 59 + }, + "frame": { + "x": 131, + "y": 59, + "w": 39, + "h": 59 + } + }, + { + "filename": "0108.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 39, + "h": 59 + }, + "frame": { + "x": 131, + "y": 59, + "w": 39, + "h": 59 + } + }, + { + "filename": "0109.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 37, + "h": 59 + }, + "frame": { + "x": 0, + "y": 60, + "w": 37, + "h": 59 + } + }, + { + "filename": "0110.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 37, + "h": 59 + }, + "frame": { + "x": 0, + "y": 60, + "w": 37, + "h": 59 + } + }, + { + "filename": "0097.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 0, + "y": 3, + "w": 40, + "h": 57 + }, + "frame": { + "x": 37, + "y": 60, + "w": 40, + "h": 57 + } + }, + { + "filename": "0098.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 0, + "y": 3, + "w": 40, + "h": 57 + }, + "frame": { + "x": 37, + "y": 60, + "w": 40, + "h": 57 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0036.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0037.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0038.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0063.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0064.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0065.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0066.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0111.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 3, + "w": 36, + "h": 57 + }, + "frame": { + "x": 0, + "y": 119, + "w": 36, + "h": 57 + } + }, + { + "filename": "0112.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 3, + "w": 36, + "h": 57 + }, + "frame": { + "x": 0, + "y": 119, + "w": 36, + "h": 57 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0004.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0041.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0042.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0059.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0060.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0069.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0070.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0095.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 115, + "w": 37, + "h": 55 + } + }, + { + "filename": "0096.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 115, + "w": 37, + "h": 55 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0039.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0040.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0061.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0062.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0067.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0068.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0087.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 36, + "h": 54 + }, + "frame": { + "x": 77, + "y": 170, + "w": 36, + "h": 54 + } + }, + { + "filename": "0088.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 36, + "h": 54 + }, + "frame": { + "x": 77, + "y": 170, + "w": 36, + "h": 54 + } + }, + { + "filename": "0089.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 7, + "w": 36, + "h": 53 + }, + "frame": { + "x": 36, + "y": 171, + "w": 36, + "h": 53 + } + }, + { + "filename": "0090.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 7, + "w": 36, + "h": 53 + }, + "frame": { + "x": 36, + "y": 171, + "w": 36, + "h": 53 + } + }, + { + "filename": "0091.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 7, + "w": 36, + "h": 53 + }, + "frame": { + "x": 36, + "y": 171, + "w": 36, + "h": 53 + } + }, + { + "filename": "0092.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 7, + "w": 36, + "h": 53 + }, + "frame": { + "x": 36, + "y": 171, + "w": 36, + "h": 53 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0043.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0044.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0057.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0058.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0071.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0072.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0085.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0086.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0045.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0046.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0055.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0056.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0073.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0074.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0083.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0084.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0093.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 36, + "h": 54 + }, + "frame": { + "x": 172, + "y": 0, + "w": 36, + "h": 54 + } + }, + { + "filename": "0094.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 36, + "h": 54 + }, + "frame": { + "x": 172, + "y": 0, + "w": 36, + "h": 54 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0047.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0048.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0053.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0054.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0075.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0076.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0081.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0082.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0049.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0050.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0051.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0052.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0077.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0078.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0079.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0080.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:898a0175e268e2bf429019b4b00a4414:3e0d176fabaa6df9ef39756046ad4ad5:319c95b9f5acf1139a5c6761349cd6ab$" + } +} diff --git a/public/images/pokemon/variant/female/178_2.png b/public/images/pokemon/variant/female/178_2.png new file mode 100644 index 00000000000..1c0fe8c9153 Binary files /dev/null and b/public/images/pokemon/variant/female/178_2.png differ diff --git a/public/images/pokemon/variant/female/178_3.json b/public/images/pokemon/variant/female/178_3.json new file mode 100644 index 00000000000..2e851f20d9d --- /dev/null +++ b/public/images/pokemon/variant/female/178_3.json @@ -0,0 +1,2372 @@ +{ + "textures": [ + { + "image": "178_3.png", + "format": "RGBA8888", + "size": { + "w": 224, + "h": 224 + }, + "scale": 1, + "frames": [ + { + "filename": "0101.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 45, + "h": 60 + }, + "frame": { + "x": 0, + "y": 0, + "w": 45, + "h": 60 + } + }, + { + "filename": "0102.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 45, + "h": 60 + }, + "frame": { + "x": 0, + "y": 0, + "w": 45, + "h": 60 + } + }, + { + "filename": "0103.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 0, + "w": 43, + "h": 60 + }, + "frame": { + "x": 45, + "y": 0, + "w": 43, + "h": 60 + } + }, + { + "filename": "0104.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 0, + "w": 43, + "h": 60 + }, + "frame": { + "x": 45, + "y": 0, + "w": 43, + "h": 60 + } + }, + { + "filename": "0105.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 43, + "h": 60 + }, + "frame": { + "x": 88, + "y": 0, + "w": 43, + "h": 60 + } + }, + { + "filename": "0106.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 43, + "h": 60 + }, + "frame": { + "x": 88, + "y": 0, + "w": 43, + "h": 60 + } + }, + { + "filename": "0099.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 1, + "w": 41, + "h": 59 + }, + "frame": { + "x": 131, + "y": 0, + "w": 41, + "h": 59 + } + }, + { + "filename": "0100.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 1, + "w": 41, + "h": 59 + }, + "frame": { + "x": 131, + "y": 0, + "w": 41, + "h": 59 + } + }, + { + "filename": "0107.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 39, + "h": 59 + }, + "frame": { + "x": 131, + "y": 59, + "w": 39, + "h": 59 + } + }, + { + "filename": "0108.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 39, + "h": 59 + }, + "frame": { + "x": 131, + "y": 59, + "w": 39, + "h": 59 + } + }, + { + "filename": "0109.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 37, + "h": 59 + }, + "frame": { + "x": 0, + "y": 60, + "w": 37, + "h": 59 + } + }, + { + "filename": "0110.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 37, + "h": 59 + }, + "frame": { + "x": 0, + "y": 60, + "w": 37, + "h": 59 + } + }, + { + "filename": "0097.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 0, + "y": 3, + "w": 40, + "h": 57 + }, + "frame": { + "x": 37, + "y": 60, + "w": 40, + "h": 57 + } + }, + { + "filename": "0098.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 0, + "y": 3, + "w": 40, + "h": 57 + }, + "frame": { + "x": 37, + "y": 60, + "w": 40, + "h": 57 + } + }, + { + "filename": "0007.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0008.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0009.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0010.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0035.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0036.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0037.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0038.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0063.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0064.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0065.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0066.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 6, + "w": 40, + "h": 54 + }, + "frame": { + "x": 37, + "y": 117, + "w": 40, + "h": 54 + } + }, + { + "filename": "0111.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 3, + "w": 36, + "h": 57 + }, + "frame": { + "x": 0, + "y": 119, + "w": 36, + "h": 57 + } + }, + { + "filename": "0112.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 3, + "w": 36, + "h": 57 + }, + "frame": { + "x": 0, + "y": 119, + "w": 36, + "h": 57 + } + }, + { + "filename": "0003.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0004.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0013.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0014.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0031.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0032.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0041.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0042.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0059.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0060.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0069.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0070.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 60, + "w": 37, + "h": 55 + } + }, + { + "filename": "0095.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 115, + "w": 37, + "h": 55 + } + }, + { + "filename": "0096.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 1, + "y": 5, + "w": 37, + "h": 55 + }, + "frame": { + "x": 77, + "y": 115, + "w": 37, + "h": 55 + } + }, + { + "filename": "0005.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0006.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0011.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0012.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0033.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0034.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0039.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0040.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0061.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0062.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0067.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0068.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 38, + "h": 54 + }, + "frame": { + "x": 114, + "y": 118, + "w": 38, + "h": 54 + } + }, + { + "filename": "0087.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 36, + "h": 54 + }, + "frame": { + "x": 77, + "y": 170, + "w": 36, + "h": 54 + } + }, + { + "filename": "0088.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 36, + "h": 54 + }, + "frame": { + "x": 77, + "y": 170, + "w": 36, + "h": 54 + } + }, + { + "filename": "0089.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 7, + "w": 36, + "h": 53 + }, + "frame": { + "x": 36, + "y": 171, + "w": 36, + "h": 53 + } + }, + { + "filename": "0090.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 7, + "w": 36, + "h": 53 + }, + "frame": { + "x": 36, + "y": 171, + "w": 36, + "h": 53 + } + }, + { + "filename": "0091.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 7, + "w": 36, + "h": 53 + }, + "frame": { + "x": 36, + "y": 171, + "w": 36, + "h": 53 + } + }, + { + "filename": "0092.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 7, + "w": 36, + "h": 53 + }, + "frame": { + "x": 36, + "y": 171, + "w": 36, + "h": 53 + } + }, + { + "filename": "0001.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0015.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0016.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0029.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0030.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0043.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0044.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0057.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0058.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0071.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0072.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0085.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0086.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 152, + "y": 118, + "w": 36, + "h": 55 + } + }, + { + "filename": "0017.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0018.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0027.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0028.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0045.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0046.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0055.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0056.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0073.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0074.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0083.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0084.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 36, + "h": 55 + }, + "frame": { + "x": 170, + "y": 59, + "w": 36, + "h": 55 + } + }, + { + "filename": "0093.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 36, + "h": 54 + }, + "frame": { + "x": 172, + "y": 0, + "w": 36, + "h": 54 + } + }, + { + "filename": "0094.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 6, + "w": 36, + "h": 54 + }, + "frame": { + "x": 172, + "y": 0, + "w": 36, + "h": 54 + } + }, + { + "filename": "0019.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0020.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0025.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0026.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0047.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0048.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0053.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0054.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0075.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0076.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0081.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0082.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 35, + "h": 55 + }, + "frame": { + "x": 188, + "y": 114, + "w": 35, + "h": 55 + } + }, + { + "filename": "0021.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0022.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0023.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0024.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0049.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0050.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0051.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0052.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0077.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0078.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0079.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + }, + { + "filename": "0080.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 45, + "h": 60 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 34, + "h": 55 + }, + "frame": { + "x": 188, + "y": 169, + "w": 34, + "h": 55 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:898a0175e268e2bf429019b4b00a4414:3e0d176fabaa6df9ef39756046ad4ad5:319c95b9f5acf1139a5c6761349cd6ab$" + } +} diff --git a/public/images/pokemon/variant/female/178_3.png b/public/images/pokemon/variant/female/178_3.png new file mode 100644 index 00000000000..0ca5fe14c72 Binary files /dev/null and b/public/images/pokemon/variant/female/178_3.png differ diff --git a/src/battle-scene.ts b/src/battle-scene.ts index b2e5635b642..eaf550e7d10 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1,11 +1,10 @@ import Phaser from "phaser"; import UI from "./ui/ui"; -import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, MovePhase, TitlePhase, SwitchPhase, SummonPhase, ToggleDoublePositionPhase } from "./phases"; import Pokemon, { PlayerPokemon, EnemyPokemon } from "./field/pokemon"; import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies } from "./data/pokemon-species"; import { Constructor } from "#app/utils"; import * as Utils from "./utils"; -import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier"; +import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, TurnHeldItemTransferModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier"; import { PokeballType } from "./data/pokeball"; import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims"; import { Phase } from "./phase"; @@ -37,8 +36,8 @@ import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin"; import { addUiThemeOverrides } from "./ui/ui-theme"; import PokemonData from "./system/pokemon-data"; import { Nature } from "./data/nature"; -import { SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges, FormChangeItem } from "./data/pokemon-forms"; -import { FormChangePhase, QuietFormChangePhase } from "./form-change-phase"; +import { SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges, FormChangeItem, SpeciesFormChange } from "./data/pokemon-forms"; +import { FormChangePhase } from "./phases/form-change-phase"; import { getTypeRgb } from "./data/type"; import PokemonSpriteSparkleHandler from "./field/pokemon-sprite-sparkle-handler"; import CharSprite from "./ui/char-sprite"; @@ -69,6 +68,22 @@ import i18next from "i18next"; import {TrainerType} from "#enums/trainer-type"; import { battleSpecDialogue } from "./data/dialogue"; import { LoadingScene } from "./loading-scene"; +import { LevelCapPhase } from "./phases/level-cap-phase"; +import { LoginPhase } from "./phases/login-phase"; +import { MessagePhase } from "./phases/message-phase"; +import { MovePhase } from "./phases/move-phase"; +import { NewBiomeEncounterPhase } from "./phases/new-biome-encounter-phase"; +import { NextEncounterPhase } from "./phases/next-encounter-phase"; +import { QuietFormChangePhase } from "./phases/quiet-form-change-phase"; +import { ReturnPhase } from "./phases/return-phase"; +import { SelectBiomePhase } from "./phases/select-biome-phase"; +import { ShowTrainerPhase } from "./phases/show-trainer-phase"; +import { SummonPhase } from "./phases/summon-phase"; +import { SwitchPhase } from "./phases/switch-phase"; +import { TitlePhase } from "./phases/title-phase"; +import { ToggleDoublePositionPhase } from "./phases/toggle-double-position-phase"; +import { TurnInitPhase } from "./phases/turn-init-phase"; +import { ShopCursorTarget } from "./enums/shop-cursor-target"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; @@ -113,6 +128,7 @@ export default class BattleScene extends SceneBase { public gameSpeed: integer = 1; public damageNumbersMode: integer = 0; public reroll: boolean = false; + public shopCursorTarget: number = ShopCursorTarget.CHECK_TEAM; public showMovesetFlyout: boolean = true; public showArenaFlyout: boolean = true; public showTimeOfDayWidget: boolean = true; @@ -1086,7 +1102,7 @@ export default class BattleScene extends SceneBase { } else if (trainerConfigs[trainerType].hasDouble) { const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8); this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance); - playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance)); + playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance)); doubleTrainer = !Utils.randSeedInt(doubleChance.value); // Add a check that special trainers can't be double except for tate and liza - they should use the normal double chance if (trainerConfigs[trainerType].trainerTypeDouble && ![ TrainerType.TATE, TrainerType.LIZA ].includes(trainerType)) { @@ -1102,7 +1118,7 @@ export default class BattleScene extends SceneBase { if (newBattleType === BattleType.WILD && !this.gameMode.isWaveFinal(newWaveIndex)) { const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8); this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance); - playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance)); + playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance)); newDouble = !Utils.randSeedInt(doubleChance.value); } else if (newBattleType === BattleType.TRAINER) { newDouble = newTrainer?.variant === TrainerVariant.DOUBLE; @@ -1504,7 +1520,7 @@ export default class BattleScene extends SceneBase { return; } const formattedMoney = Utils.formatMoney(this.moneyFormat, this.money); - this.moneyText.setText(`₽${formattedMoney}`); + this.moneyText.setText(i18next.t("battleScene:moneyOwned", { formattedMoney })); this.fieldUI.moveAbove(this.moneyText, this.luckText); if (forceVisible) { this.moneyText.setVisible(true); @@ -2122,7 +2138,7 @@ export default class BattleScene extends SceneBase { pushMovePhase(movePhase: MovePhase, priorityOverride?: integer): void { const movePriority = new Utils.IntegerHolder(priorityOverride !== undefined ? priorityOverride : movePhase.move.getMove().priority); - applyAbAttrs(ChangeMovePriorityAbAttr, movePhase.pokemon, null, movePhase.move.getMove(), movePriority); + applyAbAttrs(ChangeMovePriorityAbAttr, movePhase.pokemon, null, false, movePhase.move.getMove(), movePriority); const lowerPriorityPhase = this.phaseQueue.find(p => p instanceof MovePhase && p.move.getMove().priority < movePriority.value); if (lowerPriorityPhase) { this.phaseQueue.splice(this.phaseQueue.indexOf(lowerPriorityPhase), 0, movePhase); @@ -2421,7 +2437,6 @@ export default class BattleScene extends SceneBase { getEnemyModifierTypesForWave(difficultyWaveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD, upgradeChance) .map(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false, this)); }); - this.updateModifiers(false).then(() => resolve()); }); } @@ -2579,7 +2594,7 @@ export default class BattleScene extends SceneBase { // in case this is NECROZMA, determine which forms this const matchingFormChangeOpts = pokemonFormChanges[pokemon.species.speciesId].filter(fc => fc.findTrigger(formChangeTriggerType) && fc.canChange(pokemon)); - let matchingFormChange; + let matchingFormChange: SpeciesFormChange | null; if (pokemon.species.speciesId === Species.NECROZMA && matchingFormChangeOpts.length > 1) { // Ultra Necrozma is changing its form back, so we need to figure out into which form it devolves. const formChangeItemModifiers = (this.findModifiers(m => m instanceof PokemonFormChangeItemModifier && m.pokemonId === pokemon.id) as PokemonFormChangeItemModifier[]).filter(m => m.active).map(m => m.formChangeItem); @@ -2666,7 +2681,9 @@ export default class BattleScene extends SceneBase { if (pokemon instanceof EnemyPokemon && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) { this.fadeOutBgm(Utils.fixedInt(2000), false); this.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin, pokemon.species.name, undefined, () => { - this.addEnemyModifier(getModifierType(modifierTypes.MINI_BLACK_HOLE).newModifier(pokemon) as PersistentModifier, false, true); + const finalBossMBH = getModifierType(modifierTypes.MINI_BLACK_HOLE).newModifier(pokemon) as TurnHeldItemTransferModifier; + finalBossMBH.setTransferrableFalse(); + this.addEnemyModifier(finalBossMBH, false, true); pokemon.generateAndPopulateMoveset(1); this.setFieldScale(0.75); this.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); diff --git a/src/data/ability.ts b/src/data/ability.ts index abc45273131..35ab55c0159 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -3,7 +3,6 @@ import { Type } from "./type"; import { Constructor } from "#app/utils"; import * as Utils from "../utils"; import { BattleStat, getBattleStatName } from "./battle-stat"; -import { MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../phases"; import { getPokemonNameWithAffix } from "../messages"; import { Weather, WeatherType } from "./weather"; import { BattlerTag, GroundedTag, GulpMissileTag, SemiInvulnerableTag } from "./battler-tags"; @@ -26,6 +25,10 @@ import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import { MovePhase } from "#app/phases/move-phase.js"; +import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase.js"; +import { ShowAbilityPhase } from "#app/phases/show-ability-phase.js"; +import { StatChangePhase } from "#app/phases/stat-change-phase.js"; export class Ability implements Localizable { public id: Abilities; @@ -132,7 +135,7 @@ export abstract class AbAttr { this.showAbility = showAbility; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise { return false; } @@ -151,7 +154,7 @@ export abstract class AbAttr { } export class BlockRecoilDamageAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; @@ -167,7 +170,7 @@ export class DoubleBattleChanceAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { const doubleChance = (args[0] as Utils.IntegerHolder); doubleChance.value = Math.max(doubleChance.value / 2, 1); return true; @@ -175,7 +178,7 @@ export class DoubleBattleChanceAbAttr extends AbAttr { } export class PostBattleInitAbAttr extends AbAttr { - applyPostBattleInit(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { return false; } } @@ -189,9 +192,9 @@ export class PostBattleInitFormChangeAbAttr extends PostBattleInitAbAttr { this.formFunc = formFunc; } - applyPostBattleInit(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const formIndex = this.formFunc(pokemon); - if (formIndex !== pokemon.formIndex) { + if (formIndex !== pokemon.formIndex && !simulated) { return pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); } @@ -214,22 +217,24 @@ export class PostBattleInitStatChangeAbAttr extends PostBattleInitAbAttr { this.selfTarget = !!selfTarget; } - applyPostBattleInit(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const statChangePhases: StatChangePhase[] = []; - if (this.selfTarget) { - statChangePhases.push(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); - } else { - for (const opponent of pokemon.getOpponents()) { - statChangePhases.push(new StatChangePhase(pokemon.scene, opponent.getBattlerIndex(), false, this.stats, this.levels)); + if (!simulated) { + if (this.selfTarget) { + statChangePhases.push(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + } else { + for (const opponent of pokemon.getOpponents()) { + statChangePhases.push(new StatChangePhase(pokemon.scene, opponent.getBattlerIndex(), false, this.stats, this.levels)); + } } - } - for (const statChangePhase of statChangePhases) { - if (!this.selfTarget && !statChangePhase.getPokemon()?.summonData) { - pokemon.scene.pushPhase(statChangePhase); - } else { // TODO: This causes the ability bar to be shown at the wrong time - pokemon.scene.unshiftPhase(statChangePhase); + for (const statChangePhase of statChangePhases) { + if (!this.selfTarget && !statChangePhase.getPokemon()?.summonData) { + pokemon.scene.pushPhase(statChangePhase); + } else { // TODO: This causes the ability bar to be shown at the wrong time + pokemon.scene.unshiftPhase(statChangePhase); + } } } @@ -240,17 +245,17 @@ export class PostBattleInitStatChangeAbAttr extends PostBattleInitAbAttr { type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: Move) => boolean; export class PreDefendAbAttr extends AbAttr { - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise { return false; } } export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr { - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (pokemon.isFullHp() && - pokemon.getMaxHp() > 1 && //Checks if pokemon has wonder_guard (which forces 1hp) - (args[0] as Utils.NumberHolder).value >= pokemon.hp) { //Damage >= hp - return pokemon.addTag(BattlerTagType.STURDY, 1); + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (pokemon.isFullHp() + && pokemon.getMaxHp() > 1 //Checks if pokemon has wonder_guard (which forces 1hp) + && (args[0] as Utils.NumberHolder).value >= pokemon.hp) { //Damage >= hp + return simulated || pokemon.addTag(BattlerTagType.STURDY, 1); } return false; @@ -258,7 +263,7 @@ export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr { } export class BlockItemTheftAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; @@ -273,7 +278,7 @@ export class BlockItemTheftAbAttr extends AbAttr { } export class StabBoostAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if ((args[0] as Utils.NumberHolder).value > 1) { (args[0] as Utils.NumberHolder).value += 0.5; return true; @@ -294,9 +299,9 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr { this.damageMultiplier = damageMultiplier; } - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.condition(pokemon, attacker, move)) { - (args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * this.damageMultiplier); + (args[0] as Utils.NumberHolder).value = Utils.toDmgValue((args[0] as Utils.NumberHolder).value * this.damageMultiplier); return true; } @@ -338,7 +343,7 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { * @param args [0] {@linkcode Utils.NumberHolder} gets set to 0 if move is immuned by an ability. * @param args [1] - Whether the move is simulated. */ - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { // Field moves should ignore immunity if ([ MoveTarget.BOTH_SIDES, MoveTarget.ENEMY_SIDE, MoveTarget.USER_SIDE ].includes(move.moveTarget)) { return false; @@ -365,9 +370,9 @@ export class AttackTypeImmunityAbAttr extends TypeImmunityAbAttr { * Type immunity abilities that do not give additional benefits (HP recovery, stat boosts, etc) are not immune to status moves of the type * Example: Levitate */ - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (move.category !== MoveCategory.STATUS) { - return super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args); + return super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); } return false; } @@ -378,17 +383,14 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr { super(immuneType); } - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args); + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + const ret = super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); if (ret) { - if (!pokemon.isFullHp()) { - const simulated = args.length > 1 && args[1]; - if (!simulated) { - const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / 4), 1), i18next.t("abilityTriggers:typeImmunityHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); - } + if (!pokemon.isFullHp() && !simulated) { + const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; + pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), + Utils.toDmgValue(pokemon.getMaxHp() / 4), i18next.t("abilityTriggers:typeImmunityHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); } return true; } @@ -408,12 +410,11 @@ class TypeImmunityStatChangeAbAttr extends TypeImmunityAbAttr { this.levels = levels; } - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args); + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + const ret = super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); if (ret) { cancelled.value = true; - const simulated = args.length > 1 && args[1]; if (!simulated) { pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels)); } @@ -434,12 +435,11 @@ class TypeImmunityAddBattlerTagAbAttr extends TypeImmunityAbAttr { this.turnCount = turnCount; } - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args); + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + const ret = super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); if (ret) { cancelled.value = true; - const simulated = args.length > 1 && args[1]; if (!simulated) { pokemon.addTag(this.tagType, this.turnCount, undefined, pokemon.id); } @@ -454,7 +454,7 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { super(null, condition); } - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (move instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.type, attacker) < 2) { cancelled.value = true; (args[0] as Utils.NumberHolder).value = 0; @@ -473,7 +473,7 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { } export class PostDefendAbAttr extends AbAttr { - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { return false; } } @@ -497,12 +497,16 @@ export class PostDefendGulpMissileAbAttr extends PostDefendAbAttr { * @param {any[]} args - n/a * @returns Whether the effects of the ability are applied. */ - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { const battlerTag = pokemon.getTag(GulpMissileTag); if (!battlerTag || move.category === MoveCategory.STATUS || pokemon.getTag(SemiInvulnerableTag)) { return false; } + if (simulated) { + return true; + } + const cancelled = new Utils.BooleanHolder(false); applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled); @@ -522,12 +526,12 @@ export class PostDefendGulpMissileAbAttr extends PostDefendAbAttr { } export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { const attackPriority = new Utils.IntegerHolder(move.priority); - applyMoveAttrs(IncrementMovePriorityAttr,attacker,null,move,attackPriority); - applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, move, attackPriority); + applyMoveAttrs(IncrementMovePriorityAttr, attacker, null, move, attackPriority); + applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, simulated, move, attackPriority); - if (move.moveTarget===MoveTarget.USER || move.moveTarget===MoveTarget.NEAR_ALLY) { + if (move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY) { return false; } @@ -541,7 +545,7 @@ export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { } export class PostStatChangeAbAttr extends AbAttr { - applyPostStatChange(pokemon: Pokemon, statsChanged: BattleStat[], levelChanged: integer, selfTarget: boolean, args: any[]): boolean | Promise { + applyPostStatChange(pokemon: Pokemon, simulated: boolean, statsChanged: BattleStat[], levelChanged: integer, selfTarget: boolean, args: any[]): boolean | Promise { return false; } } @@ -555,7 +559,7 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr { this.immuneCondition = immuneCondition; } - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.immuneCondition(pokemon, attacker, move)) { cancelled.value = true; return true; @@ -576,7 +580,7 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr { * @extends PreDefendAbAttr */ export class WonderSkinAbAttr extends PreDefendAbAttr { - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { const moveAccuracy = args[0] as Utils.NumberHolder; if (move.category === MoveCategory.STATUS && moveAccuracy.value >= 50) { moveAccuracy.value = 50; @@ -597,13 +601,10 @@ export class MoveImmunityStatChangeAbAttr extends MoveImmunityAbAttr { this.levels = levels; } - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args); - if (ret) { - const simulated = args.length > 1 && args[1]; - if (!simulated) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels)); - } + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + const ret = super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + if (ret && !simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels)); } return ret; @@ -627,9 +628,11 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr { * @args N/A * @returns true if healing should be reversed on a healing move, false otherwise. */ - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (move.hasAttr(HitHealAttr)) { - pokemon.scene.queueMessage(i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) })); + if (!simulated) { + pokemon.scene.queueMessage(i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) })); + } return true; } return false; @@ -653,8 +656,12 @@ export class PostDefendStatChangeAbAttr extends PostDefendAbAttr { this.allOthers = allOthers; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (this.condition(pokemon, attacker, move)) { + if (simulated) { + return true; + } + if (this.allOthers) { const otherPokemon = pokemon.getAlly() ? pokemon.getOpponents().concat([ pokemon.getAlly() ]) : pokemon.getOpponents(); for (const other of otherPokemon) { @@ -687,13 +694,15 @@ export class PostDefendHpGatedStatChangeAbAttr extends PostDefendAbAttr { this.selfTarget = selfTarget; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + 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); 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)) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.levels)); + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.levels)); + } return true; } @@ -712,11 +721,13 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr { this.tagType = tagType; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (this.condition(pokemon, attacker, move)) { const tag = pokemon.scene.arena.getTag(this.tagType) as ArenaTrapTag; if (!pokemon.scene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers) { - pokemon.scene.arena.addTag(this.tagType, 0, undefined, pokemon.id, pokemon.isPlayer() ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER); + if (!simulated) { + pokemon.scene.arena.addTag(this.tagType, 0, undefined, pokemon.id, pokemon.isPlayer() ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER); + } return true; } } @@ -734,9 +745,9 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { this.tagType = tagType; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (this.condition(pokemon, attacker, move)) { - if (!pokemon.getTag(this.tagType)) { + 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 })); } @@ -747,8 +758,11 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { } export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr { - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (hitResult < HitResult.NO_EFFECT) { + if (simulated) { + return true; + } const type = move.type; const pokemonTypes = pokemon.getTypes(true); if (pokemonTypes.length !== 1 || pokemonTypes[0] !== type) { @@ -778,9 +792,13 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { this.terrainType = terrainType; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (hitResult < HitResult.NO_EFFECT) { - return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + if (simulated) { + return pokemon.scene.arena.terrain?.terrainType !== (this.terrainType || undefined); + } else { + return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + } } return false; @@ -798,10 +816,14 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { this.effects = effects; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + 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)) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; - return attacker.trySetStatus(effect, true, pokemon); + if (simulated) { + return attacker.canSetStatus(effect, true, false, pokemon); + } else { + return attacker.trySetStatus(effect, true, pokemon); + } } return false; @@ -813,11 +835,11 @@ export class EffectSporeAbAttr extends PostDefendContactApplyStatusEffectAbAttr super(10, StatusEffect.POISON, StatusEffect.PARALYSIS, StatusEffect.SLEEP); } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (attacker.hasAbility(Abilities.OVERCOAT) || attacker.isOfType(Type.GRASS)) { return false; } - return super.applyPostDefend(pokemon, passive, attacker, move, hitResult, args); + return super.applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args); } } @@ -834,9 +856,13 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { this.turnCount = turnCount; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + 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) { - return attacker.addTag(this.tagType, this.turnCount, move.id, attacker.id); + if (simulated) { + return attacker.canAddTag(this.tagType); + } else { + return attacker.addTag(this.tagType, this.turnCount, move.id, attacker.id); + } } return false; @@ -854,8 +880,10 @@ export class PostDefendCritStatChangeAbAttr extends PostDefendAbAttr { this.levels = levels; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels)); + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels)); + } return true; } @@ -874,10 +902,10 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { this.damageRatio = damageRatio; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { - attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); - attacker.turnData.damageTaken += Math.ceil(attacker.getMaxHp() * (1 / this.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)) { + attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); + attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); return true; } @@ -907,13 +935,15 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { this.turns = turns; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) { if (pokemon.getTag(BattlerTagType.PERISH_SONG) || attacker.getTag(BattlerTagType.PERISH_SONG)) { return false; } else { - attacker.addTag(BattlerTagType.PERISH_SONG, this.turns); - pokemon.addTag(BattlerTagType.PERISH_SONG, this.turns); + if (!simulated) { + attacker.addTag(BattlerTagType.PERISH_SONG, this.turns); + pokemon.addTag(BattlerTagType.PERISH_SONG, this.turns); + } return true; } } @@ -936,11 +966,14 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { this.condition = condition ?? null; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + 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)) { return false; } if (!pokemon.scene.arena.weather?.isImmutable()) { + if (simulated) { + return pokemon.scene.arena.weather?.weatherType !== this.weatherType; + } return pokemon.scene.arena.trySetWeather(this.weatherType, true); } @@ -953,11 +986,13 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { super(); } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + 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)) { - const tempAbilityId = attacker.getAbility().id; - attacker.summonData.ability = pokemon.getAbility().id; - pokemon.summonData.ability = tempAbilityId; + if (!simulated) { + const tempAbilityId = attacker.getAbility().id; + attacker.summonData.ability = pokemon.getAbility().id; + pokemon.summonData.ability = tempAbilityId; + } return true; } @@ -977,9 +1012,11 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { this.ability = ability; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + 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)) { - attacker.summonData.ability = this.ability; + if (!simulated) { + attacker.summonData.ability = this.ability; + } return true; } @@ -1006,9 +1043,13 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { this.chance = chance; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (!attacker.summonData.disabledMove) { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !attacker.isMax()) { + if (simulated) { + return true; + } + this.attacker = attacker; this.move = move; @@ -1041,9 +1082,11 @@ export class PostStatChangeStatChangeAbAttr extends PostStatChangeAbAttr { this.levels = levels; } - applyPostStatChange(pokemon: Pokemon, statsChanged: BattleStat[], levelsChanged: integer, selfTarget: boolean, args: any[]): boolean { + applyPostStatChange(pokemon: Pokemon, simulated: boolean, statsChanged: BattleStat[], levelsChanged: integer, selfTarget: boolean, args: any[]): boolean { if (this.condition(pokemon, statsChanged, levelsChanged) && !selfTarget) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, (pokemon).getBattlerIndex(), true, this.statsToChange, this.levels)); + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, (pokemon).getBattlerIndex(), true, this.statsToChange, this.levels)); + } return true; } @@ -1052,7 +1095,7 @@ export class PostStatChangeStatChangeAbAttr extends PostStatChangeAbAttr { } export class PreAttackAbAttr extends AbAttr { - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean | Promise { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean | Promise { return false; } } @@ -1073,7 +1116,7 @@ export class MoveEffectChanceMultiplierAbAttr extends AbAttr { * @param args [0]: {@linkcode Utils.NumberHolder} Move additional effect chance. Has to be higher than or equal to 0. * [1]: {@linkcode Moves } Move used by the ability user. */ - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { // Disable showAbility during getTargetBenefitScore this.showAbility = args[4]; if ((args[0] as Utils.NumberHolder).value <= 0 || (args[1] as Move).id === Moves.ORDER_UP) { @@ -1096,7 +1139,7 @@ export class IgnoreMoveEffectsAbAttr extends PreDefendAbAttr { /** * @param args [0]: {@linkcode Utils.NumberHolder} Move additional effect chance. */ - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if ((args[0] as Utils.NumberHolder).value <= 0) { return false; @@ -1109,14 +1152,14 @@ export class IgnoreMoveEffectsAbAttr extends PreDefendAbAttr { } export class VariableMovePowerAbAttr extends PreAttackAbAttr { - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { //const power = args[0] as Utils.NumberHolder; return false; } } export class FieldPreventExplosiveMovesAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { cancelled.value = true; return true; } @@ -1153,7 +1196,7 @@ export class FieldMultiplyBattleStatAbAttr extends AbAttr { * @param args {any[]} unused * @returns true if this changed the checked stat, false otherwise. */ - applyFieldBattleStat(pokemon: Pokemon, passive: boolean, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, args: any[]): boolean { + applyFieldBattleStat(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, args: any[]): boolean { if (!this.canStack && hasApplied.value) { return false; } @@ -1177,7 +1220,7 @@ export class MoveTypeChangeAttr extends PreAttackAbAttr { super(true); } - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { if (this.condition && this.condition(pokemon, defender, move)) { move.type = this.newType; if (args[0] && args[0] instanceof Utils.NumberHolder) { @@ -1198,7 +1241,7 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { super(true); } - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { if ( !pokemon.isTerastallized() && move.id !== Moves.STRUGGLE && @@ -1226,9 +1269,11 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { } if (pokemon.getTypes().some((t) => t !== moveCopy.type)) { - this.moveType = moveCopy.type; - pokemon.summonData.types = [moveCopy.type]; - pokemon.updateInfo(); + if (!simulated) { + this.moveType = moveCopy.type; + pokemon.summonData.types = [moveCopy.type]; + pokemon.updateInfo(); + } return true; } @@ -1304,7 +1349,7 @@ export class AddSecondStrikeAbAttr extends PreAttackAbAttr { * @param {Utils.NumberHolder} args\[2\] the damage multiplier for the current strike * @returns */ - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { const numTargets = args[0] as integer; const hitCount = args[1] as Utils.IntegerHolder; const multiplier = args[2] as Utils.NumberHolder; @@ -1349,7 +1394,7 @@ export class DamageBoostAbAttr extends PreAttackAbAttr { * @param args Utils.NumberHolder as damage * @returns true if the function succeeds */ - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { if (this.condition(pokemon, defender, move)) { const power = args[0] as Utils.NumberHolder; power.value = Math.floor(power.value * this.damageMultiplier); @@ -1370,7 +1415,7 @@ export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr { this.powerMultiplier = powerMultiplier; } - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { if (this.condition(pokemon, defender, move)) { (args[0] as Utils.NumberHolder).value *= this.powerMultiplier; @@ -1417,7 +1462,7 @@ export class VariableMovePowerBoostAbAttr extends VariableMovePowerAbAttr { /** * @override */ - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move, args: any[]): boolean { const multiplier = this.mult(pokemon, defender, move); if (multiplier !== 1) { (args[0] as Utils.NumberHolder).value *= multiplier; @@ -1446,7 +1491,7 @@ export class FieldMovePowerBoostAbAttr extends AbAttr { this.powerMultiplier = powerMultiplier; } - applyPreAttack(pokemon: Pokemon | null, passive: boolean | null, defender: Pokemon | null, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon | null, passive: boolean | null, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean { if (this.condition(pokemon, defender, move)) { (args[0] as Utils.NumberHolder).value *= this.powerMultiplier; @@ -1510,7 +1555,7 @@ export class BattleStatMultiplierAbAttr extends AbAttr { this.condition = condition ?? null; } - applyBattleStat(pokemon: Pokemon, passive: boolean, battleStat: BattleStat, statValue: Utils.NumberHolder, args: any[]): boolean | Promise { + applyBattleStat(pokemon: Pokemon, passive: boolean, simulated: boolean, battleStat: BattleStat, statValue: Utils.NumberHolder, args: any[]): boolean | Promise { const move = (args[0] as Move); if (battleStat === this.battleStat && (!this.condition || this.condition(pokemon, null, move))) { statValue.value *= this.multiplier; @@ -1536,11 +1581,11 @@ export class PostAttackAbAttr extends AbAttr { * applying the effect of any inherited class. This can be changed by providing a different {@link attackCondition} to the constructor. See {@link ConfusionOnStatusEffectAbAttr} * for an example of an effect that does not require a damaging move. */ - applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { + applyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { // When attackRequired is true, we require the move to be an attack move and to deal damage before checking secondary requirements. // If attackRequired is false, we always defer to the secondary requirements. if (this.attackCondition(pokemon, defender, move)) { - return this.applyPostAttackAfterMoveTypeCheck(pokemon, passive, defender, move, hitResult, args); + return this.applyPostAttackAfterMoveTypeCheck(pokemon, passive, simulated, defender, move, hitResult, args); } else { return false; } @@ -1549,7 +1594,7 @@ export class PostAttackAbAttr extends AbAttr { /** * This method is only called after {@link applyPostAttack} has already been applied. Use this for handling checks specific to the ability in question. */ - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { return false; } } @@ -1563,9 +1608,9 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { this.stealCondition = stealCondition ?? null; } - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { return new Promise(resolve => { - if (hitResult < HitResult.NO_EFFECT && (!this.stealCondition || this.stealCondition(pokemon, defender, move))) { + if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.stealCondition || this.stealCondition(pokemon, defender, move))) { const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferrable); if (heldItems.length) { const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; @@ -1578,7 +1623,7 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { return; } } - resolve(false); + resolve(simulated); }); } @@ -1601,14 +1646,14 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr { this.effects = effects; } - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { /**Status inflicted by abilities post attacking are also considered additional effects.*/ - if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) { + if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && !simulated && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; return attacker.trySetStatus(effect, true, pokemon); } - return false; + return simulated; } } @@ -1632,11 +1677,11 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { this.effects = effects; } - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { /**Battler tags inflicted by abilities post attacking are also considered additional effects.*/ if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; - return attacker.addTag(effect); + return simulated || attacker.addTag(effect); } return false; @@ -1652,9 +1697,9 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { this.condition = condition ?? null; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { return new Promise(resolve => { - if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) { + if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) { const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferrable); if (heldItems.length) { const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; @@ -1667,7 +1712,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { return; } } - resolve(false); + resolve(simulated); }); } @@ -1678,7 +1723,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { } export class PostVictoryAbAttr extends AbAttr { - applyPostVictory(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { return false; } } @@ -1694,12 +1739,13 @@ class PostVictoryStatChangeAbAttr extends PostVictoryAbAttr { this.levels = levels; } - applyPostVictory(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], this.levels)); - + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], this.levels)); + } return true; } } @@ -1713,10 +1759,12 @@ export class PostVictoryFormChangeAbAttr extends PostVictoryAbAttr { this.formFunc = formFunc; } - applyPostVictory(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { const formIndex = this.formFunc(pokemon); if (formIndex !== pokemon.formIndex) { - pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + if (!simulated) { + pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + } return true; } @@ -1725,7 +1773,7 @@ export class PostVictoryFormChangeAbAttr extends PostVictoryAbAttr { } export class PostKnockOutAbAttr extends AbAttr { - applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { + applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { return false; } } @@ -1741,12 +1789,13 @@ export class PostKnockOutStatChangeAbAttr extends PostKnockOutAbAttr { this.levels = levels; } - applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { + applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], this.levels)); - + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], this.levels)); + } return true; } } @@ -1756,10 +1805,12 @@ export class CopyFaintedAllyAbilityAbAttr extends PostKnockOutAbAttr { super(); } - applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { + applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { if (pokemon.isPlayer() === knockedOut.isPlayer() && !knockedOut.getAbility().hasAttr(UncopiableAbilityAbAttr)) { - pokemon.summonData.ability = knockedOut.getAbility().id; - pokemon.scene.queueMessage(i18next.t("abilityTriggers:copyFaintedAllyAbility", { pokemonNameWithAffix: getPokemonNameWithAffix(knockedOut), abilityName: allAbilities[knockedOut.getAbility().id].name })); + if (!simulated) { + pokemon.summonData.ability = knockedOut.getAbility().id; + pokemon.scene.queueMessage(i18next.t("abilityTriggers:copyFaintedAllyAbility", { pokemonNameWithAffix: getPokemonNameWithAffix(knockedOut), abilityName: allAbilities[knockedOut.getAbility().id].name })); + } return true; } @@ -1772,7 +1823,7 @@ export class IgnoreOpponentStatChangesAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]) { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]) { (args[0] as Utils.IntegerHolder).value = 0; return true; @@ -1795,7 +1846,7 @@ export class IgnoreOpponentEvasionAbAttr extends AbAttr { * @param args [0] {@linkcode Utils.IntegerHolder} of BattleStat.EVA * @returns if evasion level was successfully considered as 0 */ - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]) { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]) { (args[0] as Utils.IntegerHolder).value = 0; return true; } @@ -1806,7 +1857,7 @@ export class IntimidateImmunityAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; } @@ -1831,8 +1882,10 @@ export class PostIntimidateStatChangeAbAttr extends AbAttr { this.overwrites = !!overwrites; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - pokemon.scene.pushPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, this.stats, this.levels)); + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (!simulated) { + pokemon.scene.pushPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, this.stats, this.levels)); + } cancelled.value = this.overwrites; return true; } @@ -1850,7 +1903,7 @@ export class PostSummonAbAttr extends AbAttr { * @param args Set of unique arguments needed by this attribute * @returns true if application of the ability succeeds */ - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { return false; } } @@ -1869,9 +1922,11 @@ export class PostSummonRemoveArenaTagAbAttr extends PostSummonAbAttr { this.arenaTags = arenaTags; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { - for (const arenaTag of this.arenaTags) { - pokemon.scene.arena.removeTag(arenaTag); + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { + if (!simulated) { + for (const arenaTag of this.arenaTags) { + pokemon.scene.arena.removeTag(arenaTag); + } } return true; } @@ -1886,8 +1941,10 @@ export class PostSummonMessageAbAttr extends PostSummonAbAttr { this.messageFunc = messageFunc; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - pokemon.scene.queueMessage(this.messageFunc(pokemon)); + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (!simulated) { + pokemon.scene.queueMessage(this.messageFunc(pokemon)); + } return true; } @@ -1903,8 +1960,10 @@ export class PostSummonUnnamedMessageAbAttr extends PostSummonAbAttr { this.message = message; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - pokemon.scene.queueMessage(this.message); + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (!simulated) { + pokemon.scene.queueMessage(this.message); + } return true; } @@ -1921,8 +1980,12 @@ export class PostSummonAddBattlerTagAbAttr extends PostSummonAbAttr { this.turnCount = turnCount; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - return pokemon.addTag(this.tagType, this.turnCount); + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (simulated) { + return pokemon.canAddTag(this.tagType); + } else { + return pokemon.addTag(this.tagType, this.turnCount); + } } } @@ -1943,7 +2006,11 @@ export class PostSummonStatChangeAbAttr extends PostSummonAbAttr { this.intimidate = !!intimidate; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (simulated) { + return true; + } + queueShowAbility(pokemon, passive); // TODO: Better solution than manually showing the ability here if (this.selfTarget) { // we unshift the StatChangePhase to put it right after the showAbility and not at the end of the @@ -1954,8 +2021,8 @@ export class PostSummonStatChangeAbAttr extends PostSummonAbAttr { for (const opponent of pokemon.getOpponents()) { const cancelled = new Utils.BooleanHolder(false); if (this.intimidate) { - applyAbAttrs(IntimidateImmunityAbAttr, opponent, cancelled); - applyAbAttrs(PostIntimidateStatChangeAbAttr, opponent, cancelled); + applyAbAttrs(IntimidateImmunityAbAttr, opponent, cancelled, simulated); + applyAbAttrs(PostIntimidateStatChangeAbAttr, opponent, cancelled, simulated); } if (!cancelled.value) { const statChangePhase = new StatChangePhase(pokemon.scene, opponent.getBattlerIndex(), false, this.stats, this.levels); @@ -1977,11 +2044,14 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { this.showAnim = showAnim; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const target = pokemon.getAlly(); if (target?.isActive(true)) { - target.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / this.healRatio), 1), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim)); + if (!simulated) { + target.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(), + Utils.toDmgValue(pokemon.getMaxHp() / this.healRatio), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim)); + } + return true; } @@ -2002,14 +2072,16 @@ export class PostSummonClearAllyStatsAbAttr extends PostSummonAbAttr { super(); } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const target = pokemon.getAlly(); if (target?.isActive(true)) { - for (let s = 0; s < target.summonData.battleStats.length; s++) { - target.summonData.battleStats[s] = 0; - } + if (!simulated) { + for (let s = 0; s < target.summonData.battleStats.length; s++) { + target.summonData.battleStats[s] = 0; + } - target.scene.queueMessage(i18next.t("abilityTriggers:postSummonClearAllyStats", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); + target.scene.queueMessage(i18next.t("abilityTriggers:postSummonClearAllyStats", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); + } return true; } @@ -2040,7 +2112,7 @@ export class DownloadAbAttr extends PostSummonAbAttr { * @param {any[]} args N/A * @returns Returns true if ability is used successful, false if not. */ - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { this.enemyDef = 0; this.enemySpDef = 0; this.enemyCountTally = 0; @@ -2060,7 +2132,9 @@ export class DownloadAbAttr extends PostSummonAbAttr { } if (this.enemyDef > 0 && this.enemySpDef > 0) { // only activate if there's actually an enemy to download from - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, this.stats, 1)); + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, this.stats, 1)); + } return true; } @@ -2077,11 +2151,15 @@ export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr { this.weatherType = weatherType; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { if ((this.weatherType === WeatherType.HEAVY_RAIN || this.weatherType === WeatherType.HARSH_SUN || this.weatherType === WeatherType.STRONG_WINDS) || !pokemon.scene.arena.weather?.isImmutable()) { - return pokemon.scene.arena.trySetWeather(this.weatherType, true); + if (simulated) { + return pokemon.scene.arena.weather?.weatherType !== this.weatherType; + } else { + return pokemon.scene.arena.trySetWeather(this.weatherType, true); + } } return false; @@ -2097,8 +2175,12 @@ export class PostSummonTerrainChangeAbAttr extends PostSummonAbAttr { this.terrainType = terrainType; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (simulated) { + return pokemon.scene.arena.terrain?.terrainType !== this.terrainType; + } else { + return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + } } } @@ -2111,10 +2193,10 @@ export class PostSummonFormChangeAbAttr extends PostSummonAbAttr { this.formFunc = formFunc; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const formIndex = this.formFunc(pokemon); if (formIndex !== pokemon.formIndex) { - return pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + return simulated || pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); } return false; @@ -2126,7 +2208,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { private target: Pokemon; private targetAbilityName: string; - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const targets = pokemon.getOpponents(); if (!targets.length) { return false; @@ -2147,11 +2229,13 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { return false; } - this.target = target!; - this.targetAbilityName = allAbilities[target!.getAbility().id].name; - pokemon.summonData.ability = target!.getAbility().id; - setAbilityRevealed(target!); - pokemon.updateInfo(); + if (!simulated) { + this.target = target!; + this.targetAbilityName = allAbilities[target!.getAbility().id].name; + pokemon.summonData.ability = target!.getAbility().id; + setAbilityRevealed(target!); + pokemon.updateInfo(); + } return true; } @@ -2188,7 +2272,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt * @param args - n/a * @returns A boolean or a promise that resolves to a boolean indicating the result of the ability application. */ - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { const party = pokemon instanceof PlayerPokemon ? pokemon.scene.getPlayerField() : pokemon.scene.getEnemyField(); const allowedParty = party.filter(p => p.isAllowedInBattle()); @@ -2196,14 +2280,15 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt return false; } - for (const pokemon of allowedParty) { - if (pokemon.status && this.statusEffect.includes(pokemon.status.effect)) { - pokemon.scene.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); - pokemon.resetStatus(false); - pokemon.updateInfo(); + if (!simulated) { + for (const pokemon of allowedParty) { + if (pokemon.status && this.statusEffect.includes(pokemon.status.effect)) { + pokemon.scene.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); + pokemon.resetStatus(false); + pokemon.updateInfo(); + } } } - return true; } } @@ -2211,7 +2296,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt /** Attempt to copy the stat changes on an ally pokemon */ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { if (!pokemon.scene.currentBattle.double) { return false; } @@ -2221,8 +2306,10 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { return false; } - pokemon.summonData.battleStats = ally.summonData.battleStats; - pokemon.updateInfo(); + if (!simulated) { + pokemon.summonData.battleStats = ally.summonData.battleStats; + pokemon.updateInfo(); + } return true; } @@ -2240,10 +2327,10 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { super(true); } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const targets = pokemon.getOpponents(); - if (!targets.length) { - return false; + if (simulated || !targets.length) { + return simulated; } let target: Pokemon; @@ -2279,16 +2366,19 @@ export class PreSwitchOutAbAttr extends AbAttr { super(true); } - applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { return false; } } export class PreSwitchOutResetStatusAbAttr extends PreSwitchOutAbAttr { - applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { if (pokemon.status) { - pokemon.resetStatus(); - pokemon.updateInfo(); + if (!simulated) { + pokemon.resetStatus(); + pokemon.updateInfo(); + } + return true; } @@ -2307,7 +2397,7 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { * @param args N/A * @returns {boolean} Returns true if the weather clears, otherwise false. */ - applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { const weatherType = pokemon.scene.arena.weather?.weatherType; let turnOffWeather = false; @@ -2333,6 +2423,10 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { break; } + if (simulated) { + return turnOffWeather; + } + if (turnOffWeather) { pokemon.scene.arena.trySetWeather(WeatherType.NONE, false); return true; @@ -2343,11 +2437,14 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { } export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr { - applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { if (!pokemon.isFullHp()) { - const healAmount = Math.floor(pokemon.getMaxHp() * 0.33); - pokemon.heal(healAmount); - pokemon.updateInfo(); + if (!simulated) { + const healAmount = Utils.toDmgValue(pokemon.getMaxHp() * 0.33); + pokemon.heal(healAmount); + pokemon.updateInfo(); + } + return true; } @@ -2376,10 +2473,12 @@ export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr { * @param args N/A * @returns true if the form change was successful */ - applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { const formIndex = this.formFunc(pokemon); if (formIndex !== pokemon.formIndex) { - pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + if (!simulated) { + pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + } return true; } @@ -2389,22 +2488,22 @@ export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr { } export class PreStatChangeAbAttr extends AbAttr { - applyPreStatChange(pokemon: Pokemon | null, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreStatChange(pokemon: Pokemon | null, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } export class ProtectStatAbAttr extends PreStatChangeAbAttr { - private protectedStat: BattleStat | null; + private protectedStat?: BattleStat; constructor(protectedStat?: BattleStat) { super(); - this.protectedStat = protectedStat ?? null; + this.protectedStat = protectedStat; } - applyPreStatChange(pokemon: Pokemon, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (!this.protectedStat || stat === this.protectedStat) { + applyPreStatChange(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (Utils.isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) { cancelled.value = true; return true; } @@ -2447,16 +2546,20 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr { * @param args [0] {@linkcode StatusEffect} applied by move * @returns true if defender is confused */ - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (this.effects.indexOf(args[0]) > -1 && !defender.isFainted()) { - return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedInt(3,2), move.id, defender.id); + if (simulated) { + return defender.canAddTag(BattlerTagType.CONFUSED); + } else { + return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedInt(3,2), move.id, defender.id); + } } return false; } } export class PreSetStatusAbAttr extends AbAttr { - applyPreSetStatus(pokemon: Pokemon, passive: boolean, effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreSetStatus(pokemon: Pokemon, passive: boolean, simulated: boolean, effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } @@ -2486,7 +2589,7 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { * @param args - n/a * @returns A boolean indicating the result of the status application. */ - applyPreSetStatus(pokemon: Pokemon, passive: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreSetStatus(pokemon: Pokemon, passive: boolean, simulated: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.immuneEffects.length < 1 || this.immuneEffects.includes(effect)) { cancelled.value = true; return true; @@ -2522,7 +2625,7 @@ export class StatusEffectImmunityAbAttr extends PreSetStatusEffectImmunityAbAttr export class UserFieldStatusEffectImmunityAbAttr extends PreSetStatusEffectImmunityAbAttr { } export class PreApplyBattlerTagAbAttr extends AbAttr { - applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, simulated: boolean, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } @@ -2540,10 +2643,12 @@ export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr { this.immuneTagType = immuneTagType; } - applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, simulated: boolean, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (tag.tagType === this.immuneTagType) { cancelled.value = true; - this.battlerTag = tag; + if (!simulated) { + this.battlerTag = tag; + } return true; } @@ -2572,14 +2677,14 @@ export class BattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr { export class UserFieldBattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr { } export class BlockCritAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.BooleanHolder).value = true; return true; } } export class BonusCritAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.BooleanHolder).value = true; return true; } @@ -2594,7 +2699,7 @@ export class MultCritAbAttr extends AbAttr { this.multAmount = multAmount; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { const critMult = args[0] as Utils.NumberHolder; if (critMult.value > 1) { critMult.value *= this.multAmount; @@ -2625,7 +2730,7 @@ export class ConditionalCritAbAttr extends AbAttr { * [1] {@linkcode Pokemon} Target. * [2] {@linkcode Move} used by ability user. */ - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { const target = (args[1] as Pokemon); const move = (args[2] as Move); if (!this.condition(pokemon,target,move)) { @@ -2638,7 +2743,7 @@ export class ConditionalCritAbAttr extends AbAttr { } export class BlockNonDirectDamageAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; } @@ -2666,7 +2771,7 @@ export class BlockStatusDamageAbAttr extends AbAttr { * @param {any[]} args N/A * @returns Returns true if status damage is blocked */ - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (pokemon.status && this.effects.includes(pokemon.status.effect)) { cancelled.value = true; return true; @@ -2676,7 +2781,7 @@ export class BlockStatusDamageAbAttr extends AbAttr { } export class BlockOneHitKOAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; } @@ -2702,7 +2807,7 @@ export class ChangeMovePriorityAbAttr extends AbAttr { this.changeAmount = changeAmount; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (!this.moveFunc(pokemon, args[0] as Move)) { return false; } @@ -2715,7 +2820,7 @@ export class ChangeMovePriorityAbAttr extends AbAttr { export class IgnoreContactAbAttr extends AbAttr { } export class PreWeatherEffectAbAttr extends AbAttr { - applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather | null, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreWeatherEffect(pokemon: Pokemon, passive: Boolean, simulated: boolean, weather: Weather | null, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } @@ -2731,7 +2836,7 @@ export class BlockWeatherDamageAttr extends PreWeatherDamageAbAttr { this.weatherTypes = weatherTypes; } - applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (!this.weatherTypes.length || this.weatherTypes.indexOf(weather?.weatherType) > -1) { cancelled.value = true; } @@ -2749,7 +2854,7 @@ export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr { this.affectsImmutable = !!affectsImmutable; } - applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.affectsImmutable || weather.isImmutable()) { cancelled.value = true; return true; @@ -2856,7 +2961,7 @@ export class ForewarnAbAttr extends PostSummonAbAttr { super(true); } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { let maxPowerSeen = 0; let maxMove = ""; let movePower = 0; @@ -2880,7 +2985,9 @@ export class ForewarnAbAttr extends PostSummonAbAttr { } } } - pokemon.scene.queueMessage(i18next.t("abilityTriggers:forewarn", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: maxMove })); + if (!simulated) { + pokemon.scene.queueMessage(i18next.t("abilityTriggers:forewarn", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: maxMove })); + } return true; } } @@ -2890,17 +2997,19 @@ export class FriskAbAttr extends PostSummonAbAttr { super(true); } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - for (const opponent of pokemon.getOpponents()) { - pokemon.scene.queueMessage(i18next.t("abilityTriggers:frisk", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), opponentName: opponent.name, opponentAbilityName: opponent.getAbility().name })); - setAbilityRevealed(opponent); + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (!simulated) { + for (const opponent of pokemon.getOpponents()) { + pokemon.scene.queueMessage(i18next.t("abilityTriggers:frisk", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), opponentName: opponent.name, opponentAbilityName: opponent.getAbility().name })); + setAbilityRevealed(opponent); + } } return true; } } export class PostWeatherChangeAbAttr extends AbAttr { - applyPostWeatherChange(pokemon: Pokemon, passive: boolean, weather: WeatherType, args: any[]): boolean { + applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { return false; } } @@ -2918,13 +3027,17 @@ export class PostWeatherChangeAddBattlerTagAttr extends PostWeatherChangeAbAttr this.weatherTypes = weatherTypes; } - applyPostWeatherChange(pokemon: Pokemon, passive: boolean, weather: WeatherType, args: any[]): boolean { + applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { console.log(this.weatherTypes.find(w => weather === w), WeatherType[weather]); if (!this.weatherTypes.find(w => weather === w)) { return false; } - return pokemon.addTag(this.tagType, this.turnCount); + if (simulated) { + return pokemon.canAddTag(this.tagType); + } else { + return pokemon.addTag(this.tagType, this.turnCount); + } } } @@ -2937,7 +3050,7 @@ export class PostWeatherLapseAbAttr extends AbAttr { this.weatherTypes = weatherTypes; } - applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, weather: Weather | null, args: any[]): boolean | Promise { + applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather | null, args: any[]): boolean | Promise { return false; } @@ -2955,12 +3068,14 @@ export class PostWeatherLapseHealAbAttr extends PostWeatherLapseAbAttr { this.healFactor = healFactor; } - applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, weather: Weather, args: any[]): boolean { + applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, args: any[]): boolean { if (!pokemon.isFullHp()) { const scene = pokemon.scene; const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / (16 / this.healFactor)), 1), i18next.t("abilityTriggers:postWeatherLapseHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + if (!simulated) { + scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), + Utils.toDmgValue(pokemon.getMaxHp() / (16 / this.healFactor)), i18next.t("abilityTriggers:postWeatherLapseHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + } return true; } @@ -2977,20 +3092,24 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr { this.damageFactor = damageFactor; } - applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, weather: Weather, args: any[]): boolean { + applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, args: any[]): boolean { const scene = pokemon.scene; if (pokemon.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { return false; } - const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - scene.queueMessage(i18next.t("abilityTriggers:postWeatherLapseDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName })); - pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / (16 / this.damageFactor)), HitResult.OTHER); + + if (!simulated) { + const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; + scene.queueMessage(i18next.t("abilityTriggers:postWeatherLapseDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName })); + pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / (16 / this.damageFactor)), HitResult.OTHER); + } + return true; } } export class PostTerrainChangeAbAttr extends AbAttr { - applyPostTerrainChange(pokemon: Pokemon, passive: boolean, terrain: TerrainType, args: any[]): boolean { + applyPostTerrainChange(pokemon: Pokemon, passive: boolean, simulated: boolean, terrain: TerrainType, args: any[]): boolean { return false; } } @@ -3008,12 +3127,16 @@ export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr this.terrainTypes = terrainTypes; } - applyPostTerrainChange(pokemon: Pokemon, passive: boolean, terrain: TerrainType, args: any[]): boolean { + applyPostTerrainChange(pokemon: Pokemon, passive: boolean, simulated: boolean, terrain: TerrainType, args: any[]): boolean { if (!this.terrainTypes.find(t => t === terrain)) { return false; } - return pokemon.addTag(this.tagType, this.turnCount); + if (simulated) { + return pokemon.canAddTag(this.tagType); + } else { + return pokemon.addTag(this.tagType, this.turnCount); + } } } @@ -3025,7 +3148,7 @@ function getTerrainCondition(...terrainTypes: TerrainType[]): AbAttrCondition { } export class PostTurnAbAttr extends AbAttr { - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { return false; } } @@ -3051,13 +3174,15 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { * @param {any[]} args N/A * @returns Returns true if healed from status, false if not */ - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { if (pokemon.status && this.effects.includes(pokemon.status.effect)) { if (!pokemon.isFullHp()) { - const scene = pokemon.scene; - const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / 8), 1), i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), true)); + if (!simulated) { + const scene = pokemon.scene; + const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; + scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), + Utils.toDmgValue(pokemon.getMaxHp() / 8), i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), true)); + } return true; } } @@ -3078,17 +3203,19 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { this.allyTarget = allyTarget; } - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { if (this.allyTarget) { this.target = pokemon.getAlly(); } else { this.target = pokemon; } if (this.target?.status) { + if (!simulated) { + this.target.scene.queueMessage(getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target))); + this.target.resetStatus(false); + this.target.updateInfo(); + } - this.target.scene.queueMessage(getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target))); - this.target.resetStatus(false); - this.target.updateInfo(); return true; } @@ -3113,7 +3240,7 @@ export class PostTurnLootAbAttr extends PostTurnAbAttr { super(); } - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const pass = Phaser.Math.RND.realInRange(0, 1); // Clamp procChance to [0, 1]. Skip if didn't proc (less than pass) if (Math.max(Math.min(this.procChance(pokemon), 1), 0) < pass) { @@ -3121,7 +3248,7 @@ export class PostTurnLootAbAttr extends PostTurnAbAttr { } if (this.itemType === "EATEN_BERRIES") { - return this.createEatenBerry(pokemon); + return this.createEatenBerry(pokemon, simulated); } else { return false; } @@ -3130,15 +3257,20 @@ export class PostTurnLootAbAttr extends PostTurnAbAttr { /** * Create a new berry chosen randomly from the berries the pokemon ate this battle * @param pokemon The pokemon with this ability + * @param simulated whether the associated ability call is simulated * @returns whether a new berry was created */ - createEatenBerry(pokemon: Pokemon): boolean { + createEatenBerry(pokemon: Pokemon, simulated: boolean): boolean { const berriesEaten = pokemon.battleData.berriesEaten; if (!berriesEaten.length) { return false; } + if (simulated) { + return true; + } + const randomIdx = Utils.randSeedInt(berriesEaten.length); const chosenBerryType = berriesEaten[randomIdx]; const chosenBerry = new BerryModifierType(chosenBerryType); @@ -3167,23 +3299,37 @@ export class PostTurnLootAbAttr extends PostTurnAbAttr { } } +/** + * Attribute used for {@linkcode Abilities.MOODY} + */ export class MoodyAbAttr extends PostTurnAbAttr { constructor() { super(true); } - - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + /** + * Randomly increases one BattleStat by 2 stages and decreases a different BattleStat by 1 stage + * @param {Pokemon} pokemon Pokemon that has this ability + * @param passive N/A + * @param simulated true if applying in a simulated call. + * @param args N/A + * @returns true + * + * Any BattleStats at +6 or -6 are excluded from being increased or decreased, respectively + * If the pokemon already has all BattleStats raised to stage 6, it will only decrease one BattleStat by 1 stage + * If the pokemon already has all BattleStats lowered to stage -6, it will only increase one BattleStat by 2 stages + */ + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const selectableStats = [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD]; const increaseStatArray = selectableStats.filter(s => pokemon.summonData.battleStats[s] < 6); let decreaseStatArray = selectableStats.filter(s => pokemon.summonData.battleStats[s] > -6); - if (increaseStatArray.length > 0) { + if (!simulated && increaseStatArray.length > 0) { const increaseStat = increaseStatArray[Utils.randInt(increaseStatArray.length)]; decreaseStatArray = decreaseStatArray.filter(s => s !== increaseStat); pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [increaseStat], 2)); } - if (decreaseStatArray.length > 0) { - const decreaseStat = selectableStats[Utils.randInt(selectableStats.length)]; + if (!simulated && decreaseStatArray.length > 0) { + const decreaseStat = decreaseStatArray[Utils.randInt(decreaseStatArray.length)]; pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [decreaseStat], -1)); } return true; @@ -3203,19 +3349,24 @@ export class PostTurnStatChangeAbAttr extends PostTurnAbAttr { this.levels = levels; } - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + } return true; } } export class PostTurnHealAbAttr extends PostTurnAbAttr { - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { if (!pokemon.isFullHp()) { - const scene = pokemon.scene; - const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / 16), 1), i18next.t("abilityTriggers:postTurnHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + if (!simulated) { + const scene = pokemon.scene; + const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; + scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), + Utils.toDmgValue(pokemon.getMaxHp() / 16), i18next.t("abilityTriggers:postTurnHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + } + return true; } @@ -3232,10 +3383,13 @@ export class PostTurnFormChangeAbAttr extends PostTurnAbAttr { this.formFunc = formFunc; } - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const formIndex = this.formFunc(pokemon); if (formIndex !== pokemon.formIndex) { - pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + if (!simulated) { + pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + } + return true; } @@ -3253,15 +3407,18 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { * Deals damage to all sleeping opponents equal to 1/8 of their max hp (min 1) * @param {Pokemon} pokemon Pokemon that has this ability * @param {boolean} passive N/A + * @param {boolean} simulated true if applying in a simulated call. * @param {any[]} args N/A * @returns {boolean} true if any opponents are sleeping */ - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { let hadEffect: boolean = false; for (const opp of pokemon.getOpponents()) { if ((opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(Abilities.COMATOSE)) && !opp.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { - opp.damageAndUpdate(Math.floor(Math.max(1, opp.getMaxHp() / 8)), HitResult.OTHER); - pokemon.scene.queueMessage(i18next.t("abilityTriggers:badDreams", {pokemonName: getPokemonNameWithAffix(opp)})); + if (!simulated) { + opp.damageAndUpdate(Utils.toDmgValue(opp.getMaxHp() / 8), HitResult.OTHER); + pokemon.scene.queueMessage(i18next.t("abilityTriggers:badDreams", {pokemonName: getPokemonNameWithAffix(opp)})); + } hadEffect = true; } @@ -3286,7 +3443,10 @@ export class FetchBallAbAttr extends PostTurnAbAttr { * @param args N/A * @returns true if player has used a pokeball and this pokemon is owned by the player */ - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (simulated) { + return false; + } const lastUsed = pokemon.scene.currentBattle.lastUsedPokeball; if (lastUsed !== null && !!pokemon.isPlayer) { pokemon.scene.pokeballCounts[lastUsed]++; @@ -3309,9 +3469,13 @@ export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr { this.weatherType = weatherType; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (!pokemon.scene.arena.weather?.isImmutable()) { - return pokemon.scene.arena.trySetWeather(this.weatherType, true); + if (simulated) { + return pokemon.scene.arena.weather?.weatherType !== this.weatherType; + } else { + return pokemon.scene.arena.trySetWeather(this.weatherType, true); + } } return false; @@ -3327,8 +3491,12 @@ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr { this.terrainType = terrainType; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (simulated) { + return pokemon.scene.arena.terrain?.terrainType !== this.terrainType; + } else { + return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + } } } @@ -3337,7 +3505,7 @@ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr { * @extends AbAttr */ export class PostMoveUsedAbAttr extends AbAttr { - applyPostMoveUsed(pokemon: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], args: any[]): boolean | Promise { + applyPostMoveUsed(pokemon: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], simulated: boolean, args: any[]): boolean | Promise { return false; } } @@ -3358,20 +3526,22 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { * * @return true if the Dancer ability was resolved */ - applyPostMoveUsed(dancer: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], args: any[]): boolean | Promise { + applyPostMoveUsed(dancer: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], simulated: boolean, args: any[]): boolean | Promise { // List of tags that prevent the Dancer from replicating the move const forbiddenTags = [BattlerTagType.FLYING, BattlerTagType.UNDERWATER, BattlerTagType.UNDERGROUND, BattlerTagType.HIDDEN]; // The move to replicate cannot come from the Dancer if (source.getBattlerIndex() !== dancer.getBattlerIndex() && !dancer.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType))) { - // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance - if (move.getMove() instanceof AttackMove || move.getMove() instanceof StatusMove) { - const target = this.getTarget(dancer, source, targets); - dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, target, move, true)); - } else if (move.getMove() instanceof SelfStatusMove) { - // If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself - dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, [dancer.getBattlerIndex()], move, true)); + if (!simulated) { + // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance + if (move.getMove() instanceof AttackMove || move.getMove() instanceof StatusMove) { + const target = this.getTarget(dancer, source, targets); + dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, target, move, true)); + } else if (move.getMove() instanceof SelfStatusMove) { + // If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself + dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, [dancer.getBattlerIndex()], move, true)); + } } return true; } @@ -3402,7 +3572,7 @@ export class StatChangeMultiplierAbAttr extends AbAttr { this.multiplier = multiplier; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.IntegerHolder).value *= this.multiplier; return true; @@ -3410,8 +3580,10 @@ export class StatChangeMultiplierAbAttr extends AbAttr { } export class StatChangeCopyAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, (args[0] as BattleStat[]), (args[1] as integer), true, false, false)); + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, (args[0] as BattleStat[]), (args[1] as integer), true, false, false)); + } return true; } } @@ -3421,7 +3593,7 @@ export class BypassBurnDamageReductionAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; @@ -3445,15 +3617,15 @@ export class ReduceBurnDamageAbAttr extends AbAttr { * @param args `[0]` {@linkcode Utils.NumberHolder} The damage value being modified * @returns `true` */ - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = Math.max(Math.floor((args[0] as Utils.NumberHolder).value * this.multiplier), 1); + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + (args[0] as Utils.NumberHolder).value = Utils.toDmgValue((args[0] as Utils.NumberHolder).value * this.multiplier); return true; } } export class DoubleBerryEffectAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.NumberHolder).value *= 2; return true; @@ -3461,7 +3633,7 @@ export class DoubleBerryEffectAbAttr extends AbAttr { } export class PreventBerryUseAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; @@ -3484,24 +3656,25 @@ export class HealFromBerryUseAbAttr extends AbAttr { this.healPercent = Math.max(Math.min(healPercent, 1), 0); } - apply(pokemon: Pokemon, passive: boolean, ...args: [Utils.BooleanHolder, any[]]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, ...args: [Utils.BooleanHolder, any[]]): boolean { const { name: abilityName } = passive ? pokemon.getPassiveAbility() : pokemon.getAbility(); - pokemon.scene.unshiftPhase( - new PokemonHealPhase( - pokemon.scene, - pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() * this.healPercent), 1), - i18next.t("abilityTriggers:healFromBerryUse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), - true - ) - ); - + if (!simulated) { + pokemon.scene.unshiftPhase( + new PokemonHealPhase( + pokemon.scene, + pokemon.getBattlerIndex(), + Utils.toDmgValue(pokemon.getMaxHp() * this.healPercent), + i18next.t("abilityTriggers:healFromBerryUse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), + true + ) + ); + } return true; } } export class RunSuccessAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.IntegerHolder).value = 256; return true; @@ -3524,7 +3697,7 @@ export class CheckTrappedAbAttr extends AbAttr { this.arenaTrapCondition = condition; } - applyCheckTrapped(pokemon: Pokemon, passive: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean | Promise { + applyCheckTrapped(pokemon: Pokemon, passive: boolean, simulated: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean | Promise { return false; } } @@ -3549,7 +3722,7 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { * @param args N/A * @returns if enemy Pokemon is trapped or not */ - applyCheckTrapped(pokemon: Pokemon, passive: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean { + applyCheckTrapped(pokemon: Pokemon, passive: boolean, simulated: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean { if (this.arenaTrapCondition(pokemon, otherPokemon)) { if (otherPokemon.getTypes(true).includes(Type.GHOST) || (otherPokemon.getTypes(true).includes(Type.STELLAR) && otherPokemon.getTypes().includes(Type.GHOST))) { trapped.value = false; @@ -3571,7 +3744,7 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { } export class MaxMultiHitAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.IntegerHolder).value = 0; return true; @@ -3583,15 +3756,15 @@ export class PostBattleAbAttr extends AbAttr { super(true); } - applyPostBattle(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { return false; } } export class PostBattleLootAbAttr extends PostBattleAbAttr { - applyPostBattle(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const postBattleLoot = pokemon.scene.currentBattle.postBattleLoot; - if (postBattleLoot.length) { + if (!simulated && postBattleLoot.length) { const randItem = Utils.randSeedItem(postBattleLoot); //@ts-ignore - TODO see below if (pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, true, 1, true)) { // TODO: fix. This is a promise!? @@ -3606,7 +3779,7 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr { } export class PostFaintAbAttr extends AbAttr { - applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { return false; } } @@ -3625,7 +3798,7 @@ export class PostFaintClearWeatherAbAttr extends PostFaintAbAttr { * @param args N/A * @returns {boolean} Returns true if the weather clears, otherwise false. */ - applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { const weatherType = pokemon.scene.arena.weather?.weatherType; let turnOffWeather = false; @@ -3651,6 +3824,10 @@ export class PostFaintClearWeatherAbAttr extends PostFaintAbAttr { break; } + if (simulated) { + return turnOffWeather; + } + if (turnOffWeather) { pokemon.scene.arena.trySetWeather(WeatherType.NONE, false); return true; @@ -3669,15 +3846,17 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { this.damageRatio = damageRatio; } - applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) { const cancelled = new Utils.BooleanHolder(false); - pokemon.scene.getField(true).map(p=>applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled)); + pokemon.scene.getField(true).map(p => applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled, simulated)); if (cancelled.value || attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { return false; } - attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); - attacker.turnData.damageTaken += Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)); + if (!simulated) { + attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); + attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); + } return true; } @@ -3697,10 +3876,12 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr { super (); } - applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - const damage = pokemon.turnData.attacksReceived[0].damage; - attacker.damageAndUpdate((damage), HitResult.OTHER); - attacker.turnData.damageTaken += damage; + applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + if (!simulated) { + const damage = pokemon.turnData.attacksReceived[0].damage; + attacker.damageAndUpdate((damage), HitResult.OTHER); + attacker.turnData.damageTaken += damage; + } return true; } @@ -3710,7 +3891,7 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr { } export class RedirectMoveAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.canRedirect(args[0] as Moves)) { const target = args[1] as Utils.IntegerHolder; const newTarget = pokemon.getBattlerIndex(); @@ -3753,9 +3934,9 @@ export class ReduceStatusEffectDurationAbAttr extends AbAttr { this.statusEffect = statusEffect; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (args[0] === this.statusEffect) { - (args[1] as Utils.IntegerHolder).value = Math.floor((args[1] as Utils.IntegerHolder).value / 2); + (args[1] as Utils.IntegerHolder).value = Utils.toDmgValue((args[1] as Utils.IntegerHolder).value / 2); return true; } @@ -3782,8 +3963,10 @@ export class FlinchStatChangeAbAttr extends FlinchEffectAbAttr { this.levels = levels; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + } return true; } } @@ -3791,7 +3974,7 @@ export class FlinchStatChangeAbAttr extends FlinchEffectAbAttr { export class IncreasePpAbAttr extends AbAttr { } export class ForceSwitchOutImmunityAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; } @@ -3802,7 +3985,7 @@ export class ReduceBerryUseThresholdAbAttr extends AbAttr { super(); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { const hpRatio = pokemon.getHpRatio(); if (args[0].value < hpRatio) { @@ -3823,7 +4006,7 @@ export class WeightMultiplierAbAttr extends AbAttr { this.multiplier = multiplier; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.NumberHolder).value *= this.multiplier; return true; @@ -3835,7 +4018,7 @@ export class SyncEncounterNatureAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Pokemon).setNature(pokemon.getNature()); return true; @@ -3851,7 +4034,7 @@ export class MoveAbilityBypassAbAttr extends AbAttr { this.moveIgnoreFunc = moveIgnoreFunc || ((pokemon, move) => true); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.moveIgnoreFunc(pokemon, (args[0] as Move))) { cancelled.value = true; return true; @@ -3865,7 +4048,7 @@ export class SuppressFieldAbilitiesAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { const ability = (args[0] as Ability); if (!ability.hasAttr(UnsuppressableAbilityAbAttr) && !ability.hasAttr(SuppressFieldAbilitiesAbAttr)) { cancelled.value = true; @@ -3920,7 +4103,7 @@ export class IgnoreTypeImmunityAbAttr extends AbAttr { this.allowedMoveTypes = allowedMoveTypes; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.defenderType === (args[1] as Type) && this.allowedMoveTypes.includes(args[0] as Type)) { cancelled.value = true; return true; @@ -3943,7 +4126,7 @@ export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr { this.defenderType = defenderType; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.statusEffect.includes(args[0] as StatusEffect) && this.defenderType.includes(args[1] as Type)) { cancelled.value = true; return true; @@ -3970,8 +4153,10 @@ export class MoneyAbAttr extends PostBattleAbAttr { * @param args N/A * @returns true */ - applyPostBattle(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - pokemon.scene.currentBattle.moneyScattered += pokemon.scene.getWaveMoneyAmount(0.2); + applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (!simulated) { + pokemon.scene.currentBattle.moneyScattered += pokemon.scene.getWaveMoneyAmount(0.2); + } return true; } } @@ -4010,11 +4195,11 @@ export class PostSummonStatChangeOnArenaAbAttr extends PostSummonStatChangeAbAtt * @param {any[]} args - Additional arguments. * @returns {boolean} - Returns true if the stat change was applied, otherwise false. */ - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const side = pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; if (pokemon.scene.arena.getTagOnSide(this.tagType, side)) { - return super.applyPostSummon(pokemon, passive, args); + return super.applyPostSummon(pokemon, passive, simulated, args); } return false; } @@ -4052,12 +4237,14 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { * @param {any[]} args Additional arguments. * @returns {boolean} Whether the immunity was applied. */ - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.condition(pokemon, attacker, move)) { - (args[0] as Utils.NumberHolder).value = this.multiplier; - pokemon.removeTag(this.tagType); - if (this.recoilDamageFunc) { - pokemon.damageAndUpdate(this.recoilDamageFunc(pokemon), HitResult.OTHER); + if (!simulated) { + (args[0] as Utils.NumberHolder).value = this.multiplier; + pokemon.removeTag(this.tagType); + if (this.recoilDamageFunc) { + pokemon.damageAndUpdate(this.recoilDamageFunc(pokemon), HitResult.OTHER); + } } return true; } @@ -4101,7 +4288,10 @@ export class BypassSpeedChanceAbAttr extends AbAttr { * @param {any[]} args [0] {@linkcode Utils.BooleanHolder} set to true when the ability activated * @returns {boolean} - whether the ability was activated. */ - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (simulated) { + return false; + } const bypassSpeed = args[0] as Utils.BooleanHolder; if (!bypassSpeed.value && pokemon.randSeedInt(100) < this.chance) { @@ -4144,7 +4334,7 @@ export class PreventBypassSpeedChanceAbAttr extends AbAttr { * @argument {boolean} bypassSpeed - determines if a Pokemon is able to bypass speed at the moment * @argument {boolean} canCheckHeldItems - determines if a Pokemon has access to Quick Claw's effects or not */ - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { const bypassSpeed = args[0] as Utils.BooleanHolder; const canCheckHeldItems = args[1] as Utils.BooleanHolder; @@ -4166,7 +4356,7 @@ async function applyAbAttrsInternal( applyFunc: AbAttrApplyFunc, args: any[], showAbilityInstant: boolean = false, - isQuiet: boolean = false, + quiet: boolean = false, messages: string[] = [], ) { for (const passive of [false, true]) { @@ -4193,11 +4383,11 @@ async function applyAbAttrsInternal( if (pokemon.summonData && !pokemon.summonData.abilitiesApplied.includes(ability.id)) { pokemon.summonData.abilitiesApplied.push(ability.id); } - if (pokemon.battleData && !pokemon.battleData.abilitiesApplied.includes(ability.id)) { + if (pokemon.battleData && !quiet && !pokemon.battleData.abilitiesApplied.includes(ability.id)) { pokemon.battleData.abilitiesApplied.push(ability.id); } - if (attr.showAbility && !isQuiet) { + if (attr.showAbility && !quiet) { if (showAbilityInstant) { pokemon.scene.abilityBar.showAbility(pokemon, passive); } else { @@ -4205,12 +4395,12 @@ async function applyAbAttrsInternal( } } - const message = attr.getTriggerMessage(pokemon, ability.name, args); - if (message) { - if (!isQuiet) { + if (!quiet) { + const message = attr.getTriggerMessage(pokemon, ability.name, args); + if (message) { pokemon.scene.queueMessage(message); + messages.push(message); } - messages.push(message); } } } @@ -4219,34 +4409,33 @@ async function applyAbAttrsInternal( } } -export function applyAbAttrs(attrType: Constructor, pokemon: Pokemon, cancelled: Utils.BooleanHolder | null, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.apply(pokemon, passive, cancelled, args), args); +export function applyAbAttrs(attrType: Constructor, pokemon: Pokemon, cancelled: Utils.BooleanHolder | null, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.apply(pokemon, passive, simulated, cancelled, args), args, false, simulated); } export function applyPostBattleInitAbAttrs(attrType: Constructor, - pokemon: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostBattleInit(pokemon, passive, args), args); + pokemon: Pokemon, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostBattleInit(pokemon, passive, simulated, args), args, false, simulated); } export function applyPreDefendAbAttrs(attrType: Constructor, - pokemon: Pokemon, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, ...args: any[]): Promise { - const simulated = args.length > 1 && args[1]; - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreDefend(pokemon, passive, attacker, move, cancelled, args), args, false, simulated); + pokemon: Pokemon, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args), args, false, simulated); } export function applyPostDefendAbAttrs(attrType: Constructor, - pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult | null, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, attacker, move, hitResult, args), args); + pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult | null, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args), args, false, simulated); } export function applyPostMoveUsedAbAttrs(attrType: Constructor, - pokemon: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostMoveUsed(pokemon, move, source, targets, args), args); + pokemon: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostMoveUsed(pokemon, move, source, targets, simulated, args), args, false, simulated); } export function applyBattleStatMultiplierAbAttrs(attrType: Constructor, - pokemon: Pokemon, battleStat: BattleStat, statValue: Utils.NumberHolder, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyBattleStat(pokemon, passive, battleStat, statValue, args), args); + pokemon: Pokemon, battleStat: BattleStat, statValue: Utils.NumberHolder, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyBattleStat(pokemon, passive, simulated, battleStat, statValue, args), args, false, simulated); } /** @@ -4260,99 +4449,98 @@ export function applyBattleStatMultiplierAbAttrs(attrType: Constructor, - pokemon: Pokemon, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyFieldBattleStat(pokemon, passive, stat, statValue, checkedPokemon, hasApplied, args), args); + pokemon: Pokemon, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyFieldBattleStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, hasApplied, args), args, false, simulated); } export function applyPreAttackAbAttrs(attrType: Constructor, - pokemon: Pokemon, defender: Pokemon | null, move: Move, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreAttack(pokemon, passive, defender, move, args), args); + pokemon: Pokemon, defender: Pokemon | null, move: Move, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreAttack(pokemon, passive, simulated, defender, move, args), args, false, simulated); } export function applyPostAttackAbAttrs(attrType: Constructor, - pokemon: Pokemon, defender: Pokemon, move: Move, hitResult: HitResult | null, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostAttack(pokemon, passive, defender, move, hitResult, args), args); + pokemon: Pokemon, defender: Pokemon, move: Move, hitResult: HitResult | null, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args), args, false, simulated); } export function applyPostKnockOutAbAttrs(attrType: Constructor, - pokemon: Pokemon, knockedOut: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostKnockOut(pokemon, passive, knockedOut, args), args); + pokemon: Pokemon, knockedOut: Pokemon, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostKnockOut(pokemon, passive, simulated, knockedOut, args), args, false, simulated); } export function applyPostVictoryAbAttrs(attrType: Constructor, - pokemon: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostVictory(pokemon, passive, args), args); + pokemon: Pokemon, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostVictory(pokemon, passive, simulated, args), args, false, simulated); } export function applyPostSummonAbAttrs(attrType: Constructor, - pokemon: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostSummon(pokemon, passive, args), args); + pokemon: Pokemon, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args), args, false, simulated); } export function applyPreSwitchOutAbAttrs(attrType: Constructor, - pokemon: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreSwitchOut(pokemon, passive, args), args, true); + pokemon: Pokemon, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreSwitchOut(pokemon, passive, simulated, args), args, true, simulated); } export function applyPreStatChangeAbAttrs(attrType: Constructor, - pokemon: Pokemon | null, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreStatChange(pokemon, passive, stat, cancelled, args), args); + pokemon: Pokemon | null, stat: BattleStat, cancelled: Utils.BooleanHolder, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreStatChange(pokemon, passive, simulated, stat, cancelled, args), args, false, simulated); } export function applyPostStatChangeAbAttrs(attrType: Constructor, - pokemon: Pokemon, stats: BattleStat[], levels: integer, selfTarget: boolean, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostStatChange(pokemon, stats, levels, selfTarget, args), args); + pokemon: Pokemon, stats: BattleStat[], levels: integer, selfTarget: boolean, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostStatChange(pokemon, simulated, stats, levels, selfTarget, args), args, false, simulated); } export function applyPreSetStatusAbAttrs(attrType: Constructor, - pokemon: Pokemon, effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { - const simulated = args.length > 1 && args[1]; - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreSetStatus(pokemon, passive, effect, cancelled, args), args, false, !simulated); + pokemon: Pokemon, effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreSetStatus(pokemon, passive, simulated, effect, cancelled, args), args, false, simulated); } export function applyPreApplyBattlerTagAbAttrs(attrType: Constructor, - pokemon: Pokemon, tag: BattlerTag, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreApplyBattlerTag(pokemon, passive, tag, cancelled, args), args); + pokemon: Pokemon, tag: BattlerTag, cancelled: Utils.BooleanHolder, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args), args, false, simulated); } export function applyPreWeatherEffectAbAttrs(attrType: Constructor, - pokemon: Pokemon, weather: Weather | null, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreWeatherEffect(pokemon, passive, weather, cancelled, args), args, true); + pokemon: Pokemon, weather: Weather | null, cancelled: Utils.BooleanHolder, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreWeatherEffect(pokemon, passive, simulated, weather, cancelled, args), args, true, simulated); } export function applyPostTurnAbAttrs(attrType: Constructor, - pokemon: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostTurn(pokemon, passive, args), args); + pokemon: Pokemon, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostTurn(pokemon, passive, simulated, args), args, false, simulated); } export function applyPostWeatherChangeAbAttrs(attrType: Constructor, - pokemon: Pokemon, weather: WeatherType, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostWeatherChange(pokemon, passive, weather, args), args); + pokemon: Pokemon, weather: WeatherType, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostWeatherChange(pokemon, passive, simulated, weather, args), args, false, simulated); } export function applyPostWeatherLapseAbAttrs(attrType: Constructor, - pokemon: Pokemon, weather: Weather | null, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostWeatherLapse(pokemon, passive, weather, args), args); + pokemon: Pokemon, weather: Weather | null, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostWeatherLapse(pokemon, passive, simulated, weather, args), args, false, simulated); } export function applyPostTerrainChangeAbAttrs(attrType: Constructor, - pokemon: Pokemon, terrain: TerrainType, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostTerrainChange(pokemon, passive, terrain, args), args); + pokemon: Pokemon, terrain: TerrainType, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostTerrainChange(pokemon, passive, simulated, terrain, args), args, false, simulated); } export function applyCheckTrappedAbAttrs(attrType: Constructor, - pokemon: Pokemon, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, isQuiet: boolean, messages: string[], ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyCheckTrapped(pokemon, passive, trapped, otherPokemon, args), args, false, isQuiet, messages); + pokemon: Pokemon, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, messages: string[], simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args), args, false, simulated, messages); } export function applyPostBattleAbAttrs(attrType: Constructor, - pokemon: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostBattle(pokemon, passive, args), args); + pokemon: Pokemon, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostBattle(pokemon, passive, simulated, args), args, false, simulated); } export function applyPostFaintAbAttrs(attrType: Constructor, - pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostFaint(pokemon, passive, attacker, move, hitResult, args), args); + pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args), args, false, simulated); } function queueShowAbility(pokemon: Pokemon, passive: boolean): void { @@ -4417,7 +4605,7 @@ export function initAbilities() { .ignorable(), new Ability(Abilities.CLOUD_NINE, 3) .attr(SuppressWeatherEffectAbAttr, true) - .attr(PostSummonUnnamedMessageAbAttr, "The effects of the weather disappeared."), + .attr(PostSummonUnnamedMessageAbAttr, i18next.t("abilityTriggers:weatherEffectDisappeared")), new Ability(Abilities.COMPOUND_EYES, 3) .attr(BattleStatMultiplierAbAttr, BattleStat.ACC, 1.3), new Ability(Abilities.INSOMNIA, 3) @@ -4612,7 +4800,7 @@ export function initAbilities() { .ignorable(), new Ability(Abilities.AIR_LOCK, 3) .attr(SuppressWeatherEffectAbAttr, true) - .attr(PostSummonUnnamedMessageAbAttr, "The effects of the weather disappeared."), + .attr(PostSummonUnnamedMessageAbAttr, i18next.t("abilityTriggers:weatherEffectDisappeared")), new Ability(Abilities.TANGLED_FEET, 4) .conditionalAttr(pokemon => !!pokemon.getTag(BattlerTagType.CONFUSED), BattleStatMultiplierAbAttr, BattleStat.EVA, 2) .ignorable(), @@ -5037,8 +5225,9 @@ export function initAbilities() { .conditionalAttr(pokemon => pokemon.formIndex === 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.DISGUISE, 0, false) .attr(FormBlockDamageAbAttr, (target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getAttackTypeEffectiveness(move.type, user) > 0, 0, BattlerTagType.DISGUISE, (pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }), - (pokemon) => Math.floor(pokemon.getMaxHp() / 8)) + (pokemon) => Utils.toDmgValue(pokemon.getMaxHp() / 8)) .attr(PostBattleInitFormChangeAbAttr, () => 0) + .bypassFaint() .ignorable(), new Ability(Abilities.BATTLE_BOND, 7) .attr(PostVictoryFormChangeAbAttr, () => 2) @@ -5191,6 +5380,7 @@ export function initAbilities() { .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() .ignorable(), new Ability(Abilities.POWER_SPOT, 8) .attr(AllyMoveCategoryPowerBoostAbAttr, [MoveCategory.SPECIAL, MoveCategory.PHYSICAL], 1.3), diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index fdfcd4d076a..a60ea5c2981 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -4,7 +4,6 @@ import * as Utils from "../utils"; import { MoveCategory, allMoves, MoveTarget, IncrementMovePriorityAttr, applyMoveAttrs } from "./move"; import { getPokemonNameWithAffix } from "../messages"; import Pokemon, { HitResult, PokemonMove } from "../field/pokemon"; -import { MoveEffectPhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../phases"; import { StatusEffect } from "./status-effect"; import { BattlerIndex } from "../battle"; import { BlockNonDirectDamageAbAttr, ChangeMovePriorityAbAttr, ProtectStatAbAttr, applyAbAttrs } from "./ability"; @@ -15,6 +14,10 @@ import { Abilities } from "#enums/abilities"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; +import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase.js"; +import { ShowAbilityPhase } from "#app/phases/show-ability-phase.js"; +import { StatChangePhase } from "#app/phases/stat-change-phase.js"; export enum ArenaTagSide { BOTH, @@ -266,7 +269,7 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => { if (effectPhase instanceof MoveEffectPhase) { const attacker = effectPhase.getUserPokemon()!; applyMoveAttrs(IncrementMovePriorityAttr, attacker, null, move, priority); - applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, move, priority); + applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, false, move, priority); } return priority.value > 0; }; @@ -424,7 +427,7 @@ class WishTag extends ArenaTag { if (user) { this.battlerIndex = user.getBattlerIndex(); this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) }); - this.healHp = Math.max(Math.floor(user.getMaxHp() / 2), 1); + this.healHp = Utils.toDmgValue(user.getMaxHp() / 2); } else { console.warn("Failed to get source for WishTag onAdd"); } @@ -582,7 +585,7 @@ class SpikesTag extends ArenaTrapTag { if (!cancelled.value) { const damageHpRatio = 1 / (10 - 2 * this.layers); - const damage = Math.ceil(pokemon.getMaxHp() * damageHpRatio); + const damage = Utils.toDmgValue(pokemon.getMaxHp() * damageHpRatio); pokemon.scene.queueMessage(i18next.t("arenaTag:spikesActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); pokemon.damageAndUpdate(damage, HitResult.OTHER); @@ -742,7 +745,7 @@ class StealthRockTag extends ArenaTrapTag { const damageHpRatio = this.getDamageHpRatio(pokemon); if (damageHpRatio) { - const damage = Math.ceil(pokemon.getMaxHp() * damageHpRatio); + const damage = Utils.toDmgValue(pokemon.getMaxHp() * damageHpRatio); pokemon.scene.queueMessage(i18next.t("arenaTag:stealthRockActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); pokemon.damageAndUpdate(damage, HitResult.OTHER); if (pokemon.turnData) { diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index b059b4cf6b2..8c05d296e76 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1,5 +1,4 @@ import { ChargeAnim, CommonAnim, CommonBattleAnim, MoveChargeAnim } from "./battle-anims"; -import { CommonAnimPhase, MoveEffectPhase, MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangeCallback, StatChangePhase } from "../phases"; import { getPokemonNameWithAffix } from "../messages"; import Pokemon, { MoveResult, HitResult } from "../field/pokemon"; import { Stat, getStatName } from "./pokemon-stat"; @@ -18,6 +17,12 @@ import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import i18next from "#app/plugins/i18n.js"; +import { CommonAnimPhase } from "#app/phases/common-anim-phase.js"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; +import { MovePhase } from "#app/phases/move-phase.js"; +import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase.js"; +import { ShowAbilityPhase } from "#app/phases/show-ability-phase.js"; +import { StatChangePhase, StatChangeCallback } from "#app/phases/stat-change-phase.js"; export enum BattlerTagLapseType { FAINT, @@ -342,7 +347,7 @@ export class ConfusedTag extends BattlerTag { if (pokemon.randSeedInt(3) === 0) { const atk = pokemon.getBattleStat(Stat.ATK); const def = pokemon.getBattleStat(Stat.DEF); - const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100)); + const damage = Utils.toDmgValue(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100)); pokemon.scene.queueMessage(i18next.t("battlerTags:confusedLapseHurtItself")); pokemon.damageAndUpdate(damage); pokemon.battleData.hitCount++; @@ -519,7 +524,7 @@ export class SeedTag extends BattlerTag { if (!cancelled.value) { pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED)); - const damage = pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 8), 1)); + const damage = pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 8)); const reverseDrain = pokemon.hasAbilityWithAttr(ReverseDrainAbAttr, false); pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(), !reverseDrain ? damage : damage * -1, @@ -565,7 +570,7 @@ export class NightmareTag extends BattlerTag { applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); if (!cancelled.value) { - pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / 4)); + pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 4)); } } @@ -709,7 +714,7 @@ export class IngrainTag extends TrappedTag { new PokemonHealPhase( pokemon.scene, pokemon.getBattlerIndex(), - Math.floor(pokemon.getMaxHp() / 16), + Utils.toDmgValue(pokemon.getMaxHp() / 16), i18next.t("battlerTags:ingrainLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), true ) @@ -772,7 +777,7 @@ export class AquaRingTag extends BattlerTag { new PokemonHealPhase( pokemon.scene, pokemon.getBattlerIndex(), - Math.floor(pokemon.getMaxHp() / 16), + Utils.toDmgValue(pokemon.getMaxHp() / 16), i18next.t("battlerTags:aquaRingLapse", { moveName: this.getMoveName(), pokemonName: getPokemonNameWithAffix(pokemon) @@ -878,7 +883,7 @@ export abstract class DamagingTrapTag extends TrappedTag { applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); if (!cancelled.value) { - pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / 8)); + pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 8)); } } @@ -1062,7 +1067,7 @@ export class ContactDamageProtectedTag extends ProtectedTag { if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) { const attacker = effectPhase.getPokemon(); if (!attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { - attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); + attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); } } } @@ -1536,7 +1541,7 @@ export class SaltCuredTag extends BattlerTag { if (!cancelled.value) { const pokemonSteelOrWater = pokemon.isOfType(Type.STEEL) || pokemon.isOfType(Type.WATER); - pokemon.damageAndUpdate(Math.max(Math.floor(pokemonSteelOrWater ? pokemon.getMaxHp() / 4 : pokemon.getMaxHp() / 8), 1)); + pokemon.damageAndUpdate(Utils.toDmgValue(pokemonSteelOrWater ? pokemon.getMaxHp() / 4 : pokemon.getMaxHp() / 8)); pokemon.scene.queueMessage( i18next.t("battlerTags:saltCuredLapse", { @@ -1582,7 +1587,7 @@ export class CursedTag extends BattlerTag { applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); if (!cancelled.value) { - pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 4), 1)); + pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 4)); pokemon.scene.queueMessage(i18next.t("battlerTags:cursedLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); } } diff --git a/src/data/berry.ts b/src/data/berry.ts index 30b89848452..d0c9c311e16 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -1,4 +1,3 @@ -import { PokemonHealPhase, StatChangePhase } from "../phases"; import { getPokemonNameWithAffix } from "../messages"; import Pokemon, { HitResult } from "../field/pokemon"; import { BattleStat } from "./battle-stat"; @@ -8,6 +7,8 @@ import { DoubleBerryEffectAbAttr, ReduceBerryUseThresholdAbAttr, applyAbAttrs } import i18next from "i18next"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; +import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase.js"; +import { StatChangePhase } from "#app/phases/stat-change-phase.js"; export function getBerryName(berryType: BerryType): string { return i18next.t(`berry:${BerryType[berryType]}.name`); @@ -35,25 +36,25 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate { return (pokemon: Pokemon) => { const threshold = new Utils.NumberHolder(0.25); const battleStat = (berryType - BerryType.LIECHI) as BattleStat; - applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold); + applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold); return pokemon.getHpRatio() < threshold.value && pokemon.summonData.battleStats[battleStat] < 6; }; case BerryType.LANSAT: return (pokemon: Pokemon) => { const threshold = new Utils.NumberHolder(0.25); - applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold); + applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold); return pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST); }; case BerryType.STARF: return (pokemon: Pokemon) => { const threshold = new Utils.NumberHolder(0.25); - applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold); + applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold); return pokemon.getHpRatio() < 0.25; }; case BerryType.LEPPA: return (pokemon: Pokemon) => { const threshold = new Utils.NumberHolder(0.25); - applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold); + applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold); return !!pokemon.getMoveset().find(m => !m?.getPpRatio()); }; } @@ -69,8 +70,8 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { if (pokemon.battleData) { pokemon.battleData.berriesEaten.push(berryType); } - const hpHealed = new Utils.NumberHolder(Math.floor(pokemon.getMaxHp() / 4)); - applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, hpHealed); + const hpHealed = new Utils.NumberHolder(Utils.toDmgValue(pokemon.getMaxHp() / 4)); + applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, hpHealed); pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), hpHealed.value, i18next.t("battle:hpHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: getBerryName(berryType) }), true)); }; @@ -96,7 +97,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { } const battleStat = (berryType - BerryType.LIECHI) as BattleStat; const statLevels = new Utils.NumberHolder(1); - applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, statLevels); + applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statLevels); pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ battleStat ], statLevels.value)); }; case BerryType.LANSAT: @@ -112,7 +113,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { pokemon.battleData.berriesEaten.push(berryType); } const statLevels = new Utils.NumberHolder(2); - applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, statLevels); + applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statLevels); pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ BattleStat.RAND ], statLevels.value)); }; case BerryType.LEPPA: diff --git a/src/data/egg-moves.ts b/src/data/egg-moves.ts index 657f59bddce..f88ac2c71b2 100644 --- a/src/data/egg-moves.ts +++ b/src/data/egg-moves.ts @@ -37,7 +37,7 @@ export const speciesEggMoves = { [Species.SLOWPOKE]: [ Moves.BOUNCY_BUBBLE, Moves.FLAMETHROWER, Moves.MYSTICAL_POWER, Moves.SHED_TAIL ], [Species.MAGNEMITE]: [ Moves.PARABOLIC_CHARGE, Moves.BODY_PRESS, Moves.ICE_BEAM, Moves.THUNDERCLAP ], [Species.FARFETCHD]: [ Moves.IVY_CUDGEL, Moves.TRIPLE_ARROWS, Moves.ROOST, Moves.VICTORY_DANCE ], - [Species.DODUO]: [ Moves.ICE_SPINNER, Moves.MULTI_ATTACK, Moves.FLOATY_FALL, Moves.TRIPLE_ARROWS ], + [Species.DODUO]: [ Moves.TRIPLE_AXEL, Moves.MULTI_ATTACK, Moves.FLOATY_FALL, Moves.TRIPLE_ARROWS ], [Species.SEEL]: [ Moves.FREEZE_DRY, Moves.BOUNCY_BUBBLE, Moves.SLACK_OFF, Moves.STEAM_ERUPTION ], [Species.GRIMER]: [ Moves.SUCKER_PUNCH, Moves.CURSE, Moves.STRENGTH_SAP, Moves.NOXIOUS_TORQUE ], [Species.SHELLDER]: [ Moves.ROCK_BLAST, Moves.WATER_SHURIKEN, Moves.BANEFUL_BUNKER, Moves.BONE_RUSH ], @@ -198,7 +198,7 @@ export const speciesEggMoves = { [Species.KYOGRE]: [ Moves.BOUNCY_BUBBLE, Moves.HURRICANE, Moves.FREEZE_DRY, Moves.ELECTRO_SHOT ], [Species.GROUDON]: [ Moves.STONE_AXE, Moves.SOLAR_BLADE, Moves.MORNING_SUN, Moves.SACRED_FIRE ], [Species.RAYQUAZA]: [ Moves.V_CREATE, Moves.DRAGON_DARTS, Moves.CORE_ENFORCER, Moves.OBLIVION_WING ], - [Species.JIRACHI]: [ Moves.TACHYON_CUTTER, Moves.FLOATY_FALL, Moves.TRIPLE_ARROWS, Moves.SHELL_SMASH ], + [Species.JIRACHI]: [ Moves.TACHYON_CUTTER, Moves.TRIPLE_ARROWS, Moves.ROCK_SLIDE, Moves.SHELL_SMASH ], [Species.DEOXYS]: [ Moves.COLLISION_COURSE, Moves.EARTH_POWER, Moves.PARTING_SHOT, Moves.LUMINA_CRASH ], [Species.TURTWIG]: [ Moves.SHELL_SMASH, Moves.MIGHTY_CLEAVE, Moves.ICE_SPINNER, Moves.SAPPY_SEED ], [Species.CHIMCHAR]: [ Moves.FIERY_DANCE, Moves.SECRET_SWORD, Moves.TRIPLE_AXEL, Moves.SACRED_FIRE ], @@ -418,7 +418,7 @@ export const speciesEggMoves = { [Species.CELESTEELA]: [ Moves.RECOVER, Moves.BUZZY_BUZZ, Moves.SANDSEAR_STORM, Moves.OBLIVION_WING ], [Species.KARTANA]: [ Moves.MIGHTY_CLEAVE, Moves.PSYBLADE, Moves.BITTER_BLADE, Moves.BEHEMOTH_BLADE ], [Species.GUZZLORD]: [ Moves.SUCKER_PUNCH, Moves.COMEUPPANCE, Moves.SLACK_OFF, Moves.SHED_TAIL ], - [Species.NECROZMA]: [ Moves.CLANGOROUS_SOUL, Moves.SACRED_FIRE, Moves.ASTRAL_BARRAGE, Moves.CLANGOROUS_SOUL ], + [Species.NECROZMA]: [ Moves.CLANGOROUS_SOUL, Moves.SACRED_FIRE, Moves.ASTRAL_BARRAGE, Moves.DYNAMAX_CANNON ], [Species.MAGEARNA]: [ Moves.STRENGTH_SAP, Moves.EARTH_POWER, Moves.MOONBLAST, Moves.MAKE_IT_RAIN ], [Species.MARSHADOW]: [ Moves.POWER_UP_PUNCH, Moves.TRIPLE_AXEL, Moves.METEOR_MASH, Moves.STORM_THROW ], [Species.POIPOLE]: [ Moves.CORE_ENFORCER, Moves.ICE_BEAM, Moves.SEARING_SHOT, Moves.MALIGNANT_CHAIN ], @@ -458,7 +458,7 @@ export const speciesEggMoves = { [Species.MORPEKO]: [ Moves.TRIPLE_AXEL, Moves.OBSTRUCT, Moves.SWORDS_DANCE, Moves.COLLISION_COURSE ], [Species.CUFANT]: [ Moves.LIQUIDATION, Moves.CURSE, Moves.COMBAT_TORQUE, Moves.GIGATON_HAMMER ], [Species.DRACOZOLT]: [ Moves.TRIPLE_AXEL, Moves.DRAGON_HAMMER, Moves.FIRE_LASH, Moves.DRAGON_DANCE ], - [Species.ARCTOZOLT]: [ Moves.TRIPLE_AXEL, Moves.AQUA_STEP, Moves.HIGH_HORSEPOWER, Moves.SHIFT_GEAR ], + [Species.ARCTOZOLT]: [ Moves.MOUNTAIN_GALE, Moves.AQUA_STEP, Moves.HIGH_HORSEPOWER, Moves.SHIFT_GEAR ], [Species.DRACOVISH]: [ Moves.TRIPLE_AXEL, Moves.DRAGON_HAMMER, Moves.THUNDER_FANG, Moves.DRAGON_DANCE ], [Species.ARCTOVISH]: [ Moves.ICE_FANG, Moves.THUNDER_FANG, Moves.HIGH_HORSEPOWER, Moves.SHIFT_GEAR ], [Species.DURALUDON]: [ Moves.CORE_ENFORCER, Moves.BODY_PRESS, Moves.RECOVER, Moves.TACHYON_CUTTER ], diff --git a/src/data/move.ts b/src/data/move.ts index ad1ade806f8..7165d7517bc 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1,5 +1,4 @@ import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims"; -import { BattleEndPhase, MoveEndPhase, MovePhase, NewBattlePhase, PartyStatusCurePhase, PokemonHealPhase, StatChangePhase, SwitchPhase, SwitchSummonPhase } from "../phases"; import { BattleStat, getBattleStatName } from "./battle-stat"; import { EncoreTag, GulpMissileTag, HelpingHandTag, SemiInvulnerableTag, ShellTrapTag, StockpilingTag, TrappedTag, TypeBoostTag } from "./battler-tags"; import { getPokemonNameWithAffix } from "../messages"; @@ -29,6 +28,15 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { MoveUsedEvent } from "#app/events/battle-scene.js"; import { ModifierTier } from "#app/modifier/modifier-tier.js"; +import { PartyStatusCurePhase } from "#app/phases/party-status-cure-phase.js"; +import { BattleEndPhase } from "#app/phases/battle-end-phase.js"; +import { MoveEndPhase } from "#app/phases/move-end-phase.js"; +import { MovePhase } from "#app/phases/move-phase.js"; +import { NewBattlePhase } from "#app/phases/new-battle-phase.js"; +import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase.js"; +import { StatChangePhase } from "#app/phases/stat-change-phase.js"; +import { SwitchPhase } from "#app/phases/switch-phase.js"; +import { SwitchSummonPhase } from "#app/phases/switch-summon-phase.js"; export enum MoveCategory { PHYSICAL, @@ -583,7 +591,7 @@ export default class Move implements Localizable { case MoveFlags.IGNORE_ABILITIES: if (user.hasAbilityWithAttr(MoveAbilityBypassAbAttr)) { const abilityEffectsIgnored = new Utils.BooleanHolder(false); - applyAbAttrs(MoveAbilityBypassAbAttr, user, abilityEffectsIgnored, this); + applyAbAttrs(MoveAbilityBypassAbAttr, user, abilityEffectsIgnored, false, this); if (abilityEffectsIgnored.value) { return true; } @@ -679,11 +687,11 @@ export default class Move implements Localizable { * @param target {@linkcode Pokemon} The Pokémon being targeted by the move. * @returns The calculated accuracy of the move. */ - calculateBattleAccuracy(user: Pokemon, target: Pokemon) { + calculateBattleAccuracy(user: Pokemon, target: Pokemon, simulated: boolean = false) { const moveAccuracy = new Utils.NumberHolder(this.accuracy); applyMoveAttrs(VariableAccuracyAttr, user, target, this, moveAccuracy); - applyPreDefendAbAttrs(WonderSkinAbAttr, target, user, this, { value: false }, moveAccuracy); + applyPreDefendAbAttrs(WonderSkinAbAttr, target, user, this, { value: false }, simulated, moveAccuracy); if (moveAccuracy.value === -1) { return moveAccuracy.value; @@ -717,7 +725,7 @@ export default class Move implements Localizable { * @param target {@linkcode Pokemon} The Pokémon being targeted by the move. * @returns The calculated power of the move. */ - calculateBattlePower(source: Pokemon, target: Pokemon): number { + calculateBattlePower(source: Pokemon, target: Pokemon, simulated: boolean = false): number { if (this.category === MoveCategory.STATUS) { return -1; } @@ -725,17 +733,17 @@ export default class Move implements Localizable { const power = new Utils.NumberHolder(this.power); const typeChangeMovePowerMultiplier = new Utils.NumberHolder(1); - applyPreAttackAbAttrs(MoveTypeChangeAttr, source, target, this, typeChangeMovePowerMultiplier); + applyPreAttackAbAttrs(MoveTypeChangeAttr, source, target, this, simulated, typeChangeMovePowerMultiplier); const sourceTeraType = source.getTeraType(); if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr(MultiHitAttr) && !source.scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) { power.value = 60; } - applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, target, this, power); + applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, target, this, simulated, power); if (source.getAlly()) { - applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, source.getAlly(), target, this, power); + applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, source.getAlly(), target, this, simulated, power); } const fieldAuras = new Set( @@ -745,11 +753,11 @@ export default class Move implements Localizable { ); for (const aura of fieldAuras) { // The only relevant values are `move` and the `power` holder - aura.applyPreAttack(null, null, null, this, [power]); + aura.applyPreAttack(null, null, simulated, null, this, [power]); } const alliedField: Pokemon[] = source instanceof PlayerPokemon ? source.scene.getPlayerField() : source.scene.getEnemyField(); - alliedField.forEach(p => applyPreAttackAbAttrs(UserFieldMoveTypePowerBoostAbAttr, p, target, this, power)); + alliedField.forEach(p => applyPreAttackAbAttrs(UserFieldMoveTypePowerBoostAbAttr, p, target, this, simulated, power)); power.value *= typeChangeMovePowerMultiplier.value; @@ -977,9 +985,9 @@ export class MoveEffectAttr extends MoveAttr { */ getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): integer { const moveChance = new Utils.NumberHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, moveChance, move, target, selfEffect, showAbility); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, moveChance, move, target, selfEffect, showAbility); if (!selfEffect) { - applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, target, user, null, null, moveChance); + applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, target, user, null, null, false, moveChance); } return moveChance.value; } @@ -1155,7 +1163,7 @@ export class TargetHalfHpDamageAttr extends FixedDamageAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.IntegerHolder).value = Math.max(Math.floor(target.hp / 2), 1); + (args[0] as Utils.IntegerHolder).value = Utils.toDmgValue(target.hp / 2); return true; } @@ -1201,7 +1209,7 @@ export class CounterDamageAttr extends FixedDamageAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const damage = user.turnData.attacksReceived.filter(ar => this.moveFilter(allMoves[ar.move])).reduce((total: integer, ar: AttackMoveResult) => total + ar.damage, 0); - (args[0] as Utils.IntegerHolder).value = Math.floor(Math.max(damage * this.multiplier, 1)); + (args[0] as Utils.IntegerHolder).value = Utils.toDmgValue(damage * this.multiplier); return true; } @@ -1227,7 +1235,7 @@ export class RandomLevelDamageAttr extends FixedDamageAttr { } getDamage(user: Pokemon, target: Pokemon, move: Move): number { - return Math.max(Math.floor(user.level * (user.randSeedIntRange(50, 150) * 0.01)), 1); + return Utils.toDmgValue(user.level * (user.randSeedIntRange(50, 150) * 0.01)); } } @@ -1286,8 +1294,9 @@ export class RecoilAttr extends MoveEffectAttr { return false; } - const recoilDamage = Math.max(Math.floor((!this.useHp ? user.turnData.damageDealt : user.getMaxHp()) * this.damageRatio), - user.turnData.damageDealt ? 1 : 0); + const damageValue = (!this.useHp ? user.turnData.damageDealt : user.getMaxHp()) * this.damageRatio; + const minValue = user.turnData.damageDealt ? 1 : 0; + const recoilDamage = Utils.toDmgValue(damageValue, minValue); if (!recoilDamage) { return false; } @@ -1408,7 +1417,7 @@ export class HalfSacrificialAttr extends MoveEffectAttr { // Check to see if the Pokemon has an ability that blocks non-direct damage applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled); if (!cancelled.value) { - user.damageAndUpdate(Math.ceil(user.getMaxHp()/2), HitResult.OTHER, false, true, true); + user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp()/2), HitResult.OTHER, false, true, true); user.scene.queueMessage(i18next.t("moveTriggers:cutHpPowerUpMove", {pokemonName: getPokemonNameWithAffix(user)})); // Queue recoil message } return true; @@ -1459,7 +1468,7 @@ export class HealAttr extends MoveEffectAttr { */ addHealPhase(target: Pokemon, healRatio: number) { target.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(), - Math.max(Math.floor(target.getMaxHp() * healRatio), 1), i18next.t("moveTriggers:healHp", {pokemonName: getPokemonNameWithAffix(target)}), true, !this.showAnim)); + Utils.toDmgValue(target.getMaxHp() * healRatio), i18next.t("moveTriggers:healHp", {pokemonName: getPokemonNameWithAffix(target)}), true, !this.showAnim)); } getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { @@ -1743,7 +1752,7 @@ export class HitHealAttr extends MoveEffectAttr { message = i18next.t("battle:drainMessage", {pokemonName: getPokemonNameWithAffix(target)}); } else { // Default healing formula used by draining moves like Absorb, Draining Kiss, Bitter Blade, etc. - healAmount = Math.max(Math.floor(user.turnData.currDamageDealt * this.healRatio), 1); + healAmount = Utils.toDmgValue(user.turnData.currDamageDealt * this.healRatio); message = i18next.t("battle:regainHealth", {pokemonName: getPokemonNameWithAffix(user)}); } if (reverseDrain) { @@ -1876,7 +1885,7 @@ export class MultiHitAttr extends MoveAttr { { const rand = user.randSeedInt(16); const hitValue = new Utils.IntegerHolder(rand); - applyAbAttrs(MaxMultiHitAbAttr, user, null, hitValue); + applyAbAttrs(MaxMultiHitAbAttr, user, null, false, hitValue); if (hitValue.value >= 10) { return 2; } else if (hitValue.value >= 4) { @@ -1947,7 +1956,7 @@ export class StatusEffectAttr extends MoveEffectAttr { } if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0)) && pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) { - applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, this.effect); + applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, false, this.effect); return true; } } @@ -2703,7 +2712,7 @@ export class CutHpStatBoostAttr extends StatChangeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { return new Promise(resolve => { - user.damageAndUpdate(Math.floor(user.getMaxHp() / this.cutRatio), HitResult.OTHER, false, true); + user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / this.cutRatio), HitResult.OTHER, false, true); user.updateInfo().then(() => { const ret = super.apply(user, target, move, args); if (this.messageCallback) { @@ -3183,7 +3192,7 @@ export class CompareWeightPowerAttr extends VariablePowerAttr { export class HpPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = Math.max(Math.floor(150 * user.getHpRatio()), 1); + (args[0] as Utils.NumberHolder).value = Utils.toDmgValue(150 * user.getHpRatio()); return true; } @@ -3211,7 +3220,7 @@ export class OpponentHighHpPowerAttr extends VariablePowerAttr { * @returns true */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = Math.max(Math.floor(this.maxBasePower * target.getHpRatio()), 1); + (args[0] as Utils.NumberHolder).value = Utils.toDmgValue(this.maxBasePower * target.getHpRatio()); return true; } @@ -3405,7 +3414,7 @@ export class PresentPowerAttr extends VariablePowerAttr { // If this move is multi-hit, disable all other hits user.stopMultiHit(); target.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(), - Math.max(Math.floor(target.getMaxHp() / 4), 1), i18next.t("moveTriggers:regainedHealth", {pokemonName: getPokemonNameWithAffix(target)}), true)); + Utils.toDmgValue(target.getMaxHp() / 4), i18next.t("moveTriggers:regainedHealth", {pokemonName: getPokemonNameWithAffix(target)}), true)); } return true; @@ -3777,6 +3786,30 @@ export class TeraBlastCategoryAttr extends VariableMoveCategoryAttr { } } +/** + * Increases the power of Tera Blast if the user is Terastallized into Stellar type + * @extends VariablePowerAttr + */ +export class TeraBlastPowerAttr extends VariablePowerAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + /** + * @param user {@linkcode Pokemon} Pokemon using the move + * @param target {@linkcode Pokemon} N/A + * @param move {@linkcode Move} {@linkcode Move.TERA_BLAST} + * @param {any[]} args N/A + * @returns true or false + */ + const power = args[0] as Utils.NumberHolder; + if (user.isTerastallized() && move.type === Type.STELLAR) { + //200 instead of 100 to reflect lack of stellar being 2x dmg on any type + power.value = 200; + return true; + } + + return false; + } +} + /** * Change the move category to status when used on the ally * @extends VariableMoveCategoryAttr @@ -4030,6 +4063,28 @@ export class HiddenPowerTypeAttr extends VariableMoveTypeAttr { } } +/** + * Changes the type of Tera Blast to match the user's tera type + * @extends VariableMoveTypeAttr + */ +export class TeraBlastTypeAttr extends VariableMoveTypeAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + /** + * @param user {@linkcode Pokemon} the user's type is checked + * @param target {@linkcode Pokemon} N/A + * @param move {@linkcode Move} {@linkcode Move.TeraBlastTypeAttr} + * @param {any[]} args N/A + * @returns true or false + */ + if (user.isTerastallized()) { + move.type = user.getTeraType(); //changes move type to tera type + return true; + } + + return false; + } +} + export class MatchUserTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const userTypes = user.getTypes(true); @@ -4179,9 +4234,9 @@ const crashDamageFunc = (user: Pokemon, move: Move) => { return false; } - user.damageAndUpdate(Math.floor(user.getMaxHp() / 2), HitResult.OTHER, false, true); + user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / 2), HitResult.OTHER, false, true); user.scene.queueMessage(i18next.t("moveTriggers:keptGoingAndCrashed", {pokemonName: getPokemonNameWithAffix(user)})); - user.turnData.damageTaken += Math.floor(user.getMaxHp() / 2); + user.turnData.damageTaken += Utils.toDmgValue(user.getMaxHp() / 2); return true; }; @@ -4431,6 +4486,39 @@ export class GulpMissileTagAttr extends MoveEffectAttr { } } +/** + * Attribute to implement Jaw Lock's linked trapping effect between the user and target + * @extends AddBattlerTagAttr + */ +export class JawLockAttr extends AddBattlerTagAttr { + constructor() { + super(BattlerTagType.TRAPPED); + } + + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!super.canApply(user, target, move, args)) { + return false; + } + + // If either the user or the target already has the tag, do not apply + if (user.getTag(TrappedTag) || target.getTag(TrappedTag)) { + return false; + } + + const moveChance = this.getMoveChance(user, target, move, this.selfTarget); + if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) { + /** + * Add the tag to both the user and the target. + * The target's tag source is considered to be the user and vice versa + */ + return target.addTag(BattlerTagType.TRAPPED, 1, move.id, user.id) + && user.addTag(BattlerTagType.TRAPPED, 1, move.id, target.id); + } + + return false; + } +} + export class CurseAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move:Move, args: any[]): boolean { @@ -4442,7 +4530,7 @@ export class CurseAttr extends MoveEffectAttr { const curseRecoilDamage = Math.max(1, Math.floor(user.getMaxHp() / 2)); user.damageAndUpdate(curseRecoilDamage, HitResult.OTHER, false, true, true); user.scene.queueMessage( - i18next.t("battle:cursedOnAdd", { + i18next.t("battlerTags:cursedOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user), pokemonName: getPokemonNameWithAffix(target) }) @@ -4818,7 +4906,7 @@ export class SwapArenaTagsAttr extends MoveEffectAttr { } - user.scene.queueMessage( i18next.t("moveTriggers:swapArenaTags", {pokemonName: getPokemonNameWithAffix(user)})); + user.scene.queueMessage(i18next.t("moveTriggers:swapArenaTags", {pokemonName: getPokemonNameWithAffix(user)})); return true; } } @@ -4858,8 +4946,8 @@ export class RevivalBlessingAttr extends MoveEffectAttr { const pokemon = faintedPokemon[user.randSeedInt(faintedPokemon.length)]; const slotIndex = user.scene.getEnemyParty().findIndex(p => pokemon.id === p.id); pokemon.resetStatus(); - pokemon.heal(Math.min(Math.max(Math.ceil(Math.floor(0.5 * pokemon.getMaxHp())), 1), pokemon.getMaxHp())); - user.scene.queueMessage(`${getPokemonNameWithAffix(pokemon)} was revived!`,0,true); + pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp())); + user.scene.queueMessage(i18next.t("moveTriggers:revivalBlessing", {pokemonName: getPokemonNameWithAffix(pokemon)}),0,true); if (user.scene.currentBattle.double && user.scene.getEnemyParty().length > 1) { const allyPokemon = user.getAlly(); @@ -7551,7 +7639,7 @@ export function initMoves() { .attr(OpponentHighHpPowerAttr, 120), new AttackMove(Moves.MAGMA_STORM, Type.FIRE, MoveCategory.SPECIAL, 100, 75, 5, -1, 0, 4) .attr(TrapAttr, BattlerTagType.MAGMA_STORM), - new StatusMove(Moves.DARK_VOID, Type.DARK, 50, 10, -1, 0, 4) + new StatusMove(Moves.DARK_VOID, Type.DARK, 80, 10, -1, 0, 4) //Accuracy from Generations 4-6 .attr(StatusEffectAttr, StatusEffect.SLEEP) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.SEED_FLARE, Type.GRASS, MoveCategory.SPECIAL, 120, 85, 5, 40, 0, 4) @@ -8390,8 +8478,7 @@ export function initMoves() { .attr(HighCritAttr) .attr(BypassRedirectAttr), new AttackMove(Moves.JAW_LOCK, Type.DARK, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8) - .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1, 1, false, true) - .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, true, false, 1, 1, false, true) + .attr(JawLockAttr) .bitingMove(), new SelfStatusMove(Moves.STUFF_CHEEKS, Type.NORMAL, -1, 10, -1, 0, 8) // TODO: Stuff Cheeks should not be selectable when the user does not have a berry, see wiki .attr(EatBerryAttr) @@ -8836,7 +8923,10 @@ export function initMoves() { End Unused */ new AttackMove(Moves.TERA_BLAST, Type.NORMAL, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9) .attr(TeraBlastCategoryAttr) - .unimplemented(), + .attr(TeraBlastTypeAttr) + .attr(TeraBlastPowerAttr) + .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1, true, (user, target, move) => user.isTerastallized() && user.isOfType(Type.STELLAR)) + .partial(), new SelfStatusMove(Moves.SILK_TRAP, Type.BUG, -1, 10, -1, 4, 9) .attr(ProtectAttr, BattlerTagType.SILK_TRAP), new AttackMove(Moves.AXE_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 90, 10, 30, 0, 9) diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index 5180664ce07..95a89c7c640 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -837,6 +837,8 @@ export const pokemonFormChanges: PokemonFormChanges = { new SpeciesFormChange(Species.CRAMORANT, "", "gorging", new SpeciesFormChangeManualTrigger, true, new SpeciesFormChangeCondition(p => p.getHpRatio() < .5)), new SpeciesFormChange(Species.CRAMORANT, "gulping", "", new SpeciesFormChangeManualTrigger, true), new SpeciesFormChange(Species.CRAMORANT, "gorging", "", new SpeciesFormChangeManualTrigger, true), + new SpeciesFormChange(Species.CRAMORANT, "gulping", "", new SpeciesFormChangeActiveTrigger(false), true), + new SpeciesFormChange(Species.CRAMORANT, "gorging", "", new SpeciesFormChangeActiveTrigger(false), true), ] }; diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 762f009e4e4..6eca5d08811 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -2581,7 +2581,7 @@ export function initSpecies() { ), new PokemonSpecies(Species.WALKING_WAKE, 9, false, false, false, "Paradox Pokémon", Type.WATER, Type.DRAGON, 3.5, 280, Abilities.PROTOSYNTHESIS, Abilities.NONE, Abilities.NONE, 590, 99, 83, 91, 125, 83, 109, 5, 0, 295, GrowthRate.SLOW, null, false), new PokemonSpecies(Species.IRON_LEAVES, 9, false, false, false, "Paradox Pokémon", Type.GRASS, Type.PSYCHIC, 1.5, 125, Abilities.QUARK_DRIVE, Abilities.NONE, Abilities.NONE, 590, 90, 130, 88, 70, 108, 104, 5, 0, 295, GrowthRate.SLOW, null, false), - new PokemonSpecies(Species.DIPPLIN, 9, false, false, false, "Candy Apple Pokémon", Type.GRASS, Type.DRAGON, 0.4, 9.7, Abilities.SUPERSWEET_SYRUP, Abilities.GLUTTONY, Abilities.STICKY_HOLD, 485, 80, 80, 110, 95, 80, 40, 45, 50, 170, GrowthRate.ERRATIC, null, false), + new PokemonSpecies(Species.DIPPLIN, 9, false, false, false, "Candy Apple Pokémon", Type.GRASS, Type.DRAGON, 0.4, 9.7, Abilities.SUPERSWEET_SYRUP, Abilities.GLUTTONY, Abilities.STICKY_HOLD, 485, 80, 80, 110, 95, 80, 40, 45, 50, 170, GrowthRate.ERRATIC, 50, false), new PokemonSpecies(Species.POLTCHAGEIST, 9, false, false, false, "Matcha Pokémon", Type.GRASS, Type.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, GrowthRate.SLOW, null, false, false, new PokemonForm("Counterfeit Form", "counterfeit", Type.GRASS, Type.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, true), new PokemonForm("Artisan Form", "artisan", Type.GRASS, Type.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, true), @@ -3317,6 +3317,28 @@ export function getStarterValueFriendshipCap(value: integer): integer { } } +/** +* Method to get the daily list of starters with Pokerus. +* @param scene {@linkcode BattleScene} used as part of RNG +* @returns A list of starters with Pokerus +*/ +export function getPokerusStarters(scene: BattleScene): PokemonSpecies[] { + const pokerusStarters: PokemonSpecies[] = []; + const date = new Date(); + const starterCount = 3; //for easy future adjustment! + date.setUTCHours(0, 0, 0, 0); + scene.executeWithSeedOffset(() => { + while (pokerusStarters.length < starterCount) { + const randomSpeciesId = parseInt(Utils.randSeedItem(Object.keys(speciesStarters)), 10); + const species = getPokemonSpecies(randomSpeciesId); + if (!pokerusStarters.includes(species)) { + pokerusStarters.push(species); + } + } + }, 0, date.getTime().toString()); + return pokerusStarters; +} + export const starterPassiveAbilities = { [Species.BULBASAUR]: Abilities.GRASSY_SURGE, [Species.CHARMANDER]: Abilities.BEAST_BOOST, @@ -3559,7 +3581,7 @@ export const starterPassiveAbilities = { [Species.HEATRAN]: Abilities.EARTH_EATER, [Species.REGIGIGAS]: Abilities.MINDS_EYE, [Species.GIRATINA]: Abilities.SHADOW_SHIELD, - [Species.CRESSELIA]: Abilities.MAGIC_BOUNCE, + [Species.CRESSELIA]: Abilities.UNAWARE, [Species.PHIONE]: Abilities.SIMPLE, [Species.MANAPHY]: Abilities.PRIMORDIAL_SEA, [Species.DARKRAI]: Abilities.UNNERVE, diff --git a/src/data/terrain.ts b/src/data/terrain.ts index e29344ffea2..d4789078af7 100644 --- a/src/data/terrain.ts +++ b/src/data/terrain.ts @@ -59,7 +59,7 @@ export class Terrain { case TerrainType.PSYCHIC: if (!move.hasAttr(ProtectAttr)) { const priority = new Utils.IntegerHolder(move.priority); - applyAbAttrs(ChangeMovePriorityAbAttr, user, null, move, priority); + applyAbAttrs(ChangeMovePriorityAbAttr, user, null, false, move, priority); // Cancels move if the move has positive priority and targets a Pokemon grounded on the Psychic Terrain return priority.value > 0 && user.getOpponents().some(o => targets.includes(o.getBattlerIndex()) && o.isGrounded()); } diff --git a/src/data/trainer-config.ts b/src/data/trainer-config.ts index 2d54a79441d..5f47ce42a62 100644 --- a/src/data/trainer-config.ts +++ b/src/data/trainer-config.ts @@ -1577,11 +1577,11 @@ export const trainerConfigs: TrainerConfigs = { })), [TrainerType.RIVAL]: new TrainerConfig((t = TrainerType.RIVAL)).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setStaticParty().setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival").setMixedBattleBgm("battle_rival").setPartyTemplates(trainerPartyTemplates.RIVAL) - .setModifierRewardFuncs(() => modifierTypes.SUPER_EXP_CHARM, () => modifierTypes.EXP_SHARE, () => modifierTypes.SHINY_CHARM, () => modifierTypes.ABILITY_CHARM) + .setModifierRewardFuncs(() => modifierTypes.SUPER_EXP_CHARM, () => modifierTypes.EXP_SHARE) .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, Species.CHIKORITA, Species.CYNDAQUIL, Species.TOTODILE, Species.TREECKO, Species.TORCHIC, Species.MUDKIP, Species.TURTWIG, Species.CHIMCHAR, Species.PIPLUP, Species.SNIVY, Species.TEPIG, Species.OSHAWOTT, Species.CHESPIN, Species.FENNEKIN, Species.FROAKIE, Species.ROWLET, Species.LITTEN, Species.POPPLIO, Species.GROOKEY, Species.SCORBUNNY, Species.SOBBLE, Species.SPRIGATITO, Species.FUECOCO, Species.QUAXLY], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.PIDGEY, Species.HOOTHOOT, Species.TAILLOW, Species.STARLY, Species.PIDOVE, Species.FLETCHLING, Species.PIKIPEK, Species.ROOKIDEE, Species.WATTREL], TrainerSlot.TRAINER, true)), [TrainerType.RIVAL_2]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setStaticParty().setMoneyMultiplier(1.25).setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival").setMixedBattleBgm("battle_rival").setPartyTemplates(trainerPartyTemplates.RIVAL_2) - .setModifierRewardFuncs(() => modifierTypes.EXP_SHARE, () => modifierTypes.SHINY_CHARM) + .setModifierRewardFuncs(() => modifierTypes.EXP_SHARE) .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.IVYSAUR, Species.CHARMELEON, Species.WARTORTLE, Species.BAYLEEF, Species.QUILAVA, Species.CROCONAW, Species.GROVYLE, Species.COMBUSKEN, Species.MARSHTOMP, Species.GROTLE, Species.MONFERNO, Species.PRINPLUP, Species.SERVINE, Species.PIGNITE, Species.DEWOTT, Species.QUILLADIN, Species.BRAIXEN, Species.FROGADIER, Species.DARTRIX, Species.TORRACAT, Species.BRIONNE, Species.THWACKEY, Species.RABOOT, Species.DRIZZILE, Species.FLORAGATO, Species.CROCALOR, Species.QUAXWELL], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.PIDGEOTTO, Species.HOOTHOOT, Species.TAILLOW, Species.STARAVIA, Species.TRANQUILL, Species.FLETCHINDER, Species.TRUMBEAK, Species.CORVISQUIRE, Species.WATTREL], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450)), diff --git a/src/enums/shop-cursor-target.ts b/src/enums/shop-cursor-target.ts new file mode 100644 index 00000000000..d2f72fed0d6 --- /dev/null +++ b/src/enums/shop-cursor-target.ts @@ -0,0 +1,13 @@ +/** + * Determines the cursor target when entering the shop phase. + */ +export enum ShopCursorTarget { + /** Cursor points to Reroll */ + REROLL, + /** Cursor points to Items */ + ITEMS, + /** Cursor points to Shop */ + SHOP, + /** Cursor points to Check Team */ + CHECK_TEAM +} diff --git a/src/field/arena.ts b/src/field/arena.ts index 923a0a4e286..2ef6ce7dab3 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -4,7 +4,6 @@ import { Constructor } from "#app/utils"; import * as Utils from "../utils"; import PokemonSpecies, { getPokemonSpecies } from "../data/pokemon-species"; import { Weather, WeatherType, getTerrainClearMessage, getTerrainStartMessage, getWeatherClearMessage, getWeatherStartMessage } from "../data/weather"; -import { CommonAnimPhase } from "../phases"; import { CommonAnim } from "../data/battle-anims"; import { Type } from "../data/type"; import Move from "../data/move"; @@ -21,6 +20,7 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { TimeOfDay } from "#enums/time-of-day"; import { TrainerType } from "#enums/trainer-type"; +import { CommonAnimPhase } from "#app/phases/common-anim-phase.js"; export class Arena { public scene: BattleScene; @@ -584,6 +584,10 @@ export class Arena { return this.getTagOnSide(tagType, ArenaTagSide.BOTH); } + hasTag(tagType: ArenaTagType) : boolean { + return !!this.getTag(tagType); + } + getTagOnSide(tagType: ArenaTagType | Constructor, side: ArenaTagSide): ArenaTag | undefined { return typeof(tagType) === "string" ? this.tags.find(t => t.tagType === tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side)) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 930ffeb700f..97ecb65cc02 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -17,7 +17,6 @@ import { initMoveAnim, loadMoveAnimAssets } from "../data/battle-anims"; import { Status, StatusEffect, getRandomStatus } from "../data/status-effect"; import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from "../data/pokemon-evolutions"; import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "../data/tms"; -import { DamagePhase, FaintPhase, LearnMovePhase, MoveEffectPhase, ObtainStatusEffectPhase, StatChangePhase, SwitchSummonPhase, ToggleDoublePositionPhase, MoveEndPhase } from "../phases"; import { BattleStat } from "../data/battle-stat"; import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, ExposedTag } from "../data/battler-tags"; import { WeatherType } from "../data/weather"; @@ -51,6 +50,15 @@ import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { getPokemonNameWithAffix } from "#app/messages.js"; +import { DamagePhase } from "#app/phases/damage-phase.js"; +import { FaintPhase } from "#app/phases/faint-phase.js"; +import { LearnMovePhase } from "#app/phases/learn-move-phase.js"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; +import { MoveEndPhase } from "#app/phases/move-end-phase.js"; +import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase.js"; +import { StatChangePhase } from "#app/phases/stat-change-phase.js"; +import { SwitchSummonPhase } from "#app/phases/switch-summon-phase.js"; +import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase.js"; export enum FieldPosition { CENTER, @@ -686,7 +694,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { break; } } - applyAbAttrs(IgnoreOpponentStatChangesAbAttr, opponent, null, statLevel); + applyAbAttrs(IgnoreOpponentStatChangesAbAttr, opponent, null, false, statLevel); if (move) { applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, opponent, move, statLevel); } @@ -921,8 +929,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * by how many learnable moves there are for the {@linkcode Pokemon}. */ getLearnableLevelMoves(): Moves[] { - let levelMoves = this.getLevelMoves(1, true).map(lm => lm[1]); - if (this.metBiome === -1 && !this.scene.gameMode.isFreshStartChallenge()) { + let levelMoves = this.getLevelMoves(1, true, false, true).map(lm => lm[1]); + if (this.metBiome === -1 && !this.scene.gameMode.isFreshStartChallenge() && !this.scene.gameMode.isDaily) { levelMoves = this.getUnlockedEggMoves().concat(levelMoves); } return levelMoves.filter(lm => !this.moveset.some(m => m?.moveId === lm)); @@ -970,12 +978,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // this.scene potentially can be undefined for a fainted pokemon in doubles // use optional chaining to avoid runtime errors - if (forDefend && (this.getTag(GroundedTag) || this.scene?.arena.getTag(ArenaTagType.GRAVITY))) { - const flyingIndex = types.indexOf(Type.FLYING); - if (flyingIndex > -1) { - types.splice(flyingIndex, 1); - } - } if (!types.length) { // become UNKNOWN if no types are present types.push(Type.UNKNOWN); @@ -1120,10 +1122,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const suppressed = new Utils.BooleanHolder(false); this.scene.getField(true).filter(p => p !== this).map(p => { if (p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) { - p.getAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, false, suppressed, [ability])); + p.getAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, false, false, suppressed, [ability])); } if (p.getPassiveAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility(true)) { - p.getPassiveAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, true, suppressed, [ability])); + p.getPassiveAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, true, false, suppressed, [ability])); } }); if (suppressed.value) { @@ -1175,7 +1177,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getWeight(): number { const weight = new Utils.NumberHolder(this.species.weight); // This will trigger the ability overlay so only call this function when necessary - applyAbAttrs(WeightMultiplierAbAttr, this, null, weight); + applyAbAttrs(WeightMultiplierAbAttr, this, null, false, weight); return weight.value; } @@ -1210,11 +1212,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * * @param source - The Pokémon using the move. * @param move - The move being used. - * @returns The type damage multiplier or undefined if it's a status move + * @returns The type damage multiplier or 1 if it's a status move */ - getMoveEffectiveness(source: Pokemon, move: PokemonMove): TypeDamageMultiplier | undefined { + getMoveEffectiveness(source: Pokemon, move: PokemonMove): TypeDamageMultiplier { if (move.getMove().category === MoveCategory.STATUS) { - return undefined; + return 1; } return this.getAttackMoveEffectiveness(source, move, !this.battleData?.abilityRevealed); @@ -1235,10 +1237,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const cancelled = new Utils.BooleanHolder(false); applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier); if (!typeless && !ignoreAbility) { - applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier, true); + applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, true, typeMultiplier); } if (!cancelled.value && !ignoreAbility) { - applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier, true); + applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, true, typeMultiplier); } return (!cancelled.value ? Number(typeMultiplier.value) : 0) as TypeDamageMultiplier; @@ -1264,12 +1266,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.isTerastallized() ? 2 : 1; } const types = this.getTypes(true, true); + const arena = this.scene.arena; + + // Handle flying v ground type immunity without removing flying type so effective types are still effective + // Related to https://github.com/pagefaultgames/pokerogue/issues/524 + if (moveType === Type.GROUND && (this.isGrounded() || arena.hasTag(ArenaTagType.GRAVITY))) { + const flyingIndex = types.indexOf(Type.FLYING); + if (flyingIndex > -1) { + types.splice(flyingIndex, 1); + } + } let multiplier = types.map(defType => { if (source) { const ignoreImmunity = new Utils.BooleanHolder(false); if (source.isActive(true) && source.hasAbilityWithAttr(IgnoreTypeImmunityAbAttr)) { - applyAbAttrs(IgnoreTypeImmunityAbAttr, source, ignoreImmunity, moveType, defType); + applyAbAttrs(IgnoreTypeImmunityAbAttr, source, ignoreImmunity, false, moveType, defType); } if (ignoreImmunity.value) { return 1; @@ -1285,7 +1297,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { }).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier; // Handle strong winds lowering effectiveness of types super effective against pure flying - if (!ignoreStrongWinds && this.scene.arena.weather?.weatherType === WeatherType.STRONG_WINDS && !this.scene.arena.weather.isEffectSuppressed(this.scene) && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) { + if (!ignoreStrongWinds && arena.weather?.weatherType === WeatherType.STRONG_WINDS && !arena.weather.isEffectSuppressed(this.scene) && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) { multiplier /= 2; if (!simulated) { this.scene.queueMessage(i18next.t("weather:strongWindsEffectMessage")); @@ -1910,9 +1922,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const userAccuracyLevel = new Utils.IntegerHolder(this.summonData.battleStats[BattleStat.ACC]); const targetEvasionLevel = new Utils.IntegerHolder(target.summonData.battleStats[BattleStat.EVA]); - applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, userAccuracyLevel); - applyAbAttrs(IgnoreOpponentStatChangesAbAttr, this, null, targetEvasionLevel); - applyAbAttrs(IgnoreOpponentEvasionAbAttr, this, null, targetEvasionLevel); + applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, false, userAccuracyLevel); + applyAbAttrs(IgnoreOpponentStatChangesAbAttr, this, null, false, targetEvasionLevel); + applyAbAttrs(IgnoreOpponentEvasionAbAttr, this, null, false, targetEvasionLevel); applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, target, sourceMove, targetEvasionLevel); this.scene.applyModifiers(TempBattleStatBoosterModifier, this.isPlayer(), TempBattleStat.ACC, userAccuracyLevel); @@ -1927,7 +1939,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { : 3 / (3 + Math.min(targetEvasionLevel.value - userAccuracyLevel.value, 6)); } - applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, BattleStat.ACC, accuracyMultiplier, sourceMove); + applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, BattleStat.ACC, accuracyMultiplier, false, sourceMove); const evasionMultiplier = new Utils.NumberHolder(1); applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, target, BattleStat.EVA, evasionMultiplier); @@ -1937,6 +1949,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return accuracyMultiplier.value; } + /** + * Apply the results of a move to this pokemon + * @param {Pokemon} source The pokemon using the move + * @param {PokemonMove} battlerMove The move being used + * @returns {HitResult} The result of the attack + */ apply(source: Pokemon, move: Move): HitResult { let result: HitResult; const damage = new Utils.NumberHolder(0); @@ -1972,12 +1990,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const sourceTeraType = source.getTeraType(); if (!typeless) { - applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier); + applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier); applyMoveAttrs(NeutralDamageAgainstFlyingTypeMultiplierAttr, source, this, move, typeMultiplier); } if (!cancelled.value) { - applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier); - defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, typeMultiplier)); + applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier); + defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, false, typeMultiplier)); } if (cancelled.value) { @@ -2000,7 +2018,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const critOnly = new Utils.BooleanHolder(false); const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT); applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly); - applyAbAttrs(ConditionalCritAbAttr, source, null, critOnly, this, move); + applyAbAttrs(ConditionalCritAbAttr, source, null, false, critOnly, this, move); if (critOnly.value || critAlways) { isCritical = true; } else { @@ -2010,7 +2028,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel); const bonusCrit = new Utils.BooleanHolder(false); //@ts-ignore - if (applyAbAttrs(BonusCritAbAttr, source, null, bonusCrit)) { // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus. + if (applyAbAttrs(BonusCritAbAttr, source, null, false, bonusCrit)) { // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus. if (bonusCrit.value) { critLevel.value += 1; } @@ -2028,7 +2046,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (isCritical) { const noCritTag = this.scene.arena.getTagOnSide(NoCritTag, defendingSide); const blockCrit = new Utils.BooleanHolder(false); - applyAbAttrs(BlockCritAbAttr, this, null, blockCrit); + applyAbAttrs(BlockCritAbAttr, this, null, false, blockCrit); if (noCritTag || blockCrit.value) { isCritical = false; } @@ -2036,7 +2054,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, isCritical)); const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical)); const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1); - applyAbAttrs(MultCritAbAttr, source, null, criticalMultiplier); + applyAbAttrs(MultCritAbAttr, source, null, false, criticalMultiplier); const screenMultiplier = new Utils.NumberHolder(1); if (!isCritical) { this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, move.category, this.scene.currentBattle.double, screenMultiplier); @@ -2051,7 +2069,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { stabMultiplier.value += 0.5; } - applyAbAttrs(StabBoostAbAttr, source, null, stabMultiplier); + applyAbAttrs(StabBoostAbAttr, source, null, false, stabMultiplier); if (sourceTeraType !== Type.UNKNOWN && matchesSourceType) { stabMultiplier.value = Math.min(stabMultiplier.value + 0.5, 2.25); @@ -2069,12 +2087,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { numTargets = effectPhase.getTargets().length; } const twoStrikeMultiplier = new Utils.NumberHolder(1); - applyPreAttackAbAttrs(AddSecondStrikeAbAttr, source, this, move, numTargets, new Utils.IntegerHolder(0), twoStrikeMultiplier); + applyPreAttackAbAttrs(AddSecondStrikeAbAttr, source, this, move, false, numTargets, new Utils.IntegerHolder(0), twoStrikeMultiplier); if (!isTypeImmune) { const levelMultiplier = (2 * source.level / 5 + 2); const randomMultiplier = ((this.scene.randBattleSeedInt(16) + 85) / 100); - damage.value = Math.ceil((((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2) + damage.value = Utils.toDmgValue((((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier.value @@ -2088,14 +2106,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) { if (!move.hasAttr(BypassBurnDamageReductionAttr)) { const burnDamageReductionCancelled = new Utils.BooleanHolder(false); - applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled); + applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled, false); if (!burnDamageReductionCancelled.value) { - damage.value = Math.floor(damage.value / 2); + damage.value = Utils.toDmgValue(damage.value / 2); } } } - applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, damage); + applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, false, damage); /** * For each {@link HitsTagAttr} the move has, doubles the damage of the move if: @@ -2111,7 +2129,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (this.scene.arena.terrain?.terrainType === TerrainType.MISTY && this.isGrounded() && move.type === Type.DRAGON) { - damage.value = Math.floor(damage.value / 2); + damage.value = Utils.toDmgValue(damage.value / 2); } const fixedDamage = new Utils.IntegerHolder(0); @@ -2153,7 +2171,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage); } - applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, damage); + applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, false, damage); } // This attribute may modify damage arbitrarily, so be careful about changing its order of application. @@ -2166,7 +2184,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (damage.value) { if (this.isFullHp()) { - applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, damage); + applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, false, damage); } else if (!this.isPlayer() && damage.value >= this.hp) { this.scene.applyModifiers(EnemyEndureChanceModifier, false, this); } @@ -2210,7 +2228,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.queueMessage(i18next.t("battle:hitResultNoEffect", { pokemonName: getPokemonNameWithAffix(this) })); break; case HitResult.IMMUNE: - this.scene.queueMessage(`${this.name} is unaffected!`); + this.scene.queueMessage(i18next.t("battle:hitResultImmune", { pokemonName: this.name })); break; case HitResult.ONE_HIT_KO: this.scene.queueMessage(i18next.t("battle:hitResultOneHitKO")); @@ -2233,11 +2251,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { break; case MoveCategory.STATUS: if (!typeless) { - applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier); + applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier); } if (!cancelled.value) { - applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier); - defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, typeMultiplier)); + applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier); + defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, false, typeMultiplier)); } if (!typeMultiplier.value) { this.scene.queueMessage(i18next.t("battle:hitResultNoEffect", { pokemonName: getPokemonNameWithAffix(this) })); @@ -2329,6 +2347,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return maxForms.includes(this.getFormKey()) || (!!this.getFusionFormKey() && maxForms.includes(this.getFusionFormKey()!)); } + canAddTag(tagType: BattlerTagType): boolean { + if (this.getTag(tagType)) { + return false; + } + + const stubTag = new BattlerTag(tagType, 0, 0); + + const cancelled = new Utils.BooleanHolder(false); + applyPreApplyBattlerTagAbAttrs(BattlerTagImmunityAbAttr, this, stubTag, cancelled, true); + + const userField = this.getAlliedField(); + userField.forEach(pokemon => applyPreApplyBattlerTagAbAttrs(UserFieldBattlerTagImmunityAbAttr, pokemon, stubTag, cancelled, true)); + + return !cancelled.value; + } + addTag(tagType: BattlerTagType, turnCount: integer = 0, sourceMove?: Moves, sourceId?: integer): boolean { const existingTag = this.getTag(tagType); if (existingTag) { @@ -2715,7 +2749,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Check if the source Pokemon has an ability that cancels the Poison/Toxic immunity const cancelImmunity = new Utils.BooleanHolder(false); if (sourcePokemon) { - applyAbAttrs(IgnoreTypeStatusEffectImmunityAbAttr, sourcePokemon, cancelImmunity, effect, defType); + applyAbAttrs(IgnoreTypeStatusEffectImmunityAbAttr, sourcePokemon, cancelImmunity, false, effect, defType); if (cancelImmunity.value) { return false; } @@ -2787,7 +2821,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (effect === StatusEffect.SLEEP) { statusCureTurn = new Utils.IntegerHolder(this.randSeedIntRange(2, 4)); - applyAbAttrs(ReduceStatusEffectDurationAbAttr, this, null, effect, statusCureTurn); + applyAbAttrs(ReduceStatusEffectDurationAbAttr, this, null, false, effect, statusCureTurn); this.setFrameRate(4); @@ -3223,14 +3257,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * Causes a Pokemon to leave the field (such as in preparation for a switch out/escape). * @param clearEffects Indicates if effects should be cleared (true) or passed * to the next pokemon, such as during a baton pass (false) + * @param hideInfo Indicates if this should also play the animation to hide the Pokemon's + * info container. */ - leaveField(clearEffects: boolean = true) { + leaveField(clearEffects: boolean = true, hideInfo: boolean = true) { this.resetTurnData(); if (clearEffects) { this.resetSummonData(); this.resetBattleData(); } - this.hideInfo(); + if (hideInfo) { + this.hideInfo(); + } this.setVisible(false); this.scene.field.remove(this); this.scene.triggerPokemonFormChange(this, SpeciesFormChangeActiveTrigger, true); @@ -3417,8 +3455,8 @@ export class PlayerPokemon extends Pokemon { pokemon.resetTurnData(); pokemon.resetStatus(); - pokemon.heal(Math.min(Math.max(Math.ceil(Math.floor(0.5 * pokemon.getMaxHp())), 1), pokemon.getMaxHp())); - this.scene.queueMessage(`${pokemon.name} was revived!`,0,true); + pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp())); + this.scene.queueMessage(i18next.t("moveTriggers:revivalBlessing", {pokemonName: pokemon.name}),0,true); if (this.scene.currentBattle.double && this.scene.getParty().length > 1) { const allyPokemon = this.getAlly(); @@ -3780,7 +3818,7 @@ export class EnemyPokemon extends Pokemon { this.moveset = (formIndex !== undefined ? formIndex : this.formIndex) ? [ new PokemonMove(Moves.DYNAMAX_CANNON), - new PokemonMove(Moves.CROSS_POISON), + new PokemonMove(Moves.SLUDGE_BOMB), new PokemonMove(Moves.FLAMETHROWER), new PokemonMove(Moves.RECOVER, 0, -4) ] @@ -4344,7 +4382,7 @@ export class PokemonMove { } getMovePp(): integer { - return this.getMove().pp + this.ppUp * Math.max(Math.floor(this.getMove().pp / 5), 1); + return this.getMove().pp + this.ppUp * Utils.toDmgValue(this.getMove().pp / 5); } getPpRatio(): number { diff --git a/src/game-mode.ts b/src/game-mode.ts index e78b9017c12..f5dadad6f1b 100644 --- a/src/game-mode.ts +++ b/src/game-mode.ts @@ -62,7 +62,7 @@ export class GameMode implements GameModeConfig { * @returns true if the game mode has that challenge */ hasChallenge(challenge: Challenges): boolean { - return this.challenges.some(c => c.id === challenge); + return this.challenges.some(c => c.id === challenge && c.value !== 0); } /** diff --git a/src/locales/ca_ES/ability-trigger.ts b/src/locales/ca_ES/ability-trigger.ts index 2bdd17baa56..0b7fe8bd0bc 100644 --- a/src/locales/ca_ES/ability-trigger.ts +++ b/src/locales/ca_ES/ability-trigger.ts @@ -47,6 +47,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postFaintHpDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postSummonPressure": "{{pokemonNameWithAffix}} is exerting its Pressure!", + "weatherEffectDisappeared": "The effects of the weather disappeared.", "postSummonMoldBreaker": "{{pokemonNameWithAffix}} breaks the mold!", "postSummonAnticipation": "{{pokemonNameWithAffix}} shuddered!", "postSummonTurboblaze": "{{pokemonNameWithAffix}} is radiating a blazing aura!", diff --git a/src/locales/ca_ES/battle-scene.ts b/src/locales/ca_ES/battle-scene.ts new file mode 100644 index 00000000000..573e1791724 --- /dev/null +++ b/src/locales/ca_ES/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "₽{{formattedMoney}}" +} as const; diff --git a/src/locales/ca_ES/battle.ts b/src/locales/ca_ES/battle.ts index 522fbb3261b..f86b597874f 100644 --- a/src/locales/ca_ES/battle.ts +++ b/src/locales/ca_ES/battle.ts @@ -24,6 +24,7 @@ export const battle: SimpleTranslationEntries = { "hitResultSuperEffective": "It's super effective!", "hitResultNotVeryEffective": "It's not very effective…", "hitResultNoEffect": "It doesn't affect {{pokemonName}}!", + "hitResultImmune": "{{pokemonName}} is unaffected!", "hitResultOneHitKO": "It's a one-hit KO!", "attackFailed": "But it failed!", "attackMissed": "{{pokemonNameWithAffix}} avoided the attack!", diff --git a/src/locales/ca_ES/config.ts b/src/locales/ca_ES/config.ts index 831ab5d99f5..36aee87fc75 100644 --- a/src/locales/ca_ES/config.ts +++ b/src/locales/ca_ES/config.ts @@ -6,6 +6,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const caESConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/ca_ES/dialogue.ts b/src/locales/ca_ES/dialogue.ts index 6db337517da..e783ea14006 100644 --- a/src/locales/ca_ES/dialogue.ts +++ b/src/locales/ca_ES/dialogue.ts @@ -2573,8 +2573,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}Wow… You cleaned me out.\nAre you actually a beginner? $@c{smile}Maybe it was a bit of luck but…\nWho knows you might just be able to go all the way. $By the way, the professor asked me to give you these items. They look pretty cool. - $@c{serious_smile_fists}Good luck out there! - $@c{smile}Oh- and I hope you enjoy the event!` + $@c{serious_smile_fists}Good luck out there!` }, }, "rival_female": { @@ -2588,8 +2587,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}You just started and you're already this strong?!@d{96}\n@c{angry}You totally cheated, didn't you? $@c{smile_wave_wink}Just kidding!@d{64} @c{smile_eclosed}I lost fair and square… I have a feeling you're going to do really well out there. $@c{smile}By the way, the professor wanted me to give you some items. Hopefully they're helpful! - $@c{smile_wave}Do your best like always! I believe in you! - $@c{smile}Oh- and I hope you enjoy the event!` + $@c{smile_wave}Do your best like always! I believe in you!` }, }, "rival_2": { @@ -2605,7 +2603,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}That's alright, though. I figured this might happen.\n@c{serious_mopen_fists}It just means I need to try harder for next time!\n $@c{smile}Oh, not that you really need the help, but I had an extra one of these lying around and figured you might want it.\n $@c{serious_smile_fists}Don't expect another one after this, though!\nI can't keep giving my opponent an advantage after all. - $@c{smile}Anyway, take care, and enjoy the event!` + $@c{smile}Anyway, take care!` }, }, "rival_2_female": { @@ -2621,7 +2619,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}Aw well. That just means I'll have to train even harder for next time! $@c{smile_wave}I also got you another one of these!\n@c{smile_wave_wink}No need to thank me~. $@c{angry_mopen}This is the last one, though! You won't be getting anymore freebies from me after this! - $@c{smile_wave}Keep at it, and enjoy the event!` + $@c{smile_wave}Keep at it!` }, "defeat": { 1: "It's OK to lose sometimes…" diff --git a/src/locales/ca_ES/move-trigger.ts b/src/locales/ca_ES/move-trigger.ts index 5f9c6c0cdc2..96a9874a4fa 100644 --- a/src/locales/ca_ES/move-trigger.ts +++ b/src/locales/ca_ES/move-trigger.ts @@ -61,6 +61,7 @@ export const moveTriggers: SimpleTranslationEntries = { "faintCountdown": "{{pokemonName}}\nwill faint in {{turnCount}} turns.", "copyType": "{{pokemonName}}'s type became the same as\n{{targetPokemonName}}'s type!", "suppressAbilities": "{{pokemonName}}'s ability\nwas suppressed!", + "revivalBlessing": "{{pokemonName}} was revived!", "swapArenaTags": "{{pokemonName}} swapped the battle effects affecting each side of the field!", "exposedMove": "{{pokemonName}} identified\n{{targetPokemonName}}!", } as const; diff --git a/src/locales/ca_ES/settings.ts b/src/locales/ca_ES/settings.ts index 491bfa4a481..9c0b3f36365 100644 --- a/src/locales/ca_ES/settings.ts +++ b/src/locales/ca_ES/settings.ts @@ -96,5 +96,10 @@ export const settings: SimpleTranslationEntries = { "controller": "Controller", "gamepadSupport": "Gamepad Support", "showBgmBar": "Show Music Names", - "shopOverlayOpacity": "Shop Overlay Opacity" + "shopOverlayOpacity": "Shop Overlay Opacity", + "shopCursorTarget": "Shop Cursor Target", + "items": "Items", + "reroll": "Reroll", + "shop": "Shop", + "checkTeam": "Check Team" } as const; diff --git a/src/locales/de/ability-trigger.ts b/src/locales/de/ability-trigger.ts index 72023a842b3..9e4ef9df6ed 100644 --- a/src/locales/de/ability-trigger.ts +++ b/src/locales/de/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{abilityName}} von {{pokemonNameWithAffix}} schadet seinem Angreifer!", "postFaintHpDamage": "{{abilityName}} von {{pokemonNameWithAffix}} schadet seinem Angreifer!", "postSummonPressure": "{{pokemonNameWithAffix}} setzt Gegner mit Erzwinger unter Druck!", + "weatherEffectDisappeared": "Jegliche wetterbedingten Effekte wurden aufgehoben!", "postSummonMoldBreaker": "{{pokemonNameWithAffix}} gelingt es, gegnerische Fähigkeiten zu überbrücken!", "postSummonAnticipation": "{{pokemonNameWithAffix}} erschaudert!", "postSummonTurboblaze": "{{pokemonNameWithAffix}} strahlt eine lodernde Aura aus!", diff --git a/src/locales/de/battle-scene.ts b/src/locales/de/battle-scene.ts new file mode 100644 index 00000000000..bfa96445f6c --- /dev/null +++ b/src/locales/de/battle-scene.ts @@ -0,0 +1,6 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "{{formattedMoney}} ₽" + +} as const; diff --git a/src/locales/de/battle.ts b/src/locales/de/battle.ts index c5649a18c9a..3f4dced0c2b 100644 --- a/src/locales/de/battle.ts +++ b/src/locales/de/battle.ts @@ -24,6 +24,7 @@ export const battle: SimpleTranslationEntries = { "hitResultSuperEffective": "Das ist sehr effektiv!", "hitResultNotVeryEffective": "Das ist nicht sehr effektiv…", "hitResultNoEffect": "Es hat keine Wirkung auf {{pokemonName}}…", + "hitResultImmune": "{{pokemonName}} ist unversehrt!", "hitResultOneHitKO": "Ein K.O.-Treffer!", "attackFailed": "Es ist fehlgeschlagen!", "attackMissed": "Die Attacke hat {{pokemonNameWithAffix}} verfehlt!", diff --git a/src/locales/de/battler-tags.ts b/src/locales/de/battler-tags.ts index 27d5f14c597..da0150836b0 100644 --- a/src/locales/de/battler-tags.ts +++ b/src/locales/de/battler-tags.ts @@ -69,5 +69,5 @@ export const battlerTags: SimpleTranslationEntries = { "saltCuredLapse": "{{pokemonNameWithAffix}} wurde durch {{moveName}} verletzt!", "cursedOnAdd": "{{pokemonNameWithAffix}} nimmt einen Teil seiner KP und legt einen Fluch auf {{pokemonName}}!", "cursedLapse": "{{pokemonNameWithAffix}} wurde durch den Fluch verletzt!", - "stockpilingOnAdd": "{{pokemonNameWithAffix}} stockpiled {{stockpiledCount}}!", + "stockpilingOnAdd": "{{pokemonNameWithAffix}} hortet {{stockpiledCount}}!", } as const; diff --git a/src/locales/de/config.ts b/src/locales/de/config.ts index d0779c9eec4..080c9ecc598 100644 --- a/src/locales/de/config.ts +++ b/src/locales/de/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const deConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/de/dialogue.ts b/src/locales/de/dialogue.ts index 4c964db6dc3..a84060143fc 100644 --- a/src/locales/de/dialogue.ts +++ b/src/locales/de/dialogue.ts @@ -2641,8 +2641,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}Vielleicht war es einfach etwas Glück, aber…\nWer weiß, vielleicht schaffst du es irgendwann $ja wirklich ganz groß raus zu kommen. $Übrigens, der Professor hat mich gebeten dir diese Items zu geben. Die sehen wirklich cool aus. - $@c{serious_smile_fists}Viel Glück da draußen! - $@c{smile}Oh-und genieße das Event!` + $@c{serious_smile_fists}Viel Glück da draußen!` }, }, "rival_female": { @@ -2657,8 +2656,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}Du hast gerade erst angefangen und bist schon so stark?!@d{96} @c{angry}Du hast sowas von betrogen, oder? $@c{smile_wave_wink}Ich mach nur Spaß!@d{64} @c{smile_eclosed}Ich habe ehrlich verloren… Ich habe das Gefühl, dass du es dort draußen weit bringen wirst. $@c{smile}Übrigens, der Professor hat mich gebeten dir diese Items zu geben. Ich hoffe sie sind hilfreich! - $@c{smile_wave}Gib wie immer dein Bestes! Ich glaube an dich! - $@c{smile}Oh-und genieße das Event!` + $@c{smile_wave}Gib wie immer dein Bestes! Ich glaube an dich!` }, }, "rival_2": { @@ -2676,7 +2674,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}Nicht, dass du wirklich Hilfe benötigen würdest, aber ich habe hier noch eins von diesen Dingern herumliegen. $Du kannst es haben.\n $@c{serious_smile_fists}Erwarte aber nicht, dass ich dir noch mehr gebe!\nIch kann meinen Rivalen doch keine Vorteile verschaffen. - $@c{smile}Egal, pass auf dich auf und genieße das Event!` + $@c{smile}Egal, pass auf dich auf!` }, }, "rival_2_female": { @@ -2692,7 +2690,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}Na gut. Das bedeutet ich muss noch härter tranieren! $@c{smile_wave}Ich habe noch eins von diesen Dingern!\n@c{smile_wave_wink}Kein Grund mir zu danken~. $@c{angry_mopen}Das ist aber das Letzte! Du bekommst ab jett keine Geschenke mehr von mir! - $@c{smile_wave}Bleib stark und genieße das Event!` + $@c{smile_wave}Bleib stark!` }, "defeat": { 1: "Es ist Ok manchmal zu verlieren…" diff --git a/src/locales/de/move-trigger.ts b/src/locales/de/move-trigger.ts index efdfd7b2b2d..1c0892b659b 100644 --- a/src/locales/de/move-trigger.ts +++ b/src/locales/de/move-trigger.ts @@ -61,6 +61,7 @@ export const moveTriggers: SimpleTranslationEntries = { "faintCountdown": "{{pokemonName}} geht nach {{turnCount}} Runden K.O.!", "copyType": "{{pokemonName}} hat den Typ von {{targetPokemonName}} angenommen!", "suppressAbilities": "Die Fähigkeit von {{pokemonName}} wirkt nicht mehr!", + "revivalBlessing": "{{pokemonName}} ist wieder fit und kampfbereit!", "swapArenaTags": "{{pokemonName}} hat die Effekte, die auf den beiden Seiten des Kampffeldes wirken, miteinander getauscht!", "trickOnSwap": "{{pokemonNameWithAffix}} tauscht Items mit dem Ziel!", "trickFoeNewItem": "{{pokemonNameWithAffix}} erhält {{itemName}}.", diff --git a/src/locales/de/settings.ts b/src/locales/de/settings.ts index 7d4523c8cbd..3942e470e3f 100644 --- a/src/locales/de/settings.ts +++ b/src/locales/de/settings.ts @@ -99,4 +99,9 @@ export const settings: SimpleTranslationEntries = { "showBgmBar": "Musiknamen anzeigen", "moveTouchControls": "Bewegung Touch Steuerung", "shopOverlayOpacity": "Shop Overlay Deckkraft", + "shopCursorTarget": "Shop Cursor Target", + "items": "Items", + "reroll": "Reroll", + "shop": "Shop", + "checkTeam": "Check Team" } as const; diff --git a/src/locales/de/starter-select-ui-handler.ts b/src/locales/de/starter-select-ui-handler.ts index 284152bbd33..c96af29a3c0 100644 --- a/src/locales/de/starter-select-ui-handler.ts +++ b/src/locales/de/starter-select-ui-handler.ts @@ -7,7 +7,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; */ export const starterSelectUiHandler: SimpleTranslationEntries = { "confirmStartTeam": "Mit diesen Pokémon losziehen?", - "confirmExit": "Do you want to exit?", + "confirmExit": "Willst du zurück?", "invalidParty": "Das ist kein gültiges Team!", "gen1": "I", "gen2": "II", @@ -28,8 +28,8 @@ export const starterSelectUiHandler: SimpleTranslationEntries = { "toggleIVs": "DVs anzeigen/verbergen", "manageMoves": "Attacken ändern", "manageNature": "Wesen ändern", - "addToFavorites": "Add to Favorites", - "removeFromFavorites": "Remove from Favorites", + "addToFavorites": "Zu Favoriten hinzufügen", + "removeFromFavorites": "Von Favoriten entfernen", "useCandies": "Bonbons verwenden", "selectNature": "Wähle das neue Wesen.", "selectMoveSwapOut": "Wähle die zu ersetzende Attacke.", diff --git a/src/locales/en/ability-trigger.ts b/src/locales/en/ability-trigger.ts index 035fe8371be..2a0e0df255a 100644 --- a/src/locales/en/ability-trigger.ts +++ b/src/locales/en/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postFaintHpDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postSummonPressure": "{{pokemonNameWithAffix}} is exerting its Pressure!", + "weatherEffectDisappeared": "The effects of the weather disappeared.", "postSummonMoldBreaker": "{{pokemonNameWithAffix}} breaks the mold!", "postSummonAnticipation": "{{pokemonNameWithAffix}} shuddered!", "postSummonTurboblaze": "{{pokemonNameWithAffix}} is radiating a blazing aura!", diff --git a/src/locales/en/battle-scene.ts b/src/locales/en/battle-scene.ts new file mode 100644 index 00000000000..573e1791724 --- /dev/null +++ b/src/locales/en/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "₽{{formattedMoney}}" +} as const; diff --git a/src/locales/en/battle.ts b/src/locales/en/battle.ts index 522fbb3261b..f86b597874f 100644 --- a/src/locales/en/battle.ts +++ b/src/locales/en/battle.ts @@ -24,6 +24,7 @@ export const battle: SimpleTranslationEntries = { "hitResultSuperEffective": "It's super effective!", "hitResultNotVeryEffective": "It's not very effective…", "hitResultNoEffect": "It doesn't affect {{pokemonName}}!", + "hitResultImmune": "{{pokemonName}} is unaffected!", "hitResultOneHitKO": "It's a one-hit KO!", "attackFailed": "But it failed!", "attackMissed": "{{pokemonNameWithAffix}} avoided the attack!", diff --git a/src/locales/en/config.ts b/src/locales/en/config.ts index a98dd750fbe..d456b0540cc 100644 --- a/src/locales/en/config.ts +++ b/src/locales/en/config.ts @@ -6,6 +6,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const enConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/en/dialogue.ts b/src/locales/en/dialogue.ts index 1180e202f3c..715f245e518 100644 --- a/src/locales/en/dialogue.ts +++ b/src/locales/en/dialogue.ts @@ -2574,8 +2574,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}Wow… You cleaned me out.\nAre you actually a beginner? $@c{smile}Maybe it was a bit of luck but…\nWho knows you might just be able to go all the way. $By the way, the professor asked me to give you these items. They look pretty cool. - $@c{serious_smile_fists}Good luck out there! - $@c{smile}Oh- and I hope you enjoy the event!` + $@c{serious_smile_fists}Good luck out there!` }, }, "rival_female": { @@ -2589,8 +2588,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}You just started and you're already this strong?!@d{96}\n@c{angry}You totally cheated, didn't you? $@c{smile_wave_wink}Just kidding!@d{64} @c{smile_eclosed}I lost fair and square… I have a feeling you're going to do really well out there. $@c{smile}By the way, the professor wanted me to give you some items. Hopefully they're helpful! - $@c{smile_wave}Do your best like always! I believe in you! - $@c{smile}Oh- and I hope you enjoy the event!` + $@c{smile_wave}Do your best like always! I believe in you!` }, }, "rival_2": { @@ -2604,9 +2602,9 @@ export const PGMdialogue: DialogueTranslationEntries = { "victory": { 1: `@c{neutral_eclosed}Oh. I guess I was overconfident. $@c{smile}That's alright, though. I figured this might happen.\n@c{serious_mopen_fists}It just means I need to try harder for next time!\n - $@c{smile}Oh, not that you really need the help, but I had an extra one of each of these lying around and figured you might want them.\n + $@c{smile}Oh, not that you really need the help, but I had an extra one of these lying around and figured you might want it.\n $@c{serious_smile_fists}Don't expect another one after this, though!\nI can't keep giving my opponent an advantage after all. - $@c{smile}Anyway, take care, and enjoy the event!` + $@c{smile}Anyway, take care!` }, }, "rival_2_female": { @@ -2620,9 +2618,9 @@ export const PGMdialogue: DialogueTranslationEntries = { "victory": { 1: `@c{neutral}I… wasn't supposed to lose that time… $@c{smile}Aw well. That just means I'll have to train even harder for next time! - $@c{smile_wave}I also got you another two of these!\n@c{smile_wave_wink}No need to thank me~. + $@c{smile_wave}I also got you another one of these!\n@c{smile_wave_wink}No need to thank me~. $@c{angry_mopen}This is the last one, though! You won't be getting anymore freebies from me after this! - $@c{smile_wave}Keep at it, and enjoy the event!` + $@c{smile_wave}Keep at it!` }, "defeat": { 1: "It's OK to lose sometimes…" diff --git a/src/locales/en/move-trigger.ts b/src/locales/en/move-trigger.ts index f2371ad3e16..b140220e849 100644 --- a/src/locales/en/move-trigger.ts +++ b/src/locales/en/move-trigger.ts @@ -61,6 +61,7 @@ export const moveTriggers: SimpleTranslationEntries = { "faintCountdown": "{{pokemonName}}\nwill faint in {{turnCount}} turns.", "copyType": "{{pokemonName}}'s type became the same as\n{{targetPokemonName}}'s type!", "suppressAbilities": "{{pokemonName}}'s ability\nwas suppressed!", + "revivalBlessing": "{{pokemonName}} was revived!", "swapArenaTags": "{{pokemonName}} swapped the battle effects affecting each side of the field!", "trickOnSwap": "{{pokemonNameWithAffix}} switched items with its target!", "trickFoeNewItem": "{{pokemonNameWithAffix}} obtained one {{itemName}}.", diff --git a/src/locales/en/settings.ts b/src/locales/en/settings.ts index c63f9de6049..ad2ea914dc9 100644 --- a/src/locales/en/settings.ts +++ b/src/locales/en/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "Gamepad Support", "showBgmBar": "Show Music Names", "moveTouchControls": "Move Touch Controls", - "shopOverlayOpacity": "Shop Overlay Opacity" + "shopOverlayOpacity": "Shop Overlay Opacity", + "shopCursorTarget": "Shop Cursor Target", + "items": "Items", + "reroll": "Reroll", + "shop": "Shop", + "checkTeam": "Check Team" } as const; diff --git a/src/locales/es/ability-trigger.ts b/src/locales/es/ability-trigger.ts index 60bc186e99d..99ebfe3bd4c 100644 --- a/src/locales/es/ability-trigger.ts +++ b/src/locales/es/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postFaintHpDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postSummonPressure": "{{pokemonNameWithAffix}} is exerting its Pressure!", + "weatherEffectDisappeared": "El tiempo atmosférico ya no ejerce ninguna influencia.", "postSummonMoldBreaker": "{{pokemonNameWithAffix}} breaks the mold!", "postSummonAnticipation": "{{pokemonNameWithAffix}} shuddered!", "postSummonTurboblaze": "{{pokemonNameWithAffix}} is radiating a blazing aura!", diff --git a/src/locales/es/battle-scene.ts b/src/locales/es/battle-scene.ts new file mode 100644 index 00000000000..995ca744302 --- /dev/null +++ b/src/locales/es/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "{{formattedMoney}} ₽" +} as const; diff --git a/src/locales/es/battle.ts b/src/locales/es/battle.ts index eb37699d911..83ecbc93503 100644 --- a/src/locales/es/battle.ts +++ b/src/locales/es/battle.ts @@ -24,6 +24,7 @@ export const battle: SimpleTranslationEntries = { "hitResultSuperEffective": "¡Es supereficaz!", "hitResultNotVeryEffective": "No es muy eficaz…", "hitResultNoEffect": "No afecta a {{pokemonName}}!", + "hitResultImmune": "¡No afecta a {{pokemonName}}!", "hitResultOneHitKO": "¡KO en 1 golpe!", "attackFailed": "¡Pero ha fallado!", "attackMissed": "¡{{pokemonNameWithAffix}}\nha evitado el ataque!", diff --git a/src/locales/es/config.ts b/src/locales/es/config.ts index ce9ad19aac3..6c038188da2 100644 --- a/src/locales/es/config.ts +++ b/src/locales/es/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const esConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/es/dialogue.ts b/src/locales/es/dialogue.ts index 07e51458a29..187127d1d39 100644 --- a/src/locales/es/dialogue.ts +++ b/src/locales/es/dialogue.ts @@ -2569,8 +2569,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}Wow… You cleaned me out.\nAre you actually a beginner? $@c{smile}Maybe it was a bit of luck but…\nWho knows you might just be able to go all the way. $By the way, the professor asked me to give you these items. They look pretty cool. - $@c{serious_smile_fists}Good luck out there! - $@c{smile}Oh- and I hope you enjoy the event!` + $@c{serious_smile_fists}Good luck out there!` }, }, "rival_female": { @@ -2584,8 +2583,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}You just started and you're already this strong?!@d{96}\n@c{angry}You totally cheated, didn't you? $@c{smile_wave_wink}Just kidding!@d{64} @c{smile_eclosed}I lost fair and square… I have a feeling you're going to do really well out there. $@c{smile}By the way, the professor wanted me to give you some items. Hopefully they're helpful! - $@c{smile_wave}Do your best like always! I believe in you! - $@c{smile}Oh- and I hope you enjoy the event!` + $@c{smile_wave}Do your best like always! I believe in you!` }, }, "rival_2": { @@ -2601,7 +2599,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}That's alright, though. I figured this might happen.\n@c{serious_mopen_fists}It just means I need to try harder for next time!\n $@c{smile}Oh, not that you really need the help, but I had an extra one of these lying around and figured you might want it.\n $@c{serious_smile_fists}Don't expect another one after this, though!\nI can't keep giving my opponent an advantage after all. - $@c{smile}Anyway, take care, and enjoy the event!` + $@c{smile}Anyway, take care!` }, }, "rival_2_female": { @@ -2617,7 +2615,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}Aw well. That just means I'll have to train even harder for next time! $@c{smile_wave}I also got you another one of these!\n@c{smile_wave_wink}No need to thank me~. $@c{angry_mopen}This is the last one, though! You won't be getting anymore freebies from me after this! - $@c{smile_wave}Keep at it, and enjoy the event!` + $@c{smile_wave}Keep at it!` }, "defeat": { 1: "It's OK to lose sometimes…" diff --git a/src/locales/es/move-trigger.ts b/src/locales/es/move-trigger.ts index 583e811ae2b..930eed42ed7 100644 --- a/src/locales/es/move-trigger.ts +++ b/src/locales/es/move-trigger.ts @@ -61,6 +61,7 @@ export const moveTriggers: SimpleTranslationEntries = { "faintCountdown": "{{pokemonName}}\nwill faint in {{turnCount}} turns.", "copyType": "{{pokemonName}}'s type\nchanged to match {{targetPokemonName}}'s!", "suppressAbilities": "{{pokemonName}}'s ability\nwas suppressed!", + "revivalBlessing": "¡{{pokemonName}} ha revivido!", "swapArenaTags": "{{pokemonName}} swapped the battle effects affecting each side of the field!", "trickOnSwap": "{{pokemonNameWithAffix}} switched items with its target!", "trickFoeNewItem": "{{pokemonNameWithAffix}} obtained one {{itemName}}.", diff --git a/src/locales/es/settings.ts b/src/locales/es/settings.ts index c7f723fe80c..fbc56d92fe5 100644 --- a/src/locales/es/settings.ts +++ b/src/locales/es/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "Gamepad Support", "showBgmBar": "Show Music Names", "moveTouchControls": "Move Touch Controls", - "shopOverlayOpacity": "Opacidad de la fase de compra" + "shopOverlayOpacity": "Opacidad de la fase de compra", + "shopCursorTarget": "Shop Cursor Target", + "items": "Items", + "reroll": "Reroll", + "shop": "Shop", + "checkTeam": "Check Team" } as const; diff --git a/src/locales/fr/ability-trigger.ts b/src/locales/fr/ability-trigger.ts index cd077993b4e..92e02b82414 100644 --- a/src/locales/fr/ability-trigger.ts +++ b/src/locales/fr/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}} est blessé\npar son talent {{abilityName}} !", "postFaintHpDamage": "{{pokemonNameWithAffix}} est blessé\npar son talent {{abilityName}} !", "postSummonPressure": "{{pokemonNameWithAffix}}\naugmente la pression !", + "weatherEffectDisappeared": "Les effets de la météo se dissipent !", "postSummonMoldBreaker": "{{pokemonNameWithAffix}}\nbrise le moule !", "postSummonAnticipation": "{{pokemonNameWithAffix}}\nest tout tremblant !", "postSummonTurboblaze": "{{pokemonNameWithAffix}} dégage\nune aura de flammes incandescentes !", diff --git a/src/locales/fr/battle-scene.ts b/src/locales/fr/battle-scene.ts new file mode 100644 index 00000000000..995ca744302 --- /dev/null +++ b/src/locales/fr/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "{{formattedMoney}} ₽" +} as const; diff --git a/src/locales/fr/battle.ts b/src/locales/fr/battle.ts index 5b6fa4ddd7f..ea12f103d2f 100644 --- a/src/locales/fr/battle.ts +++ b/src/locales/fr/battle.ts @@ -24,6 +24,7 @@ export const battle: SimpleTranslationEntries = { "hitResultSuperEffective": "C’est super efficace !", "hitResultNotVeryEffective": "Ce n’est pas très efficace…", "hitResultNoEffect": "Ça n’affecte pas {{pokemonName}}…", + "hitResultImmune": "{{pokemonName}} n’est pas affecté !", "hitResultOneHitKO": "K.O. en un coup !", "attackFailed": "Mais cela échoue !", "attackMissed": "{{pokemonNameWithAffix}}\névite l’attaque !", diff --git a/src/locales/fr/config.ts b/src/locales/fr/config.ts index 246ae9a073f..a2ab67eefe0 100644 --- a/src/locales/fr/config.ts +++ b/src/locales/fr/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const frConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/fr/dialogue.ts b/src/locales/fr/dialogue.ts index 49dc6124fbf..8a8707dc4e5 100644 --- a/src/locales/fr/dialogue.ts +++ b/src/locales/fr/dialogue.ts @@ -2372,8 +2372,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}Wah… Tu m’as vraiment lavé.\nT’es vraiment un débutant ? $@c{smile}T’as peut-être eu de la chance, mais…\nPeut-être que t’arriveras jusqu’au bout du chemin. $D’ailleurs, le prof m’a demandé de te filer ces objets.\nIls ont l’air sympas. - $@c{serious_smile_fists}Bonne chance à toi ! - $@c{smile}Oh, et profite bien de l’évènement !` + $@c{serious_smile_fists}Bonne chance à toi !` }, }, "rival_female": { @@ -2387,8 +2386,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}Tu viens de commencer et t’es déjà si fort ?!@d{96}\n@c{angry}T’as triché non ? Avoue ! $@c{smile_wave_wink}J’déconne !@d{64} @c{smile_eclosed}J’ai perdu dans les règles…\nJ’ai le sentiment que tu vas très bien t’en sortir. $@c{smile}D’ailleurs, le prof veut que je te donne ces quelques objets. Ils te seront utiles, pour sûr ! - $@c{smile_wave}Fais de ton mieux, comme toujours !\nJe crois fort en toi ! - $@c{smile}Oh, et profite bien de l’évènement !` + $@c{smile_wave}Fais de ton mieux, comme toujours !\nJe crois fort en toi !` }, }, "rival_2": { @@ -2404,7 +2402,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}Pas grave, c’est OK. Je me doutais que ça arriverait.\n@c{serious_mopen_fists}Je vais juste devoir encore plus m’entrainer !\n $@c{smile}Ah, et pas que t’aies réellement besoin d’aide, mais j’ai ça en trop sur moi qui pourrait t’intéresser.\n $@c{serious_smile_fists}Mais n’espère plus en avoir d’autres !\nJe peux pas passer mon temps à aider mon adversaire. - $@c{smile}Bref, prends soin de toi et profite bien de l’évènement !` + $@c{smile}Bref, prends soin de toi !` }, }, "rival_2_female": { @@ -2419,8 +2417,8 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{neutral}Je… J’étais pas encore supposée perdre… $@c{smile}Bon. Ça veut juste dire que je vais devoir encore plus m’entrainer ! $@c{smile_wave}J’ai aussi ça en rab pour toi !\n@c{smile_wave_wink}Inutile de me remercier ~. - $@c{angry_mopen}C’étaient les derniers, terminé les cadeaux après ceux-là ! - $@c{smile_wave}Allez, tiens le coup et profite bien de l’évènement !` + $@c{angry_mopen}C’était le dernier, terminé les cadeaux après celui-là ! + $@c{smile_wave}Allez, tiens le coup !` }, "defeat": { 1: "Je suppose que c’est parfois normal de perdre…" @@ -5053,8 +5051,7 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: `@c{shock}Wah… Tu m’as vraiment lavé.\nT’es vraiment une débutante ? $@c{smile}T’as peut-être eu de la chance, mais…\nPeut-être que t’arriveras jusqu’au bout du chemin. $D’ailleurs, le prof m’a demandé de te filer ces objets.\nIls ont l’air sympas. - $@c{serious_smile_fists}Bonne chance à toi ! - $@c{smile}Oh, et profite bien de l’évènement !` + $@c{serious_smile_fists}Bonne chance à toi !` }, }, "rival_female": { @@ -5068,8 +5065,7 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: `@c{shock}Tu viens de commencer et t’es déjà si forte ?!@d{96}\n@c{angry}T’as triché non ? Avoue ! $@c{smile_wave_wink}J’déconne !@d{64} @c{smile_eclosed}J’ai perdu dans les règles…\nJ’ai le sentiment que tu vas très bien t’en sortir. $@c{smile}D’ailleurs, le prof veut que je te donne ces quelques objets. Ils te seront utiles, pour sûr ! - $@c{smile_wave}Fais de ton mieux, comme toujours !\nJe crois fort en toi ! - $@c{smile}Oh, et profite bien de l’évènement !` + $@c{smile_wave}Fais de ton mieux, comme toujours !\nJe crois fort en toi !` }, }, "rival_2": { @@ -5085,7 +5081,7 @@ export const PGFdialogue: DialogueTranslationEntries = { $@c{smile}Pas grave, c’est OK. Je me doutais que ça arriverait.\n@c{serious_mopen_fists}Je vais juste devoir encore plus m’entrainer !\n $@c{smile}Ah, et pas que t’aies réellement besoin d’aide, mais j’ai ça en trop sur moi qui pourrait t’intéresser.\n $@c{serious_smile_fists}Mais n’espère plus en avoir d’autres !\nJe peux pas passer mon temps à aider mon adversaire. - $@c{smile}Bref, prends soin de toi et profite bien de l’évènement !` + $@c{smile}Bref, prends soin de toi !` }, }, "rival_2_female": { @@ -5101,7 +5097,7 @@ export const PGFdialogue: DialogueTranslationEntries = { $@c{smile}Bon. Ça veut juste dire que je vais devoir encore plus m’entrainer ! $@c{smile_wave}J’ai aussi ça en rab pour toi !\n@c{smile_wave_wink}Inutile de me remercier ~. $@c{angry_mopen}C’était le dernier, terminé les cadeaux après celui-là ! - $@c{smile_wave}Allez, tiens le coup et profite bien de l’évènement !` + $@c{smile_wave}Allez, tiens le coup !` }, "defeat": { 1: "Je suppose que c’est parfois normal de perdre…" diff --git a/src/locales/fr/move-trigger.ts b/src/locales/fr/move-trigger.ts index 50adb91afe6..7ac30c54b36 100644 --- a/src/locales/fr/move-trigger.ts +++ b/src/locales/fr/move-trigger.ts @@ -61,6 +61,7 @@ export const moveTriggers: SimpleTranslationEntries = { "faintCountdown": "{{pokemonName}}\nsera K.O. dans {{turnCount}} tours !", "copyType": "{{pokemonName}} prend le type\nde {{targetPokemonName}} !", "suppressAbilities": "Le talent de {{pokemonName}}\na été rendu inactif !", + "revivalBlessing": "{{pokemonName}} a repris connaissance\net est prêt à se battre de nouveau !", "swapArenaTags": "Les effets affectant chaque côté du terrain\nont été échangés par {{pokemonName}} !", "trickOnSwap": "{{pokemonNameWithAffix}} échange\nun objet avec sa cible !", "trickFoeNewItem": "{{pokemonNameWithAffix}} obtient\nl’objet {{itemName}} !", diff --git a/src/locales/fr/settings.ts b/src/locales/fr/settings.ts index 95246ccb7d0..d5e4047bb12 100644 --- a/src/locales/fr/settings.ts +++ b/src/locales/fr/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "Gamepad Support", "showBgmBar": "Titre de la musique", "moveTouchControls": "Déplacer les contrôles tactiles", - "shopOverlayOpacity": "Opacité boutique" + "shopOverlayOpacity": "Opacité boutique", + "shopCursorTarget": "Choix après relance", + "items": "Obj. gratuits", + "reroll": "Relance", + "shop": "Boutique", + "checkTeam": "Équipe" } as const; diff --git a/src/locales/it/ability-trigger.ts b/src/locales/it/ability-trigger.ts index 95db5cbf26c..6ac5e76ee3e 100644 --- a/src/locales/it/ability-trigger.ts +++ b/src/locales/it/ability-trigger.ts @@ -25,39 +25,40 @@ export const abilityTriggers: SimpleTranslationEntries = { "postAttackStealHeldItem": "{{pokemonNameWithAffix}} ruba\n{{stolenItemType}} di {{defenderName}}!", "postDefendStealHeldItem": "{{pokemonNameWithAffix}} ruba\n{{stolenItemType}} di {{attackerName}}!", "copyFaintedAllyAbility": "L'abilità {{abilityName}} di {{pokemonNameWithAffix}} è passata all'alleato!", - "intimidateImmunity": "{{pokemonNameWithAffix}}'s {{abilityName}} prevented it from being Intimidated!", - "postSummonAllyHeal": "{{pokemonNameWithAffix}} drank down all the\nmatcha that {{pokemonName}} made!", - "postSummonClearAllyStats": "{{pokemonNameWithAffix}}'s stat changes\nwere removed!", - "postSummonTransform": "{{pokemonNameWithAffix}} transformed\ninto {{targetName}}!", - "protectStat": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents lowering its {{statName}}!", - "statusEffectImmunityWithName": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents {{statusEffectName}}!", - "statusEffectImmunity": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents status problems!", - "battlerTagImmunity": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents {{battlerTagName}}!", - "forewarn": "{{pokemonNameWithAffix}} was forewarned about {{moveName}}!", - "frisk": "{{pokemonNameWithAffix}} frisked {{opponentName}}'s {{opponentAbilityName}}!", - "postWeatherLapseHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", - "postWeatherLapseDamage": "{{pokemonNameWithAffix}} is hurt\nby its {{abilityName}}!", - "postTurnLootCreateEatenBerry": "{{pokemonNameWithAffix}} harvested one {{berryName}}!", - "postTurnHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", - "fetchBall": "{{pokemonNameWithAffix}} found a\n{{pokeballName}}!", - "healFromBerryUse": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP!", - "arenaTrap": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents switching!", - "postBattleLoot": "{{pokemonNameWithAffix}} picked up\n{{itemName}}!", - "postFaintContactDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", - "postFaintHpDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", - "postSummonPressure": "{{pokemonNameWithAffix}} is exerting its Pressure!", - "postSummonMoldBreaker": "{{pokemonNameWithAffix}} breaks the mold!", - "postSummonAnticipation": "{{pokemonNameWithAffix}} shuddered!", - "postSummonTurboblaze": "{{pokemonNameWithAffix}} is radiating a blazing aura!", - "postSummonTeravolt": "{{pokemonNameWithAffix}} is radiating a bursting aura!", - "postSummonDarkAura": "{{pokemonNameWithAffix}} is radiating a Dark Aura!", - "postSummonFairyAura": "{{pokemonNameWithAffix}} is radiating a Fairy Aura!", - "postSummonNeutralizingGas": "{{pokemonNameWithAffix}}'s Neutralizing Gas filled the area!", - "postSummonAsOneGlastrier": "{{pokemonNameWithAffix}} has two Abilities!", - "postSummonAsOneSpectrier": "{{pokemonNameWithAffix}} has two Abilities!", - "postSummonVesselOfRuin": "{{pokemonNameWithAffix}}'s Vessel of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", - "postSummonSwordOfRuin": "{{pokemonNameWithAffix}}'s Sword of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", - "postSummonTabletsOfRuin": "{{pokemonNameWithAffix}}'s Tablets of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", - "postSummonBeadsOfRuin": "{{pokemonNameWithAffix}}'s Beads of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", + "intimidateImmunity": "{{abilityName}} impedisce a {{pokemonNameWithAffix}} di\nessere intimidito!", + "postSummonAllyHeal": "{{pokemonNameWithAffix}} beve il\ntè che {{pokemonName}} gli ha preparato!", + "postSummonClearAllyStats": "Le statistiche di {{pokemonNameWithAffix}}\ntornano alla normalità!", + "postSummonTransform": "{{pokemonNameWithAffix}} assume le sembianze\ndi {{targetName}}!", + "protectStat": "{{abilityName}} di {{pokemonNameWithAffix}}\npreviene la riduzione del/della suo/a {{statName}}!", + "statusEffectImmunityWithName": "{{abilityName}} di {{pokemonNameWithAffix}}\nnon gli fa subire il/lo/la {{statusEffectName}}!", + "statusEffectImmunity": "{{abilityName}} di {{pokemonNameWithAffix}}\npreviene i problemi di stato!", + "battlerTagImmunity": "{{abilityName}} di {{pokemonNameWithAffix}}\npreviene {{battlerTagName}}!", + "forewarn": "{{pokemonNameWithAffix}} è stato messo in guardia da {{moveName}}!", + "frisk": "{{pokemonNameWithAffix}} perquisice {{opponentName}}\ne trova la sua abilità, {{opponentAbilityName}}!", + "postWeatherLapseHeal": "{{pokemonNameWithAffix}} recupera alcuni PS\ncon {{abilityName}}!", + "postWeatherLapseDamage": "{{pokemonNameWithAffix}} subisce danni\na causa della sua abilità, {{abilityName}}!", + "postTurnLootCreateEatenBerry": "{{pokemonNameWithAffix}} raccoglie una {{berryName}}!", + "postTurnHeal": "{{pokemonNameWithAffix}} recupera alcuni PS\ncon {{abilityName}}!", + "fetchBall": "{{pokemonNameWithAffix}} ha trovato una\n{{pokeballName}}!", + "healFromBerryUse": "{{abilityName}} di {{pokemonNameWithAffix}}\nristabilisce parte dei PS!", + "arenaTrap": "L’abilità {{abilityName}} di {{pokemonNameWithAffix}}\nimpedisce la sostituzione!", + "postBattleLoot": "{{pokemonNameWithAffix}} ha raccolto\nil/l'/lo/la {{itemName}}!", + "postFaintContactDamage": "{{abilityName}} di {{pokemonNameWithAffix}}\nferisce il Pokémon che lo ha attaccato!", + "postFaintHpDamage": "{{abilityName}} di {{pokemonNameWithAffix}}\nferisce il Pokémon che lo ha attaccato!", + "postSummonPressure": "{{pokemonNameWithAffix}} fa pressione!", + "weatherEffectDisappeared": "Le condizioni atmosferiche non hanno alcun effetto.", + "postSummonMoldBreaker": "{{pokemonNameWithAffix}} ha l’abilità Rompiforma!", + "postSummonAnticipation": "{{pokemonNameWithAffix}} rabbrividisce!", + "postSummonTurboblaze": "{{pokemonNameWithAffix}} emana un’aura infuocata!", + "postSummonTeravolt": "{{pokemonNameWithAffix}} emana un’aura repulsiva!", + "postSummonDarkAura": "L’abilità Auratetra di {{pokemonNameWithAffix}} è attiva.", + "postSummonFairyAura": "L’abilità Aurafolletto di {{pokemonNameWithAffix}} è attiva.", + "postSummonNeutralizingGas": "Il Gas Reagente di {{pokemonNameWithAffix}}\nsi diffonde tutt’intorno!", + "postSummonAsOneGlastrier": "{{pokemonNameWithAffix}} ha due abilità!", + "postSummonAsOneSpectrier": "{{pokemonNameWithAffix}} ha due abilità!", + "postSummonVesselOfRuin": "La/l'{{statName}} dei Pokémon intorno si indebolisce a causa\ndell'abilità Vaso Nefasto di {{pokemonNameWithAffix}}!", + "postSummonSwordOfRuin": "La/l'{{statName}} dei Pokémon intorno si indebolisce a causa\ndell'abilità Spada Nefasta di {{pokemonNameWithAffix}}!", + "postSummonTabletsOfRuin": "La/l'{{statName}} dei Pokémon intorno si indebolisce a causa\ndell'abilità Amuleto Nefasto di {{pokemonNameWithAffix}}!", + "postSummonBeadsOfRuin": "La/l'{{statName}} dei Pokémon intorno si indebolisce a causa\ndell'abilità Monile Nefasto di {{pokemonNameWithAffix}}!", "preventBerryUse": "{{pokemonNameWithAffix}} non riesce a\nmangiare le bacche per l'agitazione!", } as const; diff --git a/src/locales/it/achv.ts b/src/locales/it/achv.ts index 91222b81579..756d95e6431 100644 --- a/src/locales/it/achv.ts +++ b/src/locales/it/achv.ts @@ -170,8 +170,8 @@ export const PGMachv: AchievementTranslationEntries = { description: "Vinci in modalità classica", }, "UNEVOLVED_CLASSIC_VICTORY": { - name: "Bring Your Child To Work Day", - description: "Beat the game in Classic Mode with at least one unevolved party member." + name: "Alternanza scuola-lavoro", + description: "Completa la modalità classica con almeno un membro della squadra non evoluto completamente." }, "MONO_GEN_ONE": { @@ -269,8 +269,8 @@ export const PGMachv: AchievementTranslationEntries = { name: "Follettini e follettine", }, "FRESH_START": { - name: "First Try!", - description: "Complete the Fresh Start challenge." + name: "Buona la prima!", + description: "Completa la modalità sfida 'Un nuovo inizio'." } } as const; diff --git a/src/locales/it/battle-scene.ts b/src/locales/it/battle-scene.ts new file mode 100644 index 00000000000..995ca744302 --- /dev/null +++ b/src/locales/it/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "{{formattedMoney}} ₽" +} as const; diff --git a/src/locales/it/battle.ts b/src/locales/it/battle.ts index bd7227eacb6..5d024547aa8 100644 --- a/src/locales/it/battle.ts +++ b/src/locales/it/battle.ts @@ -24,6 +24,7 @@ export const battle: SimpleTranslationEntries = { "hitResultSuperEffective": "È superefficace!", "hitResultNotVeryEffective": "Non è molto efficace…", "hitResultNoEffect": "Non ha effetto su {{pokemonName}}!", + "hitResultImmune": "{{pokemonName}} è incolume!", "hitResultOneHitKO": "KO con un colpo!", "attackFailed": "Ma ha fallito!", "attackMissed": "{{pokemonNameWithAffix}}\nevita l’attacco!", @@ -74,22 +75,22 @@ export const battle: SimpleTranslationEntries = { "fainted": "{{pokemonNameWithAffix}} non è più in\ngrado di combattere!", "statsAnd": "e", "stats": "statistiche", - "statRose_one": "{{pokemonNameWithAffix}}'s {{stats}} è aumentato/a!", - "statRose_other": "{{pokemonNameWithAffix}}'s {{stats}} rose!", - "statSharplyRose_one": "{{pokemonNameWithAffix}}'s {{stats}} è aumentato/a molto!", - "statSharplyRose_other": "{{pokemonNameWithAffix}}'s {{stats}} sharply rose!", - "statRoseDrastically_one": "{{pokemonNameWithAffix}}'s {{stats}} è aumentato/a drasticamente!", - "statRoseDrastically_other": "{{pokemonNameWithAffix}}'s {{stats}} rose drastically!", - "statWontGoAnyHigher_one": "{{pokemonNameWithAffix}}'s {{stats}} non può aumentare più di così!", - "statWontGoAnyHigher_other": "{{pokemonNameWithAffix}}'s {{stats}} won't go any higher!", - "statFell_one": "{{pokemonNameWithAffix}}'s {{stats}} è diminuito/a!", - "statFell_other": "{{pokemonNameWithAffix}}'s {{stats}} fell!", - "statHarshlyFell_one": "{{pokemonNameWithAffix}}'s {{stats}} è diminuito/a molto!", - "statHarshlyFell_other": "{{pokemonNameWithAffix}}'s {{stats}} harshly fell!", - "statSeverelyFell_one": "{{pokemonNameWithAffix}}'s {{stats}} è diminuito/a drasticamente!", - "statSeverelyFell_other": "{{pokemonNameWithAffix}}'s {{stats}} severely fell!", - "statWontGoAnyLower_one": "{{pokemonNameWithAffix}}'s {{stats}} non può diminuire più di così!", - "statWontGoAnyLower_other": "{{pokemonNameWithAffix}}'s {{stats}} won't go any lower!", + "statRose_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} è aumentata!", + "statRose_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} sono aumentate!", + "statSharplyRose_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} è aumentata molto!", + "statSharplyRose_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} sono aumentate molto!", + "statRoseDrastically_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} è aumentata drasticamente!", + "statRoseDrastically_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} sono aumentate drasticamente!", + "statWontGoAnyHigher_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} non può aumentare di più!", + "statWontGoAnyHigher_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} non possono aumentare di più!", + "statFell_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} è diminuita!", + "statFell_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} sono diminuite!", + "statHarshlyFell_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} è diminuita molto!", + "statHarshlyFell_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} sono diminuite molto!", + "statSeverelyFell_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} è diminuita drasticamente!", + "statSeverelyFell_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} sono diminuite drasticamente!", + "statWontGoAnyLower_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} non può diminuire di più!", + "statWontGoAnyLower_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} non possono diminuire di più!", "transformedIntoType": "{{pokemonName}} diventa\ndi tipo {{type}} type!", "retryBattle": "Vuoi riprovare dall'inizio della lotta?", "unlockedSomething": "{{unlockedThing}}\nè stato/a sbloccato/a.", diff --git a/src/locales/it/battler-tags.ts b/src/locales/it/battler-tags.ts index 7dd3ebc6fb1..518e9194521 100644 --- a/src/locales/it/battler-tags.ts +++ b/src/locales/it/battler-tags.ts @@ -1,14 +1,14 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const battlerTags: SimpleTranslationEntries = { - "trappedDesc": "trapping", - "flinchedDesc": "flinching", - "confusedDesc": "confusion", - "infatuatedDesc": "infatuation", - "seedDesc": "seeding", - "nightmareDesc": "nightmares", - "ingrainDesc": "roots", - "drowsyDesc": "drowsiness", + "trappedDesc": "intrappolando", + "flinchedDesc": "tentennando", + "confusedDesc": "confuso", + "infatuatedDesc": "infatuato", + "seedDesc": "pieno di semi", + "nightmareDesc": "incubi", + "ingrainDesc": "radici", + "drowsyDesc": "assonnato", "rechargingLapse": "{{pokemonNameWithAffix}} deve\nricaricarsi!", "trappedOnAdd": "{{pokemonNameWithAffix}} non può\npiù fuggire!", "trappedOnRemove": "{{pokemonNameWithAffix}} è stato liberato\nda {{moveName}}", diff --git a/src/locales/it/challenges.ts b/src/locales/it/challenges.ts index 784791f5425..dde5bd0d4e7 100644 --- a/src/locales/it/challenges.ts +++ b/src/locales/it/challenges.ts @@ -2,7 +2,7 @@ import { TranslationEntries } from "#app/interfaces/locales"; export const challenges: TranslationEntries = { "title": "Modificatori delle sfide", - "illegalEvolution": "{{pokemon}} changed into an ineligble pokémon\nfor this challenge!", + "illegalEvolution": "{{pokemon}} non è più utilizzabile\nsecondo le regole della sfida!", "singleGeneration": { "name": "Mono gen", "desc": "Puoi usare solo Pokémon di {{gen}} generazione.", @@ -23,8 +23,8 @@ export const challenges: TranslationEntries = { "desc_default": "Puoi usare solo Pokémon del tipo selezionato." }, "freshStart": { - "name": "Fresh Start", - "desc": "You can only use the original starters, and only as if you had just started PokéRogue.", + "name": "Un nuovo inizio", + "desc": "Puoi usare solo gli starter originali, e come se avessi appena cominciato Pokérogue.", "value.0": "Off", "value.1": "On", } diff --git a/src/locales/it/config.ts b/src/locales/it/config.ts index ceb52665796..d6265061a9f 100644 --- a/src/locales/it/config.ts +++ b/src/locales/it/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const itConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/it/dialogue.ts b/src/locales/it/dialogue.ts index f692b9b848a..1089db4e6f8 100644 --- a/src/locales/it/dialogue.ts +++ b/src/locales/it/dialogue.ts @@ -2569,8 +2569,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}Wow… You cleaned me out.\nAre you actually a beginner? $@c{smile}Maybe it was a bit of luck but…\nWho knows you might just be able to go all the way. $By the way, the professor asked me to give you these items. They look pretty cool. - $@c{serious_smile_fists}Good luck out there! - $@c{smile}Oh- and I hope you enjoy the event!` + $@c{serious_smile_fists}Good luck out there!` }, }, "rival_female": { @@ -2584,8 +2583,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}You just started and you're already this strong?!@d{96}\n@c{angry}You totally cheated, didn't you? $@c{smile_wave_wink}Just kidding!@d{64} @c{smile_eclosed}I lost fair and square… I have a feeling you're going to do really well out there. $@c{smile}By the way, the professor wanted me to give you some items. Hopefully they're helpful! - $@c{smile_wave}Do your best like always! I believe in you! - $@c{smile}Oh- and I hope you enjoy the event!` + $@c{smile_wave}Do your best like always! I believe in you!` }, }, "rival_2": { @@ -2601,7 +2599,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}That's alright, though. I figured this might happen.\n@c{serious_mopen_fists}It just means I need to try harder for next time!\n $@c{smile}Oh, not that you really need the help, but I had an extra one of these lying around and figured you might want it.\n $@c{serious_smile_fists}Don't expect another one after this, though!\nI can't keep giving my opponent an advantage after all. - $@c{smile}Anyway, take care, and enjoy the event!` + $@c{smile}Anyway, take care!` }, }, "rival_2_female": { @@ -2617,7 +2615,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}Aw well. That just means I'll have to train even harder for next time! $@c{smile_wave}I also got you another one of these!\n@c{smile_wave_wink}No need to thank me~. $@c{angry_mopen}This is the last one, though! You won't be getting anymore freebies from me after this! - $@c{smile_wave}Keep at it, and enjoy the event!` + $@c{smile_wave}Keep at it!` }, "defeat": { 1: "It's OK to lose sometimes…" diff --git a/src/locales/it/menu-ui-handler.ts b/src/locales/it/menu-ui-handler.ts index 0cfea8d67b7..3454de24f87 100644 --- a/src/locales/it/menu-ui-handler.ts +++ b/src/locales/it/menu-ui-handler.ts @@ -25,5 +25,5 @@ export const menuUiHandler: SimpleTranslationEntries = { "unlinkGoogle": "Scollega Google", "cancel": "Annulla", "losingProgressionWarning": "Perderai tutti i progressi dall'inizio della battaglia. Confermi?", - "noEggs": "You are not hatching\nany eggs at the moment!" + "noEggs": "Non stai schiudendo\nuova al momento!" } as const; diff --git a/src/locales/it/menu.ts b/src/locales/it/menu.ts index 9766708f7ae..3787ceb0e70 100644 --- a/src/locales/it/menu.ts +++ b/src/locales/it/menu.ts @@ -17,7 +17,7 @@ export const menu: SimpleTranslationEntries = { "username": "Nome utente", "password": "Password", "login": "Accedi", - "orUse": "Or use", + "orUse": "O usa", "register": "Registrati", "emptyUsername": "Nome utente mancante!", "invalidLoginUsername": "Nome utente non valido!", @@ -39,9 +39,9 @@ export const menu: SimpleTranslationEntries = { "weeklyRankings": "Classifica settimanale", "noRankings": "Nessuna classifica", "positionIcon": "#", - "usernameScoreboard": "Username", - "score": "Score", - "wave": "Wave", + "usernameScoreboard": "Nome utente", + "score": "Punteggio", + "wave": "Onda", "loading": "Caricamento…", "loadingAsset": "Caricamento asset: {{assetName}}", "playersOnline": "Giocatori online", diff --git a/src/locales/it/modifier-type.ts b/src/locales/it/modifier-type.ts index 6f054e4e566..0b166d268a5 100644 --- a/src/locales/it/modifier-type.ts +++ b/src/locales/it/modifier-type.ts @@ -101,7 +101,7 @@ export const modifierType: ModifierTypeTranslationEntries = { }, "TmModifierTypeWithInfo": { name: "MT{{moveId}} - {{moveName}}", - description: "Insegna {{moveName}} a un Pokémon\n(Hold C or Shift for more info).", + description: "Insegna {{moveName}} a un Pokémon\n(Tieni premuto C o Shift per maggiori informazioni).", }, "EvolutionItemModifierType": { description: "Fa evolvere determinate specie di Pokémon.", @@ -153,7 +153,7 @@ export const modifierType: ModifierTypeTranslationEntries = { "REVIVER_SEED": { name: "Revitalseme", description: "Il possessore recupera 1/2 di PS in caso di KO causato da un colpo diretto." }, - "WHITE_HERB": { name: "Erbachiara", description: "An item to be held by a Pokémon. It will restore any lowered stat in battle." }, + "WHITE_HERB": { name: "Erbachiara", description: "Strumento da dare a un Pokémon. Ripristina le statistiche ridotte in lotta." }, "ETHER": { name: "Etere" }, "MAX_ETHER": { name: "Etere max" }, diff --git a/src/locales/it/modifier.ts b/src/locales/it/modifier.ts index 810524a9e5e..94512efef0d 100644 --- a/src/locales/it/modifier.ts +++ b/src/locales/it/modifier.ts @@ -1,14 +1,14 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const modifier: SimpleTranslationEntries = { - "surviveDamageApply": "{{pokemonNameWithAffix}} hung on\nusing its {{typeName}}!", - "turnHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!", - "hitHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!", - "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} was revived\nby its {{typeName}}!", - "pokemonResetNegativeStatStageApply": "{{pokemonNameWithAffix}}'s lowered stats were restored\nby its {{typeName}}!", - "moneyInterestApply": "You received interest of ₽{{moneyAmount}}\nfrom the {{typeName}}!", - "turnHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was absorbed\nby {{pokemonName}}'s {{typeName}}!", - "contactHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was snatched\nby {{pokemonName}}'s {{typeName}}!", - "enemyTurnHealApply": "{{pokemonNameWithAffix}}\nrestored some HP!", + "surviveDamageApply": "{{pokemonNameWithAffix}} resiste\ngrazie al/alla suo/a {{typeName}}!", + "turnHealApply": "{{pokemonNameWithAffix}} recupera alcuni PS con\nil/la suo/a {{typeName}}!", + "hitHealApply": "{{pokemonNameWithAffix}} recupera alcuni PS con\nil/la suo/a {{typeName}}!", + "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} torna in forze\ngrazie al/alla suo/a {{typeName}}!", + "pokemonResetNegativeStatStageApply": "La riduzione alle statistiche di {{pokemonNameWithAffix}}\nviene annullata grazie al/alla suo/a {{typeName}}!", + "moneyInterestApply": "Ricevi un interesse pari a {{moneyAmount}}₽\ngrazie al/allo/a {{typeName}}!", + "turnHeldItemTransferApply": "Il/l'/lo/la {{itemName}} di {{pokemonNameWithAffix}} è stato assorbito\ndal {{typeName}} di {{pokemonName}}!", + "contactHeldItemTransferApply": "Il/l'/lo/la {{itemName}} di {{pokemonNameWithAffix}} è stato rubato\nda {{pokemonName}} con {{typeName}}!", + "enemyTurnHealApply": "{{pokemonNameWithAffix}}\nristabilisce parte dei PS!", "bypassSpeedChanceApply": "{{pokemonName}} agisce più rapidamente del normale grazie al suo {{itemName}}!", } as const; diff --git a/src/locales/it/move-trigger.ts b/src/locales/it/move-trigger.ts index a4a0e877a01..80bfac07874 100644 --- a/src/locales/it/move-trigger.ts +++ b/src/locales/it/move-trigger.ts @@ -57,10 +57,11 @@ export const moveTriggers: SimpleTranslationEntries = { "sacrificialFullRestore": "{{pokemonName}} riceve i benefici\neffetti di Curardore!", "invertStats": "Le modifiche alle statistiche di {{pokemonName}}\nvengono invertite!", "resetStats": "Tutte le modifiche alle statistiche sono state annullate!", - "statEliminated": "All stat changes were eliminated!", + "statEliminated": "Tutte le modifiche alle statistiche sono state annullate!", "faintCountdown": "{{pokemonName}}\nandrà KO dopo {{turnCount}} turni.", "copyType": "{{pokemonName}} assume il tipo\ndi {{targetPokemonName}}!", "suppressAbilities": "L’abilità di {{pokemonName}}\nperde ogni efficacia!", + "revivalBlessing": "{{pokemonName}} torna in forze!", "swapArenaTags": "{{pokemonName}} ha invertito gli effetti attivi\nnelle due metà del campo!", "trickOnSwap": "{{pokemonNameWithAffix}} switched items with its target!", "trickFoeNewItem": "{{pokemonNameWithAffix}} obtained one {{itemName}}.", diff --git a/src/locales/it/move.ts b/src/locales/it/move.ts index a2c99218cec..1c510d4df6d 100644 --- a/src/locales/it/move.ts +++ b/src/locales/it/move.ts @@ -2915,7 +2915,7 @@ export const move: MoveTranslationEntries = { }, zippyZap: { name: "Sprintaboom", - effect: "The user attacks the target with bursts of electricity at high speed. This move always goes first and raises the user's evasiveness.", + effect: "Un attacco elettrico ad altissima velocità. Questa mossa ha priorità alta e aumenta l'elusione dell'utilizzatore.", }, splishySplash: { name: "Surfasplash", diff --git a/src/locales/it/settings.ts b/src/locales/it/settings.ts index 0d05d01ba1c..278a02bef52 100644 --- a/src/locales/it/settings.ts +++ b/src/locales/it/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "Supporto Gamepad", "showBgmBar": "Mostra Nomi Musica", "moveTouchControls": "Move Touch Controls", - "shopOverlayOpacity": "Opacità Finestra Negozio" + "shopOverlayOpacity": "Opacità Finestra Negozio", + "shopCursorTarget": "Target Cursore Negozio", + "items": "Oggetti", + "reroll": "Rerolla", + "shop": "Negozio", + "checkTeam": "Squadra" } as const; diff --git a/src/locales/ja/ability-trigger.ts b/src/locales/ja/ability-trigger.ts index 7c7d081f645..cf4c89ff5a4 100644 --- a/src/locales/ja/ability-trigger.ts +++ b/src/locales/ja/ability-trigger.ts @@ -47,6 +47,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}}は {{abilityName}}で\n相手に ダメージを 与えた!", "postFaintHpDamage": "{{pokemonNameWithAffix}}は {{abilityName}}で\n相手に ダメージを 与えた!", "postSummonPressure": "{{pokemonNameWithAffix}}は\nプレッシャーを 放っている!", + "weatherEffectDisappeared": "天候の影響が なくなった!", "postSummonMoldBreaker": "{{pokemonNameWithAffix}}は\nかたやぶりだ!", "postSummonAnticipation": "{{pokemonNameWithAffix}}は\nみぶるいした!", "postSummonTurboblaze": "{{pokemonNameWithAffix}}は\n燃え盛(もえさか)る オーラを 放っている!", diff --git a/src/locales/ja/battle-scene.ts b/src/locales/ja/battle-scene.ts new file mode 100644 index 00000000000..d2f074416e1 --- /dev/null +++ b/src/locales/ja/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "{{formattedMoney}}円" +} as const; diff --git a/src/locales/ja/battle.ts b/src/locales/ja/battle.ts index 926e2d9b0e4..061614202a0 100644 --- a/src/locales/ja/battle.ts +++ b/src/locales/ja/battle.ts @@ -24,6 +24,7 @@ export const battle: SimpleTranslationEntries = { "hitResultSuperEffective": "効果は バツグンだ!", "hitResultNotVeryEffective": "効果は 今ひとつの ようだ……", "hitResultNoEffect": "{{pokemonName}}には 効果が ないようだ…", + "hitResultImmune": "{{pokemonName}}には\n全然 効いてない!", "hitResultOneHitKO": "一撃必殺!", "attackFailed": "しかし うまく 決まらなかった!!", "attackMissed": "{{pokemonNameWithAffix}}には 当たらなかった!", diff --git a/src/locales/ja/config.ts b/src/locales/ja/config.ts index 6af79547a04..61be36c08df 100644 --- a/src/locales/ja/config.ts +++ b/src/locales/ja/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -61,6 +62,7 @@ export const jaConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/ja/dialogue.ts b/src/locales/ja/dialogue.ts index 6db337517da..e783ea14006 100644 --- a/src/locales/ja/dialogue.ts +++ b/src/locales/ja/dialogue.ts @@ -2573,8 +2573,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}Wow… You cleaned me out.\nAre you actually a beginner? $@c{smile}Maybe it was a bit of luck but…\nWho knows you might just be able to go all the way. $By the way, the professor asked me to give you these items. They look pretty cool. - $@c{serious_smile_fists}Good luck out there! - $@c{smile}Oh- and I hope you enjoy the event!` + $@c{serious_smile_fists}Good luck out there!` }, }, "rival_female": { @@ -2588,8 +2587,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}You just started and you're already this strong?!@d{96}\n@c{angry}You totally cheated, didn't you? $@c{smile_wave_wink}Just kidding!@d{64} @c{smile_eclosed}I lost fair and square… I have a feeling you're going to do really well out there. $@c{smile}By the way, the professor wanted me to give you some items. Hopefully they're helpful! - $@c{smile_wave}Do your best like always! I believe in you! - $@c{smile}Oh- and I hope you enjoy the event!` + $@c{smile_wave}Do your best like always! I believe in you!` }, }, "rival_2": { @@ -2605,7 +2603,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}That's alright, though. I figured this might happen.\n@c{serious_mopen_fists}It just means I need to try harder for next time!\n $@c{smile}Oh, not that you really need the help, but I had an extra one of these lying around and figured you might want it.\n $@c{serious_smile_fists}Don't expect another one after this, though!\nI can't keep giving my opponent an advantage after all. - $@c{smile}Anyway, take care, and enjoy the event!` + $@c{smile}Anyway, take care!` }, }, "rival_2_female": { @@ -2621,7 +2619,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}Aw well. That just means I'll have to train even harder for next time! $@c{smile_wave}I also got you another one of these!\n@c{smile_wave_wink}No need to thank me~. $@c{angry_mopen}This is the last one, though! You won't be getting anymore freebies from me after this! - $@c{smile_wave}Keep at it, and enjoy the event!` + $@c{smile_wave}Keep at it!` }, "defeat": { 1: "It's OK to lose sometimes…" diff --git a/src/locales/ja/move-trigger.ts b/src/locales/ja/move-trigger.ts index 290fe3e9340..20cf144a36d 100644 --- a/src/locales/ja/move-trigger.ts +++ b/src/locales/ja/move-trigger.ts @@ -61,6 +61,7 @@ export const moveTriggers: SimpleTranslationEntries = { "faintCountdown": "{{pokemonName}}は\n{{turnCount}}ターンごに ほろびてしまう!", "copyType": "{{pokemonName}}は {{targetPokemonName}}と\n同じタイプに なった!", "suppressAbilities": "{{pokemonName}}の とくせいが きかなくなった!", + "revivalBlessing": "{{pokemonName}}は\n復活して 戦えるようになった!", "swapArenaTags": "{{pokemonName}}は\nおたがいの ばのこうかを いれかえた!", "trickOnSwap": "{{pokemonNameWithAffix}} switched items with its target!", "trickFoeNewItem": "{{pokemonNameWithAffix}} obtained one {{itemName}}.", diff --git a/src/locales/ja/settings.ts b/src/locales/ja/settings.ts index ef20d071d2d..3be237e26ae 100644 --- a/src/locales/ja/settings.ts +++ b/src/locales/ja/settings.ts @@ -98,4 +98,9 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "コントローラーサポート", "showBgmBar": "Show Music Names", "shopOverlayOpacity": "Shop Overlay Opacity", + "shopCursorTarget": "Shop Cursor Target", + "items": "Items", + "reroll": "Reroll", + "shop": "Shop", + "checkTeam": "Check Team" } as const; diff --git a/src/locales/ko/ability-trigger.ts b/src/locales/ko/ability-trigger.ts index 974e6970569..9e330c176e7 100644 --- a/src/locales/ko/ability-trigger.ts +++ b/src/locales/ko/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}}[[는]] {{abilityName}}[[로]]\n상대에게 데미지를 입혔다!", "postFaintHpDamage": "{{pokemonNameWithAffix}}[[는]] {{abilityName}}[[로]]\n상대에게 데미지를 입혔다!", "postSummonPressure": "{{pokemonNameWithAffix}}[[는]]\n프레셔를 발산하고 있다!", + "weatherEffectDisappeared": "날씨의 영향이 없어졌다!", "postSummonMoldBreaker": "{{pokemonNameWithAffix}}의\n틀깨기!", "postSummonAnticipation": "{{pokemonNameWithAffix}}[[는]]\n몸을 떨었다!", "postSummonTurboblaze": "{{pokemonNameWithAffix}}[[는]]\n활활 타오르는 오라를 발산하고 있다!", diff --git a/src/locales/ko/battle-scene.ts b/src/locales/ko/battle-scene.ts new file mode 100644 index 00000000000..573e1791724 --- /dev/null +++ b/src/locales/ko/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "₽{{formattedMoney}}" +} as const; diff --git a/src/locales/ko/battle.ts b/src/locales/ko/battle.ts index fb070dde069..c3347e256f0 100644 --- a/src/locales/ko/battle.ts +++ b/src/locales/ko/battle.ts @@ -24,6 +24,7 @@ export const battle: SimpleTranslationEntries = { "hitResultSuperEffective": "효과가 굉장했다!", "hitResultNotVeryEffective": "효과가 별로인 듯하다…", "hitResultNoEffect": "{{pokemonName}}에게는\n효과가 없는 것 같다…", + "hitResultImmune": "{{pokemonName}}에게는\n전혀 효과가 없다!", "hitResultOneHitKO": "일격필살!", "attackFailed": "그러나 실패하고 말았다!!", "attackMissed": "{{pokemonNameWithAffix}}에게는\n맞지 않았다!", diff --git a/src/locales/ko/config.ts b/src/locales/ko/config.ts index 114950a4d35..2bc60f04bef 100644 --- a/src/locales/ko/config.ts +++ b/src/locales/ko/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const koConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/ko/dialogue.ts b/src/locales/ko/dialogue.ts index ce6af0e43e7..ed0b498abbc 100644 --- a/src/locales/ko/dialogue.ts +++ b/src/locales/ko/dialogue.ts @@ -2569,8 +2569,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}와… 정말 깔끔하게 당했네.\n초보자 맞아? $@c{smile}운이 따른 건지도 모르겠지만…\n그래도 정말 꿈을 이룰 지도. $그나저나, 박사님께서 이걸 전해달라고 하시더라.\n좋아 보이던데. - $@c{serious_smile_fists}아무튼, 힘 내는거야! - $@c{smile}아- 그리고 이벤트 즐겁게 보내!` + $@c{serious_smile_fists}아무튼, 힘 내는거야!` }, }, "rival_female": { @@ -2584,8 +2583,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}왜 벌써 이렇게 센 건데?!@d{96}\n@c{angry}아니면 뭔가 속임수, 그런 거? $@c{smile_wave_wink}농담, 농담!@d{64} @c{smile_eclosed}내가 진 거 맞아…\n너 정말 앞으로도 잘 하겠는데. $@c{smile}아 그래, 박사님께서 전해달라던 물건.\n도움이 되면 좋겠어! - $@c{smile_wave}항상 최선을 다 하라구! 믿고 있을게! - $@c{smile}아- 그리고 이벤트 즐겁게 보내!` + $@c{smile_wave}항상 최선을 다 하라구! 믿고 있을게!` }, }, "rival_2": { @@ -2601,7 +2599,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}그래, 또 지고 말았네.\n@c{serious_mopen_fists}좀 더 열심히 훈련을 해야겠어!\n $@c{smile}너한테 도움이 필요할 것 같지는 않지만, 이거.\n남는 게 있어서 말이야. $@c{serious_smile_fists}물론 이번이 마지막이야, 알겠지?\n공평하게 하지 않으면 그게 내 핑계거리가 되고 말거야. - $@c{smile}이제 갈게. 앞으로도 조심하고, 이벤트도 즐겁게 보내!` + $@c{smile}이제 갈게. 앞으로도 조심하고!` }, }, "rival_2_female": { @@ -2617,7 +2615,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}으, 그래. 더 열심히 훈련하면 되겠지! $@c{smile_wave}이것도 하나 더 챙겨왔으니 받아!\n@c{smile_wave_wink}감사 인사는 됐다구~. $@c{angry_mopen}하지만, 마지막이야!\n또 이렇게 공짜로 나눠주진 않을 테니까! - $@c{smile_wave}그럼! 이벤트 잘 즐기고!` + $@c{smile_wave}그럼!` }, "defeat": { 1: "가끔은 지는 것도 괜찮아…" diff --git a/src/locales/ko/move-trigger.ts b/src/locales/ko/move-trigger.ts index 78986115c11..3776a12aa27 100644 --- a/src/locales/ko/move-trigger.ts +++ b/src/locales/ko/move-trigger.ts @@ -8,7 +8,7 @@ export const moveTriggers: SimpleTranslationEntries = { "goingAllOutForAttack": "{{pokemonName}}[[는]]\n전력을 다하기 시작했다!", "regainedHealth": "{{pokemonName}}[[는]]\n기력을 회복했다!", "keptGoingAndCrashed": "{{pokemonName}}[[는]]\n의욕이 넘쳐서 땅에 부딪쳤다!", - "fled": "{{pokemonName}}[[는]]\N도망쳤다!", + "fled": "{{pokemonName}}[[는]]\n도망쳤다!", "cannotBeSwitchedOut": "{{pokemonName}}[[를]]\n돌아오게 할 수 없습니다!", "swappedAbilitiesWithTarget": "{{pokemonName}}[[는]]\n서로의 특성을 교체했다!", "coinsScatteredEverywhere": "돈이 주위에 흩어졌다!", @@ -61,6 +61,7 @@ export const moveTriggers: SimpleTranslationEntries = { "faintCountdown": "{{pokemonName}}[[는]]\n{{turnCount}}턴 후에 쓰러져 버린다!", "copyType": "{{pokemonName}}[[는]]\n{{targetPokemonName}}[[와]] 같은 타입이 되었다!", "suppressAbilities": "{{pokemonName}}의\n특성이 효과를 발휘하지 못하게 되었다!", + "revivalBlessing": "{{pokemonName}}[[는]]\n정신을 차려 싸울 수 있게 되었다!", "swapArenaTags": "{{pokemonName}}[[는]]\n서로의 필드 효과를 교체했다!", "trickOnSwap": "{{pokemonNameWithAffix}}[[는]] 서로의\n도구를 교체했다!", "trickFoeNewItem": "{{pokemonNameWithAffix}}[[는]]\n{{itemName}}[[를]] 손에 넣었다!", diff --git a/src/locales/ko/settings.ts b/src/locales/ko/settings.ts index aa4adfc4e41..38b0679d911 100644 --- a/src/locales/ko/settings.ts +++ b/src/locales/ko/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "게임패드 지원", "showBgmBar": "BGM 제목 보여주기", "moveTouchControls": "터치 컨트롤 이동", - "shopOverlayOpacity": "상점 오버레이 투명도" + "shopOverlayOpacity": "상점 오버레이 투명도", + "shopCursorTarget": "상점 커서 위치", + "items": "아이템", + "reroll": "갱신", + "shop": "상점", + "checkTeam": "파티 확인" } as const; diff --git a/src/locales/pt_BR/ability-trigger.ts b/src/locales/pt_BR/ability-trigger.ts index da91fa3213f..9cfa42edce3 100644 --- a/src/locales/pt_BR/ability-trigger.ts +++ b/src/locales/pt_BR/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{abilityName}} de {{pokemonNameWithAffix}}\nferiu seu adversário!", "postFaintHpDamage": "{{abilityName}} de {{pokemonNameWithAffix}}\nferiu seu adversário!", "postSummonPressure": "{{pokemonNameWithAffix}} está exercendo sua pressão!", + "weatherEffectDisappeared": "Os efeitos do clima desapareceram.", "postSummonMoldBreaker": "{{pokemonNameWithAffix}} quebra o molde!", "postSummonAnticipation": "{{pokemonNameWithAffix}} se arrepiou!", "postSummonTurboblaze": "{{pokemonNameWithAffix}} está irradiando uma aura ardente!", diff --git a/src/locales/pt_BR/battle-scene.ts b/src/locales/pt_BR/battle-scene.ts new file mode 100644 index 00000000000..573e1791724 --- /dev/null +++ b/src/locales/pt_BR/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "₽{{formattedMoney}}" +} as const; diff --git a/src/locales/pt_BR/battle.ts b/src/locales/pt_BR/battle.ts index db3d07131a4..8581474a553 100644 --- a/src/locales/pt_BR/battle.ts +++ b/src/locales/pt_BR/battle.ts @@ -24,6 +24,7 @@ export const battle: SimpleTranslationEntries = { "hitResultSuperEffective": "É supereficaz!", "hitResultNotVeryEffective": "É pouco eficaz...", "hitResultNoEffect": "Isso não afeta {{pokemonName}}!", + "hitResultImmune": "{{pokemonName}} não é afetado!", "hitResultOneHitKO": "Foi um nocaute de um golpe!", "attackFailed": "Mas falhou!", "attackMissed": "{{pokemonNameWithAffix}} desviou do ataque!", diff --git a/src/locales/pt_BR/config.ts b/src/locales/pt_BR/config.ts index b48fcfdc8d8..9ebb4867ae4 100644 --- a/src/locales/pt_BR/config.ts +++ b/src/locales/pt_BR/config.ts @@ -4,6 +4,7 @@ import { PGFachv, PGMachv } from "./achv"; import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const ptBrConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/pt_BR/dialogue.ts b/src/locales/pt_BR/dialogue.ts index cb0c05fab45..076ca00d951 100644 --- a/src/locales/pt_BR/dialogue.ts +++ b/src/locales/pt_BR/dialogue.ts @@ -2541,8 +2541,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}Caramba… Você me limpou.\nVocê é mesmo um novato? $@c{smile}Talvez tenha sido um pouco de sorte, mas…\nQuem sabe você consiga chegar até o fim. $Aliás, o professor me pediu para te dar esses itens. Eles parecem bem legais. - $@c{serious_smile_fists}Boa sorte lá fora! - $@c{smile}Ah- e eu espero que você aproveite o evento!` + $@c{serious_smile_fists}Boa sorte lá fora!` }, }, "rival_female": { @@ -2556,8 +2555,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: `@c{shock}Você acabou de começar e já está tão forte?!@d{96}\n@c{angry}Você trapaceou, não foi? $@c{smile_wave_wink}Brincadeirinha!@d{64} @c{smile_eclosed}Eu perdi de forma justa… Tenho a sensação de que você vai se sair muito bem lá fora. $@c{smile}Aliás, o professor pediu para eu te dar alguns itens. Espero que sejam úteis! - $@c{smile_wave}Dê o seu melhor, como sempre! Eu acredito em você! - $@c{smile}Ah- e eu espero que você aproveite o evento!` + $@c{smile_wave}Dê o seu melhor, como sempre! Eu acredito em você!` }, }, "rival_2": { @@ -2573,7 +2571,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $@c{smile}Tudo bem, no entanto. Eu imaginei que isso poderia acontecer.\n@c{serious_mopen_fists}Isso só significa que preciso me esforçar mais para a próxima vez!\n $@c{smile}Ah, não que você precise realmente de ajuda, mas eu tinha um extra desses itens e pensei que você poderia querer. $@c{serious_smile_fists}Não espere outro depois deste!\nNão posso continuar dando vantagem ao meu oponente. - $@c{smile}Enfim, cuide-se, e aproveite o evento!` + $@c{smile}Enfim, cuide-se!` }, }, "rival_2_female": { @@ -2587,9 +2585,9 @@ export const PGMdialogue: DialogueTranslationEntries = { "victory": { 1: `@c{neutral}Eu… não era para eu perder dessa vez… $@c{smile}Ah bem. Isso só significa que vou ter que treinar ainda mais para a próxima vez! - $@c{smile_wave}Também consegui mais dois desses para você!\n@c{smile_wave_wink}Não precisa me agradecer~. - $@c{angry_mopen}Estes são os últimos, hein! Você não vai ganhar mais nenhum presente de mim depois desse! - $@c{smile_wave}Continue assim, e aproveite o evento!` + $@c{smile_wave}Também consegui mais um desses para você!\n@c{smile_wave_wink}Não precisa me agradecer~. + $@c{angry_mopen}Este é o último, hein! Você não vai ganhar mais nenhum presente de mim depois desse! + $@c{smile_wave}Continue assim!` }, "defeat": { 1: "Está tudo bem perder às vezes…" @@ -2779,6 +2777,90 @@ export const PGFdialogue: DialogueTranslationEntries = { 9: "Estou realmente cansando de batalhar… Deve haver algo novo para fazer…" } }, + "breeder": { + "encounter": { + 1: "Pokémon obedientes, Pokémon egoístas… Pokémon têm características únicas.", + 2: "Embora minha criação e comportamento sejam ruins, criei meus Pokémon bem.", + 3: "Hmm, você disciplina seus Pokémon? Mimar demais não é bom." + }, + "victory": { + 1: "É importante nutrir e treinar as características de cada Pokémon.", + 2: "Ao contrário do meu lado diabólico, esses são bons Pokémon.", + 3: "Muito elogio pode estragar tanto Pokémon quanto pessoas." + }, + "defeat": { + 1: "Você não deve ficar com raiva dos seus Pokémon, mesmo se perder uma batalha.", + 2: "Certo? Pokémon bons, né? Eu sou adequado para criar coisas.", + 3: "Não importa o quanto você ame seus Pokémon, ainda precisa discipliná-los quando se comportam mal." + } + }, + "breeder_female": { + "encounter": { + 1: "Pokémon nunca te traem. Eles retribuem todo o amor que você dá a eles.", + 2: "Quer uma dica para treinar bons Pokémon?", + 3: "Eu criei esses Pokémon muito especiais usando um método especial." + }, + "victory": { + 1: "Ugh… Não era para ser assim. Será que administrei a mistura errada?", + 2: "Como isso aconteceu com meus Pokémon… O que você está dando de comer aos seus Pokémon?", + 3: "Se eu perder, isso significa que eu estava só matando o tempo. Não machuca meu ego nem um pouco." + }, + "defeat": { + 1: "Isso prova que meus Pokémon aceitaram meu amor.", + 2: "O verdadeiro truque para treinar bons Pokémon é capturar bons Pokémon.", + 3: "Pokémon serão fortes ou fracos dependendo de como você os cria." + } + }, + "fisherman": { + "encounter": { + 1: "Anem! Você me fez perder uma fisgada!\nO que vai fazer sobre isso?", + 2: "Sai daqui! Você está assustando os Pokémon!", + 3: "Vamos ver se você consegue fisgar uma vitória!", + }, + "victory": { + 1: "Esqueça isso.", + 2: "Da próxima vez, eu vou pescar a vitória!", + 3: "Acho que subestimei a força das correntes dessa vez.", + }, + }, + "fisherman_female": { + "encounter": { + 1: "Uau! Peguei um grande!", + 2: "Linha lançada, pronta para pescar o sucesso!", + 3: "Pronta para fazer ondas!" + }, + "victory": { + 1: "Vou voltar com um anzol mais forte.", + 2: "Vou pescar a vitória na próxima vez.", + 3: "Estou só afiando meus anzóis para a revanche!" + }, + }, + "swimmer": { + "encounter": { + 1: "Hora de mergulhar!", + 2: "Vamos surfar nas ondas da vitória!", + 3: "Pronto para fazer um splash!", + }, + "victory": { + 1: "Molhado na derrota!", + 2: "Uma onda de derrota!", + 3: "De volta à praia, eu acho.", + }, + }, + "backpacker": { + "encounter": { + 1: "Prepare-se, vamos começar!", + 2: "Vamos ver se você consegue acompanhar!", + 3: "Prepare-se, desafiante!", + 4: "Passei 20 anos tentando me encontrar… Mas onde estou?" + }, + "victory": { + 1: "Dessa vez tropecei!", + 2: "Ah, acho que estou perdido.", + 3: "Caminho sem saída!", + 4: "Espere um segundo! Ei! Você não sabe quem eu sou?" + }, + }, "ace_trainer": { "encounter": { 1: "Você parece bastante confiante.", @@ -2799,6 +2881,14 @@ export const PGFdialogue: DialogueTranslationEntries = { 4: "Claro que sou forte e não perco. É importante ganhar com graça." } }, + "parasol_lady": { + "encounter": { + 1: "Hora de embelezar o campo de batalha com elegância e postura!", + }, + "victory": { + 1: "Minha elegância permanece inabalável!", + } + }, "twins": { "encounter": { 1: "Prepare-se, porque quando nos unimos, é o dobro do problema!", @@ -2816,6 +2906,18 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Dobro de sorrisos, dobro da dança da vitória!" } }, + "cyclist": { + "encounter": { + 1: "Prepare-se para comer poeira!", + 2: "Prepare-se, desafiante! Estou prestes a te deixar para trás!", + 3: "Pé no pedal, vamos ver se você consegue acompanhar!" + }, + "victory": { + 1: "As rodas podem estar paradas, mas a determinação continua a pedalar.", + 2: "Fui mais rápido!", + 3: "O caminho para a vitória tem muitas curvas e voltas para explorar." + }, + }, "black_belt": { "encounter": { 1: "Elogio sua coragem ao me desafiar! Pois eu sou o que tem o chute mais forte!", @@ -2826,6 +2928,100 @@ export const PGFdialogue: DialogueTranslationEntries = { 2: "Hmmm… Se eu ia perder de qualquer maneira, esperava ficar totalmente destruído no processo." }, }, + "battle_girl": { + "encounter": { + 1: "Você não precisa tentar me impressionar. Você pode perder contra mim.", + }, + "victory": { + 1: "É difícil dizer adeus, mas estamos ficando sem tempo…", + }, + }, + "hiker": { + "encounter": { + 1: "Minha barriga de meia-idade me deu tanta gravidade quanto as montanhas que eu escalo!", + 2: "Herdei esse corpo ossudo dos meus pais… Sou como uma cadeia de montanhas viva…", + }, + "victory": { + 1: "Pelo menos não posso perder quando se trata de IMC!", + 2: "Não é suficiente… Nunca é suficiente. Meu colesterol ruim não está alto o suficiente…" + }, + }, + "ranger": { + "encounter": { + 1: "Quando estou cercado pela natureza, a maioria das outras coisas deixa de importar.", + 2: "Quando estou vivendo sem natureza na minha vida, às vezes sinto uma crise de ansiedade se aproximando." + }, + "victory": { + 1: "Não importa para a vastidão da natureza se eu ganhar ou perder…", + 2: "Algo assim é bastante trivial comparado aos sentimentos sufocantes da vida na cidade." + }, + "defeat": { + 1: "Ganhei a batalha. Mas a vitória não é nada comparada à vastidão da natureza…", + 2: "Tenho certeza de que como você se sente não é tão ruim se comparar aos meus ataques de ansiedade…" + } + }, + "scientist": { + "encounter": { + 1: "Minha pesquisa levará este mundo à paz e alegria.", + }, + "victory": { + 1: "Sou um gênio… Não devo perder para alguém como você…", + }, + }, + "school_kid": { + "encounter": { + 1: "Heehee. Estou confiante nos meus cálculos e análises.", + 2: "Estou ganhando o máximo de experiência que posso porque quero ser um Líder de Ginásio um dia." + }, + "victory": { + 1: "Aff… Cálculo e análise talvez não sejam páreo para o acaso…", + 2: "Até experiências difíceis e desafiadoras têm seu propósito, eu acho." + } + }, + "artist": { + "encounter": { + 1: "Eu costumava ser popular, mas agora estou acabado.", + }, + "victory": { + 1: "À medida que os tempos mudam, os valores também mudam. Percebi isso tarde demais.", + }, + }, + "guitarist": { + "encounter": { + 1: "Prepare-se para sentir o ritmo da derrota enquanto eu toco minha vitória!", + }, + "victory": { + 1: "Silenciado por agora, mas minha melodia de resiliência continuará a tocar.", + }, + }, + "worker": { + "encounter": { + 1: "Me incomoda que as pessoas sempre me entendam mal. Sou muito mais puro do que todos pensam.", + }, + "victory": { + 1: "Eu realmente não quero que minha pele queime, então quero ficar na sombra enquanto trabalho.", + }, + }, + "worker_female": { + "encounter": { + 1: `Me incomoda que as pessoas sempre me entendam mal. + $Sou muito mais pura do que todos pensam.` + }, + "victory": { + 1: "Eu realmente não quero que minha pele queime, então quero ficar na sombra enquanto trabalho." + }, + "defeat": { + 1: "Meu corpo e mente nem sempre estão necessariamente em sincronia." + } + }, + "worker_double": { + "encounter": { + 1: "Vou te mostrar que podemos te quebrar. Estamos treinando no campo!", + }, + "victory": { + 1: "Que estranho… Como isso pode ser… Não deveria ter sido superado.", + }, + }, "hex_maniac": { "encounter": { 1: "Normalmente, só escuto música clássica, mas se eu perder, acho que vou tentar um pouco de new age!", @@ -2840,6 +3036,32 @@ export const PGFdialogue: DialogueTranslationEntries = { 2: "Não fique presa na tristeza ou frustração. Você pode usar seus rancores para se motivar." } }, + "psychic": { + "encounter": { + 1: "Oi! Concentre-se!", + }, + "victory": { + 1: "Perdi minha concentração!", + }, + }, + "officer": { + "encounter": { + 1: "Prepare-se, porque a justiça está prestes a ser servida!", + 2: "Pronto para defender a lei e servir a justiça no campo de batalha!" + }, + "victory": { + 1: "O peso da justiça parece mais pesado do que nunca…", + 2: "As sombras da derrota pairam no distrito." + } + }, + "beauty": { + "encounter": { + 1: "Minha última batalha… É assim que eu gostaria que víssemos esta partida…", + }, + "victory": { + 1: "Foi divertido… Vamos ter outra última batalha algum dia…", + }, + }, "baker": { "encounter": { 1: "Espero que esteja pronta para saborear a derrota!" @@ -2848,6 +3070,26 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Vou assar uma revanche." }, }, + "biker": { + "encounter": { + 1: "Hora de acelerar e te deixar na poeira!" + }, + "victory": { + 1: "Vou me ajustar para a próxima corrida." + }, + }, + "firebreather": { + "encounter": { + 1: "Minhas chamas irão te consumir!", + 2: "Minha alma está pegando fogo. Irei te mostrar como queima!", + 3: "Cola aqui e dá uma olhada!" + }, + "victory": { + 1: "Fui reduzido a cinzas…", + 2: "Uau! Isso foi quente!", + 3: "Ai! Queimei minha língua!" + }, + }, "sailor": { "encounter": { 1: "Mano, você vai andar na prancha se perder!", @@ -2860,6 +3102,18 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Estou achando que quem tá enjoado sou eu..." }, }, + "archer": { + "encounter": { + 1: "Antes de você ir mais longe, vamos ver como você se sai contra nós, Equipe Rocket!", + 2: "Eu tenho recebido relatórios de que suas habilidades não são insignificantes. Vamos ver se são verdadeiros.", + 3: "Eu sou Archer, um Admin da Equipe Rocket. E não tenho piedade dos inimigos da nossa organização." + }, + "victory": { + 1: "Que vexame!", + 2: "Com minhas habilidades atuais, eu não estava à altura da tarefa, afinal.", + 3: "M-me perdoe, Giovanni... Por ser derrotado por um mero treinador..." + }, + }, "ariana": { "encounter": { 1: "Pera aí! Não podemos deixar alguém solto por aí. Isso é prejudicial para o orgulho da Equipe Rocket, entende?", @@ -2872,6 +3126,30 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Aaaieeeee! Isso não pode estar acontecendo! Eu lutei muito, mas ainda perdi…" }, }, + "proton": { + "encounter": { + 1: "O que você quer? Se você interromper nosso trabalho, não espere misericórdia!", + 2: "O que temos aqui? Costumam me chamar de o cara mais assustador e cruel da Equipe Rocket… Eu recomendo fortemente que você não interfira nos nossos negócios!", + 3: "Eu sou Proton, um Admin da Equipe Rocket. Estou aqui para acabar com a sua intromissão!" + }, + "victory": { + 1: "A fortaleza caiu!", + 2: "Você pode ter vencido desta vez… Mas tudo o que fez foi aumentar a ira da Equipe Rocket…", + 3: "Fui derrotado… Mas não esquecerei disso!" + }, + }, + "petrel": { + "encounter": { + 1: "Muhahaha, estávamos esperando por você. Eu? Você não sabe quem eu sou? Sou eu, Giovanni. O majestoso Giovanni em pessoa! Wahahaha! ...Huh? Eu não pareço nada com Giovanni? Eu nem mesmo pareço com Giovanni? Como assim? Trabalhei tanto para imitá-lo!", + 2: "Eu sou Petrel, um Admin da Equipe Rocket. Não permitirei que você interfira em nossos planos!", + 3: "O Executivo da Rocket, Petrel, vai lidar com este intruso!" + }, + "victory": { + 1: "OK, OK. Vou te contar onde ele está.", + 2: "Eu... Eu não consegui fazer nada... Giovanni, por favor, me perdoe...", + 3: "Não, eu não posso deixar isso me afetar. Tenho que informar os outros…" + }, + }, "tabitha": { "encounter": { 1: "Hehehe! Então você veio até aqui! Mas você chegou tarde demais!", @@ -2884,6 +3162,18 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Ahya! Como isso pode ser? Para um Admin como eu perder para uma treinadora qualquer..." }, }, + "courtney": { + "encounter": { + 1: "A coisa... A coisa que você segura... É o que... É o que nós da Equipe Magma procuramos...", + 2: "... Bem então... Deletando...", + 3: "...Ha. ...Analisando... ...Hah♪" + }, + "victory": { + 1: "... ...Mudar...o mundo.", + 2: "Como antecipado. Não antecipado. Você. Bloqueio de alvo... concluído. Iniciando... experimento. Você. Para sempre. Aha... ♪", + 3: "... De novo? Isso não foi antecipado. ...Eu sabia. Você... é interessante! ...Haha. ♪" + }, + }, "shelly": { "encounter": { 1: "Ahahahaha! Você vai se meter nos assuntos da Equipe Aqua? Você é absolutamente destemida, simplesmente ignorante ou ambos! Você é tão fofa que chega a ser nojenta! Vou te derrubar", @@ -2920,6 +3210,30 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Derrotada... Este foi um erro caro." } }, + "jupiter": { + "encounter": { + 1: "Júpiter, Comandante da Equipe Galáctica, ao seu serviço.", + 2: "A resistência é inútil. A Equipe Galáctica prevalecerá!", + 3: "Você está tremendo... já está com medo?" + }, + "victory": { + 1: "De jeito nenhum... Eu perdi?!", + 2: "Impressionante, você tem coragem!", + 3: "Perder assim... Que embaraço." + } + }, + "saturn": { + "encounter": { + 1: "Eu sou Saturno, Comandante da Equipe Galáctica.", + 2: "Nossa missão é absoluta. Qualquer obstáculo será obliterado!", + 3: "É medo o que vejo em seus olhos?" + }, + "victory": { + 1: "Impossível... Derrotado por você?!", + 2: "Você provou ser um adversário digno.", + 3: "Derrotado... Isso é inaceitável." + } + }, "zinzolin": { "encounter": { 1: "Você poderia se tornar uma ameaça para a Equipe Plasma, então vamos eliminá-la aqui e agora!", @@ -2984,6 +3298,38 @@ export const PGFdialogue: DialogueTranslationEntries = { 5: "Você diz o que? Equipe Rocket tchau-tchau a vai-vai? Quebrado é diz você?" // Uso de gramática incorreta é proposital }, }, + "magma_grunt": { + "encounter": { + 1: "Se você se meter com a Equipe Magma, não teremos piedade!", + 2: "É melhor você não interferir em nossos planos! Estamos tornando o mundo um lugar melhor!", + 3: "Você está no caminho! A Equipe Magma não tem tempo para crianças como você!", + 4: "Espero que você tenha trazido marshmallows porque as coisas estão prestes a esquentar!", + 5: "Vamos usar o poder de um vulcão! Vai ser... explosivo! Entendeu? Heh heh!" + }, + "victory": { + 1: "Ahn? Eu perdi?!", + 2: "Não posso acreditar que perdi! Até pulei o almoço por isso.", + 3: "De jeito nenhum! Você é apenas uma criança!", + 4: "Urrrgh... Eu deveria ter me escondido em nosso esconderijo imediatamente...", + 5: "Você me venceu... Você acha que o chefe vai cortar meu salário por isso?" + }, + }, + "aqua_grunt": { + "encounter": { + 1: "Não pegamos leve com quem se mete com a Equipe Aqua, nem mesmo crianças!", + 2: "Grrr... Você tem coragem de se intrometer com a Equipe Aqua!", + 3: "Você está prestes a se molhar! E não apenas por causa dos meus Pokémon aquáticos!", + 4: "Nós, da Equipe Aqua, existimos para o bem de todos!", + 5: "Prepare-se para ser levado pelas ondas do meu... uh, Pokémon! Sim, meu Pokémon!" + }, + "victory": { + 1: "Tá de brincadeira!", + 2: "Arrgh, eu não contei que seria atrapalhado por uma criança intrometida!", + 3: "Eu perdi?! Acho que vou ter que nadar de volta para o esconderijo agora...", + 4: "Oh, cara, que desastre... O chefe vai ficar furioso...", + 5: "Você me venceu... Você acha que o chefe vai me fazer andar na prancha por isso?" + }, + }, "galactic_grunt": { "encounter": { 1: "Não mexa com a Equipe Galáctica!", @@ -3000,6 +3346,104 @@ export const PGFdialogue: DialogueTranslationEntries = { 5: "Nota para mim mesmo: praticar batalhas Pokémon, o mais rápido possível." }, }, + "plasma_grunt": { + "encounter": { + 1: "Não toleramos pessoas que pensam diferente de nós!", + 2: "Se eu ganhar de você, liberte seus Pokémon!", + 3: "Se você atrapalhar a Equipe Plasma, eu cuidarei de você!", + 4: "A Equipe Plasma vai libertar os Pokémon de humanos egoístas como você!", + 5: "Nossos penteados são de outro mundo... mas nossas habilidades de batalha? Você descobrirá em breve." + }, + "victory": { + 1: "Plasmaaaaaaaaa!", + 2: "Como eu pude perder...", + 3: "...Que Pokémon fraco, vou ter que roubar alguns melhores!", + 4: "Grandes planos são sempre interrompidos.", + 5: "Isso é ruim... Ruim ruim ruim ruim ruim ruim ruim! Ruim para a Equipe Plasma! Ou Plasruim, para abreviar!" + }, + }, + "flare_grunt": { + "encounter": { + 1: "Seus Pokémon não são páreo para a elegância da Equipe Flare.", + 2: "Espero que você tenha trazido seus óculos de sol, porque as coisas vão ficar brilhantes!", + 3: "A Equipe Flare vai purificar o mundo da imperfeição!", + 4: "Prepare-se para enfrentar o brilho da Equipe Flare!", + 5: "A moda é o mais importante para nós!" + }, + "victory": { + 1: "O futuro não parece brilhante para mim.", + 2: "Talvez haja mais na batalha do que eu pensei. De volta à prancheta.", + 3: "Gahh?! Eu perdi?!", + 4: "Mesmo na derrota, a elegância da Equipe Flare brilha.", + 5: "Você pode ter me vencido, mas quando eu perco, eu saio com estilo!" + }, + }, + "rocket_boss_giovanni_1": { + "encounter": { + 1: "Tenho que admitir, estou impressionado que tenha chegado até aqui!" + }, + "victory": { + 1: "QUÊ! Isso não é possível!" + }, + "defeat": { + 1: "Guarde minhas palavras.\nNão ser capaz de medir sua própria força mostra que você ainda é uma criança." + } + }, + "rocket_boss_giovanni_2": { + "encounter": { + 1: "Meus antigos associados precisam de mim... Você vai ficar no meu caminho?" + }, + "victory": { + 1: "Como isso é possível...?\nO precioso sonho da Equipe Rocket se tornou pouco mais que uma ilusão..." + }, + "defeat": { + 1: "A Equipe Rocket renascerá, e eu dominarei o mundo!" + } + }, + "magma_boss_maxie_1": { + "encounter": { + 1: "Eu vou te enterrar com minhas próprias mãos.\nEspero que você aprecie essa honra!" + }, + "victory": { + 1: "Ugh! Você é... bastante capaz...\nEu fiquei para trás, mas apenas por um triz..." + }, + "defeat": { + 1: "A Equipe Magma vai prevalecer!" + } + }, + "magma_boss_maxie_2": { + "encounter": { + 1: "Você é o último obstáculo entre mim e meus objetivos.\nPrepare-se para meu ataque final! Fuhahaha!" + }, + "victory": { + 1: "Isso... Isso não é... Ngh..." + }, + "defeat": { + 1: "E agora... Eu transformarei este planeta em uma terra ideal para a humanidade." + } + }, + "aqua_boss_archie_1": { + "encounter": { + 1: "Eu sou o líder da Equipe Aqua, então temo que esse seja o fim da linha para você." + }, + "victory": { + 1: "Vamos nos encontrar de novo em algum lugar. Eu vou ter certeza de lembrar desse rosto." + }, + "defeat": { + 1: "Brilhante! Nada vai parar minha equipe agora!" + } + }, + "aqua_boss_archie_2": { + "encounter": { + 1: "Estive esperando tanto tempo por este dia.\nEste é o verdadeiro poder da minha equipe!" + }, + "victory": { + 1: "Como eu suspeitava..." + }, + "defeat": { + 1: "Eu vou voltar tudo neste mundo ao seu estado puro e original!!" + } + }, "galactic_boss_cyrus_1": { "encounter": { 1: "Você foi compelida a vir aqui por tal sentimentalismo vazio\nEu farei você se arrepender de ter ouvido seu coração!" @@ -3011,6 +3455,78 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Eu criarei meu novo mundo..." } }, + "galactic_boss_cyrus_2": { + "encounter": { + 1: "Nos encontramos novamente. Parece que nossos destinos estão entrelaçados.\nMas aqui e agora, eu finalmente quebrarei esse vínculo!" + }, + "victory": { + 1: "Como? Como? COMO?!" + }, + "defeat": { + 1: "Até logo." + } + }, + "plasma_boss_ghetsis_1": { + "encounter": { + 1: "Ninguém pode me deter! Não importa quem seja ou o que faça!" + }, + "victory": { + 1: "Como isso é possível? Eu sou o criador da Equipe Plasma! Eu sou perfeito!" + }, + "defeat": { + 1: "Eu sou o governante perfeito de um novo mundo perfeito! Mwa ha ha!" + } + }, + "plasma_boss_ghetsis_2": { + "encounter": { + 1: "Vamos! Eu quero ver sua cara depois que você perder toda a esperança!" + }, + "victory": { + 1: "Meus cálculos... Não! Meus planos cuidadosos! O mundo deveria ser meu!" + }, + "defeat": { + 1: "Kyurem! Use Absofusion!" + } + }, + "flare_boss_lysandre_1": { + "encounter": { + 1: "Você está aqui para me deter? Mostre-me em batalha." + }, + "victory": { + 1: "Você está aqui para me deter. Mas eu peço que você espere." + }, + "defeat": { + 1: "Pokémon... não devem mais existir." + } + }, + "flare_boss_lysandre_2": { + "encounter": { + 1: "O futuro que você quer, ou o futuro que eu quero... Vamos ver qual é o mais merecedor, não é mesmo?" + }, + "victory": { + 1: "Uau!" + }, + "defeat": { + 1: "Tolos sem visão continuarão a poluir este belo mundo." + } + }, + "brock": { + "encounter": { + 1: "Minha especialidade em Pokémon do tipo Pedra vai te derrubar! Vamos lá!", + 2: "Minha vontade firme como pedra vai te sobrecarregar!", + 3: "Permita-me mostrar a verdadeira força dos meus Pokémon!" + }, + "victory": { + 1: "A força dos seus Pokémon superou minhas defesas de pedra!", + 2: "O mundo é enorme! Estou feliz por ter tido a chance de batalhar com você.", + 3: "Talvez eu deva voltar a perseguir meu sonho de ser Criador de Pokémon…" + }, + "defeat": { + 1: "A melhor defesa é um bom ataque!\nEssa é a minha maneira de fazer as coisas!", + 2: "Venha estudar rochas comigo da próxima vez para aprender melhor a combatê-las!", + 3: "Hah, todas as minhas viagens pelas regiões estão valendo a pena!" + } + }, "misty": { "encounter": { 1: "Minha política é um ataque total com Pokémon do tipo Água!", @@ -3045,6 +3561,77 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Uma batalha de Pokémon é guerra, e eu te mostrei combate em primeira mão!" } }, + "erika": { + "encounter": { + 1: "Ah, o tempo está adorável aqui…\nOh, uma batalha? Muito bem então.", + 2: "Minhas habilidades de batalha Pokémon rivalizam com minhas habilidades de arranjo de flores.", + 3: "Oh, espero que o aroma agradável dos meus Pokémon não me faça dormir de novo…", + 4: "Ver flores em um jardim é tão calmante." + }, + "victory": { + 1: "Oh! Eu concedo a derrota.", + 2: "Aquela partida foi muito agradável.", + 3: "Ah, parece que perdi…", + 4: "Oh, meu Deus." + }, + "defeat": { + 1: "Tinha medo de adormecer…", + 2: "Oh, meu Deus, parece que meus Pokémon de Grama te dominaram.", + 3: "Essa batalha foi uma experiência tão calmante.", + 4: "Oh… É só isso?" + } + }, + "janine": { + "encounter": { + 1: "Estou dominando a arte dos ataques venenosos.\nVou lutar com você hoje!", + 2: "Meu pai confia que posso me defender.\nVou provar que ele está certo!", + 3: "Minhas técnicas de ninja só perdem para as do meu pai!\nVocê consegue acompanhar?" + }, + "victory": { + 1: "Ainda preciso de treinamento… Entendi.", + 2: "Sua técnica de batalha superou a minha.", + 3: "Vou me aplicar de verdade e melhorar minhas habilidades." + }, + "defeat": { + 1: "Hehe… o veneno drenou todas as suas forças para lutar.", + 2: "Ha! Você não teve chance contra minhas habilidades superiores de ninja!", + 3: "A fé do meu pai em mim não foi mal colocada." + } + }, + "sabrina": { + "encounter": { + 1: "Através da minha habilidade psíquica, tive uma visão da sua chegada!", + 2: "Não gosto de lutar, mas se você quiser, vou mostrar meus poderes!", + 3: "Posso sentir grande ambição em você. Vou ver se não é infundada." + }, + "victory": { + 1: "Seu poder… Ele supera o que eu previa…", + 2: "Não consegui prever seu poder com precisão.", + 3: "Mesmo com meus imensos poderes psíquicos, não consigo sentir outro tão forte quanto você." + }, + "defeat": { + 1: "Essa vitória… É exatamente como previ nas minhas visões!", + 2: "Talvez fosse outra pessoa que eu sentisse um grande desejo…", + 3: "Aprimore suas habilidades antes de entrar em batalha precipitadamente.\nVocê nunca sabe o que o futuro pode reservar se fizer isso…" + } + }, + "blaine": { + "encounter": { + 1: "Hah! Espero que tenha trazido uma Cura de Queimadura!", + 2: "Meus Pokémon de Fogo vão incinerar todos os desafiantes!", + 3: "Prepare-se para brincar com fogo!" + }, + "victory": { + 1: "Queimei até não restar nada! Nem cinzas sobraram!", + 2: "Não acendi as chamas alto o suficiente?", + 3: "Estou completamente exausto… Mas isso faz minha motivação para melhorar queimar ainda mais!" + }, + "defeat": { + 1: "Meu inferno ardente não pode ser apagado!", + 2: "Meus Pokémon foram fortalecidos com o calor desta vitória!", + 3: "Hah! Minha paixão queima mais do que a sua!" + } + }, "giovanni": { "encounter": { 1: "Eu, o líder da Equipe Rocket, vou te fazer sentir um mundo de dor!", @@ -3062,6 +3649,23 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Espero que entenda o quão tolo foi me desafiar." } }, + "roxanne": { + "encounter": { + 1: "Você poderia gentilmente demonstrar como batalha?", + 2: "Você pode aprender muitas coisas batalhando com muitos treinadores.", + 3: "Oh, você me pegou estrategizando.\nGostaria de batalhar?" + }, + "victory": { + 1: "Oh, parece que perdi.\nEu entendo.", + 2: "Parece que ainda tenho muito mais a aprender quando se trata de batalhas.", + 3: "Vou levar o que aprendi aqui hoje a sério." + }, + "defeat": { + 1: "Aprendi muitas coisas com nossa batalha.\nEspero que você também tenha aprendido.", + 2: "Espero batalhar com você novamente.\nEspero que use o que aprendeu aqui.", + 3: "Venci devido a tudo o que aprendi." + } + }, "brawly": { "encounter": { 1: "Oh cara, uma desafiante!\nVamos ver o que você pode fazer!", @@ -3096,6 +3700,40 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Wahahahaha! Que batalha eletrizante!" } }, + "flannery": { + "encounter": { + 1: "Meus Pokémon de fogo estão prontos para queimar a concorrência!\nVamos nessa!", + 2: "Prepare-se para sentir o calor da minha determinação!\nNão vou segurar nada!", + 3: "Minhas habilidades vão incinerar você!\nPrepare-se para a batalha mais quente da sua vida!" + }, + "victory": { + 1: "Essa derrota só faz minha determinação queimar mais!", + 2: "Essa perda não apagará minhas chamas!\nEstarei de volta mais forte!", + 3: "Vou usar essa experiência para reacender meu espírito competitivo!" + }, + "defeat": { + 1: "Minhas chamas nunca se apagarão!\nSou muito apaixonada por isso!", + 2: "Você foi incrível!\nVamos fazer isso de novo algum dia!", + 3: "Que batalha ardente!\nMal posso esperar pela próxima!" + } + }, + "norman": { + "encounter": { + 1: "Você está pronto para enfrentar a força pura do meu time?\nVou te mostrar o poder do equilíbrio!", + 2: "Minha experiência em batalha vai fazer você suar!\nPrepare-se!", + 3: "Treinei meu time rigorosamente.\nVamos ver se você consegue igualar!" + }, + "victory": { + 1: "Parece que subestimei você.\nFoi uma batalha dura.", + 2: "Você é forte, mas ainda há muito para aprender.", + 3: "Essa derrota não abalará minha determinação.\nEstarei de volta mais forte!" + }, + "defeat": { + 1: "Você lutou bravamente!\nEspero batalhar com você novamente.", + 2: "Sua força é incrível!\nNão posso esperar pela nossa próxima batalha.", + 3: "Foi uma honra batalhar com você!\nAté a próxima!" + } + }, "winona": { "encounter": { 1: "Tenho sobrevoado os céus em busca de presas...\nE você é meu alvo!", @@ -3147,6 +3785,60 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Tudo graças ao meu treinamento rigoroso com Tate.\nPosso me sincronizar com meus Pokémon." } }, + "juan": { + "encounter": { + 1: "Agora não é hora de agir timidamente.\nVamos batalhar!", + 2: "Ahahaha, você será testemunha da minha arte com Pokémon de Água!", + 3: "Um tufão se aproxima!\nVocê será capaz de me testar?", + 4: "Por favor, você será testemunha da nossa arte.\nUma grande ilusão de água esculpida por meus Pokémon e por mim!" + }, + "victory": { + 1: "Você pode ser um gênio que pode enfrentar Wallace!", + 2: "Eu me concentrei na elegância enquanto você treinava.\nÉ natural que você me derrotasse.", + 3: "Ahahaha!\nMuito bem, você venceu desta vez.", + 4: "De você, sinto o brilho brilhante da habilidade que superará tudo." + }, + "defeat": { + 1: "Meus Pokémon e eu esculpimos uma ilusão de Água e saímos vitoriosos.", + 2: "Ahahaha, eu venci, e você perdeu.", + 3: "Posso emprestar meu traje? Pode te ajudar a batalhar!\nAhahaha, estou brincando!", + 4: "Eu sou o vencedor! O que quer dizer, você perdeu." + } + }, + "crasher_wake": { + "encounter": { + 1: "Crash! Crash! Cuidado!\nDemolidor Wake… está… aqui!", + 2: "Crash! Crash! Demolidor Wake!", + 3: "Sou a onda de poder que vai te lavar!" + }, + "victory": { + 1: "Isso coloca um sorriso no meu rosto!\nGuhahaha! Foi uma explosão!", + 2: "Hunwah! Acabou e terminou!\nComo vou dizer isso...\nQuero mais! Queria batalhar muito mais!", + 3: "O QUÊ?!" + }, + "defeat": { + 1: "Siiiiim! Isso mesmo!", + 2: "Eu venci, mas quero mais! Queria batalhar muito mais!", + 3: "Até logo!" + } + }, + "falkner": { + "encounter": { + 1: "Vou mostrar o verdadeiro poder dos magníficos Pokémon pássaros!", + 2: "Ventos, fiquem comigo!", + 3: "Pai! Espero que esteja vendo minha batalha de cima!" + }, + "victory": { + 1: "Eu entendo... Vou sair graciosamente.", + 2: "Uma derrota é uma derrota. Você é realmente forte.", + 3: "...Droga! Sim, eu perdi." + }, + "defeat": { + 1: "Pai! Venci com seus amados Pokémon pássaros...", + 2: "Pokémon pássaros são os melhores afinal!", + 3: "Sinto que estou alcançando meu pai!" + } + }, "nessa": { "encounter": { 1: "Não importa que tipo de plano sua mente refinada possa estar tramando, meu parceiro e eu vamos afundá-la.", @@ -3295,6 +3987,42 @@ export const PGFdialogue: DialogueTranslationEntries = { 6: "Eu sabia que venceria!" } }, + "crispin": { + "encounter": { + 1: "Quero vencer, então é exatamente isso que vou fazer!", + 2: "Eu batalho porque quero batalhar! E sabe de uma coisa? É assim que deve ser!" + }, + "victory": { + 1: "Queria vencer... mas perdi!", + 2: "Eu perdi... porque não consegui vencer!" + }, + "defeat": { + 1: "Ei, espere um segundo. Eu acabei de vencer? Acho que acabei de vencer! Que satisfação!", + 2: "Uou! Isso foi incrível!" + } + }, + "amarys": { + "encounter": { + 1: "Quero ser a pessoa a ajudar alguém em particular. Sendo assim, não posso me dar ao luxo de perder.\n... Nossa batalha começa agora." + }, + "victory": { + 1: "Eu sou... não o suficiente, eu vejo." + }, + "defeat": { + 1: "A vitória pertence a mim. Bem lutado." + } + }, + "lacey": { + "encounter": { + 1: "Vou enfrentar você com meu time usual como membro da Elite dos Quatro." + }, + "victory": { + 1: "Foi uma excelente batalha. Estou ansiosa para o próximo desafio." + }, + "defeat": { + 1: "Fufufu... Nada mal.\nDesafiantes que derrotam a Elite dos Quatro são dignos de notar." + } + }, "drayton": { "encounter": { 1: `Cara, eu amo cadeiras. Você não ama cadeiras? Que salva-vidas. @@ -3319,6 +4047,23 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Hohoho... De fato. Pequenas lâminas frágeis de grama conseguem quebrar até mesmo concreto." } }, + "viola": { + "encounter": { + 1: `Seja as lágrimas de frustração que seguem uma derrota ou o florescer da alegria que vem com a vitória… + $Ambos são ótimos temas para minha câmera! Fantástico! Isso vai ser simplesmente fantástico! + $Agora venha para cima de mim!`, + 2: "Minha lente está sempre focada na vitória – não vou deixar nada estragar esta foto!" + }, + "victory": { + 1: "Você e seus Pokémon me mostraram uma nova profundidade de campo! Fantástico! Simplesmente fantástico!", + 2: `O mundo que você vê através de uma lente, e o mundo que você vê com um Pokémon ao seu lado… + $O mesmo mundo pode parecer completamente diferente dependendo do seu ponto de vista.` + }, + "defeat": { + 1: "A foto do momento da minha vitória vai ser um verdadeiro sucesso!", + 2: "Sim! Tirei ótimas fotos!" + } + }, "candice": { "encounter": { 1: `Você quer desafiar a Candice? Com certeza! Eu estava esperando por alguém forte! @@ -3347,6 +4092,40 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Sim! Meus Pokémon e eu somos perfeitamente bons!" } }, + "aaron": { + "encounter": { + 1: "Ok! Deixe-me enfrentar você!" + }, + "victory": { + 1: "Batalhar é um assunto profundo e complexo..." + }, + "defeat": { + 1: "Vencer um membro da Elite dos Quatro não é fácil." + } + }, + "cress": { + "encounter": { + 1: "Isso mesmo! Serei eu e meus estimados tipos Água que você deve enfrentar na batalha!" + }, + "victory": { + 1: "Perder? Eu? Não acredito nisso." + }, + "defeat": { + 1: "Este é o resultado apropriado quando eu sou seu oponente." + } + }, + "allister": { + "encounter": { + 1: "Sou Allister.\nA-aqui... vou eu..." + }, + "victory": { + 1: `Quase perdi minha máscara de tanto choque... Isso foi… + $Uau. Posso ver sua habilidade pelo que ela é.`, + }, + "defeat": { + 1: "I-isso foi incrível!" + } + }, "clay": { "encounter": { 1: "Harrumph! Me deixou esperando, não foi, garota? Tudo bem, hora de ver o que você pode fazer!" @@ -3369,6 +4148,68 @@ export const PGFdialogue: DialogueTranslationEntries = { "defeat": { 1: "Volte para me ver novamente, ouviu?" } + }, "tulip": { + "encounter": { + 1: "Permita-me usar minhas habilidades para deixar seus lindos Pokémon ainda mais bonitos!" + }, + "victory": { + 1: "Sua força tem uma magia que não pode ser apagada." + }, + "defeat": { + 1: "Você sabe, na minha linha de trabalho, pessoas que carecem de talento em uma área ou outra frequentemente desaparecem rapidamente - nunca mais se ouve falar delas." + } + }, + "sidney": { + "encounter": { + 1: `Gostei desse olhar que você me deu. Acho que você vai ser um bom desafio. + $Isso é ótimo! Parece muito bom! Vamos nessa! + $Você e eu, vamos curtir uma batalha que só pode acontecer aqui!`, + }, + "victory": { + 1: "E aí, gostou? Eu perdi! Mas foi divertido, então não importa." + }, + "defeat": { + 1: "Sem ressentimentos, beleza?" + } + }, + "phoebe": { + "encounter": { + 1: `Enquanto treinava, adquiri a habilidade de me comunicar com Pokémon do tipo Fantasma. + $Sim, o vínculo que desenvolvi com os Pokémon é extremamente forte. + $Então, vamos lá, tente ver se você consegue até mesmo causar dano aos meus Pokémon!`, + }, + "victory": { + 1: "Ah, droga. Eu perdi." + }, + "defeat": { + 1: "Estou ansiosa para batalhar com você de novo algum dia!" + } + }, + "glacia": { + "encounter": { + 1: `Tudo o que vi foram desafios de Treinadores fracos e seus Pokémon. + $E você? Ficaria extremamente satisfeita se pudesse dar tudo de mim contra você!`, + }, + "victory": { + 1: `Você e seus Pokémon… Como seus espíritos queimam! + $O calor consumido é esmagador. + $Não é surpresa que minhas habilidades geladas falharam em te machucar.`, + }, + "defeat": { + 1: "Uma batalha intensamente apaixonada, sem dúvida." + } + }, + "drake": { + "encounter": { + 1: `Para nós, batalhar com Pokémon como parceiros, você sabe o que é necessário? Você sabe o que precisa? + $Se não souber, nunca prevalecerá contra mim!`, + }, + "victory": { + 1: "Excelente, deve-se dizer." + }, + "defeat": { + 1: "Dei meu máximo nessa batalha!" + } }, "wallace": { "encounter": { @@ -3420,6 +4261,17 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Estou encantada! Sim, encantada por poder esmagar você sob meu calcanhar." } }, + "hala": { + "encounter": { + 1: "O velho Hala está aqui para fazer você gritar!" + }, + "victory": { + 1: "Pude sentir o poder que você ganhou na sua jornada." + }, + "defeat": { + 1: "Haha! Que batalha deliciosa!" + } + }, "molayne": { "encounter": { 1: `Dei a posição de capitão ao meu primo Sophocles, mas estou confiante na minha habilidade. @@ -3443,6 +4295,17 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Nahahaha! Você realmente é algo mais, garota!" } }, + "bruno": { + "encounter": { + 1: "Nós vamos te triturar com nosso poder superior! Hoo hah!" + }, + "victory": { + 1: "Por quê? Como eu poderia perder?" + }, + "defeat": { + 1: "Você pode me desafiar o quanto quiser, mas os resultados nunca vão mudar!" + } + }, "bugsy": { "encounter": { 1: "Sou Bugsy! Eu nunca perco quando se trata de Pokémon do tipo Inseto!" @@ -3454,6 +4317,30 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Obrigado! Graças à nossa batalha, eu também pude fazer progressos na minha pesquisa!" } }, + "koga": { + "encounter": { + 1: "Fwahahahaha! Pokémon não são apenas sobre força bruta--você verá em breve!" + }, + "victory": { + 1: "Ah! Você provou seu valor!" + }, + "defeat": { + 1: "Você aprendeu a temer as técnicas do ninja?" + } + }, + "bertha": { + "encounter": { + 1: "Bem, você mostraria a esta velha senhora o quanto aprendeu?" + }, + "victory": { + 1: `Bem! Querida criança, devo dizer, isso foi muito impressionante. + $Seus Pokémon acreditaram em você e fizeram o melhor para te dar a vitória. + $Mesmo tendo perdido, me encontro com esse sorriso bobo!`, + }, + "defeat": { + 1: "Hahahahah! Parece que esta velha senhora ganhou!" + } + }, "lenora": { "encounter": { 1: "Bem, desafiadora, vou pesquisar como você batalha com os Pokémon que criou com tanto carinho!" @@ -3465,6 +4352,18 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Ah ha ha! Se você perder, certifique-se de analisar o porquê e use esse conhecimento na próxima batalha!" } }, + "siebold": { + "encounter": { + 1: "Enquanto eu estiver vivo, continuarei em busca da culinária suprema... e dos oponentes mais fortes em batalha!" + }, + "victory": { + 1: "Guardarei minha memória de você e seus Pokémon para sempre em meu coração." + }, + "defeat": { + 1: `Nossa batalha Pokémon foi como alimento para minha alma. Isso vai me manter em frente. + $É assim que vou prestar meus respeitos a você por dar tudo de si na batalha!`, + } + }, "roxie": { "encounter": { 1: "Prepare-se! Vou arrancar algum senso de você!" @@ -3476,6 +4375,40 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Ei, vamos lá! Seja séria! Você tem que dar mais de si!" } }, + "olivia": { + "encounter": { + 1: "Não precisa de introdução aqui. Hora de batalhar comigo, Olivia!" + }, + "victory": { + 1: "Realmente encantador… Tanto você quanto seus Pokémon…" + }, + "defeat": { + 1: "Mmm-hmm." + } + }, + "poppy": { + "encounter": { + 1: "Oooh! Você quer ter uma batalha Pokémon comigo?" + }, + "victory": { + 1: "Uagh?! Mmmuuuggghhh…" + }, + "defeat": { + 1: `Yaaay! Eu consegui! Eu der-ro-tei você! Você pode vir para… Para… Uma revanche? + $Venha para uma revanche quando quiser!`, + } + }, + "agatha": { + "encounter": { + 1: "Pokémon são para batalhas! Vou te mostrar como um verdadeiro Treinador batalha!" + }, + "victory": { + 1: "Oh meu! Você é algo especial, criança!" + }, + "defeat": { + 1: "Bahaha. É assim que uma batalha adequada é feita!" + } + }, "flint": { "encounter": { 1: "Espero que você esteja aquecida, porque aqui vem o Big Bang!" @@ -3487,6 +4420,17 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Huh? Isso é tudo? Acho que você precisa de um pouco mais de paixão." } }, + "grimsley": { + "encounter": { + 1: "O vencedor leva tudo, e não sobra nada para o perdedor." + }, + "victory": { + 1: "Quando se perde, perde-se tudo… A próxima coisa que vou procurar será a vitória, também!" + }, + "defeat": { + 1: "Se alguém vence, a pessoa que lutou contra essa pessoa perde." + } + }, "caitlin": { "encounter": { 1: `Sou eu que apareci quando a flor se abriu. Você que estava esperando… @@ -3501,6 +4445,18 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Aspiro a reivindicar a vitória com elegância e graça." } }, + "diantha": { + "encounter": { + 1: `Batalhar contra você e seus Pokémon, todos vocês cheios de esperança para o futuro… + $Honestamente, isso apenas me enche da energia que preciso para continuar enfrentando cada novo dia! Sim!`, + }, + "victory": { + 1: "Testemunhar os espíritos nobres de você e seus Pokémon em batalha realmente tocou meu coração…" + }, + "defeat": { + 1: "Oh, fantástico! O que achou? Minha equipe foi bem legal, né?" + } + }, "wikstrom": { "encounter": { 1: `Bem encontrado, jovem desafiadora! Verdadeiramente sou a lâmina famosa de aço endurecido, Duque Wikstrom! @@ -3525,6 +4481,18 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Ehaha! Que vitória incrível!" } }, + "larry_elite": { + "encounter": { + 1: `Olá… Sou eu, Larry. + $Eu também sou membro da Elite dos Quatro, sim… Infelizmente para mim.`, + }, + "victory": { + 1: "Bem, isso tirou o vento debaixo das nossas asas…" + }, + "defeat": { + 1: "É hora de uma reunião com o chefe." + } + }, "lance": { "encounter": { 1: "Estive esperando por você. Permita-me testar suas habilidades.", @@ -3539,6 +4507,23 @@ export const PGFdialogue: DialogueTranslationEntries = { 2: "Não é que você seja fraca. Não se incomode com isso." } }, + "karen": { + "encounter": { + 1: "Eu sou Karen. Você gostaria de um duelo com meus Pokémon do tipo Sombrio?", + 2: "Sou diferente daqueles que você já conheceu.", + 3: "Você montou uma equipe charmosa. Nossa batalha deve ser boa." + }, + "victory": { + 1: "Não! Eu não posso vencer. Como você ficou tão forte?", + 2: "Não me desviarei do meu caminho escolhido.", + 3: "O Campeão está ansioso para te conhecer." + }, + "defeat": { + 1: "Isso era o que eu esperava.", + 2: "Bem, isso foi relativamente divertido.", + 3: "Venha me visitar a qualquer momento." + } + }, "milo": { "encounter": { 1: `Parece que você entende bem os Pokémon. @@ -3552,6 +4537,20 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Isso realmente vai te deixar em choque e admiração." } }, + "lucian": { + "encounter": { + 1: `Só um momento, por favor. O livro que estou lendo está quase no clímax emocionante… + $O herói obteve uma espada mística e está prestes a enfrentar sua prova final… Ah, tanto faz. + $Já que você chegou tão longe, vou deixar isso de lado e batalhar com você. + $Deixe-me ver se você alcançará tanta glória quanto o herói do meu livro!`, + }, + "victory": { + 1: "Eu vejo… Parece que você me colocou em xeque-mate." + }, + "defeat": { + 1: "Tenho uma reputação a manter." + } + }, "drasna": { "encounter": { 1: `Você deve ser uma Treinadora forte. Sim, bastante forte… @@ -3564,6 +4563,29 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Como isso é possível?" } }, + "kahili": { + "encounter": { + 1: "Então, aqui está você… Por que não vemos para quem os ventos favorecem hoje, você… ou eu?" + }, + "victory": { + 1: "É frustrante para mim como membro da Elite dos Quatro, mas parece que sua força é real." + }, + "defeat": { + 1: "Essa foi uma jogada de mestre!" + } + }, + "hassel": { + "encounter": { + 1: "Prepare-se para aprender em primeira mão como é a respiração ardente de uma batalha feroz!" + }, + "victory": { + 1: `A sorte sorriu para mim desta vez, mas… + $Julgando pelo andamento da luta, quem sabe se serei tão sortudo na próxima vez.`, + }, + "defeat": { + 1: "Essa foi uma jogada de mestre!" + } + }, "blue": { "encounter": { 1: "Você deve ser muito boa para chegar tão longe." @@ -3575,6 +4597,39 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Viu? Meu poder é o que me trouxe até aqui." } }, + "piers": { + "encounter": { + 1: "Prepare-se para uma mosh pit comigo e minha galera! Spikemuth, é hora de roquear!" + }, + "victory": { + 1: "Eu e minha equipe demos o nosso melhor. Vamos nos encontrar novamente para uma batalha algum dia…" + }, + "defeat": { + 1: "Minha garganta está desgastada de tanto gritar… Mas essa foi uma batalha empolgante!" + } + }, + "red": { + "encounter": { + 1: "…!" + }, + "victory": { + 1: "…?" + }, + "defeat": { + 1: "…!" + } + }, + "jasmine": { + "encounter": { + 1: "Oh… Seus Pokémon são impressionantes. Acho que vou gostar disso." + }, + "victory": { + 1: "Você é realmente forte. Vou ter que me esforçar muito mais também." + }, + "defeat": { + 1: "Eu nunca esperei ganhar." + } + }, "lance_champion": { "encounter": { 1: "Ainda sou o Campeão. Não vou segurar nada." @@ -3586,6 +4641,96 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Defendi com sucesso meu Campeonato." } }, + "steven": { + "encounter": { + 1: `Diga-me… O que você viu na sua jornada com seus Pokémon? + $O que você sentiu, encontrando tantos outros Treinadores por aí? + $Viajar por esta terra rica… Isso despertou algo dentro de você? + $Quero que você venha até mim com tudo o que aprendeu. + $Meus Pokémon e eu responderemos com tudo o que sabemos!`, + }, + "victory": { + 1: "Então eu, o Campeão, caio em derrota…" + }, + "defeat": { + 1: "Esse tempo foi bem gasto! Obrigado!" + } + }, + "cynthia": { + "encounter": { + 1: "Eu, Cynthia, aceito seu desafio! Não haverá nenhuma trégua da minha parte!" + }, + "victory": { + 1: "Não importa o quão divertida a batalha seja, ela sempre terminará algum dia…" + }, + "defeat": { + 1: "Mesmo que você perca, nunca perca o amor pelos Pokémon." + } + }, + "iris": { + "encounter": { + 1: `Sabe de uma coisa? Estou realmente ansiosa para ter batalhas sérias com Treinadores fortes! + $Quero dizer, vamos lá! Os Treinadores que chegam aqui são Treinadores que desejam a vitória com todas as fibras do seu ser! + $E eles estão batalhando ao lado de Pokémon que passaram por inúmeras batalhas difíceis! + $Se eu batalhar com pessoas assim, não só eu ficarei mais forte, meus Pokémon também! + $E nós vamos nos conhecer ainda melhor! OK! Prepare-se! + $Sou Iris, a Campeã da Liga Pokémon, e vou te derrotar!`, + }, + "victory": { + 1: "Aghhhh… Eu dei o meu melhor, mas nós perdemos…" + }, + "defeat": { + 1: "Yay! Nós vencemos!" + } + }, + "hau": { + "encounter": { + 1: `Eu me pergunto se um Treinador batalha de maneira diferente dependendo se ele é de uma região quente ou fria. + $Vamos testar isso!`, + }, + "victory": { + 1: "Isso foi incrível! Acho que entendi um pouco melhor seu estilo agora!" + }, + "defeat": { + 1: "Cara, essa foi uma batalha e tanto!" + } + }, + "geeta": { + "encounter": { + 1: `Decidi entrar na batalha mais uma vez. + $Venha agora… Mostre-me os frutos do seu treinamento.`, + }, + "victory": { + 1: "Estou ansiosa para notícias de todas as suas conquistas!" + }, + "defeat": { + 1: "Qual o problema? Isso é tudo?" + } + }, + "nemona": { + "encounter": { + 1: "Yesss! Estou tão empolgada! Hora de soltar tudo!" + }, + "victory": { + 1: "Bem, isso foi ruim, mas ainda me diverti! Eu te pego na próxima!" + }, + "defeat": { + 1: "Bem, essa foi uma ótima batalha! Frutífera, com certeza." + } + }, + "leon": { + "encounter": { + 1: "Vamos ter um tempo absolutamente campeão!" + }, + "victory": { + 1: `Meu tempo como Campeão acabou… + $Mas que tempo campeão foi! + $Obrigado pela melhor batalha que já tive!`, + }, + "defeat": { + 1: "Um tempo absolutamente campeão, foi!" + } + }, "whitney": { "encounter": { 1: "Eai! Você não acha que os Pokémon são, tipo, super fofos?" @@ -3619,6 +4764,28 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Coma, meu adorável Vivillon!" } }, + "pryce": { + "encounter": { + 1: "A juventude sozinha não garante a vitória! Experiência é o que conta." + }, + "victory": { + 1: "Excelente! Isso foi perfeito. Tente não esquecer o que sente agora." + }, + "defeat": { + 1: "Exatamente como eu imaginei." + } + }, + "clair": { + "encounter": { + 1: "Você sabe quem eu sou? E ainda se atreve a me desafiar?" + }, + "victory": { + 1: "Eu me pergunto até onde você pode ir com seu nível de habilidade. Isso deve ser fascinante." + }, + "defeat": { + 1: "E é isso." + } + }, "maylene": { "encounter": { 1: `Vim desafiá-la agora e não vou segurar nada. @@ -3631,6 +4798,18 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Isso foi incrível." } }, + "fantina": { + "encounter": { + 1: `Você vai me desafiar, não é? Mas eu vou ganhar. + $É o que a Líder do Ginásio de Hearthome faz, não?`, + }, + "victory": { + 1: "Você é tão incrivelmente forte. Sei porque perdi." + }, + "defeat": { + 1: "Estou tão, tão, muito feliz!" + } + }, "byron": { "encounter": { 1: `Treinadora! Você é jovem, assim como meu filho, Roark. @@ -3644,6 +4823,17 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Gwahahaha! Como foram meus Pokémon robustos?!" } }, + "olympia": { + "encounter": { + 1: "Um costume antigo decidindo o destino de alguém. A batalha começa!" + }, + "victory": { + 1: "Crie seu próprio caminho. Não deixe nada te atrapalhar. Seu destino, seu futuro." + }, + "defeat": { + 1: "Nosso caminho está claro agora." + } + }, "volkner": { "encounter": { 1: `Já que você chegou tão longe, deve ser bastante forte… @@ -3659,6 +4849,108 @@ export const PGFdialogue: DialogueTranslationEntries = { $Isso não é o que eu queria!`, } }, + "burgh": { + "encounter": { + 1: `M'hm… Se eu ganhar esta batalha, sinto que posso desenhar um quadro diferente de qualquer outro. + $OK! Posso ouvir minha musa da batalha claramente. Vamos direto ao ponto!`, + 2: `Claro, estou realmente orgulhoso de todos os meus Pokémon! + $Bem agora… Vamos direto ao ponto!` + }, + "victory": { + 1: "Acabou? Minha musa me abandonou?", + 2: "Hmm… Acabou! Você é incrível!" + }, + "defeat": { + 1: "Uau… É bonito de alguma forma, não é…", + 2: `Às vezes ouço as pessoas dizerem que foi uma vitória feia. + $Acho que se você está dando o seu melhor, qualquer vitória é bonita.` + } + }, + "elesa": { + "encounter": { + 1: `C'est fini! Quando tenho certeza disso, sinto um choque elétrico percorrer meu corpo! + $Quero sentir essa sensação, então agora meus amados Pokémon vão fazer sua cabeça girar!`, + }, + "victory": { + 1: "Eu queria fazer sua cabeça girar, mas você me surpreendeu." + }, + "defeat": { + 1: "Isso foi insatisfatório de alguma forma… Você dará tudo de si na próxima vez?" + } + }, + "skyla": { + "encounter": { + 1: `Finalmente é hora do confronto! Isso significa a batalha Pokémon que decide quem está no topo, certo? + $Eu amo estar no topo! Porque você pode ver para sempre e sempre de lugares altos! + $Então, que tal nós nos divertirmos?`, + }, + "victory": { + 1: "Ser seu oponente na batalha é uma nova fonte de força para mim. Obrigada!" + }, + "defeat": { + 1: "Ganhar ou perder, você sempre ganha algo com uma batalha, certo?" + } + }, + "brycen": { + "encounter": { + 1: `Há também força em estar com outras pessoas e Pokémon. + $Receber o apoio deles te fortalece. Vou te mostrar esse poder!`, + }, + "victory": { + 1: "A maravilhosa combinação de você e seus Pokémon! Que amizade linda!" + }, + "defeat": { + 1: "Condições extremas realmente testam e treinam você!" + } + }, + "drayden": { + "encounter": { + 1: `O que eu quero encontrar é um jovem Treinador que possa me mostrar um futuro brilhante. + $Vamos batalhar com tudo o que temos: sua habilidade, minha experiência e o amor com que criamos nossos Pokémon!`, + }, + "victory": { + 1: "Esse sentimento intenso que me invade após uma derrota… Não sei como descrevê-lo." + }, + "defeat": { + 1: "Harrumph! Sei que sua habilidade é maior que isso!" + } + }, + "grant": { + "encounter": { + 1: `Só há uma coisa que desejo. + $Que, superando um ao outro, encontremos um caminho para alturas ainda maiores.`, + }, + "victory": { + 1: "Você é uma parede que não consigo superar!" + }, + "defeat": { + 1: `Não desista. + $Isso é tudo o que realmente importa. + $As lições mais importantes da vida são simples.`, + } + }, + "korrina": { + "encounter": { + 1: "Hora da grande aparição de Lady Korrina!" + }, + "victory": { + 1: "É o seu próprio ser que permite que seus Pokémon evoluam!" + }, + "defeat": { + 1: "Que batalha explosiva!" + } + }, + "clemont": { + "encounter": { + 1: "Oh! Estou feliz por termos nos encontrado!" + }, + "victory": { + 1: "Sua paixão pela batalha me inspira!" + }, + "defeat": { + 1: "Parece que minha Máquina Treinadora-Crescer-Forte, Mach 2 está realmente funcionando!" + } + }, "valerie": { "encounter": { 1: `Oh, se não é uma jovem Treinadora… É adorável conhecê-la assim. @@ -3732,6 +5024,42 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Bom trabalho, eu suponho." } }, + "gordie": { + "encounter": { + 1: "Então, vamos acabar com isso." + }, + "victory": { + 1: "Eu só quero me enterrar em um buraco… Bem, acho que seria mais como cair daqui." + }, + "defeat": { + 1: "Batalhe como sempre faz, a vitória seguirá!" + } + }, + "marnie": { + "encounter": { + 1: `A verdade é que, quando tudo está dito e feito… Eu realmente só quero me tornar Campeã por mim mesma! + $Então, não leve para o pessoal quando eu chutar seu traseiro!`, + }, + "victory": { + 1: "OK, então eu perdi… Mas consegui ver muitos dos pontos bons de você e seus Pokémon!" + }, + "defeat": { + 1: "Espero que você tenha gostado das nossas táticas de batalha." + } + }, + "raihan": { + "encounter": { + 1: "Vou derrotar o Campeão, vencer todo o torneio e provar ao mundo o quão forte o grande Raihan realmente é!" + }, + "victory": { + 1: `Eu pareço bem mesmo quando perco. + $É uma verdadeira maldição. + $Acho que é hora de mais uma selfie!`, + }, + "defeat": { + 1: "Vamos tirar uma selfie para lembrar disso." + } + }, "brassius": { "encounter": { 1: "Pressuponho que você está pronta? Que nossa obra de arte colaborativa comece!" @@ -3757,6 +5085,17 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Seus olhos são MEUS!" } }, + "larry": { + "encounter": { + 1: "Quando tudo está dito e feito, a simplicidade é mais forte." + }, + "victory": { + 1: "Uma porção de derrota, hein?" + }, + "defeat": { + 1: "Vou encerrar o dia." + } + }, "ryme": { "encounter": { 1: "Vamos lá, baby! Me agite até os ossos!" @@ -3768,6 +5107,31 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Até mais, baby!" } }, + "grusha": { + "encounter": { + 1: "Tudo o que preciso fazer é garantir que o poder do meu Pokémon te arrependa até os ossos!" + }, + "victory": { + 1: "Sua paixão ardente... Eu meio que gosto, para ser honesto." + }, + "defeat": { + 1: "As coisas não esquentaram para você." + } + }, + "marnie_elite": { + "encounter": { + 1: "Você chegou até aqui, hein? Vamos ver se você pode lidar com meus Pokémon!", + 2: "Vou dar o meu melhor, mas não pense que vou pegar leve com você!" + }, + "victory": { + 1: "Não acredito que perdi... Mas você mereceu essa vitória. Bem feito!", + 2: "Parece que ainda tenho muito a aprender. Porém, grande batalha!" + }, + "defeat": { + 1: "Você lutou bem, mas eu tenho a vantagem! Melhor sorte na próxima vez!", + 2: "Parece que meu treinamento valeu a pena. Obrigado pela batalha!" + } + }, "nessa_elite": { "encounter": { 1: "As marés estão mudando a meu favor. Pronta para ser levada pela corrente?", @@ -3782,6 +5146,20 @@ export const PGFdialogue: DialogueTranslationEntries = { 2: "Você lutou bem, mas o poder do oceano é imparável!" } }, + "bea_elite": { + "encounter": { + 1: "Prepare-se! Meu espírito de luta brilha intensamente!", + 2: "Vamos ver se você consegue acompanhar meu ritmo implacável!" + }, + "victory": { + 1: "Sua força... É impressionante. Você realmente merece essa vitória.", + 2: "Nunca senti essa intensidade antes. Trabalho incrível!" + }, + "defeat": { + 1: "Outra vitória para meu rigoroso regime de treinamento! Bem feito!", + 2: "Você tem força, mas eu treinei mais. Grande batalha!" + } + }, "allister_elite": { "encounter": { 1: "As sombras caem... Você está pronta para enfrentar seus medos?", @@ -3810,6 +5188,32 @@ export const PGFdialogue: DialogueTranslationEntries = { 2: "Você foi pega na minha tempestade! Melhor sorte na próxima vez!" } }, + "alder": { + "encounter": { + 1: "Se prepare para uma batalha contra o Treinador mais forte de Unova!" + }, + "victory": { + 1: "Muito bem! Você certamente é um talento incomparável." + }, + "defeat": { + 1: `Um vento fresco sopra em meu coração... + $Que esforço extraordinário!` + } + }, + "kieran": { + "encounter": { + 1: `Através do trabalho duro, eu me torno cada vez mais forte! + $Eu não perco.` + }, + "victory": { + 1: `Eu não acredito... + $Que batalha divertida e emocionante!` + }, + "defeat": { + 1: `Uau, que batalha! + $Hora de você treinar ainda mais.` + } + }, "rival": { "encounter": { 1: `@c{smile}Eai, estava procurando você! Sabia que você estava ansiosa para começar, mas esperava pelo menos um tchau… @@ -3821,8 +5225,7 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: `@c{shock}Caramba… Você me limpou.\nVocê é mesmo uma novata? $@c{smile}Talvez tenha sido um pouco de sorte, mas…\nQuem sabe você consiga chegar até o fim. $Aliás, o professor me pediu para te dar esses itens. Eles parecem bem legais. - $@c{serious_smile_fists}Boa sorte lá fora! - $@c{smile}Ah- e eu espero que você aproveite o evento!` + $@c{serious_smile_fists}Boa sorte lá fora!` }, }, "rival_female": { @@ -3836,8 +5239,7 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: `@c{shock}Você acabou de começar e já está tão forte?!@d{96}\n@c{angry}Você trapaceou, não foi? $@c{smile_wave_wink}Brincadeirinha!@d{64} @c{smile_eclosed}Eu perdi de forma justa… Tenho a sensação de que você vai se sair muito bem lá fora. $@c{smile}Aliás, o professor pediu para eu te dar alguns itens. Espero que sejam úteis! - $@c{smile_wave}Dê o seu melhor, como sempre! Eu acredito em você! - $@c{smile}Ah- e eu espero que você aproveite o evento!` + $@c{smile_wave}Dê o seu melhor, como sempre! Eu acredito em você!` }, }, "rival_2": { @@ -3853,7 +5255,7 @@ export const PGFdialogue: DialogueTranslationEntries = { $@c{smile}Tudo bem, no entanto. Eu imaginei que isso poderia acontecer.\n@c{serious_mopen_fists}Isso só significa que preciso me esforçar mais para a próxima vez!\n $@c{smile}Ah, não que você precise realmente de ajuda, mas eu tinha um extra desses itens e pensei que você poderia querer. $@c{serious_smile_fists}Não espere outro depois deste!\nNão posso continuar dando vantagem ao meu oponente. - $@c{smile}Enfim, cuide-se, e aproveite o evento!` + $@c{smile}Enfim, cuide-se!` }, }, "rival_2_female": { @@ -3867,9 +5269,9 @@ export const PGFdialogue: DialogueTranslationEntries = { "victory": { 1: `@c{neutral}Eu… não era para eu perder dessa vez… $@c{smile}Ah bem. Isso só significa que vou ter que treinar ainda mais para a próxima vez! - $@c{smile_wave}Também consegui mais dois desses para você!\n@c{smile_wave_wink}Não precisa me agradecer~. - $@c{angry_mopen}Estes são os últimos, hein! Você não vai ganhar mais nenhum presente de mim depois desse! - $@c{smile_wave}Continue assim, e aproveite o evento!` + $@c{smile_wave}Também consegui mais um desses para você!\n@c{smile_wave_wink}Não precisa me agradecer~. + $@c{angry_mopen}Este é o último, hein! Você não vai ganhar mais nenhum presente de mim depois desse! + $@c{smile_wave}Continue assim!` }, "defeat": { 1: "Está tudo bem perder às vezes…" diff --git a/src/locales/pt_BR/menu.ts b/src/locales/pt_BR/menu.ts index 927ccce518b..87be5d8bed0 100644 --- a/src/locales/pt_BR/menu.ts +++ b/src/locales/pt_BR/menu.ts @@ -35,11 +35,11 @@ export const menu: SimpleTranslationEntries = { "sessionSuccess": "Sessão carregada com sucesso.", "failedToLoadSession": "Não foi possível carregar os dados da sua sessão.\nEles podem estar corrompidos.", "boyOrGirl": "Você é um menino ou uma menina?", - "evolving": "Que?\n{{pokemonName}} tá evoluindo!", + "evolving": "Quê?\n{{pokemonName}} tá evoluindo!", "stoppedEvolving": "{{pokemonName}} parou de evoluir.", "pauseEvolutionsQuestion": "Gostaria de pausar evoluções para {{pokemonName}}?\nEvoluções podem ser religadas na tela de equipe.", "evolutionsPaused": "Evoluções foram paradas para {{pokemonName}}.", - "evolutionDone": "Parabéns!\nSeu {{pokemonName}} evolui para {{evolvedPokemonName}}!", + "evolutionDone": "Parabéns!\nSeu {{pokemonName}} evoluiu para {{evolvedPokemonName}}!", "dailyRankings": "Classificação Diária", "weeklyRankings": "Classificação Semanal", "noRankings": "Sem Classificação", diff --git a/src/locales/pt_BR/modifier.ts b/src/locales/pt_BR/modifier.ts index 168665205c3..eadd5c5667a 100644 --- a/src/locales/pt_BR/modifier.ts +++ b/src/locales/pt_BR/modifier.ts @@ -4,7 +4,7 @@ export const modifier: SimpleTranslationEntries = { "surviveDamageApply": "{{pokemonNameWithAffix}} aguentou o tranco\nusando sua {{typeName}}!", "turnHealApply": "{{pokemonNameWithAffix}} restaurou um pouco de PS usando\nsuas {{typeName}}!", "hitHealApply": "{{pokemonNameWithAffix}} restaurou um pouco de PS usando\nsua {{typeName}}!", - "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} foi revivido\npor sua {{typeName}}!", + "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} foi reanimado\npor sua {{typeName}}!", "pokemonResetNegativeStatStageApply": "Os atributos diminuídos de {{pokemonNameWithAffix}} foram\nrestaurados por seu(sua) {{typeName}}!", "moneyInterestApply": "Você recebeu um juros de ₽{{moneyAmount}}\nde sua {{typeName}}!", "turnHeldItemTransferApply": "{{itemName}} de {{pokemonNameWithAffix}} foi absorvido(a)\npelo {{typeName}} de {{pokemonName}}!", diff --git a/src/locales/pt_BR/move-trigger.ts b/src/locales/pt_BR/move-trigger.ts index 45de6201d72..e4538252ad0 100644 --- a/src/locales/pt_BR/move-trigger.ts +++ b/src/locales/pt_BR/move-trigger.ts @@ -26,7 +26,7 @@ export const moveTriggers: SimpleTranslationEntries = { "soothingAromaWaftedThroughArea": "Um aroma suave se espalhou pelo ambiente!", "sprangUp": "{{pokemonName}} se levantou!", "choseDoomDesireAsDestiny": "{{pokemonName}} escolheu\no Desejo da Perdição como seu destino!", - "vanishedInstantly": "{{pokemonName}} desapareceu/nde repente!", + "vanishedInstantly": "{{pokemonName}} desapareceu\nde repente!", "tookTargetIntoSky": "{{pokemonName}} levou {{targetName}}\npara o céu!", "becameCloakedInFreezingLight": "{{pokemonName}} ficou envolto/nem uma luz congelante!", "becameCloakedInFreezingAir": "{{pokemonName}} ficou envolto/nem ar congelante!", @@ -61,6 +61,7 @@ export const moveTriggers: SimpleTranslationEntries = { "faintCountdown": "{{pokemonName}}\nirá desmaiar em {{turnCount}} turnos.", "copyType": "O tipo de {{pokemonName}}\nmudou para combinar com {{targetPokemonName}}!", "suppressAbilities": "A habilidade de {{pokemonName}}\nfoi suprimida!", + "revivalBlessing": "{{pokemonName}} foi reanimado!", "swapArenaTags": "{{pokemonName}} trocou os efeitos de batalha que afetam cada lado do campo!", "trickOnSwap": "{{pokemonNameWithAffix}} trocou de itens com seu alvo!", "trickFoeNewItem": "{{pokemonNameWithAffix}} obteve um(a) {{itemName}}.", diff --git a/src/locales/pt_BR/party-ui-handler.ts b/src/locales/pt_BR/party-ui-handler.ts index 08132b4bfc0..1f3e0fbe242 100644 --- a/src/locales/pt_BR/party-ui-handler.ts +++ b/src/locales/pt_BR/party-ui-handler.ts @@ -15,7 +15,7 @@ export const partyUiHandler: SimpleTranslationEntries = { "ALL": "Tudo", "PASS_BATON": "Passar Bastão", "UNPAUSE_EVOLUTION": "Ativar Evolução", - "REVIVE": "Reviver", + "REVIVE": "Reanimar", "RENAME": "Renomear", "choosePokemon": "Escolha um Pokémon.", diff --git a/src/locales/pt_BR/pokemon-form.ts b/src/locales/pt_BR/pokemon-form.ts index 062fc165ae0..dbe63fb7864 100644 --- a/src/locales/pt_BR/pokemon-form.ts +++ b/src/locales/pt_BR/pokemon-form.ts @@ -29,7 +29,7 @@ export const pokemonForm: SimpleTranslationEntries = { "pikachuPartner": "Parceiro", "eeveePartner": "Parceiro", // 2G - "pichuSpiky": "Spiky", + "pichuSpiky": "Orelha Espetada", "unownA": "A", "unownB": "B", "unownC": "C", @@ -74,8 +74,8 @@ export const pokemonForm: SimpleTranslationEntries = { "rotomFrost": "Congelante", "rotomFan": "Ventilador", "rotomMow": "Corte", - "giratinaAltered": "Altered", - "shayminLand": "Land", + "giratinaAltered": "Alterado", + "shayminLand": "Terrestre", // 5G "basculinRedStriped": "Listras Vermelhas", "basculinBlueStriped": "Listras Azuis", @@ -84,11 +84,11 @@ export const pokemonForm: SimpleTranslationEntries = { "deerlingSummer": "Verão", "deerlingAutumn": "Outono", "deerlingWinter": "Inverno", - "tornadusIncarnate": "Incarnate", - "thundurusIncarnate": "Incarnate", - "landorusIncarnate": "Incarnate", - "keldeoOrdinary": "Ordinary", - "meloettaAria": "Aria", + "tornadusIncarnate": "Materializado", + "thundurusIncarnate": "Materializado", + "landorusIncarnate": "Materializado", + "keldeoOrdinary": "Comum", + "meloettaAria": "Ária", // 6G "froakieBattleBond": "Vínculo de Batalha", "scatterbugMeadow": "Prado", @@ -165,11 +165,11 @@ export const pokemonForm: SimpleTranslationEntries = { "eiscueNoIce": "Descongelado", "indeedeeMale": "Macho", "indeedeeFemale": "Fêmea", - "morpekoFullBelly": "Full Belly", - "zacianHeroOfManyBattles": "Hero Of Many Battles", - "zamazentaHeroOfManyBattles": "Hero Of Many Battles", + "morpekoFullBelly": "Saciado", + "zacianHeroOfManyBattles": "Herói Veterano", + "zamazentaHeroOfManyBattles": "Herói Veterano", "zarudeDada": "Papa", - "enamorusIncarnate": "Incarnate", + "enamorusIncarnate": "Materializado", // 9G "squawkabillyGreenPlumage": "Plumas Verdes", "squawkabillyBluePlumage": "Plumas Azuis", diff --git a/src/locales/pt_BR/settings.ts b/src/locales/pt_BR/settings.ts index 14646b59742..e34fdfa0e5d 100644 --- a/src/locales/pt_BR/settings.ts +++ b/src/locales/pt_BR/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "Suporte para Controle", "showBgmBar": "Exibir Nomes das Músicas", "moveTouchControls": "Move Touch Controls", - "shopOverlayOpacity": "Opacidade da Loja" + "shopOverlayOpacity": "Opacidade da Loja", + "shopCursorTarget": "Alvo do Cursor da Loja", + "items": "Itens", + "reroll": "Atualizar", + "shop": "Loja", + "checkTeam": "Checar Time" } as const; diff --git a/src/locales/zh_CN/ability-trigger.ts b/src/locales/zh_CN/ability-trigger.ts index 0d69a78f0f7..337c0655c87 100644 --- a/src/locales/zh_CN/ability-trigger.ts +++ b/src/locales/zh_CN/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}}的{{abilityName}}\n使对方受到了伤害!", "postFaintHpDamage": "{{pokemonNameWithAffix}}的{{abilityName}}\n使对方受到了伤害!", "postSummonPressure": "从{{pokemonNameWithAffix}}的身上\n感到了一种压迫感!", + "weatherEffectDisappeared": "天气的影响消失了!", "postSummonMoldBreaker": "{{pokemonNameWithAffix}}\n打破了常规!", "postSummonAnticipation": "{{pokemonNameWithAffix}}\n发抖了!", "postSummonTurboblaze": "{{pokemonNameWithAffix}}\n正在释放炽焰气场!", diff --git a/src/locales/zh_CN/battle-scene.ts b/src/locales/zh_CN/battle-scene.ts new file mode 100644 index 00000000000..573e1791724 --- /dev/null +++ b/src/locales/zh_CN/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "₽{{formattedMoney}}" +} as const; diff --git a/src/locales/zh_CN/battle.ts b/src/locales/zh_CN/battle.ts index 5a73fbba09f..7b757941983 100644 --- a/src/locales/zh_CN/battle.ts +++ b/src/locales/zh_CN/battle.ts @@ -24,6 +24,7 @@ export const battle: SimpleTranslationEntries = { "hitResultSuperEffective": "效果拔群!", "hitResultNotVeryEffective": "收效甚微…", "hitResultNoEffect": "对{{pokemonName}}没有效果!!", + "hitResultImmune": "对于{{pokemonName}},\n完全没有效果!", "hitResultOneHitKO": "一击必杀!", "attackFailed": "但是失败了!", "attackMissed": "没有命中{{pokemonNameWithAffix}}!", diff --git a/src/locales/zh_CN/config.ts b/src/locales/zh_CN/config.ts index 24c8b870ffa..99b4e56ffc2 100644 --- a/src/locales/zh_CN/config.ts +++ b/src/locales/zh_CN/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const zhCnConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/zh_CN/dialogue.ts b/src/locales/zh_CN/dialogue.ts index 29afb56c40e..20d1d0d6040 100644 --- a/src/locales/zh_CN/dialogue.ts +++ b/src/locales/zh_CN/dialogue.ts @@ -2463,7 +2463,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: "@c{smile}嘿,我在找你呢!我知道你急着上路,\n但至少说个再见吧…$@c{smile_eclosed}所以你终于要开始追逐梦想了?\n我几乎不敢相信。$@c{serious_smile_fists}来都来了,来一场对战怎么样?\n毕竟,我想看看你是不是准备周全了。$@c{serious_mopen_fists}不要手下留情,我想让你全力以赴!", }, "victory": { - 1: "@c{shock}哇…你彻底击败了我。\n你是真初学者吗?$@c{smile}也许是靠点运气,但是…\n谁知道,你可能真的能一路走下去。$顺便说一下,博士让我给你这些东西。它们看起来可牛了。$@c{serious_smile_fists}祝你好运!$@c{smile}哦!我希望你能喜欢这次的活动! ", + 1: "@c{shock}哇…你彻底击败了我。\n你是真初学者吗?$@c{smile}也许是靠点运气,但是…\n谁知道,你可能真的能一路走下去。$顺便说一下,博士让我给你这些东西。它们看起来可牛了。$@c{serious_smile_fists}祝你好运!", }, }, "rival_female": { @@ -2471,7 +2471,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: "@c{smile_wave}你在这儿啊!我到处找你呢!$@c{angry_mopen}你忘了和你最好的朋友说再见了吗?$@c{smile_ehalf}你要去追逐梦想了,对吧?\n从今天开始,是不是…$@c{smile}不管怎样,忘了我的事就原谅你吧,\n但有个条件。@c{smile_wave_wink}你必须和我对战!$@c{angry_mopen}全力以赴!\n你也不想让你的冒险在开始之前就结束了,对吧?", }, "victory": { - 1: "@c{shock}你刚开始就已经这么强了?!@d{96}$@c{angry}你是不是开了?$@c{smile_wave_wink}只是开个玩笑啦!@d{64} @c{smile_eclosed}我输地心服口服了…\n我感觉你出去挺有天赋的。$@c{smile}顺便说一下,博士想让我给你一些东西。\n希望它们能帮上忙!$@c{smile_wave}像往常一样尽力而为!\n我相信你!$@c{smile}哦!我希望你能喜欢这次的活动! ", + 1: "@c{shock}你刚开始就已经这么强了?!@d{96}$@c{angry}你是不是开了?$@c{smile_wave_wink}只是开个玩笑啦!@d{64} @c{smile_eclosed}我输地心服口服了…\n我感觉你出去挺有天赋的。$@c{smile}顺便说一下,博士想让我给你一些东西。\n希望它们能帮上忙!$@c{smile_wave}像往常一样尽力而为!\n我相信你!", }, }, "rival_2": { @@ -2479,7 +2479,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: "@c{smile}嘿,你也在这里吗?$@c{smile_eclosed}一路过关斩将,是吧?$@c{serious_mopen_fists}我知道看起来好像我尾随着你来到这里,\n怎么可能啦。$@c{serious_smile_fists}说真的,自从你在老家打败我后,\n我就一直很渴望再比一场。$我自己也进行了很多训练,\n所以这次我肯定会好好打一场。$@c{serious_mopen_fists}不要手下留情,就像以前一样!$让我们开始吧!", }, "victory": { - 1: "@c{neutral_eclosed}哦。我过于自信了。$@c{smile}不过没关系。我猜到可能会这样。$@c{serious_mopen_fists}这只意味着我下次需要更努力!$$@c{smile}呃,不是特意帮你,我正好有多余的这个,\n我觉得你可能想要。$$@c{serious_smile_fists}不过这次之后别指望再有了!$我不能一直给我的对手优势。$@c{smile}反正,保重,要享受活动哦!", + 1: "@c{neutral_eclosed}哦。我过于自信了。$@c{smile}不过没关系。我猜到可能会这样。$@c{serious_mopen_fists}这只意味着我下次需要更努力!$$@c{smile}呃,不是特意帮你,我正好有多余的这个,\n我觉得你可能想要。$$@c{serious_smile_fists}不过这次之后别指望再有了!$我不能一直给我的对手优势。$@c{smile}反正,保重!", }, }, "rival_2_female": { @@ -2487,7 +2487,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: "@c{smile_wave}哦,真巧,在这里遇见你。\n看来你还没输过嘛。@c{angry_mopen}哈……好家伙!$@c{angry_mopen}我知道你在想什么,\n不,我才不会跟踪你什么呢。 @c{smile_eclosed}我只是碰巧在附近。$@c{smile_ehalf}我为你感到高兴,但我只想让你知道\n有时输了是可以接受的。$@c{smile}我们从错误中学到的东西\n往往比我们一直成功时学到的还要多。$@c{angry_mopen}无论如何,我为了我们的复赛已经努力训练了\n所以你最好全力以赴!", }, "victory": { - 1: "@c{neutral}我……没打算会输来着……$@c{smile}嗷……好吧。看来我要再更加努力训练了!$@c{smile_wave}我还给你带了个这个$@c{smile_wave_wink}不用谢我哦~.$@c{angry_mopen}不过,这是最后一个啦!\n你可别想再从我这赚小便宜了~$@c{smile_wave}要保重哦,要享受活动哦!", + 1: "@c{neutral}我……没打算会输来着……$@c{smile}嗷……好吧。看来我要再更加努力训练了!$@c{smile_wave}我还给你带了个这个$@c{smile_wave_wink}不用谢我哦~.$@c{angry_mopen}不过,这是最后一个啦!\n你可别想再从我这赚小便宜了~$@c{smile_wave}要保重哦!", }, "defeat": { 1: "输了有时候也不要紧的…", diff --git a/src/locales/zh_CN/move-trigger.ts b/src/locales/zh_CN/move-trigger.ts index a34a2fe96f7..dce2e0c7dca 100644 --- a/src/locales/zh_CN/move-trigger.ts +++ b/src/locales/zh_CN/move-trigger.ts @@ -61,6 +61,7 @@ export const moveTriggers: SimpleTranslationEntries = { "faintCountdown": "{{pokemonName}}\n将在{{turnCount}}回合后灭亡!", "copyType": "{{pokemonName}}\n变成了{{targetPokemonName}}的属性!", "suppressAbilities": "{{pokemonName}}的特性\n变得无效了!", + "revivalBlessing": "{{pokemonName}}复活了!", "swapArenaTags": "{{pokemonName}}\n交换了双方的场地效果!", "trickOnSwap": "{{pokemonNameWithAffix}}\n互换了各自的道具!", "trickFoeNewItem": "{{pokemonNameWithAffix}}\n获得了{{itemName}}!", diff --git a/src/locales/zh_CN/settings.ts b/src/locales/zh_CN/settings.ts index 422b1bd591d..d727ac2bef5 100644 --- a/src/locales/zh_CN/settings.ts +++ b/src/locales/zh_CN/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "手柄支持", "showBgmBar": "显示音乐名称", "moveTouchControls": "移动触摸控制", - "shopOverlayOpacity": "商店显示不透明度" + "shopOverlayOpacity": "商店显示不透明度", + "shopCursorTarget": "商店指针位置", + "items": "道具", + "reroll": "刷新", + "shop": "购买", + "checkTeam": "检查队伍" } as const; diff --git a/src/locales/zh_TW/ability-trigger.ts b/src/locales/zh_TW/ability-trigger.ts index c0d348633bc..14aa6de1af7 100644 --- a/src/locales/zh_TW/ability-trigger.ts +++ b/src/locales/zh_TW/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postFaintHpDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postSummonPressure": "{{pokemonNameWithAffix}} is exerting its Pressure!", + "weatherEffectDisappeared": "天氣的影響消失了!", "postSummonMoldBreaker": "{{pokemonNameWithAffix}} breaks the mold!", "postSummonAnticipation": "{{pokemonNameWithAffix}} shuddered!", "postSummonTurboblaze": "{{pokemonNameWithAffix}} is radiating a blazing aura!", diff --git a/src/locales/zh_TW/battle-scene.ts b/src/locales/zh_TW/battle-scene.ts new file mode 100644 index 00000000000..573e1791724 --- /dev/null +++ b/src/locales/zh_TW/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "₽{{formattedMoney}}" +} as const; diff --git a/src/locales/zh_TW/battle.ts b/src/locales/zh_TW/battle.ts index 357ad4ffbc3..116c891d74c 100644 --- a/src/locales/zh_TW/battle.ts +++ b/src/locales/zh_TW/battle.ts @@ -21,6 +21,7 @@ export const battle: SimpleTranslationEntries = { "hitResultSuperEffective": "效果拔群!", "hitResultNotVeryEffective": "收效甚微…", "hitResultNoEffect": "對 {{pokemonName}} 沒有效果!", + "hitResultImmune": "對于{{pokemonName}},\n完全沒有效果!", "hitResultOneHitKO": "一擊切殺!", "attackFailed": "但是失敗了!", "attackMissed": "沒有命中{{pokemonNameWithAffix}}!", diff --git a/src/locales/zh_TW/config.ts b/src/locales/zh_TW/config.ts index 004ed1da1ab..269ea3003b9 100644 --- a/src/locales/zh_TW/config.ts +++ b/src/locales/zh_TW/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const zhTwConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/zh_TW/dialogue.ts b/src/locales/zh_TW/dialogue.ts index 06ccc745a49..cfe43317bb7 100644 --- a/src/locales/zh_TW/dialogue.ts +++ b/src/locales/zh_TW/dialogue.ts @@ -2463,7 +2463,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: "@c{smile}嘿,我在找你呢!我知道你急著上路,\n但至少說個再見吧…$@c{smile_eclosed}所以你終於要開始追逐夢想了?\n我幾乎不敢相信。$@c{serious_smile_fists}來都來了,來一場對戰怎麼樣?\n畢竟,我想看看你是不是準備周全了。$@c{serious_mopen_fists}不要手下留情,我想讓你全力以赴!", }, "victory": { - 1: "@c{shock}哇…你徹底擊敗了我。\n你是真初學者嗎?$@c{smile}也許是靠點運氣,但是…\n誰知道,你可能真的能一路走下去。$順便說一下,博士讓我給你這些東西。它們看起來可牛了。$@c{serious_smile_fists}祝你好运!$@c{smile}哦!我希望你能喜歡這次的活動!", + 1: "@c{shock}哇…你徹底擊敗了我。\n你是真初學者嗎?$@c{smile}也許是靠點運氣,但是…\n誰知道,你可能真的能一路走下去。$順便說一下,博士讓我給你這些東西。它們看起來可牛了。$@c{serious_smile_fists}祝你好运!", }, }, "rival_female": { @@ -2471,7 +2471,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: "@c{smile_wave}你在這兒啊!我到處找你呢!$@c{angry_mopen}你忘了和你最好的朋友說再見了嗎?$@c{smile_ehalf}你要去追逐夢想了,對吧?\n從今天開始,是不是…$@c{smile}不管怎樣,忘了我的事就原諒你吧,\n但有個條件。@c{smile_wave_wink}你必須和我對戰!$@c{angry_mopen}全力以赴!\n你也不想讓你的冒險在開始之前就結束了,對吧?", }, "victory": { - 1: "@c{shock}你剛開始就已經這麼強了?!@d{96}$@c{angry}你是不是開了?$@c{smile_wave_wink}只是開個玩笑啦!@d{64} @c{smile_eclosed}我輸地心服口服了…\n我感覺你出去挺有天賦的。$@c{smile}順便說一下,博士想讓我給你一些東西。\n希望它們能幫上忙!$@c{smile_wave}像往常一樣盡力而為!\n我相信你!$@c{smile}哦!我希望你能喜歡這次的活動!", + 1: "@c{shock}你剛開始就已經這麼強了?!@d{96}$@c{angry}你是不是開了?$@c{smile_wave_wink}只是開個玩笑啦!@d{64} @c{smile_eclosed}我輸地心服口服了…\n我感覺你出去挺有天賦的。$@c{smile}順便說一下,博士想讓我給你一些東西。\n希望它們能幫上忙!$@c{smile_wave}像往常一樣盡力而為!\n我相信你!", }, }, "rival_2": { @@ -2479,7 +2479,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: "@c{smile}嘿,你也在這裡嗎?$@c{smile_eclosed}一路過關斬將,是吧?$@c{serious_mopen_fists}我知道看起來好像我尾隨著你來到這裡,\n怎麼可能啦。$@c{serious_smile_fists}說真的,自從你在老家打敗我後,\n我就一直很渴望再比一場。$我自己也進行了很多訓練,\n所以這次我肯定會好好打一場。$@c{serious_mopen_fists}不要手下留情,就像以前一樣!$讓我們開始吧!", }, "victory": { - 1: "@c{neutral_eclosed}哦。我過於自信了。$@c{smile}不過沒關係。我猜到可能會這樣。$@c{serious_mopen_fists}這只意味著我下次需要更努力!$$@c{smile}呃,不是特意幫你,我正好有多餘的這個,\n我覺得你可能想要。$$@c{serious_smile_fists}不過這次之後別指望再有了!$我不能一直給我的對手優勢。$@c{smile}反正,保重, 要享受活動哦!", + 1: "@c{neutral_eclosed}哦。我過於自信了。$@c{smile}不過沒關係。我猜到可能會這樣。$@c{serious_mopen_fists}這只意味著我下次需要更努力!$$@c{smile}呃,不是特意幫你,我正好有多餘的這個,\n我覺得你可能想要。$$@c{serious_smile_fists}不過這次之後別指望再有了!$我不能一直給我的對手優勢。$@c{smile}反正,保重!", }, }, "rival_2_female": { @@ -2487,7 +2487,7 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: "@c{smile_wave}哦,真巧,在這裡遇見你。\n看來你還沒輸過嘛。@c{angry_mopen}哈……好傢伙!$@c{angry_mopen}我知道你在想什麼,\n不,我才不會跟蹤你什麼呢。 @c{smile_eclosed}我只是碰巧在附近。$@c{smile_ehalf}我為你感到高興,但我只想讓你知道\n有時輸了是可以接受的。$@c{smile}我們從錯誤中學到的東西\n往往比我們一直成功時學到的還要多。$@c{angry_mopen}無論如何,我為了我們的複賽已經努力訓練了\n所以你最好全力以赴!", }, "victory": { - 1: "@c{neutral}我……沒打算會輸來著……$@c{smile}嗷……好吧。看來我要再更加努力訓練了!$@c{smile_wave}我還給你帶了個這個$@c{smile_wave_wink}不用謝我哦~.$@c{angry_mopen}不過,這是最後一個啦!\n 你可別想再從我這賺小便宜了~$@c{smile_wave}要保重哦,要享受活動哦!", + 1: "@c{neutral}我……沒打算會輸來著……$@c{smile}嗷……好吧。看來我要再更加努力訓練了!$@c{smile_wave}我還給你帶了個這個$@c{smile_wave_wink}不用謝我哦~.$@c{angry_mopen}不過,這是最後一個啦!\n 你可別想再從我這賺小便宜了~$@c{smile_wave}要保重哦!", }, "defeat": { 1: "輸了有時候也不要緊的…", diff --git a/src/locales/zh_TW/move-trigger.ts b/src/locales/zh_TW/move-trigger.ts index 2c14d0e41a0..e6b704293ac 100644 --- a/src/locales/zh_TW/move-trigger.ts +++ b/src/locales/zh_TW/move-trigger.ts @@ -61,6 +61,7 @@ export const moveTriggers: SimpleTranslationEntries = { "faintCountdown": "{{pokemonName}}\n將在{{turnCount}}回合後滅亡!", "copyType": "{{pokemonName}}變成了{{targetPokemonName}}的屬性!", "suppressAbilities": "{{pokemonName}}的特性\n變得無效了!", + "revivalBlessing": "{{pokemonName}}復活了!", "swapArenaTags": "{{pokemonName}}\n交換了雙方的場地效果!", "trickOnSwap": "{{pokemonNameWithAffix}}\n互換了各自的道具!", "trickFoeNewItem": "{{pokemonNameWithAffix}}\n獲得了{{itemName}}!", diff --git a/src/locales/zh_TW/settings.ts b/src/locales/zh_TW/settings.ts index 35499f45d15..7e6bf25bd7a 100644 --- a/src/locales/zh_TW/settings.ts +++ b/src/locales/zh_TW/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "手柄支持", "showBgmBar": "Show Music Names", "moveTouchControls": "移動觸控控制", - "shopOverlayOpacity": "Shop Overlay Opacity" + "shopOverlayOpacity": "Shop Overlay Opacity", + "shopCursorTarget": "Shop Cursor Target", + "items": "Items", + "reroll": "Reroll", + "shop": "Shop", + "checkTeam": "Check Team" } as const; diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index a791f543d9c..365fc433d2f 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -2209,7 +2209,7 @@ export function getDefaultModifierTypeForTier(tier: ModifierTier): ModifierType } export class ModifierTypeOption { - public type: ModifierType | null; + public type: ModifierType; public upgradeCount: integer; public cost: integer; diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index a32f3c019f4..99f4540f493 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1,5 +1,4 @@ import * as ModifierTypes from "./modifier-type"; -import { LearnMovePhase, LevelUpPhase, PokemonHealPhase } from "../phases"; import BattleScene from "../battle-scene"; import { getLevelTotalExp } from "../data/exp"; import { MAX_PER_TYPE_POKEBALLS, PokeballType } from "../data/pokeball"; @@ -7,7 +6,7 @@ import Pokemon, { PlayerPokemon } from "../field/pokemon"; import { Stat } from "../data/pokemon-stat"; import { addTextObject, TextStyle } from "../ui/text"; import { Type } from "../data/type"; -import { EvolutionPhase } from "../evolution-phase"; +import { EvolutionPhase } from "../phases/evolution-phase"; import { FusionSpeciesFormEvolution, pokemonEvolutions, pokemonPrevolutions } from "../data/pokemon-evolutions"; import { getPokemonNameWithAffix } from "../messages"; import * as Utils from "../utils"; @@ -28,6 +27,9 @@ import i18next from "i18next"; import { allMoves } from "#app/data/move"; import { Abilities } from "#app/enums/abilities"; +import { LearnMovePhase } from "#app/phases/learn-move-phase.js"; +import { LevelUpPhase } from "#app/phases/level-up-phase.js"; +import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase.js"; export type ModifierPredicate = (modifier: Modifier) => boolean; @@ -1158,7 +1160,7 @@ export class TurnHealModifier extends PokemonHeldItemModifier { if (!pokemon.isFullHp()) { const scene = pokemon.scene; scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / 16) * this.stackCount, 1), i18next.t("modifier:turnHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true)); + Utils.toDmgValue(pokemon.getMaxHp() / 16) * this.stackCount, i18next.t("modifier:turnHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true)); return true; } @@ -1249,7 +1251,7 @@ export class HitHealModifier extends PokemonHeldItemModifier { if (pokemon.turnData.damageDealt && !pokemon.isFullHp()) { const scene = pokemon.scene; scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.turnData.damageDealt / 8) * this.stackCount, 1), i18next.t("modifier:hitHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true)); + Utils.toDmgValue(pokemon.turnData.damageDealt / 8) * this.stackCount, i18next.t("modifier:hitHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true)); } return true; @@ -1384,7 +1386,7 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier { const pokemon = args[0] as Pokemon; pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / 2), 1), i18next.t("modifier:pokemonInstantReviveApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), false, false, true)); + Utils.toDmgValue(pokemon.getMaxHp() / 2), i18next.t("modifier:pokemonInstantReviveApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), false, false, true)); pokemon.resetStatus(true, false, true); return true; @@ -2338,7 +2340,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier { * @see {@linkcode modifierTypes[MINI_BLACK_HOLE]} */ export class TurnHeldItemTransferModifier extends HeldItemTransferModifier { - readonly isTransferrable: boolean = true; + isTransferrable: boolean = true; constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) { super(type, pokemonId, stackCount); } @@ -2362,6 +2364,10 @@ export class TurnHeldItemTransferModifier extends HeldItemTransferModifier { getMaxHeldItemCount(pokemon: Pokemon): integer { return 1; } + + setTransferrableFalse(): void { + this.isTransferrable = false; + } } /** @@ -2410,7 +2416,7 @@ export class ContactHeldItemTransferChanceModifier extends HeldItemTransferModif } getTransferMessage(pokemon: Pokemon, targetPokemon: Pokemon, item: ModifierTypes.ModifierType): string { - return i18next.t("modifier:contactHeldItemTransferApply", { pokemonNameWithAffix: getPokemonNameWithAffix(targetPokemon), itemName: item.name, pokemonName: pokemon.name, typeName: this.type.name }); + return i18next.t("modifier:contactHeldItemTransferApply", { pokemonNameWithAffix: getPokemonNameWithAffix(targetPokemon), itemName: item.name, pokemonName: getPokemonNameWithAffix(pokemon), typeName: this.type.name }); } getMaxHeldItemCount(pokemon: Pokemon): integer { diff --git a/src/phases.ts b/src/phases.ts deleted file mode 100644 index cafaa4bd02b..00000000000 --- a/src/phases.ts +++ /dev/null @@ -1,5847 +0,0 @@ -import BattleScene, { bypassLogin } from "./battle-scene"; -import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon"; -import * as Utils from "./utils"; -import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, MoveTarget, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, PreMoveMessageAttr, HealStatusEffectAttr, NoEffectAttr, BypassRedirectAttr, FixedDamageAttr, PostVictoryStatChangeAttr, ForceSwitchOutAttr, VariableTargetAttr, IncrementMovePriorityAttr, MoveHeaderAttr, MoveCategory } from "./data/move"; -import { Mode } from "./ui/ui"; -import { Command } from "./ui/command-ui-handler"; -import { Stat } from "./data/pokemon-stat"; -import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, Modifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TurnHealModifier, TurnHeldItemTransferModifier, MoneyMultiplierModifier, MoneyInterestModifier, IvScannerModifier, LapsingPokemonHeldItemModifier, PokemonMultiHitModifier, overrideModifiers, overrideHeldItems, BypassSpeedChanceModifier, TurnStatusEffectModifier, PokemonResetNegativeStatStageModifier } from "./modifier/modifier"; -import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler"; -import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball"; -import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./data/battle-anims"; -import { StatusEffect, getStatusEffectActivationText, getStatusEffectCatchRateMultiplier, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText } from "./data/status-effect"; -import { SummaryUiMode } from "./ui/summary-ui-handler"; -import EvolutionSceneHandler from "./ui/evolution-scene-handler"; -import { EvolutionPhase } from "./evolution-phase"; -import { Phase } from "./phase"; -import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./data/battle-stat"; -import { biomeLinks, getBiomeName } from "./data/biomes"; -import { ModifierTier } from "./modifier/modifier-tier"; -import { FusePokemonModifierType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, RememberMoveModifierType, TmModifierType, getDailyRunStarterModifiers, getEnemyBuffModifierForWave, getModifierType, getPlayerModifierTypeOptions, getPlayerShopModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds } from "./modifier/modifier-type"; -import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import { BattlerTagLapseType, CenterOfAttentionTag, EncoreTag, ProtectedTag, SemiInvulnerableTag, TrappedTag } from "./data/battler-tags"; -import { getPokemonNameWithAffix } from "./messages"; -import { Starter } from "./ui/starter-select-ui-handler"; -import { Gender } from "./data/gender"; -import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather"; -import { ArenaTagSide, ArenaTrapTag, ConditionalProtectTag, MistTag, TrickRoomTag } from "./data/arena-tag"; -import { CheckTrappedAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, ChangeMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, PreventBypassSpeedChanceAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, PokemonTypeChangeAbAttr, applyPreAttackAbAttrs, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr, MaxMultiHitAbAttr, HealFromBerryUseAbAttr, IgnoreMoveEffectsAbAttr, BlockStatusDamageAbAttr, BypassSpeedChanceAbAttr, AddSecondStrikeAbAttr, ReduceBurnDamageAbAttr } from "./data/ability"; -import { Unlockables, getUnlockableName } from "./system/unlockables"; -import { getBiomeKey } from "./field/arena"; -import { BattleType, BattlerIndex, TurnCommand } from "./battle"; -import { ChallengeAchv, HealAchv, LevelAchv, achvs } from "./system/achv"; -import { TrainerSlot, trainerConfigs } from "./data/trainer-config"; -import { EggHatchPhase } from "./egg-hatch-phase"; -import { Egg } from "./data/egg"; -import { vouchers } from "./system/voucher"; -import { clientSessionId, loggedInUser, updateUserInfo } from "./account"; -import { SessionSaveData } from "./system/game-data"; -import { addPokeballCaptureStars, addPokeballOpenParticles } from "./field/anims"; -import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangePreMoveTrigger } from "./data/pokemon-forms"; -import { battleSpecDialogue, getCharVariantFromDialogue, miscDialogue } from "./data/dialogue"; -import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "./ui/modifier-select-ui-handler"; -import { SettingKeys } from "./system/settings/settings"; -import { Tutorial, handleTutorial } from "./tutorial"; -import { TerrainType } from "./data/terrain"; -import { OptionSelectConfig, OptionSelectItem } from "./ui/abstact-option-select-ui-handler"; -import { SaveSlotUiMode } from "./ui/save-slot-select-ui-handler"; -import { fetchDailyRunSeed, getDailyRunStarters } from "./data/daily-run"; -import { GameMode, GameModes, getGameMode } from "./game-mode"; -import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./data/pokemon-species"; -import i18next from "./plugins/i18n"; -import Overrides from "#app/overrides"; -import { TextStyle, addTextObject, getTextColor } from "./ui/text"; -import { Type } from "./data/type"; -import { BerryUsedEvent, EncounterPhaseEvent, MoveUsedEvent, TurnEndEvent, TurnInitEvent } from "./events/battle-scene"; -import { Abilities } from "#enums/abilities"; -import { ArenaTagType } from "#enums/arena-tag-type"; -import { BattleSpec } from "#enums/battle-spec"; -import { BattleStyle } from "#enums/battle-style"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { Biome } from "#enums/biome"; -import { ExpNotification } from "#enums/exp-notification"; -import { Moves } from "#enums/moves"; -import { PlayerGender } from "#enums/player-gender"; -import { Species } from "#enums/species"; -import { TrainerType } from "#enums/trainer-type"; -import { applyChallenges, ChallengeType } from "./data/challenge"; -import { pokemonEvolutions } from "./data/pokemon-evolutions"; - -const { t } = i18next; - -export class LoginPhase extends Phase { - private showText: boolean; - - constructor(scene: BattleScene, showText?: boolean) { - super(scene); - - this.showText = showText === undefined || !!showText; - } - - start(): void { - super.start(); - - const hasSession = !!Utils.getCookie(Utils.sessionIdKey); - - this.scene.ui.setMode(Mode.LOADING, { buttonActions: [] }); - Utils.executeIf(bypassLogin || hasSession, updateUserInfo).then(response => { - const success = response ? response[0] : false; - const statusCode = response ? response[1] : null; - if (!success) { - if (!statusCode || statusCode === 400) { - if (this.showText) { - this.scene.ui.showText(i18next.t("menu:logInOrCreateAccount")); - } - - this.scene.playSound("menu_open"); - - const loadData = () => { - updateUserInfo().then(success => { - if (!success[0]) { - Utils.removeCookie(Utils.sessionIdKey); - this.scene.reset(true, true); - return; - } - this.scene.gameData.loadSystem().then(() => this.end()); - }); - }; - - this.scene.ui.setMode(Mode.LOGIN_FORM, { - buttonActions: [ - () => { - this.scene.ui.playSelect(); - loadData(); - }, () => { - this.scene.playSound("menu_open"); - this.scene.ui.setMode(Mode.REGISTRATION_FORM, { - buttonActions: [ - () => { - this.scene.ui.playSelect(); - updateUserInfo().then(success => { - if (!success[0]) { - Utils.removeCookie(Utils.sessionIdKey); - this.scene.reset(true, true); - return; - } - this.end(); - } ); - }, () => { - this.scene.unshiftPhase(new LoginPhase(this.scene, false)); - this.end(); - } - ] - }); - }, () => { - const redirectUri = encodeURIComponent(`${import.meta.env.VITE_SERVER_URL}/auth/discord/callback`); - const discordId = import.meta.env.VITE_DISCORD_CLIENT_ID; - const discordUrl = `https://discord.com/api/oauth2/authorize?client_id=${discordId}&redirect_uri=${redirectUri}&response_type=code&scope=identify&prompt=none`; - window.open(discordUrl, "_self"); - }, () => { - const redirectUri = encodeURIComponent(`${import.meta.env.VITE_SERVER_URL}/auth/google/callback`); - const googleId = import.meta.env.VITE_GOOGLE_CLIENT_ID; - const googleUrl = `https://accounts.google.com/o/oauth2/auth?client_id=${googleId}&redirect_uri=${redirectUri}&response_type=code&scope=openid`; - window.open(googleUrl, "_self"); - } - ] - }); - } else if (statusCode === 401) { - Utils.removeCookie(Utils.sessionIdKey); - this.scene.reset(true, true); - } else { - this.scene.unshiftPhase(new UnavailablePhase(this.scene)); - super.end(); - } - return null; - } else { - this.scene.gameData.loadSystem().then(success => { - if (success || bypassLogin) { - this.end(); - } else { - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.showText(t("menu:failedToLoadSaveData")); - } - }); - } - }); - } - - end(): void { - this.scene.ui.setMode(Mode.MESSAGE); - - if (!this.scene.gameData.gender) { - this.scene.unshiftPhase(new SelectGenderPhase(this.scene)); - } - - handleTutorial(this.scene, Tutorial.Intro).then(() => super.end()); - } -} - -export class TitlePhase extends Phase { - private loaded: boolean; - private lastSessionData: SessionSaveData; - public gameMode: GameModes; - - constructor(scene: BattleScene) { - super(scene); - - this.loaded = false; - } - - start(): void { - super.start(); - - this.scene.ui.clearText(); - this.scene.ui.fadeIn(250); - - this.scene.playBgm("title", true); - - this.scene.gameData.getSession(loggedInUser?.lastSessionSlot ?? -1).then(sessionData => { - if (sessionData) { - this.lastSessionData = sessionData; - const biomeKey = getBiomeKey(sessionData.arena.biome); - const bgTexture = `${biomeKey}_bg`; - this.scene.arenaBg.setTexture(bgTexture); - } - this.showOptions(); - }).catch(err => { - console.error(err); - this.showOptions(); - }); - } - - showOptions(): void { - const options: OptionSelectItem[] = []; - if (loggedInUser && loggedInUser.lastSessionSlot > -1) { - options.push({ - label: i18next.t("continue", {ns: "menu"}), - handler: () => { - this.loadSaveSlot(this.lastSessionData || !loggedInUser ? -1 : loggedInUser.lastSessionSlot); - return true; - } - }); - } - options.push({ - label: i18next.t("menu:newGame"), - handler: () => { - const setModeAndEnd = (gameMode: GameModes) => { - this.gameMode = gameMode; - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.clearText(); - this.end(); - }; - if (this.scene.gameData.unlocks[Unlockables.ENDLESS_MODE]) { - const options: OptionSelectItem[] = [ - { - label: GameMode.getModeName(GameModes.CLASSIC), - handler: () => { - setModeAndEnd(GameModes.CLASSIC); - return true; - } - }, - { - label: GameMode.getModeName(GameModes.CHALLENGE), - handler: () => { - setModeAndEnd(GameModes.CHALLENGE); - return true; - } - }, - { - label: GameMode.getModeName(GameModes.ENDLESS), - handler: () => { - setModeAndEnd(GameModes.ENDLESS); - return true; - } - } - ]; - if (this.scene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE]) { - options.push({ - label: GameMode.getModeName(GameModes.SPLICED_ENDLESS), - handler: () => { - setModeAndEnd(GameModes.SPLICED_ENDLESS); - return true; - } - }); - } - options.push({ - label: i18next.t("menu:cancel"), - handler: () => { - this.scene.clearPhaseQueue(); - this.scene.pushPhase(new TitlePhase(this.scene)); - super.end(); - return true; - } - }); - this.scene.ui.showText(i18next.t("menu:selectGameMode"), null, () => this.scene.ui.setOverlayMode(Mode.OPTION_SELECT, { options: options })); - } else { - this.gameMode = GameModes.CLASSIC; - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.clearText(); - this.end(); - } - return true; - } - }, - { - label: i18next.t("menu:loadGame"), - handler: () => { - this.scene.ui.setOverlayMode(Mode.SAVE_SLOT, SaveSlotUiMode.LOAD, - (slotId: integer) => { - if (slotId === -1) { - return this.showOptions(); - } - this.loadSaveSlot(slotId); - }); - return true; - } - }, - { - label: i18next.t("menu:dailyRun"), - handler: () => { - this.initDailyRun(); - return true; - }, - keepOpen: true - }, - { - label: i18next.t("menu:settings"), - handler: () => { - this.scene.ui.setOverlayMode(Mode.SETTINGS); - return true; - }, - keepOpen: true - }); - const config: OptionSelectConfig = { - options: options, - noCancel: true, - yOffset: 47 - }; - this.scene.ui.setMode(Mode.TITLE, config); - } - - loadSaveSlot(slotId: integer): void { - this.scene.sessionSlotId = slotId > -1 || !loggedInUser ? slotId : loggedInUser.lastSessionSlot; - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.resetModeChain(); - this.scene.gameData.loadSession(this.scene, slotId, slotId === -1 ? this.lastSessionData : undefined).then((success: boolean) => { - if (success) { - this.loaded = true; - this.scene.ui.showText(i18next.t("menu:sessionSuccess"), null, () => this.end()); - } else { - this.end(); - } - }).catch(err => { - console.error(err); - this.scene.ui.showText(i18next.t("menu:failedToLoadSession"), null); - }); - } - - initDailyRun(): void { - this.scene.ui.setMode(Mode.SAVE_SLOT, SaveSlotUiMode.SAVE, (slotId: integer) => { - this.scene.clearPhaseQueue(); - if (slotId === -1) { - this.scene.pushPhase(new TitlePhase(this.scene)); - return super.end(); - } - this.scene.sessionSlotId = slotId; - - const generateDaily = (seed: string) => { - this.scene.gameMode = getGameMode(GameModes.DAILY); - - this.scene.setSeed(seed); - this.scene.resetSeed(1); - - this.scene.money = this.scene.gameMode.getStartingMoney(); - - const starters = getDailyRunStarters(this.scene, seed); - const startingLevel = this.scene.gameMode.getStartingLevel(); - - const party = this.scene.getParty(); - const loadPokemonAssets: Promise[] = []; - for (const starter of starters) { - const starterProps = this.scene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr); - const starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0)); - const starterGender = starter.species.malePercent !== null - ? !starterProps.female ? Gender.MALE : Gender.FEMALE - : Gender.GENDERLESS; - const starterPokemon = this.scene.addPlayerPokemon(starter.species, startingLevel, starter.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, starterProps.variant, undefined, starter.nature); - starterPokemon.setVisible(false); - party.push(starterPokemon); - loadPokemonAssets.push(starterPokemon.loadAssets()); - } - - regenerateModifierPoolThresholds(party, ModifierPoolType.DAILY_STARTER); - const modifiers: Modifier[] = Array(3).fill(null).map(() => modifierTypes.EXP_SHARE().withIdFromFunc(modifierTypes.EXP_SHARE).newModifier()) - .concat(Array(3).fill(null).map(() => modifierTypes.GOLDEN_EXP_CHARM().withIdFromFunc(modifierTypes.GOLDEN_EXP_CHARM).newModifier())) - .concat(getDailyRunStarterModifiers(party)) - .filter((m) => m !== null); - - for (const m of modifiers) { - this.scene.addModifier(m, true, false, false, true); - } - this.scene.updateModifiers(true, true); - - Promise.all(loadPokemonAssets).then(() => { - this.scene.time.delayedCall(500, () => this.scene.playBgm()); - this.scene.gameData.gameStats.dailyRunSessionsPlayed++; - this.scene.newArena(this.scene.gameMode.getStartingBiome(this.scene)); - this.scene.newBattle(); - this.scene.arena.init(); - this.scene.sessionPlayTime = 0; - this.scene.lastSavePlayTime = 0; - this.end(); - }); - }; - - // If Online, calls seed fetch from db to generate daily run. If Offline, generates a daily run based on current date. - if (!Utils.isLocal) { - fetchDailyRunSeed().then(seed => { - if (seed) { - generateDaily(seed); - } else { - throw new Error("Daily run seed is null!"); - } - }).catch(err => { - console.error("Failed to load daily run:\n", err); - }); - } else { - generateDaily(btoa(new Date().toISOString().substring(0, 10))); - } - }); - } - - end(): void { - if (!this.loaded && !this.scene.gameMode.isDaily) { - this.scene.arena.preloadBgm(); - this.scene.gameMode = getGameMode(this.gameMode); - if (this.gameMode === GameModes.CHALLENGE) { - this.scene.pushPhase(new SelectChallengePhase(this.scene)); - } else { - this.scene.pushPhase(new SelectStarterPhase(this.scene)); - } - this.scene.newArena(this.scene.gameMode.getStartingBiome(this.scene)); - } else { - this.scene.playBgm(); - } - - this.scene.pushPhase(new EncounterPhase(this.scene, this.loaded)); - - if (this.loaded) { - const availablePartyMembers = this.scene.getParty().filter(p => p.isAllowedInBattle()).length; - - this.scene.pushPhase(new SummonPhase(this.scene, 0, true, true)); - if (this.scene.currentBattle.double && availablePartyMembers > 1) { - this.scene.pushPhase(new SummonPhase(this.scene, 1, true, true)); - } - - if (this.scene.currentBattle.battleType !== BattleType.TRAINER && (this.scene.currentBattle.waveIndex > 1 || !this.scene.gameMode.isDaily)) { - const minPartySize = this.scene.currentBattle.double ? 2 : 1; - if (availablePartyMembers > minPartySize) { - this.scene.pushPhase(new CheckSwitchPhase(this.scene, 0, this.scene.currentBattle.double)); - if (this.scene.currentBattle.double) { - this.scene.pushPhase(new CheckSwitchPhase(this.scene, 1, this.scene.currentBattle.double)); - } - } - } - } - - for (const achv of Object.keys(this.scene.gameData.achvUnlocks)) { - if (vouchers.hasOwnProperty(achv)) { - this.scene.validateVoucher(vouchers[achv]); - } - } - - super.end(); - } -} - -export class UnavailablePhase extends Phase { - constructor(scene: BattleScene) { - super(scene); - } - - start(): void { - this.scene.ui.setMode(Mode.UNAVAILABLE, () => { - this.scene.unshiftPhase(new LoginPhase(this.scene, true)); - this.end(); - }); - } -} - -export class ReloadSessionPhase extends Phase { - private systemDataStr: string | null; - - constructor(scene: BattleScene, systemDataStr?: string) { - super(scene); - - this.systemDataStr = systemDataStr ?? null; - } - - start(): void { - this.scene.ui.setMode(Mode.SESSION_RELOAD); - - let delayElapsed = false; - let loaded = false; - - this.scene.time.delayedCall(Utils.fixedInt(1500), () => { - if (loaded) { - this.end(); - } else { - delayElapsed = true; - } - }); - - this.scene.gameData.clearLocalData(); - - (this.systemDataStr ? this.scene.gameData.initSystem(this.systemDataStr) : this.scene.gameData.loadSystem()).then(() => { - if (delayElapsed) { - this.end(); - } else { - loaded = true; - } - }); - } -} - -export class OutdatedPhase extends Phase { - constructor(scene: BattleScene) { - super(scene); - } - - start(): void { - this.scene.ui.setMode(Mode.OUTDATED); - } -} - -export class SelectGenderPhase extends Phase { - constructor(scene: BattleScene) { - super(scene); - } - - start(): void { - super.start(); - - this.scene.ui.showText(i18next.t("menu:boyOrGirl"), null, () => { - this.scene.ui.setMode(Mode.OPTION_SELECT, { - options: [ - { - label: i18next.t("settings:boy"), - handler: () => { - this.scene.gameData.gender = PlayerGender.MALE; - this.scene.gameData.saveSetting(SettingKeys.Player_Gender, 0); - this.scene.gameData.saveSystem().then(() => this.end()); - return true; - } - }, - { - label: i18next.t("settings:girl"), - handler: () => { - this.scene.gameData.gender = PlayerGender.FEMALE; - this.scene.gameData.saveSetting(SettingKeys.Player_Gender, 1); - this.scene.gameData.saveSystem().then(() => this.end()); - return true; - } - } - ] - }); - }); - } - - end(): void { - this.scene.ui.setMode(Mode.MESSAGE); - super.end(); - } -} - -export class SelectChallengePhase extends Phase { - constructor(scene: BattleScene) { - super(scene); - } - - start() { - super.start(); - - this.scene.playBgm("menu"); - - this.scene.ui.setMode(Mode.CHALLENGE_SELECT); - } -} - -export class SelectStarterPhase extends Phase { - - constructor(scene: BattleScene) { - super(scene); - } - - start() { - super.start(); - - this.scene.playBgm("menu"); - - this.scene.ui.setMode(Mode.STARTER_SELECT, (starters: Starter[]) => { - this.scene.ui.clearText(); - this.scene.ui.setMode(Mode.SAVE_SLOT, SaveSlotUiMode.SAVE, (slotId: integer) => { - if (slotId === -1) { - this.scene.clearPhaseQueue(); - this.scene.pushPhase(new TitlePhase(this.scene)); - return this.end(); - } - this.scene.sessionSlotId = slotId; - this.initBattle(starters); - }); - }); - } - - /** - * Initialize starters before starting the first battle - * @param starters {@linkcode Pokemon} with which to start the first battle - */ - initBattle(starters: Starter[]) { - const party = this.scene.getParty(); - const loadPokemonAssets: Promise[] = []; - starters.forEach((starter: Starter, i: integer) => { - if (!i && Overrides.STARTER_SPECIES_OVERRIDE) { - starter.species = getPokemonSpecies(Overrides.STARTER_SPECIES_OVERRIDE as Species); - } - const starterProps = this.scene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr); - let starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0)); - if ( - starter.species.speciesId in Overrides.STARTER_FORM_OVERRIDES && - starter.species.forms[Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!] - ) { - starterFormIndex = Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!; - } - - let starterGender = starter.species.malePercent !== null - ? !starterProps.female ? Gender.MALE : Gender.FEMALE - : Gender.GENDERLESS; - if (Overrides.GENDER_OVERRIDE !== null) { - starterGender = Overrides.GENDER_OVERRIDE; - } - const starterIvs = this.scene.gameData.dexData[starter.species.speciesId].ivs.slice(0); - const starterPokemon = this.scene.addPlayerPokemon(starter.species, this.scene.gameMode.getStartingLevel(), starter.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, starterProps.variant, starterIvs, starter.nature); - starter.moveset && starterPokemon.tryPopulateMoveset(starter.moveset); - if (starter.passive) { - starterPokemon.passive = true; - } - starterPokemon.luck = this.scene.gameData.getDexAttrLuck(this.scene.gameData.dexData[starter.species.speciesId].caughtAttr); - if (starter.pokerus) { - starterPokemon.pokerus = true; - } - - if (starter.nickname) { - starterPokemon.nickname = starter.nickname; - } - - if (this.scene.gameMode.isSplicedOnly) { - starterPokemon.generateFusionSpecies(true); - } - starterPokemon.setVisible(false); - applyChallenges(this.scene.gameMode, ChallengeType.STARTER_MODIFY, starterPokemon); - party.push(starterPokemon); - loadPokemonAssets.push(starterPokemon.loadAssets()); - }); - overrideModifiers(this.scene); - overrideHeldItems(this.scene, party[0]); - Promise.all(loadPokemonAssets).then(() => { - SoundFade.fadeOut(this.scene, this.scene.sound.get("menu"), 500, true); - this.scene.time.delayedCall(500, () => this.scene.playBgm()); - if (this.scene.gameMode.isClassic) { - this.scene.gameData.gameStats.classicSessionsPlayed++; - } else { - this.scene.gameData.gameStats.endlessSessionsPlayed++; - } - this.scene.newBattle(); - this.scene.arena.init(); - this.scene.sessionPlayTime = 0; - this.scene.lastSavePlayTime = 0; - // Ensures Keldeo (or any future Pokemon that have this type of form change) starts in the correct form - this.scene.getParty().forEach((p: PlayerPokemon) => { - this.scene.triggerPokemonFormChange(p, SpeciesFormChangeMoveLearnedTrigger); - }); - this.end(); - }); - } -} - -export class BattlePhase extends Phase { - constructor(scene: BattleScene) { - super(scene); - } - - showEnemyTrainer(trainerSlot: TrainerSlot = TrainerSlot.NONE): void { - const sprites = this.scene.currentBattle.trainer?.getSprites()!; // TODO: is this bang correct? - const tintSprites = this.scene.currentBattle.trainer?.getTintSprites()!; // TODO: is this bang correct? - for (let i = 0; i < sprites.length; i++) { - const visible = !trainerSlot || !i === (trainerSlot === TrainerSlot.TRAINER) || sprites.length < 2; - [sprites[i], tintSprites[i]].map(sprite => { - if (visible) { - sprite.x = trainerSlot || sprites.length < 2 ? 0 : i ? 16 : -16; - } - sprite.setVisible(visible); - sprite.clearTint(); - }); - sprites[i].setVisible(visible); - tintSprites[i].setVisible(visible); - sprites[i].clearTint(); - tintSprites[i].clearTint(); - } - this.scene.tweens.add({ - targets: this.scene.currentBattle.trainer, - x: "-=16", - y: "+=16", - alpha: 1, - ease: "Sine.easeInOut", - duration: 750 - }); - } - - hideEnemyTrainer(): void { - this.scene.tweens.add({ - targets: this.scene.currentBattle.trainer, - x: "+=16", - y: "-=16", - alpha: 0, - ease: "Sine.easeInOut", - duration: 750 - }); - } -} - -type PokemonFunc = (pokemon: Pokemon) => void; - -export abstract class FieldPhase extends BattlePhase { - getOrder(): BattlerIndex[] { - const playerField = this.scene.getPlayerField().filter(p => p.isActive()) as Pokemon[]; - const enemyField = this.scene.getEnemyField().filter(p => p.isActive()) as Pokemon[]; - - // We shuffle the list before sorting so speed ties produce random results - let orderedTargets: Pokemon[] = playerField.concat(enemyField); - // We seed it with the current turn to prevent an inconsistency where it - // was varying based on how long since you last reloaded - this.scene.executeWithSeedOffset(() => { - orderedTargets = Utils.randSeedShuffle(orderedTargets); - }, this.scene.currentBattle.turn, this.scene.waveSeed); - - orderedTargets.sort((a: Pokemon, b: Pokemon) => { - const aSpeed = a?.getBattleStat(Stat.SPD) || 0; - const bSpeed = b?.getBattleStat(Stat.SPD) || 0; - - return bSpeed - aSpeed; - }); - - const speedReversed = new Utils.BooleanHolder(false); - this.scene.arena.applyTags(TrickRoomTag, speedReversed); - - if (speedReversed.value) { - orderedTargets = orderedTargets.reverse(); - } - - return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : 0)); - } - - executeForAll(func: PokemonFunc): void { - const field = this.scene.getField(true).filter(p => p.summonData); - field.forEach(pokemon => func(pokemon)); - } -} - -export abstract class PokemonPhase extends FieldPhase { - protected battlerIndex: BattlerIndex | integer; - public player: boolean; - public fieldIndex: integer; - - constructor(scene: BattleScene, battlerIndex?: BattlerIndex | integer) { - super(scene); - - if (battlerIndex === undefined) { - battlerIndex = scene.getField().find(p => p?.isActive())!.getBattlerIndex(); // TODO: is the bang correct here? - } - - this.battlerIndex = battlerIndex; - this.player = battlerIndex < 2; - this.fieldIndex = battlerIndex % 2; - } - - getPokemon(): Pokemon { - if (this.battlerIndex > BattlerIndex.ENEMY_2) { - return this.scene.getPokemonById(this.battlerIndex)!; //TODO: is this bang correct? - } - return this.scene.getField()[this.battlerIndex]!; //TODO: is this bang correct? - } -} - -export abstract class PartyMemberPokemonPhase extends FieldPhase { - protected partyMemberIndex: integer; - protected fieldIndex: integer; - protected player: boolean; - - constructor(scene: BattleScene, partyMemberIndex: integer, player: boolean) { - super(scene); - - this.partyMemberIndex = partyMemberIndex; - this.fieldIndex = partyMemberIndex < this.scene.currentBattle.getBattlerCount() - ? partyMemberIndex - : -1; - this.player = player; - } - - getParty(): Pokemon[] { - return this.player ? this.scene.getParty() : this.scene.getEnemyParty(); - } - - getPokemon(): Pokemon { - return this.getParty()[this.partyMemberIndex]; - } -} - -export abstract class PlayerPartyMemberPokemonPhase extends PartyMemberPokemonPhase { - constructor(scene: BattleScene, partyMemberIndex: integer) { - super(scene, partyMemberIndex, true); - } - - getPlayerPokemon(): PlayerPokemon { - return super.getPokemon() as PlayerPokemon; - } -} - -export abstract class EnemyPartyMemberPokemonPhase extends PartyMemberPokemonPhase { - constructor(scene: BattleScene, partyMemberIndex: integer) { - super(scene, partyMemberIndex, false); - } - - getEnemyPokemon(): EnemyPokemon { - return super.getPokemon() as EnemyPokemon; - } -} - -export class EncounterPhase extends BattlePhase { - private loaded: boolean; - - constructor(scene: BattleScene, loaded?: boolean) { - super(scene); - - this.loaded = !!loaded; - } - - start() { - super.start(); - - this.scene.updateGameInfo(); - - this.scene.initSession(); - - this.scene.eventTarget.dispatchEvent(new EncounterPhaseEvent()); - - // Failsafe if players somehow skip floor 200 in classic mode - if (this.scene.gameMode.isClassic && this.scene.currentBattle.waveIndex > 200) { - this.scene.unshiftPhase(new GameOverPhase(this.scene)); - } - - const loadEnemyAssets: Promise[] = []; - - const battle = this.scene.currentBattle; - - let totalBst = 0; - - battle.enemyLevels?.forEach((level, e) => { - if (!this.loaded) { - if (battle.battleType === BattleType.TRAINER) { - battle.enemyParty[e] = battle.trainer?.genPartyMember(e)!; // TODO:: is the bang correct here? - } else { - const enemySpecies = this.scene.randomSpecies(battle.waveIndex, level, true); - battle.enemyParty[e] = this.scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, !!this.scene.getEncounterBossSegments(battle.waveIndex, level, enemySpecies)); - if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { - battle.enemyParty[e].ivs = new Array(6).fill(31); - } - this.scene.getParty().slice(0, !battle.double ? 1 : 2).reverse().forEach(playerPokemon => { - applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, battle.enemyParty[e]); - }); - } - } - const enemyPokemon = this.scene.getEnemyParty()[e]; - if (e < (battle.double ? 2 : 1)) { - enemyPokemon.setX(-66 + enemyPokemon.getFieldPositionOffset()[0]); - enemyPokemon.resetSummonData(); - } - - if (!this.loaded) { - this.scene.gameData.setPokemonSeen(enemyPokemon, true, battle.battleType === BattleType.TRAINER); - } - - if (enemyPokemon.species.speciesId === Species.ETERNATUS) { - if (this.scene.gameMode.isClassic && (battle.battleSpec === BattleSpec.FINAL_BOSS || this.scene.gameMode.isWaveFinal(battle.waveIndex))) { - if (battle.battleSpec !== BattleSpec.FINAL_BOSS) { - enemyPokemon.formIndex = 1; - enemyPokemon.updateScale(); - } - enemyPokemon.setBoss(); - } else if (!(battle.waveIndex % 1000)) { - enemyPokemon.formIndex = 1; - enemyPokemon.updateScale(); - } - } - - totalBst += enemyPokemon.getSpeciesForm().baseTotal; - - loadEnemyAssets.push(enemyPokemon.loadAssets()); - - console.log(getPokemonNameWithAffix(enemyPokemon), enemyPokemon.species.speciesId, enemyPokemon.stats); - }); - - if (this.scene.getParty().filter(p => p.isShiny()).length === 6) { - this.scene.validateAchv(achvs.SHINY_PARTY); - } - - if (battle.battleType === BattleType.TRAINER) { - loadEnemyAssets.push(battle.trainer?.loadAssets().then(() => battle.trainer?.initSprite())!); // TODO: is this bang correct? - } else { - // This block only applies for double battles to init the boss segments (idk why it's split up like this) - if (battle.enemyParty.filter(p => p.isBoss()).length > 1) { - for (const enemyPokemon of battle.enemyParty) { - // If the enemy pokemon is a boss and wasn't populated from data source, then set it up - if (enemyPokemon.isBoss() && !enemyPokemon.isPopulatedFromDataSource) { - enemyPokemon.setBoss(true, Math.ceil(enemyPokemon.bossSegments * (enemyPokemon.getSpeciesForm().baseTotal / totalBst))); - enemyPokemon.initBattleInfo(); - } - } - } - } - - Promise.all(loadEnemyAssets).then(() => { - battle.enemyParty.forEach((enemyPokemon, e) => { - if (e < (battle.double ? 2 : 1)) { - if (battle.battleType === BattleType.WILD) { - this.scene.field.add(enemyPokemon); - battle.seenEnemyPartyMemberIds.add(enemyPokemon.id); - const playerPokemon = this.scene.getPlayerPokemon(); - if (playerPokemon?.visible) { - this.scene.field.moveBelow(enemyPokemon as Pokemon, playerPokemon); - } - enemyPokemon.tint(0, 0.5); - } else if (battle.battleType === BattleType.TRAINER) { - enemyPokemon.setVisible(false); - this.scene.currentBattle.trainer?.tint(0, 0.5); - } - if (battle.double) { - enemyPokemon.setFieldPosition(e ? FieldPosition.RIGHT : FieldPosition.LEFT); - } - } - }); - - if (!this.loaded) { - regenerateModifierPoolThresholds(this.scene.getEnemyField(), battle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD); - this.scene.generateEnemyModifiers(); - } - - this.scene.ui.setMode(Mode.MESSAGE).then(() => { - if (!this.loaded) { - //@ts-ignore - this.scene.gameData.saveAll(this.scene, true, battle.waveIndex % 10 === 1 || this.scene.lastSavePlayTime >= 300).then(success => { // TODO: get rid of ts-ignore - this.scene.disableMenu = false; - if (!success) { - return this.scene.reset(true); - } - this.doEncounter(); - }); - } else { - this.doEncounter(); - } - }); - }); - } - - doEncounter() { - this.scene.playBgm(undefined, true); - this.scene.updateModifiers(false); - this.scene.setFieldScale(1); - - /*if (startingWave > 10) { - for (let m = 0; m < Math.min(Math.floor(startingWave / 10), 99); m++) - this.scene.addModifier(getPlayerModifierTypeOptionsForWave((m + 1) * 10, 1, this.scene.getParty())[0].type.newModifier(), true); - this.scene.updateModifiers(true); - }*/ - - for (const pokemon of this.scene.getParty()) { - if (pokemon) { - pokemon.resetBattleData(); - } - } - - if (!this.loaded) { - this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena), false); - } - - const enemyField = this.scene.getEnemyField(); - this.scene.tweens.add({ - targets: [this.scene.arenaEnemy, this.scene.currentBattle.trainer, enemyField, this.scene.arenaPlayer, this.scene.trainer].flat(), - x: (_target, _key, value, fieldIndex: integer) => fieldIndex < 2 + (enemyField.length) ? value + 300 : value - 300, - duration: 2000, - onComplete: () => { - if (!this.tryOverrideForBattleSpec()) { - this.doEncounterCommon(); - } - } - }); - } - - getEncounterMessage(): string { - const enemyField = this.scene.getEnemyField(); - - if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { - return i18next.t("battle:bossAppeared", { bossName: getPokemonNameWithAffix(enemyField[0])}); - } - - if (this.scene.currentBattle.battleType === BattleType.TRAINER) { - if (this.scene.currentBattle.double) { - return i18next.t("battle:trainerAppearedDouble", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }); - - } else { - return i18next.t("battle:trainerAppeared", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }); - } - } - - return enemyField.length === 1 - ? i18next.t("battle:singleWildAppeared", { pokemonName: enemyField[0].getNameToRender() }) - : i18next.t("battle:multiWildAppeared", { pokemonName1: enemyField[0].getNameToRender(), pokemonName2: enemyField[1].getNameToRender() }); - } - - doEncounterCommon(showEncounterMessage: boolean = true) { - const enemyField = this.scene.getEnemyField(); - - if (this.scene.currentBattle.battleType === BattleType.WILD) { - enemyField.forEach(enemyPokemon => { - enemyPokemon.untint(100, "Sine.easeOut"); - enemyPokemon.cry(); - enemyPokemon.showInfo(); - if (enemyPokemon.isShiny()) { - this.scene.validateAchv(achvs.SEE_SHINY); - } - }); - this.scene.updateFieldScale(); - if (showEncounterMessage) { - this.scene.ui.showText(this.getEncounterMessage(), null, () => this.end(), 1500); - } else { - this.end(); - } - } else if (this.scene.currentBattle.battleType === BattleType.TRAINER) { - const trainer = this.scene.currentBattle.trainer; - trainer?.untint(100, "Sine.easeOut"); - trainer?.playAnim(); - - const doSummon = () => { - this.scene.currentBattle.started = true; - this.scene.playBgm(undefined); - this.scene.pbTray.showPbTray(this.scene.getParty()); - this.scene.pbTrayEnemy.showPbTray(this.scene.getEnemyParty()); - const doTrainerSummon = () => { - this.hideEnemyTrainer(); - const availablePartyMembers = this.scene.getEnemyParty().filter(p => !p.isFainted()).length; - this.scene.unshiftPhase(new SummonPhase(this.scene, 0, false)); - if (this.scene.currentBattle.double && availablePartyMembers > 1) { - this.scene.unshiftPhase(new SummonPhase(this.scene, 1, false)); - } - this.end(); - }; - if (showEncounterMessage) { - this.scene.ui.showText(this.getEncounterMessage(), null, doTrainerSummon, 1500, true); - } else { - doTrainerSummon(); - } - }; - - const encounterMessages = this.scene.currentBattle.trainer?.getEncounterMessages(); - - if (!encounterMessages?.length) { - doSummon(); - } else { - let message: string; - this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex); - message = message!; // tell TS compiler it's defined now - const showDialogueAndSummon = () => { - this.scene.ui.showDialogue(message, trainer?.getName(TrainerSlot.NONE, true), null, () => { - this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doSummon())); - }); - }; - if (this.scene.currentBattle.trainer?.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { - this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(trainer?.getKey()!, getCharVariantFromDialogue(encounterMessages[0])).then(() => showDialogueAndSummon())); // TODO: is this bang correct? - } else { - showDialogueAndSummon(); - } - } - } - } - - end() { - const enemyField = this.scene.getEnemyField(); - - enemyField.forEach((enemyPokemon, e) => { - if (enemyPokemon.isShiny()) { - this.scene.unshiftPhase(new ShinySparklePhase(this.scene, BattlerIndex.ENEMY + e)); - } - }); - - if (this.scene.currentBattle.battleType !== BattleType.TRAINER) { - enemyField.map(p => this.scene.pushConditionalPhase(new PostSummonPhase(this.scene, p.getBattlerIndex()), () => { - // if there is not a player party, we can't continue - if (!this.scene.getParty()?.length) { - return false; - } - // how many player pokemon are on the field ? - const pokemonsOnFieldCount = this.scene.getParty().filter(p => p.isOnField()).length; - // if it's a 2vs1, there will never be a 2nd pokemon on our field even - const requiredPokemonsOnField = Math.min(this.scene.getParty().filter((p) => !p.isFainted()).length, 2); - // if it's a double, there should be 2, otherwise 1 - if (this.scene.currentBattle.double) { - return pokemonsOnFieldCount === requiredPokemonsOnField; - } - return pokemonsOnFieldCount === 1; - })); - const ivScannerModifier = this.scene.findModifier(m => m instanceof IvScannerModifier); - if (ivScannerModifier) { - enemyField.map(p => this.scene.pushPhase(new ScanIvsPhase(this.scene, p.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6)))); - } - } - - if (!this.loaded) { - const availablePartyMembers = this.scene.getParty().filter(p => p.isAllowedInBattle()); - - if (!availablePartyMembers[0].isOnField()) { - this.scene.pushPhase(new SummonPhase(this.scene, 0)); - } - - if (this.scene.currentBattle.double) { - if (availablePartyMembers.length > 1) { - this.scene.pushPhase(new ToggleDoublePositionPhase(this.scene, true)); - if (!availablePartyMembers[1].isOnField()) { - this.scene.pushPhase(new SummonPhase(this.scene, 1)); - } - } - } else { - if (availablePartyMembers.length > 1 && availablePartyMembers[1].isOnField()) { - this.scene.pushPhase(new ReturnPhase(this.scene, 1)); - } - this.scene.pushPhase(new ToggleDoublePositionPhase(this.scene, false)); - } - - if (this.scene.currentBattle.battleType !== BattleType.TRAINER && (this.scene.currentBattle.waveIndex > 1 || !this.scene.gameMode.isDaily)) { - const minPartySize = this.scene.currentBattle.double ? 2 : 1; - if (availablePartyMembers.length > minPartySize) { - this.scene.pushPhase(new CheckSwitchPhase(this.scene, 0, this.scene.currentBattle.double)); - if (this.scene.currentBattle.double) { - this.scene.pushPhase(new CheckSwitchPhase(this.scene, 1, this.scene.currentBattle.double)); - } - } - } - } - handleTutorial(this.scene, Tutorial.Access_Menu).then(() => super.end()); - } - - tryOverrideForBattleSpec(): boolean { - switch (this.scene.currentBattle.battleSpec) { - case BattleSpec.FINAL_BOSS: - const enemy = this.scene.getEnemyPokemon(); - this.scene.ui.showText(this.getEncounterMessage(), null, () => { - const count = 5643853 + this.scene.gameData.gameStats.classicSessionsPlayed; - //The two lines below check if English ordinals (1st, 2nd, 3rd, Xth) are used and determine which one to use. - //Otherwise, it defaults to an empty string. - //As of 08-07-24: Spanish and Italian default to the English translations - const ordinalUse = ["en", "es", "it"]; - const currentLanguage = i18next.resolvedLanguage ?? "en"; - const ordinalIndex = (ordinalUse.includes(currentLanguage)) ? ["st", "nd", "rd"][((count + 90) % 100 - 10) % 10 - 1] ?? "th" : ""; - const cycleCount = count.toLocaleString() + ordinalIndex; - const encounterDialogue = i18next.t(`${(this.scene.gameData.gender === PlayerGender.FEMALE) ? "PGF" : "PGM"}battleSpecDialogue:encounter`, {cycleCount: cycleCount}); - this.scene.ui.showDialogue(encounterDialogue, enemy?.species.name, null, () => { - this.doEncounterCommon(false); - }); - }, 1500, true); - return true; - } - - return false; - } -} - -export class NextEncounterPhase extends EncounterPhase { - constructor(scene: BattleScene) { - super(scene); - } - - start() { - super.start(); - } - - doEncounter(): void { - this.scene.playBgm(undefined, true); - - for (const pokemon of this.scene.getParty()) { - if (pokemon) { - pokemon.resetBattleData(); - } - } - - this.scene.arenaNextEnemy.setBiome(this.scene.arena.biomeType); - this.scene.arenaNextEnemy.setVisible(true); - - const enemyField = this.scene.getEnemyField(); - this.scene.tweens.add({ - targets: [this.scene.arenaEnemy, this.scene.arenaNextEnemy, this.scene.currentBattle.trainer, enemyField, this.scene.lastEnemyTrainer].flat(), - x: "+=300", - duration: 2000, - onComplete: () => { - this.scene.arenaEnemy.setBiome(this.scene.arena.biomeType); - this.scene.arenaEnemy.setX(this.scene.arenaNextEnemy.x); - this.scene.arenaEnemy.setAlpha(1); - this.scene.arenaNextEnemy.setX(this.scene.arenaNextEnemy.x - 300); - this.scene.arenaNextEnemy.setVisible(false); - if (this.scene.lastEnemyTrainer) { - this.scene.lastEnemyTrainer.destroy(); - } - - if (!this.tryOverrideForBattleSpec()) { - this.doEncounterCommon(); - } - } - }); - } -} - -export class NewBiomeEncounterPhase extends NextEncounterPhase { - constructor(scene: BattleScene) { - super(scene); - } - - doEncounter(): void { - this.scene.playBgm(undefined, true); - - for (const pokemon of this.scene.getParty()) { - if (pokemon) { - pokemon.resetBattleData(); - } - } - - this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena), false); - - for (const pokemon of this.scene.getParty().filter(p => p.isOnField())) { - applyAbAttrs(PostBiomeChangeAbAttr, pokemon, null); - } - - const enemyField = this.scene.getEnemyField(); - this.scene.tweens.add({ - targets: [this.scene.arenaEnemy, enemyField].flat(), - x: "+=300", - duration: 2000, - onComplete: () => { - if (!this.tryOverrideForBattleSpec()) { - this.doEncounterCommon(); - } - } - }); - } -} - -export class PostSummonPhase extends PokemonPhase { - constructor(scene: BattleScene, battlerIndex: BattlerIndex) { - super(scene, battlerIndex); - } - - start() { - super.start(); - - const pokemon = this.getPokemon(); - - if (pokemon.status?.effect === StatusEffect.TOXIC) { - pokemon.status.turnCount = 0; - } - this.scene.arena.applyTags(ArenaTrapTag, pokemon); - applyPostSummonAbAttrs(PostSummonAbAttr, pokemon).then(() => this.end()); - } -} - -export class SelectBiomePhase extends BattlePhase { - constructor(scene: BattleScene) { - super(scene); - } - - start() { - super.start(); - - const currentBiome = this.scene.arena.biomeType; - - const setNextBiome = (nextBiome: Biome) => { - if (this.scene.currentBattle.waveIndex % 10 === 1) { - this.scene.applyModifiers(MoneyInterestModifier, true, this.scene); - this.scene.unshiftPhase(new PartyHealPhase(this.scene, false)); - } - this.scene.unshiftPhase(new SwitchBiomePhase(this.scene, nextBiome)); - this.end(); - }; - - if ((this.scene.gameMode.isClassic && this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex + 9)) - || (this.scene.gameMode.isDaily && this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) - || (this.scene.gameMode.hasShortBiomes && !(this.scene.currentBattle.waveIndex % 50))) { - setNextBiome(Biome.END); - } else if (this.scene.gameMode.hasRandomBiomes) { - setNextBiome(this.generateNextBiome()); - } else if (Array.isArray(biomeLinks[currentBiome])) { - let biomes: Biome[] = []; - this.scene.executeWithSeedOffset(() => { - biomes = (biomeLinks[currentBiome] as (Biome | [Biome, integer])[]) - .filter(b => !Array.isArray(b) || !Utils.randSeedInt(b[1])) - .map(b => !Array.isArray(b) ? b : b[0]); - }, this.scene.currentBattle.waveIndex); - if (biomes.length > 1 && this.scene.findModifier(m => m instanceof MapModifier)) { - let biomeChoices: Biome[] = []; - this.scene.executeWithSeedOffset(() => { - biomeChoices = (!Array.isArray(biomeLinks[currentBiome]) - ? [biomeLinks[currentBiome] as Biome] - : biomeLinks[currentBiome] as (Biome | [Biome, integer])[]) - .filter((b, i) => !Array.isArray(b) || !Utils.randSeedInt(b[1])) - .map(b => Array.isArray(b) ? b[0] : b); - }, this.scene.currentBattle.waveIndex); - const biomeSelectItems = biomeChoices.map(b => { - const ret: OptionSelectItem = { - label: getBiomeName(b), - handler: () => { - this.scene.ui.setMode(Mode.MESSAGE); - setNextBiome(b); - return true; - } - }; - return ret; - }); - this.scene.ui.setMode(Mode.OPTION_SELECT, { - options: biomeSelectItems, - delay: 1000 - }); - } else { - setNextBiome(biomes[Utils.randSeedInt(biomes.length)]); - } - } else if (biomeLinks.hasOwnProperty(currentBiome)) { - setNextBiome(biomeLinks[currentBiome] as Biome); - } else { - setNextBiome(this.generateNextBiome()); - } - } - - generateNextBiome(): Biome { - if (!(this.scene.currentBattle.waveIndex % 50)) { - return Biome.END; - } - return this.scene.generateRandomBiome(this.scene.currentBattle.waveIndex); - } -} - -export class SwitchBiomePhase extends BattlePhase { - private nextBiome: Biome; - - constructor(scene: BattleScene, nextBiome: Biome) { - super(scene); - - this.nextBiome = nextBiome; - } - - start() { - super.start(); - - if (this.nextBiome === undefined) { - return this.end(); - } - - this.scene.tweens.add({ - targets: [this.scene.arenaEnemy, this.scene.lastEnemyTrainer], - x: "+=300", - duration: 2000, - onComplete: () => { - this.scene.arenaEnemy.setX(this.scene.arenaEnemy.x - 600); - - this.scene.newArena(this.nextBiome); - - const biomeKey = getBiomeKey(this.nextBiome); - const bgTexture = `${biomeKey}_bg`; - this.scene.arenaBgTransition.setTexture(bgTexture); - this.scene.arenaBgTransition.setAlpha(0); - this.scene.arenaBgTransition.setVisible(true); - this.scene.arenaPlayerTransition.setBiome(this.nextBiome); - this.scene.arenaPlayerTransition.setAlpha(0); - this.scene.arenaPlayerTransition.setVisible(true); - - this.scene.tweens.add({ - targets: [this.scene.arenaPlayer, this.scene.arenaBgTransition, this.scene.arenaPlayerTransition], - duration: 1000, - delay: 1000, - ease: "Sine.easeInOut", - alpha: (target: any) => target === this.scene.arenaPlayer ? 0 : 1, - onComplete: () => { - this.scene.arenaBg.setTexture(bgTexture); - this.scene.arenaPlayer.setBiome(this.nextBiome); - this.scene.arenaPlayer.setAlpha(1); - this.scene.arenaEnemy.setBiome(this.nextBiome); - this.scene.arenaEnemy.setAlpha(1); - this.scene.arenaNextEnemy.setBiome(this.nextBiome); - this.scene.arenaBgTransition.setVisible(false); - this.scene.arenaPlayerTransition.setVisible(false); - if (this.scene.lastEnemyTrainer) { - this.scene.lastEnemyTrainer.destroy(); - } - - this.end(); - } - }); - } - }); - } -} - -export class SummonPhase extends PartyMemberPokemonPhase { - private loaded: boolean; - - constructor(scene: BattleScene, fieldIndex: integer, player: boolean = true, loaded: boolean = false) { - super(scene, fieldIndex, player); - - this.loaded = loaded; - } - - start() { - super.start(); - - this.preSummon(); - } - - /** - * Sends out a Pokemon before the battle begins and shows the appropriate messages - */ - preSummon(): void { - const partyMember = this.getPokemon(); - // If the Pokemon about to be sent out is fainted or illegal under a challenge, switch to the first non-fainted legal Pokemon - if (!partyMember.isAllowedInBattle()) { - console.warn("The Pokemon about to be sent out is fainted or illegal under a challenge. Attempting to resolve..."); - - // First check if they're somehow still in play, if so remove them. - if (partyMember.isOnField()) { - partyMember.leaveField(); - } - - const party = this.getParty(); - - // Find the first non-fainted Pokemon index above the current one - const legalIndex = party.findIndex((p, i) => i > this.partyMemberIndex && p.isAllowedInBattle()); - if (legalIndex === -1) { - console.error("Party Details:\n", party); - console.error("All available Pokemon were fainted or illegal!"); - this.scene.clearPhaseQueue(); - this.scene.unshiftPhase(new GameOverPhase(this.scene)); - this.end(); - return; - } - - // Swaps the fainted Pokemon and the first non-fainted legal Pokemon in the party - [party[this.partyMemberIndex], party[legalIndex]] = [party[legalIndex], party[this.partyMemberIndex]]; - console.warn("Swapped %s %O with %s %O", getPokemonNameWithAffix(partyMember), partyMember, getPokemonNameWithAffix(party[0]), party[0]); - } - - if (this.player) { - this.scene.ui.showText(i18next.t("battle:playerGo", { pokemonName: getPokemonNameWithAffix(this.getPokemon()) })); - if (this.player) { - this.scene.pbTray.hide(); - } - this.scene.trainer.setTexture(`trainer_${this.scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`); - this.scene.time.delayedCall(562, () => { - this.scene.trainer.setFrame("2"); - this.scene.time.delayedCall(64, () => { - this.scene.trainer.setFrame("3"); - }); - }); - this.scene.tweens.add({ - targets: this.scene.trainer, - x: -36, - duration: 1000, - onComplete: () => this.scene.trainer.setVisible(false) - }); - this.scene.time.delayedCall(750, () => this.summon()); - } else { - const trainerName = this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); - const pokemonName = this.getPokemon().getNameToRender(); - const message = i18next.t("battle:trainerSendOut", { trainerName, pokemonName }); - - this.scene.pbTrayEnemy.hide(); - this.scene.ui.showText(message, null, () => this.summon()); - } - } - - summon(): void { - const pokemon = this.getPokemon(); - - const pokeball = this.scene.addFieldSprite(this.player ? 36 : 248, this.player ? 80 : 44, "pb", getPokeballAtlasKey(pokemon.pokeball)); - pokeball.setVisible(false); - pokeball.setOrigin(0.5, 0.625); - this.scene.field.add(pokeball); - - if (this.fieldIndex === 1) { - pokemon.setFieldPosition(FieldPosition.RIGHT, 0); - } else { - const availablePartyMembers = this.getParty().filter(p => p.isAllowedInBattle()).length; - pokemon.setFieldPosition(!this.scene.currentBattle.double || availablePartyMembers === 1 ? FieldPosition.CENTER : FieldPosition.LEFT); - } - - const fpOffset = pokemon.getFieldPositionOffset(); - - pokeball.setVisible(true); - - this.scene.tweens.add({ - targets: pokeball, - duration: 650, - x: (this.player ? 100 : 236) + fpOffset[0] - }); - - this.scene.tweens.add({ - targets: pokeball, - duration: 150, - ease: "Cubic.easeOut", - y: (this.player ? 70 : 34) + fpOffset[1], - onComplete: () => { - this.scene.tweens.add({ - targets: pokeball, - duration: 500, - ease: "Cubic.easeIn", - angle: 1440, - y: (this.player ? 132 : 86) + fpOffset[1], - onComplete: () => { - this.scene.playSound("pb_rel"); - pokeball.destroy(); - this.scene.add.existing(pokemon); - this.scene.field.add(pokemon); - if (!this.player) { - const playerPokemon = this.scene.getPlayerPokemon() as Pokemon; - if (playerPokemon?.visible) { - this.scene.field.moveBelow(pokemon, playerPokemon); - } - this.scene.currentBattle.seenEnemyPartyMemberIds.add(pokemon.id); - } - addPokeballOpenParticles(this.scene, pokemon.x, pokemon.y - 16, pokemon.pokeball); - this.scene.updateModifiers(this.player); - this.scene.updateFieldScale(); - pokemon.showInfo(); - pokemon.playAnim(); - pokemon.setVisible(true); - pokemon.getSprite().setVisible(true); - pokemon.setScale(0.5); - pokemon.tint(getPokeballTintColor(pokemon.pokeball)); - pokemon.untint(250, "Sine.easeIn"); - this.scene.updateFieldScale(); - this.scene.tweens.add({ - targets: pokemon, - duration: 250, - ease: "Sine.easeIn", - scale: pokemon.getSpriteScale(), - onComplete: () => { - pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 }); - pokemon.getSprite().clearTint(); - pokemon.resetSummonData(); - this.scene.time.delayedCall(1000, () => this.end()); - } - }); - } - }); - } - }); - } - - onEnd(): void { - const pokemon = this.getPokemon(); - - if (pokemon.isShiny()) { - this.scene.unshiftPhase(new ShinySparklePhase(this.scene, pokemon.getBattlerIndex())); - } - - pokemon.resetTurnData(); - - if (!this.loaded || this.scene.currentBattle.battleType === BattleType.TRAINER || (this.scene.currentBattle.waveIndex % 10) === 1) { - this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true); - this.queuePostSummon(); - } - } - - queuePostSummon(): void { - this.scene.pushPhase(new PostSummonPhase(this.scene, this.getPokemon().getBattlerIndex())); - } - - end() { - this.onEnd(); - - super.end(); - } -} - -export class SwitchSummonPhase extends SummonPhase { - private slotIndex: integer; - private doReturn: boolean; - private batonPass: boolean; - - private lastPokemon: Pokemon; - - /** - * Constructor for creating a new SwitchSummonPhase - * @param scene {@linkcode BattleScene} the scene the phase is associated with - * @param fieldIndex integer representing position on the battle field - * @param slotIndex integer for the index of pokemon (in party of 6) to switch into - * @param doReturn boolean whether to render "comeback" dialogue - * @param batonPass boolean if the switch is from baton pass - * @param player boolean if the switch is from the player - */ - constructor(scene: BattleScene, fieldIndex: integer, slotIndex: integer, doReturn: boolean, batonPass: boolean, player?: boolean) { - super(scene, fieldIndex, player !== undefined ? player : true); - - this.slotIndex = slotIndex; - this.doReturn = doReturn; - this.batonPass = batonPass; - } - - start(): void { - super.start(); - } - - preSummon(): void { - if (!this.player) { - if (this.slotIndex === -1) { - //@ts-ignore - this.slotIndex = this.scene.currentBattle.trainer?.getNextSummonIndex(!this.fieldIndex ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); // TODO: what would be the default trainer-slot fallback? - } - if (this.slotIndex > -1) { - this.showEnemyTrainer(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); - this.scene.pbTrayEnemy.showPbTray(this.scene.getEnemyParty()); - } - } - - if (!this.doReturn || (this.slotIndex !== -1 && !(this.player ? this.scene.getParty() : this.scene.getEnemyParty())[this.slotIndex])) { - if (this.player) { - return this.switchAndSummon(); - } else { - this.scene.time.delayedCall(750, () => this.switchAndSummon()); - return; - } - } - - const pokemon = this.getPokemon(); - - if (!this.batonPass) { - (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.removeTagsBySourceId(pokemon.id)); - } - - this.scene.ui.showText(this.player ? - i18next.t("battle:playerComeBack", { pokemonName: getPokemonNameWithAffix(pokemon) }) : - i18next.t("battle:trainerComeBack", { - trainerName: this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER), - pokemonName: getPokemonNameWithAffix(pokemon) - }) - ); - this.scene.playSound("pb_rel"); - pokemon.hideInfo(); // this is also done by pokemon.leaveField(), but needs to go earlier for animation purposes - pokemon.tint(getPokeballTintColor(pokemon.pokeball), 1, 250, "Sine.easeIn"); - this.scene.tweens.add({ - targets: pokemon, - duration: 250, - ease: "Sine.easeIn", - scale: 0.5, - onComplete: () => { - // 300ms delay on leaveField is necessary to avoid calling hideInfo() twice - // and double-animating the stats panel slideout - this.scene.time.delayedCall(300, () => pokemon.leaveField(!this.batonPass)); - this.scene.time.delayedCall(750, () => this.switchAndSummon()); - } - }); - } - - switchAndSummon() { - const party = this.player ? this.getParty() : this.scene.getEnemyParty(); - const switchedInPokemon = party[this.slotIndex]; - this.lastPokemon = this.getPokemon(); - applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, this.lastPokemon); - if (this.batonPass && switchedInPokemon) { - (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.transferTagsBySourceId(this.lastPokemon.id, switchedInPokemon.id)); - if (!this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id)) { - const batonPassModifier = this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier - && (m as SwitchEffectTransferModifier).pokemonId === this.lastPokemon.id) as SwitchEffectTransferModifier; - if (batonPassModifier && !this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id)) { - this.scene.tryTransferHeldItemModifier(batonPassModifier, switchedInPokemon, false); - } - } - } - if (switchedInPokemon) { - party[this.slotIndex] = this.lastPokemon; - party[this.fieldIndex] = switchedInPokemon; - const showTextAndSummon = () => { - this.scene.ui.showText(this.player ? - i18next.t("battle:playerGo", { pokemonName: getPokemonNameWithAffix(switchedInPokemon) }) : - i18next.t("battle:trainerGo", { - trainerName: this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER), - pokemonName: this.getPokemon().getNameToRender() - }) - ); - // Ensure improperly persisted summon data (such as tags) is cleared upon switching - if (!this.batonPass) { - switchedInPokemon.resetBattleData(); - switchedInPokemon.resetSummonData(); - } - this.summon(); - }; - if (this.player) { - showTextAndSummon(); - } else { - this.scene.time.delayedCall(1500, () => { - this.hideEnemyTrainer(); - this.scene.pbTrayEnemy.hide(); - showTextAndSummon(); - }); - } - } else { - this.end(); - } - } - - onEnd(): void { - super.onEnd(); - - const pokemon = this.getPokemon(); - - const moveId = this.lastPokemon?.scene.currentBattle.lastMove; - const lastUsedMove = moveId ? allMoves[moveId] : undefined; - - const currentCommand = pokemon.scene.currentBattle.turnCommands[this.fieldIndex]?.command; - const lastPokemonIsForceSwitchedAndNotFainted = lastUsedMove?.hasAttr(ForceSwitchOutAttr) && !this.lastPokemon.isFainted(); - - // Compensate for turn spent summoning - // Or compensate for force switch move if switched out pokemon is not fainted - if (currentCommand === Command.POKEMON || lastPokemonIsForceSwitchedAndNotFainted) { - pokemon.battleSummonData.turnCount--; - } - - if (this.batonPass && pokemon) { - pokemon.transferSummon(this.lastPokemon); - } - - this.lastPokemon?.resetSummonData(); - - this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true); - } - - queuePostSummon(): void { - this.scene.unshiftPhase(new PostSummonPhase(this.scene, this.getPokemon().getBattlerIndex())); - } -} - -export class ReturnPhase extends SwitchSummonPhase { - constructor(scene: BattleScene, fieldIndex: integer) { - super(scene, fieldIndex, -1, true, false); - } - - switchAndSummon(): void { - this.end(); - } - - summon(): void { } - - onEnd(): void { - const pokemon = this.getPokemon(); - - pokemon.resetTurnData(); - pokemon.resetSummonData(); - - this.scene.updateFieldScale(); - - this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger); - } -} - -export class ShowTrainerPhase extends BattlePhase { - constructor(scene: BattleScene) { - super(scene); - } - - start() { - super.start(); - - this.scene.trainer.setVisible(true); - - this.scene.trainer.setTexture(`trainer_${this.scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`); - - this.scene.tweens.add({ - targets: this.scene.trainer, - x: 106, - duration: 1000, - onComplete: () => this.end() - }); - } -} - -export class ToggleDoublePositionPhase extends BattlePhase { - private double: boolean; - - constructor(scene: BattleScene, double: boolean) { - super(scene); - - this.double = double; - } - - start() { - super.start(); - - const playerPokemon = this.scene.getPlayerField().find(p => p.isActive(true)); - if (playerPokemon) { - playerPokemon.setFieldPosition(this.double && this.scene.getParty().filter(p => p.isAllowedInBattle()).length > 1 ? FieldPosition.LEFT : FieldPosition.CENTER, 500).then(() => { - if (playerPokemon.getFieldIndex() === 1) { - const party = this.scene.getParty(); - party[1] = party[0]; - party[0] = playerPokemon; - } - this.end(); - }); - } else { - this.end(); - } - } -} - -export class CheckSwitchPhase extends BattlePhase { - protected fieldIndex: integer; - protected useName: boolean; - - constructor(scene: BattleScene, fieldIndex: integer, useName: boolean) { - super(scene); - - this.fieldIndex = fieldIndex; - this.useName = useName; - } - - start() { - super.start(); - - const pokemon = this.scene.getPlayerField()[this.fieldIndex]; - - if (this.scene.battleStyle === BattleStyle.SET) { - super.end(); - return; - } - - if (this.scene.field.getAll().indexOf(pokemon) === -1) { - this.scene.unshiftPhase(new SummonMissingPhase(this.scene, this.fieldIndex)); - super.end(); - return; - } - - if (!this.scene.getParty().slice(1).filter(p => p.isActive()).length) { - super.end(); - return; - } - - if (pokemon.getTag(BattlerTagType.FRENZY)) { - super.end(); - return; - } - - this.scene.ui.showText(i18next.t("battle:switchQuestion", { pokemonName: this.useName ? getPokemonNameWithAffix(pokemon) : i18next.t("battle:pokemon") }), null, () => { - this.scene.ui.setMode(Mode.CONFIRM, () => { - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.tryRemovePhase(p => p instanceof PostSummonPhase && p.player && p.fieldIndex === this.fieldIndex); - this.scene.unshiftPhase(new SwitchPhase(this.scene, this.fieldIndex, false, true)); - this.end(); - }, () => { - this.scene.ui.setMode(Mode.MESSAGE); - this.end(); - }); - }); - } -} - -export class SummonMissingPhase extends SummonPhase { - constructor(scene: BattleScene, fieldIndex: integer) { - super(scene, fieldIndex); - } - - preSummon(): void { - this.scene.ui.showText(i18next.t("battle:sendOutPokemon", { pokemonName: getPokemonNameWithAffix(this.getPokemon()) })); - this.scene.time.delayedCall(250, () => this.summon()); - } -} - -export class LevelCapPhase extends FieldPhase { - constructor(scene: BattleScene) { - super(scene); - } - - start(): void { - super.start(); - - this.scene.ui.setMode(Mode.MESSAGE).then(() => { - this.scene.playSound("level_up_fanfare"); - this.scene.ui.showText(i18next.t("battle:levelCapUp", { levelCap: this.scene.getMaxExpLevel() }), null, () => this.end(), null, true); - this.executeForAll(pokemon => pokemon.updateInfo(true)); - }); - } -} - -export class TurnInitPhase extends FieldPhase { - constructor(scene: BattleScene) { - super(scene); - } - - start() { - super.start(); - - this.scene.getPlayerField().forEach(p => { - // If this pokemon is in play and evolved into something illegal under the current challenge, force a switch - if (p.isOnField() && !p.isAllowedInBattle()) { - this.scene.queueMessage(i18next.t("challenges:illegalEvolution", { "pokemon": p.name }), null, true); - - const allowedPokemon = this.scene.getParty().filter(p => p.isAllowedInBattle()); - - if (!allowedPokemon.length) { - // If there are no longer any legal pokemon in the party, game over. - this.scene.clearPhaseQueue(); - this.scene.unshiftPhase(new GameOverPhase(this.scene)); - } else if (allowedPokemon.length >= this.scene.currentBattle.getBattlerCount() || (this.scene.currentBattle.double && !allowedPokemon[0].isActive(true))) { - // If there is at least one pokemon in the back that is legal to switch in, force a switch. - p.switchOut(false); - } else { - // If there are no pokemon in the back but we're not game overing, just hide the pokemon. - // This should only happen in double battles. - p.leaveField(); - } - if (allowedPokemon.length === 1 && this.scene.currentBattle.double) { - this.scene.unshiftPhase(new ToggleDoublePositionPhase(this.scene, true)); - } - } - }); - - //this.scene.pushPhase(new MoveAnimTestPhase(this.scene)); - this.scene.eventTarget.dispatchEvent(new TurnInitEvent()); - - this.scene.getField().forEach((pokemon, i) => { - if (pokemon?.isActive()) { - if (pokemon.isPlayer()) { - this.scene.currentBattle.addParticipant(pokemon as PlayerPokemon); - } - - pokemon.resetTurnData(); - - this.scene.pushPhase(pokemon.isPlayer() ? new CommandPhase(this.scene, i) : new EnemyCommandPhase(this.scene, i - BattlerIndex.ENEMY)); - } - }); - - this.scene.pushPhase(new TurnStartPhase(this.scene)); - - this.end(); - } -} - -export class CommandPhase extends FieldPhase { - protected fieldIndex: integer; - - constructor(scene: BattleScene, fieldIndex: integer) { - super(scene); - - this.fieldIndex = fieldIndex; - } - - start() { - super.start(); - - if (this.fieldIndex) { - // If we somehow are attempting to check the right pokemon but there's only one pokemon out - // Switch back to the center pokemon. This can happen rarely in double battles with mid turn switching - if (this.scene.getPlayerField().filter(p => p.isActive()).length === 1) { - this.fieldIndex = FieldPosition.CENTER; - } else { - const allyCommand = this.scene.currentBattle.turnCommands[this.fieldIndex - 1]; - if (allyCommand?.command === Command.BALL || allyCommand?.command === Command.RUN) { - this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: allyCommand?.command, skip: true }; - } - } - } - - if (this.scene.currentBattle.turnCommands[this.fieldIndex]?.skip) { - return this.end(); - } - - const playerPokemon = this.scene.getPlayerField()[this.fieldIndex]; - - const moveQueue = playerPokemon.getMoveQueue(); - - while (moveQueue.length && moveQueue[0] - && moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move) - || !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move)]!.isUsable(playerPokemon, moveQueue[0].ignorePP))) { // TODO: is the bang correct? - moveQueue.shift(); - } - - if (moveQueue.length) { - const queuedMove = moveQueue[0]; - if (!queuedMove.move) { - this.handleCommand(Command.FIGHT, -1, false); - } else { - const moveIndex = playerPokemon.getMoveset().findIndex(m => m?.moveId === queuedMove.move); - if (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex]!.isUsable(playerPokemon, queuedMove.ignorePP)) { // TODO: is the bang correct? - this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, { targets: queuedMove.targets, multiple: queuedMove.targets.length > 1 }); - } else { - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - } - } - } else { - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - } - } - - handleCommand(command: Command, cursor: integer, ...args: any[]): boolean { - const playerPokemon = this.scene.getPlayerField()[this.fieldIndex]; - const enemyField = this.scene.getEnemyField(); - let success: boolean; - - switch (command) { - case Command.FIGHT: - let useStruggle = false; - if (cursor === -1 || - playerPokemon.trySelectMove(cursor, args[0] as boolean) || - (useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m?.isUsable(playerPokemon)).length)) { - const moveId = !useStruggle ? cursor > -1 ? playerPokemon.getMoveset()[cursor]!.moveId : Moves.NONE : Moves.STRUGGLE; // TODO: is the bang correct? - const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: { move: moveId, targets: [], ignorePP: args[0] }, args: args }; - const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, moveId) : args[2]; - if (!moveId) { - turnCommand.targets = [this.fieldIndex]; - } - console.log(moveTargets, getPokemonNameWithAffix(playerPokemon)); - if (moveTargets.targets.length > 1 && moveTargets.multiple) { - this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex)); - } - if (moveTargets.targets.length <= 1 || moveTargets.multiple) { - turnCommand.move!.targets = moveTargets.targets; //TODO: is the bang correct here? - } else if (playerPokemon.getTag(BattlerTagType.CHARGING) && playerPokemon.getMoveQueue().length >= 1) { - turnCommand.move!.targets = playerPokemon.getMoveQueue()[0].targets; //TODO: is the bang correct here? - } else { - this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex)); - } - this.scene.currentBattle.turnCommands[this.fieldIndex] = turnCommand; - success = true; - } else if (cursor < playerPokemon.getMoveset().length) { - const move = playerPokemon.getMoveset()[cursor]!; //TODO: is this bang correct? - this.scene.ui.setMode(Mode.MESSAGE); - - // Decides between a Disabled, Not Implemented, or No PP translation message - const errorMessage = - playerPokemon.summonData.disabledMove === move.moveId ? "battle:moveDisabled" : - move.getName().endsWith(" (N)") ? "battle:moveNotImplemented" : "battle:moveNoPP"; - const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator - - this.scene.ui.showText(i18next.t(errorMessage, { moveName: moveName }), null, () => { - this.scene.ui.clearText(); - this.scene.ui.setMode(Mode.FIGHT, this.fieldIndex); - }, null, true); - } - break; - case Command.BALL: - if (!this.scene.gameMode.isFreshStartChallenge() && this.scene.arena.biomeType === Biome.END && (!this.scene.gameMode.isClassic || (this.scene.getEnemyField().filter(p => p.isActive(true)).some(p => !p.scene.gameData.dexData[p.species.speciesId].caughtAttr) && this.scene.gameData.getStarterCount(d => !!d.caughtAttr) < Object.keys(speciesStarters).length - 1))) { - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.showText(i18next.t("battle:noPokeballForce"), null, () => { - this.scene.ui.showText("", 0); - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - }, null, true); - } else if (this.scene.currentBattle.battleType === BattleType.TRAINER) { - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.showText(i18next.t("battle:noPokeballTrainer"), null, () => { - this.scene.ui.showText("", 0); - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - }, null, true); - } else { - const targets = this.scene.getEnemyField().filter(p => p.isActive(true)).map(p => p.getBattlerIndex()); - if (targets.length > 1) { - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.showText(i18next.t("battle:noPokeballMulti"), null, () => { - this.scene.ui.showText("", 0); - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - }, null, true); - } else if (cursor < 5) { - const targetPokemon = this.scene.getEnemyField().find(p => p.isActive(true)); - if (targetPokemon?.isBoss() && targetPokemon?.bossSegmentIndex >= 1 && !targetPokemon?.hasAbility(Abilities.WONDER_GUARD, false, true) && cursor < PokeballType.MASTER_BALL) { - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.showText(i18next.t("battle:noPokeballStrong"), null, () => { - this.scene.ui.showText("", 0); - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - }, null, true); - } else { - this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor }; - this.scene.currentBattle.turnCommands[this.fieldIndex]!.targets = targets; - if (this.fieldIndex) { - this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; - } - success = true; - } - } - } - break; - case Command.POKEMON: - case Command.RUN: - const isSwitch = command === Command.POKEMON; - if (!isSwitch && this.scene.arena.biomeType === Biome.END) { - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.showText(i18next.t("battle:noEscapeForce"), null, () => { - this.scene.ui.showText("", 0); - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - }, null, true); - } else if (!isSwitch && this.scene.currentBattle.battleType === BattleType.TRAINER) { - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.showText(i18next.t("battle:noEscapeTrainer"), null, () => { - this.scene.ui.showText("", 0); - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - }, null, true); - } else { - const trapTag = playerPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag; - const trapped = new Utils.BooleanHolder(false); - const batonPass = isSwitch && args[0] as boolean; - const trappedAbMessages: string[] = []; - if (!batonPass) { - enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped, playerPokemon, true, trappedAbMessages)); - } - if (batonPass || (!trapTag && !trapped.value)) { - this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch - ? { command: Command.POKEMON, cursor: cursor, args: args } - : { command: Command.RUN }; - success = true; - if (!isSwitch && this.fieldIndex) { - this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; - } - } else if (trapTag) { - if (trapTag.sourceMove === Moves.INGRAIN && trapTag.sourceId && this.scene.getPokemonById(trapTag.sourceId)?.isOfType(Type.GHOST)) { - success = true; - this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch - ? { command: Command.POKEMON, cursor: cursor, args: args } - : { command: Command.RUN }; - break; - } - if (!isSwitch) { - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - this.scene.ui.setMode(Mode.MESSAGE); - } - this.scene.ui.showText( - i18next.t("battle:noEscapePokemon", { - pokemonName: trapTag.sourceId && this.scene.getPokemonById(trapTag.sourceId) ? getPokemonNameWithAffix(this.scene.getPokemonById(trapTag.sourceId)!) : "", - moveName: trapTag.getMoveName(), - escapeVerb: isSwitch ? i18next.t("battle:escapeVerbSwitch") : i18next.t("battle:escapeVerbFlee") - }), - null, - () => { - this.scene.ui.showText("", 0); - if (!isSwitch) { - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - } - }, null, true); - } else if (trapped.value && trappedAbMessages.length > 0) { - if (!isSwitch) { - this.scene.ui.setMode(Mode.MESSAGE); - } - this.scene.ui.showText(trappedAbMessages[0], null, () => { - this.scene.ui.showText("", 0); - if (!isSwitch) { - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - } - }, null, true); - } - } - break; - } - - if (success!) { // TODO: is the bang correct? - this.end(); - } - - return success!; // TODO: is the bang correct? - } - - cancel() { - if (this.fieldIndex) { - this.scene.unshiftPhase(new CommandPhase(this.scene, 0)); - this.scene.unshiftPhase(new CommandPhase(this.scene, 1)); - this.end(); - } - } - - checkFightOverride(): boolean { - const pokemon = this.getPokemon(); - - const encoreTag = pokemon.getTag(EncoreTag) as EncoreTag; - - if (!encoreTag) { - return false; - } - - const moveIndex = pokemon.getMoveset().findIndex(m => m?.moveId === encoreTag.moveId); - - if (moveIndex === -1 || !pokemon.getMoveset()[moveIndex]!.isUsable(pokemon)) { // TODO: is this bang correct? - return false; - } - - this.handleCommand(Command.FIGHT, moveIndex, false); - - return true; - } - - getFieldIndex(): integer { - return this.fieldIndex; - } - - getPokemon(): PlayerPokemon { - return this.scene.getPlayerField()[this.fieldIndex]; - } - - end() { - this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); - } -} - -/** - * Phase for determining an enemy AI's action for the next turn. - * During this phase, the enemy decides whether to switch (if it has a trainer) - * or to use a move from its moveset. - * - * For more information on how the Enemy AI works, see docs/enemy-ai.md - * @see {@linkcode Pokemon.getMatchupScore} - * @see {@linkcode EnemyPokemon.getNextMove} - */ -export class EnemyCommandPhase extends FieldPhase { - protected fieldIndex: integer; - - constructor(scene: BattleScene, fieldIndex: integer) { - super(scene); - - this.fieldIndex = fieldIndex; - } - - start() { - super.start(); - - const enemyPokemon = this.scene.getEnemyField()[this.fieldIndex]; - - const battle = this.scene.currentBattle; - - const trainer = battle.trainer; - - /** - * If the enemy has a trainer, decide whether or not the enemy should switch - * to another member in its party. - * - * This block compares the active enemy Pokemon's {@linkcode Pokemon.getMatchupScore | matchup score} - * against the active player Pokemon with the enemy party's other non-fainted Pokemon. If a party - * member's matchup score is 3x the active enemy's score (or 2x for "boss" trainers), - * the enemy will switch to that Pokemon. - */ - if (trainer && !enemyPokemon.getMoveQueue().length) { - const opponents = enemyPokemon.getOpponents(); - - const trapTag = enemyPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag; - const trapped = new Utils.BooleanHolder(false); - opponents.forEach(playerPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, playerPokemon, trapped, enemyPokemon, true, [])); - if (!trapTag && !trapped.value) { - const partyMemberScores = trainer.getPartyMemberMatchupScores(enemyPokemon.trainerSlot, true); - - if (partyMemberScores.length) { - const matchupScores = opponents.map(opp => enemyPokemon.getMatchupScore(opp)); - const matchupScore = matchupScores.reduce((total, score) => total += score, 0) / matchupScores.length; - - const sortedPartyMemberScores = trainer.getSortedPartyMemberMatchupScores(partyMemberScores); - - const switchMultiplier = 1 - (battle.enemySwitchCounter ? Math.pow(0.1, (1 / battle.enemySwitchCounter)) : 0); - - if (sortedPartyMemberScores[0][1] * switchMultiplier >= matchupScore * (trainer.config.isBoss ? 2 : 3)) { - const index = trainer.getNextSummonIndex(enemyPokemon.trainerSlot, partyMemberScores); - - battle.turnCommands[this.fieldIndex + BattlerIndex.ENEMY] = - { command: Command.POKEMON, cursor: index, args: [false] }; - - battle.enemySwitchCounter++; - - return this.end(); - } - } - } - } - - /** Select a move to use (and a target to use it against, if applicable) */ - const nextMove = enemyPokemon.getNextMove(); - - this.scene.currentBattle.turnCommands[this.fieldIndex + BattlerIndex.ENEMY] = - { command: Command.FIGHT, move: nextMove }; - - this.scene.currentBattle.enemySwitchCounter = Math.max(this.scene.currentBattle.enemySwitchCounter - 1, 0); - - this.end(); - } -} - -export class SelectTargetPhase extends PokemonPhase { - constructor(scene: BattleScene, fieldIndex: integer) { - super(scene, fieldIndex); - } - - start() { - super.start(); - - const turnCommand = this.scene.currentBattle.turnCommands[this.fieldIndex]; - const move = turnCommand?.move?.move; - this.scene.ui.setMode(Mode.TARGET_SELECT, this.fieldIndex, move, (targets: BattlerIndex[]) => { - this.scene.ui.setMode(Mode.MESSAGE); - if (targets.length < 1) { - this.scene.currentBattle.turnCommands[this.fieldIndex] = null; - this.scene.unshiftPhase(new CommandPhase(this.scene, this.fieldIndex)); - } else { - turnCommand!.targets = targets; //TODO: is the bang correct here? - } - if (turnCommand?.command === Command.BALL && this.fieldIndex) { - this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; //TODO: is the bang correct here? - } - this.end(); - }); - } -} - -export class TurnStartPhase extends FieldPhase { - constructor(scene: BattleScene) { - super(scene); - } - - start() { - super.start(); - - const field = this.scene.getField(); - const order = this.getOrder(); - - const battlerBypassSpeed = {}; - - this.scene.getField(true).filter(p => p.summonData).map(p => { - const bypassSpeed = new Utils.BooleanHolder(false); - const canCheckHeldItems = new Utils.BooleanHolder(true); - applyAbAttrs(BypassSpeedChanceAbAttr, p, null, bypassSpeed); - applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, bypassSpeed, canCheckHeldItems); - if (canCheckHeldItems.value) { - this.scene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed); - } - battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed; - }); - - const moveOrder = order.slice(0); - - moveOrder.sort((a, b) => { - const aCommand = this.scene.currentBattle.turnCommands[a]; - const bCommand = this.scene.currentBattle.turnCommands[b]; - - if (aCommand?.command !== bCommand?.command) { - if (aCommand?.command === Command.FIGHT) { - return 1; - } else if (bCommand?.command === Command.FIGHT) { - return -1; - } - } else if (aCommand?.command === Command.FIGHT) { - const aMove = allMoves[aCommand.move!.move];//TODO: is the bang correct here? - const bMove = allMoves[bCommand!.move!.move];//TODO: is the bang correct here? - - const aPriority = new Utils.IntegerHolder(aMove.priority); - const bPriority = new Utils.IntegerHolder(bMove.priority); - - applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? - applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? - - applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? - applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? - - if (aPriority.value !== bPriority.value) { - const bracketDifference = Math.ceil(aPriority.value) - Math.ceil(bPriority.value); - const hasSpeedDifference = battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value; - if (bracketDifference === 0 && hasSpeedDifference) { - return battlerBypassSpeed[a].value ? -1 : 1; - } - return aPriority.value < bPriority.value ? 1 : -1; - } - } - - if (battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) { - return battlerBypassSpeed[a].value ? -1 : 1; - } - - const aIndex = order.indexOf(a); - const bIndex = order.indexOf(b); - - return aIndex < bIndex ? -1 : aIndex > bIndex ? 1 : 0; - }); - - let orderIndex = 0; - - for (const o of moveOrder) { - - const pokemon = field[o]; - const turnCommand = this.scene.currentBattle.turnCommands[o]; - - if (turnCommand?.skip) { - continue; - } - - switch (turnCommand?.command) { - case Command.FIGHT: - const queuedMove = turnCommand.move; - pokemon.turnData.order = orderIndex++; - if (!queuedMove) { - continue; - } - const move = pokemon.getMoveset().find(m => m?.moveId === queuedMove.move) || new PokemonMove(queuedMove.move); - if (move.getMove().hasAttr(MoveHeaderAttr)) { - this.scene.unshiftPhase(new MoveHeaderPhase(this.scene, pokemon, move)); - } - if (pokemon.isPlayer()) { - if (turnCommand.cursor === -1) { - this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move));//TODO: is the bang correct here? - } else { - const playerPhase = new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move, false, queuedMove.ignorePP);//TODO: is the bang correct here? - this.scene.pushPhase(playerPhase); - } - } else { - this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move, false, queuedMove.ignorePP));//TODO: is the bang correct here? - } - break; - case Command.BALL: - this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, turnCommand.targets![0] % 2, turnCommand.cursor!));//TODO: is the bang correct here? - break; - case Command.POKEMON: - this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, pokemon.getFieldIndex(), turnCommand.cursor!, true, turnCommand.args![0] as boolean, pokemon.isPlayer()));//TODO: is the bang correct here? - break; - case Command.RUN: - let runningPokemon = pokemon; - if (this.scene.currentBattle.double) { - const playerActivePokemon = field.filter(pokemon => { - if (!!pokemon) { - return pokemon.isPlayer() && pokemon.isActive(); - } else { - return; - } - }); - // if only one pokemon is alive, use that one - if (playerActivePokemon.length > 1) { - // find which active pokemon has faster speed - const fasterPokemon = playerActivePokemon[0].getStat(Stat.SPD) > playerActivePokemon[1].getStat(Stat.SPD) ? playerActivePokemon[0] : playerActivePokemon[1]; - // check if either active pokemon has the ability "Run Away" - const hasRunAway = playerActivePokemon.find(p => p.hasAbility(Abilities.RUN_AWAY)); - runningPokemon = hasRunAway !== undefined ? hasRunAway : fasterPokemon; - } - } - this.scene.unshiftPhase(new AttemptRunPhase(this.scene, runningPokemon.getFieldIndex())); - break; - } - } - - - this.scene.pushPhase(new WeatherEffectPhase(this.scene)); - - for (const o of order) { - if (field[o].status && field[o].status.isPostTurn()) { - this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, o)); - } - } - - this.scene.pushPhase(new BerryPhase(this.scene)); - this.scene.pushPhase(new TurnEndPhase(this.scene)); - - /** - * this.end() will call shiftPhase(), which dumps everything from PrependQueue (aka everything that is unshifted()) to the front - * of the queue and dequeues to start the next phase - * this is important since stuff like SwitchSummon, AttemptRun, AttemptCapture Phases break the "flow" and should take precedence - */ - this.end(); - } -} - -/** The phase after attacks where the pokemon eat berries */ -export class BerryPhase extends FieldPhase { - start() { - super.start(); - - this.executeForAll((pokemon) => { - const hasUsableBerry = !!this.scene.findModifier((m) => { - return m instanceof BerryModifier && m.shouldApply([pokemon]); - }, pokemon.isPlayer()); - - if (hasUsableBerry) { - const cancelled = new Utils.BooleanHolder(false); - pokemon.getOpponents().map((opp) => applyAbAttrs(PreventBerryUseAbAttr, opp, cancelled)); - - if (cancelled.value) { - pokemon.scene.queueMessage(i18next.t("abilityTriggers:preventBerryUse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); - } else { - this.scene.unshiftPhase( - new CommonAnimPhase(this.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM) - ); - - for (const berryModifier of this.scene.applyModifiers(BerryModifier, pokemon.isPlayer(), pokemon) as BerryModifier[]) { - if (berryModifier.consumed) { - if (!--berryModifier.stackCount) { - this.scene.removeModifier(berryModifier); - } else { - berryModifier.consumed = false; - } - } - this.scene.eventTarget.dispatchEvent(new BerryUsedEvent(berryModifier)); // Announce a berry was used - } - - this.scene.updateModifiers(pokemon.isPlayer()); - - applyAbAttrs(HealFromBerryUseAbAttr, pokemon, new Utils.BooleanHolder(false)); - } - } - }); - - this.end(); - } -} - -export class TurnEndPhase extends FieldPhase { - constructor(scene: BattleScene) { - super(scene); - } - - start() { - super.start(); - - this.scene.currentBattle.incrementTurn(this.scene); - this.scene.eventTarget.dispatchEvent(new TurnEndEvent(this.scene.currentBattle.turn)); - - const handlePokemon = (pokemon: Pokemon) => { - pokemon.lapseTags(BattlerTagLapseType.TURN_END); - - if (pokemon.summonData.disabledMove && !--pokemon.summonData.disabledTurns) { - this.scene.pushPhase(new MessagePhase(this.scene, i18next.t("battle:notDisabled", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: allMoves[pokemon.summonData.disabledMove].name }))); - pokemon.summonData.disabledMove = Moves.NONE; - } - - this.scene.applyModifiers(TurnHealModifier, pokemon.isPlayer(), pokemon); - - if (this.scene.arena.terrain?.terrainType === TerrainType.GRASSY && pokemon.isGrounded()) { - this.scene.unshiftPhase(new PokemonHealPhase(this.scene, pokemon.getBattlerIndex(), - Math.max(pokemon.getMaxHp() >> 4, 1), i18next.t("battle:turnEndHpRestore", { pokemonName: getPokemonNameWithAffix(pokemon) }), true)); - } - - if (!pokemon.isPlayer()) { - this.scene.applyModifiers(EnemyTurnHealModifier, false, pokemon); - this.scene.applyModifier(EnemyStatusEffectHealChanceModifier, false, pokemon); - } - - applyPostTurnAbAttrs(PostTurnAbAttr, pokemon); - - this.scene.applyModifiers(TurnStatusEffectModifier, pokemon.isPlayer(), pokemon); - - this.scene.applyModifiers(TurnHeldItemTransferModifier, pokemon.isPlayer(), pokemon); - - pokemon.battleSummonData.turnCount++; - }; - - this.executeForAll(handlePokemon); - - this.scene.arena.lapseTags(); - - if (this.scene.arena.weather && !this.scene.arena.weather.lapse()) { - this.scene.arena.trySetWeather(WeatherType.NONE, false); - } - - if (this.scene.arena.terrain && !this.scene.arena.terrain.lapse()) { - this.scene.arena.trySetTerrain(TerrainType.NONE, false); - } - - this.end(); - } -} - -export class BattleEndPhase extends BattlePhase { - start() { - super.start(); - - this.scene.currentBattle.addBattleScore(this.scene); - - this.scene.gameData.gameStats.battles++; - if (this.scene.currentBattle.trainer) { - this.scene.gameData.gameStats.trainersDefeated++; - } - if (this.scene.gameMode.isEndless && this.scene.currentBattle.waveIndex + 1 > this.scene.gameData.gameStats.highestEndlessWave) { - this.scene.gameData.gameStats.highestEndlessWave = this.scene.currentBattle.waveIndex + 1; - } - - // Endless graceful end - if (this.scene.gameMode.isEndless && this.scene.currentBattle.waveIndex >= 5850) { - this.scene.clearPhaseQueue(); - this.scene.unshiftPhase(new GameOverPhase(this.scene, true)); - } - - for (const pokemon of this.scene.getField()) { - if (pokemon) { - pokemon.resetBattleSummonData(); - } - } - - for (const pokemon of this.scene.getParty().filter(p => p.isAllowedInBattle())) { - applyPostBattleAbAttrs(PostBattleAbAttr, pokemon); - } - - if (this.scene.currentBattle.moneyScattered) { - this.scene.currentBattle.pickUpScatteredMoney(this.scene); - } - - this.scene.clearEnemyHeldItemModifiers(); - - const lapsingModifiers = this.scene.findModifiers(m => m instanceof LapsingPersistentModifier || m instanceof LapsingPokemonHeldItemModifier) as (LapsingPersistentModifier | LapsingPokemonHeldItemModifier)[]; - for (const m of lapsingModifiers) { - const args: any[] = []; - if (m instanceof LapsingPokemonHeldItemModifier) { - args.push(this.scene.getPokemonById(m.pokemonId)); - } - if (!m.lapse(args)) { - this.scene.removeModifier(m); - } - } - - this.scene.updateModifiers().then(() => this.end()); - } -} - -export class NewBattlePhase extends BattlePhase { - start() { - super.start(); - - this.scene.newBattle(); - - this.end(); - } -} - -export class CommonAnimPhase extends PokemonPhase { - private anim: CommonAnim | null; - private targetIndex: integer | undefined; - - constructor(scene: BattleScene, battlerIndex?: BattlerIndex, targetIndex?: BattlerIndex | undefined, anim?: CommonAnim) { - super(scene, battlerIndex); - - this.anim = anim!; // TODO: is this bang correct? - this.targetIndex = targetIndex; - } - - setAnimation(anim: CommonAnim) { - this.anim = anim; - } - - start() { - new CommonBattleAnim(this.anim, this.getPokemon(), this.targetIndex !== undefined ? (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField())[this.targetIndex] : this.getPokemon()).play(this.scene, () => { - this.end(); - }); - } -} - -export class MoveHeaderPhase extends BattlePhase { - public pokemon: Pokemon; - public move: PokemonMove; - - constructor(scene: BattleScene, pokemon: Pokemon, move: PokemonMove) { - super(scene); - - this.pokemon = pokemon; - this.move = move; - } - - canMove(): boolean { - return this.pokemon.isActive(true) && this.move.isUsable(this.pokemon); - } - - start() { - super.start(); - - if (this.canMove()) { - applyMoveAttrs(MoveHeaderAttr, this.pokemon, null, this.move.getMove()).then(() => this.end()); - } else { - this.end(); - } - } -} - -export class MovePhase extends BattlePhase { - public pokemon: Pokemon; - public move: PokemonMove; - public targets: BattlerIndex[]; - protected followUp: boolean; - protected ignorePp: boolean; - protected failed: boolean; - protected cancelled: boolean; - - constructor(scene: BattleScene, pokemon: Pokemon, targets: BattlerIndex[], move: PokemonMove, followUp?: boolean, ignorePp?: boolean) { - super(scene); - - this.pokemon = pokemon; - this.targets = targets; - this.move = move; - this.followUp = !!followUp; - this.ignorePp = !!ignorePp; - this.failed = false; - this.cancelled = false; - } - - canMove(): boolean { - return this.pokemon.isActive(true) && this.move.isUsable(this.pokemon, this.ignorePp) && !!this.targets.length; - } - - /**Signifies the current move should fail but still use PP */ - fail(): void { - this.failed = true; - } - - /**Signifies the current move should cancel and retain PP */ - cancel(): void { - this.cancelled = true; - } - - start() { - super.start(); - - console.log(Moves[this.move.moveId]); - - if (!this.canMove()) { - if (this.move.moveId && this.pokemon.summonData?.disabledMove === this.move.moveId) { - this.scene.queueMessage(`${this.move.getName()} is disabled!`); - } - if (this.pokemon.isActive(true) && this.move.ppUsed >= this.move.getMovePp()) { // if the move PP was reduced from Spite or otherwise, the move fails - this.fail(); - this.showMoveText(); - this.showFailedText(); - } - return this.end(); - } - - if (!this.followUp) { - if (this.move.getMove().checkFlag(MoveFlags.IGNORE_ABILITIES, this.pokemon, null)) { - this.scene.arena.setIgnoreAbilities(); - } - } else { - this.pokemon.turnData.hitsLeft = 0; // TODO: is `0` correct? - this.pokemon.turnData.hitCount = 0; // TODO: is `0` correct? - } - - // Move redirection abilities (ie. Storm Drain) only support single target moves - const moveTarget = this.targets.length === 1 - ? new Utils.IntegerHolder(this.targets[0]) - : null; - if (moveTarget) { - const oldTarget = moveTarget.value; - this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, this.move.moveId, moveTarget)); - this.pokemon.getOpponents().forEach(p => { - const redirectTag = p.getTag(CenterOfAttentionTag) as CenterOfAttentionTag; - if (redirectTag && (!redirectTag.powder || (!this.pokemon.isOfType(Type.GRASS) && !this.pokemon.hasAbility(Abilities.OVERCOAT)))) { - moveTarget.value = p.getBattlerIndex(); - } - }); - //Check if this move is immune to being redirected, and restore its target to the intended target if it is. - if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) || this.move.getMove().hasAttr(BypassRedirectAttr))) { - //If an ability prevented this move from being redirected, display its ability pop up. - if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) && !this.move.getMove().hasAttr(BypassRedirectAttr)) && oldTarget !== moveTarget.value) { - this.scene.unshiftPhase(new ShowAbilityPhase(this.scene, this.pokemon.getBattlerIndex(), this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr))); - } - moveTarget.value = oldTarget; - } - this.targets[0] = moveTarget.value; - } - - // Check for counterattack moves to switch target - if (this.targets.length === 1 && this.targets[0] === BattlerIndex.ATTACKER) { - if (this.pokemon.turnData.attacksReceived.length) { - const attack = this.pokemon.turnData.attacksReceived[0]; - this.targets[0] = attack.sourceBattlerIndex; - - // account for metal burst and comeuppance hitting remaining targets in double battles - // counterattack will redirect to remaining ally if original attacker faints - if (this.scene.currentBattle.double && this.move.getMove().hasFlag(MoveFlags.REDIRECT_COUNTER)) { - if (this.scene.getField()[this.targets[0]].hp === 0) { - const opposingField = this.pokemon.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField(); - //@ts-ignore - this.targets[0] = opposingField.find(p => p.hp > 0)?.getBattlerIndex(); //TODO: fix ts-ignore - } - } - } - if (this.targets[0] === BattlerIndex.ATTACKER) { - this.fail(); // Marks the move as failed for later in doMove - this.showMoveText(); - this.showFailedText(); - } - } - - const targets = this.scene.getField(true).filter(p => { - if (this.targets.indexOf(p.getBattlerIndex()) > -1) { - return true; - } - return false; - }); - - const doMove = () => { - this.pokemon.turnData.acted = true; // Record that the move was attempted, even if it fails - - this.pokemon.lapseTags(BattlerTagLapseType.PRE_MOVE); - - let ppUsed = 1; - // Filter all opponents to include only those this move is targeting - const targetedOpponents = this.pokemon.getOpponents().filter(o => this.targets.includes(o.getBattlerIndex())); - for (const opponent of targetedOpponents) { - if (this.move.ppUsed + ppUsed >= this.move.getMovePp()) { // If we're already at max PP usage, stop checking - break; - } - if (opponent.hasAbilityWithAttr(IncreasePpAbAttr)) { // Accounting for abilities like Pressure - ppUsed++; - } - } - - if (!this.followUp && this.canMove() && !this.cancelled) { - this.pokemon.lapseTags(BattlerTagLapseType.MOVE); - } - - const moveQueue = this.pokemon.getMoveQueue(); - if (this.cancelled || this.failed) { - if (this.failed) { - this.move.usePp(ppUsed); // Only use PP if the move failed - this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed)); - } - - // Record a failed move so Abilities like Truant don't trigger next turn and soft-lock - this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL }); - - this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); // Remove any tags from moves like Fly/Dive/etc. - moveQueue.shift(); // Remove the second turn of charge moves - return this.end(); - } - - this.scene.triggerPokemonFormChange(this.pokemon, SpeciesFormChangePreMoveTrigger); - - if (this.move.moveId) { - this.showMoveText(); - } - - // This should only happen when there are no valid targets left on the field - if ((moveQueue.length && moveQueue[0].move === Moves.NONE) || !targets.length) { - this.showFailedText(); - this.cancel(); - - // Record a failed move so Abilities like Truant don't trigger next turn and soft-lock - this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL }); - - this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); // Remove any tags from moves like Fly/Dive/etc. - - moveQueue.shift(); - return this.end(); - } - - if (!moveQueue.length || !moveQueue.shift()?.ignorePP) { // using .shift here clears out two turn moves once they've been used - this.move.usePp(ppUsed); - this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed)); - } - - if (!allMoves[this.move.moveId].hasAttr(CopyMoveAttr)) { - this.scene.currentBattle.lastMove = this.move.moveId; - } - - // Assume conditions affecting targets only apply to moves with a single target - let success = this.move.getMove().applyConditions(this.pokemon, targets[0], this.move.getMove()); - const cancelled = new Utils.BooleanHolder(false); - let failedText = this.move.getMove().getFailedText(this.pokemon, targets[0], this.move.getMove(), cancelled); - if (success && this.scene.arena.isMoveWeatherCancelled(this.move.getMove())) { - success = false; - } else if (success && this.scene.arena.isMoveTerrainCancelled(this.pokemon, this.targets, this.move.getMove())) { - success = false; - if (failedText === null) { - failedText = getTerrainBlockMessage(targets[0], this.scene.arena.terrain?.terrainType!); // TODO: is this bang correct? - } - } - - /** - * Trigger pokemon type change before playing the move animation - * Will still change the user's type when using Roar, Whirlwind, Trick-or-Treat, and Forest's Curse, - * regardless of whether the move successfully executes or not. - */ - if (success || [Moves.ROAR, Moves.WHIRLWIND, Moves.TRICK_OR_TREAT, Moves.FORESTS_CURSE].includes(this.move.moveId)) { - applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, this.move.getMove()); - } - - if (success) { - this.scene.unshiftPhase(this.getEffectPhase()); - } else { - this.pokemon.pushMoveHistory({ move: this.move.moveId, targets: this.targets, result: MoveResult.FAIL, virtual: this.move.virtual }); - if (!cancelled.value) { - this.showFailedText(failedText); - } - } - // Checks if Dancer ability is triggered - if (this.move.getMove().hasFlag(MoveFlags.DANCE_MOVE) && !this.followUp) { - // Pokemon with Dancer can be on either side of the battle so we check in both cases - this.scene.getPlayerField().forEach(pokemon => { - applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets); - }); - this.scene.getEnemyField().forEach(pokemon => { - applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets); - }); - } - this.end(); - }; - - if (!this.followUp && this.pokemon.status && !this.pokemon.status.isPostTurn()) { - this.pokemon.status.incrementTurn(); - let activated = false; - let healed = false; - - switch (this.pokemon.status.effect) { - case StatusEffect.PARALYSIS: - if (!this.pokemon.randSeedInt(4)) { - activated = true; - this.cancelled = true; - } - break; - case StatusEffect.SLEEP: - applyMoveAttrs(BypassSleepAttr, this.pokemon, null, this.move.getMove()); - healed = this.pokemon.status.turnCount === this.pokemon.status.cureTurn; - activated = !healed && !this.pokemon.getTag(BattlerTagType.BYPASS_SLEEP); - this.cancelled = activated; - break; - case StatusEffect.FREEZE: - healed = !!this.move.getMove().findAttr(attr => attr instanceof HealStatusEffectAttr && attr.selfTarget && attr.isOfEffect(StatusEffect.FREEZE)) || !this.pokemon.randSeedInt(5); - activated = !healed; - this.cancelled = activated; - break; - } - - if (activated) { - this.scene.queueMessage(getStatusEffectActivationText(this.pokemon.status.effect, getPokemonNameWithAffix(this.pokemon))); - this.scene.unshiftPhase(new CommonAnimPhase(this.scene, this.pokemon.getBattlerIndex(), undefined, CommonAnim.POISON + (this.pokemon.status.effect - 1))); - doMove(); - } else { - if (healed) { - this.scene.queueMessage(getStatusEffectHealText(this.pokemon.status.effect, getPokemonNameWithAffix(this.pokemon))); - this.pokemon.resetStatus(); - this.pokemon.updateInfo(); - } - doMove(); - } - } else { - doMove(); - } - } - - getEffectPhase(): MoveEffectPhase { - return new MoveEffectPhase(this.scene, this.pokemon.getBattlerIndex(), this.targets, this.move); - } - - showMoveText(): void { - if (this.move.getMove().hasAttr(ChargeAttr)) { - const lastMove = this.pokemon.getLastXMoves() as TurnMove[]; - if (!lastMove.length || lastMove[0].move !== this.move.getMove().id || lastMove[0].result !== MoveResult.OTHER) { - this.scene.queueMessage(i18next.t("battle:useMove", { - pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon), - moveName: this.move.getName() - }), 500); - return; - } - } - - if (this.pokemon.getTag(BattlerTagType.RECHARGING || BattlerTagType.INTERRUPTED)) { - return; - } - - this.scene.queueMessage(i18next.t("battle:useMove", { - pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon), - moveName: this.move.getName() - }), 500); - applyMoveAttrs(PreMoveMessageAttr, this.pokemon, this.pokemon.getOpponents().find(() => true)!, this.move.getMove()); //TODO: is the bang correct here? - } - - showFailedText(failedText: string | null = null): void { - this.scene.queueMessage(failedText || i18next.t("battle:attackFailed")); - } - - end() { - if (!this.followUp && this.canMove()) { - this.scene.unshiftPhase(new MoveEndPhase(this.scene, this.pokemon.getBattlerIndex())); - } - - super.end(); - } -} - -export class MoveEffectPhase extends PokemonPhase { - public move: PokemonMove; - protected targets: BattlerIndex[]; - - constructor(scene: BattleScene, battlerIndex: BattlerIndex, targets: BattlerIndex[], move: PokemonMove) { - super(scene, battlerIndex); - this.move = move; - /** - * In double battles, if the right Pokemon selects a spread move and the left Pokemon dies - * with no party members available to switch in, then the right Pokemon takes the index - * of the left Pokemon and gets hit unless this is checked. - */ - if (targets.includes(battlerIndex) && this.move.getMove().moveTarget === MoveTarget.ALL_NEAR_OTHERS) { - const i = targets.indexOf(battlerIndex); - targets.splice(i, i + 1); - } - this.targets = targets; - } - - start() { - super.start(); - - /** The Pokemon using this phase's invoked move */ - const user = this.getUserPokemon(); - /** All Pokemon targeted by this phase's invoked move */ - const targets = this.getTargets(); - - /** If the user was somehow removed from the field, end this phase */ - if (!user?.isOnField()) { - return super.end(); - } - - /** - * Does an effect from this move override other effects on this turn? - * e.g. Charging moves (Fly, etc.) on their first turn of use. - */ - const overridden = new Utils.BooleanHolder(false); - /** The {@linkcode Move} object from {@linkcode allMoves} invoked by this phase */ - const move = this.move.getMove(); - - // Assume single target for override - applyMoveAttrs(OverrideMoveEffectAttr, user, this.getTarget() ?? null, move, overridden, this.move.virtual).then(() => { - // If other effects were overriden, stop this phase before they can be applied - if (overridden.value) { - return this.end(); - } - - user.lapseTags(BattlerTagLapseType.MOVE_EFFECT); - - /** - * If this phase is for the first hit of the invoked move, - * resolve the move's total hit count. This block combines the - * effects of the move itself, Parental Bond, and Multi-Lens to do so. - */ - if (user.turnData.hitsLeft === undefined) { - const hitCount = new Utils.IntegerHolder(1); - // Assume single target for multi hit - applyMoveAttrs(MultiHitAttr, user, this.getTarget() ?? null, move, hitCount); - // If Parental Bond is applicable, double the hit count - applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, targets.length, hitCount, new Utils.IntegerHolder(0)); - // If Multi-Lens is applicable, multiply the hit count by 1 + the number of Multi-Lenses held by the user - if (move instanceof AttackMove && !move.hasAttr(FixedDamageAttr)) { - this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0)); - } - // Set the user's relevant turnData fields to reflect the final hit count - user.turnData.hitCount = hitCount.value; - user.turnData.hitsLeft = hitCount.value; - } - - /** - * Log to be entered into the user's move history once the move result is resolved. - * Note that `result` (a {@linkcode MoveResult}) logs whether the move was successfully - * used in the sense of "Does it have an effect on the user?". - */ - const moveHistoryEntry = { move: this.move.moveId, targets: this.targets, result: MoveResult.PENDING, virtual: this.move.virtual }; - - /** - * Stores results of hit checks of the invoked move against all targets, organized by battler index. - * @see {@linkcode hitCheck} - */ - const targetHitChecks = Object.fromEntries(targets.map(p => [p.getBattlerIndex(), this.hitCheck(p)])); - const hasActiveTargets = targets.some(t => t.isActive(true)); - /** - * If no targets are left for the move to hit (FAIL), or the invoked move is single-target - * (and not random target) and failed the hit check against its target (MISS), log the move - * as FAILed or MISSed (depending on the conditions above) and end this phase. - */ - if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) { - this.stopMultiHit(); - if (hasActiveTargets) { - this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: this.getTarget()? getPokemonNameWithAffix(this.getTarget()!) : "" })); - moveHistoryEntry.result = MoveResult.MISS; - applyMoveAttrs(MissEffectAttr, user, null, move); - } else { - this.scene.queueMessage(i18next.t("battle:attackFailed")); - moveHistoryEntry.result = MoveResult.FAIL; - } - user.pushMoveHistory(moveHistoryEntry); - return this.end(); - } - - /** All move effect attributes are chained together in this array to be applied asynchronously. */ - const applyAttrs: Promise[] = []; - - // Move animation only needs one target - new MoveAnim(move.id as Moves, user, this.getTarget()?.getBattlerIndex()!).play(this.scene, () => { // TODO: is the bang correct here? - /** Has the move successfully hit a target (for damage) yet? */ - let hasHit: boolean = false; - for (const target of targets) { - /** - * If the move missed a target, stop all future hits against that target - * and move on to the next target (if there is one). - */ - if (!targetHitChecks[target.getBattlerIndex()]) { - this.stopMultiHit(target); - this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); - if (moveHistoryEntry.result === MoveResult.PENDING) { - moveHistoryEntry.result = MoveResult.MISS; - } - user.pushMoveHistory(moveHistoryEntry); - applyMoveAttrs(MissEffectAttr, user, null, move); - continue; - } - - /** The {@linkcode ArenaTagSide} to which the target belongs */ - const targetSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; - /** Has the invoked move been cancelled by conditional protection (e.g Quick Guard)? */ - const hasConditionalProtectApplied = new Utils.BooleanHolder(false); - /** Does the applied conditional protection bypass Protect-ignoring effects? */ - const bypassIgnoreProtect = new Utils.BooleanHolder(false); - // If the move is not targeting a Pokemon on the user's side, try to apply conditional protection effects - if (!this.move.getMove().isAllyTarget()) { - this.scene.arena.applyTagsForSide(ConditionalProtectTag, targetSide, hasConditionalProtectApplied, user, target, move.id, bypassIgnoreProtect); - } - - /** Is the target protected by Protect, etc. or a relevant conditional protection effect? */ - const isProtected = (bypassIgnoreProtect.value || !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target)) - && (hasConditionalProtectApplied.value || target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))); - - /** Does this phase represent the invoked move's first strike? */ - const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount); - - // Only log the move's result on the first strike - if (firstHit) { - user.pushMoveHistory(moveHistoryEntry); - } - - /** - * Since all fail/miss checks have applied, the move is considered successfully applied. - * It's worth noting that if the move has no effect or is protected against, this assignment - * is overwritten and the move is logged as a FAIL. - */ - moveHistoryEntry.result = MoveResult.SUCCESS; - - /** - * Stores the result of applying the invoked move to the target. - * If the target is protected, the result is always `NO_EFFECT`. - * Otherwise, the hit result is based on type effectiveness, immunities, - * and other factors that may negate the attack or status application. - * - * Internally, the call to {@linkcode Pokemon.apply} is where damage is calculated - * (for attack moves) and the target's HP is updated. However, this isn't - * made visible to the user until the resulting {@linkcode DamagePhase} - * is invoked. - */ - const hitResult = !isProtected ? target.apply(user, move) : HitResult.NO_EFFECT; - - /** Does {@linkcode hitResult} indicate that damage was dealt to the target? */ - const dealsDamage = [ - HitResult.EFFECTIVE, - HitResult.SUPER_EFFECTIVE, - HitResult.NOT_VERY_EFFECTIVE, - HitResult.ONE_HIT_KO - ].includes(hitResult); - - /** Is this target the first one hit by the move on its current strike? */ - const firstTarget = dealsDamage && !hasHit; - if (firstTarget) { - hasHit = true; - } - - /** - * If the move has no effect on the target (i.e. the target is protected or immune), - * change the logged move result to FAIL. - */ - if (hitResult === HitResult.NO_EFFECT) { - moveHistoryEntry.result = MoveResult.FAIL; - } - - /** Does this phase represent the invoked move's last strike? */ - const lastHit = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive()); - - /** - * If the user can change forms by using the invoked move, - * it only changes forms after the move's last hit - * (see Relic Song's interaction with Parental Bond when used by Meloetta). - */ - if (lastHit) { - this.scene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger); - } - - /** - * Create a Promise that applys *all* effects from the invoked move's MoveEffectAttrs. - * These are ordered by trigger type (see {@linkcode MoveEffectTrigger}), and each trigger - * type requires different conditions to be met with respect to the move's hit result. - */ - applyAttrs.push(new Promise(resolve => { - // Apply all effects with PRE_MOVE triggers (if the target isn't immune to the move) - applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && hitResult !== HitResult.NO_EFFECT, - user, target, move).then(() => { - // All other effects require the move to not have failed or have been cancelled to trigger - if (hitResult !== HitResult.FAIL) { - /** Are the move's effects tied to the first turn of a charge move? */ - const chargeEffect = !!move.getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget() ?? null, move)); - /** - * If the invoked move's effects are meant to trigger during the move's "charge turn," - * ignore all effects after this point. - * Otherwise, apply all self-targeted POST_APPLY effects. - */ - Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY - && attr.selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, move)).then(() => { - // All effects past this point require the move to have hit the target - if (hitResult !== HitResult.NO_EFFECT) { - // Apply all non-self-targeted POST_APPLY effects - applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY - && !(attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, this.move.getMove()).then(() => { - /** - * If the move hit, and the target doesn't have Shield Dust, - * apply the chance to flinch the target gained from King's Rock - */ - if (dealsDamage && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr)) { - const flinched = new Utils.BooleanHolder(false); - user.scene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched); - if (flinched.value) { - target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id); - } - } - // If the move was not protected against, apply all HIT effects - Utils.executeIf(!isProtected && !chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT - && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && (!attr.firstTargetOnly || firstTarget), user, target, this.move.getMove()).then(() => { - // Apply the target's post-defend ability effects (as long as the target is active or can otherwise apply them) - return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move.getMove(), hitResult).then(() => { - // If the invoked move is an enemy attack, apply the enemy's status effect-inflicting tags and tokens - target.lapseTag(BattlerTagType.BEAK_BLAST_CHARGING); - if (move.category === MoveCategory.PHYSICAL && user.isPlayer() !== target.isPlayer()) { - target.lapseTag(BattlerTagType.SHELL_TRAP); - } - if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) { - user.scene.applyShuffledModifiers(this.scene, EnemyAttackStatusEffectChanceModifier, false, target); - } - })).then(() => { - // Apply the user's post-attack ability effects - applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move.getMove(), hitResult).then(() => { - /** - * If the invoked move is an attack, apply the user's chance to - * steal an item from the target granted by Grip Claw - */ - if (this.move.getMove() instanceof AttackMove) { - this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target); - } - resolve(); - }); - }); - }) - ).then(() => resolve()); - }); - } else { - applyMoveAttrs(NoEffectAttr, user, null, move).then(() => resolve()); - } - }); - } else { - resolve(); - } - }); - })); - } - // Apply the move's POST_TARGET effects on the move's last hit, after all targeted effects have resolved - const postTarget = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive()) ? - applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET, user, null, move) : - null; - - if (!!postTarget) { - if (applyAttrs.length) { // If there is a pending asynchronous move effect, do this after - applyAttrs[applyAttrs.length - 1]?.then(() => postTarget); - } else { // Otherwise, push a new asynchronous move effect - applyAttrs.push(postTarget); - } - } - - // Wait for all move effects to finish applying, then end this phase - Promise.allSettled(applyAttrs).then(() => this.end()); - }); - }); - } - - end() { - const move = this.move.getMove(); - move.type = move.defaultType; - const user = this.getUserPokemon(); - /** - * If this phase isn't for the invoked move's last strike, - * unshift another MoveEffectPhase for the next strike. - * Otherwise, queue a message indicating the number of times the move has struck - * (if the move has struck more than once), then apply the heal from Shell Bell - * to the user. - */ - if (user) { - if (user.turnData.hitsLeft && --user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) { - this.scene.unshiftPhase(this.getNewHitPhase()); - } else { - // Queue message for number of hits made by multi-move - // If multi-hit attack only hits once, still want to render a message - const hitsTotal = user.turnData.hitCount! - Math.max(user.turnData.hitsLeft!, 0); // TODO: are those bangs correct? - if (hitsTotal > 1 || (user.turnData.hitsLeft && user.turnData.hitsLeft > 0)) { - // If there are multiple hits, or if there are hits of the multi-hit move left - this.scene.queueMessage(i18next.t("battle:attackHitsCount", { count: hitsTotal })); - } - this.scene.applyModifiers(HitHealModifier, this.player, user); - } - } - - super.end(); - } - - /** - * Resolves whether this phase's invoked move hits or misses the given target - * @param target {@linkcode Pokemon} the Pokemon targeted by the invoked move - * @returns `true` if the move does not miss the target; `false` otherwise - */ - hitCheck(target: Pokemon): boolean { - // Moves targeting the user and entry hazards can't miss - if ([MoveTarget.USER, MoveTarget.ENEMY_SIDE].includes(this.move.getMove().moveTarget)) { - return true; - } - - const user = this.getUserPokemon()!; // TODO: is this bang correct? - - // Hit check only calculated on first hit for multi-hit moves unless flag is set to check all hits. - // However, if an ability with the MaxMultiHitAbAttr, namely Skill Link, is present, act as a normal - // multi-hit move and proceed with all hits - if (user.turnData.hitsLeft < user.turnData.hitCount) { - if (!this.move.getMove().hasFlag(MoveFlags.CHECK_ALL_HITS) || user.hasAbilityWithAttr(MaxMultiHitAbAttr)) { - return true; - } - } - - if (user.hasAbilityWithAttr(AlwaysHitAbAttr) || target.hasAbilityWithAttr(AlwaysHitAbAttr)) { - return true; - } - - // If the user should ignore accuracy on a target, check who the user targeted last turn and see if they match - if (user.getTag(BattlerTagType.IGNORE_ACCURACY) && (user.getLastXMoves().find(() => true)?.targets || []).indexOf(target.getBattlerIndex()) !== -1) { - return true; - } - - if (target.getTag(BattlerTagType.ALWAYS_GET_HIT)) { - return true; - } - - const semiInvulnerableTag = target.getTag(SemiInvulnerableTag); - if (semiInvulnerableTag && !this.move.getMove().getAttrs(HitsTagAttr).some(hta => hta.tagType === semiInvulnerableTag.tagType)) { - return false; - } - - const moveAccuracy = this.move.getMove().calculateBattleAccuracy(user!, target); // TODO: is the bang correct here? - - if (moveAccuracy === -1) { - return true; - } - - const accuracyMultiplier = user.getAccuracyMultiplier(target, this.move.getMove()); - const rand = user.randSeedInt(100, 1); - - return rand <= moveAccuracy * (accuracyMultiplier!); // TODO: is this bang correct? - } - - /** Returns the {@linkcode Pokemon} using this phase's invoked move */ - getUserPokemon(): Pokemon | undefined { - if (this.battlerIndex > BattlerIndex.ENEMY_2) { - return this.scene.getPokemonById(this.battlerIndex) ?? undefined; - } - return (this.player ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex]; - } - - /** Returns an array of all {@linkcode Pokemon} targeted by this phase's invoked move */ - getTargets(): Pokemon[] { - return this.scene.getField(true).filter(p => this.targets.indexOf(p.getBattlerIndex()) > -1); - } - - /** Returns the first target of this phase's invoked move */ - getTarget(): Pokemon | undefined { - return this.getTargets()[0]; - } - - /** - * Removes the given {@linkcode Pokemon} from this phase's target list - * @param target {@linkcode Pokemon} the Pokemon to be removed - */ - removeTarget(target: Pokemon): void { - const targetIndex = this.targets.findIndex(ind => ind === target.getBattlerIndex()); - if (targetIndex !== -1) { - this.targets.splice(this.targets.findIndex(ind => ind === target.getBattlerIndex()), 1); - } - } - - /** - * Prevents subsequent strikes of this phase's invoked move from occurring - * @param target {@linkcode Pokemon} if defined, only stop subsequent - * strikes against this Pokemon - */ - stopMultiHit(target?: Pokemon): void { - /** If given a specific target, remove the target from subsequent strikes */ - if (target) { - this.removeTarget(target); - } - /** - * If no target specified, or the specified target was the last of this move's - * targets, completely cancel all subsequent strikes. - */ - if (!target || this.targets.length === 0 ) { - this.getUserPokemon()!.turnData.hitCount = 1; // TODO: is the bang correct here? - this.getUserPokemon()!.turnData.hitsLeft = 1; // TODO: is the bang correct here? - } - } - - /** Returns a new MoveEffectPhase with the same properties as this phase */ - getNewHitPhase() { - return new MoveEffectPhase(this.scene, this.battlerIndex, this.targets, this.move); - } -} - -export class MoveEndPhase extends PokemonPhase { - constructor(scene: BattleScene, battlerIndex: BattlerIndex) { - super(scene, battlerIndex); - } - - start() { - super.start(); - - const pokemon = this.getPokemon(); - if (pokemon.isActive(true)) { - pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE); - } - - this.scene.arena.setIgnoreAbilities(false); - - this.end(); - } -} - -export class MoveAnimTestPhase extends BattlePhase { - private moveQueue: Moves[]; - - constructor(scene: BattleScene, moveQueue?: Moves[]) { - super(scene); - - this.moveQueue = moveQueue || Utils.getEnumValues(Moves).slice(1); - } - - start() { - const moveQueue = this.moveQueue.slice(0); - this.playMoveAnim(moveQueue, true); - } - - playMoveAnim(moveQueue: Moves[], player: boolean) { - const moveId = player ? moveQueue[0] : moveQueue.shift(); - if (moveId === undefined) { - this.playMoveAnim(this.moveQueue.slice(0), true); - return; - } else if (player) { - console.log(Moves[moveId]); - } - - initMoveAnim(this.scene, moveId).then(() => { - loadMoveAnimAssets(this.scene, [moveId], true) - .then(() => { - new MoveAnim(moveId, player ? this.scene.getPlayerPokemon()! : this.scene.getEnemyPokemon()!, (player !== (allMoves[moveId] instanceof SelfStatusMove) ? this.scene.getEnemyPokemon()! : this.scene.getPlayerPokemon()!).getBattlerIndex()).play(this.scene, () => { // TODO: are the bangs correct here? - if (player) { - this.playMoveAnim(moveQueue, false); - } else { - this.playMoveAnim(moveQueue, true); - } - }); - }); - }); - } -} - -export class ShowAbilityPhase extends PokemonPhase { - private passive: boolean; - - constructor(scene: BattleScene, battlerIndex: BattlerIndex, passive: boolean = false) { - super(scene, battlerIndex); - - this.passive = passive; - } - - start() { - super.start(); - - const pokemon = this.getPokemon(); - - if (pokemon) { - this.scene.abilityBar.showAbility(pokemon, this.passive); - - if (pokemon?.battleData) { - pokemon.battleData.abilityRevealed = true; - } - } - - this.end(); - } -} - -export type StatChangeCallback = (target: Pokemon | null, changed: BattleStat[], relativeChanges: number[]) => void; - -export class StatChangePhase extends PokemonPhase { - private stats: BattleStat[]; - private selfTarget: boolean; - private levels: integer; - private showMessage: boolean; - private ignoreAbilities: boolean; - private canBeCopied: boolean; - private onChange: StatChangeCallback | null; - - - constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], levels: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true, onChange: StatChangeCallback | null = null) { - super(scene, battlerIndex); - - this.selfTarget = selfTarget; - this.stats = stats; - this.levels = levels; - this.showMessage = showMessage; - this.ignoreAbilities = ignoreAbilities; - this.canBeCopied = canBeCopied; - this.onChange = onChange; - } - - start() { - const pokemon = this.getPokemon(); - - let random = false; - - if (this.stats.length === 1 && this.stats[0] === BattleStat.RAND) { - this.stats[0] = this.getRandomStat(); - random = true; - } - - this.aggregateStatChanges(random); - - if (!pokemon.isActive(true)) { - return this.end(); - } - - const filteredStats = this.stats.map(s => s !== BattleStat.RAND ? s : this.getRandomStat()).filter(stat => { - const cancelled = new Utils.BooleanHolder(false); - - if (!this.selfTarget && this.levels < 0) { - this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, cancelled); - } - - if (!cancelled.value && !this.selfTarget && this.levels < 0) { - applyPreStatChangeAbAttrs(ProtectStatAbAttr, this.getPokemon(), stat, cancelled); - } - - return !cancelled.value; - }); - - const levels = new Utils.IntegerHolder(this.levels); - - if (!this.ignoreAbilities) { - applyAbAttrs(StatChangeMultiplierAbAttr, pokemon, null, levels); - } - - const battleStats = this.getPokemon().summonData.battleStats; - const relLevels = filteredStats.map(stat => (levels.value >= 1 ? Math.min(battleStats![stat] + levels.value, 6) : Math.max(battleStats![stat] + levels.value, -6)) - battleStats![stat]); - - this.onChange && this.onChange(this.getPokemon(), filteredStats, relLevels); - - const end = () => { - if (this.showMessage) { - const messages = this.getStatChangeMessages(filteredStats, levels.value, relLevels); - for (const message of messages) { - this.scene.queueMessage(message); - } - } - - for (const stat of filteredStats) { - pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + levels.value, 6), -6); - } - - if (levels.value > 0 && this.canBeCopied) { - for (const opponent of pokemon.getOpponents()) { - applyAbAttrs(StatChangeCopyAbAttr, opponent, null, this.stats, levels.value); - } - } - - applyPostStatChangeAbAttrs(PostStatChangeAbAttr, pokemon, filteredStats, this.levels, this.selfTarget); - - // Look for any other stat change phases; if this is the last one, do White Herb check - const existingPhase = this.scene.findPhase(p => p instanceof StatChangePhase && p.battlerIndex === this.battlerIndex); - if (!(existingPhase instanceof StatChangePhase)) { - // Apply White Herb if needed - const whiteHerb = this.scene.applyModifier(PokemonResetNegativeStatStageModifier, this.player, pokemon) as PokemonResetNegativeStatStageModifier; - // If the White Herb was applied, consume it - if (whiteHerb) { - --whiteHerb.stackCount; - if (whiteHerb.stackCount <= 0) { - this.scene.removeModifier(whiteHerb); - } - this.scene.updateModifiers(this.player); - } - } - - pokemon.updateInfo(); - - handleTutorial(this.scene, Tutorial.Stat_Change).then(() => super.end()); - }; - - if (relLevels.filter(l => l).length && this.scene.moveAnimations) { - pokemon.enableMask(); - const pokemonMaskSprite = pokemon.maskSprite; - - const tileX = (this.player ? 106 : 236) * pokemon.getSpriteScale() * this.scene.field.scale; - const tileY = ((this.player ? 148 : 84) + (levels.value >= 1 ? 160 : 0)) * pokemon.getSpriteScale() * this.scene.field.scale; - const tileWidth = 156 * this.scene.field.scale * pokemon.getSpriteScale(); - const tileHeight = 316 * this.scene.field.scale * pokemon.getSpriteScale(); - - // On increase, show the red sprite located at ATK - // On decrease, show the blue sprite located at SPD - const spriteColor = levels.value >= 1 ? BattleStat[BattleStat.ATK].toLowerCase() : BattleStat[BattleStat.SPD].toLowerCase(); - const statSprite = this.scene.add.tileSprite(tileX, tileY, tileWidth, tileHeight, "battle_stats", spriteColor); - statSprite.setPipeline(this.scene.fieldSpritePipeline); - statSprite.setAlpha(0); - statSprite.setScale(6); - statSprite.setOrigin(0.5, 1); - - this.scene.playSound(`stat_${levels.value >= 1 ? "up" : "down"}`); - - statSprite.setMask(new Phaser.Display.Masks.BitmapMask(this.scene, pokemonMaskSprite ?? undefined)); - - this.scene.tweens.add({ - targets: statSprite, - duration: 250, - alpha: 0.8375, - onComplete: () => { - this.scene.tweens.add({ - targets: statSprite, - delay: 1000, - duration: 250, - alpha: 0 - }); - } - }); - - this.scene.tweens.add({ - targets: statSprite, - duration: 1500, - y: `${levels.value >= 1 ? "-" : "+"}=${160 * 6}` - }); - - this.scene.time.delayedCall(1750, () => { - pokemon.disableMask(); - end(); - }); - } else { - end(); - } - } - - getRandomStat(): BattleStat { - const allStats = Utils.getEnumValues(BattleStat); - return this.getPokemon() ? allStats[this.getPokemon()!.randSeedInt(BattleStat.SPD + 1)] : BattleStat.ATK; // TODO: return default ATK on random? idk... - } - - aggregateStatChanges(random: boolean = false): void { - const isAccEva = [BattleStat.ACC, BattleStat.EVA].some(s => this.stats.includes(s)); - let existingPhase: StatChangePhase; - if (this.stats.length === 1) { - while ((existingPhase = (this.scene.findPhase(p => p instanceof StatChangePhase && p.battlerIndex === this.battlerIndex && p.stats.length === 1 - && (p.stats[0] === this.stats[0] || (random && p.stats[0] === BattleStat.RAND)) - && p.selfTarget === this.selfTarget && p.showMessage === this.showMessage && p.ignoreAbilities === this.ignoreAbilities) as StatChangePhase))) { - if (existingPhase.stats[0] === BattleStat.RAND) { - existingPhase.stats[0] = this.getRandomStat(); - if (existingPhase.stats[0] !== this.stats[0]) { - continue; - } - } - this.levels += existingPhase.levels; - - if (!this.scene.tryRemovePhase(p => p === existingPhase)) { - break; - } - } - } - while ((existingPhase = (this.scene.findPhase(p => p instanceof StatChangePhase && p.battlerIndex === this.battlerIndex && p.selfTarget === this.selfTarget - && ([BattleStat.ACC, BattleStat.EVA].some(s => p.stats.includes(s)) === isAccEva) - && p.levels === this.levels && p.showMessage === this.showMessage && p.ignoreAbilities === this.ignoreAbilities) as StatChangePhase))) { - this.stats.push(...existingPhase.stats); - if (!this.scene.tryRemovePhase(p => p === existingPhase)) { - break; - } - } - } - - getStatChangeMessages(stats: BattleStat[], levels: integer, relLevels: integer[]): string[] { - const messages: string[] = []; - - const relLevelStatIndexes = {}; - for (let rl = 0; rl < relLevels.length; rl++) { - const relLevel = relLevels[rl]; - if (!relLevelStatIndexes[relLevel]) { - relLevelStatIndexes[relLevel] = []; - } - relLevelStatIndexes[relLevel].push(rl); - } - - Object.keys(relLevelStatIndexes).forEach(rl => { - const relLevelStats = stats.filter((_, i) => relLevelStatIndexes[rl].includes(i)); - let statsFragment = ""; - - if (relLevelStats.length > 1) { - statsFragment = relLevelStats.length >= 5 - ? i18next.t("battle:stats") - : `${relLevelStats.slice(0, -1).map(s => getBattleStatName(s)).join(", ")}${relLevelStats.length > 2 ? "," : ""} ${i18next.t("battle:statsAnd")} ${getBattleStatName(relLevelStats[relLevelStats.length - 1])}`; - messages.push(getBattleStatLevelChangeDescription(getPokemonNameWithAffix(this.getPokemon()), statsFragment, Math.abs(parseInt(rl)), levels >= 1,relLevelStats.length)); - } else { - statsFragment = getBattleStatName(relLevelStats[0]); - messages.push(getBattleStatLevelChangeDescription(getPokemonNameWithAffix(this.getPokemon()), statsFragment, Math.abs(parseInt(rl)), levels >= 1,relLevelStats.length)); - } - }); - - return messages; - } -} - -export class WeatherEffectPhase extends CommonAnimPhase { - public weather: Weather | null; - - constructor(scene: BattleScene) { - super(scene, undefined, undefined, CommonAnim.SUNNY + ((scene?.arena?.weather?.weatherType || WeatherType.NONE) - 1)); - this.weather = scene?.arena?.weather; - } - - start() { - // Update weather state with any changes that occurred during the turn - this.weather = this.scene?.arena?.weather; - - if (!this.weather) { - this.end(); - return; - } - - this.setAnimation(CommonAnim.SUNNY + (this.weather.weatherType - 1)); - - if (this.weather.isDamaging()) { - - const cancelled = new Utils.BooleanHolder(false); - - this.executeForAll((pokemon: Pokemon) => applyPreWeatherEffectAbAttrs(SuppressWeatherEffectAbAttr, pokemon, this.weather, cancelled)); - - if (!cancelled.value) { - const inflictDamage = (pokemon: Pokemon) => { - const cancelled = new Utils.BooleanHolder(false); - - applyPreWeatherEffectAbAttrs(PreWeatherDamageAbAttr, pokemon, this.weather , cancelled); - applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); - - if (cancelled.value) { - return; - } - - const damage = Math.ceil(pokemon.getMaxHp() / 16); - - this.scene.queueMessage(getWeatherDamageMessage(this.weather?.weatherType!, pokemon)!); // TODO: are those bangs correct? - pokemon.damageAndUpdate(damage, HitResult.EFFECTIVE, false, false, true); - }; - - this.executeForAll((pokemon: Pokemon) => { - const immune = !pokemon || !!pokemon.getTypes(true, true).filter(t => this.weather?.isTypeDamageImmune(t)).length; - if (!immune) { - inflictDamage(pokemon); - } - }); - } - } - - this.scene.ui.showText(getWeatherLapseMessage(this.weather.weatherType)!, null, () => { // TODO: is this bang correct? - this.executeForAll((pokemon: Pokemon) => applyPostWeatherLapseAbAttrs(PostWeatherLapseAbAttr, pokemon, this.weather)); - - super.start(); - }); - } -} - -export class ObtainStatusEffectPhase extends PokemonPhase { - private statusEffect: StatusEffect | undefined; - private cureTurn: integer | null; - private sourceText: string | null; - private sourcePokemon: Pokemon | null; - - constructor(scene: BattleScene, battlerIndex: BattlerIndex, statusEffect?: StatusEffect, cureTurn?: integer | null, sourceText?: string, sourcePokemon?: Pokemon) { - super(scene, battlerIndex); - - this.statusEffect = statusEffect; - this.cureTurn = cureTurn!; // TODO: is this bang correct? - this.sourceText = sourceText!; // TODO: is this bang correct? - this.sourcePokemon = sourcePokemon!; // For tracking which Pokemon caused the status effect // TODO: is this bang correct? - } - - start() { - const pokemon = this.getPokemon(); - if (!pokemon?.status) { - if (pokemon?.trySetStatus(this.statusEffect, false, this.sourcePokemon)) { - if (this.cureTurn) { - pokemon.status!.cureTurn = this.cureTurn; // TODO: is this bang correct? - } - pokemon.updateInfo(true); - new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect! - 1), pokemon).play(this.scene, () => { - 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; - } - } else if (pokemon.status.effect === this.statusEffect) { - this.scene.queueMessage(getStatusEffectOverlapText(this.statusEffect, getPokemonNameWithAffix(pokemon))); - } - this.end(); - } -} - -export class PostTurnStatusEffectPhase extends PokemonPhase { - constructor(scene: BattleScene, battlerIndex: BattlerIndex) { - super(scene, battlerIndex); - } - - start() { - const pokemon = this.getPokemon(); - if (pokemon?.isActive(true) && pokemon.status && pokemon.status.isPostTurn()) { - pokemon.status.incrementTurn(); - const cancelled = new Utils.BooleanHolder(false); - applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); - applyAbAttrs(BlockStatusDamageAbAttr, pokemon, cancelled); - - if (!cancelled.value) { - this.scene.queueMessage(getStatusEffectActivationText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); - const damage = new Utils.NumberHolder(0); - switch (pokemon.status.effect) { - case StatusEffect.POISON: - damage.value = Math.max(pokemon.getMaxHp() >> 3, 1); - break; - case StatusEffect.TOXIC: - damage.value = Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 1); - break; - case StatusEffect.BURN: - damage.value = Math.max(pokemon.getMaxHp() >> 4, 1); - applyAbAttrs(ReduceBurnDamageAbAttr, pokemon, null, damage); - break; - } - if (damage.value) { - // Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ... - this.scene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage.value, false, true)); - pokemon.updateInfo(); - } - new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => this.end()); - } else { - this.end(); - } - } else { - this.end(); - } - } - - override end() { - if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { - this.scene.initFinalBossPhaseTwo(this.getPokemon()); - } else { - super.end(); - } - } -} - -export class MessagePhase extends Phase { - private text: string; - private callbackDelay: integer | null; - private prompt: boolean | null; - private promptDelay: integer | null; - - constructor(scene: BattleScene, text: string, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) { - super(scene); - - this.text = text; - this.callbackDelay = callbackDelay!; // TODO: is this bang correct? - this.prompt = prompt!; // TODO: is this bang correct? - this.promptDelay = promptDelay!; // TODO: is this bang correct? - } - - start() { - super.start(); - - if (this.text.indexOf("$") > -1) { - const pageIndex = this.text.indexOf("$"); - this.scene.unshiftPhase(new MessagePhase(this.scene, this.text.slice(pageIndex + 1), this.callbackDelay, this.prompt, this.promptDelay)); - this.text = this.text.slice(0, pageIndex).trim(); - } - - this.scene.ui.showText(this.text, null, () => this.end(), this.callbackDelay || (this.prompt ? 0 : 1500), this.prompt, this.promptDelay); - } - - end() { - if (this.scene.abilityBar.shown) { - this.scene.abilityBar.hide(); - } - - super.end(); - } -} - -export class DamagePhase extends PokemonPhase { - private amount: integer; - private damageResult: DamageResult; - private critical: boolean; - - constructor(scene: BattleScene, battlerIndex: BattlerIndex, amount: integer, damageResult?: DamageResult, critical: boolean = false) { - super(scene, battlerIndex); - - this.amount = amount; - this.damageResult = damageResult || HitResult.EFFECTIVE; - this.critical = critical; - } - - start() { - super.start(); - - if (this.damageResult === HitResult.ONE_HIT_KO) { - if (this.scene.moveAnimations) { - this.scene.toggleInvert(true); - } - this.scene.time.delayedCall(Utils.fixedInt(1000), () => { - this.scene.toggleInvert(false); - this.applyDamage(); - }); - return; - } - - this.applyDamage(); - } - - updateAmount(amount: integer): void { - this.amount = amount; - } - - applyDamage() { - switch (this.damageResult) { - case HitResult.EFFECTIVE: - this.scene.playSound("hit"); - break; - case HitResult.SUPER_EFFECTIVE: - case HitResult.ONE_HIT_KO: - this.scene.playSound("hit_strong"); - break; - case HitResult.NOT_VERY_EFFECTIVE: - this.scene.playSound("hit_weak"); - break; - } - - if (this.amount) { - this.scene.damageNumberHandler.add(this.getPokemon(), this.amount, this.damageResult, this.critical); - } - - if (this.damageResult !== HitResult.OTHER) { - const flashTimer = this.scene.time.addEvent({ - delay: 100, - repeat: 5, - startAt: 200, - callback: () => { - this.getPokemon().getSprite().setVisible(flashTimer.repeatCount % 2 === 0); - if (!flashTimer.repeatCount) { - this.getPokemon().updateInfo().then(() => this.end()); - } - } - }); - } else { - this.getPokemon().updateInfo().then(() => this.end()); - } - } - - override end() { - if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { - this.scene.initFinalBossPhaseTwo(this.getPokemon()); - } else { - super.end(); - } - } -} - -export class FaintPhase extends PokemonPhase { - private preventEndure: boolean; - - constructor(scene: BattleScene, battlerIndex: BattlerIndex, preventEndure?: boolean) { - super(scene, battlerIndex); - - this.preventEndure = preventEndure!; // TODO: is this bang correct? - } - - start() { - super.start(); - - if (!this.preventEndure) { - const instantReviveModifier = this.scene.applyModifier(PokemonInstantReviveModifier, this.player, this.getPokemon()) as PokemonInstantReviveModifier; - - if (instantReviveModifier) { - if (!--instantReviveModifier.stackCount) { - this.scene.removeModifier(instantReviveModifier); - } - this.scene.updateModifiers(this.player); - return this.end(); - } - } - - if (!this.tryOverrideForBattleSpec()) { - this.doFaint(); - } - } - - doFaint(): void { - const pokemon = this.getPokemon(); - - - // Track total times pokemon have been KO'd for supreme overlord/last respects - if (pokemon.isPlayer()) { - this.scene.currentBattle.playerFaints += 1; - } else { - this.scene.currentBattle.enemyFaints += 1; - } - - this.scene.queueMessage(i18next.t("battle:fainted", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), null, true); - - if (pokemon.turnData?.attacksReceived?.length) { - const lastAttack = pokemon.turnData.attacksReceived[0]; - applyPostFaintAbAttrs(PostFaintAbAttr, pokemon, this.scene.getPokemonById(lastAttack.sourceId)!, new PokemonMove(lastAttack.move).getMove(), lastAttack.result); // TODO: is this bang correct? - } - - const alivePlayField = this.scene.getField(true); - alivePlayField.forEach(p => applyPostKnockOutAbAttrs(PostKnockOutAbAttr, p, pokemon)); - if (pokemon.turnData?.attacksReceived?.length) { - const defeatSource = this.scene.getPokemonById(pokemon.turnData.attacksReceived[0].sourceId); - if (defeatSource?.isOnField()) { - applyPostVictoryAbAttrs(PostVictoryAbAttr, defeatSource); - const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move]; - const pvattrs = pvmove.getAttrs(PostVictoryStatChangeAttr); - if (pvattrs.length) { - for (const pvattr of pvattrs) { - pvattr.applyPostVictory(defeatSource, defeatSource, pvmove); - } - } - } - } - - if (this.player) { - const nonFaintedLegalPartyMembers = this.scene.getParty().filter(p => p.isAllowedInBattle()); - const nonFaintedPartyMemberCount = nonFaintedLegalPartyMembers.length; - if (!nonFaintedPartyMemberCount) { - this.scene.unshiftPhase(new GameOverPhase(this.scene)); - } else if (nonFaintedPartyMemberCount === 1 && this.scene.currentBattle.double) { - this.scene.unshiftPhase(new ToggleDoublePositionPhase(this.scene, true)); - } else if (nonFaintedPartyMemberCount >= this.scene.currentBattle.getBattlerCount()) { - this.scene.pushPhase(new SwitchPhase(this.scene, this.fieldIndex, true, false)); - } - } else { - this.scene.unshiftPhase(new VictoryPhase(this.scene, this.battlerIndex)); - if (this.scene.currentBattle.battleType === BattleType.TRAINER) { - const hasReservePartyMember = !!this.scene.getEnemyParty().filter(p => p.isActive() && !p.isOnField() && p.trainerSlot === (pokemon as EnemyPokemon).trainerSlot).length; - if (hasReservePartyMember) { - this.scene.pushPhase(new SwitchSummonPhase(this.scene, this.fieldIndex, -1, false, false, false)); - } - } - } - - // in double battles redirect potential moves off fainted pokemon - if (this.scene.currentBattle.double) { - const allyPokemon = pokemon.getAlly(); - this.scene.redirectPokemonMoves(pokemon, allyPokemon); - } - - pokemon.lapseTags(BattlerTagLapseType.FAINT); - this.scene.getField(true).filter(p => p !== pokemon).forEach(p => p.removeTagsBySourceId(pokemon.id)); - - pokemon.faintCry(() => { - if (pokemon instanceof PlayerPokemon) { - pokemon.addFriendship(-10); - } - pokemon.hideInfo(); - this.scene.playSound("faint"); - this.scene.tweens.add({ - targets: pokemon, - duration: 500, - y: pokemon.y + 150, - ease: "Sine.easeIn", - onComplete: () => { - pokemon.setVisible(false); - pokemon.y -= 150; - pokemon.trySetStatus(StatusEffect.FAINT); - if (pokemon.isPlayer()) { - this.scene.currentBattle.removeFaintedParticipant(pokemon as PlayerPokemon); - } else { - this.scene.addFaintedEnemyScore(pokemon as EnemyPokemon); - this.scene.currentBattle.addPostBattleLoot(pokemon as EnemyPokemon); - } - this.scene.field.remove(pokemon); - this.end(); - } - }); - }); - } - - tryOverrideForBattleSpec(): boolean { - switch (this.scene.currentBattle.battleSpec) { - case BattleSpec.FINAL_BOSS: - if (!this.player) { - const enemy = this.getPokemon(); - if (enemy.formIndex) { - this.scene.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].secondStageWin, enemy.species.name, null, () => this.doFaint()); - } else { - // Final boss' HP threshold has been bypassed; cancel faint and force check for 2nd phase - enemy.hp++; - this.scene.unshiftPhase(new DamagePhase(this.scene, enemy.getBattlerIndex(), 0, HitResult.OTHER)); - this.end(); - } - return true; - } - } - - return false; - } -} - -export class VictoryPhase extends PokemonPhase { - constructor(scene: BattleScene, battlerIndex: BattlerIndex) { - super(scene, battlerIndex); - } - - start() { - super.start(); - - this.scene.gameData.gameStats.pokemonDefeated++; - - const participantIds = this.scene.currentBattle.playerParticipantIds; - const party = this.scene.getParty(); - const expShareModifier = this.scene.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier; - const expBalanceModifier = this.scene.findModifier(m => m instanceof ExpBalanceModifier) as ExpBalanceModifier; - const multipleParticipantExpBonusModifier = this.scene.findModifier(m => m instanceof MultipleParticipantExpBonusModifier) as MultipleParticipantExpBonusModifier; - const nonFaintedPartyMembers = party.filter(p => p.hp); - const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < this.scene.getMaxExpLevel()); - const partyMemberExp: number[] = []; - - if (participantIds.size) { - let expValue = this.getPokemon().getExpValue(); - if (this.scene.currentBattle.battleType === BattleType.TRAINER) { - expValue = Math.floor(expValue * 1.5); - } - for (const partyMember of nonFaintedPartyMembers) { - const pId = partyMember.id; - const participated = participantIds.has(pId); - if (participated) { - partyMember.addFriendship(2); - } - if (!expPartyMembers.includes(partyMember)) { - continue; - } - if (!participated && !expShareModifier) { - partyMemberExp.push(0); - continue; - } - let expMultiplier = 0; - if (participated) { - expMultiplier += (1 / participantIds.size); - if (participantIds.size > 1 && multipleParticipantExpBonusModifier) { - expMultiplier += multipleParticipantExpBonusModifier.getStackCount() * 0.2; - } - } else if (expShareModifier) { - expMultiplier += (expShareModifier.getStackCount() * 0.2) / participantIds.size; - } - if (partyMember.pokerus) { - expMultiplier *= 1.5; - } - if (Overrides.XP_MULTIPLIER_OVERRIDE !== null) { - expMultiplier = Overrides.XP_MULTIPLIER_OVERRIDE; - } - const pokemonExp = new Utils.NumberHolder(expValue * expMultiplier); - this.scene.applyModifiers(PokemonExpBoosterModifier, true, partyMember, pokemonExp); - partyMemberExp.push(Math.floor(pokemonExp.value)); - } - - if (expBalanceModifier) { - let totalLevel = 0; - let totalExp = 0; - expPartyMembers.forEach((expPartyMember, epm) => { - totalExp += partyMemberExp[epm]; - totalLevel += expPartyMember.level; - }); - - const medianLevel = Math.floor(totalLevel / expPartyMembers.length); - - const recipientExpPartyMemberIndexes: number[] = []; - expPartyMembers.forEach((expPartyMember, epm) => { - if (expPartyMember.level <= medianLevel) { - recipientExpPartyMemberIndexes.push(epm); - } - }); - - const splitExp = Math.floor(totalExp / recipientExpPartyMemberIndexes.length); - - expPartyMembers.forEach((_partyMember, pm) => { - partyMemberExp[pm] = Phaser.Math.Linear(partyMemberExp[pm], recipientExpPartyMemberIndexes.indexOf(pm) > -1 ? splitExp : 0, 0.2 * expBalanceModifier.getStackCount()); - }); - } - - for (let pm = 0; pm < expPartyMembers.length; pm++) { - const exp = partyMemberExp[pm]; - - if (exp) { - const partyMemberIndex = party.indexOf(expPartyMembers[pm]); - this.scene.unshiftPhase(expPartyMembers[pm].isOnField() ? new ExpPhase(this.scene, partyMemberIndex, exp) : new ShowPartyExpBarPhase(this.scene, partyMemberIndex, exp)); - } - } - } - - if (!this.scene.getEnemyParty().find(p => this.scene.currentBattle.battleType ? !p?.isFainted(true) : p.isOnField())) { - this.scene.pushPhase(new BattleEndPhase(this.scene)); - if (this.scene.currentBattle.battleType === BattleType.TRAINER) { - this.scene.pushPhase(new TrainerVictoryPhase(this.scene)); - } - if (this.scene.gameMode.isEndless || !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) { - this.scene.pushPhase(new EggLapsePhase(this.scene)); - if (this.scene.currentBattle.waveIndex % 10) { - this.scene.pushPhase(new SelectModifierPhase(this.scene)); - } else if (this.scene.gameMode.isDaily) { - this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_CHARM)); - if (this.scene.currentBattle.waveIndex > 10 && !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) { - this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.GOLDEN_POKEBALL)); - } - } else { - const superExpWave = !this.scene.gameMode.isEndless ? (this.scene.offsetGym ? 0 : 20) : 10; - if (this.scene.gameMode.isEndless && this.scene.currentBattle.waveIndex === 10) { - this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_SHARE)); - } - if (this.scene.currentBattle.waveIndex <= 750 && (this.scene.currentBattle.waveIndex <= 500 || (this.scene.currentBattle.waveIndex % 30) === superExpWave)) { - this.scene.pushPhase(new ModifierRewardPhase(this.scene, (this.scene.currentBattle.waveIndex % 30) !== superExpWave || this.scene.currentBattle.waveIndex > 250 ? modifierTypes.EXP_CHARM : modifierTypes.SUPER_EXP_CHARM)); - } - if (this.scene.currentBattle.waveIndex <= 150 && !(this.scene.currentBattle.waveIndex % 50)) { - this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.GOLDEN_POKEBALL)); - } - if (this.scene.gameMode.isEndless && !(this.scene.currentBattle.waveIndex % 50)) { - this.scene.pushPhase(new ModifierRewardPhase(this.scene, !(this.scene.currentBattle.waveIndex % 250) ? modifierTypes.VOUCHER_PREMIUM : modifierTypes.VOUCHER_PLUS)); - this.scene.pushPhase(new AddEnemyBuffModifierPhase(this.scene)); - } - } - this.scene.pushPhase(new NewBattlePhase(this.scene)); - } else { - this.scene.currentBattle.battleType = BattleType.CLEAR; - this.scene.score += this.scene.gameMode.getClearScoreBonus(); - this.scene.updateScoreText(); - this.scene.pushPhase(new GameOverPhase(this.scene, true)); - } - } - - this.end(); - } -} - -export class TrainerVictoryPhase extends BattlePhase { - constructor(scene: BattleScene) { - super(scene); - } - - start() { - this.scene.disableMenu = true; - - this.scene.playBgm(this.scene.currentBattle.trainer?.config.victoryBgm); - - this.scene.unshiftPhase(new MoneyRewardPhase(this.scene, this.scene.currentBattle.trainer?.config.moneyMultiplier!)); // TODO: is this bang correct? - - const modifierRewardFuncs = this.scene.currentBattle.trainer?.config.modifierRewardFuncs!; // TODO: is this bang correct? - for (const modifierRewardFunc of modifierRewardFuncs) { - this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, modifierRewardFunc)); - } - - const trainerType = this.scene.currentBattle.trainer?.config.trainerType!; // TODO: is this bang correct? - if (vouchers.hasOwnProperty(TrainerType[trainerType])) { - if (!this.scene.validateVoucher(vouchers[TrainerType[trainerType]]) && this.scene.currentBattle.trainer?.config.isBoss) { - this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, [modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][vouchers[TrainerType[trainerType]].voucherType])); - } - } - - this.scene.ui.showText(i18next.t("battle:trainerDefeated", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }), null, () => { - const victoryMessages = this.scene.currentBattle.trainer?.getVictoryMessages()!; // TODO: is this bang correct? - let message: string; - this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(victoryMessages), this.scene.currentBattle.waveIndex); - message = message!; // tell TS compiler it's defined now - - const showMessage = () => { - const originalFunc = showMessageOrEnd; - showMessageOrEnd = () => this.scene.ui.showDialogue(message, this.scene.currentBattle.trainer?.getName(), null, originalFunc); - - showMessageOrEnd(); - }; - let showMessageOrEnd = () => this.end(); - if (victoryMessages?.length) { - if (this.scene.currentBattle.trainer?.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { - const originalFunc = showMessageOrEnd; - showMessageOrEnd = () => this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => originalFunc())); - this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(this.scene.currentBattle.trainer?.getKey()!, getCharVariantFromDialogue(victoryMessages[0])).then(() => showMessage())); // TODO: is this bang correct? - } else { - showMessage(); - } - } else { - showMessageOrEnd(); - } - }, null, true); - - this.showEnemyTrainer(); - } -} - -export class MoneyRewardPhase extends BattlePhase { - private moneyMultiplier: number; - - constructor(scene: BattleScene, moneyMultiplier: number) { - super(scene); - - this.moneyMultiplier = moneyMultiplier; - } - - start() { - const moneyAmount = new Utils.IntegerHolder(this.scene.getWaveMoneyAmount(this.moneyMultiplier)); - - this.scene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount); - - if (this.scene.arena.getTag(ArenaTagType.HAPPY_HOUR)) { - moneyAmount.value *= 2; - } - - this.scene.addMoney(moneyAmount.value); - - const userLocale = navigator.language || "en-US"; - const formattedMoneyAmount = moneyAmount.value.toLocaleString(userLocale); - const message = i18next.t("battle:moneyWon", { moneyAmount: formattedMoneyAmount }); - - this.scene.ui.showText(message, null, () => this.end(), null, true); - } -} - -export class ModifierRewardPhase extends BattlePhase { - protected modifierType: ModifierType; - - constructor(scene: BattleScene, modifierTypeFunc: ModifierTypeFunc) { - super(scene); - - this.modifierType = getModifierType(modifierTypeFunc); - } - - start() { - super.start(); - - this.doReward().then(() => this.end()); - } - - doReward(): Promise { - return new Promise(resolve => { - const newModifier = this.modifierType.newModifier(); - this.scene.addModifier(newModifier).then(() => { - this.scene.playSound("item_fanfare"); - this.scene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }), null, () => resolve(), null, true); - }); - }); - } -} - -export class GameOverModifierRewardPhase extends ModifierRewardPhase { - constructor(scene: BattleScene, modifierTypeFunc: ModifierTypeFunc) { - super(scene, modifierTypeFunc); - } - - doReward(): Promise { - return new Promise(resolve => { - const newModifier = this.modifierType.newModifier(); - this.scene.addModifier(newModifier).then(() => { - this.scene.playSound("level_up_fanfare"); - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.fadeIn(250).then(() => { - this.scene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }), null, () => { - this.scene.time.delayedCall(1500, () => this.scene.arenaBg.setVisible(true)); - resolve(); - }, null, true, 1500); - }); - }); - }); - } -} - -export class RibbonModifierRewardPhase extends ModifierRewardPhase { - private species: PokemonSpecies; - - constructor(scene: BattleScene, modifierTypeFunc: ModifierTypeFunc, species: PokemonSpecies) { - super(scene, modifierTypeFunc); - - this.species = species; - } - - doReward(): Promise { - return new Promise(resolve => { - const newModifier = this.modifierType.newModifier(); - this.scene.addModifier(newModifier).then(() => { - this.scene.playSound("level_up_fanfare"); - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.showText(i18next.t("battle:beatModeFirstTime", { - speciesName: this.species.name, - gameMode: this.scene.gameMode.getName(), - newModifier: newModifier?.type.name - }), null, () => { - resolve(); - }, null, true, 1500); - }); - }); - } -} - -export class GameOverPhase extends BattlePhase { - private victory: boolean; - private firstRibbons: PokemonSpecies[] = []; - - constructor(scene: BattleScene, victory?: boolean) { - super(scene); - - this.victory = !!victory; - } - - start() { - super.start(); - - // Failsafe if players somehow skip floor 200 in classic mode - if (this.scene.gameMode.isClassic && this.scene.currentBattle.waveIndex > 200) { - this.victory = true; - } - - if (this.victory && this.scene.gameMode.isEndless) { - this.scene.ui.showDialogue(i18next.t("PGMmiscDialogue:ending_endless"), i18next.t("PGMmiscDialogue:ending_name"), 0, () => this.handleGameOver()); - } else if (this.victory || !this.scene.enableRetries) { - this.handleGameOver(); - } else { - this.scene.ui.showText(i18next.t("battle:retryBattle"), null, () => { - this.scene.ui.setMode(Mode.CONFIRM, () => { - this.scene.ui.fadeOut(1250).then(() => { - this.scene.reset(); - this.scene.clearPhaseQueue(); - this.scene.gameData.loadSession(this.scene, this.scene.sessionSlotId).then(() => { - this.scene.pushPhase(new EncounterPhase(this.scene, true)); - - const availablePartyMembers = this.scene.getParty().filter(p => p.isAllowedInBattle()).length; - - this.scene.pushPhase(new SummonPhase(this.scene, 0)); - if (this.scene.currentBattle.double && availablePartyMembers > 1) { - this.scene.pushPhase(new SummonPhase(this.scene, 1)); - } - if (this.scene.currentBattle.waveIndex > 1 && this.scene.currentBattle.battleType !== BattleType.TRAINER) { - this.scene.pushPhase(new CheckSwitchPhase(this.scene, 0, this.scene.currentBattle.double)); - if (this.scene.currentBattle.double && availablePartyMembers > 1) { - this.scene.pushPhase(new CheckSwitchPhase(this.scene, 1, this.scene.currentBattle.double)); - } - } - - this.scene.ui.fadeIn(1250); - this.end(); - }); - }); - }, () => this.handleGameOver(), false, 0, 0, 1000); - }); - } - } - - handleGameOver(): void { - const doGameOver = (newClear: boolean) => { - this.scene.disableMenu = true; - this.scene.time.delayedCall(1000, () => { - let firstClear = false; - if (this.victory && newClear) { - if (this.scene.gameMode.isClassic) { - firstClear = this.scene.validateAchv(achvs.CLASSIC_VICTORY); - this.scene.validateAchv(achvs.UNEVOLVED_CLASSIC_VICTORY); - this.scene.gameData.gameStats.sessionsWon++; - for (const pokemon of this.scene.getParty()) { - this.awardRibbon(pokemon); - - if (pokemon.species.getRootSpeciesId() !== pokemon.species.getRootSpeciesId(true)) { - this.awardRibbon(pokemon, true); - } - } - } else if (this.scene.gameMode.isDaily && newClear) { - this.scene.gameData.gameStats.dailyRunSessionsWon++; - } - } - const fadeDuration = this.victory ? 10000 : 5000; - this.scene.fadeOutBgm(fadeDuration, true); - const activeBattlers = this.scene.getField().filter(p => p?.isActive(true)); - activeBattlers.map(p => p.hideInfo()); - this.scene.ui.fadeOut(fadeDuration).then(() => { - activeBattlers.map(a => a.setVisible(false)); - this.scene.setFieldScale(1, true); - this.scene.clearPhaseQueue(); - this.scene.ui.clearText(); - - if (this.victory && this.scene.gameMode.isChallenge) { - this.scene.gameMode.challenges.forEach(c => this.scene.validateAchvs(ChallengeAchv, c)); - } - - const clear = (endCardPhase?: EndCardPhase) => { - if (newClear) { - this.handleUnlocks(); - } - if (this.victory && newClear) { - for (const species of this.firstRibbons) { - this.scene.unshiftPhase(new RibbonModifierRewardPhase(this.scene, modifierTypes.VOUCHER_PLUS, species)); - } - if (!firstClear) { - this.scene.unshiftPhase(new GameOverModifierRewardPhase(this.scene, modifierTypes.VOUCHER_PREMIUM)); - } - } - this.scene.pushPhase(new PostGameOverPhase(this.scene, endCardPhase)); - this.end(); - }; - - if (this.victory && this.scene.gameMode.isClassic) { - const message = miscDialogue.ending[this.scene.gameData.gender === PlayerGender.FEMALE ? 0 : 1]; - - if (!this.scene.ui.shouldSkipDialogue(message)) { - this.scene.ui.fadeIn(500).then(() => { - this.scene.charSprite.showCharacter(`rival_${this.scene.gameData.gender === PlayerGender.FEMALE ? "m" : "f"}`, getCharVariantFromDialogue(miscDialogue.ending[this.scene.gameData.gender === PlayerGender.FEMALE ? 0 : 1])).then(() => { - this.scene.ui.showDialogue(message, this.scene.gameData.gender === PlayerGender.FEMALE ? trainerConfigs[TrainerType.RIVAL].name : trainerConfigs[TrainerType.RIVAL].nameFemale, null, () => { - this.scene.ui.fadeOut(500).then(() => { - this.scene.charSprite.hide().then(() => { - const endCardPhase = new EndCardPhase(this.scene); - this.scene.unshiftPhase(endCardPhase); - clear(endCardPhase); - }); - }); - }); - }); - }); - } else { - const endCardPhase = new EndCardPhase(this.scene); - this.scene.unshiftPhase(endCardPhase); - clear(endCardPhase); - } - } else { - clear(); - } - }); - }); - }; - - /* Added a local check to see if the game is running offline on victory - If Online, execute apiFetch as intended - If Offline, execute offlineNewClear(), a localStorage implementation of newClear daily run checks */ - if (this.victory) { - if (!Utils.isLocal) { - Utils.apiFetch(`savedata/session/newclear?slot=${this.scene.sessionSlotId}&clientSessionId=${clientSessionId}`, true) - .then(response => response.json()) - .then(newClear => doGameOver(newClear)); - } else { - this.scene.gameData.offlineNewClear(this.scene).then(result => { - doGameOver(result); - }); - } - } else { - doGameOver(false); - } - } - - handleUnlocks(): void { - if (this.victory && this.scene.gameMode.isClassic) { - if (!this.scene.gameData.unlocks[Unlockables.ENDLESS_MODE]) { - this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.ENDLESS_MODE)); - } - if (this.scene.getParty().filter(p => p.fusionSpecies).length && !this.scene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE]) { - this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.SPLICED_ENDLESS_MODE)); - } - if (!this.scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE]) { - this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.MINI_BLACK_HOLE)); - } - if (!this.scene.gameData.unlocks[Unlockables.EVIOLITE] && this.scene.getParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)) { - this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.EVIOLITE)); - } - } - } - - awardRibbon(pokemon: Pokemon, forStarter: boolean = false): void { - const speciesId = getPokemonSpecies(pokemon.species.speciesId); - const speciesRibbonCount = this.scene.gameData.incrementRibbonCount(speciesId, forStarter); - // first time classic win, award voucher - if (speciesRibbonCount === 1) { - this.firstRibbons.push(getPokemonSpecies(pokemon.species.getRootSpeciesId(forStarter))); - } - } -} - -export class EndCardPhase extends Phase { - public endCard: Phaser.GameObjects.Image; - public text: Phaser.GameObjects.Text; - - constructor(scene: BattleScene) { - super(scene); - } - - start(): void { - super.start(); - - this.scene.ui.getMessageHandler().bg.setVisible(false); - this.scene.ui.getMessageHandler().nameBoxContainer.setVisible(false); - - this.endCard = this.scene.add.image(0, 0, `end_${this.scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}`); - this.endCard.setOrigin(0); - this.endCard.setScale(0.5); - this.scene.field.add(this.endCard); - - this.text = addTextObject(this.scene, this.scene.game.canvas.width / 12, (this.scene.game.canvas.height / 6) - 16, i18next.t("battle:congratulations"), TextStyle.SUMMARY, { fontSize: "128px" }); - this.text.setOrigin(0.5); - this.scene.field.add(this.text); - - this.scene.ui.clearText(); - - this.scene.ui.fadeIn(1000).then(() => { - - this.scene.ui.showText("", null, () => { - this.scene.ui.getMessageHandler().bg.setVisible(true); - this.end(); - }, null, true); - }); - } -} - -export class UnlockPhase extends Phase { - private unlockable: Unlockables; - - constructor(scene: BattleScene, unlockable: Unlockables) { - super(scene); - - this.unlockable = unlockable; - } - - start(): void { - this.scene.time.delayedCall(2000, () => { - this.scene.gameData.unlocks[this.unlockable] = true; - this.scene.playSound("level_up_fanfare"); - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.showText(i18next.t("battle:unlockedSomething", { unlockedThing: getUnlockableName(this.unlockable) }), null, () => { - this.scene.time.delayedCall(1500, () => this.scene.arenaBg.setVisible(true)); - this.end(); - }, null, true, 1500); - }); - } -} - -export class PostGameOverPhase extends Phase { - private endCardPhase: EndCardPhase | null; - - constructor(scene: BattleScene, endCardPhase?: EndCardPhase) { - super(scene); - - this.endCardPhase = endCardPhase!; // TODO: is this bang correct? - } - - start() { - super.start(); - - const saveAndReset = () => { - this.scene.gameData.saveAll(this.scene, true, true, true).then(success => { - if (!success) { - return this.scene.reset(true); - } - this.scene.gameData.tryClearSession(this.scene, this.scene.sessionSlotId).then((success: boolean | [boolean, boolean]) => { - if (!success[0]) { - return this.scene.reset(true); - } - this.scene.reset(); - this.scene.unshiftPhase(new TitlePhase(this.scene)); - this.end(); - }); - }); - }; - - if (this.endCardPhase) { - this.scene.ui.fadeOut(500).then(() => { - this.scene.ui.getMessageHandler().bg.setVisible(true); - - this.endCardPhase?.endCard.destroy(); - this.endCardPhase?.text.destroy(); - saveAndReset(); - }); - } else { - saveAndReset(); - } - } -} - -/** - * Opens the party selector UI and transitions into a {@linkcode SwitchSummonPhase} - * for the player (if a switch would be valid for the current battle state). - */ -export class SwitchPhase extends BattlePhase { - protected fieldIndex: integer; - private isModal: boolean; - private doReturn: boolean; - - /** - * Creates a new SwitchPhase - * @param scene {@linkcode BattleScene} Current battle scene - * @param fieldIndex Field index to switch out - * @param isModal Indicates if the switch should be forced (true) or is - * optional (false). - * @param doReturn Indicates if the party member on the field should be - * recalled to ball or has already left the field. Passed to {@linkcode SwitchSummonPhase}. - */ - constructor(scene: BattleScene, fieldIndex: integer, isModal: boolean, doReturn: boolean) { - super(scene); - - this.fieldIndex = fieldIndex; - this.isModal = isModal; - this.doReturn = doReturn; - } - - start() { - super.start(); - - // Skip modal switch if impossible (no remaining party members that aren't in battle) - if (this.isModal && !this.scene.getParty().filter(p => p.isAllowedInBattle() && !p.isActive(true)).length) { - return super.end(); - } - - // Skip if the fainted party member has been revived already. doReturn is - // only passed as `false` from FaintPhase (as opposed to other usages such - // as ForceSwitchOutAttr or CheckSwitchPhase), so we only want to check this - // if the mon should have already been returned but is still alive and well - // on the field. see also; battle.test.ts - if (this.isModal && !this.doReturn && !this.scene.getParty()[this.fieldIndex].isFainted()) { - return super.end(); - } - - // Check if there is any space still in field - if (this.isModal && this.scene.getPlayerField().filter(p => p.isAllowedInBattle() && p.isActive(true)).length >= this.scene.currentBattle.getBattlerCount()) { - return super.end(); - } - - // Override field index to 0 in case of double battle where 2/3 remaining legal party members fainted at once - const fieldIndex = this.scene.currentBattle.getBattlerCount() === 1 || this.scene.getParty().filter(p => p.isAllowedInBattle()).length > 1 ? this.fieldIndex : 0; - - this.scene.ui.setMode(Mode.PARTY, this.isModal ? PartyUiMode.FAINT_SWITCH : PartyUiMode.POST_BATTLE_SWITCH, fieldIndex, (slotIndex: integer, option: PartyOption) => { - if (slotIndex >= this.scene.currentBattle.getBattlerCount() && slotIndex < 6) { - this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, fieldIndex, slotIndex, this.doReturn, option === PartyOption.PASS_BATON)); - } - this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); - }, PartyUiHandler.FilterNonFainted); - } -} - -export class ExpPhase extends PlayerPartyMemberPokemonPhase { - private expValue: number; - - constructor(scene: BattleScene, partyMemberIndex: integer, expValue: number) { - super(scene, partyMemberIndex); - - this.expValue = expValue; - } - - start() { - super.start(); - - const pokemon = this.getPokemon(); - const exp = new Utils.NumberHolder(this.expValue); - this.scene.applyModifiers(ExpBoosterModifier, true, exp); - exp.value = Math.floor(exp.value); - this.scene.ui.showText(i18next.t("battle:expGain", { pokemonName: getPokemonNameWithAffix(pokemon), exp: exp.value }), null, () => { - const lastLevel = pokemon.level; - pokemon.addExp(exp.value); - const newLevel = pokemon.level; - if (newLevel > lastLevel) { - this.scene.unshiftPhase(new LevelUpPhase(this.scene, this.partyMemberIndex, lastLevel, newLevel)); - } - pokemon.updateInfo().then(() => this.end()); - }, null, true); - } -} - -export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase { - private expValue: number; - - constructor(scene: BattleScene, partyMemberIndex: integer, expValue: number) { - super(scene, partyMemberIndex); - - this.expValue = expValue; - } - - start() { - super.start(); - - const pokemon = this.getPokemon(); - const exp = new Utils.NumberHolder(this.expValue); - this.scene.applyModifiers(ExpBoosterModifier, true, exp); - exp.value = Math.floor(exp.value); - - const lastLevel = pokemon.level; - pokemon.addExp(exp.value); - const newLevel = pokemon.level; - if (newLevel > lastLevel) { - this.scene.unshiftPhase(new LevelUpPhase(this.scene, this.partyMemberIndex, lastLevel, newLevel)); - } - this.scene.unshiftPhase(new HidePartyExpBarPhase(this.scene)); - pokemon.updateInfo(); - - if (this.scene.expParty === ExpNotification.SKIP) { - this.end(); - } else if (this.scene.expParty === ExpNotification.ONLY_LEVEL_UP) { - if (newLevel > lastLevel) { // this means if we level up - // instead of displaying the exp gain in the small frame, we display the new level - // we use the same method for mode 0 & 1, by giving a parameter saying to display the exp or the level - this.scene.partyExpBar.showPokemonExp(pokemon, exp.value, this.scene.expParty === ExpNotification.ONLY_LEVEL_UP, newLevel).then(() => { - setTimeout(() => this.end(), 800 / Math.pow(2, this.scene.expGainsSpeed)); - }); - } else { - this.end(); - } - } else if (this.scene.expGainsSpeed < 3) { - this.scene.partyExpBar.showPokemonExp(pokemon, exp.value, false, newLevel).then(() => { - setTimeout(() => this.end(), 500 / Math.pow(2, this.scene.expGainsSpeed)); - }); - } else { - this.end(); - } - - } -} - -export class HidePartyExpBarPhase extends BattlePhase { - constructor(scene: BattleScene) { - super(scene); - } - - start() { - super.start(); - - this.scene.partyExpBar.hide().then(() => this.end()); - } -} - -export class LevelUpPhase extends PlayerPartyMemberPokemonPhase { - private lastLevel: integer; - private level: integer; - - constructor(scene: BattleScene, partyMemberIndex: integer, lastLevel: integer, level: integer) { - super(scene, partyMemberIndex); - - this.lastLevel = lastLevel; - this.level = level; - this.scene = scene; - } - - start() { - super.start(); - - if (this.level > this.scene.gameData.gameStats.highestLevel) { - this.scene.gameData.gameStats.highestLevel = this.level; - } - - this.scene.validateAchvs(LevelAchv, new Utils.IntegerHolder(this.level)); - - const pokemon = this.getPokemon(); - const prevStats = pokemon.stats.slice(0); - pokemon.calculateStats(); - pokemon.updateInfo(); - if (this.scene.expParty === ExpNotification.DEFAULT) { - this.scene.playSound("level_up_fanfare"); - this.scene.ui.showText(i18next.t("battle:levelUp", { pokemonName: getPokemonNameWithAffix(this.getPokemon()), level: this.level }), null, () => this.scene.ui.getMessageHandler().promptLevelUpStats(this.partyMemberIndex, prevStats, false).then(() => this.end()), null, true); - } else if (this.scene.expParty === ExpNotification.SKIP) { - this.end(); - } else { - // we still want to display the stats if activated - this.scene.ui.getMessageHandler().promptLevelUpStats(this.partyMemberIndex, prevStats, false).then(() => this.end()); - } - if (this.lastLevel < 100) { // this feels like an unnecessary optimization - const levelMoves = this.getPokemon().getLevelMoves(this.lastLevel + 1); - for (const lm of levelMoves) { - this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, lm[1])); - } - } - if (!pokemon.pauseEvolutions) { - const evolution = pokemon.getEvolution(); - if (evolution) { - this.scene.unshiftPhase(new EvolutionPhase(this.scene, pokemon as PlayerPokemon, evolution, this.lastLevel)); - } - } - } -} - -export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { - private moveId: Moves; - - constructor(scene: BattleScene, partyMemberIndex: integer, moveId: Moves) { - super(scene, partyMemberIndex); - - this.moveId = moveId; - } - - start() { - super.start(); - - const pokemon = this.getPokemon(); - const move = allMoves[this.moveId]; - - const existingMoveIndex = pokemon.getMoveset().findIndex(m => m?.moveId === move.id); - - if (existingMoveIndex > -1) { - return this.end(); - } - - const emptyMoveIndex = pokemon.getMoveset().length < 4 - ? pokemon.getMoveset().length - : pokemon.getMoveset().findIndex(m => m === null); - - const messageMode = this.scene.ui.getHandler() instanceof EvolutionSceneHandler - ? Mode.EVOLUTION_SCENE - : Mode.MESSAGE; - - if (emptyMoveIndex > -1) { - pokemon.setMove(emptyMoveIndex, this.moveId); - initMoveAnim(this.scene, this.moveId).then(() => { - loadMoveAnimAssets(this.scene, [this.moveId], true) - .then(() => { - this.scene.ui.setMode(messageMode).then(() => { - this.scene.playSound("level_up_fanfare"); - this.scene.ui.showText(i18next.t("battle:learnMove", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => { - this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeMoveLearnedTrigger, true); - this.end(); - }, messageMode === Mode.EVOLUTION_SCENE ? 1000 : null, true); - }); - }); - }); - } else { - this.scene.ui.setMode(messageMode).then(() => { - this.scene.ui.showText(i18next.t("battle:learnMovePrompt", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => { - this.scene.ui.showText(i18next.t("battle:learnMoveLimitReached", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { - this.scene.ui.showText(i18next.t("battle:learnMoveReplaceQuestion", { moveName: move.name }), null, () => { - const noHandler = () => { - this.scene.ui.setMode(messageMode).then(() => { - this.scene.ui.showText(i18next.t("battle:learnMoveStopTeaching", { moveName: move.name }), null, () => { - this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => { - this.scene.ui.setMode(messageMode); - this.scene.ui.showText(i18next.t("battle:learnMoveNotLearned", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => this.end(), null, true); - }, () => { - this.scene.ui.setMode(messageMode); - this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId)); - this.end(); - }); - }); - }); - }; - this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => { - this.scene.ui.setMode(messageMode); - this.scene.ui.showText(i18next.t("battle:learnMoveForgetQuestion"), null, () => { - this.scene.ui.setModeWithoutClear(Mode.SUMMARY, this.getPokemon(), SummaryUiMode.LEARN_MOVE, move, (moveIndex: integer) => { - if (moveIndex === 4) { - noHandler(); - return; - } - this.scene.ui.setMode(messageMode).then(() => { - this.scene.ui.showText(i18next.t("battle:countdownPoof"), null, () => { - this.scene.ui.showText(i18next.t("battle:learnMoveForgetSuccess", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: pokemon.moveset[moveIndex]!.getName() }), null, () => { // TODO: is the bang correct? - this.scene.ui.showText(i18next.t("battle:learnMoveAnd"), null, () => { - pokemon.setMove(moveIndex, Moves.NONE); - this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId)); - this.end(); - }, null, true); - }, null, true); - }, null, true); - }); - }); - }, null, true); - }, noHandler); - }); - }, null, true); - }, null, true); - }); - } - } -} - -export class PokemonHealPhase extends CommonAnimPhase { - private hpHealed: integer; - private message: string | null; - private showFullHpMessage: boolean; - private skipAnim: boolean; - private revive: boolean; - private healStatus: boolean; - private preventFullHeal: boolean; - - constructor(scene: BattleScene, battlerIndex: BattlerIndex, hpHealed: integer, message: string | null, showFullHpMessage: boolean, skipAnim: boolean = false, revive: boolean = false, healStatus: boolean = false, preventFullHeal: boolean = false) { - super(scene, battlerIndex, undefined, CommonAnim.HEALTH_UP); - - this.hpHealed = hpHealed; - this.message = message; - this.showFullHpMessage = showFullHpMessage; - this.skipAnim = skipAnim; - this.revive = revive; - this.healStatus = healStatus; - this.preventFullHeal = preventFullHeal; - } - - start() { - if (!this.skipAnim && (this.revive || this.getPokemon().hp) && !this.getPokemon().isFullHp()) { - super.start(); - } else { - this.end(); - } - } - - end() { - const pokemon = this.getPokemon(); - - if (!pokemon.isOnField() || (!this.revive && !pokemon.isActive())) { - super.end(); - return; - } - - const hasMessage = !!this.message; - const healOrDamage = (!pokemon.isFullHp() || this.hpHealed < 0); - let lastStatusEffect = StatusEffect.NONE; - - if (healOrDamage) { - const hpRestoreMultiplier = new Utils.IntegerHolder(1); - if (!this.revive) { - this.scene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier); - } - const healAmount = new Utils.NumberHolder(Math.floor(this.hpHealed * hpRestoreMultiplier.value)); - if (healAmount.value < 0) { - pokemon.damageAndUpdate(healAmount.value * -1, HitResult.HEAL as DamageResult); - healAmount.value = 0; - } - // Prevent healing to full if specified (in case of healing tokens so Sturdy doesn't cause a softlock) - if (this.preventFullHeal && pokemon.hp + healAmount.value >= pokemon.getMaxHp()) { - healAmount.value = (pokemon.getMaxHp() - pokemon.hp) - 1; - } - healAmount.value = pokemon.heal(healAmount.value); - if (healAmount.value) { - this.scene.damageNumberHandler.add(pokemon, healAmount.value, HitResult.HEAL); - } - if (pokemon.isPlayer()) { - this.scene.validateAchvs(HealAchv, healAmount); - if (healAmount.value > this.scene.gameData.gameStats.highestHeal) { - this.scene.gameData.gameStats.highestHeal = healAmount.value; - } - } - if (this.healStatus && !this.revive && pokemon.status) { - lastStatusEffect = pokemon.status.effect; - pokemon.resetStatus(); - } - pokemon.updateInfo().then(() => super.end()); - } else if (this.healStatus && !this.revive && pokemon.status) { - lastStatusEffect = pokemon.status.effect; - pokemon.resetStatus(); - pokemon.updateInfo().then(() => super.end()); - } else if (this.showFullHpMessage) { - this.message = i18next.t("battle:hpIsFull", { pokemonName: getPokemonNameWithAffix(pokemon) }); - } - - if (this.message) { - this.scene.queueMessage(this.message); - } - - if (this.healStatus && lastStatusEffect && !hasMessage) { - this.scene.queueMessage(getStatusEffectHealText(lastStatusEffect, getPokemonNameWithAffix(pokemon))); - } - - if (!healOrDamage && !lastStatusEffect) { - super.end(); - } - } -} - -export class AttemptCapturePhase extends PokemonPhase { - private pokeballType: PokeballType; - private pokeball: Phaser.GameObjects.Sprite; - private originalY: number; - - constructor(scene: BattleScene, targetIndex: integer, pokeballType: PokeballType) { - super(scene, BattlerIndex.ENEMY + targetIndex); - - this.pokeballType = pokeballType; - } - - start() { - super.start(); - - const pokemon = this.getPokemon() as EnemyPokemon; - - if (!pokemon?.hp) { - return this.end(); - } - - this.scene.pokeballCounts[this.pokeballType]--; - - this.originalY = pokemon.y; - - const _3m = 3 * pokemon.getMaxHp(); - const _2h = 2 * pokemon.hp; - const catchRate = pokemon.species.catchRate; - const pokeballMultiplier = getPokeballCatchMultiplier(this.pokeballType); - const statusMultiplier = pokemon.status ? getStatusEffectCatchRateMultiplier(pokemon.status.effect) : 1; - const x = Math.round((((_3m - _2h) * catchRate * pokeballMultiplier) / _3m) * statusMultiplier); - const y = Math.round(65536 / Math.sqrt(Math.sqrt(255 / x))); - const fpOffset = pokemon.getFieldPositionOffset(); - - const pokeballAtlasKey = getPokeballAtlasKey(this.pokeballType); - this.pokeball = this.scene.addFieldSprite(16, 80, "pb", pokeballAtlasKey); - this.pokeball.setOrigin(0.5, 0.625); - this.scene.field.add(this.pokeball); - - this.scene.playSound("pb_throw"); - this.scene.time.delayedCall(300, () => { - this.scene.field.moveBelow(this.pokeball as Phaser.GameObjects.GameObject, pokemon); - }); - - this.scene.tweens.add({ - targets: this.pokeball, - x: { value: 236 + fpOffset[0], ease: "Linear" }, - y: { value: 16 + fpOffset[1], ease: "Cubic.easeOut" }, - duration: 500, - onComplete: () => { - this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`); - this.scene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}_open`)); - this.scene.playSound("pb_rel"); - pokemon.tint(getPokeballTintColor(this.pokeballType)); - - addPokeballOpenParticles(this.scene, this.pokeball.x, this.pokeball.y, this.pokeballType); - - this.scene.tweens.add({ - targets: pokemon, - duration: 500, - ease: "Sine.easeIn", - scale: 0.25, - y: 20, - onComplete: () => { - this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`); - pokemon.setVisible(false); - this.scene.playSound("pb_catch"); - this.scene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}`)); - - const doShake = () => { - let shakeCount = 0; - const pbX = this.pokeball.x; - const shakeCounter = this.scene.tweens.addCounter({ - from: 0, - to: 1, - repeat: 4, - yoyo: true, - ease: "Cubic.easeOut", - duration: 250, - repeatDelay: 500, - onUpdate: t => { - if (shakeCount && shakeCount < 4) { - const value = t.getValue(); - const directionMultiplier = shakeCount % 2 === 1 ? 1 : -1; - this.pokeball.setX(pbX + value * 4 * directionMultiplier); - this.pokeball.setAngle(value * 27.5 * directionMultiplier); - } - }, - onRepeat: () => { - if (!pokemon.species.isObtainable()) { - shakeCounter.stop(); - this.failCatch(shakeCount); - } else if (shakeCount++ < 3) { - if (pokeballMultiplier === -1 || pokemon.randSeedInt(65536) < y) { - this.scene.playSound("pb_move"); - } else { - shakeCounter.stop(); - this.failCatch(shakeCount); - } - } else { - this.scene.playSound("pb_lock"); - addPokeballCaptureStars(this.scene, this.pokeball); - - const pbTint = this.scene.add.sprite(this.pokeball.x, this.pokeball.y, "pb", "pb"); - pbTint.setOrigin(this.pokeball.originX, this.pokeball.originY); - pbTint.setTintFill(0); - pbTint.setAlpha(0); - this.scene.field.add(pbTint); - this.scene.tweens.add({ - targets: pbTint, - alpha: 0.375, - duration: 200, - easing: "Sine.easeOut", - onComplete: () => { - this.scene.tweens.add({ - targets: pbTint, - alpha: 0, - duration: 200, - easing: "Sine.easeIn", - onComplete: () => pbTint.destroy() - }); - } - }); - } - }, - onComplete: () => { - this.catch(); - } - }); - }; - - this.scene.time.delayedCall(250, () => doPokeballBounceAnim(this.scene, this.pokeball, 16, 72, 350, doShake)); - } - }); - } - }); - } - - failCatch(shakeCount: integer) { - const pokemon = this.getPokemon(); - - this.scene.playSound("pb_rel"); - pokemon.setY(this.originalY); - if (pokemon.status?.effect !== StatusEffect.SLEEP) { - pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 }); - } - pokemon.tint(getPokeballTintColor(this.pokeballType)); - pokemon.setVisible(true); - pokemon.untint(250, "Sine.easeOut"); - - const pokeballAtlasKey = getPokeballAtlasKey(this.pokeballType); - this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`); - this.scene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}_open`)); - - this.scene.tweens.add({ - targets: pokemon, - duration: 250, - ease: "Sine.easeOut", - scale: 1 - }); - - this.scene.currentBattle.lastUsedPokeball = this.pokeballType; - this.removePb(); - this.end(); - } - - catch() { - const pokemon = this.getPokemon() as EnemyPokemon; - - const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm(); - - if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1) { - this.scene.validateAchv(achvs.HIDDEN_ABILITY); - } - - if (pokemon.species.subLegendary) { - this.scene.validateAchv(achvs.CATCH_SUB_LEGENDARY); - } - - if (pokemon.species.legendary) { - this.scene.validateAchv(achvs.CATCH_LEGENDARY); - } - - if (pokemon.species.mythical) { - this.scene.validateAchv(achvs.CATCH_MYTHICAL); - } - - this.scene.pokemonInfoContainer.show(pokemon, true); - - this.scene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs); - - this.scene.ui.showText(i18next.t("battle:pokemonCaught", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { - const end = () => { - this.scene.unshiftPhase(new VictoryPhase(this.scene, this.battlerIndex)); - this.scene.pokemonInfoContainer.hide(); - this.removePb(); - this.end(); - }; - const removePokemon = () => { - this.scene.addFaintedEnemyScore(pokemon); - this.scene.getPlayerField().filter(p => p.isActive(true)).forEach(playerPokemon => playerPokemon.removeTagsBySourceId(pokemon.id)); - pokemon.hp = 0; - pokemon.trySetStatus(StatusEffect.FAINT); - this.scene.clearEnemyHeldItemModifiers(); - this.scene.field.remove(pokemon, true); - }; - const addToParty = () => { - const newPokemon = pokemon.addToParty(this.pokeballType); - const modifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier, false); - if (this.scene.getParty().filter(p => p.isShiny()).length === 6) { - this.scene.validateAchv(achvs.SHINY_PARTY); - } - Promise.all(modifiers.map(m => this.scene.addModifier(m, true))).then(() => { - this.scene.updateModifiers(true); - removePokemon(); - if (newPokemon) { - newPokemon.loadAssets().then(end); - } else { - end(); - } - }); - }; - Promise.all([pokemon.hideInfo(), this.scene.gameData.setPokemonCaught(pokemon)]).then(() => { - if (this.scene.getParty().length === 6) { - const promptRelease = () => { - this.scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => { - this.scene.pokemonInfoContainer.makeRoomForConfirmUi(1, true); - this.scene.ui.setMode(Mode.CONFIRM, () => { - const newPokemon = this.scene.addPlayerPokemon(pokemon.species, pokemon.level, pokemon.abilityIndex, pokemon.formIndex, pokemon.gender, pokemon.shiny, pokemon.variant, pokemon.ivs, pokemon.nature, pokemon); - this.scene.ui.setMode(Mode.SUMMARY, newPokemon, 0, SummaryUiMode.DEFAULT, () => { - this.scene.ui.setMode(Mode.MESSAGE).then(() => { - promptRelease(); - }); - }, false); - }, () => { - this.scene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, this.fieldIndex, (slotIndex: integer, _option: PartyOption) => { - this.scene.ui.setMode(Mode.MESSAGE).then(() => { - if (slotIndex < 6) { - addToParty(); - } else { - promptRelease(); - } - }); - }); - }, () => { - this.scene.ui.setMode(Mode.MESSAGE).then(() => { - removePokemon(); - end(); - }); - }, "fullParty"); - }); - }; - promptRelease(); - } else { - addToParty(); - } - }); - }, 0, true); - } - - removePb() { - this.scene.tweens.add({ - targets: this.pokeball, - duration: 250, - delay: 250, - ease: "Sine.easeIn", - alpha: 0, - onComplete: () => this.pokeball.destroy() - }); - } -} - -export class AttemptRunPhase extends PokemonPhase { - constructor(scene: BattleScene, fieldIndex: integer) { - super(scene, fieldIndex); - } - - start() { - super.start(); - - const playerPokemon = this.getPokemon(); - const enemyField = this.scene.getEnemyField(); - - const enemySpeed = enemyField.reduce((total: integer, enemyPokemon: Pokemon) => total + enemyPokemon.getStat(Stat.SPD), 0) / enemyField.length; - - const escapeChance = new Utils.IntegerHolder((((playerPokemon.getStat(Stat.SPD) * 128) / enemySpeed) + (30 * this.scene.currentBattle.escapeAttempts++)) % 256); - applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, escapeChance); - - if (playerPokemon.randSeedInt(256) < escapeChance.value) { - this.scene.playSound("flee"); - this.scene.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500); - - this.scene.tweens.add({ - targets: [this.scene.arenaEnemy, enemyField].flat(), - alpha: 0, - duration: 250, - ease: "Sine.easeIn", - onComplete: () => enemyField.forEach(enemyPokemon => enemyPokemon.destroy()) - }); - - this.scene.clearEnemyHeldItemModifiers(); - - enemyField.forEach(enemyPokemon => { - enemyPokemon.hideInfo().then(() => enemyPokemon.destroy()); - enemyPokemon.hp = 0; - enemyPokemon.trySetStatus(StatusEffect.FAINT); - }); - - this.scene.pushPhase(new BattleEndPhase(this.scene)); - this.scene.pushPhase(new NewBattlePhase(this.scene)); - } else { - this.scene.queueMessage(i18next.t("battle:runAwayCannotEscape"), null, true, 500); - } - - this.end(); - } -} - -export class SelectModifierPhase extends BattlePhase { - private rerollCount: integer; - private modifierTiers: ModifierTier[]; - - constructor(scene: BattleScene, rerollCount: integer = 0, modifierTiers?: ModifierTier[]) { - super(scene); - - this.rerollCount = rerollCount; - this.modifierTiers = modifierTiers!; // TODO: is this bang correct? - } - - start() { - super.start(); - - if (!this.rerollCount) { - this.updateSeed(); - } else { - this.scene.reroll = false; - } - - const party = this.scene.getParty(); - regenerateModifierPoolThresholds(party, this.getPoolType(), this.rerollCount); - const modifierCount = new Utils.IntegerHolder(3); - if (this.isPlayer()) { - this.scene.applyModifiers(ExtraModifierModifier, true, modifierCount); - } - const typeOptions: ModifierTypeOption[] = this.getModifierTypeOptions(modifierCount.value); - - const modifierSelectCallback = (rowCursor: integer, cursor: integer) => { - if (rowCursor < 0 || cursor < 0) { - this.scene.ui.showText(i18next.t("battle:skipItemQuestion"), null, () => { - this.scene.ui.setOverlayMode(Mode.CONFIRM, () => { - this.scene.ui.revertMode(); - this.scene.ui.setMode(Mode.MESSAGE); - super.end(); - }, () => this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers))); - }); - return false; - } - let modifierType: ModifierType; - let cost: integer; - switch (rowCursor) { - case 0: - switch (cursor) { - case 0: - const rerollCost = this.getRerollCost(typeOptions, this.scene.lockModifierTiers); - if (this.scene.money < rerollCost) { - this.scene.ui.playError(); - return false; - } else { - this.scene.reroll = true; - this.scene.unshiftPhase(new SelectModifierPhase(this.scene, this.rerollCount + 1, typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as ModifierTier[])); - this.scene.ui.clearText(); - this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); - if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { - this.scene.money -= rerollCost; - this.scene.updateMoneyText(); - this.scene.animateMoneyChanged(false); - } - this.scene.playSound("buy"); - } - break; - case 1: - this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: integer, itemIndex: integer, itemQuantity: integer, toSlotIndex: integer) => { - if (toSlotIndex !== undefined && fromSlotIndex < 6 && toSlotIndex < 6 && fromSlotIndex !== toSlotIndex && itemIndex > -1) { - const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier - && m.isTransferrable && m.pokemonId === party[fromSlotIndex].id) as PokemonHeldItemModifier[]; - const itemModifier = itemModifiers[itemIndex]; - this.scene.tryTransferHeldItemModifier(itemModifier, party[toSlotIndex], true, itemQuantity); - } else { - this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); - } - }, PartyUiHandler.FilterItemMaxStacks); - break; - case 2: - this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.CHECK, -1, () => { - this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); - }); - break; - case 3: - this.scene.lockModifierTiers = !this.scene.lockModifierTiers; - const uiHandler = this.scene.ui.getHandler() as ModifierSelectUiHandler; - uiHandler.setRerollCost(this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); - uiHandler.updateLockRaritiesText(); - uiHandler.updateRerollCostText(); - return false; - } - return true; - case 1: - if (typeOptions[cursor].type) { - modifierType = typeOptions[cursor].type; - } - break; - default: - const shopOptions = getPlayerShopModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex, this.scene.getWaveMoneyAmount(1)); - const shopOption = shopOptions[rowCursor > 2 || shopOptions.length <= SHOP_OPTIONS_ROW_LIMIT ? cursor : cursor + SHOP_OPTIONS_ROW_LIMIT]; - if (shopOption.type) { - modifierType = shopOption.type; - } - cost = shopOption.cost; - break; - } - - if (cost! && (this.scene.money < cost) && !Overrides.WAIVE_ROLL_FEE_OVERRIDE) { // TODO: is the bang on cost correct? - this.scene.ui.playError(); - return false; - } - - const applyModifier = (modifier: Modifier, playSound: boolean = false) => { - const result = this.scene.addModifier(modifier, false, playSound); - if (cost) { - result.then(success => { - if (success) { - if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { - this.scene.money -= cost; - this.scene.updateMoneyText(); - this.scene.animateMoneyChanged(false); - } - this.scene.playSound("buy"); - (this.scene.ui.getHandler() as ModifierSelectUiHandler).updateCostText(); - } else { - this.scene.ui.playError(); - } - }); - } else { - const doEnd = () => { - this.scene.ui.clearText(); - this.scene.ui.setMode(Mode.MESSAGE); - super.end(); - }; - if (result instanceof Promise) { - result.then(() => doEnd()); - } else { - doEnd(); - } - } - }; - - if (modifierType! instanceof PokemonModifierType) { //TODO: is the bang correct? - if (modifierType instanceof FusePokemonModifierType) { - this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.SPLICE, -1, (fromSlotIndex: integer, spliceSlotIndex: integer) => { - if (spliceSlotIndex !== undefined && fromSlotIndex < 6 && spliceSlotIndex < 6 && fromSlotIndex !== spliceSlotIndex) { - this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer()).then(() => { - const modifier = modifierType.newModifier(party[fromSlotIndex], party[spliceSlotIndex])!; //TODO: is the bang correct? - applyModifier(modifier, true); - }); - } else { - this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); - } - }, modifierType.selectFilter); - } else { - const pokemonModifierType = modifierType as PokemonModifierType; - const isMoveModifier = modifierType instanceof PokemonMoveModifierType; - const isTmModifier = modifierType instanceof TmModifierType; - const isRememberMoveModifier = modifierType instanceof RememberMoveModifierType; - const isPpRestoreModifier = (modifierType instanceof PokemonPpRestoreModifierType || modifierType instanceof PokemonPpUpModifierType); - const partyUiMode = isMoveModifier ? PartyUiMode.MOVE_MODIFIER - : isTmModifier ? PartyUiMode.TM_MODIFIER - : isRememberMoveModifier ? PartyUiMode.REMEMBER_MOVE_MODIFIER - : PartyUiMode.MODIFIER; - const tmMoveId = isTmModifier - ? (modifierType as TmModifierType).moveId - : undefined; - this.scene.ui.setModeWithoutClear(Mode.PARTY, partyUiMode, -1, (slotIndex: integer, option: PartyOption) => { - if (slotIndex < 6) { - this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer()).then(() => { - const modifier = !isMoveModifier - ? !isRememberMoveModifier - ? modifierType.newModifier(party[slotIndex]) - : modifierType.newModifier(party[slotIndex], option as integer) - : modifierType.newModifier(party[slotIndex], option - PartyOption.MOVE_1); - applyModifier(modifier!, true); // TODO: is the bang correct? - }); - } else { - this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); - } - }, pokemonModifierType.selectFilter, modifierType instanceof PokemonMoveModifierType ? (modifierType as PokemonMoveModifierType).moveSelectFilter : undefined, tmMoveId, isPpRestoreModifier); - } - } else { - applyModifier(modifierType!.newModifier()!); // TODO: is the bang correct? - } - - return !cost!;// TODO: is the bang correct? - }; - this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); - } - - updateSeed(): void { - this.scene.resetSeed(); - } - - isPlayer(): boolean { - return true; - } - - getRerollCost(typeOptions: ModifierTypeOption[], lockRarities: boolean): integer { - let baseValue = 0; - if (Overrides.WAIVE_ROLL_FEE_OVERRIDE) { - return baseValue; - } else if (lockRarities) { - const tierValues = [50, 125, 300, 750, 2000]; - for (const opt of typeOptions) { - baseValue += opt.type?.tier ? tierValues[opt.type.tier] : 0; - } - } else { - baseValue = 250; - } - return Math.min(Math.ceil(this.scene.currentBattle.waveIndex / 10) * baseValue * Math.pow(2, this.rerollCount), Number.MAX_SAFE_INTEGER); - } - - getPoolType(): ModifierPoolType { - return ModifierPoolType.PLAYER; - } - - getModifierTypeOptions(modifierCount: integer): ModifierTypeOption[] { - return getPlayerModifierTypeOptions(modifierCount, this.scene.getParty(), this.scene.lockModifierTiers ? this.modifierTiers : undefined); - } - - addModifier(modifier: Modifier): Promise { - return this.scene.addModifier(modifier, false, true); - } -} - -export class EggLapsePhase extends Phase { - constructor(scene: BattleScene) { - super(scene); - } - - start() { - super.start(); - - const eggsToHatch: Egg[] = this.scene.gameData.eggs.filter((egg: Egg) => { - return Overrides.EGG_IMMEDIATE_HATCH_OVERRIDE ? true : --egg.hatchWaves < 1; - }); - - let eggCount: integer = eggsToHatch.length; - - if (eggCount) { - this.scene.queueMessage(i18next.t("battle:eggHatching")); - - for (const egg of eggsToHatch) { - this.scene.unshiftPhase(new EggHatchPhase(this.scene, egg, eggCount)); - if (eggCount > 0) { - eggCount--; - } - } - - } - this.end(); - } -} - -export class AddEnemyBuffModifierPhase extends Phase { - constructor(scene: BattleScene) { - super(scene); - } - - start() { - super.start(); - - const waveIndex = this.scene.currentBattle.waveIndex; - const tier = !(waveIndex % 1000) ? ModifierTier.ULTRA : !(waveIndex % 250) ? ModifierTier.GREAT : ModifierTier.COMMON; - - regenerateModifierPoolThresholds(this.scene.getEnemyParty(), ModifierPoolType.ENEMY_BUFF); - - const count = Math.ceil(waveIndex / 250); - for (let i = 0; i < count; i++) { - this.scene.addEnemyModifier(getEnemyBuffModifierForWave(tier, this.scene.findModifiers(m => m instanceof EnemyPersistentModifier, false), this.scene), true, true); - } - this.scene.updateModifiers(false, true).then(() => this.end()); - } -} - -/** - * Cures the party of all non-volatile status conditions, shows a message - * @param {BattleScene} scene The current scene - * @param {Pokemon} user The user of the move that cures the party - * @param {string} message The message that should be displayed - * @param {Abilities} abilityCondition Pokemon with this ability will not be affected ie. Soundproof - */ -export class PartyStatusCurePhase extends BattlePhase { - private user: Pokemon; - private message: string; - private abilityCondition: Abilities; - - constructor(scene: BattleScene, user: Pokemon, message: string, abilityCondition: Abilities) { - super(scene); - - this.user = user; - this.message = message; - this.abilityCondition = abilityCondition; - } - - start() { - super.start(); - for (const pokemon of this.scene.getParty()) { - if (!pokemon.isOnField() || pokemon === this.user) { - pokemon.resetStatus(false); - pokemon.updateInfo(true); - } else { - if (!pokemon.hasAbility(this.abilityCondition)) { - pokemon.resetStatus(); - pokemon.updateInfo(true); - } else { - // Manually show ability bar, since we're not hooked into the targeting system - pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.id, pokemon.getPassiveAbility()?.id === this.abilityCondition)); - } - } - } - if (this.message) { - this.scene.queueMessage(this.message); - } - this.end(); - } -} - -export class PartyHealPhase extends BattlePhase { - private resumeBgm: boolean; - - constructor(scene: BattleScene, resumeBgm: boolean) { - super(scene); - - this.resumeBgm = resumeBgm; - } - - start() { - super.start(); - - const bgmPlaying = this.scene.isBgmPlaying(); - if (bgmPlaying) { - this.scene.fadeOutBgm(1000, false); - } - this.scene.ui.fadeOut(1000).then(() => { - for (const pokemon of this.scene.getParty()) { - pokemon.hp = pokemon.getMaxHp(); - pokemon.resetStatus(); - for (const move of pokemon.moveset) { - move!.ppUsed = 0; // TODO: is this bang correct? - } - pokemon.updateInfo(true); - } - const healSong = this.scene.playSoundWithoutBgm("heal"); - this.scene.time.delayedCall(Utils.fixedInt(healSong.totalDuration * 1000), () => { - healSong.destroy(); - if (this.resumeBgm && bgmPlaying) { - this.scene.playBgm(); - } - this.scene.ui.fadeIn(500).then(() => this.end()); - }); - }); - } -} - -export class ShinySparklePhase extends PokemonPhase { - constructor(scene: BattleScene, battlerIndex: BattlerIndex) { - super(scene, battlerIndex); - } - - start() { - super.start(); - - this.getPokemon().sparkle(); - this.scene.time.delayedCall(1000, () => this.end()); - } -} - -export class ScanIvsPhase extends PokemonPhase { - private shownIvs: integer; - - constructor(scene: BattleScene, battlerIndex: BattlerIndex, shownIvs: integer) { - super(scene, battlerIndex); - - this.shownIvs = shownIvs; - } - - start() { - super.start(); - - if (!this.shownIvs) { - return this.end(); - } - - const pokemon = this.getPokemon(); - - let enemyIvs: number[] = []; - let statsContainer: Phaser.GameObjects.Sprite[] = []; - let statsContainerLabels: Phaser.GameObjects.Sprite[] = []; - const enemyField = this.scene.getEnemyField(); - const uiTheme = (this.scene as BattleScene).uiTheme; // Assuming uiTheme is accessible - for (let e = 0; e < enemyField.length; e++) { - enemyIvs = enemyField[e].ivs; - const currentIvs = this.scene.gameData.dexData[enemyField[e].species.getRootSpeciesId()].ivs; // we are using getRootSpeciesId() here because we want to check against the baby form, not the mid form if it exists - const ivsToShow = this.scene.ui.getMessageHandler().getTopIvs(enemyIvs, this.shownIvs); - statsContainer = enemyField[e].getBattleInfo().getStatsValueContainer().list as Phaser.GameObjects.Sprite[]; - statsContainerLabels = statsContainer.filter(m => m.name.indexOf("icon_stat_label") >= 0); - for (let s = 0; s < statsContainerLabels.length; s++) { - const ivStat = Stat[statsContainerLabels[s].frame.name]; - if (enemyIvs[ivStat] > currentIvs[ivStat] && ivsToShow.indexOf(Number(ivStat)) >= 0) { - const hexColour = enemyIvs[ivStat] === 31 ? getTextColor(TextStyle.PERFECT_IV, false, uiTheme) : getTextColor(TextStyle.SUMMARY_GREEN, false, uiTheme); - const hexTextColour = Phaser.Display.Color.HexStringToColor(hexColour).color; - statsContainerLabels[s].setTint(hexTextColour); - } - statsContainerLabels[s].setVisible(true); - } - } - - if (!this.scene.hideIvs) { - this.scene.ui.showText(i18next.t("battle:ivScannerUseQuestion", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { - this.scene.ui.setMode(Mode.CONFIRM, () => { - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.clearText(); - new CommonBattleAnim(CommonAnim.LOCK_ON, pokemon, pokemon).play(this.scene, () => { - this.scene.ui.getMessageHandler().promptIvs(pokemon.id, pokemon.ivs, this.shownIvs).then(() => this.end()); - }); - }, () => { - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.clearText(); - this.end(); - }); - }); - } else { - this.end(); - } - } -} - -export class TrainerMessageTestPhase extends BattlePhase { - private trainerTypes: TrainerType[]; - - constructor(scene: BattleScene, ...trainerTypes: TrainerType[]) { - super(scene); - - this.trainerTypes = trainerTypes; - } - - start() { - super.start(); - - const testMessages: string[] = []; - - for (const t of Object.keys(trainerConfigs)) { - const type = parseInt(t); - if (this.trainerTypes.length && !this.trainerTypes.find(tt => tt === type as TrainerType)) { - continue; - } - const config = trainerConfigs[type]; - [config.encounterMessages, config.femaleEncounterMessages, config.victoryMessages, config.femaleVictoryMessages, config.defeatMessages, config.femaleDefeatMessages] - .map(messages => { - if (messages?.length) { - testMessages.push(...messages); - } - }); - } - - for (const message of testMessages) { - this.scene.pushPhase(new TestMessagePhase(this.scene, message)); - } - - this.end(); - } -} - -export class TestMessagePhase extends MessagePhase { - constructor(scene: BattleScene, message: string) { - super(scene, message, null, true); - } -} diff --git a/src/phases/add-enemy-buff-modifier-phase.ts b/src/phases/add-enemy-buff-modifier-phase.ts new file mode 100644 index 00000000000..a9936eb765d --- /dev/null +++ b/src/phases/add-enemy-buff-modifier-phase.ts @@ -0,0 +1,26 @@ +import BattleScene from "#app/battle-scene.js"; +import { ModifierTier } from "#app/modifier/modifier-tier.js"; +import { regenerateModifierPoolThresholds, ModifierPoolType, getEnemyBuffModifierForWave } from "#app/modifier/modifier-type.js"; +import { EnemyPersistentModifier } from "#app/modifier/modifier.js"; +import { Phase } from "#app/phase.js"; + +export class AddEnemyBuffModifierPhase extends Phase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + + const waveIndex = this.scene.currentBattle.waveIndex; + const tier = !(waveIndex % 1000) ? ModifierTier.ULTRA : !(waveIndex % 250) ? ModifierTier.GREAT : ModifierTier.COMMON; + + regenerateModifierPoolThresholds(this.scene.getEnemyParty(), ModifierPoolType.ENEMY_BUFF); + + const count = Math.ceil(waveIndex / 250); + for (let i = 0; i < count; i++) { + this.scene.addEnemyModifier(getEnemyBuffModifierForWave(tier, this.scene.findModifiers(m => m instanceof EnemyPersistentModifier, false), this.scene), true, true); + } + this.scene.updateModifiers(false, true).then(() => this.end()); + } +} diff --git a/src/phases/attempt-capture-phase.ts b/src/phases/attempt-capture-phase.ts new file mode 100644 index 00000000000..3c165a25157 --- /dev/null +++ b/src/phases/attempt-capture-phase.ts @@ -0,0 +1,288 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { getPokeballCatchMultiplier, getPokeballAtlasKey, getPokeballTintColor, doPokeballBounceAnim } from "#app/data/pokeball.js"; +import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect.js"; +import { PokeballType } from "#app/enums/pokeball.js"; +import { StatusEffect } from "#app/enums/status-effect.js"; +import { addPokeballOpenParticles, addPokeballCaptureStars } from "#app/field/anims.js"; +import { EnemyPokemon } from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { PokemonHeldItemModifier } from "#app/modifier/modifier.js"; +import { achvs } from "#app/system/achv.js"; +import { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler.js"; +import { SummaryUiMode } from "#app/ui/summary-ui-handler.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next from "i18next"; +import { PokemonPhase } from "./pokemon-phase"; +import { VictoryPhase } from "./victory-phase"; + +export class AttemptCapturePhase extends PokemonPhase { + private pokeballType: PokeballType; + private pokeball: Phaser.GameObjects.Sprite; + private originalY: number; + + constructor(scene: BattleScene, targetIndex: integer, pokeballType: PokeballType) { + super(scene, BattlerIndex.ENEMY + targetIndex); + + this.pokeballType = pokeballType; + } + + start() { + super.start(); + + const pokemon = this.getPokemon() as EnemyPokemon; + + if (!pokemon?.hp) { + return this.end(); + } + + this.scene.pokeballCounts[this.pokeballType]--; + + this.originalY = pokemon.y; + + const _3m = 3 * pokemon.getMaxHp(); + const _2h = 2 * pokemon.hp; + const catchRate = pokemon.species.catchRate; + const pokeballMultiplier = getPokeballCatchMultiplier(this.pokeballType); + const statusMultiplier = pokemon.status ? getStatusEffectCatchRateMultiplier(pokemon.status.effect) : 1; + const x = Math.round((((_3m - _2h) * catchRate * pokeballMultiplier) / _3m) * statusMultiplier); + const y = Math.round(65536 / Math.sqrt(Math.sqrt(255 / x))); + const fpOffset = pokemon.getFieldPositionOffset(); + + const pokeballAtlasKey = getPokeballAtlasKey(this.pokeballType); + this.pokeball = this.scene.addFieldSprite(16, 80, "pb", pokeballAtlasKey); + this.pokeball.setOrigin(0.5, 0.625); + this.scene.field.add(this.pokeball); + + this.scene.playSound("pb_throw"); + this.scene.time.delayedCall(300, () => { + this.scene.field.moveBelow(this.pokeball as Phaser.GameObjects.GameObject, pokemon); + }); + + this.scene.tweens.add({ + targets: this.pokeball, + x: { value: 236 + fpOffset[0], ease: "Linear" }, + y: { value: 16 + fpOffset[1], ease: "Cubic.easeOut" }, + duration: 500, + onComplete: () => { + this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`); + this.scene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}_open`)); + this.scene.playSound("pb_rel"); + pokemon.tint(getPokeballTintColor(this.pokeballType)); + + addPokeballOpenParticles(this.scene, this.pokeball.x, this.pokeball.y, this.pokeballType); + + this.scene.tweens.add({ + targets: pokemon, + duration: 500, + ease: "Sine.easeIn", + scale: 0.25, + y: 20, + onComplete: () => { + this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`); + pokemon.setVisible(false); + this.scene.playSound("pb_catch"); + this.scene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}`)); + + const doShake = () => { + let shakeCount = 0; + const pbX = this.pokeball.x; + const shakeCounter = this.scene.tweens.addCounter({ + from: 0, + to: 1, + repeat: 4, + yoyo: true, + ease: "Cubic.easeOut", + duration: 250, + repeatDelay: 500, + onUpdate: t => { + if (shakeCount && shakeCount < 4) { + const value = t.getValue(); + const directionMultiplier = shakeCount % 2 === 1 ? 1 : -1; + this.pokeball.setX(pbX + value * 4 * directionMultiplier); + this.pokeball.setAngle(value * 27.5 * directionMultiplier); + } + }, + onRepeat: () => { + if (!pokemon.species.isObtainable()) { + shakeCounter.stop(); + this.failCatch(shakeCount); + } else if (shakeCount++ < 3) { + if (pokeballMultiplier === -1 || pokemon.randSeedInt(65536) < y) { + this.scene.playSound("pb_move"); + } else { + shakeCounter.stop(); + this.failCatch(shakeCount); + } + } else { + this.scene.playSound("pb_lock"); + addPokeballCaptureStars(this.scene, this.pokeball); + + const pbTint = this.scene.add.sprite(this.pokeball.x, this.pokeball.y, "pb", "pb"); + pbTint.setOrigin(this.pokeball.originX, this.pokeball.originY); + pbTint.setTintFill(0); + pbTint.setAlpha(0); + this.scene.field.add(pbTint); + this.scene.tweens.add({ + targets: pbTint, + alpha: 0.375, + duration: 200, + easing: "Sine.easeOut", + onComplete: () => { + this.scene.tweens.add({ + targets: pbTint, + alpha: 0, + duration: 200, + easing: "Sine.easeIn", + onComplete: () => pbTint.destroy() + }); + } + }); + } + }, + onComplete: () => { + this.catch(); + } + }); + }; + + this.scene.time.delayedCall(250, () => doPokeballBounceAnim(this.scene, this.pokeball, 16, 72, 350, doShake)); + } + }); + } + }); + } + + failCatch(shakeCount: integer) { + const pokemon = this.getPokemon(); + + this.scene.playSound("pb_rel"); + pokemon.setY(this.originalY); + if (pokemon.status?.effect !== StatusEffect.SLEEP) { + pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 }); + } + pokemon.tint(getPokeballTintColor(this.pokeballType)); + pokemon.setVisible(true); + pokemon.untint(250, "Sine.easeOut"); + + const pokeballAtlasKey = getPokeballAtlasKey(this.pokeballType); + this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`); + this.scene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}_open`)); + + this.scene.tweens.add({ + targets: pokemon, + duration: 250, + ease: "Sine.easeOut", + scale: 1 + }); + + this.scene.currentBattle.lastUsedPokeball = this.pokeballType; + this.removePb(); + this.end(); + } + + catch() { + const pokemon = this.getPokemon() as EnemyPokemon; + + const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm(); + + if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1) { + this.scene.validateAchv(achvs.HIDDEN_ABILITY); + } + + if (pokemon.species.subLegendary) { + this.scene.validateAchv(achvs.CATCH_SUB_LEGENDARY); + } + + if (pokemon.species.legendary) { + this.scene.validateAchv(achvs.CATCH_LEGENDARY); + } + + if (pokemon.species.mythical) { + this.scene.validateAchv(achvs.CATCH_MYTHICAL); + } + + this.scene.pokemonInfoContainer.show(pokemon, true); + + this.scene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs); + + this.scene.ui.showText(i18next.t("battle:pokemonCaught", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { + const end = () => { + this.scene.unshiftPhase(new VictoryPhase(this.scene, this.battlerIndex)); + this.scene.pokemonInfoContainer.hide(); + this.removePb(); + this.end(); + }; + const removePokemon = () => { + this.scene.addFaintedEnemyScore(pokemon); + this.scene.getPlayerField().filter(p => p.isActive(true)).forEach(playerPokemon => playerPokemon.removeTagsBySourceId(pokemon.id)); + pokemon.hp = 0; + pokemon.trySetStatus(StatusEffect.FAINT); + this.scene.clearEnemyHeldItemModifiers(); + this.scene.field.remove(pokemon, true); + }; + const addToParty = () => { + const newPokemon = pokemon.addToParty(this.pokeballType); + const modifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier, false); + if (this.scene.getParty().filter(p => p.isShiny()).length === 6) { + this.scene.validateAchv(achvs.SHINY_PARTY); + } + Promise.all(modifiers.map(m => this.scene.addModifier(m, true))).then(() => { + this.scene.updateModifiers(true); + removePokemon(); + if (newPokemon) { + newPokemon.loadAssets().then(end); + } else { + end(); + } + }); + }; + Promise.all([pokemon.hideInfo(), this.scene.gameData.setPokemonCaught(pokemon)]).then(() => { + if (this.scene.getParty().length === 6) { + const promptRelease = () => { + this.scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => { + this.scene.pokemonInfoContainer.makeRoomForConfirmUi(1, true); + this.scene.ui.setMode(Mode.CONFIRM, () => { + const newPokemon = this.scene.addPlayerPokemon(pokemon.species, pokemon.level, pokemon.abilityIndex, pokemon.formIndex, pokemon.gender, pokemon.shiny, pokemon.variant, pokemon.ivs, pokemon.nature, pokemon); + this.scene.ui.setMode(Mode.SUMMARY, newPokemon, 0, SummaryUiMode.DEFAULT, () => { + this.scene.ui.setMode(Mode.MESSAGE).then(() => { + promptRelease(); + }); + }, false); + }, () => { + this.scene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, this.fieldIndex, (slotIndex: integer, _option: PartyOption) => { + this.scene.ui.setMode(Mode.MESSAGE).then(() => { + if (slotIndex < 6) { + addToParty(); + } else { + promptRelease(); + } + }); + }); + }, () => { + this.scene.ui.setMode(Mode.MESSAGE).then(() => { + removePokemon(); + end(); + }); + }, "fullParty"); + }); + }; + promptRelease(); + } else { + addToParty(); + } + }); + }, 0, true); + } + + removePb() { + this.scene.tweens.add({ + targets: this.pokeball, + duration: 250, + delay: 250, + ease: "Sine.easeIn", + alpha: 0, + onComplete: () => this.pokeball.destroy() + }); + } +} diff --git a/src/phases/attempt-run-phase.ts b/src/phases/attempt-run-phase.ts new file mode 100644 index 00000000000..17625c57fc6 --- /dev/null +++ b/src/phases/attempt-run-phase.ts @@ -0,0 +1,56 @@ +import BattleScene from "#app/battle-scene.js"; +import { applyAbAttrs, RunSuccessAbAttr } from "#app/data/ability.js"; +import { Stat } from "#app/enums/stat.js"; +import { StatusEffect } from "#app/enums/status-effect.js"; +import Pokemon from "#app/field/pokemon.js"; +import i18next from "i18next"; +import * as Utils from "#app/utils.js"; +import { BattleEndPhase } from "./battle-end-phase"; +import { NewBattlePhase } from "./new-battle-phase"; +import { PokemonPhase } from "./pokemon-phase"; + +export class AttemptRunPhase extends PokemonPhase { + constructor(scene: BattleScene, fieldIndex: integer) { + super(scene, fieldIndex); + } + + start() { + super.start(); + + const playerPokemon = this.getPokemon(); + const enemyField = this.scene.getEnemyField(); + + const enemySpeed = enemyField.reduce((total: integer, enemyPokemon: Pokemon) => total + enemyPokemon.getStat(Stat.SPD), 0) / enemyField.length; + + const escapeChance = new Utils.IntegerHolder((((playerPokemon.getStat(Stat.SPD) * 128) / enemySpeed) + (30 * this.scene.currentBattle.escapeAttempts++)) % 256); + applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance); + + if (playerPokemon.randSeedInt(256) < escapeChance.value) { + this.scene.playSound("flee"); + this.scene.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500); + + this.scene.tweens.add({ + targets: [this.scene.arenaEnemy, enemyField].flat(), + alpha: 0, + duration: 250, + ease: "Sine.easeIn", + onComplete: () => enemyField.forEach(enemyPokemon => enemyPokemon.destroy()) + }); + + this.scene.clearEnemyHeldItemModifiers(); + + enemyField.forEach(enemyPokemon => { + enemyPokemon.hideInfo().then(() => enemyPokemon.destroy()); + enemyPokemon.hp = 0; + enemyPokemon.trySetStatus(StatusEffect.FAINT); + }); + + this.scene.pushPhase(new BattleEndPhase(this.scene)); + this.scene.pushPhase(new NewBattlePhase(this.scene)); + } else { + this.scene.queueMessage(i18next.t("battle:runAwayCannotEscape"), null, true, 500); + } + + this.end(); + } +} diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts new file mode 100644 index 00000000000..a9999370cdd --- /dev/null +++ b/src/phases/battle-end-phase.ts @@ -0,0 +1,55 @@ +import { applyPostBattleAbAttrs, PostBattleAbAttr } from "#app/data/ability.js"; +import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier.js"; +import { BattlePhase } from "./battle-phase"; +import { GameOverPhase } from "./game-over-phase"; + +export class BattleEndPhase extends BattlePhase { + start() { + super.start(); + + this.scene.currentBattle.addBattleScore(this.scene); + + this.scene.gameData.gameStats.battles++; + if (this.scene.currentBattle.trainer) { + this.scene.gameData.gameStats.trainersDefeated++; + } + if (this.scene.gameMode.isEndless && this.scene.currentBattle.waveIndex + 1 > this.scene.gameData.gameStats.highestEndlessWave) { + this.scene.gameData.gameStats.highestEndlessWave = this.scene.currentBattle.waveIndex + 1; + } + + // Endless graceful end + if (this.scene.gameMode.isEndless && this.scene.currentBattle.waveIndex >= 5850) { + this.scene.clearPhaseQueue(); + this.scene.unshiftPhase(new GameOverPhase(this.scene, true)); + } + + for (const pokemon of this.scene.getField()) { + if (pokemon) { + pokemon.resetBattleSummonData(); + } + } + + for (const pokemon of this.scene.getParty().filter(p => p.isAllowedInBattle())) { + applyPostBattleAbAttrs(PostBattleAbAttr, pokemon); + } + + if (this.scene.currentBattle.moneyScattered) { + this.scene.currentBattle.pickUpScatteredMoney(this.scene); + } + + this.scene.clearEnemyHeldItemModifiers(); + + const lapsingModifiers = this.scene.findModifiers(m => m instanceof LapsingPersistentModifier || m instanceof LapsingPokemonHeldItemModifier) as (LapsingPersistentModifier | LapsingPokemonHeldItemModifier)[]; + for (const m of lapsingModifiers) { + const args: any[] = []; + if (m instanceof LapsingPokemonHeldItemModifier) { + args.push(this.scene.getPokemonById(m.pokemonId)); + } + if (!m.lapse(args)) { + this.scene.removeModifier(m); + } + } + + this.scene.updateModifiers().then(() => this.end()); + } +} diff --git a/src/phases/battle-phase.ts b/src/phases/battle-phase.ts new file mode 100644 index 00000000000..3e7e0e28596 --- /dev/null +++ b/src/phases/battle-phase.ts @@ -0,0 +1,47 @@ +import BattleScene from "#app/battle-scene.js"; +import { TrainerSlot } from "#app/data/trainer-config.js"; +import { Phase } from "#app/phase.js"; + +export class BattlePhase extends Phase { + constructor(scene: BattleScene) { + super(scene); + } + + showEnemyTrainer(trainerSlot: TrainerSlot = TrainerSlot.NONE): void { + const sprites = this.scene.currentBattle.trainer?.getSprites()!; // TODO: is this bang correct? + const tintSprites = this.scene.currentBattle.trainer?.getTintSprites()!; // TODO: is this bang correct? + for (let i = 0; i < sprites.length; i++) { + const visible = !trainerSlot || !i === (trainerSlot === TrainerSlot.TRAINER) || sprites.length < 2; + [sprites[i], tintSprites[i]].map(sprite => { + if (visible) { + sprite.x = trainerSlot || sprites.length < 2 ? 0 : i ? 16 : -16; + } + sprite.setVisible(visible); + sprite.clearTint(); + }); + sprites[i].setVisible(visible); + tintSprites[i].setVisible(visible); + sprites[i].clearTint(); + tintSprites[i].clearTint(); + } + this.scene.tweens.add({ + targets: this.scene.currentBattle.trainer, + x: "-=16", + y: "+=16", + alpha: 1, + ease: "Sine.easeInOut", + duration: 750 + }); + } + + hideEnemyTrainer(): void { + this.scene.tweens.add({ + targets: this.scene.currentBattle.trainer, + x: "+=16", + y: "-=16", + alpha: 0, + ease: "Sine.easeInOut", + duration: 750 + }); + } +} diff --git a/src/phases/berry-phase.ts b/src/phases/berry-phase.ts new file mode 100644 index 00000000000..504fb6ec163 --- /dev/null +++ b/src/phases/berry-phase.ts @@ -0,0 +1,52 @@ +import { applyAbAttrs, PreventBerryUseAbAttr, HealFromBerryUseAbAttr } from "#app/data/ability.js"; +import { CommonAnim } from "#app/data/battle-anims.js"; +import { BerryUsedEvent } from "#app/events/battle-scene.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { BerryModifier } from "#app/modifier/modifier.js"; +import i18next from "i18next"; +import * as Utils from "#app/utils.js"; +import { FieldPhase } from "./field-phase"; +import { CommonAnimPhase } from "./common-anim-phase"; + +/** The phase after attacks where the pokemon eat berries */ +export class BerryPhase extends FieldPhase { + start() { + super.start(); + + this.executeForAll((pokemon) => { + const hasUsableBerry = !!this.scene.findModifier((m) => { + return m instanceof BerryModifier && m.shouldApply([pokemon]); + }, pokemon.isPlayer()); + + if (hasUsableBerry) { + const cancelled = new Utils.BooleanHolder(false); + pokemon.getOpponents().map((opp) => applyAbAttrs(PreventBerryUseAbAttr, opp, cancelled)); + + if (cancelled.value) { + pokemon.scene.queueMessage(i18next.t("abilityTriggers:preventBerryUse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + } else { + this.scene.unshiftPhase( + new CommonAnimPhase(this.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM) + ); + + for (const berryModifier of this.scene.applyModifiers(BerryModifier, pokemon.isPlayer(), pokemon) as BerryModifier[]) { + if (berryModifier.consumed) { + if (!--berryModifier.stackCount) { + this.scene.removeModifier(berryModifier); + } else { + berryModifier.consumed = false; + } + } + this.scene.eventTarget.dispatchEvent(new BerryUsedEvent(berryModifier)); // Announce a berry was used + } + + this.scene.updateModifiers(pokemon.isPlayer()); + + applyAbAttrs(HealFromBerryUseAbAttr, pokemon, new Utils.BooleanHolder(false)); + } + } + }); + + this.end(); + } +} diff --git a/src/phases/check-switch-phase.ts b/src/phases/check-switch-phase.ts new file mode 100644 index 00000000000..cd8f2b00c46 --- /dev/null +++ b/src/phases/check-switch-phase.ts @@ -0,0 +1,61 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattleStyle } from "#app/enums/battle-style.js"; +import { BattlerTagType } from "#app/enums/battler-tag-type.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next from "i18next"; +import { BattlePhase } from "./battle-phase"; +import { PostSummonPhase } from "./post-summon-phase"; +import { SummonMissingPhase } from "./summon-missing-phase"; +import { SwitchPhase } from "./switch-phase"; + +export class CheckSwitchPhase extends BattlePhase { + protected fieldIndex: integer; + protected useName: boolean; + + constructor(scene: BattleScene, fieldIndex: integer, useName: boolean) { + super(scene); + + this.fieldIndex = fieldIndex; + this.useName = useName; + } + + start() { + super.start(); + + const pokemon = this.scene.getPlayerField()[this.fieldIndex]; + + if (this.scene.battleStyle === BattleStyle.SET) { + super.end(); + return; + } + + if (this.scene.field.getAll().indexOf(pokemon) === -1) { + this.scene.unshiftPhase(new SummonMissingPhase(this.scene, this.fieldIndex)); + super.end(); + return; + } + + if (!this.scene.getParty().slice(1).filter(p => p.isActive()).length) { + super.end(); + return; + } + + if (pokemon.getTag(BattlerTagType.FRENZY)) { + super.end(); + return; + } + + this.scene.ui.showText(i18next.t("battle:switchQuestion", { pokemonName: this.useName ? getPokemonNameWithAffix(pokemon) : i18next.t("battle:pokemon") }), null, () => { + this.scene.ui.setMode(Mode.CONFIRM, () => { + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.tryRemovePhase(p => p instanceof PostSummonPhase && p.player && p.fieldIndex === this.fieldIndex); + this.scene.unshiftPhase(new SwitchPhase(this.scene, this.fieldIndex, false, true)); + this.end(); + }, () => { + this.scene.ui.setMode(Mode.MESSAGE); + this.end(); + }); + }); + } +} diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts new file mode 100644 index 00000000000..68ede826d95 --- /dev/null +++ b/src/phases/command-phase.ts @@ -0,0 +1,288 @@ +import BattleScene from "#app/battle-scene.js"; +import { TurnCommand, BattleType } from "#app/battle.js"; +import { applyCheckTrappedAbAttrs, CheckTrappedAbAttr } from "#app/data/ability.js"; +import { TrappedTag, EncoreTag } from "#app/data/battler-tags.js"; +import { MoveTargetSet, getMoveTargets } from "#app/data/move.js"; +import { speciesStarters } from "#app/data/pokemon-species.js"; +import { Type } from "#app/data/type.js"; +import { Abilities } from "#app/enums/abilities.js"; +import { BattlerTagType } from "#app/enums/battler-tag-type.js"; +import { Biome } from "#app/enums/biome.js"; +import { Moves } from "#app/enums/moves.js"; +import { PokeballType } from "#app/enums/pokeball.js"; +import { FieldPosition, PlayerPokemon } from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { Command } from "#app/ui/command-ui-handler.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next from "i18next"; +import * as Utils from "#app/utils.js"; +import { FieldPhase } from "./field-phase"; +import { SelectTargetPhase } from "./select-target-phase"; + +export class CommandPhase extends FieldPhase { + protected fieldIndex: integer; + + constructor(scene: BattleScene, fieldIndex: integer) { + super(scene); + + this.fieldIndex = fieldIndex; + } + + start() { + super.start(); + + if (this.fieldIndex) { + // If we somehow are attempting to check the right pokemon but there's only one pokemon out + // Switch back to the center pokemon. This can happen rarely in double battles with mid turn switching + if (this.scene.getPlayerField().filter(p => p.isActive()).length === 1) { + this.fieldIndex = FieldPosition.CENTER; + } else { + const allyCommand = this.scene.currentBattle.turnCommands[this.fieldIndex - 1]; + if (allyCommand?.command === Command.BALL || allyCommand?.command === Command.RUN) { + this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: allyCommand?.command, skip: true }; + } + } + } + + if (this.scene.currentBattle.turnCommands[this.fieldIndex]?.skip) { + return this.end(); + } + + const playerPokemon = this.scene.getPlayerField()[this.fieldIndex]; + + const moveQueue = playerPokemon.getMoveQueue(); + + while (moveQueue.length && moveQueue[0] + && moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move) + || !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move)]!.isUsable(playerPokemon, moveQueue[0].ignorePP))) { // TODO: is the bang correct? + moveQueue.shift(); + } + + if (moveQueue.length) { + const queuedMove = moveQueue[0]; + if (!queuedMove.move) { + this.handleCommand(Command.FIGHT, -1, false); + } else { + const moveIndex = playerPokemon.getMoveset().findIndex(m => m?.moveId === queuedMove.move); + if (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex]!.isUsable(playerPokemon, queuedMove.ignorePP)) { // TODO: is the bang correct? + this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, { targets: queuedMove.targets, multiple: queuedMove.targets.length > 1 }); + } else { + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + } + } + } else { + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + } + } + + handleCommand(command: Command, cursor: integer, ...args: any[]): boolean { + const playerPokemon = this.scene.getPlayerField()[this.fieldIndex]; + const enemyField = this.scene.getEnemyField(); + let success: boolean; + + switch (command) { + case Command.FIGHT: + let useStruggle = false; + if (cursor === -1 || + playerPokemon.trySelectMove(cursor, args[0] as boolean) || + (useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m?.isUsable(playerPokemon)).length)) { + const moveId = !useStruggle ? cursor > -1 ? playerPokemon.getMoveset()[cursor]!.moveId : Moves.NONE : Moves.STRUGGLE; // TODO: is the bang correct? + const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: { move: moveId, targets: [], ignorePP: args[0] }, args: args }; + const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, moveId) : args[2]; + if (!moveId) { + turnCommand.targets = [this.fieldIndex]; + } + console.log(moveTargets, getPokemonNameWithAffix(playerPokemon)); + if (moveTargets.targets.length > 1 && moveTargets.multiple) { + this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex)); + } + if (moveTargets.targets.length <= 1 || moveTargets.multiple) { + turnCommand.move!.targets = moveTargets.targets; //TODO: is the bang correct here? + } else if (playerPokemon.getTag(BattlerTagType.CHARGING) && playerPokemon.getMoveQueue().length >= 1) { + turnCommand.move!.targets = playerPokemon.getMoveQueue()[0].targets; //TODO: is the bang correct here? + } else { + this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex)); + } + this.scene.currentBattle.turnCommands[this.fieldIndex] = turnCommand; + success = true; + } else if (cursor < playerPokemon.getMoveset().length) { + const move = playerPokemon.getMoveset()[cursor]!; //TODO: is this bang correct? + this.scene.ui.setMode(Mode.MESSAGE); + + // Decides between a Disabled, Not Implemented, or No PP translation message + const errorMessage = + playerPokemon.summonData.disabledMove === move.moveId ? "battle:moveDisabled" : + move.getName().endsWith(" (N)") ? "battle:moveNotImplemented" : "battle:moveNoPP"; + const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator + + this.scene.ui.showText(i18next.t(errorMessage, { moveName: moveName }), null, () => { + this.scene.ui.clearText(); + this.scene.ui.setMode(Mode.FIGHT, this.fieldIndex); + }, null, true); + } + break; + case Command.BALL: + const notInDex = (this.scene.getEnemyField().filter(p => p.isActive(true)).some(p => !p.scene.gameData.dexData[p.species.speciesId].caughtAttr) && this.scene.gameData.getStarterCount(d => !!d.caughtAttr) < Object.keys(speciesStarters).length - 1); + if (this.scene.arena.biomeType === Biome.END && (!this.scene.gameMode.isClassic || this.scene.gameMode.isFreshStartChallenge() || notInDex )) { + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.showText(i18next.t("battle:noPokeballForce"), null, () => { + this.scene.ui.showText("", 0); + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + }, null, true); + } else if (this.scene.currentBattle.battleType === BattleType.TRAINER) { + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.showText(i18next.t("battle:noPokeballTrainer"), null, () => { + this.scene.ui.showText("", 0); + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + }, null, true); + } else { + const targets = this.scene.getEnemyField().filter(p => p.isActive(true)).map(p => p.getBattlerIndex()); + if (targets.length > 1) { + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.showText(i18next.t("battle:noPokeballMulti"), null, () => { + this.scene.ui.showText("", 0); + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + }, null, true); + } else if (cursor < 5) { + const targetPokemon = this.scene.getEnemyField().find(p => p.isActive(true)); + if (targetPokemon?.isBoss() && targetPokemon?.bossSegmentIndex >= 1 && !targetPokemon?.hasAbility(Abilities.WONDER_GUARD, false, true) && cursor < PokeballType.MASTER_BALL) { + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.showText(i18next.t("battle:noPokeballStrong"), null, () => { + this.scene.ui.showText("", 0); + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + }, null, true); + } else { + this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor }; + this.scene.currentBattle.turnCommands[this.fieldIndex]!.targets = targets; + if (this.fieldIndex) { + this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; + } + success = true; + } + } + } + break; + case Command.POKEMON: + case Command.RUN: + const isSwitch = command === Command.POKEMON; + if (!isSwitch && this.scene.arena.biomeType === Biome.END) { + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.showText(i18next.t("battle:noEscapeForce"), null, () => { + this.scene.ui.showText("", 0); + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + }, null, true); + } else if (!isSwitch && this.scene.currentBattle.battleType === BattleType.TRAINER) { + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.showText(i18next.t("battle:noEscapeTrainer"), null, () => { + this.scene.ui.showText("", 0); + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + }, null, true); + } else { + const trapTag = playerPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag; + const trapped = new Utils.BooleanHolder(false); + const batonPass = isSwitch && args[0] as boolean; + const trappedAbMessages: string[] = []; + if (!batonPass) { + enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped, playerPokemon, trappedAbMessages, true)); + } + if (batonPass || (!trapTag && !trapped.value)) { + this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch + ? { command: Command.POKEMON, cursor: cursor, args: args } + : { command: Command.RUN }; + success = true; + if (!isSwitch && this.fieldIndex) { + this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; + } + } else if (trapTag) { + if (trapTag.sourceMove === Moves.INGRAIN && trapTag.sourceId && this.scene.getPokemonById(trapTag.sourceId)?.isOfType(Type.GHOST)) { + success = true; + this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch + ? { command: Command.POKEMON, cursor: cursor, args: args } + : { command: Command.RUN }; + break; + } + if (!isSwitch) { + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + this.scene.ui.setMode(Mode.MESSAGE); + } + this.scene.ui.showText( + i18next.t("battle:noEscapePokemon", { + pokemonName: trapTag.sourceId && this.scene.getPokemonById(trapTag.sourceId) ? getPokemonNameWithAffix(this.scene.getPokemonById(trapTag.sourceId)!) : "", + moveName: trapTag.getMoveName(), + escapeVerb: isSwitch ? i18next.t("battle:escapeVerbSwitch") : i18next.t("battle:escapeVerbFlee") + }), + null, + () => { + this.scene.ui.showText("", 0); + if (!isSwitch) { + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + } + }, null, true); + } else if (trapped.value && trappedAbMessages.length > 0) { + if (!isSwitch) { + this.scene.ui.setMode(Mode.MESSAGE); + } + this.scene.ui.showText(trappedAbMessages[0], null, () => { + this.scene.ui.showText("", 0); + if (!isSwitch) { + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + } + }, null, true); + } + } + break; + } + + if (success!) { // TODO: is the bang correct? + this.end(); + } + + return success!; // TODO: is the bang correct? + } + + cancel() { + if (this.fieldIndex) { + this.scene.unshiftPhase(new CommandPhase(this.scene, 0)); + this.scene.unshiftPhase(new CommandPhase(this.scene, 1)); + this.end(); + } + } + + checkFightOverride(): boolean { + const pokemon = this.getPokemon(); + + const encoreTag = pokemon.getTag(EncoreTag) as EncoreTag; + + if (!encoreTag) { + return false; + } + + const moveIndex = pokemon.getMoveset().findIndex(m => m?.moveId === encoreTag.moveId); + + if (moveIndex === -1 || !pokemon.getMoveset()[moveIndex]!.isUsable(pokemon)) { // TODO: is this bang correct? + return false; + } + + this.handleCommand(Command.FIGHT, moveIndex, false); + + return true; + } + + getFieldIndex(): integer { + return this.fieldIndex; + } + + getPokemon(): PlayerPokemon { + return this.scene.getPlayerField()[this.fieldIndex]; + } + + end() { + this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); + } +} diff --git a/src/phases/common-anim-phase.ts b/src/phases/common-anim-phase.ts new file mode 100644 index 00000000000..d3663abe3b6 --- /dev/null +++ b/src/phases/common-anim-phase.ts @@ -0,0 +1,26 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims.js"; +import { PokemonPhase } from "./pokemon-phase"; + +export class CommonAnimPhase extends PokemonPhase { + private anim: CommonAnim | null; + private targetIndex: integer | undefined; + + constructor(scene: BattleScene, battlerIndex?: BattlerIndex, targetIndex?: BattlerIndex | undefined, anim?: CommonAnim) { + super(scene, battlerIndex); + + this.anim = anim!; // TODO: is this bang correct? + this.targetIndex = targetIndex; + } + + setAnimation(anim: CommonAnim) { + this.anim = anim; + } + + start() { + new CommonBattleAnim(this.anim, this.getPokemon(), this.targetIndex !== undefined ? (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField())[this.targetIndex] : this.getPokemon()).play(this.scene, () => { + this.end(); + }); + } +} diff --git a/src/phases/damage-phase.ts b/src/phases/damage-phase.ts new file mode 100644 index 00000000000..9f63ce35cf2 --- /dev/null +++ b/src/phases/damage-phase.ts @@ -0,0 +1,84 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { BattleSpec } from "#app/enums/battle-spec.js"; +import { DamageResult, HitResult } from "#app/field/pokemon.js"; +import * as Utils from "#app/utils.js"; +import { PokemonPhase } from "./pokemon-phase"; + +export class DamagePhase extends PokemonPhase { + private amount: integer; + private damageResult: DamageResult; + private critical: boolean; + + constructor(scene: BattleScene, battlerIndex: BattlerIndex, amount: integer, damageResult?: DamageResult, critical: boolean = false) { + super(scene, battlerIndex); + + this.amount = amount; + this.damageResult = damageResult || HitResult.EFFECTIVE; + this.critical = critical; + } + + start() { + super.start(); + + if (this.damageResult === HitResult.ONE_HIT_KO) { + if (this.scene.moveAnimations) { + this.scene.toggleInvert(true); + } + this.scene.time.delayedCall(Utils.fixedInt(1000), () => { + this.scene.toggleInvert(false); + this.applyDamage(); + }); + return; + } + + this.applyDamage(); + } + + updateAmount(amount: integer): void { + this.amount = amount; + } + + applyDamage() { + switch (this.damageResult) { + case HitResult.EFFECTIVE: + this.scene.playSound("hit"); + break; + case HitResult.SUPER_EFFECTIVE: + case HitResult.ONE_HIT_KO: + this.scene.playSound("hit_strong"); + break; + case HitResult.NOT_VERY_EFFECTIVE: + this.scene.playSound("hit_weak"); + break; + } + + if (this.amount) { + this.scene.damageNumberHandler.add(this.getPokemon(), this.amount, this.damageResult, this.critical); + } + + if (this.damageResult !== HitResult.OTHER) { + const flashTimer = this.scene.time.addEvent({ + delay: 100, + repeat: 5, + startAt: 200, + callback: () => { + this.getPokemon().getSprite().setVisible(flashTimer.repeatCount % 2 === 0); + if (!flashTimer.repeatCount) { + this.getPokemon().updateInfo().then(() => this.end()); + } + } + }); + } else { + this.getPokemon().updateInfo().then(() => this.end()); + } + } + + override end() { + if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { + this.scene.initFinalBossPhaseTwo(this.getPokemon()); + } else { + super.end(); + } + } +} diff --git a/src/egg-hatch-phase.ts b/src/phases/egg-hatch-phase.ts similarity index 95% rename from src/egg-hatch-phase.ts rename to src/phases/egg-hatch-phase.ts index 73c88cbde37..6f3f0b37905 100644 --- a/src/egg-hatch-phase.ts +++ b/src/phases/egg-hatch-phase.ts @@ -1,18 +1,17 @@ -import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; +import BattleScene, { AnySound } from "#app/battle-scene.js"; +import { Egg, EGG_SEED } from "#app/data/egg.js"; +import { EggCountChangedEvent } from "#app/events/egg.js"; +import { PlayerPokemon } from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { Phase } from "#app/phase.js"; +import { achvs } from "#app/system/achv.js"; +import EggCounterContainer from "#app/ui/egg-counter-container.js"; +import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler.js"; +import PokemonInfoContainer from "#app/ui/pokemon-info-container.js"; +import { Mode } from "#app/ui/ui.js"; import i18next from "i18next"; -import { Phase } from "./phase"; -import BattleScene, { AnySound } from "./battle-scene"; -import * as Utils from "./utils"; -import { Mode } from "./ui/ui"; -import { EGG_SEED, Egg } from "./data/egg"; -import EggHatchSceneHandler from "./ui/egg-hatch-scene-handler"; -import { PlayerPokemon } from "./field/pokemon"; -import { achvs } from "./system/achv"; -import PokemonInfoContainer from "./ui/pokemon-info-container"; -import EggCounterContainer from "./ui/egg-counter-container"; -import { EggCountChangedEvent } from "./events/egg"; -import { getPokemonNameWithAffix } from "./messages"; - +import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; +import * as Utils from "#app/utils.js"; /** * Class that represents egg hatching */ diff --git a/src/phases/egg-lapse-phase.ts b/src/phases/egg-lapse-phase.ts new file mode 100644 index 00000000000..50d7106f229 --- /dev/null +++ b/src/phases/egg-lapse-phase.ts @@ -0,0 +1,35 @@ +import BattleScene from "#app/battle-scene.js"; +import { Egg } from "#app/data/egg.js"; +import { Phase } from "#app/phase.js"; +import i18next from "i18next"; +import Overrides from "#app/overrides"; +import { EggHatchPhase } from "./egg-hatch-phase"; + +export class EggLapsePhase extends Phase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + + const eggsToHatch: Egg[] = this.scene.gameData.eggs.filter((egg: Egg) => { + return Overrides.EGG_IMMEDIATE_HATCH_OVERRIDE ? true : --egg.hatchWaves < 1; + }); + + let eggCount: integer = eggsToHatch.length; + + if (eggCount) { + this.scene.queueMessage(i18next.t("battle:eggHatching")); + + for (const egg of eggsToHatch) { + this.scene.unshiftPhase(new EggHatchPhase(this.scene, egg, eggCount)); + if (eggCount > 0) { + eggCount--; + } + } + + } + this.end(); + } +} diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts new file mode 100644 index 00000000000..dfa198c8339 --- /dev/null +++ b/src/phases/encounter-phase.ts @@ -0,0 +1,379 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattleType, BattlerIndex } from "#app/battle.js"; +import { applyAbAttrs, SyncEncounterNatureAbAttr } from "#app/data/ability.js"; +import { getCharVariantFromDialogue } from "#app/data/dialogue.js"; +import { TrainerSlot } from "#app/data/trainer-config.js"; +import { getRandomWeatherType } from "#app/data/weather.js"; +import { BattleSpec } from "#app/enums/battle-spec.js"; +import { PlayerGender } from "#app/enums/player-gender.js"; +import { Species } from "#app/enums/species.js"; +import { EncounterPhaseEvent } from "#app/events/battle-scene.js"; +import Pokemon, { FieldPosition } from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { regenerateModifierPoolThresholds, ModifierPoolType } from "#app/modifier/modifier-type.js"; +import { IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier.js"; +import { achvs } from "#app/system/achv.js"; +import { handleTutorial, Tutorial } from "#app/tutorial.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next from "i18next"; +import { BattlePhase } from "./battle-phase"; +import * as Utils from "#app/utils.js"; +import { CheckSwitchPhase } from "./check-switch-phase"; +import { GameOverPhase } from "./game-over-phase"; +import { PostSummonPhase } from "./post-summon-phase"; +import { ReturnPhase } from "./return-phase"; +import { ScanIvsPhase } from "./scan-ivs-phase"; +import { ShinySparklePhase } from "./shiny-sparkle-phase"; +import { SummonPhase } from "./summon-phase"; +import { ToggleDoublePositionPhase } from "./toggle-double-position-phase"; + +export class EncounterPhase extends BattlePhase { + private loaded: boolean; + + constructor(scene: BattleScene, loaded?: boolean) { + super(scene); + + this.loaded = !!loaded; + } + + start() { + super.start(); + + this.scene.updateGameInfo(); + + this.scene.initSession(); + + this.scene.eventTarget.dispatchEvent(new EncounterPhaseEvent()); + + // Failsafe if players somehow skip floor 200 in classic mode + if (this.scene.gameMode.isClassic && this.scene.currentBattle.waveIndex > 200) { + this.scene.unshiftPhase(new GameOverPhase(this.scene)); + } + + const loadEnemyAssets: Promise[] = []; + + const battle = this.scene.currentBattle; + + let totalBst = 0; + + battle.enemyLevels?.forEach((level, e) => { + if (!this.loaded) { + if (battle.battleType === BattleType.TRAINER) { + battle.enemyParty[e] = battle.trainer?.genPartyMember(e)!; // TODO:: is the bang correct here? + } else { + const enemySpecies = this.scene.randomSpecies(battle.waveIndex, level, true); + battle.enemyParty[e] = this.scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, !!this.scene.getEncounterBossSegments(battle.waveIndex, level, enemySpecies)); + if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { + battle.enemyParty[e].ivs = new Array(6).fill(31); + } + this.scene.getParty().slice(0, !battle.double ? 1 : 2).reverse().forEach(playerPokemon => { + applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, false, battle.enemyParty[e]); + }); + } + } + const enemyPokemon = this.scene.getEnemyParty()[e]; + if (e < (battle.double ? 2 : 1)) { + enemyPokemon.setX(-66 + enemyPokemon.getFieldPositionOffset()[0]); + enemyPokemon.resetSummonData(); + } + + if (!this.loaded) { + this.scene.gameData.setPokemonSeen(enemyPokemon, true, battle.battleType === BattleType.TRAINER); + } + + if (enemyPokemon.species.speciesId === Species.ETERNATUS) { + if (this.scene.gameMode.isClassic && (battle.battleSpec === BattleSpec.FINAL_BOSS || this.scene.gameMode.isWaveFinal(battle.waveIndex))) { + if (battle.battleSpec !== BattleSpec.FINAL_BOSS) { + enemyPokemon.formIndex = 1; + enemyPokemon.updateScale(); + } + enemyPokemon.setBoss(); + } else if (!(battle.waveIndex % 1000)) { + enemyPokemon.formIndex = 1; + enemyPokemon.updateScale(); + const bossMBH = this.scene.findModifier(m => m instanceof TurnHeldItemTransferModifier && m.pokemonId === enemyPokemon.id, false) as TurnHeldItemTransferModifier; + this.scene.removeModifier(bossMBH!); + bossMBH?.setTransferrableFalse(); + this.scene.addEnemyModifier(bossMBH!); + } + } + + totalBst += enemyPokemon.getSpeciesForm().baseTotal; + + loadEnemyAssets.push(enemyPokemon.loadAssets()); + + console.log(getPokemonNameWithAffix(enemyPokemon), enemyPokemon.species.speciesId, enemyPokemon.stats); + }); + + if (this.scene.getParty().filter(p => p.isShiny()).length === 6) { + this.scene.validateAchv(achvs.SHINY_PARTY); + } + + if (battle.battleType === BattleType.TRAINER) { + loadEnemyAssets.push(battle.trainer?.loadAssets().then(() => battle.trainer?.initSprite())!); // TODO: is this bang correct? + } else { + // This block only applies for double battles to init the boss segments (idk why it's split up like this) + if (battle.enemyParty.filter(p => p.isBoss()).length > 1) { + for (const enemyPokemon of battle.enemyParty) { + // If the enemy pokemon is a boss and wasn't populated from data source, then set it up + if (enemyPokemon.isBoss() && !enemyPokemon.isPopulatedFromDataSource) { + enemyPokemon.setBoss(true, Math.ceil(enemyPokemon.bossSegments * (enemyPokemon.getSpeciesForm().baseTotal / totalBst))); + enemyPokemon.initBattleInfo(); + } + } + } + } + + Promise.all(loadEnemyAssets).then(() => { + battle.enemyParty.forEach((enemyPokemon, e) => { + if (e < (battle.double ? 2 : 1)) { + if (battle.battleType === BattleType.WILD) { + this.scene.field.add(enemyPokemon); + battle.seenEnemyPartyMemberIds.add(enemyPokemon.id); + const playerPokemon = this.scene.getPlayerPokemon(); + if (playerPokemon?.visible) { + this.scene.field.moveBelow(enemyPokemon as Pokemon, playerPokemon); + } + enemyPokemon.tint(0, 0.5); + } else if (battle.battleType === BattleType.TRAINER) { + enemyPokemon.setVisible(false); + this.scene.currentBattle.trainer?.tint(0, 0.5); + } + if (battle.double) { + enemyPokemon.setFieldPosition(e ? FieldPosition.RIGHT : FieldPosition.LEFT); + } + } + }); + + if (!this.loaded) { + regenerateModifierPoolThresholds(this.scene.getEnemyField(), battle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD); + this.scene.generateEnemyModifiers(); + } + + this.scene.ui.setMode(Mode.MESSAGE).then(() => { + if (!this.loaded) { + //@ts-ignore + this.scene.gameData.saveAll(this.scene, true, battle.waveIndex % 10 === 1 || this.scene.lastSavePlayTime >= 300).then(success => { // TODO: get rid of ts-ignore + this.scene.disableMenu = false; + if (!success) { + return this.scene.reset(true); + } + this.doEncounter(); + }); + } else { + this.doEncounter(); + } + }); + }); + } + + doEncounter() { + this.scene.playBgm(undefined, true); + this.scene.updateModifiers(false); + this.scene.setFieldScale(1); + + /*if (startingWave > 10) { + for (let m = 0; m < Math.min(Math.floor(startingWave / 10), 99); m++) + this.scene.addModifier(getPlayerModifierTypeOptionsForWave((m + 1) * 10, 1, this.scene.getParty())[0].type.newModifier(), true); + this.scene.updateModifiers(true); + }*/ + + for (const pokemon of this.scene.getParty()) { + if (pokemon) { + pokemon.resetBattleData(); + } + } + + if (!this.loaded) { + this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena), false); + } + + const enemyField = this.scene.getEnemyField(); + this.scene.tweens.add({ + targets: [this.scene.arenaEnemy, this.scene.currentBattle.trainer, enemyField, this.scene.arenaPlayer, this.scene.trainer].flat(), + x: (_target, _key, value, fieldIndex: integer) => fieldIndex < 2 + (enemyField.length) ? value + 300 : value - 300, + duration: 2000, + onComplete: () => { + if (!this.tryOverrideForBattleSpec()) { + this.doEncounterCommon(); + } + } + }); + } + + getEncounterMessage(): string { + const enemyField = this.scene.getEnemyField(); + + if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { + return i18next.t("battle:bossAppeared", { bossName: getPokemonNameWithAffix(enemyField[0])}); + } + + if (this.scene.currentBattle.battleType === BattleType.TRAINER) { + if (this.scene.currentBattle.double) { + return i18next.t("battle:trainerAppearedDouble", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }); + + } else { + return i18next.t("battle:trainerAppeared", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }); + } + } + + return enemyField.length === 1 + ? i18next.t("battle:singleWildAppeared", { pokemonName: enemyField[0].getNameToRender() }) + : i18next.t("battle:multiWildAppeared", { pokemonName1: enemyField[0].getNameToRender(), pokemonName2: enemyField[1].getNameToRender() }); + } + + doEncounterCommon(showEncounterMessage: boolean = true) { + const enemyField = this.scene.getEnemyField(); + + if (this.scene.currentBattle.battleType === BattleType.WILD) { + enemyField.forEach(enemyPokemon => { + enemyPokemon.untint(100, "Sine.easeOut"); + enemyPokemon.cry(); + enemyPokemon.showInfo(); + if (enemyPokemon.isShiny()) { + this.scene.validateAchv(achvs.SEE_SHINY); + } + }); + this.scene.updateFieldScale(); + if (showEncounterMessage) { + this.scene.ui.showText(this.getEncounterMessage(), null, () => this.end(), 1500); + } else { + this.end(); + } + } else if (this.scene.currentBattle.battleType === BattleType.TRAINER) { + const trainer = this.scene.currentBattle.trainer; + trainer?.untint(100, "Sine.easeOut"); + trainer?.playAnim(); + + const doSummon = () => { + this.scene.currentBattle.started = true; + this.scene.playBgm(undefined); + this.scene.pbTray.showPbTray(this.scene.getParty()); + this.scene.pbTrayEnemy.showPbTray(this.scene.getEnemyParty()); + const doTrainerSummon = () => { + this.hideEnemyTrainer(); + const availablePartyMembers = this.scene.getEnemyParty().filter(p => !p.isFainted()).length; + this.scene.unshiftPhase(new SummonPhase(this.scene, 0, false)); + if (this.scene.currentBattle.double && availablePartyMembers > 1) { + this.scene.unshiftPhase(new SummonPhase(this.scene, 1, false)); + } + this.end(); + }; + if (showEncounterMessage) { + this.scene.ui.showText(this.getEncounterMessage(), null, doTrainerSummon, 1500, true); + } else { + doTrainerSummon(); + } + }; + + const encounterMessages = this.scene.currentBattle.trainer?.getEncounterMessages(); + + if (!encounterMessages?.length) { + doSummon(); + } else { + let message: string; + this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex); + message = message!; // tell TS compiler it's defined now + const showDialogueAndSummon = () => { + this.scene.ui.showDialogue(message, trainer?.getName(TrainerSlot.NONE, true), null, () => { + this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doSummon())); + }); + }; + if (this.scene.currentBattle.trainer?.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { + this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(trainer?.getKey()!, getCharVariantFromDialogue(encounterMessages[0])).then(() => showDialogueAndSummon())); // TODO: is this bang correct? + } else { + showDialogueAndSummon(); + } + } + } + } + + end() { + const enemyField = this.scene.getEnemyField(); + + enemyField.forEach((enemyPokemon, e) => { + if (enemyPokemon.isShiny()) { + this.scene.unshiftPhase(new ShinySparklePhase(this.scene, BattlerIndex.ENEMY + e)); + } + }); + + if (this.scene.currentBattle.battleType !== BattleType.TRAINER) { + enemyField.map(p => this.scene.pushConditionalPhase(new PostSummonPhase(this.scene, p.getBattlerIndex()), () => { + // if there is not a player party, we can't continue + if (!this.scene.getParty()?.length) { + return false; + } + // how many player pokemon are on the field ? + const pokemonsOnFieldCount = this.scene.getParty().filter(p => p.isOnField()).length; + // if it's a 2vs1, there will never be a 2nd pokemon on our field even + const requiredPokemonsOnField = Math.min(this.scene.getParty().filter((p) => !p.isFainted()).length, 2); + // if it's a double, there should be 2, otherwise 1 + if (this.scene.currentBattle.double) { + return pokemonsOnFieldCount === requiredPokemonsOnField; + } + return pokemonsOnFieldCount === 1; + })); + const ivScannerModifier = this.scene.findModifier(m => m instanceof IvScannerModifier); + if (ivScannerModifier) { + enemyField.map(p => this.scene.pushPhase(new ScanIvsPhase(this.scene, p.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6)))); + } + } + + if (!this.loaded) { + const availablePartyMembers = this.scene.getParty().filter(p => p.isAllowedInBattle()); + + if (!availablePartyMembers[0].isOnField()) { + this.scene.pushPhase(new SummonPhase(this.scene, 0)); + } + + if (this.scene.currentBattle.double) { + if (availablePartyMembers.length > 1) { + this.scene.pushPhase(new ToggleDoublePositionPhase(this.scene, true)); + if (!availablePartyMembers[1].isOnField()) { + this.scene.pushPhase(new SummonPhase(this.scene, 1)); + } + } + } else { + if (availablePartyMembers.length > 1 && availablePartyMembers[1].isOnField()) { + this.scene.pushPhase(new ReturnPhase(this.scene, 1)); + } + this.scene.pushPhase(new ToggleDoublePositionPhase(this.scene, false)); + } + + if (this.scene.currentBattle.battleType !== BattleType.TRAINER && (this.scene.currentBattle.waveIndex > 1 || !this.scene.gameMode.isDaily)) { + const minPartySize = this.scene.currentBattle.double ? 2 : 1; + if (availablePartyMembers.length > minPartySize) { + this.scene.pushPhase(new CheckSwitchPhase(this.scene, 0, this.scene.currentBattle.double)); + if (this.scene.currentBattle.double) { + this.scene.pushPhase(new CheckSwitchPhase(this.scene, 1, this.scene.currentBattle.double)); + } + } + } + } + handleTutorial(this.scene, Tutorial.Access_Menu).then(() => super.end()); + } + + tryOverrideForBattleSpec(): boolean { + switch (this.scene.currentBattle.battleSpec) { + case BattleSpec.FINAL_BOSS: + const enemy = this.scene.getEnemyPokemon(); + this.scene.ui.showText(this.getEncounterMessage(), null, () => { + const count = 5643853 + this.scene.gameData.gameStats.classicSessionsPlayed; + //The two lines below check if English ordinals (1st, 2nd, 3rd, Xth) are used and determine which one to use. + //Otherwise, it defaults to an empty string. + //As of 08-07-24: Spanish and Italian default to the English translations + const ordinalUse = ["en", "es", "it"]; + const currentLanguage = i18next.resolvedLanguage ?? "en"; + const ordinalIndex = (ordinalUse.includes(currentLanguage)) ? ["st", "nd", "rd"][((count + 90) % 100 - 10) % 10 - 1] ?? "th" : ""; + const cycleCount = count.toLocaleString() + ordinalIndex; + const encounterDialogue = i18next.t(`${(this.scene.gameData.gender === PlayerGender.FEMALE) ? "PGF" : "PGM"}battleSpecDialogue:encounter`, {cycleCount: cycleCount}); + this.scene.ui.showDialogue(encounterDialogue, enemy?.species.name, null, () => { + this.doEncounterCommon(false); + }); + }, 1500, true); + return true; + } + + return false; + } +} diff --git a/src/phases/end-card-phase.ts b/src/phases/end-card-phase.ts new file mode 100644 index 00000000000..0b70664b993 --- /dev/null +++ b/src/phases/end-card-phase.ts @@ -0,0 +1,40 @@ +import BattleScene from "#app/battle-scene.js"; +import { PlayerGender } from "#app/enums/player-gender.js"; +import { Phase } from "#app/phase.js"; +import { addTextObject, TextStyle } from "#app/ui/text.js"; +import i18next from "i18next"; + +export class EndCardPhase extends Phase { + public endCard: Phaser.GameObjects.Image; + public text: Phaser.GameObjects.Text; + + constructor(scene: BattleScene) { + super(scene); + } + + start(): void { + super.start(); + + this.scene.ui.getMessageHandler().bg.setVisible(false); + this.scene.ui.getMessageHandler().nameBoxContainer.setVisible(false); + + this.endCard = this.scene.add.image(0, 0, `end_${this.scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}`); + this.endCard.setOrigin(0); + this.endCard.setScale(0.5); + this.scene.field.add(this.endCard); + + this.text = addTextObject(this.scene, this.scene.game.canvas.width / 12, (this.scene.game.canvas.height / 6) - 16, i18next.t("battle:congratulations"), TextStyle.SUMMARY, { fontSize: "128px" }); + this.text.setOrigin(0.5); + this.scene.field.add(this.text); + + this.scene.ui.clearText(); + + this.scene.ui.fadeIn(1000).then(() => { + + this.scene.ui.showText("", null, () => { + this.scene.ui.getMessageHandler().bg.setVisible(true); + this.end(); + }, null, true); + }); + } +} diff --git a/src/phases/end-evolution-phase.ts b/src/phases/end-evolution-phase.ts new file mode 100644 index 00000000000..2a6d492a425 --- /dev/null +++ b/src/phases/end-evolution-phase.ts @@ -0,0 +1,16 @@ +import BattleScene from "#app/battle-scene.js"; +import { Phase } from "#app/phase.js"; +import { Mode } from "#app/ui/ui.js"; + +export class EndEvolutionPhase extends Phase { + + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + + this.scene.ui.setModeForceTransition(Mode.MESSAGE).then(() => this.end()); + } +} diff --git a/src/phases/enemy-command-phase.ts b/src/phases/enemy-command-phase.ts new file mode 100644 index 00000000000..0b62fcbe813 --- /dev/null +++ b/src/phases/enemy-command-phase.ts @@ -0,0 +1,86 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { applyCheckTrappedAbAttrs, CheckTrappedAbAttr } from "#app/data/ability.js"; +import { TrappedTag } from "#app/data/battler-tags.js"; +import { Command } from "#app/ui/command-ui-handler.js"; +import * as Utils from "#app/utils.js"; +import { FieldPhase } from "./field-phase"; + +/** + * Phase for determining an enemy AI's action for the next turn. + * During this phase, the enemy decides whether to switch (if it has a trainer) + * or to use a move from its moveset. + * + * For more information on how the Enemy AI works, see docs/enemy-ai.md + * @see {@linkcode Pokemon.getMatchupScore} + * @see {@linkcode EnemyPokemon.getNextMove} + */ +export class EnemyCommandPhase extends FieldPhase { + protected fieldIndex: integer; + + constructor(scene: BattleScene, fieldIndex: integer) { + super(scene); + + this.fieldIndex = fieldIndex; + } + + start() { + super.start(); + + const enemyPokemon = this.scene.getEnemyField()[this.fieldIndex]; + + const battle = this.scene.currentBattle; + + const trainer = battle.trainer; + + /** + * If the enemy has a trainer, decide whether or not the enemy should switch + * to another member in its party. + * + * This block compares the active enemy Pokemon's {@linkcode Pokemon.getMatchupScore | matchup score} + * against the active player Pokemon with the enemy party's other non-fainted Pokemon. If a party + * member's matchup score is 3x the active enemy's score (or 2x for "boss" trainers), + * the enemy will switch to that Pokemon. + */ + if (trainer && !enemyPokemon.getMoveQueue().length) { + const opponents = enemyPokemon.getOpponents(); + + const trapTag = enemyPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag; + const trapped = new Utils.BooleanHolder(false); + opponents.forEach(playerPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, playerPokemon, trapped, enemyPokemon, [], true)); + if (!trapTag && !trapped.value) { + const partyMemberScores = trainer.getPartyMemberMatchupScores(enemyPokemon.trainerSlot, true); + + if (partyMemberScores.length) { + const matchupScores = opponents.map(opp => enemyPokemon.getMatchupScore(opp)); + const matchupScore = matchupScores.reduce((total, score) => total += score, 0) / matchupScores.length; + + const sortedPartyMemberScores = trainer.getSortedPartyMemberMatchupScores(partyMemberScores); + + const switchMultiplier = 1 - (battle.enemySwitchCounter ? Math.pow(0.1, (1 / battle.enemySwitchCounter)) : 0); + + if (sortedPartyMemberScores[0][1] * switchMultiplier >= matchupScore * (trainer.config.isBoss ? 2 : 3)) { + const index = trainer.getNextSummonIndex(enemyPokemon.trainerSlot, partyMemberScores); + + battle.turnCommands[this.fieldIndex + BattlerIndex.ENEMY] = + { command: Command.POKEMON, cursor: index, args: [false] }; + + battle.enemySwitchCounter++; + + return this.end(); + } + } + } + } + + /** Select a move to use (and a target to use it against, if applicable) */ + const nextMove = enemyPokemon.getNextMove(); + + this.scene.currentBattle.turnCommands[this.fieldIndex + BattlerIndex.ENEMY] = + { command: Command.FIGHT, move: nextMove }; + + this.scene.currentBattle.enemySwitchCounter = Math.max(this.scene.currentBattle.enemySwitchCounter - 1, 0); + + this.end(); + } +} diff --git a/src/phases/enemy-party-member-pokemon-phase.ts b/src/phases/enemy-party-member-pokemon-phase.ts new file mode 100644 index 00000000000..10af0913f93 --- /dev/null +++ b/src/phases/enemy-party-member-pokemon-phase.ts @@ -0,0 +1,13 @@ +import BattleScene from "#app/battle-scene.js"; +import { EnemyPokemon } from "#app/field/pokemon.js"; +import { PartyMemberPokemonPhase } from "./party-member-pokemon-phase"; + +export abstract class EnemyPartyMemberPokemonPhase extends PartyMemberPokemonPhase { + constructor(scene: BattleScene, partyMemberIndex: integer) { + super(scene, partyMemberIndex, false); + } + + getEnemyPokemon(): EnemyPokemon { + return super.getPokemon() as EnemyPokemon; + } +} diff --git a/src/evolution-phase.ts b/src/phases/evolution-phase.ts similarity index 96% rename from src/evolution-phase.ts rename to src/phases/evolution-phase.ts index 7b50a6368f6..398450ec693 100644 --- a/src/evolution-phase.ts +++ b/src/phases/evolution-phase.ts @@ -1,16 +1,17 @@ import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import { Phase } from "./phase"; -import BattleScene from "./battle-scene"; -import { SpeciesFormEvolution } from "./data/pokemon-evolutions"; -import EvolutionSceneHandler from "./ui/evolution-scene-handler"; -import * as Utils from "./utils"; -import { Mode } from "./ui/ui"; -import { LearnMovePhase } from "./phases"; -import { cos, sin } from "./field/anims"; -import { PlayerPokemon } from "./field/pokemon"; -import { getTypeRgb } from "./data/type"; +import { Phase } from "../phase"; +import BattleScene from "../battle-scene"; +import { SpeciesFormEvolution } from "../data/pokemon-evolutions"; +import EvolutionSceneHandler from "../ui/evolution-scene-handler"; +import * as Utils from "../utils"; +import { Mode } from "../ui/ui"; +import { cos, sin } from "../field/anims"; +import { PlayerPokemon } from "../field/pokemon"; +import { getTypeRgb } from "../data/type"; import i18next from "i18next"; -import { getPokemonNameWithAffix } from "./messages"; +import { getPokemonNameWithAffix } from "../messages"; +import { LearnMovePhase } from "./learn-move-phase"; +import { EndEvolutionPhase } from "./end-evolution-phase"; export class EvolutionPhase extends Phase { protected pokemon: PlayerPokemon; @@ -530,16 +531,3 @@ export class EvolutionPhase extends Phase { updateParticle(); } } - -export class EndEvolutionPhase extends Phase { - - constructor(scene: BattleScene) { - super(scene); - } - - start() { - super.start(); - - this.scene.ui.setModeForceTransition(Mode.MESSAGE).then(() => this.end()); - } -} diff --git a/src/phases/exp-phase.ts b/src/phases/exp-phase.ts new file mode 100644 index 00000000000..9c2ba95d550 --- /dev/null +++ b/src/phases/exp-phase.ts @@ -0,0 +1,35 @@ +import BattleScene from "#app/battle-scene.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { ExpBoosterModifier } from "#app/modifier/modifier.js"; +import i18next from "i18next"; +import * as Utils from "#app/utils.js"; +import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; +import { LevelUpPhase } from "./level-up-phase"; + +export class ExpPhase extends PlayerPartyMemberPokemonPhase { + private expValue: number; + + constructor(scene: BattleScene, partyMemberIndex: integer, expValue: number) { + super(scene, partyMemberIndex); + + this.expValue = expValue; + } + + start() { + super.start(); + + const pokemon = this.getPokemon(); + const exp = new Utils.NumberHolder(this.expValue); + this.scene.applyModifiers(ExpBoosterModifier, true, exp); + exp.value = Math.floor(exp.value); + this.scene.ui.showText(i18next.t("battle:expGain", { pokemonName: getPokemonNameWithAffix(pokemon), exp: exp.value }), null, () => { + const lastLevel = pokemon.level; + pokemon.addExp(exp.value); + const newLevel = pokemon.level; + if (newLevel > lastLevel) { + this.scene.unshiftPhase(new LevelUpPhase(this.scene, this.partyMemberIndex, lastLevel, newLevel)); + } + pokemon.updateInfo().then(() => this.end()); + }, null, true); + } +} diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts new file mode 100644 index 00000000000..14727f992d2 --- /dev/null +++ b/src/phases/faint-phase.ts @@ -0,0 +1,171 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex, BattleType } from "#app/battle.js"; +import { applyPostFaintAbAttrs, PostFaintAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr } from "#app/data/ability.js"; +import { BattlerTagLapseType } from "#app/data/battler-tags.js"; +import { battleSpecDialogue } from "#app/data/dialogue.js"; +import { allMoves, PostVictoryStatChangeAttr } from "#app/data/move.js"; +import { BattleSpec } from "#app/enums/battle-spec.js"; +import { StatusEffect } from "#app/enums/status-effect.js"; +import { PokemonMove, EnemyPokemon, PlayerPokemon, HitResult } from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { PokemonInstantReviveModifier } from "#app/modifier/modifier.js"; +import i18next from "i18next"; +import { DamagePhase } from "./damage-phase"; +import { PokemonPhase } from "./pokemon-phase"; +import { SwitchSummonPhase } from "./switch-summon-phase"; +import { ToggleDoublePositionPhase } from "./toggle-double-position-phase"; +import { GameOverPhase } from "./game-over-phase"; +import { SwitchPhase } from "./switch-phase"; +import { VictoryPhase } from "./victory-phase"; + +export class FaintPhase extends PokemonPhase { + private preventEndure: boolean; + + constructor(scene: BattleScene, battlerIndex: BattlerIndex, preventEndure?: boolean) { + super(scene, battlerIndex); + + this.preventEndure = preventEndure!; // TODO: is this bang correct? + } + + start() { + super.start(); + + if (!this.preventEndure) { + const instantReviveModifier = this.scene.applyModifier(PokemonInstantReviveModifier, this.player, this.getPokemon()) as PokemonInstantReviveModifier; + + if (instantReviveModifier) { + if (!--instantReviveModifier.stackCount) { + this.scene.removeModifier(instantReviveModifier); + } + this.scene.updateModifiers(this.player); + return this.end(); + } + } + + if (!this.tryOverrideForBattleSpec()) { + this.doFaint(); + } + } + + doFaint(): void { + const pokemon = this.getPokemon(); + + + // Track total times pokemon have been KO'd for supreme overlord/last respects + if (pokemon.isPlayer()) { + this.scene.currentBattle.playerFaints += 1; + } else { + this.scene.currentBattle.enemyFaints += 1; + } + + this.scene.queueMessage(i18next.t("battle:fainted", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), null, true); + + if (pokemon.turnData?.attacksReceived?.length) { + const lastAttack = pokemon.turnData.attacksReceived[0]; + applyPostFaintAbAttrs(PostFaintAbAttr, pokemon, this.scene.getPokemonById(lastAttack.sourceId)!, new PokemonMove(lastAttack.move).getMove(), lastAttack.result); // TODO: is this bang correct? + } + + const alivePlayField = this.scene.getField(true); + alivePlayField.forEach(p => applyPostKnockOutAbAttrs(PostKnockOutAbAttr, p, pokemon)); + if (pokemon.turnData?.attacksReceived?.length) { + const defeatSource = this.scene.getPokemonById(pokemon.turnData.attacksReceived[0].sourceId); + if (defeatSource?.isOnField()) { + applyPostVictoryAbAttrs(PostVictoryAbAttr, defeatSource); + const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move]; + const pvattrs = pvmove.getAttrs(PostVictoryStatChangeAttr); + if (pvattrs.length) { + for (const pvattr of pvattrs) { + pvattr.applyPostVictory(defeatSource, defeatSource, pvmove); + } + } + } + } + + if (this.player) { + /** The total number of Pokemon in the player's party that can legally fight */ + const legalPlayerPokemon = this.scene.getParty().filter(p => p.isAllowedInBattle()); + /** The total number of legal player Pokemon that aren't currently on the field */ + const legalPlayerPartyPokemon = legalPlayerPokemon.filter(p => !p.isActive(true)); + if (!legalPlayerPokemon.length) { + /** If the player doesn't have any legal Pokemon, end the game */ + this.scene.unshiftPhase(new GameOverPhase(this.scene)); + } else if (this.scene.currentBattle.double && legalPlayerPokemon.length === 1 && legalPlayerPartyPokemon.length === 0) { + /** + * If the player has exactly one Pokemon in total at this point in a double battle, and that Pokemon + * is already on the field, unshift a phase that moves that Pokemon to center position. + */ + this.scene.unshiftPhase(new ToggleDoublePositionPhase(this.scene, true)); + } else if (legalPlayerPartyPokemon.length > 0) { + /** + * If previous conditions weren't met, and the player has at least 1 legal Pokemon off the field, + * push a phase that prompts the player to summon a Pokemon from their party. + */ + this.scene.pushPhase(new SwitchPhase(this.scene, this.fieldIndex, true, false)); + } + } else { + this.scene.unshiftPhase(new VictoryPhase(this.scene, this.battlerIndex)); + if (this.scene.currentBattle.battleType === BattleType.TRAINER) { + const hasReservePartyMember = !!this.scene.getEnemyParty().filter(p => p.isActive() && !p.isOnField() && p.trainerSlot === (pokemon as EnemyPokemon).trainerSlot).length; + if (hasReservePartyMember) { + this.scene.pushPhase(new SwitchSummonPhase(this.scene, this.fieldIndex, -1, false, false, false)); + } + } + } + + // in double battles redirect potential moves off fainted pokemon + if (this.scene.currentBattle.double) { + const allyPokemon = pokemon.getAlly(); + this.scene.redirectPokemonMoves(pokemon, allyPokemon); + } + + pokemon.lapseTags(BattlerTagLapseType.FAINT); + this.scene.getField(true).filter(p => p !== pokemon).forEach(p => p.removeTagsBySourceId(pokemon.id)); + + pokemon.faintCry(() => { + if (pokemon instanceof PlayerPokemon) { + pokemon.addFriendship(-10); + } + pokemon.hideInfo(); + this.scene.playSound("faint"); + this.scene.tweens.add({ + targets: pokemon, + duration: 500, + y: pokemon.y + 150, + ease: "Sine.easeIn", + onComplete: () => { + pokemon.setVisible(false); + pokemon.y -= 150; + pokemon.trySetStatus(StatusEffect.FAINT); + if (pokemon.isPlayer()) { + this.scene.currentBattle.removeFaintedParticipant(pokemon as PlayerPokemon); + } else { + this.scene.addFaintedEnemyScore(pokemon as EnemyPokemon); + this.scene.currentBattle.addPostBattleLoot(pokemon as EnemyPokemon); + } + this.scene.field.remove(pokemon); + this.end(); + } + }); + }); + } + + tryOverrideForBattleSpec(): boolean { + switch (this.scene.currentBattle.battleSpec) { + case BattleSpec.FINAL_BOSS: + if (!this.player) { + const enemy = this.getPokemon(); + if (enemy.formIndex) { + this.scene.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].secondStageWin, enemy.species.name, null, () => this.doFaint()); + } else { + // Final boss' HP threshold has been bypassed; cancel faint and force check for 2nd phase + enemy.hp++; + this.scene.unshiftPhase(new DamagePhase(this.scene, enemy.getBattlerIndex(), 0, HitResult.OTHER)); + this.end(); + } + return true; + } + } + + return false; + } +} diff --git a/src/phases/field-phase.ts b/src/phases/field-phase.ts new file mode 100644 index 00000000000..a9622271f14 --- /dev/null +++ b/src/phases/field-phase.ts @@ -0,0 +1,44 @@ +import { BattlerIndex } from "#app/battle.js"; +import { TrickRoomTag } from "#app/data/arena-tag.js"; +import { Stat } from "#app/enums/stat.js"; +import Pokemon from "#app/field/pokemon.js"; +import { BattlePhase } from "./battle-phase"; +import * as Utils from "#app/utils.js"; + +type PokemonFunc = (pokemon: Pokemon) => void; + +export abstract class FieldPhase extends BattlePhase { + getOrder(): BattlerIndex[] { + const playerField = this.scene.getPlayerField().filter(p => p.isActive()) as Pokemon[]; + const enemyField = this.scene.getEnemyField().filter(p => p.isActive()) as Pokemon[]; + + // We shuffle the list before sorting so speed ties produce random results + let orderedTargets: Pokemon[] = playerField.concat(enemyField); + // We seed it with the current turn to prevent an inconsistency where it + // was varying based on how long since you last reloaded + this.scene.executeWithSeedOffset(() => { + orderedTargets = Utils.randSeedShuffle(orderedTargets); + }, this.scene.currentBattle.turn, this.scene.waveSeed); + + orderedTargets.sort((a: Pokemon, b: Pokemon) => { + const aSpeed = a?.getBattleStat(Stat.SPD) || 0; + const bSpeed = b?.getBattleStat(Stat.SPD) || 0; + + return bSpeed - aSpeed; + }); + + const speedReversed = new Utils.BooleanHolder(false); + this.scene.arena.applyTags(TrickRoomTag, speedReversed); + + if (speedReversed.value) { + orderedTargets = orderedTargets.reverse(); + } + + return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : 0)); + } + + executeForAll(func: PokemonFunc): void { + const field = this.scene.getField(true).filter(p => p.summonData); + field.forEach(pokemon => func(pokemon)); + } +} diff --git a/src/form-change-phase.ts b/src/phases/form-change-phase.ts similarity index 57% rename from src/form-change-phase.ts rename to src/phases/form-change-phase.ts index 5acbc4fb77c..88e0dd00ce1 100644 --- a/src/form-change-phase.ts +++ b/src/phases/form-change-phase.ts @@ -1,17 +1,14 @@ -import BattleScene from "./battle-scene"; -import * as Utils from "./utils"; -import { SpeciesFormKey } from "./data/pokemon-species"; -import { achvs } from "./system/achv"; -import { SpeciesFormChange, getSpeciesFormChangeMessage } from "./data/pokemon-forms"; -import { EndEvolutionPhase, EvolutionPhase } from "./evolution-phase"; -import Pokemon, { EnemyPokemon, PlayerPokemon } from "./field/pokemon"; -import { Mode } from "./ui/ui"; -import PartyUiHandler from "./ui/party-ui-handler"; -import { BattleSpec } from "#enums/battle-spec"; -import { BattlePhase, MovePhase, PokemonHealPhase } from "./phases"; -import { getTypeRgb } from "./data/type"; -import { getPokemonNameWithAffix } from "./messages"; -import { SemiInvulnerableTag } from "./data/battler-tags"; +import BattleScene from "../battle-scene"; +import * as Utils from "../utils"; +import { SpeciesFormKey } from "../data/pokemon-species"; +import { achvs } from "../system/achv"; +import { SpeciesFormChange, getSpeciesFormChangeMessage } from "../data/pokemon-forms"; +import { PlayerPokemon } from "../field/pokemon"; +import { Mode } from "../ui/ui"; +import PartyUiHandler from "../ui/party-ui-handler"; +import { getPokemonNameWithAffix } from "../messages"; +import { EndEvolutionPhase } from "./end-evolution-phase"; +import { EvolutionPhase } from "./evolution-phase"; export class FormChangePhase extends EvolutionPhase { private formChange: SpeciesFormChange; @@ -175,126 +172,3 @@ export class FormChangePhase extends EvolutionPhase { } } } - -export class QuietFormChangePhase extends BattlePhase { - protected pokemon: Pokemon; - protected formChange: SpeciesFormChange; - - constructor(scene: BattleScene, pokemon: Pokemon, formChange: SpeciesFormChange) { - super(scene); - this.pokemon = pokemon; - this.formChange = formChange; - } - - start(): void { - super.start(); - - if (this.pokemon.formIndex === this.pokemon.species.forms.findIndex(f => f.formKey === this.formChange.formKey)) { - return this.end(); - } - - const preName = getPokemonNameWithAffix(this.pokemon); - - if (!this.pokemon.isOnField() || this.pokemon.getTag(SemiInvulnerableTag)) { - this.pokemon.changeForm(this.formChange).then(() => { - this.scene.ui.showText(getSpeciesFormChangeMessage(this.pokemon, this.formChange, preName), null, () => this.end(), 1500); - }); - return; - } - - const getPokemonSprite = () => { - const sprite = this.scene.addPokemonSprite(this.pokemon, this.pokemon.x + this.pokemon.getSprite().x, this.pokemon.y + this.pokemon.getSprite().y, "pkmn__sub"); - sprite.setOrigin(0.5, 1); - sprite.play(this.pokemon.getBattleSpriteKey()).stop(); - sprite.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(this.pokemon.getTeraType()) }); - [ "spriteColors", "fusionSpriteColors" ].map(k => { - if (this.pokemon.summonData?.speciesForm) { - k += "Base"; - } - sprite.pipelineData[k] = this.pokemon.getSprite().pipelineData[k]; - }); - this.scene.field.add(sprite); - return sprite; - }; - - const [ pokemonTintSprite, pokemonFormTintSprite ] = [ getPokemonSprite(), getPokemonSprite() ]; - - this.pokemon.getSprite().on("animationupdate", (_anim, frame) => { - if (frame.textureKey === pokemonTintSprite.texture.key) { - pokemonTintSprite.setFrame(frame.textureFrame); - } else { - pokemonFormTintSprite.setFrame(frame.textureFrame); - } - }); - - pokemonTintSprite.setAlpha(0); - pokemonTintSprite.setTintFill(0xFFFFFF); - pokemonFormTintSprite.setVisible(false); - pokemonFormTintSprite.setTintFill(0xFFFFFF); - - this.scene.playSound("PRSFX- Transform"); - - this.scene.tweens.add({ - targets: pokemonTintSprite, - alpha: 1, - duration: 1000, - ease: "Cubic.easeIn", - onComplete: () => { - this.pokemon.setVisible(false); - this.pokemon.changeForm(this.formChange).then(() => { - pokemonFormTintSprite.setScale(0.01); - pokemonFormTintSprite.play(this.pokemon.getBattleSpriteKey()).stop(); - pokemonFormTintSprite.setVisible(true); - this.scene.tweens.add({ - targets: pokemonTintSprite, - delay: 250, - scale: 0.01, - ease: "Cubic.easeInOut", - duration: 500, - onComplete: () => pokemonTintSprite.destroy() - }); - this.scene.tweens.add({ - targets: pokemonFormTintSprite, - delay: 250, - scale: this.pokemon.getSpriteScale(), - ease: "Cubic.easeInOut", - duration: 500, - onComplete: () => { - this.pokemon.setVisible(true); - this.scene.tweens.add({ - targets: pokemonFormTintSprite, - delay: 250, - alpha: 0, - ease: "Cubic.easeOut", - duration: 1000, - onComplete: () => { - pokemonTintSprite.setVisible(false); - this.scene.ui.showText(getSpeciesFormChangeMessage(this.pokemon, this.formChange, preName), null, () => this.end(), 1500); - } - }); - } - }); - }); - } - }); - } - - end(): void { - if (this.pokemon.scene?.currentBattle.battleSpec === BattleSpec.FINAL_BOSS && this.pokemon instanceof EnemyPokemon) { - this.scene.playBgm(); - this.scene.unshiftPhase(new PokemonHealPhase(this.scene, this.pokemon.getBattlerIndex(), this.pokemon.getMaxHp(), null, false, false, false, true)); - this.pokemon.findAndRemoveTags(() => true); - this.pokemon.bossSegments = 5; - this.pokemon.bossSegmentIndex = 4; - this.pokemon.initBattleInfo(); - this.pokemon.cry(); - - const movePhase = this.scene.findPhase(p => p instanceof MovePhase && p.pokemon === this.pokemon) as MovePhase; - if (movePhase) { - movePhase.cancel(); - } - } - - super.end(); - } -} diff --git a/src/phases/game-over-modifier-reward-phase.ts b/src/phases/game-over-modifier-reward-phase.ts new file mode 100644 index 00000000000..e2f4d134cba --- /dev/null +++ b/src/phases/game-over-modifier-reward-phase.ts @@ -0,0 +1,27 @@ +import BattleScene from "#app/battle-scene.js"; +import { ModifierTypeFunc } from "#app/modifier/modifier-type.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next from "i18next"; +import { ModifierRewardPhase } from "./modifier-reward-phase"; + +export class GameOverModifierRewardPhase extends ModifierRewardPhase { + constructor(scene: BattleScene, modifierTypeFunc: ModifierTypeFunc) { + super(scene, modifierTypeFunc); + } + + doReward(): Promise { + return new Promise(resolve => { + const newModifier = this.modifierType.newModifier(); + this.scene.addModifier(newModifier).then(() => { + this.scene.playSound("level_up_fanfare"); + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.fadeIn(250).then(() => { + this.scene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }), null, () => { + this.scene.time.delayedCall(1500, () => this.scene.arenaBg.setVisible(true)); + resolve(); + }, null, true, 1500); + }); + }); + }); + } +} diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts new file mode 100644 index 00000000000..4beed489f29 --- /dev/null +++ b/src/phases/game-over-phase.ts @@ -0,0 +1,203 @@ +import { clientSessionId } from "#app/account.js"; +import BattleScene from "#app/battle-scene.js"; +import { BattleType } from "#app/battle.js"; +import { miscDialogue, getCharVariantFromDialogue } from "#app/data/dialogue.js"; +import { pokemonEvolutions } from "#app/data/pokemon-evolutions.js"; +import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species.js"; +import { trainerConfigs } from "#app/data/trainer-config.js"; +import { PlayerGender } from "#app/enums/player-gender.js"; +import { TrainerType } from "#app/enums/trainer-type.js"; +import Pokemon from "#app/field/pokemon.js"; +import { modifierTypes } from "#app/modifier/modifier-type.js"; +import { achvs, ChallengeAchv } from "#app/system/achv.js"; +import { Unlockables } from "#app/system/unlockables.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next from "i18next"; +import * as Utils from "#app/utils.js"; +import { BattlePhase } from "./battle-phase"; +import { CheckSwitchPhase } from "./check-switch-phase"; +import { EncounterPhase } from "./encounter-phase"; +import { GameOverModifierRewardPhase } from "./game-over-modifier-reward-phase"; +import { RibbonModifierRewardPhase } from "./ribbon-modifier-reward-phase"; +import { SummonPhase } from "./summon-phase"; +import { EndCardPhase } from "./end-card-phase"; +import { PostGameOverPhase } from "./post-game-over-phase"; +import { UnlockPhase } from "./unlock-phase"; + +export class GameOverPhase extends BattlePhase { + private victory: boolean; + private firstRibbons: PokemonSpecies[] = []; + + constructor(scene: BattleScene, victory?: boolean) { + super(scene); + + this.victory = !!victory; + } + + start() { + super.start(); + + // Failsafe if players somehow skip floor 200 in classic mode + if (this.scene.gameMode.isClassic && this.scene.currentBattle.waveIndex > 200) { + this.victory = true; + } + + if (this.victory && this.scene.gameMode.isEndless) { + this.scene.ui.showDialogue(i18next.t("PGMmiscDialogue:ending_endless"), i18next.t("PGMmiscDialogue:ending_name"), 0, () => this.handleGameOver()); + } else if (this.victory || !this.scene.enableRetries) { + this.handleGameOver(); + } else { + this.scene.ui.showText(i18next.t("battle:retryBattle"), null, () => { + this.scene.ui.setMode(Mode.CONFIRM, () => { + this.scene.ui.fadeOut(1250).then(() => { + this.scene.reset(); + this.scene.clearPhaseQueue(); + this.scene.gameData.loadSession(this.scene, this.scene.sessionSlotId).then(() => { + this.scene.pushPhase(new EncounterPhase(this.scene, true)); + + const availablePartyMembers = this.scene.getParty().filter(p => p.isAllowedInBattle()).length; + + this.scene.pushPhase(new SummonPhase(this.scene, 0)); + if (this.scene.currentBattle.double && availablePartyMembers > 1) { + this.scene.pushPhase(new SummonPhase(this.scene, 1)); + } + if (this.scene.currentBattle.waveIndex > 1 && this.scene.currentBattle.battleType !== BattleType.TRAINER) { + this.scene.pushPhase(new CheckSwitchPhase(this.scene, 0, this.scene.currentBattle.double)); + if (this.scene.currentBattle.double && availablePartyMembers > 1) { + this.scene.pushPhase(new CheckSwitchPhase(this.scene, 1, this.scene.currentBattle.double)); + } + } + + this.scene.ui.fadeIn(1250); + this.end(); + }); + }); + }, () => this.handleGameOver(), false, 0, 0, 1000); + }); + } + } + + handleGameOver(): void { + const doGameOver = (newClear: boolean) => { + this.scene.disableMenu = true; + this.scene.time.delayedCall(1000, () => { + let firstClear = false; + if (this.victory && newClear) { + if (this.scene.gameMode.isClassic) { + firstClear = this.scene.validateAchv(achvs.CLASSIC_VICTORY); + this.scene.validateAchv(achvs.UNEVOLVED_CLASSIC_VICTORY); + this.scene.gameData.gameStats.sessionsWon++; + for (const pokemon of this.scene.getParty()) { + this.awardRibbon(pokemon); + + if (pokemon.species.getRootSpeciesId() !== pokemon.species.getRootSpeciesId(true)) { + this.awardRibbon(pokemon, true); + } + } + } else if (this.scene.gameMode.isDaily && newClear) { + this.scene.gameData.gameStats.dailyRunSessionsWon++; + } + } + const fadeDuration = this.victory ? 10000 : 5000; + this.scene.fadeOutBgm(fadeDuration, true); + const activeBattlers = this.scene.getField().filter(p => p?.isActive(true)); + activeBattlers.map(p => p.hideInfo()); + this.scene.ui.fadeOut(fadeDuration).then(() => { + activeBattlers.map(a => a.setVisible(false)); + this.scene.setFieldScale(1, true); + this.scene.clearPhaseQueue(); + this.scene.ui.clearText(); + + if (this.victory && this.scene.gameMode.isChallenge) { + this.scene.gameMode.challenges.forEach(c => this.scene.validateAchvs(ChallengeAchv, c)); + } + + const clear = (endCardPhase?: EndCardPhase) => { + if (newClear) { + this.handleUnlocks(); + } + if (this.victory && newClear) { + for (const species of this.firstRibbons) { + this.scene.unshiftPhase(new RibbonModifierRewardPhase(this.scene, modifierTypes.VOUCHER_PLUS, species)); + } + if (!firstClear) { + this.scene.unshiftPhase(new GameOverModifierRewardPhase(this.scene, modifierTypes.VOUCHER_PREMIUM)); + } + } + this.scene.pushPhase(new PostGameOverPhase(this.scene, endCardPhase)); + this.end(); + }; + + if (this.victory && this.scene.gameMode.isClassic) { + const message = miscDialogue.ending[this.scene.gameData.gender === PlayerGender.FEMALE ? 0 : 1]; + + if (!this.scene.ui.shouldSkipDialogue(message)) { + this.scene.ui.fadeIn(500).then(() => { + this.scene.charSprite.showCharacter(`rival_${this.scene.gameData.gender === PlayerGender.FEMALE ? "m" : "f"}`, getCharVariantFromDialogue(miscDialogue.ending[this.scene.gameData.gender === PlayerGender.FEMALE ? 0 : 1])).then(() => { + this.scene.ui.showDialogue(message, this.scene.gameData.gender === PlayerGender.FEMALE ? trainerConfigs[TrainerType.RIVAL].name : trainerConfigs[TrainerType.RIVAL].nameFemale, null, () => { + this.scene.ui.fadeOut(500).then(() => { + this.scene.charSprite.hide().then(() => { + const endCardPhase = new EndCardPhase(this.scene); + this.scene.unshiftPhase(endCardPhase); + clear(endCardPhase); + }); + }); + }); + }); + }); + } else { + const endCardPhase = new EndCardPhase(this.scene); + this.scene.unshiftPhase(endCardPhase); + clear(endCardPhase); + } + } else { + clear(); + } + }); + }); + }; + + /* Added a local check to see if the game is running offline on victory + If Online, execute apiFetch as intended + If Offline, execute offlineNewClear(), a localStorage implementation of newClear daily run checks */ + if (this.victory) { + if (!Utils.isLocal) { + Utils.apiFetch(`savedata/session/newclear?slot=${this.scene.sessionSlotId}&clientSessionId=${clientSessionId}`, true) + .then(response => response.json()) + .then(newClear => doGameOver(newClear)); + } else { + this.scene.gameData.offlineNewClear(this.scene).then(result => { + doGameOver(result); + }); + } + } else { + doGameOver(false); + } + } + + handleUnlocks(): void { + if (this.victory && this.scene.gameMode.isClassic) { + if (!this.scene.gameData.unlocks[Unlockables.ENDLESS_MODE]) { + this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.ENDLESS_MODE)); + } + if (this.scene.getParty().filter(p => p.fusionSpecies).length && !this.scene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE]) { + this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.SPLICED_ENDLESS_MODE)); + } + if (!this.scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE]) { + this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.MINI_BLACK_HOLE)); + } + if (!this.scene.gameData.unlocks[Unlockables.EVIOLITE] && this.scene.getParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)) { + this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.EVIOLITE)); + } + } + } + + awardRibbon(pokemon: Pokemon, forStarter: boolean = false): void { + const speciesId = getPokemonSpecies(pokemon.species.speciesId); + const speciesRibbonCount = this.scene.gameData.incrementRibbonCount(speciesId, forStarter); + // first time classic win, award voucher + if (speciesRibbonCount === 1) { + this.firstRibbons.push(getPokemonSpecies(pokemon.species.getRootSpeciesId(forStarter))); + } + } +} diff --git a/src/phases/hide-party-exp-bar-phase.ts b/src/phases/hide-party-exp-bar-phase.ts new file mode 100644 index 00000000000..c2c9d96462e --- /dev/null +++ b/src/phases/hide-party-exp-bar-phase.ts @@ -0,0 +1,14 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlePhase } from "./battle-phase"; + +export class HidePartyExpBarPhase extends BattlePhase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + + this.scene.partyExpBar.hide().then(() => this.end()); + } +} diff --git a/src/phases/learn-move-phase.ts b/src/phases/learn-move-phase.ts new file mode 100644 index 00000000000..e30fc0c3d10 --- /dev/null +++ b/src/phases/learn-move-phase.ts @@ -0,0 +1,103 @@ +import BattleScene from "#app/battle-scene.js"; +import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims.js"; +import { allMoves } from "#app/data/move.js"; +import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms.js"; +import { Moves } from "#app/enums/moves.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import EvolutionSceneHandler from "#app/ui/evolution-scene-handler.js"; +import { SummaryUiMode } from "#app/ui/summary-ui-handler.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next from "i18next"; +import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; + +export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { + private moveId: Moves; + + constructor(scene: BattleScene, partyMemberIndex: integer, moveId: Moves) { + super(scene, partyMemberIndex); + + this.moveId = moveId; + } + + start() { + super.start(); + + const pokemon = this.getPokemon(); + const move = allMoves[this.moveId]; + + const existingMoveIndex = pokemon.getMoveset().findIndex(m => m?.moveId === move.id); + + if (existingMoveIndex > -1) { + return this.end(); + } + + const emptyMoveIndex = pokemon.getMoveset().length < 4 + ? pokemon.getMoveset().length + : pokemon.getMoveset().findIndex(m => m === null); + + const messageMode = this.scene.ui.getHandler() instanceof EvolutionSceneHandler + ? Mode.EVOLUTION_SCENE + : Mode.MESSAGE; + + if (emptyMoveIndex > -1) { + pokemon.setMove(emptyMoveIndex, this.moveId); + initMoveAnim(this.scene, this.moveId).then(() => { + loadMoveAnimAssets(this.scene, [this.moveId], true) + .then(() => { + this.scene.ui.setMode(messageMode).then(() => { + this.scene.playSound("level_up_fanfare"); + this.scene.ui.showText(i18next.t("battle:learnMove", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => { + this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeMoveLearnedTrigger, true); + this.end(); + }, messageMode === Mode.EVOLUTION_SCENE ? 1000 : null, true); + }); + }); + }); + } else { + this.scene.ui.setMode(messageMode).then(() => { + this.scene.ui.showText(i18next.t("battle:learnMovePrompt", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => { + this.scene.ui.showText(i18next.t("battle:learnMoveLimitReached", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { + this.scene.ui.showText(i18next.t("battle:learnMoveReplaceQuestion", { moveName: move.name }), null, () => { + const noHandler = () => { + this.scene.ui.setMode(messageMode).then(() => { + this.scene.ui.showText(i18next.t("battle:learnMoveStopTeaching", { moveName: move.name }), null, () => { + this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => { + this.scene.ui.setMode(messageMode); + this.scene.ui.showText(i18next.t("battle:learnMoveNotLearned", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => this.end(), null, true); + }, () => { + this.scene.ui.setMode(messageMode); + this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId)); + this.end(); + }); + }); + }); + }; + this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => { + this.scene.ui.setMode(messageMode); + this.scene.ui.showText(i18next.t("battle:learnMoveForgetQuestion"), null, () => { + this.scene.ui.setModeWithoutClear(Mode.SUMMARY, this.getPokemon(), SummaryUiMode.LEARN_MOVE, move, (moveIndex: integer) => { + if (moveIndex === 4) { + noHandler(); + return; + } + this.scene.ui.setMode(messageMode).then(() => { + this.scene.ui.showText(i18next.t("battle:countdownPoof"), null, () => { + this.scene.ui.showText(i18next.t("battle:learnMoveForgetSuccess", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: pokemon.moveset[moveIndex]!.getName() }), null, () => { // TODO: is the bang correct? + this.scene.ui.showText(i18next.t("battle:learnMoveAnd"), null, () => { + pokemon.setMove(moveIndex, Moves.NONE); + this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId)); + this.end(); + }, null, true); + }, null, true); + }, null, true); + }); + }); + }, null, true); + }, noHandler); + }); + }, null, true); + }, null, true); + }); + } + } +} diff --git a/src/phases/level-cap-phase.ts b/src/phases/level-cap-phase.ts new file mode 100644 index 00000000000..4a07e7d131e --- /dev/null +++ b/src/phases/level-cap-phase.ts @@ -0,0 +1,20 @@ +import BattleScene from "#app/battle-scene.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next from "i18next"; +import { FieldPhase } from "./field-phase"; + +export class LevelCapPhase extends FieldPhase { + constructor(scene: BattleScene) { + super(scene); + } + + start(): void { + super.start(); + + this.scene.ui.setMode(Mode.MESSAGE).then(() => { + this.scene.playSound("level_up_fanfare"); + this.scene.ui.showText(i18next.t("battle:levelCapUp", { levelCap: this.scene.getMaxExpLevel() }), null, () => this.end(), null, true); + this.executeForAll(pokemon => pokemon.updateInfo(true)); + }); + } +} diff --git a/src/phases/level-up-phase.ts b/src/phases/level-up-phase.ts new file mode 100644 index 00000000000..a8a6b8f3d80 --- /dev/null +++ b/src/phases/level-up-phase.ts @@ -0,0 +1,59 @@ +import BattleScene from "#app/battle-scene.js"; +import { ExpNotification } from "#app/enums/exp-notification.js"; +import { EvolutionPhase } from "#app/phases/evolution-phase.js"; +import { PlayerPokemon } from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { LevelAchv } from "#app/system/achv.js"; +import i18next from "i18next"; +import * as Utils from "#app/utils.js"; +import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; +import { LearnMovePhase } from "./learn-move-phase"; + +export class LevelUpPhase extends PlayerPartyMemberPokemonPhase { + private lastLevel: integer; + private level: integer; + + constructor(scene: BattleScene, partyMemberIndex: integer, lastLevel: integer, level: integer) { + super(scene, partyMemberIndex); + + this.lastLevel = lastLevel; + this.level = level; + this.scene = scene; + } + + start() { + super.start(); + + if (this.level > this.scene.gameData.gameStats.highestLevel) { + this.scene.gameData.gameStats.highestLevel = this.level; + } + + this.scene.validateAchvs(LevelAchv, new Utils.IntegerHolder(this.level)); + + const pokemon = this.getPokemon(); + const prevStats = pokemon.stats.slice(0); + pokemon.calculateStats(); + pokemon.updateInfo(); + if (this.scene.expParty === ExpNotification.DEFAULT) { + this.scene.playSound("level_up_fanfare"); + this.scene.ui.showText(i18next.t("battle:levelUp", { pokemonName: getPokemonNameWithAffix(this.getPokemon()), level: this.level }), null, () => this.scene.ui.getMessageHandler().promptLevelUpStats(this.partyMemberIndex, prevStats, false).then(() => this.end()), null, true); + } else if (this.scene.expParty === ExpNotification.SKIP) { + this.end(); + } else { + // we still want to display the stats if activated + this.scene.ui.getMessageHandler().promptLevelUpStats(this.partyMemberIndex, prevStats, false).then(() => this.end()); + } + if (this.lastLevel < 100) { // this feels like an unnecessary optimization + const levelMoves = this.getPokemon().getLevelMoves(this.lastLevel + 1); + for (const lm of levelMoves) { + this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, lm[1])); + } + } + if (!pokemon.pauseEvolutions) { + const evolution = pokemon.getEvolution(); + if (evolution) { + this.scene.unshiftPhase(new EvolutionPhase(this.scene, pokemon as PlayerPokemon, evolution, this.lastLevel)); + } + } + } +} diff --git a/src/phases/login-phase.ts b/src/phases/login-phase.ts new file mode 100644 index 00000000000..b58cc9bca1f --- /dev/null +++ b/src/phases/login-phase.ts @@ -0,0 +1,116 @@ +import { updateUserInfo } from "#app/account.js"; +import BattleScene, { bypassLogin } from "#app/battle-scene.js"; +import { Phase } from "#app/phase.js"; +import { handleTutorial, Tutorial } from "#app/tutorial.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next, { t } from "i18next"; +import * as Utils from "#app/utils.js"; +import { SelectGenderPhase } from "./select-gender-phase"; +import { UnavailablePhase } from "./unavailable-phase"; + +export class LoginPhase extends Phase { + private showText: boolean; + + constructor(scene: BattleScene, showText?: boolean) { + super(scene); + + this.showText = showText === undefined || !!showText; + } + + start(): void { + super.start(); + + const hasSession = !!Utils.getCookie(Utils.sessionIdKey); + + this.scene.ui.setMode(Mode.LOADING, { buttonActions: [] }); + Utils.executeIf(bypassLogin || hasSession, updateUserInfo).then(response => { + const success = response ? response[0] : false; + const statusCode = response ? response[1] : null; + if (!success) { + if (!statusCode || statusCode === 400) { + if (this.showText) { + this.scene.ui.showText(i18next.t("menu:logInOrCreateAccount")); + } + + this.scene.playSound("menu_open"); + + const loadData = () => { + updateUserInfo().then(success => { + if (!success[0]) { + Utils.removeCookie(Utils.sessionIdKey); + this.scene.reset(true, true); + return; + } + this.scene.gameData.loadSystem().then(() => this.end()); + }); + }; + + this.scene.ui.setMode(Mode.LOGIN_FORM, { + buttonActions: [ + () => { + this.scene.ui.playSelect(); + loadData(); + }, () => { + this.scene.playSound("menu_open"); + this.scene.ui.setMode(Mode.REGISTRATION_FORM, { + buttonActions: [ + () => { + this.scene.ui.playSelect(); + updateUserInfo().then(success => { + if (!success[0]) { + Utils.removeCookie(Utils.sessionIdKey); + this.scene.reset(true, true); + return; + } + this.end(); + } ); + }, () => { + this.scene.unshiftPhase(new LoginPhase(this.scene, false)); + this.end(); + } + ] + }); + }, () => { + const redirectUri = encodeURIComponent(`${import.meta.env.VITE_SERVER_URL}/auth/discord/callback`); + const discordId = import.meta.env.VITE_DISCORD_CLIENT_ID; + const discordUrl = `https://discord.com/api/oauth2/authorize?client_id=${discordId}&redirect_uri=${redirectUri}&response_type=code&scope=identify&prompt=none`; + window.open(discordUrl, "_self"); + }, () => { + const redirectUri = encodeURIComponent(`${import.meta.env.VITE_SERVER_URL}/auth/google/callback`); + const googleId = import.meta.env.VITE_GOOGLE_CLIENT_ID; + const googleUrl = `https://accounts.google.com/o/oauth2/auth?client_id=${googleId}&redirect_uri=${redirectUri}&response_type=code&scope=openid`; + window.open(googleUrl, "_self"); + } + ] + }); + } else if (statusCode === 401) { + Utils.removeCookie(Utils.sessionIdKey); + this.scene.reset(true, true); + } else { + this.scene.unshiftPhase(new UnavailablePhase(this.scene)); + super.end(); + } + return null; + } else { + this.scene.gameData.loadSystem().then(success => { + if (success || bypassLogin) { + this.end(); + } else { + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.showText(t("menu:failedToLoadSaveData")); + } + }); + } + }); + } + + end(): void { + this.scene.ui.setMode(Mode.MESSAGE); + + if (!this.scene.gameData.gender) { + this.scene.unshiftPhase(new SelectGenderPhase(this.scene)); + } + + handleTutorial(this.scene, Tutorial.Intro).then(() => super.end()); + } +} diff --git a/src/phases/message-phase.ts b/src/phases/message-phase.ts new file mode 100644 index 00000000000..46e907ae2ba --- /dev/null +++ b/src/phases/message-phase.ts @@ -0,0 +1,38 @@ +import BattleScene from "#app/battle-scene.js"; +import { Phase } from "#app/phase.js"; + +export class MessagePhase extends Phase { + private text: string; + private callbackDelay: integer | null; + private prompt: boolean | null; + private promptDelay: integer | null; + + constructor(scene: BattleScene, text: string, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) { + super(scene); + + this.text = text; + this.callbackDelay = callbackDelay!; // TODO: is this bang correct? + this.prompt = prompt!; // TODO: is this bang correct? + this.promptDelay = promptDelay!; // TODO: is this bang correct? + } + + start() { + super.start(); + + if (this.text.indexOf("$") > -1) { + const pageIndex = this.text.indexOf("$"); + this.scene.unshiftPhase(new MessagePhase(this.scene, this.text.slice(pageIndex + 1), this.callbackDelay, this.prompt, this.promptDelay)); + this.text = this.text.slice(0, pageIndex).trim(); + } + + this.scene.ui.showText(this.text, null, () => this.end(), this.callbackDelay || (this.prompt ? 0 : 1500), this.prompt, this.promptDelay); + } + + end() { + if (this.scene.abilityBar.shown) { + this.scene.abilityBar.hide(); + } + + super.end(); + } +} diff --git a/src/phases/modifier-reward-phase.ts b/src/phases/modifier-reward-phase.ts new file mode 100644 index 00000000000..4d083a367a6 --- /dev/null +++ b/src/phases/modifier-reward-phase.ts @@ -0,0 +1,30 @@ +import BattleScene from "#app/battle-scene.js"; +import { ModifierType, ModifierTypeFunc, getModifierType } from "#app/modifier/modifier-type.js"; +import i18next from "i18next"; +import { BattlePhase } from "./battle-phase"; + +export class ModifierRewardPhase extends BattlePhase { + protected modifierType: ModifierType; + + constructor(scene: BattleScene, modifierTypeFunc: ModifierTypeFunc) { + super(scene); + + this.modifierType = getModifierType(modifierTypeFunc); + } + + start() { + super.start(); + + this.doReward().then(() => this.end()); + } + + doReward(): Promise { + return new Promise(resolve => { + const newModifier = this.modifierType.newModifier(); + this.scene.addModifier(newModifier).then(() => { + this.scene.playSound("item_fanfare"); + this.scene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }), null, () => resolve(), null, true); + }); + }); + } +} diff --git a/src/phases/money-reward-phase.ts b/src/phases/money-reward-phase.ts new file mode 100644 index 00000000000..e0bff608972 --- /dev/null +++ b/src/phases/money-reward-phase.ts @@ -0,0 +1,34 @@ +import BattleScene from "#app/battle-scene.js"; +import { ArenaTagType } from "#app/enums/arena-tag-type.js"; +import { MoneyMultiplierModifier } from "#app/modifier/modifier.js"; +import i18next from "i18next"; +import * as Utils from "#app/utils.js"; +import { BattlePhase } from "./battle-phase"; + +export class MoneyRewardPhase extends BattlePhase { + private moneyMultiplier: number; + + constructor(scene: BattleScene, moneyMultiplier: number) { + super(scene); + + this.moneyMultiplier = moneyMultiplier; + } + + start() { + const moneyAmount = new Utils.IntegerHolder(this.scene.getWaveMoneyAmount(this.moneyMultiplier)); + + this.scene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount); + + if (this.scene.arena.getTag(ArenaTagType.HAPPY_HOUR)) { + moneyAmount.value *= 2; + } + + this.scene.addMoney(moneyAmount.value); + + const userLocale = navigator.language || "en-US"; + const formattedMoneyAmount = moneyAmount.value.toLocaleString(userLocale); + const message = i18next.t("battle:moneyWon", { moneyAmount: formattedMoneyAmount }); + + this.scene.ui.showText(message, null, () => this.end(), null, true); + } +} diff --git a/src/phases/move-anim-test-phase.ts b/src/phases/move-anim-test-phase.ts new file mode 100644 index 00000000000..7fb3c1d61e7 --- /dev/null +++ b/src/phases/move-anim-test-phase.ts @@ -0,0 +1,44 @@ +import BattleScene from "#app/battle-scene.js"; +import { initMoveAnim, loadMoveAnimAssets, MoveAnim } from "#app/data/battle-anims.js"; +import { allMoves, SelfStatusMove } from "#app/data/move.js"; +import { Moves } from "#app/enums/moves.js"; +import * as Utils from "#app/utils.js"; +import { BattlePhase } from "./battle-phase"; + +export class MoveAnimTestPhase extends BattlePhase { + private moveQueue: Moves[]; + + constructor(scene: BattleScene, moveQueue?: Moves[]) { + super(scene); + + this.moveQueue = moveQueue || Utils.getEnumValues(Moves).slice(1); + } + + start() { + const moveQueue = this.moveQueue.slice(0); + this.playMoveAnim(moveQueue, true); + } + + playMoveAnim(moveQueue: Moves[], player: boolean) { + const moveId = player ? moveQueue[0] : moveQueue.shift(); + if (moveId === undefined) { + this.playMoveAnim(this.moveQueue.slice(0), true); + return; + } else if (player) { + console.log(Moves[moveId]); + } + + initMoveAnim(this.scene, moveId).then(() => { + loadMoveAnimAssets(this.scene, [moveId], true) + .then(() => { + new MoveAnim(moveId, player ? this.scene.getPlayerPokemon()! : this.scene.getEnemyPokemon()!, (player !== (allMoves[moveId] instanceof SelfStatusMove) ? this.scene.getEnemyPokemon()! : this.scene.getPlayerPokemon()!).getBattlerIndex()).play(this.scene, () => { // TODO: are the bangs correct here? + if (player) { + this.playMoveAnim(moveQueue, false); + } else { + this.playMoveAnim(moveQueue, true); + } + }); + }); + }); + } +} diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts new file mode 100644 index 00000000000..12018656458 --- /dev/null +++ b/src/phases/move-effect-phase.ts @@ -0,0 +1,447 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { applyPreAttackAbAttrs, AddSecondStrikeAbAttr, IgnoreMoveEffectsAbAttr, applyPostDefendAbAttrs, PostDefendAbAttr, applyPostAttackAbAttrs, PostAttackAbAttr, MaxMultiHitAbAttr, AlwaysHitAbAttr } from "#app/data/ability.js"; +import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag.js"; +import { MoveAnim } from "#app/data/battle-anims.js"; +import { BattlerTagLapseType, ProtectedTag, SemiInvulnerableTag } from "#app/data/battler-tags.js"; +import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, MoveEffectTrigger, ChargeAttr, MoveCategory, NoEffectAttr, HitsTagAttr } from "#app/data/move.js"; +import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms.js"; +import { BattlerTagType } from "#app/enums/battler-tag-type.js"; +import { Moves } from "#app/enums/moves.js"; +import Pokemon, { PokemonMove, MoveResult, HitResult } from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { PokemonMultiHitModifier, FlinchChanceModifier, EnemyAttackStatusEffectChanceModifier, ContactHeldItemTransferChanceModifier, HitHealModifier } from "#app/modifier/modifier.js"; +import i18next from "i18next"; +import * as Utils from "#app/utils.js"; +import { PokemonPhase } from "./pokemon-phase"; + +export class MoveEffectPhase extends PokemonPhase { + public move: PokemonMove; + protected targets: BattlerIndex[]; + + constructor(scene: BattleScene, battlerIndex: BattlerIndex, targets: BattlerIndex[], move: PokemonMove) { + super(scene, battlerIndex); + this.move = move; + /** + * In double battles, if the right Pokemon selects a spread move and the left Pokemon dies + * with no party members available to switch in, then the right Pokemon takes the index + * of the left Pokemon and gets hit unless this is checked. + */ + if (targets.includes(battlerIndex) && this.move.getMove().moveTarget === MoveTarget.ALL_NEAR_OTHERS) { + const i = targets.indexOf(battlerIndex); + targets.splice(i, i + 1); + } + this.targets = targets; + } + + start() { + super.start(); + + /** The Pokemon using this phase's invoked move */ + const user = this.getUserPokemon(); + /** All Pokemon targeted by this phase's invoked move */ + const targets = this.getTargets(); + + /** If the user was somehow removed from the field, end this phase */ + if (!user?.isOnField()) { + return super.end(); + } + + /** + * Does an effect from this move override other effects on this turn? + * e.g. Charging moves (Fly, etc.) on their first turn of use. + */ + const overridden = new Utils.BooleanHolder(false); + /** The {@linkcode Move} object from {@linkcode allMoves} invoked by this phase */ + const move = this.move.getMove(); + + // Assume single target for override + applyMoveAttrs(OverrideMoveEffectAttr, user, this.getTarget() ?? null, move, overridden, this.move.virtual).then(() => { + // If other effects were overriden, stop this phase before they can be applied + if (overridden.value) { + return this.end(); + } + + user.lapseTags(BattlerTagLapseType.MOVE_EFFECT); + + /** + * If this phase is for the first hit of the invoked move, + * resolve the move's total hit count. This block combines the + * effects of the move itself, Parental Bond, and Multi-Lens to do so. + */ + if (user.turnData.hitsLeft === undefined) { + const hitCount = new Utils.IntegerHolder(1); + // Assume single target for multi hit + applyMoveAttrs(MultiHitAttr, user, this.getTarget() ?? null, move, hitCount); + // If Parental Bond is applicable, double the hit count + applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, false, targets.length, hitCount, new Utils.IntegerHolder(0)); + // If Multi-Lens is applicable, multiply the hit count by 1 + the number of Multi-Lenses held by the user + if (move instanceof AttackMove && !move.hasAttr(FixedDamageAttr)) { + this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0)); + } + // Set the user's relevant turnData fields to reflect the final hit count + user.turnData.hitCount = hitCount.value; + user.turnData.hitsLeft = hitCount.value; + } + + /** + * Log to be entered into the user's move history once the move result is resolved. + * Note that `result` (a {@linkcode MoveResult}) logs whether the move was successfully + * used in the sense of "Does it have an effect on the user?". + */ + const moveHistoryEntry = { move: this.move.moveId, targets: this.targets, result: MoveResult.PENDING, virtual: this.move.virtual }; + + /** + * Stores results of hit checks of the invoked move against all targets, organized by battler index. + * @see {@linkcode hitCheck} + */ + const targetHitChecks = Object.fromEntries(targets.map(p => [p.getBattlerIndex(), this.hitCheck(p)])); + const hasActiveTargets = targets.some(t => t.isActive(true)); + /** + * If no targets are left for the move to hit (FAIL), or the invoked move is single-target + * (and not random target) and failed the hit check against its target (MISS), log the move + * as FAILed or MISSed (depending on the conditions above) and end this phase. + */ + if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) { + this.stopMultiHit(); + if (hasActiveTargets) { + this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: this.getTarget()? getPokemonNameWithAffix(this.getTarget()!) : "" })); + moveHistoryEntry.result = MoveResult.MISS; + applyMoveAttrs(MissEffectAttr, user, null, move); + } else { + this.scene.queueMessage(i18next.t("battle:attackFailed")); + moveHistoryEntry.result = MoveResult.FAIL; + } + user.pushMoveHistory(moveHistoryEntry); + return this.end(); + } + + /** All move effect attributes are chained together in this array to be applied asynchronously. */ + const applyAttrs: Promise[] = []; + + // Move animation only needs one target + new MoveAnim(move.id as Moves, user, this.getTarget()?.getBattlerIndex()!).play(this.scene, () => { // TODO: is the bang correct here? + /** Has the move successfully hit a target (for damage) yet? */ + let hasHit: boolean = false; + for (const target of targets) { + /** + * If the move missed a target, stop all future hits against that target + * and move on to the next target (if there is one). + */ + if (!targetHitChecks[target.getBattlerIndex()]) { + this.stopMultiHit(target); + this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); + if (moveHistoryEntry.result === MoveResult.PENDING) { + moveHistoryEntry.result = MoveResult.MISS; + } + user.pushMoveHistory(moveHistoryEntry); + applyMoveAttrs(MissEffectAttr, user, null, move); + continue; + } + + /** The {@linkcode ArenaTagSide} to which the target belongs */ + const targetSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + /** Has the invoked move been cancelled by conditional protection (e.g Quick Guard)? */ + const hasConditionalProtectApplied = new Utils.BooleanHolder(false); + /** Does the applied conditional protection bypass Protect-ignoring effects? */ + const bypassIgnoreProtect = new Utils.BooleanHolder(false); + // If the move is not targeting a Pokemon on the user's side, try to apply conditional protection effects + if (!this.move.getMove().isAllyTarget()) { + this.scene.arena.applyTagsForSide(ConditionalProtectTag, targetSide, hasConditionalProtectApplied, user, target, move.id, bypassIgnoreProtect); + } + + /** Is the target protected by Protect, etc. or a relevant conditional protection effect? */ + const isProtected = (bypassIgnoreProtect.value || !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target)) + && (hasConditionalProtectApplied.value || target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))); + + /** Does this phase represent the invoked move's first strike? */ + const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount); + + // Only log the move's result on the first strike + if (firstHit) { + user.pushMoveHistory(moveHistoryEntry); + } + + /** + * Since all fail/miss checks have applied, the move is considered successfully applied. + * It's worth noting that if the move has no effect or is protected against, this assignment + * is overwritten and the move is logged as a FAIL. + */ + moveHistoryEntry.result = MoveResult.SUCCESS; + + /** + * Stores the result of applying the invoked move to the target. + * If the target is protected, the result is always `NO_EFFECT`. + * Otherwise, the hit result is based on type effectiveness, immunities, + * and other factors that may negate the attack or status application. + * + * Internally, the call to {@linkcode Pokemon.apply} is where damage is calculated + * (for attack moves) and the target's HP is updated. However, this isn't + * made visible to the user until the resulting {@linkcode DamagePhase} + * is invoked. + */ + const hitResult = !isProtected ? target.apply(user, move) : HitResult.NO_EFFECT; + + /** Does {@linkcode hitResult} indicate that damage was dealt to the target? */ + const dealsDamage = [ + HitResult.EFFECTIVE, + HitResult.SUPER_EFFECTIVE, + HitResult.NOT_VERY_EFFECTIVE, + HitResult.ONE_HIT_KO + ].includes(hitResult); + + /** Is this target the first one hit by the move on its current strike? */ + const firstTarget = dealsDamage && !hasHit; + if (firstTarget) { + hasHit = true; + } + + /** + * If the move has no effect on the target (i.e. the target is protected or immune), + * change the logged move result to FAIL. + */ + if (hitResult === HitResult.NO_EFFECT) { + moveHistoryEntry.result = MoveResult.FAIL; + } + + /** Does this phase represent the invoked move's last strike? */ + const lastHit = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive()); + + /** + * If the user can change forms by using the invoked move, + * it only changes forms after the move's last hit + * (see Relic Song's interaction with Parental Bond when used by Meloetta). + */ + if (lastHit) { + this.scene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger); + } + + /** + * Create a Promise that applys *all* effects from the invoked move's MoveEffectAttrs. + * These are ordered by trigger type (see {@linkcode MoveEffectTrigger}), and each trigger + * type requires different conditions to be met with respect to the move's hit result. + */ + applyAttrs.push(new Promise(resolve => { + // Apply all effects with PRE_MOVE triggers (if the target isn't immune to the move) + applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && hitResult !== HitResult.NO_EFFECT, + user, target, move).then(() => { + // All other effects require the move to not have failed or have been cancelled to trigger + if (hitResult !== HitResult.FAIL) { + /** Are the move's effects tied to the first turn of a charge move? */ + const chargeEffect = !!move.getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget() ?? null, move)); + /** + * If the invoked move's effects are meant to trigger during the move's "charge turn," + * ignore all effects after this point. + * Otherwise, apply all self-targeted POST_APPLY effects. + */ + Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY + && attr.selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, move)).then(() => { + // All effects past this point require the move to have hit the target + if (hitResult !== HitResult.NO_EFFECT) { + // Apply all non-self-targeted POST_APPLY effects + applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY + && !(attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, this.move.getMove()).then(() => { + /** + * If the move hit, and the target doesn't have Shield Dust, + * apply the chance to flinch the target gained from King's Rock + */ + if (dealsDamage && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr)) { + const flinched = new Utils.BooleanHolder(false); + user.scene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched); + if (flinched.value) { + target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id); + } + } + // If the move was not protected against, apply all HIT effects + Utils.executeIf(!isProtected && !chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT + && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && (!attr.firstTargetOnly || firstTarget), user, target, this.move.getMove()).then(() => { + // Apply the target's post-defend ability effects (as long as the target is active or can otherwise apply them) + return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move.getMove(), hitResult).then(() => { + // If the invoked move is an enemy attack, apply the enemy's status effect-inflicting tags and tokens + target.lapseTag(BattlerTagType.BEAK_BLAST_CHARGING); + if (move.category === MoveCategory.PHYSICAL && user.isPlayer() !== target.isPlayer()) { + target.lapseTag(BattlerTagType.SHELL_TRAP); + } + if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) { + user.scene.applyShuffledModifiers(this.scene, EnemyAttackStatusEffectChanceModifier, false, target); + } + })).then(() => { + // Apply the user's post-attack ability effects + applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move.getMove(), hitResult).then(() => { + /** + * If the invoked move is an attack, apply the user's chance to + * steal an item from the target granted by Grip Claw + */ + if (this.move.getMove() instanceof AttackMove) { + this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target); + } + resolve(); + }); + }); + }) + ).then(() => resolve()); + }); + } else { + applyMoveAttrs(NoEffectAttr, user, null, move).then(() => resolve()); + } + }); + } else { + resolve(); + } + }); + })); + } + // Apply the move's POST_TARGET effects on the move's last hit, after all targeted effects have resolved + const postTarget = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive()) ? + applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET, user, null, move) : + null; + + if (!!postTarget) { + if (applyAttrs.length) { // If there is a pending asynchronous move effect, do this after + applyAttrs[applyAttrs.length - 1]?.then(() => postTarget); + } else { // Otherwise, push a new asynchronous move effect + applyAttrs.push(postTarget); + } + } + + // Wait for all move effects to finish applying, then end this phase + Promise.allSettled(applyAttrs).then(() => this.end()); + }); + }); + } + + end() { + const move = this.move.getMove(); + move.type = move.defaultType; + const user = this.getUserPokemon(); + /** + * If this phase isn't for the invoked move's last strike, + * unshift another MoveEffectPhase for the next strike. + * Otherwise, queue a message indicating the number of times the move has struck + * (if the move has struck more than once), then apply the heal from Shell Bell + * to the user. + */ + if (user) { + if (user.turnData.hitsLeft && --user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) { + this.scene.unshiftPhase(this.getNewHitPhase()); + } else { + // Queue message for number of hits made by multi-move + // If multi-hit attack only hits once, still want to render a message + const hitsTotal = user.turnData.hitCount! - Math.max(user.turnData.hitsLeft!, 0); // TODO: are those bangs correct? + if (hitsTotal > 1 || (user.turnData.hitsLeft && user.turnData.hitsLeft > 0)) { + // If there are multiple hits, or if there are hits of the multi-hit move left + this.scene.queueMessage(i18next.t("battle:attackHitsCount", { count: hitsTotal })); + } + this.scene.applyModifiers(HitHealModifier, this.player, user); + } + } + + super.end(); + } + + /** + * Resolves whether this phase's invoked move hits or misses the given target + * @param target {@linkcode Pokemon} the Pokemon targeted by the invoked move + * @returns `true` if the move does not miss the target; `false` otherwise + */ + hitCheck(target: Pokemon): boolean { + // Moves targeting the user and entry hazards can't miss + if ([MoveTarget.USER, MoveTarget.ENEMY_SIDE].includes(this.move.getMove().moveTarget)) { + return true; + } + + const user = this.getUserPokemon()!; // TODO: is this bang correct? + + // Hit check only calculated on first hit for multi-hit moves unless flag is set to check all hits. + // However, if an ability with the MaxMultiHitAbAttr, namely Skill Link, is present, act as a normal + // multi-hit move and proceed with all hits + if (user.turnData.hitsLeft < user.turnData.hitCount) { + if (!this.move.getMove().hasFlag(MoveFlags.CHECK_ALL_HITS) || user.hasAbilityWithAttr(MaxMultiHitAbAttr)) { + return true; + } + } + + if (user.hasAbilityWithAttr(AlwaysHitAbAttr) || target.hasAbilityWithAttr(AlwaysHitAbAttr)) { + return true; + } + + // If the user should ignore accuracy on a target, check who the user targeted last turn and see if they match + if (user.getTag(BattlerTagType.IGNORE_ACCURACY) && (user.getLastXMoves().find(() => true)?.targets || []).indexOf(target.getBattlerIndex()) !== -1) { + return true; + } + + if (target.getTag(BattlerTagType.ALWAYS_GET_HIT)) { + return true; + } + + const semiInvulnerableTag = target.getTag(SemiInvulnerableTag); + if (semiInvulnerableTag && !this.move.getMove().getAttrs(HitsTagAttr).some(hta => hta.tagType === semiInvulnerableTag.tagType)) { + return false; + } + + const moveAccuracy = this.move.getMove().calculateBattleAccuracy(user!, target); // TODO: is the bang correct here? + + if (moveAccuracy === -1) { + return true; + } + + const accuracyMultiplier = user.getAccuracyMultiplier(target, this.move.getMove()); + const rand = user.randSeedInt(100, 1); + + return rand <= moveAccuracy * (accuracyMultiplier!); // TODO: is this bang correct? + } + + /** Returns the {@linkcode Pokemon} using this phase's invoked move */ + getUserPokemon(): Pokemon | undefined { + if (this.battlerIndex > BattlerIndex.ENEMY_2) { + return this.scene.getPokemonById(this.battlerIndex) ?? undefined; + } + return (this.player ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex]; + } + + /** Returns an array of all {@linkcode Pokemon} targeted by this phase's invoked move */ + getTargets(): Pokemon[] { + return this.scene.getField(true).filter(p => this.targets.indexOf(p.getBattlerIndex()) > -1); + } + + /** Returns the first target of this phase's invoked move */ + getTarget(): Pokemon | undefined { + return this.getTargets()[0]; + } + + /** + * Removes the given {@linkcode Pokemon} from this phase's target list + * @param target {@linkcode Pokemon} the Pokemon to be removed + */ + removeTarget(target: Pokemon): void { + const targetIndex = this.targets.findIndex(ind => ind === target.getBattlerIndex()); + if (targetIndex !== -1) { + this.targets.splice(this.targets.findIndex(ind => ind === target.getBattlerIndex()), 1); + } + } + + /** + * Prevents subsequent strikes of this phase's invoked move from occurring + * @param target {@linkcode Pokemon} if defined, only stop subsequent + * strikes against this Pokemon + */ + stopMultiHit(target?: Pokemon): void { + /** If given a specific target, remove the target from subsequent strikes */ + if (target) { + this.removeTarget(target); + } + /** + * If no target specified, or the specified target was the last of this move's + * targets, completely cancel all subsequent strikes. + */ + if (!target || this.targets.length === 0 ) { + this.getUserPokemon()!.turnData.hitCount = 1; // TODO: is the bang correct here? + this.getUserPokemon()!.turnData.hitsLeft = 1; // TODO: is the bang correct here? + } + } + + /** Returns a new MoveEffectPhase with the same properties as this phase */ + getNewHitPhase() { + return new MoveEffectPhase(this.scene, this.battlerIndex, this.targets, this.move); + } +} diff --git a/src/phases/move-end-phase.ts b/src/phases/move-end-phase.ts new file mode 100644 index 00000000000..4bce2185aea --- /dev/null +++ b/src/phases/move-end-phase.ts @@ -0,0 +1,23 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { BattlerTagLapseType } from "#app/data/battler-tags.js"; +import { PokemonPhase } from "./pokemon-phase"; + +export class MoveEndPhase extends PokemonPhase { + constructor(scene: BattleScene, battlerIndex: BattlerIndex) { + super(scene, battlerIndex); + } + + start() { + super.start(); + + const pokemon = this.getPokemon(); + if (pokemon.isActive(true)) { + pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE); + } + + this.scene.arena.setIgnoreAbilities(false); + + this.end(); + } +} diff --git a/src/phases/move-header-phase.ts b/src/phases/move-header-phase.ts new file mode 100644 index 00000000000..5166f287731 --- /dev/null +++ b/src/phases/move-header-phase.ts @@ -0,0 +1,30 @@ +import BattleScene from "#app/battle-scene.js"; +import { applyMoveAttrs, MoveHeaderAttr } from "#app/data/move.js"; +import Pokemon, { PokemonMove } from "#app/field/pokemon.js"; +import { BattlePhase } from "./battle-phase"; + +export class MoveHeaderPhase extends BattlePhase { + public pokemon: Pokemon; + public move: PokemonMove; + + constructor(scene: BattleScene, pokemon: Pokemon, move: PokemonMove) { + super(scene); + + this.pokemon = pokemon; + this.move = move; + } + + canMove(): boolean { + return this.pokemon.isActive(true) && this.move.isUsable(this.pokemon); + } + + start() { + super.start(); + + if (this.canMove()) { + applyMoveAttrs(MoveHeaderAttr, this.pokemon, null, this.move.getMove()).then(() => this.end()); + } else { + this.end(); + } + } +} diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts new file mode 100644 index 00000000000..c446660b16f --- /dev/null +++ b/src/phases/move-phase.ts @@ -0,0 +1,329 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { applyAbAttrs, RedirectMoveAbAttr, BlockRedirectAbAttr, IncreasePpAbAttr, applyPreAttackAbAttrs, PokemonTypeChangeAbAttr, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr } from "#app/data/ability.js"; +import { CommonAnim } from "#app/data/battle-anims.js"; +import { CenterOfAttentionTag, BattlerTagLapseType } from "#app/data/battler-tags.js"; +import { MoveFlags, BypassRedirectAttr, allMoves, CopyMoveAttr, applyMoveAttrs, BypassSleepAttr, HealStatusEffectAttr, ChargeAttr, PreMoveMessageAttr } from "#app/data/move.js"; +import { SpeciesFormChangePreMoveTrigger } from "#app/data/pokemon-forms.js"; +import { getStatusEffectActivationText, getStatusEffectHealText } from "#app/data/status-effect.js"; +import { Type } from "#app/data/type.js"; +import { getTerrainBlockMessage } from "#app/data/weather.js"; +import { Abilities } from "#app/enums/abilities.js"; +import { BattlerTagType } from "#app/enums/battler-tag-type.js"; +import { Moves } from "#app/enums/moves.js"; +import { StatusEffect } from "#app/enums/status-effect.js"; +import { MoveUsedEvent } from "#app/events/battle-scene.js"; +import Pokemon, { PokemonMove, MoveResult, TurnMove } from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import i18next from "i18next"; +import * as Utils from "#app/utils.js"; +import { BattlePhase } from "./battle-phase"; +import { CommonAnimPhase } from "./common-anim-phase"; +import { MoveEffectPhase } from "./move-effect-phase"; +import { MoveEndPhase } from "./move-end-phase"; +import { ShowAbilityPhase } from "./show-ability-phase"; + +export class MovePhase extends BattlePhase { + public pokemon: Pokemon; + public move: PokemonMove; + public targets: BattlerIndex[]; + protected followUp: boolean; + protected ignorePp: boolean; + protected failed: boolean; + protected cancelled: boolean; + + constructor(scene: BattleScene, pokemon: Pokemon, targets: BattlerIndex[], move: PokemonMove, followUp?: boolean, ignorePp?: boolean) { + super(scene); + + this.pokemon = pokemon; + this.targets = targets; + this.move = move; + this.followUp = !!followUp; + this.ignorePp = !!ignorePp; + this.failed = false; + this.cancelled = false; + } + + canMove(): boolean { + return this.pokemon.isActive(true) && this.move.isUsable(this.pokemon, this.ignorePp) && !!this.targets.length; + } + + /**Signifies the current move should fail but still use PP */ + fail(): void { + this.failed = true; + } + + /**Signifies the current move should cancel and retain PP */ + cancel(): void { + this.cancelled = true; + } + + start() { + super.start(); + + console.log(Moves[this.move.moveId]); + + if (!this.canMove()) { + if (this.move.moveId && this.pokemon.summonData?.disabledMove === this.move.moveId) { + this.scene.queueMessage(i18next.t("battle:moveDisabled", { moveName: this.move.getName() })); + } + if (this.pokemon.isActive(true) && this.move.ppUsed >= this.move.getMovePp()) { // if the move PP was reduced from Spite or otherwise, the move fails + this.fail(); + this.showMoveText(); + this.showFailedText(); + } + return this.end(); + } + + if (!this.followUp) { + if (this.move.getMove().checkFlag(MoveFlags.IGNORE_ABILITIES, this.pokemon, null)) { + this.scene.arena.setIgnoreAbilities(); + } + } else { + this.pokemon.turnData.hitsLeft = 0; // TODO: is `0` correct? + this.pokemon.turnData.hitCount = 0; // TODO: is `0` correct? + } + + // Move redirection abilities (ie. Storm Drain) only support single target moves + const moveTarget = this.targets.length === 1 + ? new Utils.IntegerHolder(this.targets[0]) + : null; + if (moveTarget) { + const oldTarget = moveTarget.value; + this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, false, this.move.moveId, moveTarget)); + this.pokemon.getOpponents().forEach(p => { + const redirectTag = p.getTag(CenterOfAttentionTag) as CenterOfAttentionTag; + if (redirectTag && (!redirectTag.powder || (!this.pokemon.isOfType(Type.GRASS) && !this.pokemon.hasAbility(Abilities.OVERCOAT)))) { + moveTarget.value = p.getBattlerIndex(); + } + }); + //Check if this move is immune to being redirected, and restore its target to the intended target if it is. + if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) || this.move.getMove().hasAttr(BypassRedirectAttr))) { + //If an ability prevented this move from being redirected, display its ability pop up. + if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) && !this.move.getMove().hasAttr(BypassRedirectAttr)) && oldTarget !== moveTarget.value) { + this.scene.unshiftPhase(new ShowAbilityPhase(this.scene, this.pokemon.getBattlerIndex(), this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr))); + } + moveTarget.value = oldTarget; + } + this.targets[0] = moveTarget.value; + } + + // Check for counterattack moves to switch target + if (this.targets.length === 1 && this.targets[0] === BattlerIndex.ATTACKER) { + if (this.pokemon.turnData.attacksReceived.length) { + const attack = this.pokemon.turnData.attacksReceived[0]; + this.targets[0] = attack.sourceBattlerIndex; + + // account for metal burst and comeuppance hitting remaining targets in double battles + // counterattack will redirect to remaining ally if original attacker faints + if (this.scene.currentBattle.double && this.move.getMove().hasFlag(MoveFlags.REDIRECT_COUNTER)) { + if (this.scene.getField()[this.targets[0]].hp === 0) { + const opposingField = this.pokemon.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField(); + //@ts-ignore + this.targets[0] = opposingField.find(p => p.hp > 0)?.getBattlerIndex(); //TODO: fix ts-ignore + } + } + } + if (this.targets[0] === BattlerIndex.ATTACKER) { + this.fail(); // Marks the move as failed for later in doMove + this.showMoveText(); + this.showFailedText(); + } + } + + const targets = this.scene.getField(true).filter(p => { + if (this.targets.indexOf(p.getBattlerIndex()) > -1) { + return true; + } + return false; + }); + + const doMove = () => { + this.pokemon.turnData.acted = true; // Record that the move was attempted, even if it fails + + this.pokemon.lapseTags(BattlerTagLapseType.PRE_MOVE); + + let ppUsed = 1; + // Filter all opponents to include only those this move is targeting + const targetedOpponents = this.pokemon.getOpponents().filter(o => this.targets.includes(o.getBattlerIndex())); + for (const opponent of targetedOpponents) { + if (this.move.ppUsed + ppUsed >= this.move.getMovePp()) { // If we're already at max PP usage, stop checking + break; + } + if (opponent.hasAbilityWithAttr(IncreasePpAbAttr)) { // Accounting for abilities like Pressure + ppUsed++; + } + } + + if (!this.followUp && this.canMove() && !this.cancelled) { + this.pokemon.lapseTags(BattlerTagLapseType.MOVE); + } + + const moveQueue = this.pokemon.getMoveQueue(); + if (this.cancelled || this.failed) { + if (this.failed) { + this.move.usePp(ppUsed); // Only use PP if the move failed + this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed)); + } + + // Record a failed move so Abilities like Truant don't trigger next turn and soft-lock + this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL }); + + this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); // Remove any tags from moves like Fly/Dive/etc. + moveQueue.shift(); // Remove the second turn of charge moves + return this.end(); + } + + this.scene.triggerPokemonFormChange(this.pokemon, SpeciesFormChangePreMoveTrigger); + + if (this.move.moveId) { + this.showMoveText(); + } + + // This should only happen when there are no valid targets left on the field + if ((moveQueue.length && moveQueue[0].move === Moves.NONE) || !targets.length) { + this.showFailedText(); + this.cancel(); + + // Record a failed move so Abilities like Truant don't trigger next turn and soft-lock + this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL }); + + this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); // Remove any tags from moves like Fly/Dive/etc. + + moveQueue.shift(); + return this.end(); + } + + if (!moveQueue.length || !moveQueue.shift()?.ignorePP) { // using .shift here clears out two turn moves once they've been used + this.move.usePp(ppUsed); + this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed)); + } + + if (!allMoves[this.move.moveId].hasAttr(CopyMoveAttr)) { + this.scene.currentBattle.lastMove = this.move.moveId; + } + + // Assume conditions affecting targets only apply to moves with a single target + let success = this.move.getMove().applyConditions(this.pokemon, targets[0], this.move.getMove()); + const cancelled = new Utils.BooleanHolder(false); + let failedText = this.move.getMove().getFailedText(this.pokemon, targets[0], this.move.getMove(), cancelled); + if (success && this.scene.arena.isMoveWeatherCancelled(this.move.getMove())) { + success = false; + } else if (success && this.scene.arena.isMoveTerrainCancelled(this.pokemon, this.targets, this.move.getMove())) { + success = false; + if (failedText === null) { + failedText = getTerrainBlockMessage(targets[0], this.scene.arena.terrain?.terrainType!); // TODO: is this bang correct? + } + } + + /** + * Trigger pokemon type change before playing the move animation + * Will still change the user's type when using Roar, Whirlwind, Trick-or-Treat, and Forest's Curse, + * regardless of whether the move successfully executes or not. + */ + if (success || [Moves.ROAR, Moves.WHIRLWIND, Moves.TRICK_OR_TREAT, Moves.FORESTS_CURSE].includes(this.move.moveId)) { + applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, this.move.getMove()); + } + + if (success) { + this.scene.unshiftPhase(this.getEffectPhase()); + } else { + this.pokemon.pushMoveHistory({ move: this.move.moveId, targets: this.targets, result: MoveResult.FAIL, virtual: this.move.virtual }); + if (!cancelled.value) { + this.showFailedText(failedText); + } + } + // Checks if Dancer ability is triggered + if (this.move.getMove().hasFlag(MoveFlags.DANCE_MOVE) && !this.followUp) { + // Pokemon with Dancer can be on either side of the battle so we check in both cases + this.scene.getPlayerField().forEach(pokemon => { + applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets); + }); + this.scene.getEnemyField().forEach(pokemon => { + applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets); + }); + } + this.end(); + }; + + if (!this.followUp && this.pokemon.status && !this.pokemon.status.isPostTurn()) { + this.pokemon.status.incrementTurn(); + let activated = false; + let healed = false; + + switch (this.pokemon.status.effect) { + case StatusEffect.PARALYSIS: + if (!this.pokemon.randSeedInt(4)) { + activated = true; + this.cancelled = true; + } + break; + case StatusEffect.SLEEP: + applyMoveAttrs(BypassSleepAttr, this.pokemon, null, this.move.getMove()); + healed = this.pokemon.status.turnCount === this.pokemon.status.cureTurn; + activated = !healed && !this.pokemon.getTag(BattlerTagType.BYPASS_SLEEP); + this.cancelled = activated; + break; + case StatusEffect.FREEZE: + healed = !!this.move.getMove().findAttr(attr => attr instanceof HealStatusEffectAttr && attr.selfTarget && attr.isOfEffect(StatusEffect.FREEZE)) || !this.pokemon.randSeedInt(5); + activated = !healed; + this.cancelled = activated; + break; + } + + if (activated) { + this.scene.queueMessage(getStatusEffectActivationText(this.pokemon.status.effect, getPokemonNameWithAffix(this.pokemon))); + this.scene.unshiftPhase(new CommonAnimPhase(this.scene, this.pokemon.getBattlerIndex(), undefined, CommonAnim.POISON + (this.pokemon.status.effect - 1))); + doMove(); + } else { + if (healed) { + this.scene.queueMessage(getStatusEffectHealText(this.pokemon.status.effect, getPokemonNameWithAffix(this.pokemon))); + this.pokemon.resetStatus(); + this.pokemon.updateInfo(); + } + doMove(); + } + } else { + doMove(); + } + } + + getEffectPhase(): MoveEffectPhase { + return new MoveEffectPhase(this.scene, this.pokemon.getBattlerIndex(), this.targets, this.move); + } + + showMoveText(): void { + if (this.move.getMove().hasAttr(ChargeAttr)) { + const lastMove = this.pokemon.getLastXMoves() as TurnMove[]; + if (!lastMove.length || lastMove[0].move !== this.move.getMove().id || lastMove[0].result !== MoveResult.OTHER) { + this.scene.queueMessage(i18next.t("battle:useMove", { + pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon), + moveName: this.move.getName() + }), 500); + return; + } + } + + if (this.pokemon.getTag(BattlerTagType.RECHARGING || BattlerTagType.INTERRUPTED)) { + return; + } + + this.scene.queueMessage(i18next.t("battle:useMove", { + pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon), + moveName: this.move.getName() + }), 500); + applyMoveAttrs(PreMoveMessageAttr, this.pokemon, this.pokemon.getOpponents().find(() => true)!, this.move.getMove()); //TODO: is the bang correct here? + } + + showFailedText(failedText: string | null = null): void { + this.scene.queueMessage(failedText || i18next.t("battle:attackFailed")); + } + + end() { + if (!this.followUp && this.canMove()) { + this.scene.unshiftPhase(new MoveEndPhase(this.scene, this.pokemon.getBattlerIndex())); + } + + super.end(); + } +} diff --git a/src/phases/new-battle-phase.ts b/src/phases/new-battle-phase.ts new file mode 100644 index 00000000000..5a422c9e6c7 --- /dev/null +++ b/src/phases/new-battle-phase.ts @@ -0,0 +1,11 @@ +import { BattlePhase } from "./battle-phase"; + +export class NewBattlePhase extends BattlePhase { + start() { + super.start(); + + this.scene.newBattle(); + + this.end(); + } +} diff --git a/src/phases/new-biome-encounter-phase.ts b/src/phases/new-biome-encounter-phase.ts new file mode 100644 index 00000000000..c447e78f7b1 --- /dev/null +++ b/src/phases/new-biome-encounter-phase.ts @@ -0,0 +1,38 @@ +import BattleScene from "#app/battle-scene.js"; +import { applyAbAttrs, PostBiomeChangeAbAttr } from "#app/data/ability.js"; +import { getRandomWeatherType } from "#app/data/weather.js"; +import { NextEncounterPhase } from "./next-encounter-phase"; + +export class NewBiomeEncounterPhase extends NextEncounterPhase { + constructor(scene: BattleScene) { + super(scene); + } + + doEncounter(): void { + this.scene.playBgm(undefined, true); + + for (const pokemon of this.scene.getParty()) { + if (pokemon) { + pokemon.resetBattleData(); + } + } + + this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena), false); + + for (const pokemon of this.scene.getParty().filter(p => p.isOnField())) { + applyAbAttrs(PostBiomeChangeAbAttr, pokemon, null); + } + + const enemyField = this.scene.getEnemyField(); + this.scene.tweens.add({ + targets: [this.scene.arenaEnemy, enemyField].flat(), + x: "+=300", + duration: 2000, + onComplete: () => { + if (!this.tryOverrideForBattleSpec()) { + this.doEncounterCommon(); + } + } + }); + } +} diff --git a/src/phases/next-encounter-phase.ts b/src/phases/next-encounter-phase.ts new file mode 100644 index 00000000000..89987534fc0 --- /dev/null +++ b/src/phases/next-encounter-phase.ts @@ -0,0 +1,46 @@ +import BattleScene from "#app/battle-scene.js"; +import { EncounterPhase } from "./encounter-phase"; + +export class NextEncounterPhase extends EncounterPhase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + } + + doEncounter(): void { + this.scene.playBgm(undefined, true); + + for (const pokemon of this.scene.getParty()) { + if (pokemon) { + pokemon.resetBattleData(); + } + } + + this.scene.arenaNextEnemy.setBiome(this.scene.arena.biomeType); + this.scene.arenaNextEnemy.setVisible(true); + + const enemyField = this.scene.getEnemyField(); + this.scene.tweens.add({ + targets: [this.scene.arenaEnemy, this.scene.arenaNextEnemy, this.scene.currentBattle.trainer, enemyField, this.scene.lastEnemyTrainer].flat(), + x: "+=300", + duration: 2000, + onComplete: () => { + this.scene.arenaEnemy.setBiome(this.scene.arena.biomeType); + this.scene.arenaEnemy.setX(this.scene.arenaNextEnemy.x); + this.scene.arenaEnemy.setAlpha(1); + this.scene.arenaNextEnemy.setX(this.scene.arenaNextEnemy.x - 300); + this.scene.arenaNextEnemy.setVisible(false); + if (this.scene.lastEnemyTrainer) { + this.scene.lastEnemyTrainer.destroy(); + } + + if (!this.tryOverrideForBattleSpec()) { + this.doEncounterCommon(); + } + } + }); + } +} diff --git a/src/phases/obtain-status-effect-phase.ts b/src/phases/obtain-status-effect-phase.ts new file mode 100644 index 00000000000..ac6e66a2e9f --- /dev/null +++ b/src/phases/obtain-status-effect-phase.ts @@ -0,0 +1,48 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { CommonBattleAnim, CommonAnim } from "#app/data/battle-anims.js"; +import { getStatusEffectObtainText, getStatusEffectOverlapText } from "#app/data/status-effect.js"; +import { StatusEffect } from "#app/enums/status-effect.js"; +import Pokemon from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { PokemonPhase } from "./pokemon-phase"; +import { PostTurnStatusEffectPhase } from "./post-turn-status-effect-phase"; + +export class ObtainStatusEffectPhase extends PokemonPhase { + private statusEffect: StatusEffect | undefined; + private cureTurn: integer | null; + private sourceText: string | null; + private sourcePokemon: Pokemon | null; + + constructor(scene: BattleScene, battlerIndex: BattlerIndex, statusEffect?: StatusEffect, cureTurn?: integer | null, sourceText?: string, sourcePokemon?: Pokemon) { + super(scene, battlerIndex); + + this.statusEffect = statusEffect; + this.cureTurn = cureTurn!; // TODO: is this bang correct? + this.sourceText = sourceText!; // TODO: is this bang correct? + this.sourcePokemon = sourcePokemon!; // For tracking which Pokemon caused the status effect // TODO: is this bang correct? + } + + start() { + const pokemon = this.getPokemon(); + if (!pokemon?.status) { + if (pokemon?.trySetStatus(this.statusEffect, false, this.sourcePokemon)) { + if (this.cureTurn) { + pokemon.status!.cureTurn = this.cureTurn; // TODO: is this bang correct? + } + pokemon.updateInfo(true); + new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect! - 1), pokemon).play(this.scene, () => { + 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; + } + } else if (pokemon.status.effect === this.statusEffect) { + this.scene.queueMessage(getStatusEffectOverlapText(this.statusEffect, getPokemonNameWithAffix(pokemon))); + } + this.end(); + } +} diff --git a/src/phases/outdated-phase.ts b/src/phases/outdated-phase.ts new file mode 100644 index 00000000000..72d1bb3671d --- /dev/null +++ b/src/phases/outdated-phase.ts @@ -0,0 +1,13 @@ +import BattleScene from "#app/battle-scene.js"; +import { Phase } from "#app/phase.js"; +import { Mode } from "#app/ui/ui.js"; + +export class OutdatedPhase extends Phase { + constructor(scene: BattleScene) { + super(scene); + } + + start(): void { + this.scene.ui.setMode(Mode.OUTDATED); + } +} diff --git a/src/phases/party-heal-phase.ts b/src/phases/party-heal-phase.ts new file mode 100644 index 00000000000..d9179826a19 --- /dev/null +++ b/src/phases/party-heal-phase.ts @@ -0,0 +1,40 @@ +import BattleScene from "#app/battle-scene.js"; +import * as Utils from "#app/utils.js"; +import { BattlePhase } from "./battle-phase"; + +export class PartyHealPhase extends BattlePhase { + private resumeBgm: boolean; + + constructor(scene: BattleScene, resumeBgm: boolean) { + super(scene); + + this.resumeBgm = resumeBgm; + } + + start() { + super.start(); + + const bgmPlaying = this.scene.isBgmPlaying(); + if (bgmPlaying) { + this.scene.fadeOutBgm(1000, false); + } + this.scene.ui.fadeOut(1000).then(() => { + for (const pokemon of this.scene.getParty()) { + pokemon.hp = pokemon.getMaxHp(); + pokemon.resetStatus(); + for (const move of pokemon.moveset) { + move!.ppUsed = 0; // TODO: is this bang correct? + } + pokemon.updateInfo(true); + } + const healSong = this.scene.playSoundWithoutBgm("heal"); + this.scene.time.delayedCall(Utils.fixedInt(healSong.totalDuration * 1000), () => { + healSong.destroy(); + if (this.resumeBgm && bgmPlaying) { + this.scene.playBgm(); + } + this.scene.ui.fadeIn(500).then(() => this.end()); + }); + }); + } +} diff --git a/src/phases/party-member-pokemon-phase.ts b/src/phases/party-member-pokemon-phase.ts new file mode 100644 index 00000000000..1f27826884e --- /dev/null +++ b/src/phases/party-member-pokemon-phase.ts @@ -0,0 +1,27 @@ +import BattleScene from "#app/battle-scene.js"; +import Pokemon from "#app/field/pokemon.js"; +import { FieldPhase } from "./field-phase"; + +export abstract class PartyMemberPokemonPhase extends FieldPhase { + protected partyMemberIndex: integer; + protected fieldIndex: integer; + protected player: boolean; + + constructor(scene: BattleScene, partyMemberIndex: integer, player: boolean) { + super(scene); + + this.partyMemberIndex = partyMemberIndex; + this.fieldIndex = partyMemberIndex < this.scene.currentBattle.getBattlerCount() + ? partyMemberIndex + : -1; + this.player = player; + } + + getParty(): Pokemon[] { + return this.player ? this.scene.getParty() : this.scene.getEnemyParty(); + } + + getPokemon(): Pokemon { + return this.getParty()[this.partyMemberIndex]; + } +} diff --git a/src/phases/party-status-cure-phase.ts b/src/phases/party-status-cure-phase.ts new file mode 100644 index 00000000000..a11aa01b63a --- /dev/null +++ b/src/phases/party-status-cure-phase.ts @@ -0,0 +1,48 @@ +import BattleScene from "#app/battle-scene.js"; +import { Abilities } from "#app/enums/abilities.js"; +import Pokemon from "#app/field/pokemon.js"; +import { BattlePhase } from "./battle-phase"; +import { ShowAbilityPhase } from "./show-ability-phase"; + +/** + * Cures the party of all non-volatile status conditions, shows a message + * @param {BattleScene} scene The current scene + * @param {Pokemon} user The user of the move that cures the party + * @param {string} message The message that should be displayed + * @param {Abilities} abilityCondition Pokemon with this ability will not be affected ie. Soundproof + */ +export class PartyStatusCurePhase extends BattlePhase { + private user: Pokemon; + private message: string; + private abilityCondition: Abilities; + + constructor(scene: BattleScene, user: Pokemon, message: string, abilityCondition: Abilities) { + super(scene); + + this.user = user; + this.message = message; + this.abilityCondition = abilityCondition; + } + + start() { + super.start(); + for (const pokemon of this.scene.getParty()) { + if (!pokemon.isOnField() || pokemon === this.user) { + pokemon.resetStatus(false); + pokemon.updateInfo(true); + } else { + if (!pokemon.hasAbility(this.abilityCondition)) { + pokemon.resetStatus(); + pokemon.updateInfo(true); + } else { + // Manually show ability bar, since we're not hooked into the targeting system + pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.id, pokemon.getPassiveAbility()?.id === this.abilityCondition)); + } + } + } + if (this.message) { + this.scene.queueMessage(this.message); + } + this.end(); + } +} diff --git a/src/phases/player-party-member-pokemon-phase.ts b/src/phases/player-party-member-pokemon-phase.ts new file mode 100644 index 00000000000..4b1600b33d2 --- /dev/null +++ b/src/phases/player-party-member-pokemon-phase.ts @@ -0,0 +1,13 @@ +import BattleScene from "#app/battle-scene.js"; +import { PlayerPokemon } from "#app/field/pokemon.js"; +import { PartyMemberPokemonPhase } from "./party-member-pokemon-phase"; + +export abstract class PlayerPartyMemberPokemonPhase extends PartyMemberPokemonPhase { + constructor(scene: BattleScene, partyMemberIndex: integer) { + super(scene, partyMemberIndex, true); + } + + getPlayerPokemon(): PlayerPokemon { + return super.getPokemon() as PlayerPokemon; + } +} diff --git a/src/phases/pokemon-heal-phase.ts b/src/phases/pokemon-heal-phase.ts new file mode 100644 index 00000000000..6db8aeb4fca --- /dev/null +++ b/src/phases/pokemon-heal-phase.ts @@ -0,0 +1,104 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { CommonAnim } from "#app/data/battle-anims.js"; +import { getStatusEffectHealText } from "#app/data/status-effect.js"; +import { StatusEffect } from "#app/enums/status-effect.js"; +import { HitResult, DamageResult } from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { HealingBoosterModifier } from "#app/modifier/modifier.js"; +import { HealAchv } from "#app/system/achv.js"; +import i18next from "i18next"; +import * as Utils from "#app/utils.js"; +import { CommonAnimPhase } from "./common-anim-phase"; + +export class PokemonHealPhase extends CommonAnimPhase { + private hpHealed: integer; + private message: string | null; + private showFullHpMessage: boolean; + private skipAnim: boolean; + private revive: boolean; + private healStatus: boolean; + private preventFullHeal: boolean; + + constructor(scene: BattleScene, battlerIndex: BattlerIndex, hpHealed: integer, message: string | null, showFullHpMessage: boolean, skipAnim: boolean = false, revive: boolean = false, healStatus: boolean = false, preventFullHeal: boolean = false) { + super(scene, battlerIndex, undefined, CommonAnim.HEALTH_UP); + + this.hpHealed = hpHealed; + this.message = message; + this.showFullHpMessage = showFullHpMessage; + this.skipAnim = skipAnim; + this.revive = revive; + this.healStatus = healStatus; + this.preventFullHeal = preventFullHeal; + } + + start() { + if (!this.skipAnim && (this.revive || this.getPokemon().hp) && !this.getPokemon().isFullHp()) { + super.start(); + } else { + this.end(); + } + } + + end() { + const pokemon = this.getPokemon(); + + if (!pokemon.isOnField() || (!this.revive && !pokemon.isActive())) { + super.end(); + return; + } + + const hasMessage = !!this.message; + const healOrDamage = (!pokemon.isFullHp() || this.hpHealed < 0); + let lastStatusEffect = StatusEffect.NONE; + + if (healOrDamage) { + const hpRestoreMultiplier = new Utils.IntegerHolder(1); + if (!this.revive) { + this.scene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier); + } + const healAmount = new Utils.NumberHolder(Math.floor(this.hpHealed * hpRestoreMultiplier.value)); + if (healAmount.value < 0) { + pokemon.damageAndUpdate(healAmount.value * -1, HitResult.HEAL as DamageResult); + healAmount.value = 0; + } + // Prevent healing to full if specified (in case of healing tokens so Sturdy doesn't cause a softlock) + if (this.preventFullHeal && pokemon.hp + healAmount.value >= pokemon.getMaxHp()) { + healAmount.value = (pokemon.getMaxHp() - pokemon.hp) - 1; + } + healAmount.value = pokemon.heal(healAmount.value); + if (healAmount.value) { + this.scene.damageNumberHandler.add(pokemon, healAmount.value, HitResult.HEAL); + } + if (pokemon.isPlayer()) { + this.scene.validateAchvs(HealAchv, healAmount); + if (healAmount.value > this.scene.gameData.gameStats.highestHeal) { + this.scene.gameData.gameStats.highestHeal = healAmount.value; + } + } + if (this.healStatus && !this.revive && pokemon.status) { + lastStatusEffect = pokemon.status.effect; + pokemon.resetStatus(); + } + pokemon.updateInfo().then(() => super.end()); + } else if (this.healStatus && !this.revive && pokemon.status) { + lastStatusEffect = pokemon.status.effect; + pokemon.resetStatus(); + pokemon.updateInfo().then(() => super.end()); + } else if (this.showFullHpMessage) { + this.message = i18next.t("battle:hpIsFull", { pokemonName: getPokemonNameWithAffix(pokemon) }); + } + + if (this.message) { + this.scene.queueMessage(this.message); + } + + if (this.healStatus && lastStatusEffect && !hasMessage) { + this.scene.queueMessage(getStatusEffectHealText(lastStatusEffect, getPokemonNameWithAffix(pokemon))); + } + + if (!healOrDamage && !lastStatusEffect) { + super.end(); + } + } +} diff --git a/src/phases/pokemon-phase.ts b/src/phases/pokemon-phase.ts new file mode 100644 index 00000000000..871ee57d7a5 --- /dev/null +++ b/src/phases/pokemon-phase.ts @@ -0,0 +1,29 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import Pokemon from "#app/field/pokemon.js"; +import { FieldPhase } from "./field-phase"; + +export abstract class PokemonPhase extends FieldPhase { + protected battlerIndex: BattlerIndex | integer; + public player: boolean; + public fieldIndex: integer; + + constructor(scene: BattleScene, battlerIndex?: BattlerIndex | integer) { + super(scene); + + if (battlerIndex === undefined) { + battlerIndex = scene.getField().find(p => p?.isActive())!.getBattlerIndex(); // TODO: is the bang correct here? + } + + this.battlerIndex = battlerIndex; + this.player = battlerIndex < 2; + this.fieldIndex = battlerIndex % 2; + } + + getPokemon(): Pokemon { + if (this.battlerIndex > BattlerIndex.ENEMY_2) { + return this.scene.getPokemonById(this.battlerIndex)!; //TODO: is this bang correct? + } + return this.scene.getField()[this.battlerIndex]!; //TODO: is this bang correct? + } +} diff --git a/src/phases/post-game-over-phase.ts b/src/phases/post-game-over-phase.ts new file mode 100644 index 00000000000..02413b41a23 --- /dev/null +++ b/src/phases/post-game-over-phase.ts @@ -0,0 +1,46 @@ +import BattleScene from "#app/battle-scene.js"; +import { Phase } from "#app/phase.js"; +import { EndCardPhase } from "./end-card-phase"; +import { TitlePhase } from "./title-phase"; + +export class PostGameOverPhase extends Phase { + private endCardPhase: EndCardPhase | null; + + constructor(scene: BattleScene, endCardPhase?: EndCardPhase) { + super(scene); + + this.endCardPhase = endCardPhase!; // TODO: is this bang correct? + } + + start() { + super.start(); + + const saveAndReset = () => { + this.scene.gameData.saveAll(this.scene, true, true, true).then(success => { + if (!success) { + return this.scene.reset(true); + } + this.scene.gameData.tryClearSession(this.scene, this.scene.sessionSlotId).then((success: boolean | [boolean, boolean]) => { + if (!success[0]) { + return this.scene.reset(true); + } + this.scene.reset(); + this.scene.unshiftPhase(new TitlePhase(this.scene)); + this.end(); + }); + }); + }; + + if (this.endCardPhase) { + this.scene.ui.fadeOut(500).then(() => { + this.scene.ui.getMessageHandler().bg.setVisible(true); + + this.endCardPhase?.endCard.destroy(); + this.endCardPhase?.text.destroy(); + saveAndReset(); + }); + } else { + saveAndReset(); + } + } +} diff --git a/src/phases/post-summon-phase.ts b/src/phases/post-summon-phase.ts new file mode 100644 index 00000000000..e671bf30ed1 --- /dev/null +++ b/src/phases/post-summon-phase.ts @@ -0,0 +1,24 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { applyPostSummonAbAttrs, PostSummonAbAttr } from "#app/data/ability.js"; +import { ArenaTrapTag } from "#app/data/arena-tag.js"; +import { StatusEffect } from "#app/enums/status-effect.js"; +import { PokemonPhase } from "./pokemon-phase"; + +export class PostSummonPhase extends PokemonPhase { + constructor(scene: BattleScene, battlerIndex: BattlerIndex) { + super(scene, battlerIndex); + } + + start() { + super.start(); + + const pokemon = this.getPokemon(); + + if (pokemon.status?.effect === StatusEffect.TOXIC) { + pokemon.status.turnCount = 0; + } + this.scene.arena.applyTags(ArenaTrapTag, pokemon); + applyPostSummonAbAttrs(PostSummonAbAttr, pokemon).then(() => this.end()); + } +} diff --git a/src/phases/post-turn-status-effect-phase.ts b/src/phases/post-turn-status-effect-phase.ts new file mode 100644 index 00000000000..47db32303a5 --- /dev/null +++ b/src/phases/post-turn-status-effect-phase.ts @@ -0,0 +1,61 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { applyAbAttrs, BlockNonDirectDamageAbAttr, BlockStatusDamageAbAttr, ReduceBurnDamageAbAttr } from "#app/data/ability.js"; +import { CommonBattleAnim, CommonAnim } from "#app/data/battle-anims.js"; +import { getStatusEffectActivationText } from "#app/data/status-effect.js"; +import { BattleSpec } from "#app/enums/battle-spec.js"; +import { StatusEffect } from "#app/enums/status-effect.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import * as Utils from "#app/utils.js"; +import { PokemonPhase } from "./pokemon-phase"; + +export class PostTurnStatusEffectPhase extends PokemonPhase { + constructor(scene: BattleScene, battlerIndex: BattlerIndex) { + super(scene, battlerIndex); + } + + start() { + const pokemon = this.getPokemon(); + if (pokemon?.isActive(true) && pokemon.status && pokemon.status.isPostTurn()) { + pokemon.status.incrementTurn(); + const cancelled = new Utils.BooleanHolder(false); + applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); + applyAbAttrs(BlockStatusDamageAbAttr, pokemon, cancelled); + + if (!cancelled.value) { + this.scene.queueMessage(getStatusEffectActivationText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); + const damage = new Utils.NumberHolder(0); + switch (pokemon.status.effect) { + case StatusEffect.POISON: + damage.value = Math.max(pokemon.getMaxHp() >> 3, 1); + break; + case StatusEffect.TOXIC: + damage.value = Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 1); + break; + case StatusEffect.BURN: + damage.value = Math.max(pokemon.getMaxHp() >> 4, 1); + applyAbAttrs(ReduceBurnDamageAbAttr, pokemon, null, false, damage); + break; + } + if (damage.value) { + // Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ... + this.scene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage.value, false, true)); + pokemon.updateInfo(); + } + new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => this.end()); + } else { + this.end(); + } + } else { + this.end(); + } + } + + override end() { + if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { + this.scene.initFinalBossPhaseTwo(this.getPokemon()); + } else { + super.end(); + } + } +} diff --git a/src/phases/quiet-form-change-phase.ts b/src/phases/quiet-form-change-phase.ts new file mode 100644 index 00000000000..3d30d36907e --- /dev/null +++ b/src/phases/quiet-form-change-phase.ts @@ -0,0 +1,133 @@ +import BattleScene from "#app/battle-scene.js"; +import { SemiInvulnerableTag } from "#app/data/battler-tags.js"; +import { SpeciesFormChange, getSpeciesFormChangeMessage } from "#app/data/pokemon-forms.js"; +import { getTypeRgb } from "#app/data/type.js"; +import { BattleSpec } from "#app/enums/battle-spec.js"; +import Pokemon, { EnemyPokemon } from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { BattlePhase } from "./battle-phase"; +import { MovePhase } from "./move-phase"; +import { PokemonHealPhase } from "./pokemon-heal-phase"; + +export class QuietFormChangePhase extends BattlePhase { + protected pokemon: Pokemon; + protected formChange: SpeciesFormChange; + + constructor(scene: BattleScene, pokemon: Pokemon, formChange: SpeciesFormChange) { + super(scene); + this.pokemon = pokemon; + this.formChange = formChange; + } + + start(): void { + super.start(); + + if (this.pokemon.formIndex === this.pokemon.species.forms.findIndex(f => f.formKey === this.formChange.formKey)) { + return this.end(); + } + + const preName = getPokemonNameWithAffix(this.pokemon); + + if (!this.pokemon.isOnField() || this.pokemon.getTag(SemiInvulnerableTag)) { + this.pokemon.changeForm(this.formChange).then(() => { + this.scene.ui.showText(getSpeciesFormChangeMessage(this.pokemon, this.formChange, preName), null, () => this.end(), 1500); + }); + return; + } + + const getPokemonSprite = () => { + const sprite = this.scene.addPokemonSprite(this.pokemon, this.pokemon.x + this.pokemon.getSprite().x, this.pokemon.y + this.pokemon.getSprite().y, "pkmn__sub"); + sprite.setOrigin(0.5, 1); + sprite.play(this.pokemon.getBattleSpriteKey()).stop(); + sprite.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(this.pokemon.getTeraType()) }); + [ "spriteColors", "fusionSpriteColors" ].map(k => { + if (this.pokemon.summonData?.speciesForm) { + k += "Base"; + } + sprite.pipelineData[k] = this.pokemon.getSprite().pipelineData[k]; + }); + this.scene.field.add(sprite); + return sprite; + }; + + const [ pokemonTintSprite, pokemonFormTintSprite ] = [ getPokemonSprite(), getPokemonSprite() ]; + + this.pokemon.getSprite().on("animationupdate", (_anim, frame) => { + if (frame.textureKey === pokemonTintSprite.texture.key) { + pokemonTintSprite.setFrame(frame.textureFrame); + } else { + pokemonFormTintSprite.setFrame(frame.textureFrame); + } + }); + + pokemonTintSprite.setAlpha(0); + pokemonTintSprite.setTintFill(0xFFFFFF); + pokemonFormTintSprite.setVisible(false); + pokemonFormTintSprite.setTintFill(0xFFFFFF); + + this.scene.playSound("PRSFX- Transform"); + + this.scene.tweens.add({ + targets: pokemonTintSprite, + alpha: 1, + duration: 1000, + ease: "Cubic.easeIn", + onComplete: () => { + this.pokemon.setVisible(false); + this.pokemon.changeForm(this.formChange).then(() => { + pokemonFormTintSprite.setScale(0.01); + pokemonFormTintSprite.play(this.pokemon.getBattleSpriteKey()).stop(); + pokemonFormTintSprite.setVisible(true); + this.scene.tweens.add({ + targets: pokemonTintSprite, + delay: 250, + scale: 0.01, + ease: "Cubic.easeInOut", + duration: 500, + onComplete: () => pokemonTintSprite.destroy() + }); + this.scene.tweens.add({ + targets: pokemonFormTintSprite, + delay: 250, + scale: this.pokemon.getSpriteScale(), + ease: "Cubic.easeInOut", + duration: 500, + onComplete: () => { + this.pokemon.setVisible(true); + this.scene.tweens.add({ + targets: pokemonFormTintSprite, + delay: 250, + alpha: 0, + ease: "Cubic.easeOut", + duration: 1000, + onComplete: () => { + pokemonTintSprite.setVisible(false); + this.scene.ui.showText(getSpeciesFormChangeMessage(this.pokemon, this.formChange, preName), null, () => this.end(), 1500); + } + }); + } + }); + }); + } + }); + } + + end(): void { + if (this.pokemon.scene?.currentBattle.battleSpec === BattleSpec.FINAL_BOSS && this.pokemon instanceof EnemyPokemon) { + this.scene.playBgm(); + this.scene.unshiftPhase(new PokemonHealPhase(this.scene, this.pokemon.getBattlerIndex(), this.pokemon.getMaxHp(), null, false, false, false, true)); + this.pokemon.findAndRemoveTags(() => true); + this.pokemon.bossSegments = 5; + this.pokemon.bossSegmentIndex = 4; + this.pokemon.initBattleInfo(); + this.pokemon.cry(); + + const movePhase = this.scene.findPhase(p => p instanceof MovePhase && p.pokemon === this.pokemon) as MovePhase; + if (movePhase) { + movePhase.cancel(); + } + } + + super.end(); + } +} diff --git a/src/phases/reload-session-phase.ts b/src/phases/reload-session-phase.ts new file mode 100644 index 00000000000..a61c52323bf --- /dev/null +++ b/src/phases/reload-session-phase.ts @@ -0,0 +1,39 @@ +import BattleScene from "#app/battle-scene.js"; +import { Phase } from "#app/phase.js"; +import { Mode } from "#app/ui/ui.js"; +import * as Utils from "#app/utils.js"; + +export class ReloadSessionPhase extends Phase { + private systemDataStr: string | null; + + constructor(scene: BattleScene, systemDataStr?: string) { + super(scene); + + this.systemDataStr = systemDataStr ?? null; + } + + start(): void { + this.scene.ui.setMode(Mode.SESSION_RELOAD); + + let delayElapsed = false; + let loaded = false; + + this.scene.time.delayedCall(Utils.fixedInt(1500), () => { + if (loaded) { + this.end(); + } else { + delayElapsed = true; + } + }); + + this.scene.gameData.clearLocalData(); + + (this.systemDataStr ? this.scene.gameData.initSystem(this.systemDataStr) : this.scene.gameData.loadSystem()).then(() => { + if (delayElapsed) { + this.end(); + } else { + loaded = true; + } + }); + } +} diff --git a/src/phases/return-phase.ts b/src/phases/return-phase.ts new file mode 100644 index 00000000000..e1753670ad4 --- /dev/null +++ b/src/phases/return-phase.ts @@ -0,0 +1,26 @@ +import BattleScene from "#app/battle-scene.js"; +import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms.js"; +import { SwitchSummonPhase } from "./switch-summon-phase"; + +export class ReturnPhase extends SwitchSummonPhase { + constructor(scene: BattleScene, fieldIndex: integer) { + super(scene, fieldIndex, -1, true, false); + } + + switchAndSummon(): void { + this.end(); + } + + summon(): void { } + + onEnd(): void { + const pokemon = this.getPokemon(); + + pokemon.resetTurnData(); + pokemon.resetSummonData(); + + this.scene.updateFieldScale(); + + this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger); + } +} diff --git a/src/phases/ribbon-modifier-reward-phase.ts b/src/phases/ribbon-modifier-reward-phase.ts new file mode 100644 index 00000000000..4a80325b7e7 --- /dev/null +++ b/src/phases/ribbon-modifier-reward-phase.ts @@ -0,0 +1,33 @@ +import BattleScene from "#app/battle-scene.js"; +import PokemonSpecies from "#app/data/pokemon-species.js"; +import { ModifierTypeFunc } from "#app/modifier/modifier-type.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next from "i18next"; +import { ModifierRewardPhase } from "./modifier-reward-phase"; + +export class RibbonModifierRewardPhase extends ModifierRewardPhase { + private species: PokemonSpecies; + + constructor(scene: BattleScene, modifierTypeFunc: ModifierTypeFunc, species: PokemonSpecies) { + super(scene, modifierTypeFunc); + + this.species = species; + } + + doReward(): Promise { + return new Promise(resolve => { + const newModifier = this.modifierType.newModifier(); + this.scene.addModifier(newModifier).then(() => { + this.scene.playSound("level_up_fanfare"); + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.showText(i18next.t("battle:beatModeFirstTime", { + speciesName: this.species.name, + gameMode: this.scene.gameMode.getName(), + newModifier: newModifier?.type.name + }), null, () => { + resolve(); + }, null, true, 1500); + }); + }); + } +} diff --git a/src/phases/scan-ivs-phase.ts b/src/phases/scan-ivs-phase.ts new file mode 100644 index 00000000000..f5e1a814612 --- /dev/null +++ b/src/phases/scan-ivs-phase.ts @@ -0,0 +1,69 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { CommonBattleAnim, CommonAnim } from "#app/data/battle-anims.js"; +import { Stat } from "#app/enums/stat.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { getTextColor, TextStyle } from "#app/ui/text.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next from "i18next"; +import { PokemonPhase } from "./pokemon-phase"; + +export class ScanIvsPhase extends PokemonPhase { + private shownIvs: integer; + + constructor(scene: BattleScene, battlerIndex: BattlerIndex, shownIvs: integer) { + super(scene, battlerIndex); + + this.shownIvs = shownIvs; + } + + start() { + super.start(); + + if (!this.shownIvs) { + return this.end(); + } + + const pokemon = this.getPokemon(); + + let enemyIvs: number[] = []; + let statsContainer: Phaser.GameObjects.Sprite[] = []; + let statsContainerLabels: Phaser.GameObjects.Sprite[] = []; + const enemyField = this.scene.getEnemyField(); + const uiTheme = (this.scene as BattleScene).uiTheme; // Assuming uiTheme is accessible + for (let e = 0; e < enemyField.length; e++) { + enemyIvs = enemyField[e].ivs; + const currentIvs = this.scene.gameData.dexData[enemyField[e].species.getRootSpeciesId()].ivs; // we are using getRootSpeciesId() here because we want to check against the baby form, not the mid form if it exists + const ivsToShow = this.scene.ui.getMessageHandler().getTopIvs(enemyIvs, this.shownIvs); + statsContainer = enemyField[e].getBattleInfo().getStatsValueContainer().list as Phaser.GameObjects.Sprite[]; + statsContainerLabels = statsContainer.filter(m => m.name.indexOf("icon_stat_label") >= 0); + for (let s = 0; s < statsContainerLabels.length; s++) { + const ivStat = Stat[statsContainerLabels[s].frame.name]; + if (enemyIvs[ivStat] > currentIvs[ivStat] && ivsToShow.indexOf(Number(ivStat)) >= 0) { + const hexColour = enemyIvs[ivStat] === 31 ? getTextColor(TextStyle.PERFECT_IV, false, uiTheme) : getTextColor(TextStyle.SUMMARY_GREEN, false, uiTheme); + const hexTextColour = Phaser.Display.Color.HexStringToColor(hexColour).color; + statsContainerLabels[s].setTint(hexTextColour); + } + statsContainerLabels[s].setVisible(true); + } + } + + if (!this.scene.hideIvs) { + this.scene.ui.showText(i18next.t("battle:ivScannerUseQuestion", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { + this.scene.ui.setMode(Mode.CONFIRM, () => { + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.clearText(); + new CommonBattleAnim(CommonAnim.LOCK_ON, pokemon, pokemon).play(this.scene, () => { + this.scene.ui.getMessageHandler().promptIvs(pokemon.id, pokemon.ivs, this.shownIvs).then(() => this.end()); + }); + }, () => { + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.clearText(); + this.end(); + }); + }); + } else { + this.end(); + } + } +} diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts new file mode 100644 index 00000000000..68c2cd29f26 --- /dev/null +++ b/src/phases/select-biome-phase.ts @@ -0,0 +1,84 @@ +import BattleScene from "#app/battle-scene.js"; +import { biomeLinks, getBiomeName } from "#app/data/biomes.js"; +import { Biome } from "#app/enums/biome.js"; +import { MoneyInterestModifier, MapModifier } from "#app/modifier/modifier.js"; +import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler.js"; +import { Mode } from "#app/ui/ui.js"; +import { BattlePhase } from "./battle-phase"; +import * as Utils from "#app/utils.js"; +import { PartyHealPhase } from "./party-heal-phase"; +import { SwitchBiomePhase } from "./switch-biome-phase"; + +export class SelectBiomePhase extends BattlePhase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + + const currentBiome = this.scene.arena.biomeType; + + const setNextBiome = (nextBiome: Biome) => { + if (this.scene.currentBattle.waveIndex % 10 === 1) { + this.scene.applyModifiers(MoneyInterestModifier, true, this.scene); + this.scene.unshiftPhase(new PartyHealPhase(this.scene, false)); + } + this.scene.unshiftPhase(new SwitchBiomePhase(this.scene, nextBiome)); + this.end(); + }; + + if ((this.scene.gameMode.isClassic && this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex + 9)) + || (this.scene.gameMode.isDaily && this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) + || (this.scene.gameMode.hasShortBiomes && !(this.scene.currentBattle.waveIndex % 50))) { + setNextBiome(Biome.END); + } else if (this.scene.gameMode.hasRandomBiomes) { + setNextBiome(this.generateNextBiome()); + } else if (Array.isArray(biomeLinks[currentBiome])) { + let biomes: Biome[] = []; + this.scene.executeWithSeedOffset(() => { + biomes = (biomeLinks[currentBiome] as (Biome | [Biome, integer])[]) + .filter(b => !Array.isArray(b) || !Utils.randSeedInt(b[1])) + .map(b => !Array.isArray(b) ? b : b[0]); + }, this.scene.currentBattle.waveIndex); + if (biomes.length > 1 && this.scene.findModifier(m => m instanceof MapModifier)) { + let biomeChoices: Biome[] = []; + this.scene.executeWithSeedOffset(() => { + biomeChoices = (!Array.isArray(biomeLinks[currentBiome]) + ? [biomeLinks[currentBiome] as Biome] + : biomeLinks[currentBiome] as (Biome | [Biome, integer])[]) + .filter((b, i) => !Array.isArray(b) || !Utils.randSeedInt(b[1])) + .map(b => Array.isArray(b) ? b[0] : b); + }, this.scene.currentBattle.waveIndex); + const biomeSelectItems = biomeChoices.map(b => { + const ret: OptionSelectItem = { + label: getBiomeName(b), + handler: () => { + this.scene.ui.setMode(Mode.MESSAGE); + setNextBiome(b); + return true; + } + }; + return ret; + }); + this.scene.ui.setMode(Mode.OPTION_SELECT, { + options: biomeSelectItems, + delay: 1000 + }); + } else { + setNextBiome(biomes[Utils.randSeedInt(biomes.length)]); + } + } else if (biomeLinks.hasOwnProperty(currentBiome)) { + setNextBiome(biomeLinks[currentBiome] as Biome); + } else { + setNextBiome(this.generateNextBiome()); + } + } + + generateNextBiome(): Biome { + if (!(this.scene.currentBattle.waveIndex % 50)) { + return Biome.END; + } + return this.scene.generateRandomBiome(this.scene.currentBattle.waveIndex); + } +} diff --git a/src/phases/select-challenge-phase.ts b/src/phases/select-challenge-phase.ts new file mode 100644 index 00000000000..eaf830e0059 --- /dev/null +++ b/src/phases/select-challenge-phase.ts @@ -0,0 +1,17 @@ +import BattleScene from "#app/battle-scene.js"; +import { Phase } from "#app/phase.js"; +import { Mode } from "#app/ui/ui.js"; + +export class SelectChallengePhase extends Phase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + + this.scene.playBgm("menu"); + + this.scene.ui.setMode(Mode.CHALLENGE_SELECT); + } +} diff --git a/src/phases/select-gender-phase.ts b/src/phases/select-gender-phase.ts new file mode 100644 index 00000000000..3fc6916e233 --- /dev/null +++ b/src/phases/select-gender-phase.ts @@ -0,0 +1,46 @@ +import BattleScene from "#app/battle-scene.js"; +import { PlayerGender } from "#app/enums/player-gender.js"; +import { Phase } from "#app/phase.js"; +import { SettingKeys } from "#app/system/settings/settings.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next from "i18next"; + +export class SelectGenderPhase extends Phase { + constructor(scene: BattleScene) { + super(scene); + } + + start(): void { + super.start(); + + this.scene.ui.showText(i18next.t("menu:boyOrGirl"), null, () => { + this.scene.ui.setMode(Mode.OPTION_SELECT, { + options: [ + { + label: i18next.t("settings:boy"), + handler: () => { + this.scene.gameData.gender = PlayerGender.MALE; + this.scene.gameData.saveSetting(SettingKeys.Player_Gender, 0); + this.scene.gameData.saveSystem().then(() => this.end()); + return true; + } + }, + { + label: i18next.t("settings:girl"), + handler: () => { + this.scene.gameData.gender = PlayerGender.FEMALE; + this.scene.gameData.saveSetting(SettingKeys.Player_Gender, 1); + this.scene.gameData.saveSystem().then(() => this.end()); + return true; + } + } + ] + }); + }); + } + + end(): void { + this.scene.ui.setMode(Mode.MESSAGE); + super.end(); + } +} diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts new file mode 100644 index 00000000000..57b842e7b38 --- /dev/null +++ b/src/phases/select-modifier-phase.ts @@ -0,0 +1,234 @@ +import BattleScene from "#app/battle-scene.js"; +import { ModifierTier } from "#app/modifier/modifier-tier.js"; +import { regenerateModifierPoolThresholds, ModifierTypeOption, ModifierType, getPlayerShopModifierTypeOptionsForWave, PokemonModifierType, FusePokemonModifierType, PokemonMoveModifierType, TmModifierType, RememberMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, ModifierPoolType, getPlayerModifierTypeOptions } from "#app/modifier/modifier-type.js"; +import { ExtraModifierModifier, Modifier, PokemonHeldItemModifier } from "#app/modifier/modifier.js"; +import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler.js"; +import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next from "i18next"; +import * as Utils from "#app/utils.js"; +import { BattlePhase } from "./battle-phase"; +import Overrides from "#app/overrides"; + +export class SelectModifierPhase extends BattlePhase { + private rerollCount: integer; + private modifierTiers: ModifierTier[]; + + constructor(scene: BattleScene, rerollCount: integer = 0, modifierTiers?: ModifierTier[]) { + super(scene); + + this.rerollCount = rerollCount; + this.modifierTiers = modifierTiers!; // TODO: is this bang correct? + } + + start() { + super.start(); + + if (!this.rerollCount) { + this.updateSeed(); + } else { + this.scene.reroll = false; + } + + const party = this.scene.getParty(); + regenerateModifierPoolThresholds(party, this.getPoolType(), this.rerollCount); + const modifierCount = new Utils.IntegerHolder(3); + if (this.isPlayer()) { + this.scene.applyModifiers(ExtraModifierModifier, true, modifierCount); + } + const typeOptions: ModifierTypeOption[] = this.getModifierTypeOptions(modifierCount.value); + + const modifierSelectCallback = (rowCursor: integer, cursor: integer) => { + if (rowCursor < 0 || cursor < 0) { + this.scene.ui.showText(i18next.t("battle:skipItemQuestion"), null, () => { + this.scene.ui.setOverlayMode(Mode.CONFIRM, () => { + this.scene.ui.revertMode(); + this.scene.ui.setMode(Mode.MESSAGE); + super.end(); + }, () => this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers))); + }); + return false; + } + let modifierType: ModifierType; + let cost: integer; + switch (rowCursor) { + case 0: + switch (cursor) { + case 0: + const rerollCost = this.getRerollCost(typeOptions, this.scene.lockModifierTiers); + if (this.scene.money < rerollCost) { + this.scene.ui.playError(); + return false; + } else { + this.scene.reroll = true; + this.scene.unshiftPhase(new SelectModifierPhase(this.scene, this.rerollCount + 1, typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as ModifierTier[])); + this.scene.ui.clearText(); + this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); + if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { + this.scene.money -= rerollCost; + this.scene.updateMoneyText(); + this.scene.animateMoneyChanged(false); + } + this.scene.playSound("buy"); + } + break; + case 1: + this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: integer, itemIndex: integer, itemQuantity: integer, toSlotIndex: integer) => { + if (toSlotIndex !== undefined && fromSlotIndex < 6 && toSlotIndex < 6 && fromSlotIndex !== toSlotIndex && itemIndex > -1) { + const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier + && m.isTransferrable && m.pokemonId === party[fromSlotIndex].id) as PokemonHeldItemModifier[]; + const itemModifier = itemModifiers[itemIndex]; + this.scene.tryTransferHeldItemModifier(itemModifier, party[toSlotIndex], true, itemQuantity); + } else { + this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); + } + }, PartyUiHandler.FilterItemMaxStacks); + break; + case 2: + this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.CHECK, -1, () => { + this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); + }); + break; + case 3: + this.scene.lockModifierTiers = !this.scene.lockModifierTiers; + const uiHandler = this.scene.ui.getHandler() as ModifierSelectUiHandler; + uiHandler.setRerollCost(this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); + uiHandler.updateLockRaritiesText(); + uiHandler.updateRerollCostText(); + return false; + } + return true; + case 1: + if (typeOptions[cursor].type) { + modifierType = typeOptions[cursor].type; + } + break; + default: + const shopOptions = getPlayerShopModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex, this.scene.getWaveMoneyAmount(1)); + const shopOption = shopOptions[rowCursor > 2 || shopOptions.length <= SHOP_OPTIONS_ROW_LIMIT ? cursor : cursor + SHOP_OPTIONS_ROW_LIMIT]; + if (shopOption.type) { + modifierType = shopOption.type; + } + cost = shopOption.cost; + break; + } + + if (cost! && (this.scene.money < cost) && !Overrides.WAIVE_ROLL_FEE_OVERRIDE) { // TODO: is the bang on cost correct? + this.scene.ui.playError(); + return false; + } + + const applyModifier = (modifier: Modifier, playSound: boolean = false) => { + const result = this.scene.addModifier(modifier, false, playSound); + if (cost) { + result.then(success => { + if (success) { + if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { + this.scene.money -= cost; + this.scene.updateMoneyText(); + this.scene.animateMoneyChanged(false); + } + this.scene.playSound("buy"); + (this.scene.ui.getHandler() as ModifierSelectUiHandler).updateCostText(); + } else { + this.scene.ui.playError(); + } + }); + } else { + const doEnd = () => { + this.scene.ui.clearText(); + this.scene.ui.setMode(Mode.MESSAGE); + super.end(); + }; + if (result instanceof Promise) { + result.then(() => doEnd()); + } else { + doEnd(); + } + } + }; + + if (modifierType! instanceof PokemonModifierType) { //TODO: is the bang correct? + if (modifierType instanceof FusePokemonModifierType) { + this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.SPLICE, -1, (fromSlotIndex: integer, spliceSlotIndex: integer) => { + if (spliceSlotIndex !== undefined && fromSlotIndex < 6 && spliceSlotIndex < 6 && fromSlotIndex !== spliceSlotIndex) { + this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer()).then(() => { + const modifier = modifierType.newModifier(party[fromSlotIndex], party[spliceSlotIndex])!; //TODO: is the bang correct? + applyModifier(modifier, true); + }); + } else { + this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); + } + }, modifierType.selectFilter); + } else { + const pokemonModifierType = modifierType as PokemonModifierType; + const isMoveModifier = modifierType instanceof PokemonMoveModifierType; + const isTmModifier = modifierType instanceof TmModifierType; + const isRememberMoveModifier = modifierType instanceof RememberMoveModifierType; + const isPpRestoreModifier = (modifierType instanceof PokemonPpRestoreModifierType || modifierType instanceof PokemonPpUpModifierType); + const partyUiMode = isMoveModifier ? PartyUiMode.MOVE_MODIFIER + : isTmModifier ? PartyUiMode.TM_MODIFIER + : isRememberMoveModifier ? PartyUiMode.REMEMBER_MOVE_MODIFIER + : PartyUiMode.MODIFIER; + const tmMoveId = isTmModifier + ? (modifierType as TmModifierType).moveId + : undefined; + this.scene.ui.setModeWithoutClear(Mode.PARTY, partyUiMode, -1, (slotIndex: integer, option: PartyOption) => { + if (slotIndex < 6) { + this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer()).then(() => { + const modifier = !isMoveModifier + ? !isRememberMoveModifier + ? modifierType.newModifier(party[slotIndex]) + : modifierType.newModifier(party[slotIndex], option as integer) + : modifierType.newModifier(party[slotIndex], option - PartyOption.MOVE_1); + applyModifier(modifier!, true); // TODO: is the bang correct? + }); + } else { + this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); + } + }, pokemonModifierType.selectFilter, modifierType instanceof PokemonMoveModifierType ? (modifierType as PokemonMoveModifierType).moveSelectFilter : undefined, tmMoveId, isPpRestoreModifier); + } + } else { + applyModifier(modifierType!.newModifier()!); // TODO: is the bang correct? + } + + return !cost!;// TODO: is the bang correct? + }; + this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); + } + + updateSeed(): void { + this.scene.resetSeed(); + } + + isPlayer(): boolean { + return true; + } + + getRerollCost(typeOptions: ModifierTypeOption[], lockRarities: boolean): number { + let baseValue = 0; + if (Overrides.WAIVE_ROLL_FEE_OVERRIDE) { + return baseValue; + } else if (lockRarities) { + const tierValues = [50, 125, 300, 750, 2000]; + for (const opt of typeOptions) { + baseValue += tierValues[opt.type.tier ?? 0]; + } + } else { + baseValue = 250; + } + return Math.min(Math.ceil(this.scene.currentBattle.waveIndex / 10) * baseValue * Math.pow(2, this.rerollCount), Number.MAX_SAFE_INTEGER); + } + + getPoolType(): ModifierPoolType { + return ModifierPoolType.PLAYER; + } + + getModifierTypeOptions(modifierCount: integer): ModifierTypeOption[] { + return getPlayerModifierTypeOptions(modifierCount, this.scene.getParty(), this.scene.lockModifierTiers ? this.modifierTiers : undefined); + } + + addModifier(modifier: Modifier): Promise { + return this.scene.addModifier(modifier, false, true); + } +} diff --git a/src/phases/select-starter-phase.ts b/src/phases/select-starter-phase.ts new file mode 100644 index 00000000000..ad972a49225 --- /dev/null +++ b/src/phases/select-starter-phase.ts @@ -0,0 +1,112 @@ +import BattleScene from "#app/battle-scene.js"; +import { applyChallenges, ChallengeType } from "#app/data/challenge.js"; +import { Gender } from "#app/data/gender.js"; +import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms.js"; +import { getPokemonSpecies } from "#app/data/pokemon-species.js"; +import { Species } from "#app/enums/species.js"; +import { PlayerPokemon } from "#app/field/pokemon.js"; +import { overrideModifiers, overrideHeldItems } from "#app/modifier/modifier.js"; +import { Phase } from "#app/phase.js"; +import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler.js"; +import { Starter } from "#app/ui/starter-select-ui-handler.js"; +import { Mode } from "#app/ui/ui.js"; +import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; +import { TitlePhase } from "./title-phase"; +import Overrides from "#app/overrides"; + +export class SelectStarterPhase extends Phase { + + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + + this.scene.playBgm("menu"); + + this.scene.ui.setMode(Mode.STARTER_SELECT, (starters: Starter[]) => { + this.scene.ui.clearText(); + this.scene.ui.setMode(Mode.SAVE_SLOT, SaveSlotUiMode.SAVE, (slotId: integer) => { + if (slotId === -1) { + this.scene.clearPhaseQueue(); + this.scene.pushPhase(new TitlePhase(this.scene)); + return this.end(); + } + this.scene.sessionSlotId = slotId; + this.initBattle(starters); + }); + }); + } + + /** + * Initialize starters before starting the first battle + * @param starters {@linkcode Pokemon} with which to start the first battle + */ + initBattle(starters: Starter[]) { + const party = this.scene.getParty(); + const loadPokemonAssets: Promise[] = []; + starters.forEach((starter: Starter, i: integer) => { + if (!i && Overrides.STARTER_SPECIES_OVERRIDE) { + starter.species = getPokemonSpecies(Overrides.STARTER_SPECIES_OVERRIDE as Species); + } + const starterProps = this.scene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr); + let starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0)); + if ( + starter.species.speciesId in Overrides.STARTER_FORM_OVERRIDES && + starter.species.forms[Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!] + ) { + starterFormIndex = Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!; + } + + let starterGender = starter.species.malePercent !== null + ? !starterProps.female ? Gender.MALE : Gender.FEMALE + : Gender.GENDERLESS; + if (Overrides.GENDER_OVERRIDE !== null) { + starterGender = Overrides.GENDER_OVERRIDE; + } + const starterIvs = this.scene.gameData.dexData[starter.species.speciesId].ivs.slice(0); + const starterPokemon = this.scene.addPlayerPokemon(starter.species, this.scene.gameMode.getStartingLevel(), starter.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, starterProps.variant, starterIvs, starter.nature); + starter.moveset && starterPokemon.tryPopulateMoveset(starter.moveset); + if (starter.passive) { + starterPokemon.passive = true; + } + starterPokemon.luck = this.scene.gameData.getDexAttrLuck(this.scene.gameData.dexData[starter.species.speciesId].caughtAttr); + if (starter.pokerus) { + starterPokemon.pokerus = true; + } + + if (starter.nickname) { + starterPokemon.nickname = starter.nickname; + } + + if (this.scene.gameMode.isSplicedOnly) { + starterPokemon.generateFusionSpecies(true); + } + starterPokemon.setVisible(false); + applyChallenges(this.scene.gameMode, ChallengeType.STARTER_MODIFY, starterPokemon); + party.push(starterPokemon); + loadPokemonAssets.push(starterPokemon.loadAssets()); + }); + overrideModifiers(this.scene); + overrideHeldItems(this.scene, party[0]); + Promise.all(loadPokemonAssets).then(() => { + SoundFade.fadeOut(this.scene, this.scene.sound.get("menu"), 500, true); + this.scene.time.delayedCall(500, () => this.scene.playBgm()); + if (this.scene.gameMode.isClassic) { + this.scene.gameData.gameStats.classicSessionsPlayed++; + } else { + this.scene.gameData.gameStats.endlessSessionsPlayed++; + } + this.scene.newBattle(); + this.scene.arena.init(); + this.scene.sessionPlayTime = 0; + this.scene.lastSavePlayTime = 0; + // Ensures Keldeo (or any future Pokemon that have this type of form change) starts in the correct form + this.scene.getParty().forEach((p: PlayerPokemon) => { + this.scene.triggerPokemonFormChange(p, SpeciesFormChangeMoveLearnedTrigger); + }); + this.end(); + }); + } +} diff --git a/src/phases/select-target-phase.ts b/src/phases/select-target-phase.ts new file mode 100644 index 00000000000..fe72335e312 --- /dev/null +++ b/src/phases/select-target-phase.ts @@ -0,0 +1,32 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { Command } from "#app/ui/command-ui-handler.js"; +import { Mode } from "#app/ui/ui.js"; +import { CommandPhase } from "./command-phase"; +import { PokemonPhase } from "./pokemon-phase"; + +export class SelectTargetPhase extends PokemonPhase { + constructor(scene: BattleScene, fieldIndex: integer) { + super(scene, fieldIndex); + } + + start() { + super.start(); + + const turnCommand = this.scene.currentBattle.turnCommands[this.fieldIndex]; + const move = turnCommand?.move?.move; + this.scene.ui.setMode(Mode.TARGET_SELECT, this.fieldIndex, move, (targets: BattlerIndex[]) => { + this.scene.ui.setMode(Mode.MESSAGE); + if (targets.length < 1) { + this.scene.currentBattle.turnCommands[this.fieldIndex] = null; + this.scene.unshiftPhase(new CommandPhase(this.scene, this.fieldIndex)); + } else { + turnCommand!.targets = targets; //TODO: is the bang correct here? + } + if (turnCommand?.command === Command.BALL && this.fieldIndex) { + this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; //TODO: is the bang correct here? + } + this.end(); + }); + } +} diff --git a/src/phases/shiny-sparkle-phase.ts b/src/phases/shiny-sparkle-phase.ts new file mode 100644 index 00000000000..4cd2b68f881 --- /dev/null +++ b/src/phases/shiny-sparkle-phase.ts @@ -0,0 +1,16 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { PokemonPhase } from "./pokemon-phase"; + +export class ShinySparklePhase extends PokemonPhase { + constructor(scene: BattleScene, battlerIndex: BattlerIndex) { + super(scene, battlerIndex); + } + + start() { + super.start(); + + this.getPokemon().sparkle(); + this.scene.time.delayedCall(1000, () => this.end()); + } +} diff --git a/src/phases/show-ability-phase.ts b/src/phases/show-ability-phase.ts new file mode 100644 index 00000000000..ee0b98f7886 --- /dev/null +++ b/src/phases/show-ability-phase.ts @@ -0,0 +1,29 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { PokemonPhase } from "./pokemon-phase"; + +export class ShowAbilityPhase extends PokemonPhase { + private passive: boolean; + + constructor(scene: BattleScene, battlerIndex: BattlerIndex, passive: boolean = false) { + super(scene, battlerIndex); + + this.passive = passive; + } + + start() { + super.start(); + + const pokemon = this.getPokemon(); + + if (pokemon) { + this.scene.abilityBar.showAbility(pokemon, this.passive); + + if (pokemon?.battleData) { + pokemon.battleData.abilityRevealed = true; + } + } + + this.end(); + } +} diff --git a/src/phases/show-party-exp-bar-phase.ts b/src/phases/show-party-exp-bar-phase.ts new file mode 100644 index 00000000000..9920472e801 --- /dev/null +++ b/src/phases/show-party-exp-bar-phase.ts @@ -0,0 +1,56 @@ +import BattleScene from "#app/battle-scene.js"; +import { ExpNotification } from "#app/enums/exp-notification.js"; +import { ExpBoosterModifier } from "#app/modifier/modifier.js"; +import * as Utils from "#app/utils.js"; +import { HidePartyExpBarPhase } from "./hide-party-exp-bar-phase"; +import { LevelUpPhase } from "./level-up-phase"; +import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; + +export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase { + private expValue: number; + + constructor(scene: BattleScene, partyMemberIndex: integer, expValue: number) { + super(scene, partyMemberIndex); + + this.expValue = expValue; + } + + start() { + super.start(); + + const pokemon = this.getPokemon(); + const exp = new Utils.NumberHolder(this.expValue); + this.scene.applyModifiers(ExpBoosterModifier, true, exp); + exp.value = Math.floor(exp.value); + + const lastLevel = pokemon.level; + pokemon.addExp(exp.value); + const newLevel = pokemon.level; + if (newLevel > lastLevel) { + this.scene.unshiftPhase(new LevelUpPhase(this.scene, this.partyMemberIndex, lastLevel, newLevel)); + } + this.scene.unshiftPhase(new HidePartyExpBarPhase(this.scene)); + pokemon.updateInfo(); + + if (this.scene.expParty === ExpNotification.SKIP) { + this.end(); + } else if (this.scene.expParty === ExpNotification.ONLY_LEVEL_UP) { + if (newLevel > lastLevel) { // this means if we level up + // instead of displaying the exp gain in the small frame, we display the new level + // we use the same method for mode 0 & 1, by giving a parameter saying to display the exp or the level + this.scene.partyExpBar.showPokemonExp(pokemon, exp.value, this.scene.expParty === ExpNotification.ONLY_LEVEL_UP, newLevel).then(() => { + setTimeout(() => this.end(), 800 / Math.pow(2, this.scene.expGainsSpeed)); + }); + } else { + this.end(); + } + } else if (this.scene.expGainsSpeed < 3) { + this.scene.partyExpBar.showPokemonExp(pokemon, exp.value, false, newLevel).then(() => { + setTimeout(() => this.end(), 500 / Math.pow(2, this.scene.expGainsSpeed)); + }); + } else { + this.end(); + } + + } +} diff --git a/src/phases/show-trainer-phase.ts b/src/phases/show-trainer-phase.ts new file mode 100644 index 00000000000..8a869f582d8 --- /dev/null +++ b/src/phases/show-trainer-phase.ts @@ -0,0 +1,24 @@ +import BattleScene from "#app/battle-scene.js"; +import { PlayerGender } from "#app/enums/player-gender.js"; +import { BattlePhase } from "./battle-phase"; + +export class ShowTrainerPhase extends BattlePhase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + + this.scene.trainer.setVisible(true); + + this.scene.trainer.setTexture(`trainer_${this.scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`); + + this.scene.tweens.add({ + targets: this.scene.trainer, + x: 106, + duration: 1000, + onComplete: () => this.end() + }); + } +} diff --git a/src/phases/stat-change-phase.ts b/src/phases/stat-change-phase.ts new file mode 100644 index 00000000000..fec3da9bc9a --- /dev/null +++ b/src/phases/stat-change-phase.ts @@ -0,0 +1,234 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { applyPreStatChangeAbAttrs, ProtectStatAbAttr, applyAbAttrs, StatChangeMultiplierAbAttr, StatChangeCopyAbAttr, applyPostStatChangeAbAttrs, PostStatChangeAbAttr } from "#app/data/ability.js"; +import { MistTag, ArenaTagSide } from "#app/data/arena-tag.js"; +import { BattleStat, getBattleStatName, getBattleStatLevelChangeDescription } from "#app/data/battle-stat.js"; +import Pokemon from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { PokemonResetNegativeStatStageModifier } from "#app/modifier/modifier.js"; +import { handleTutorial, Tutorial } from "#app/tutorial.js"; +import i18next from "i18next"; +import * as Utils from "#app/utils.js"; +import { PokemonPhase } from "./pokemon-phase"; + +export type StatChangeCallback = (target: Pokemon | null, changed: BattleStat[], relativeChanges: number[]) => void; + +export class StatChangePhase extends PokemonPhase { + private stats: BattleStat[]; + private selfTarget: boolean; + private levels: integer; + private showMessage: boolean; + private ignoreAbilities: boolean; + private canBeCopied: boolean; + private onChange: StatChangeCallback | null; + + + constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], levels: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true, onChange: StatChangeCallback | null = null) { + super(scene, battlerIndex); + + this.selfTarget = selfTarget; + this.stats = stats; + this.levels = levels; + this.showMessage = showMessage; + this.ignoreAbilities = ignoreAbilities; + this.canBeCopied = canBeCopied; + this.onChange = onChange; + } + + start() { + const pokemon = this.getPokemon(); + + let random = false; + + if (this.stats.length === 1 && this.stats[0] === BattleStat.RAND) { + this.stats[0] = this.getRandomStat(); + random = true; + } + + this.aggregateStatChanges(random); + + if (!pokemon.isActive(true)) { + return this.end(); + } + + const filteredStats = this.stats.map(s => s !== BattleStat.RAND ? s : this.getRandomStat()).filter(stat => { + const cancelled = new Utils.BooleanHolder(false); + + if (!this.selfTarget && this.levels < 0) { + this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, cancelled); + } + + if (!cancelled.value && !this.selfTarget && this.levels < 0) { + applyPreStatChangeAbAttrs(ProtectStatAbAttr, this.getPokemon(), stat, cancelled); + } + + return !cancelled.value; + }); + + const levels = new Utils.IntegerHolder(this.levels); + + if (!this.ignoreAbilities) { + applyAbAttrs(StatChangeMultiplierAbAttr, pokemon, null, false, levels); + } + + const battleStats = this.getPokemon().summonData.battleStats; + const relLevels = filteredStats.map(stat => (levels.value >= 1 ? Math.min(battleStats![stat] + levels.value, 6) : Math.max(battleStats![stat] + levels.value, -6)) - battleStats![stat]); + + this.onChange && this.onChange(this.getPokemon(), filteredStats, relLevels); + + const end = () => { + if (this.showMessage) { + const messages = this.getStatChangeMessages(filteredStats, levels.value, relLevels); + for (const message of messages) { + this.scene.queueMessage(message); + } + } + + for (const stat of filteredStats) { + pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + levels.value, 6), -6); + } + + if (levels.value > 0 && this.canBeCopied) { + for (const opponent of pokemon.getOpponents()) { + applyAbAttrs(StatChangeCopyAbAttr, opponent, null, false, this.stats, levels.value); + } + } + + applyPostStatChangeAbAttrs(PostStatChangeAbAttr, pokemon, filteredStats, this.levels, this.selfTarget); + + // Look for any other stat change phases; if this is the last one, do White Herb check + const existingPhase = this.scene.findPhase(p => p instanceof StatChangePhase && p.battlerIndex === this.battlerIndex); + if (!(existingPhase instanceof StatChangePhase)) { + // Apply White Herb if needed + const whiteHerb = this.scene.applyModifier(PokemonResetNegativeStatStageModifier, this.player, pokemon) as PokemonResetNegativeStatStageModifier; + // If the White Herb was applied, consume it + if (whiteHerb) { + --whiteHerb.stackCount; + if (whiteHerb.stackCount <= 0) { + this.scene.removeModifier(whiteHerb); + } + this.scene.updateModifiers(this.player); + } + } + + pokemon.updateInfo(); + + handleTutorial(this.scene, Tutorial.Stat_Change).then(() => super.end()); + }; + + if (relLevels.filter(l => l).length && this.scene.moveAnimations) { + pokemon.enableMask(); + const pokemonMaskSprite = pokemon.maskSprite; + + const tileX = (this.player ? 106 : 236) * pokemon.getSpriteScale() * this.scene.field.scale; + const tileY = ((this.player ? 148 : 84) + (levels.value >= 1 ? 160 : 0)) * pokemon.getSpriteScale() * this.scene.field.scale; + const tileWidth = 156 * this.scene.field.scale * pokemon.getSpriteScale(); + const tileHeight = 316 * this.scene.field.scale * pokemon.getSpriteScale(); + + // On increase, show the red sprite located at ATK + // On decrease, show the blue sprite located at SPD + const spriteColor = levels.value >= 1 ? BattleStat[BattleStat.ATK].toLowerCase() : BattleStat[BattleStat.SPD].toLowerCase(); + const statSprite = this.scene.add.tileSprite(tileX, tileY, tileWidth, tileHeight, "battle_stats", spriteColor); + statSprite.setPipeline(this.scene.fieldSpritePipeline); + statSprite.setAlpha(0); + statSprite.setScale(6); + statSprite.setOrigin(0.5, 1); + + this.scene.playSound(`stat_${levels.value >= 1 ? "up" : "down"}`); + + statSprite.setMask(new Phaser.Display.Masks.BitmapMask(this.scene, pokemonMaskSprite ?? undefined)); + + this.scene.tweens.add({ + targets: statSprite, + duration: 250, + alpha: 0.8375, + onComplete: () => { + this.scene.tweens.add({ + targets: statSprite, + delay: 1000, + duration: 250, + alpha: 0 + }); + } + }); + + this.scene.tweens.add({ + targets: statSprite, + duration: 1500, + y: `${levels.value >= 1 ? "-" : "+"}=${160 * 6}` + }); + + this.scene.time.delayedCall(1750, () => { + pokemon.disableMask(); + end(); + }); + } else { + end(); + } + } + + getRandomStat(): BattleStat { + const allStats = Utils.getEnumValues(BattleStat); + return this.getPokemon() ? allStats[this.getPokemon()!.randSeedInt(BattleStat.SPD + 1)] : BattleStat.ATK; // TODO: return default ATK on random? idk... + } + + aggregateStatChanges(random: boolean = false): void { + const isAccEva = [BattleStat.ACC, BattleStat.EVA].some(s => this.stats.includes(s)); + let existingPhase: StatChangePhase; + if (this.stats.length === 1) { + while ((existingPhase = (this.scene.findPhase(p => p instanceof StatChangePhase && p.battlerIndex === this.battlerIndex && p.stats.length === 1 + && (p.stats[0] === this.stats[0] || (random && p.stats[0] === BattleStat.RAND)) + && p.selfTarget === this.selfTarget && p.showMessage === this.showMessage && p.ignoreAbilities === this.ignoreAbilities) as StatChangePhase))) { + if (existingPhase.stats[0] === BattleStat.RAND) { + existingPhase.stats[0] = this.getRandomStat(); + if (existingPhase.stats[0] !== this.stats[0]) { + continue; + } + } + this.levels += existingPhase.levels; + + if (!this.scene.tryRemovePhase(p => p === existingPhase)) { + break; + } + } + } + while ((existingPhase = (this.scene.findPhase(p => p instanceof StatChangePhase && p.battlerIndex === this.battlerIndex && p.selfTarget === this.selfTarget + && ([BattleStat.ACC, BattleStat.EVA].some(s => p.stats.includes(s)) === isAccEva) + && p.levels === this.levels && p.showMessage === this.showMessage && p.ignoreAbilities === this.ignoreAbilities) as StatChangePhase))) { + this.stats.push(...existingPhase.stats); + if (!this.scene.tryRemovePhase(p => p === existingPhase)) { + break; + } + } + } + + getStatChangeMessages(stats: BattleStat[], levels: integer, relLevels: integer[]): string[] { + const messages: string[] = []; + + const relLevelStatIndexes = {}; + for (let rl = 0; rl < relLevels.length; rl++) { + const relLevel = relLevels[rl]; + if (!relLevelStatIndexes[relLevel]) { + relLevelStatIndexes[relLevel] = []; + } + relLevelStatIndexes[relLevel].push(rl); + } + + Object.keys(relLevelStatIndexes).forEach(rl => { + const relLevelStats = stats.filter((_, i) => relLevelStatIndexes[rl].includes(i)); + let statsFragment = ""; + + if (relLevelStats.length > 1) { + statsFragment = relLevelStats.length >= 5 + ? i18next.t("battle:stats") + : `${relLevelStats.slice(0, -1).map(s => getBattleStatName(s)).join(", ")}${relLevelStats.length > 2 ? "," : ""} ${i18next.t("battle:statsAnd")} ${getBattleStatName(relLevelStats[relLevelStats.length - 1])}`; + messages.push(getBattleStatLevelChangeDescription(getPokemonNameWithAffix(this.getPokemon()), statsFragment, Math.abs(parseInt(rl)), levels >= 1,relLevelStats.length)); + } else { + statsFragment = getBattleStatName(relLevelStats[0]); + messages.push(getBattleStatLevelChangeDescription(getPokemonNameWithAffix(this.getPokemon()), statsFragment, Math.abs(parseInt(rl)), levels >= 1,relLevelStats.length)); + } + }); + + return messages; + } +} diff --git a/src/phases/summon-missing-phase.ts b/src/phases/summon-missing-phase.ts new file mode 100644 index 00000000000..bb9607285ad --- /dev/null +++ b/src/phases/summon-missing-phase.ts @@ -0,0 +1,15 @@ +import BattleScene from "#app/battle-scene.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import i18next from "i18next"; +import { SummonPhase } from "./summon-phase"; + +export class SummonMissingPhase extends SummonPhase { + constructor(scene: BattleScene, fieldIndex: integer) { + super(scene, fieldIndex); + } + + preSummon(): void { + this.scene.ui.showText(i18next.t("battle:sendOutPokemon", { pokemonName: getPokemonNameWithAffix(this.getPokemon()) })); + this.scene.time.delayedCall(250, () => this.summon()); + } +} diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts new file mode 100644 index 00000000000..50424170ea7 --- /dev/null +++ b/src/phases/summon-phase.ts @@ -0,0 +1,194 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattleType } from "#app/battle.js"; +import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball.js"; +import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms.js"; +import { TrainerSlot } from "#app/data/trainer-config.js"; +import { PlayerGender } from "#app/enums/player-gender.js"; +import { addPokeballOpenParticles } from "#app/field/anims.js"; +import Pokemon, { FieldPosition } from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import i18next from "i18next"; +import { PartyMemberPokemonPhase } from "./party-member-pokemon-phase"; +import { PostSummonPhase } from "./post-summon-phase"; +import { GameOverPhase } from "./game-over-phase"; +import { ShinySparklePhase } from "./shiny-sparkle-phase"; + +export class SummonPhase extends PartyMemberPokemonPhase { + private loaded: boolean; + + constructor(scene: BattleScene, fieldIndex: integer, player: boolean = true, loaded: boolean = false) { + super(scene, fieldIndex, player); + + this.loaded = loaded; + } + + start() { + super.start(); + + this.preSummon(); + } + + /** + * Sends out a Pokemon before the battle begins and shows the appropriate messages + */ + preSummon(): void { + const partyMember = this.getPokemon(); + // If the Pokemon about to be sent out is fainted or illegal under a challenge, switch to the first non-fainted legal Pokemon + if (!partyMember.isAllowedInBattle()) { + console.warn("The Pokemon about to be sent out is fainted or illegal under a challenge. Attempting to resolve..."); + + // First check if they're somehow still in play, if so remove them. + if (partyMember.isOnField()) { + partyMember.leaveField(); + } + + const party = this.getParty(); + + // Find the first non-fainted Pokemon index above the current one + const legalIndex = party.findIndex((p, i) => i > this.partyMemberIndex && p.isAllowedInBattle()); + if (legalIndex === -1) { + console.error("Party Details:\n", party); + console.error("All available Pokemon were fainted or illegal!"); + this.scene.clearPhaseQueue(); + this.scene.unshiftPhase(new GameOverPhase(this.scene)); + this.end(); + return; + } + + // Swaps the fainted Pokemon and the first non-fainted legal Pokemon in the party + [party[this.partyMemberIndex], party[legalIndex]] = [party[legalIndex], party[this.partyMemberIndex]]; + console.warn("Swapped %s %O with %s %O", getPokemonNameWithAffix(partyMember), partyMember, getPokemonNameWithAffix(party[0]), party[0]); + } + + if (this.player) { + this.scene.ui.showText(i18next.t("battle:playerGo", { pokemonName: getPokemonNameWithAffix(this.getPokemon()) })); + if (this.player) { + this.scene.pbTray.hide(); + } + this.scene.trainer.setTexture(`trainer_${this.scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`); + this.scene.time.delayedCall(562, () => { + this.scene.trainer.setFrame("2"); + this.scene.time.delayedCall(64, () => { + this.scene.trainer.setFrame("3"); + }); + }); + this.scene.tweens.add({ + targets: this.scene.trainer, + x: -36, + duration: 1000, + onComplete: () => this.scene.trainer.setVisible(false) + }); + this.scene.time.delayedCall(750, () => this.summon()); + } else { + const trainerName = this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); + const pokemonName = this.getPokemon().getNameToRender(); + const message = i18next.t("battle:trainerSendOut", { trainerName, pokemonName }); + + this.scene.pbTrayEnemy.hide(); + this.scene.ui.showText(message, null, () => this.summon()); + } + } + + summon(): void { + const pokemon = this.getPokemon(); + + const pokeball = this.scene.addFieldSprite(this.player ? 36 : 248, this.player ? 80 : 44, "pb", getPokeballAtlasKey(pokemon.pokeball)); + pokeball.setVisible(false); + pokeball.setOrigin(0.5, 0.625); + this.scene.field.add(pokeball); + + if (this.fieldIndex === 1) { + pokemon.setFieldPosition(FieldPosition.RIGHT, 0); + } else { + const availablePartyMembers = this.getParty().filter(p => p.isAllowedInBattle()).length; + pokemon.setFieldPosition(!this.scene.currentBattle.double || availablePartyMembers === 1 ? FieldPosition.CENTER : FieldPosition.LEFT); + } + + const fpOffset = pokemon.getFieldPositionOffset(); + + pokeball.setVisible(true); + + this.scene.tweens.add({ + targets: pokeball, + duration: 650, + x: (this.player ? 100 : 236) + fpOffset[0] + }); + + this.scene.tweens.add({ + targets: pokeball, + duration: 150, + ease: "Cubic.easeOut", + y: (this.player ? 70 : 34) + fpOffset[1], + onComplete: () => { + this.scene.tweens.add({ + targets: pokeball, + duration: 500, + ease: "Cubic.easeIn", + angle: 1440, + y: (this.player ? 132 : 86) + fpOffset[1], + onComplete: () => { + this.scene.playSound("pb_rel"); + pokeball.destroy(); + this.scene.add.existing(pokemon); + this.scene.field.add(pokemon); + if (!this.player) { + const playerPokemon = this.scene.getPlayerPokemon() as Pokemon; + if (playerPokemon?.visible) { + this.scene.field.moveBelow(pokemon, playerPokemon); + } + this.scene.currentBattle.seenEnemyPartyMemberIds.add(pokemon.id); + } + addPokeballOpenParticles(this.scene, pokemon.x, pokemon.y - 16, pokemon.pokeball); + this.scene.updateModifiers(this.player); + this.scene.updateFieldScale(); + pokemon.showInfo(); + pokemon.playAnim(); + pokemon.setVisible(true); + pokemon.getSprite().setVisible(true); + pokemon.setScale(0.5); + pokemon.tint(getPokeballTintColor(pokemon.pokeball)); + pokemon.untint(250, "Sine.easeIn"); + this.scene.updateFieldScale(); + this.scene.tweens.add({ + targets: pokemon, + duration: 250, + ease: "Sine.easeIn", + scale: pokemon.getSpriteScale(), + onComplete: () => { + pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 }); + pokemon.getSprite().clearTint(); + pokemon.resetSummonData(); + this.scene.time.delayedCall(1000, () => this.end()); + } + }); + } + }); + } + }); + } + + onEnd(): void { + const pokemon = this.getPokemon(); + + if (pokemon.isShiny()) { + this.scene.unshiftPhase(new ShinySparklePhase(this.scene, pokemon.getBattlerIndex())); + } + + pokemon.resetTurnData(); + + if (!this.loaded || this.scene.currentBattle.battleType === BattleType.TRAINER || (this.scene.currentBattle.waveIndex % 10) === 1) { + this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true); + this.queuePostSummon(); + } + } + + queuePostSummon(): void { + this.scene.pushPhase(new PostSummonPhase(this.scene, this.getPokemon().getBattlerIndex())); + } + + end() { + this.onEnd(); + + super.end(); + } +} diff --git a/src/phases/switch-biome-phase.ts b/src/phases/switch-biome-phase.ts new file mode 100644 index 00000000000..f20cd59b240 --- /dev/null +++ b/src/phases/switch-biome-phase.ts @@ -0,0 +1,65 @@ +import BattleScene from "#app/battle-scene.js"; +import { Biome } from "#app/enums/biome.js"; +import { getBiomeKey } from "#app/field/arena.js"; +import { BattlePhase } from "./battle-phase"; + +export class SwitchBiomePhase extends BattlePhase { + private nextBiome: Biome; + + constructor(scene: BattleScene, nextBiome: Biome) { + super(scene); + + this.nextBiome = nextBiome; + } + + start() { + super.start(); + + if (this.nextBiome === undefined) { + return this.end(); + } + + this.scene.tweens.add({ + targets: [this.scene.arenaEnemy, this.scene.lastEnemyTrainer], + x: "+=300", + duration: 2000, + onComplete: () => { + this.scene.arenaEnemy.setX(this.scene.arenaEnemy.x - 600); + + this.scene.newArena(this.nextBiome); + + const biomeKey = getBiomeKey(this.nextBiome); + const bgTexture = `${biomeKey}_bg`; + this.scene.arenaBgTransition.setTexture(bgTexture); + this.scene.arenaBgTransition.setAlpha(0); + this.scene.arenaBgTransition.setVisible(true); + this.scene.arenaPlayerTransition.setBiome(this.nextBiome); + this.scene.arenaPlayerTransition.setAlpha(0); + this.scene.arenaPlayerTransition.setVisible(true); + + this.scene.tweens.add({ + targets: [this.scene.arenaPlayer, this.scene.arenaBgTransition, this.scene.arenaPlayerTransition], + duration: 1000, + delay: 1000, + ease: "Sine.easeInOut", + alpha: (target: any) => target === this.scene.arenaPlayer ? 0 : 1, + onComplete: () => { + this.scene.arenaBg.setTexture(bgTexture); + this.scene.arenaPlayer.setBiome(this.nextBiome); + this.scene.arenaPlayer.setAlpha(1); + this.scene.arenaEnemy.setBiome(this.nextBiome); + this.scene.arenaEnemy.setAlpha(1); + this.scene.arenaNextEnemy.setBiome(this.nextBiome); + this.scene.arenaBgTransition.setVisible(false); + this.scene.arenaPlayerTransition.setVisible(false); + if (this.scene.lastEnemyTrainer) { + this.scene.lastEnemyTrainer.destroy(); + } + + this.end(); + } + }); + } + }); + } +} diff --git a/src/phases/switch-phase.ts b/src/phases/switch-phase.ts new file mode 100644 index 00000000000..93b0943febf --- /dev/null +++ b/src/phases/switch-phase.ts @@ -0,0 +1,65 @@ +import BattleScene from "#app/battle-scene.js"; +import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler.js"; +import { Mode } from "#app/ui/ui.js"; +import { BattlePhase } from "./battle-phase"; +import { SwitchSummonPhase } from "./switch-summon-phase"; + +/** + * Opens the party selector UI and transitions into a {@linkcode SwitchSummonPhase} + * for the player (if a switch would be valid for the current battle state). + */ +export class SwitchPhase extends BattlePhase { + protected fieldIndex: integer; + private isModal: boolean; + private doReturn: boolean; + + /** + * Creates a new SwitchPhase + * @param scene {@linkcode BattleScene} Current battle scene + * @param fieldIndex Field index to switch out + * @param isModal Indicates if the switch should be forced (true) or is + * optional (false). + * @param doReturn Indicates if the party member on the field should be + * recalled to ball or has already left the field. Passed to {@linkcode SwitchSummonPhase}. + */ + constructor(scene: BattleScene, fieldIndex: integer, isModal: boolean, doReturn: boolean) { + super(scene); + + this.fieldIndex = fieldIndex; + this.isModal = isModal; + this.doReturn = doReturn; + } + + start() { + super.start(); + + // Skip modal switch if impossible (no remaining party members that aren't in battle) + if (this.isModal && !this.scene.getParty().filter(p => p.isAllowedInBattle() && !p.isActive(true)).length) { + return super.end(); + } + + // Skip if the fainted party member has been revived already. doReturn is + // only passed as `false` from FaintPhase (as opposed to other usages such + // as ForceSwitchOutAttr or CheckSwitchPhase), so we only want to check this + // if the mon should have already been returned but is still alive and well + // on the field. see also; battle.test.ts + if (this.isModal && !this.doReturn && !this.scene.getParty()[this.fieldIndex].isFainted()) { + return super.end(); + } + + // Check if there is any space still in field + if (this.isModal && this.scene.getPlayerField().filter(p => p.isAllowedInBattle() && p.isActive(true)).length >= this.scene.currentBattle.getBattlerCount()) { + return super.end(); + } + + // Override field index to 0 in case of double battle where 2/3 remaining legal party members fainted at once + const fieldIndex = this.scene.currentBattle.getBattlerCount() === 1 || this.scene.getParty().filter(p => p.isAllowedInBattle()).length > 1 ? this.fieldIndex : 0; + + this.scene.ui.setMode(Mode.PARTY, this.isModal ? PartyUiMode.FAINT_SWITCH : PartyUiMode.POST_BATTLE_SWITCH, fieldIndex, (slotIndex: integer, option: PartyOption) => { + if (slotIndex >= this.scene.currentBattle.getBattlerCount() && slotIndex < 6) { + this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, fieldIndex, slotIndex, this.doReturn, option === PartyOption.PASS_BATON)); + } + this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); + }, PartyUiHandler.FilterNonFainted); + } +} diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts new file mode 100644 index 00000000000..3e401925cea --- /dev/null +++ b/src/phases/switch-summon-phase.ts @@ -0,0 +1,168 @@ +import BattleScene from "#app/battle-scene.js"; +import { applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr } from "#app/data/ability.js"; +import { allMoves, ForceSwitchOutAttr } from "#app/data/move.js"; +import { getPokeballTintColor } from "#app/data/pokeball.js"; +import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms.js"; +import { TrainerSlot } from "#app/data/trainer-config.js"; +import Pokemon from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { SwitchEffectTransferModifier } from "#app/modifier/modifier.js"; +import { Command } from "#app/ui/command-ui-handler.js"; +import i18next from "i18next"; +import { PostSummonPhase } from "./post-summon-phase"; +import { SummonPhase } from "./summon-phase"; + +export class SwitchSummonPhase extends SummonPhase { + private slotIndex: integer; + private doReturn: boolean; + private batonPass: boolean; + + private lastPokemon: Pokemon; + + /** + * Constructor for creating a new SwitchSummonPhase + * @param scene {@linkcode BattleScene} the scene the phase is associated with + * @param fieldIndex integer representing position on the battle field + * @param slotIndex integer for the index of pokemon (in party of 6) to switch into + * @param doReturn boolean whether to render "comeback" dialogue + * @param batonPass boolean if the switch is from baton pass + * @param player boolean if the switch is from the player + */ + constructor(scene: BattleScene, fieldIndex: integer, slotIndex: integer, doReturn: boolean, batonPass: boolean, player?: boolean) { + super(scene, fieldIndex, player !== undefined ? player : true); + + this.slotIndex = slotIndex; + this.doReturn = doReturn; + this.batonPass = batonPass; + } + + start(): void { + super.start(); + } + + preSummon(): void { + if (!this.player) { + if (this.slotIndex === -1) { + //@ts-ignore + this.slotIndex = this.scene.currentBattle.trainer?.getNextSummonIndex(!this.fieldIndex ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); // TODO: what would be the default trainer-slot fallback? + } + if (this.slotIndex > -1) { + this.showEnemyTrainer(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); + this.scene.pbTrayEnemy.showPbTray(this.scene.getEnemyParty()); + } + } + + if (!this.doReturn || (this.slotIndex !== -1 && !(this.player ? this.scene.getParty() : this.scene.getEnemyParty())[this.slotIndex])) { + if (this.player) { + return this.switchAndSummon(); + } else { + this.scene.time.delayedCall(750, () => this.switchAndSummon()); + return; + } + } + + const pokemon = this.getPokemon(); + + if (!this.batonPass) { + (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.removeTagsBySourceId(pokemon.id)); + } + + this.scene.ui.showText(this.player ? + i18next.t("battle:playerComeBack", { pokemonName: getPokemonNameWithAffix(pokemon) }) : + i18next.t("battle:trainerComeBack", { + trainerName: this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER), + pokemonName: getPokemonNameWithAffix(pokemon) + }) + ); + this.scene.playSound("pb_rel"); + pokemon.hideInfo(); + pokemon.tint(getPokeballTintColor(pokemon.pokeball), 1, 250, "Sine.easeIn"); + this.scene.tweens.add({ + targets: pokemon, + duration: 250, + ease: "Sine.easeIn", + scale: 0.5, + onComplete: () => { + pokemon.leaveField(!this.batonPass, false); + this.scene.time.delayedCall(750, () => this.switchAndSummon()); + } + }); + } + + switchAndSummon() { + const party = this.player ? this.getParty() : this.scene.getEnemyParty(); + const switchedInPokemon = party[this.slotIndex]; + this.lastPokemon = this.getPokemon(); + applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, this.lastPokemon); + if (this.batonPass && switchedInPokemon) { + (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.transferTagsBySourceId(this.lastPokemon.id, switchedInPokemon.id)); + if (!this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id)) { + const batonPassModifier = this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier + && (m as SwitchEffectTransferModifier).pokemonId === this.lastPokemon.id) as SwitchEffectTransferModifier; + if (batonPassModifier && !this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id)) { + this.scene.tryTransferHeldItemModifier(batonPassModifier, switchedInPokemon, false); + } + } + } + if (switchedInPokemon) { + party[this.slotIndex] = this.lastPokemon; + party[this.fieldIndex] = switchedInPokemon; + const showTextAndSummon = () => { + this.scene.ui.showText(this.player ? + i18next.t("battle:playerGo", { pokemonName: getPokemonNameWithAffix(switchedInPokemon) }) : + i18next.t("battle:trainerGo", { + trainerName: this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER), + pokemonName: this.getPokemon().getNameToRender() + }) + ); + // Ensure improperly persisted summon data (such as tags) is cleared upon switching + if (!this.batonPass) { + switchedInPokemon.resetBattleData(); + switchedInPokemon.resetSummonData(); + } + this.summon(); + }; + if (this.player) { + showTextAndSummon(); + } else { + this.scene.time.delayedCall(1500, () => { + this.hideEnemyTrainer(); + this.scene.pbTrayEnemy.hide(); + showTextAndSummon(); + }); + } + } else { + this.end(); + } + } + + onEnd(): void { + super.onEnd(); + + const pokemon = this.getPokemon(); + + const moveId = this.lastPokemon?.scene.currentBattle.lastMove; + const lastUsedMove = moveId ? allMoves[moveId] : undefined; + + const currentCommand = pokemon.scene.currentBattle.turnCommands[this.fieldIndex]?.command; + const lastPokemonIsForceSwitchedAndNotFainted = lastUsedMove?.hasAttr(ForceSwitchOutAttr) && !this.lastPokemon.isFainted(); + + // Compensate for turn spent summoning + // Or compensate for force switch move if switched out pokemon is not fainted + if (currentCommand === Command.POKEMON || lastPokemonIsForceSwitchedAndNotFainted) { + pokemon.battleSummonData.turnCount--; + } + + if (this.batonPass && pokemon) { + pokemon.transferSummon(this.lastPokemon); + } + + this.lastPokemon?.resetSummonData(); + + this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true); + } + + queuePostSummon(): void { + this.scene.unshiftPhase(new PostSummonPhase(this.scene, this.getPokemon().getBattlerIndex())); + } +} diff --git a/src/phases/test-message-phase.ts b/src/phases/test-message-phase.ts new file mode 100644 index 00000000000..14fed24ef4b --- /dev/null +++ b/src/phases/test-message-phase.ts @@ -0,0 +1,8 @@ +import BattleScene from "#app/battle-scene.js"; +import { MessagePhase } from "./message-phase"; + +export class TestMessagePhase extends MessagePhase { + constructor(scene: BattleScene, message: string) { + super(scene, message, null, true); + } +} diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts new file mode 100644 index 00000000000..c74dca97f5c --- /dev/null +++ b/src/phases/title-phase.ts @@ -0,0 +1,303 @@ +import { loggedInUser } from "#app/account.js"; +import BattleScene from "#app/battle-scene.js"; +import { BattleType } from "#app/battle.js"; +import { getDailyRunStarters, fetchDailyRunSeed } from "#app/data/daily-run.js"; +import { Gender } from "#app/data/gender.js"; +import { getBiomeKey } from "#app/field/arena.js"; +import { GameModes, GameMode, getGameMode } from "#app/game-mode.js"; +import { regenerateModifierPoolThresholds, ModifierPoolType, modifierTypes, getDailyRunStarterModifiers } from "#app/modifier/modifier-type.js"; +import { Phase } from "#app/phase.js"; +import { SessionSaveData } from "#app/system/game-data.js"; +import { Unlockables } from "#app/system/unlockables.js"; +import { vouchers } from "#app/system/voucher.js"; +import { OptionSelectItem, OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler.js"; +import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next from "i18next"; +import * as Utils from "#app/utils.js"; +import { Modifier } from "#app/modifier/modifier.js"; +import { CheckSwitchPhase } from "./check-switch-phase"; +import { EncounterPhase } from "./encounter-phase"; +import { SelectChallengePhase } from "./select-challenge-phase"; +import { SelectStarterPhase } from "./select-starter-phase"; +import { SummonPhase } from "./summon-phase"; + + +export class TitlePhase extends Phase { + private loaded: boolean; + private lastSessionData: SessionSaveData; + public gameMode: GameModes; + + constructor(scene: BattleScene) { + super(scene); + + this.loaded = false; + } + + start(): void { + super.start(); + + this.scene.ui.clearText(); + this.scene.ui.fadeIn(250); + + this.scene.playBgm("title", true); + + this.scene.gameData.getSession(loggedInUser?.lastSessionSlot ?? -1).then(sessionData => { + if (sessionData) { + this.lastSessionData = sessionData; + const biomeKey = getBiomeKey(sessionData.arena.biome); + const bgTexture = `${biomeKey}_bg`; + this.scene.arenaBg.setTexture(bgTexture); + } + this.showOptions(); + }).catch(err => { + console.error(err); + this.showOptions(); + }); + } + + showOptions(): void { + const options: OptionSelectItem[] = []; + if (loggedInUser && loggedInUser.lastSessionSlot > -1) { + options.push({ + label: i18next.t("continue", {ns: "menu"}), + handler: () => { + this.loadSaveSlot(this.lastSessionData || !loggedInUser ? -1 : loggedInUser.lastSessionSlot); + return true; + } + }); + } + options.push({ + label: i18next.t("menu:newGame"), + handler: () => { + const setModeAndEnd = (gameMode: GameModes) => { + this.gameMode = gameMode; + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.clearText(); + this.end(); + }; + if (this.scene.gameData.unlocks[Unlockables.ENDLESS_MODE]) { + const options: OptionSelectItem[] = [ + { + label: GameMode.getModeName(GameModes.CLASSIC), + handler: () => { + setModeAndEnd(GameModes.CLASSIC); + return true; + } + }, + { + label: GameMode.getModeName(GameModes.CHALLENGE), + handler: () => { + setModeAndEnd(GameModes.CHALLENGE); + return true; + } + }, + { + label: GameMode.getModeName(GameModes.ENDLESS), + handler: () => { + setModeAndEnd(GameModes.ENDLESS); + return true; + } + } + ]; + if (this.scene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE]) { + options.push({ + label: GameMode.getModeName(GameModes.SPLICED_ENDLESS), + handler: () => { + setModeAndEnd(GameModes.SPLICED_ENDLESS); + return true; + } + }); + } + options.push({ + label: i18next.t("menu:cancel"), + handler: () => { + this.scene.clearPhaseQueue(); + this.scene.pushPhase(new TitlePhase(this.scene)); + super.end(); + return true; + } + }); + this.scene.ui.showText(i18next.t("menu:selectGameMode"), null, () => this.scene.ui.setOverlayMode(Mode.OPTION_SELECT, { options: options })); + } else { + this.gameMode = GameModes.CLASSIC; + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.clearText(); + this.end(); + } + return true; + } + }, + { + label: i18next.t("menu:loadGame"), + handler: () => { + this.scene.ui.setOverlayMode(Mode.SAVE_SLOT, SaveSlotUiMode.LOAD, + (slotId: integer) => { + if (slotId === -1) { + return this.showOptions(); + } + this.loadSaveSlot(slotId); + }); + return true; + } + }, + { + label: i18next.t("menu:dailyRun"), + handler: () => { + this.initDailyRun(); + return true; + }, + keepOpen: true + }, + { + label: i18next.t("menu:settings"), + handler: () => { + this.scene.ui.setOverlayMode(Mode.SETTINGS); + return true; + }, + keepOpen: true + }); + const config: OptionSelectConfig = { + options: options, + noCancel: true, + yOffset: 47 + }; + this.scene.ui.setMode(Mode.TITLE, config); + } + + loadSaveSlot(slotId: integer): void { + this.scene.sessionSlotId = slotId > -1 || !loggedInUser ? slotId : loggedInUser.lastSessionSlot; + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.resetModeChain(); + this.scene.gameData.loadSession(this.scene, slotId, slotId === -1 ? this.lastSessionData : undefined).then((success: boolean) => { + if (success) { + this.loaded = true; + this.scene.ui.showText(i18next.t("menu:sessionSuccess"), null, () => this.end()); + } else { + this.end(); + } + }).catch(err => { + console.error(err); + this.scene.ui.showText(i18next.t("menu:failedToLoadSession"), null); + }); + } + + initDailyRun(): void { + this.scene.ui.setMode(Mode.SAVE_SLOT, SaveSlotUiMode.SAVE, (slotId: integer) => { + this.scene.clearPhaseQueue(); + if (slotId === -1) { + this.scene.pushPhase(new TitlePhase(this.scene)); + return super.end(); + } + this.scene.sessionSlotId = slotId; + + const generateDaily = (seed: string) => { + this.scene.gameMode = getGameMode(GameModes.DAILY); + + this.scene.setSeed(seed); + this.scene.resetSeed(1); + + this.scene.money = this.scene.gameMode.getStartingMoney(); + + const starters = getDailyRunStarters(this.scene, seed); + const startingLevel = this.scene.gameMode.getStartingLevel(); + + const party = this.scene.getParty(); + const loadPokemonAssets: Promise[] = []; + for (const starter of starters) { + const starterProps = this.scene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr); + const starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0)); + const starterGender = starter.species.malePercent !== null + ? !starterProps.female ? Gender.MALE : Gender.FEMALE + : Gender.GENDERLESS; + const starterPokemon = this.scene.addPlayerPokemon(starter.species, startingLevel, starter.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, starterProps.variant, undefined, starter.nature); + starterPokemon.setVisible(false); + party.push(starterPokemon); + loadPokemonAssets.push(starterPokemon.loadAssets()); + } + + regenerateModifierPoolThresholds(party, ModifierPoolType.DAILY_STARTER); + + const modifiers: Modifier[] = Array(3).fill(null).map(() => modifierTypes.EXP_SHARE().withIdFromFunc(modifierTypes.EXP_SHARE).newModifier()) + .concat(Array(3).fill(null).map(() => modifierTypes.GOLDEN_EXP_CHARM().withIdFromFunc(modifierTypes.GOLDEN_EXP_CHARM).newModifier())) + .concat(getDailyRunStarterModifiers(party)) + .filter((m) => m !== null); + + for (const m of modifiers) { + this.scene.addModifier(m, true, false, false, true); + } + this.scene.updateModifiers(true, true); + + Promise.all(loadPokemonAssets).then(() => { + this.scene.time.delayedCall(500, () => this.scene.playBgm()); + this.scene.gameData.gameStats.dailyRunSessionsPlayed++; + this.scene.newArena(this.scene.gameMode.getStartingBiome(this.scene)); + this.scene.newBattle(); + this.scene.arena.init(); + this.scene.sessionPlayTime = 0; + this.scene.lastSavePlayTime = 0; + this.end(); + }); + }; + + // If Online, calls seed fetch from db to generate daily run. If Offline, generates a daily run based on current date. + if (!Utils.isLocal) { + fetchDailyRunSeed().then(seed => { + if (seed) { + generateDaily(seed); + } else { + throw new Error("Daily run seed is null!"); + } + }).catch(err => { + console.error("Failed to load daily run:\n", err); + }); + } else { + generateDaily(btoa(new Date().toISOString().substring(0, 10))); + } + }); + } + + end(): void { + if (!this.loaded && !this.scene.gameMode.isDaily) { + this.scene.arena.preloadBgm(); + this.scene.gameMode = getGameMode(this.gameMode); + if (this.gameMode === GameModes.CHALLENGE) { + this.scene.pushPhase(new SelectChallengePhase(this.scene)); + } else { + this.scene.pushPhase(new SelectStarterPhase(this.scene)); + } + this.scene.newArena(this.scene.gameMode.getStartingBiome(this.scene)); + } else { + this.scene.playBgm(); + } + + this.scene.pushPhase(new EncounterPhase(this.scene, this.loaded)); + + if (this.loaded) { + const availablePartyMembers = this.scene.getParty().filter(p => p.isAllowedInBattle()).length; + + this.scene.pushPhase(new SummonPhase(this.scene, 0, true, true)); + if (this.scene.currentBattle.double && availablePartyMembers > 1) { + this.scene.pushPhase(new SummonPhase(this.scene, 1, true, true)); + } + + if (this.scene.currentBattle.battleType !== BattleType.TRAINER && (this.scene.currentBattle.waveIndex > 1 || !this.scene.gameMode.isDaily)) { + const minPartySize = this.scene.currentBattle.double ? 2 : 1; + if (availablePartyMembers > minPartySize) { + this.scene.pushPhase(new CheckSwitchPhase(this.scene, 0, this.scene.currentBattle.double)); + if (this.scene.currentBattle.double) { + this.scene.pushPhase(new CheckSwitchPhase(this.scene, 1, this.scene.currentBattle.double)); + } + } + } + } + + for (const achv of Object.keys(this.scene.gameData.achvUnlocks)) { + if (vouchers.hasOwnProperty(achv)) { + this.scene.validateVoucher(vouchers[achv]); + } + } + + super.end(); + } +} diff --git a/src/phases/toggle-double-position-phase.ts b/src/phases/toggle-double-position-phase.ts new file mode 100644 index 00000000000..fe3d0482483 --- /dev/null +++ b/src/phases/toggle-double-position-phase.ts @@ -0,0 +1,31 @@ +import BattleScene from "#app/battle-scene.js"; +import { FieldPosition } from "#app/field/pokemon.js"; +import { BattlePhase } from "./battle-phase"; + +export class ToggleDoublePositionPhase extends BattlePhase { + private double: boolean; + + constructor(scene: BattleScene, double: boolean) { + super(scene); + + this.double = double; + } + + start() { + super.start(); + + const playerPokemon = this.scene.getPlayerField().find(p => p.isActive(true)); + if (playerPokemon) { + playerPokemon.setFieldPosition(this.double && this.scene.getParty().filter(p => p.isAllowedInBattle()).length > 1 ? FieldPosition.LEFT : FieldPosition.CENTER, 500).then(() => { + if (playerPokemon.getFieldIndex() === 1) { + const party = this.scene.getParty(); + party[1] = party[0]; + party[0] = playerPokemon; + } + this.end(); + }); + } else { + this.end(); + } + } +} diff --git a/src/phases/trainer-message-test-phase.ts b/src/phases/trainer-message-test-phase.ts new file mode 100644 index 00000000000..4ea451660c3 --- /dev/null +++ b/src/phases/trainer-message-test-phase.ts @@ -0,0 +1,41 @@ +import BattleScene from "#app/battle-scene.js"; +import { trainerConfigs } from "#app/data/trainer-config.js"; +import { TrainerType } from "#app/enums/trainer-type.js"; +import { BattlePhase } from "./battle-phase"; +import { TestMessagePhase } from "./test-message-phase"; + +export class TrainerMessageTestPhase extends BattlePhase { + private trainerTypes: TrainerType[]; + + constructor(scene: BattleScene, ...trainerTypes: TrainerType[]) { + super(scene); + + this.trainerTypes = trainerTypes; + } + + start() { + super.start(); + + const testMessages: string[] = []; + + for (const t of Object.keys(trainerConfigs)) { + const type = parseInt(t); + if (this.trainerTypes.length && !this.trainerTypes.find(tt => tt === type as TrainerType)) { + continue; + } + const config = trainerConfigs[type]; + [config.encounterMessages, config.femaleEncounterMessages, config.victoryMessages, config.femaleVictoryMessages, config.defeatMessages, config.femaleDefeatMessages] + .map(messages => { + if (messages?.length) { + testMessages.push(...messages); + } + }); + } + + for (const message of testMessages) { + this.scene.pushPhase(new TestMessagePhase(this.scene, message)); + } + + this.end(); + } +} diff --git a/src/phases/trainer-victory-phase.ts b/src/phases/trainer-victory-phase.ts new file mode 100644 index 00000000000..7b8ee05de44 --- /dev/null +++ b/src/phases/trainer-victory-phase.ts @@ -0,0 +1,65 @@ +import BattleScene from "#app/battle-scene.js"; +import { getCharVariantFromDialogue } from "#app/data/dialogue.js"; +import { TrainerSlot } from "#app/data/trainer-config.js"; +import { TrainerType } from "#app/enums/trainer-type.js"; +import { modifierTypes } from "#app/modifier/modifier-type.js"; +import { vouchers } from "#app/system/voucher.js"; +import i18next from "i18next"; +import * as Utils from "#app/utils.js"; +import { BattlePhase } from "./battle-phase"; +import { ModifierRewardPhase } from "./modifier-reward-phase"; +import { MoneyRewardPhase } from "./money-reward-phase"; + +export class TrainerVictoryPhase extends BattlePhase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + this.scene.disableMenu = true; + + this.scene.playBgm(this.scene.currentBattle.trainer?.config.victoryBgm); + + this.scene.unshiftPhase(new MoneyRewardPhase(this.scene, this.scene.currentBattle.trainer?.config.moneyMultiplier!)); // TODO: is this bang correct? + + const modifierRewardFuncs = this.scene.currentBattle.trainer?.config.modifierRewardFuncs!; // TODO: is this bang correct? + for (const modifierRewardFunc of modifierRewardFuncs) { + this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, modifierRewardFunc)); + } + + const trainerType = this.scene.currentBattle.trainer?.config.trainerType!; // TODO: is this bang correct? + if (vouchers.hasOwnProperty(TrainerType[trainerType])) { + if (!this.scene.validateVoucher(vouchers[TrainerType[trainerType]]) && this.scene.currentBattle.trainer?.config.isBoss) { + this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, [modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][vouchers[TrainerType[trainerType]].voucherType])); + } + } + + this.scene.ui.showText(i18next.t("battle:trainerDefeated", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }), null, () => { + const victoryMessages = this.scene.currentBattle.trainer?.getVictoryMessages()!; // TODO: is this bang correct? + let message: string; + this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(victoryMessages), this.scene.currentBattle.waveIndex); + message = message!; // tell TS compiler it's defined now + + const showMessage = () => { + const originalFunc = showMessageOrEnd; + showMessageOrEnd = () => this.scene.ui.showDialogue(message, this.scene.currentBattle.trainer?.getName(), null, originalFunc); + + showMessageOrEnd(); + }; + let showMessageOrEnd = () => this.end(); + if (victoryMessages?.length) { + if (this.scene.currentBattle.trainer?.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { + const originalFunc = showMessageOrEnd; + showMessageOrEnd = () => this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => originalFunc())); + this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(this.scene.currentBattle.trainer?.getKey()!, getCharVariantFromDialogue(victoryMessages[0])).then(() => showMessage())); // TODO: is this bang correct? + } else { + showMessage(); + } + } else { + showMessageOrEnd(); + } + }, null, true); + + this.showEnemyTrainer(); + } +} diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts new file mode 100644 index 00000000000..62589e99b79 --- /dev/null +++ b/src/phases/turn-end-phase.ts @@ -0,0 +1,71 @@ +import BattleScene from "#app/battle-scene.js"; +import { applyPostTurnAbAttrs, PostTurnAbAttr } from "#app/data/ability.js"; +import { BattlerTagLapseType } from "#app/data/battler-tags.js"; +import { allMoves } from "#app/data/move.js"; +import { TerrainType } from "#app/data/terrain.js"; +import { Moves } from "#app/enums/moves.js"; +import { WeatherType } from "#app/enums/weather-type.js"; +import { TurnEndEvent } from "#app/events/battle-scene.js"; +import Pokemon from "#app/field/pokemon.js"; +import { getPokemonNameWithAffix } from "#app/messages.js"; +import { TurnHealModifier, EnemyTurnHealModifier, EnemyStatusEffectHealChanceModifier, TurnStatusEffectModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier.js"; +import i18next from "i18next"; +import { FieldPhase } from "./field-phase"; +import { MessagePhase } from "./message-phase"; +import { PokemonHealPhase } from "./pokemon-heal-phase"; + +export class TurnEndPhase extends FieldPhase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + + this.scene.currentBattle.incrementTurn(this.scene); + this.scene.eventTarget.dispatchEvent(new TurnEndEvent(this.scene.currentBattle.turn)); + + const handlePokemon = (pokemon: Pokemon) => { + pokemon.lapseTags(BattlerTagLapseType.TURN_END); + + if (pokemon.summonData.disabledMove && !--pokemon.summonData.disabledTurns) { + this.scene.pushPhase(new MessagePhase(this.scene, i18next.t("battle:notDisabled", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: allMoves[pokemon.summonData.disabledMove].name }))); + pokemon.summonData.disabledMove = Moves.NONE; + } + + this.scene.applyModifiers(TurnHealModifier, pokemon.isPlayer(), pokemon); + + if (this.scene.arena.terrain?.terrainType === TerrainType.GRASSY && pokemon.isGrounded()) { + this.scene.unshiftPhase(new PokemonHealPhase(this.scene, pokemon.getBattlerIndex(), + Math.max(pokemon.getMaxHp() >> 4, 1), i18next.t("battle:turnEndHpRestore", { pokemonName: getPokemonNameWithAffix(pokemon) }), true)); + } + + if (!pokemon.isPlayer()) { + this.scene.applyModifiers(EnemyTurnHealModifier, false, pokemon); + this.scene.applyModifier(EnemyStatusEffectHealChanceModifier, false, pokemon); + } + + applyPostTurnAbAttrs(PostTurnAbAttr, pokemon); + + this.scene.applyModifiers(TurnStatusEffectModifier, pokemon.isPlayer(), pokemon); + + this.scene.applyModifiers(TurnHeldItemTransferModifier, pokemon.isPlayer(), pokemon); + + pokemon.battleSummonData.turnCount++; + }; + + this.executeForAll(handlePokemon); + + this.scene.arena.lapseTags(); + + if (this.scene.arena.weather && !this.scene.arena.weather.lapse()) { + this.scene.arena.trySetWeather(WeatherType.NONE, false); + } + + if (this.scene.arena.terrain && !this.scene.arena.terrain.lapse()) { + this.scene.arena.trySetTerrain(TerrainType.NONE, false); + } + + this.end(); + } +} diff --git a/src/phases/turn-init-phase.ts b/src/phases/turn-init-phase.ts new file mode 100644 index 00000000000..a999d57ca0f --- /dev/null +++ b/src/phases/turn-init-phase.ts @@ -0,0 +1,65 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex } from "#app/battle.js"; +import { TurnInitEvent } from "#app/events/battle-scene.js"; +import { PlayerPokemon } from "#app/field/pokemon.js"; +import i18next from "i18next"; +import { FieldPhase } from "./field-phase"; +import { ToggleDoublePositionPhase } from "./toggle-double-position-phase"; +import { CommandPhase } from "./command-phase"; +import { EnemyCommandPhase } from "./enemy-command-phase"; +import { GameOverPhase } from "./game-over-phase"; +import { TurnStartPhase } from "./turn-start-phase"; + +export class TurnInitPhase extends FieldPhase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + + this.scene.getPlayerField().forEach(p => { + // If this pokemon is in play and evolved into something illegal under the current challenge, force a switch + if (p.isOnField() && !p.isAllowedInBattle()) { + this.scene.queueMessage(i18next.t("challenges:illegalEvolution", { "pokemon": p.name }), null, true); + + const allowedPokemon = this.scene.getParty().filter(p => p.isAllowedInBattle()); + + if (!allowedPokemon.length) { + // If there are no longer any legal pokemon in the party, game over. + this.scene.clearPhaseQueue(); + this.scene.unshiftPhase(new GameOverPhase(this.scene)); + } else if (allowedPokemon.length >= this.scene.currentBattle.getBattlerCount() || (this.scene.currentBattle.double && !allowedPokemon[0].isActive(true))) { + // If there is at least one pokemon in the back that is legal to switch in, force a switch. + p.switchOut(false); + } else { + // If there are no pokemon in the back but we're not game overing, just hide the pokemon. + // This should only happen in double battles. + p.leaveField(); + } + if (allowedPokemon.length === 1 && this.scene.currentBattle.double) { + this.scene.unshiftPhase(new ToggleDoublePositionPhase(this.scene, true)); + } + } + }); + + //this.scene.pushPhase(new MoveAnimTestPhase(this.scene)); + this.scene.eventTarget.dispatchEvent(new TurnInitEvent()); + + this.scene.getField().forEach((pokemon, i) => { + if (pokemon?.isActive()) { + if (pokemon.isPlayer()) { + this.scene.currentBattle.addParticipant(pokemon as PlayerPokemon); + } + + pokemon.resetTurnData(); + + this.scene.pushPhase(pokemon.isPlayer() ? new CommandPhase(this.scene, i) : new EnemyCommandPhase(this.scene, i - BattlerIndex.ENEMY)); + } + }); + + this.scene.pushPhase(new TurnStartPhase(this.scene)); + + this.end(); + } +} diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts new file mode 100644 index 00000000000..e4064fc784a --- /dev/null +++ b/src/phases/turn-start-phase.ts @@ -0,0 +1,172 @@ +import BattleScene from "#app/battle-scene.js"; +import { applyAbAttrs, BypassSpeedChanceAbAttr, PreventBypassSpeedChanceAbAttr, ChangeMovePriorityAbAttr } from "#app/data/ability.js"; +import { allMoves, applyMoveAttrs, IncrementMovePriorityAttr, MoveHeaderAttr } from "#app/data/move.js"; +import { Abilities } from "#app/enums/abilities.js"; +import { Stat } from "#app/enums/stat.js"; +import { PokemonMove } from "#app/field/pokemon.js"; +import { BypassSpeedChanceModifier } from "#app/modifier/modifier.js"; +import { Command } from "#app/ui/command-ui-handler.js"; +import * as Utils from "#app/utils.js"; +import { AttemptCapturePhase } from "./attempt-capture-phase"; +import { AttemptRunPhase } from "./attempt-run-phase"; +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"; + +export class TurnStartPhase extends FieldPhase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + + const field = this.scene.getField(); + const order = this.getOrder(); + + const battlerBypassSpeed = {}; + + this.scene.getField(true).filter(p => p.summonData).map(p => { + const bypassSpeed = new Utils.BooleanHolder(false); + const canCheckHeldItems = new Utils.BooleanHolder(true); + applyAbAttrs(BypassSpeedChanceAbAttr, p, null, false, bypassSpeed); + applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, false, bypassSpeed, canCheckHeldItems); + if (canCheckHeldItems.value) { + this.scene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed); + } + battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed; + }); + + const moveOrder = order.slice(0); + + moveOrder.sort((a, b) => { + const aCommand = this.scene.currentBattle.turnCommands[a]; + const bCommand = this.scene.currentBattle.turnCommands[b]; + + if (aCommand?.command !== bCommand?.command) { + if (aCommand?.command === Command.FIGHT) { + return 1; + } else if (bCommand?.command === Command.FIGHT) { + return -1; + } + } else if (aCommand?.command === Command.FIGHT) { + const aMove = allMoves[aCommand.move!.move];//TODO: is the bang correct here? + const bMove = allMoves[bCommand!.move!.move];//TODO: is the bang correct here? + + const aPriority = new Utils.IntegerHolder(aMove.priority); + const bPriority = new Utils.IntegerHolder(bMove.priority); + + applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? + applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? + + applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, false, aMove, aPriority); //TODO: is the bang correct here? + applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, false, bMove, bPriority); //TODO: is the bang correct here? + + if (aPriority.value !== bPriority.value) { + const bracketDifference = Math.ceil(aPriority.value) - Math.ceil(bPriority.value); + const hasSpeedDifference = battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value; + if (bracketDifference === 0 && hasSpeedDifference) { + return battlerBypassSpeed[a].value ? -1 : 1; + } + return aPriority.value < bPriority.value ? 1 : -1; + } + } + + if (battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) { + return battlerBypassSpeed[a].value ? -1 : 1; + } + + const aIndex = order.indexOf(a); + const bIndex = order.indexOf(b); + + return aIndex < bIndex ? -1 : aIndex > bIndex ? 1 : 0; + }); + + let orderIndex = 0; + + for (const o of moveOrder) { + + const pokemon = field[o]; + const turnCommand = this.scene.currentBattle.turnCommands[o]; + + if (turnCommand?.skip) { + continue; + } + + switch (turnCommand?.command) { + case Command.FIGHT: + const queuedMove = turnCommand.move; + pokemon.turnData.order = orderIndex++; + if (!queuedMove) { + continue; + } + const move = pokemon.getMoveset().find(m => m?.moveId === queuedMove.move) || new PokemonMove(queuedMove.move); + if (move.getMove().hasAttr(MoveHeaderAttr)) { + this.scene.unshiftPhase(new MoveHeaderPhase(this.scene, pokemon, move)); + } + if (pokemon.isPlayer()) { + if (turnCommand.cursor === -1) { + this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move));//TODO: is the bang correct here? + } else { + const playerPhase = new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move, false, queuedMove.ignorePP);//TODO: is the bang correct here? + this.scene.pushPhase(playerPhase); + } + } else { + this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move, false, queuedMove.ignorePP));//TODO: is the bang correct here? + } + break; + case Command.BALL: + this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, turnCommand.targets![0] % 2, turnCommand.cursor!));//TODO: is the bang correct here? + break; + case Command.POKEMON: + this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, pokemon.getFieldIndex(), turnCommand.cursor!, true, turnCommand.args![0] as boolean, pokemon.isPlayer()));//TODO: is the bang correct here? + break; + case Command.RUN: + let runningPokemon = pokemon; + if (this.scene.currentBattle.double) { + const playerActivePokemon = field.filter(pokemon => { + if (!!pokemon) { + return pokemon.isPlayer() && pokemon.isActive(); + } else { + return; + } + }); + // if only one pokemon is alive, use that one + if (playerActivePokemon.length > 1) { + // find which active pokemon has faster speed + const fasterPokemon = playerActivePokemon[0].getStat(Stat.SPD) > playerActivePokemon[1].getStat(Stat.SPD) ? playerActivePokemon[0] : playerActivePokemon[1]; + // check if either active pokemon has the ability "Run Away" + const hasRunAway = playerActivePokemon.find(p => p.hasAbility(Abilities.RUN_AWAY)); + runningPokemon = hasRunAway !== undefined ? hasRunAway : fasterPokemon; + } + } + this.scene.unshiftPhase(new AttemptRunPhase(this.scene, runningPokemon.getFieldIndex())); + break; + } + } + + + this.scene.pushPhase(new WeatherEffectPhase(this.scene)); + + for (const o of order) { + if (field[o].status && field[o].status.isPostTurn()) { + this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, o)); + } + } + + this.scene.pushPhase(new BerryPhase(this.scene)); + this.scene.pushPhase(new TurnEndPhase(this.scene)); + + /** + * this.end() will call shiftPhase(), which dumps everything from PrependQueue (aka everything that is unshifted()) to the front + * of the queue and dequeues to start the next phase + * this is important since stuff like SwitchSummon, AttemptRun, AttemptCapture Phases break the "flow" and should take precedence + */ + this.end(); + } +} diff --git a/src/phases/unavailable-phase.ts b/src/phases/unavailable-phase.ts new file mode 100644 index 00000000000..4757af5e15d --- /dev/null +++ b/src/phases/unavailable-phase.ts @@ -0,0 +1,17 @@ +import BattleScene from "#app/battle-scene.js"; +import { Phase } from "#app/phase.js"; +import { Mode } from "#app/ui/ui.js"; +import { LoginPhase } from "./login-phase"; + +export class UnavailablePhase extends Phase { + constructor(scene: BattleScene) { + super(scene); + } + + start(): void { + this.scene.ui.setMode(Mode.UNAVAILABLE, () => { + this.scene.unshiftPhase(new LoginPhase(this.scene, true)); + this.end(); + }); + } +} diff --git a/src/phases/unlock-phase.ts b/src/phases/unlock-phase.ts new file mode 100644 index 00000000000..ce06e2445ac --- /dev/null +++ b/src/phases/unlock-phase.ts @@ -0,0 +1,27 @@ +import BattleScene from "#app/battle-scene.js"; +import { Phase } from "#app/phase.js"; +import { Unlockables, getUnlockableName } from "#app/system/unlockables.js"; +import { Mode } from "#app/ui/ui.js"; +import i18next from "i18next"; + +export class UnlockPhase extends Phase { + private unlockable: Unlockables; + + constructor(scene: BattleScene, unlockable: Unlockables) { + super(scene); + + this.unlockable = unlockable; + } + + start(): void { + this.scene.time.delayedCall(2000, () => { + this.scene.gameData.unlocks[this.unlockable] = true; + this.scene.playSound("level_up_fanfare"); + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.showText(i18next.t("battle:unlockedSomething", { unlockedThing: getUnlockableName(this.unlockable) }), null, () => { + this.scene.time.delayedCall(1500, () => this.scene.arenaBg.setVisible(true)); + this.end(); + }, null, true, 1500); + }); + } +} diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts new file mode 100644 index 00000000000..b7587de4dbb --- /dev/null +++ b/src/phases/victory-phase.ts @@ -0,0 +1,151 @@ +import BattleScene from "#app/battle-scene.js"; +import { BattlerIndex, BattleType } from "#app/battle.js"; +import { modifierTypes } from "#app/modifier/modifier-type.js"; +import { ExpShareModifier, ExpBalanceModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier.js"; +import * as Utils from "#app/utils.js"; +import Overrides from "#app/overrides"; +import { BattleEndPhase } from "./battle-end-phase"; +import { NewBattlePhase } from "./new-battle-phase"; +import { PokemonPhase } from "./pokemon-phase"; +import { AddEnemyBuffModifierPhase } from "./add-enemy-buff-modifier-phase"; +import { EggLapsePhase } from "./egg-lapse-phase"; +import { ExpPhase } from "./exp-phase"; +import { GameOverPhase } from "./game-over-phase"; +import { ModifierRewardPhase } from "./modifier-reward-phase"; +import { SelectModifierPhase } from "./select-modifier-phase"; +import { ShowPartyExpBarPhase } from "./show-party-exp-bar-phase"; +import { TrainerVictoryPhase } from "./trainer-victory-phase"; + +export class VictoryPhase extends PokemonPhase { + constructor(scene: BattleScene, battlerIndex: BattlerIndex) { + super(scene, battlerIndex); + } + + start() { + super.start(); + + this.scene.gameData.gameStats.pokemonDefeated++; + + const participantIds = this.scene.currentBattle.playerParticipantIds; + const party = this.scene.getParty(); + const expShareModifier = this.scene.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier; + const expBalanceModifier = this.scene.findModifier(m => m instanceof ExpBalanceModifier) as ExpBalanceModifier; + const multipleParticipantExpBonusModifier = this.scene.findModifier(m => m instanceof MultipleParticipantExpBonusModifier) as MultipleParticipantExpBonusModifier; + const nonFaintedPartyMembers = party.filter(p => p.hp); + const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < this.scene.getMaxExpLevel()); + const partyMemberExp: number[] = []; + + if (participantIds.size) { + let expValue = this.getPokemon().getExpValue(); + if (this.scene.currentBattle.battleType === BattleType.TRAINER) { + expValue = Math.floor(expValue * 1.5); + } + for (const partyMember of nonFaintedPartyMembers) { + const pId = partyMember.id; + const participated = participantIds.has(pId); + if (participated) { + partyMember.addFriendship(2); + } + if (!expPartyMembers.includes(partyMember)) { + continue; + } + if (!participated && !expShareModifier) { + partyMemberExp.push(0); + continue; + } + let expMultiplier = 0; + if (participated) { + expMultiplier += (1 / participantIds.size); + if (participantIds.size > 1 && multipleParticipantExpBonusModifier) { + expMultiplier += multipleParticipantExpBonusModifier.getStackCount() * 0.2; + } + } else if (expShareModifier) { + expMultiplier += (expShareModifier.getStackCount() * 0.2) / participantIds.size; + } + if (partyMember.pokerus) { + expMultiplier *= 1.5; + } + if (Overrides.XP_MULTIPLIER_OVERRIDE !== null) { + expMultiplier = Overrides.XP_MULTIPLIER_OVERRIDE; + } + const pokemonExp = new Utils.NumberHolder(expValue * expMultiplier); + this.scene.applyModifiers(PokemonExpBoosterModifier, true, partyMember, pokemonExp); + partyMemberExp.push(Math.floor(pokemonExp.value)); + } + + if (expBalanceModifier) { + let totalLevel = 0; + let totalExp = 0; + expPartyMembers.forEach((expPartyMember, epm) => { + totalExp += partyMemberExp[epm]; + totalLevel += expPartyMember.level; + }); + + const medianLevel = Math.floor(totalLevel / expPartyMembers.length); + + const recipientExpPartyMemberIndexes: number[] = []; + expPartyMembers.forEach((expPartyMember, epm) => { + if (expPartyMember.level <= medianLevel) { + recipientExpPartyMemberIndexes.push(epm); + } + }); + + const splitExp = Math.floor(totalExp / recipientExpPartyMemberIndexes.length); + + expPartyMembers.forEach((_partyMember, pm) => { + partyMemberExp[pm] = Phaser.Math.Linear(partyMemberExp[pm], recipientExpPartyMemberIndexes.indexOf(pm) > -1 ? splitExp : 0, 0.2 * expBalanceModifier.getStackCount()); + }); + } + + for (let pm = 0; pm < expPartyMembers.length; pm++) { + const exp = partyMemberExp[pm]; + + if (exp) { + const partyMemberIndex = party.indexOf(expPartyMembers[pm]); + this.scene.unshiftPhase(expPartyMembers[pm].isOnField() ? new ExpPhase(this.scene, partyMemberIndex, exp) : new ShowPartyExpBarPhase(this.scene, partyMemberIndex, exp)); + } + } + } + + if (!this.scene.getEnemyParty().find(p => this.scene.currentBattle.battleType ? !p?.isFainted(true) : p.isOnField())) { + this.scene.pushPhase(new BattleEndPhase(this.scene)); + if (this.scene.currentBattle.battleType === BattleType.TRAINER) { + this.scene.pushPhase(new TrainerVictoryPhase(this.scene)); + } + if (this.scene.gameMode.isEndless || !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) { + this.scene.pushPhase(new EggLapsePhase(this.scene)); + if (this.scene.currentBattle.waveIndex % 10) { + this.scene.pushPhase(new SelectModifierPhase(this.scene)); + } else if (this.scene.gameMode.isDaily) { + this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_CHARM)); + if (this.scene.currentBattle.waveIndex > 10 && !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) { + this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.GOLDEN_POKEBALL)); + } + } else { + const superExpWave = !this.scene.gameMode.isEndless ? (this.scene.offsetGym ? 0 : 20) : 10; + if (this.scene.gameMode.isEndless && this.scene.currentBattle.waveIndex === 10) { + this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_SHARE)); + } + if (this.scene.currentBattle.waveIndex <= 750 && (this.scene.currentBattle.waveIndex <= 500 || (this.scene.currentBattle.waveIndex % 30) === superExpWave)) { + this.scene.pushPhase(new ModifierRewardPhase(this.scene, (this.scene.currentBattle.waveIndex % 30) !== superExpWave || this.scene.currentBattle.waveIndex > 250 ? modifierTypes.EXP_CHARM : modifierTypes.SUPER_EXP_CHARM)); + } + if (this.scene.currentBattle.waveIndex <= 150 && !(this.scene.currentBattle.waveIndex % 50)) { + this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.GOLDEN_POKEBALL)); + } + if (this.scene.gameMode.isEndless && !(this.scene.currentBattle.waveIndex % 50)) { + this.scene.pushPhase(new ModifierRewardPhase(this.scene, !(this.scene.currentBattle.waveIndex % 250) ? modifierTypes.VOUCHER_PREMIUM : modifierTypes.VOUCHER_PLUS)); + this.scene.pushPhase(new AddEnemyBuffModifierPhase(this.scene)); + } + } + this.scene.pushPhase(new NewBattlePhase(this.scene)); + } else { + this.scene.currentBattle.battleType = BattleType.CLEAR; + this.scene.score += this.scene.gameMode.getClearScoreBonus(); + this.scene.updateScoreText(); + this.scene.pushPhase(new GameOverPhase(this.scene, true)); + } + } + + this.end(); + } +} diff --git a/src/phases/weather-effect-phase.ts b/src/phases/weather-effect-phase.ts new file mode 100644 index 00000000000..6f5fbc0fce3 --- /dev/null +++ b/src/phases/weather-effect-phase.ts @@ -0,0 +1,67 @@ +import BattleScene from "#app/battle-scene.js"; +import { applyPreWeatherEffectAbAttrs, SuppressWeatherEffectAbAttr, PreWeatherDamageAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPostWeatherLapseAbAttrs, PostWeatherLapseAbAttr } from "#app/data/ability.js"; +import { CommonAnim } from "#app/data/battle-anims.js"; +import { Weather, getWeatherDamageMessage, getWeatherLapseMessage } from "#app/data/weather.js"; +import { WeatherType } from "#app/enums/weather-type.js"; +import Pokemon, { HitResult } from "#app/field/pokemon.js"; +import * as Utils from "#app/utils.js"; +import { CommonAnimPhase } from "./common-anim-phase"; + +export class WeatherEffectPhase extends CommonAnimPhase { + public weather: Weather | null; + + constructor(scene: BattleScene) { + super(scene, undefined, undefined, CommonAnim.SUNNY + ((scene?.arena?.weather?.weatherType || WeatherType.NONE) - 1)); + this.weather = scene?.arena?.weather; + } + + start() { + // Update weather state with any changes that occurred during the turn + this.weather = this.scene?.arena?.weather; + + if (!this.weather) { + this.end(); + return; + } + + this.setAnimation(CommonAnim.SUNNY + (this.weather.weatherType - 1)); + + if (this.weather.isDamaging()) { + + const cancelled = new Utils.BooleanHolder(false); + + this.executeForAll((pokemon: Pokemon) => applyPreWeatherEffectAbAttrs(SuppressWeatherEffectAbAttr, pokemon, this.weather, cancelled)); + + if (!cancelled.value) { + const inflictDamage = (pokemon: Pokemon) => { + const cancelled = new Utils.BooleanHolder(false); + + applyPreWeatherEffectAbAttrs(PreWeatherDamageAbAttr, pokemon, this.weather , cancelled); + applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); + + if (cancelled.value) { + return; + } + + const damage = Math.ceil(pokemon.getMaxHp() / 16); + + this.scene.queueMessage(getWeatherDamageMessage(this.weather?.weatherType!, pokemon)!); // TODO: are those bangs correct? + pokemon.damageAndUpdate(damage, HitResult.EFFECTIVE, false, false, true); + }; + + this.executeForAll((pokemon: Pokemon) => { + const immune = !pokemon || !!pokemon.getTypes(true, true).filter(t => this.weather?.isTypeDamageImmune(t)).length; + if (!immune) { + inflictDamage(pokemon); + } + }); + } + } + + this.scene.ui.showText(getWeatherLapseMessage(this.weather.weatherType)!, null, () => { // TODO: is this bang correct? + this.executeForAll((pokemon: Pokemon) => applyPostWeatherLapseAbAttrs(PostWeatherLapseAbAttr, pokemon, this.weather)); + + super.start(); + }); + } +} diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index 33f00d22555..b786787f83e 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -26,6 +26,7 @@ const unicodeRanges = { kana: "U+3040-30FF", CJKCommon: "U+2E80-2EFF,U+3000-303F,U+31C0-31EF,U+3200-32FF,U+3400-4DBF,U+F900-FAFF,U+FE30-FE4F", CJKIdeograph: "U+4E00-9FFF", + specialCharacters: "U+266A,U+2605,U+2665,U+2663" //♪.★,♥,♣ }; const rangesByLanguage = { korean: [unicodeRanges.CJKCommon, unicodeRanges.hangul].join(","), @@ -34,6 +35,15 @@ const rangesByLanguage = { }; const fonts: Array = [ + // unicode (special character from PokePT) + { + face: new FontFace("emerald", "url(./fonts/PokePT_Wansung.woff2)", { unicodeRange: unicodeRanges.specialCharacters }), + }, + { + face: new FontFace("pkmnems", "url(./fonts/PokePT_Wansung.woff2)", { unicodeRange: unicodeRanges.specialCharacters }), + extraOptions: { sizeAdjust: "133%" }, + }, + // unicode (korean) { face: new FontFace("emerald", "url(./fonts/PokePT_Wansung.woff2)", { unicodeRange: rangesByLanguage.korean }), }, diff --git a/src/system/game-data.ts b/src/system/game-data.ts index d64aa8f8e91..a9acd80fdee 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -27,7 +27,6 @@ import { Tutorial } from "../tutorial"; import { speciesEggMoves } from "../data/egg-moves"; import { allMoves } from "../data/move"; import { TrainerVariant } from "../field/trainer"; -import { OutdatedPhase, ReloadSessionPhase } from "#app/phases"; import { Variant, variantData } from "#app/data/variant"; import {setSettingGamepad, SettingGamepad, settingGamepadDefaults} from "./settings/settings-gamepad"; import {setSettingKeyboard, SettingKeyboard} from "#app/system/settings/settings-keyboard"; @@ -43,6 +42,8 @@ import { Species } from "#enums/species"; import { applyChallenges, ChallengeType } from "#app/data/challenge.js"; import { WeatherType } from "#app/enums/weather-type.js"; import { TerrainType } from "#app/data/terrain.js"; +import { OutdatedPhase } from "#app/phases/outdated-phase.js"; +import { ReloadSessionPhase } from "#app/phases/reload-session-phase.js"; export const defaultStarterSpecies: Species[] = [ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, @@ -1709,7 +1710,7 @@ export class GameData { } getFormAttr(formIndex: integer): bigint { - return BigInt(1 << (7 + formIndex)); + return BigInt(1) << BigInt(7 + formIndex); } consolidateDexData(dexData: DexData): void { diff --git a/src/system/settings/settings.ts b/src/system/settings/settings.ts index 4d8097897e9..4bd69788f04 100644 --- a/src/system/settings/settings.ts +++ b/src/system/settings/settings.ts @@ -8,13 +8,22 @@ import SettingsUiHandler from "#app/ui/settings/settings-ui-handler"; import { EaseType } from "#enums/ease-type"; import { MoneyFormat } from "#enums/money-format"; import { PlayerGender } from "#enums/player-gender"; +import { getIsInitialized, initI18n } from "#app/plugins/i18n.js"; +import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; + +function getTranslation(key: string): string { + if (!getIsInitialized()) { + initI18n(); + } + return i18next.t(key); +} const VOLUME_OPTIONS: SettingOption[] = new Array(11).fill(null).map((_, i) => i ? { value: (i * 10).toString(), label: (i * 10).toString(), } : { value: "Mute", - label: i18next.t("settings:mute") + label: getTranslation("settings:mute") }); const SHOP_OVERLAY_OPACITY_OPTIONS: SettingOption[] = new Array(9).fill(null).map((_, i) => { const value = ((i + 1) * 10).toString(); @@ -94,6 +103,7 @@ export const SettingKeys = { Damage_Numbers: "DAMAGE_NUMBERS", Move_Animations: "MOVE_ANIMATIONS", Show_Stats_on_Level_Up: "SHOW_LEVEL_UP_STATS", + Reroll_Target: "REROLL_TARGET", Candy_Upgrade_Notification: "CANDY_UPGRADE_NOTIFICATION", Candy_Upgrade_Display: "CANDY_UPGRADE_DISPLAY", Move_Info: "MOVE_INFO", @@ -569,6 +579,30 @@ export const Setting: Array = [ activatable: true, isHidden: () => !hasTouchscreen() }, + { + key: SettingKeys.Reroll_Target, + label: i18next.t("settings:shopCursorTarget"), + options: [ + { + value:"Reroll", + label: i18next.t("settings:reroll") + }, + { + value:"Items", + label: i18next.t("settings:items") + }, + { + value:"Shop", + label: i18next.t("settings:shop") + }, + { + value:"Check Team", + label: i18next.t("settings:checkTeam") + } + ], + default: ShopCursorTarget.CHECK_TEAM, + type: SettingType.DISPLAY + }, { key: SettingKeys.Shop_Overlay_Opacity, label: i18next.t("settings:shopOverlayOpacity"), @@ -701,6 +735,8 @@ export function setSetting(scene: BattleScene, setting: string, value: integer): case SettingKeys.Show_Stats_on_Level_Up: scene.showLevelUpStats = Setting[index].options[value].value === "On"; break; + case SettingKeys.Reroll_Target: + scene.shopCursorTarget = value; case SettingKeys.EXP_Gains_Speed: scene.expGainsSpeed = value; break; diff --git a/src/system/voucher.ts b/src/system/voucher.ts index 0c71e3c0286..2f94308d9c8 100644 --- a/src/system/voucher.ts +++ b/src/system/voucher.ts @@ -1,9 +1,10 @@ import BattleScene from "../battle-scene"; import i18next from "i18next"; -import { Achv, AchvTier, achvs, getAchievementDescription } from "./achv"; +import { AchvTier, achvs, getAchievementDescription } from "./achv"; import { PlayerGender } from "#enums/player-gender"; import { TrainerType } from "#enums/trainer-type"; import { ConditionFn } from "#app/@types/common.js"; +import { trainerConfigs } from "#app/data/trainer-config.js"; export enum VoucherType { REGULAR, @@ -88,42 +89,36 @@ export interface Vouchers { export const vouchers: Vouchers = {}; -const voucherAchvs: Achv[] = [ achvs.CLASSIC_VICTORY ]; - export function initVouchers() { - import("../data/trainer-config").then(tc => { - const trainerConfigs = tc.trainerConfigs; + for (const achv of [achvs.CLASSIC_VICTORY]) { + const voucherType = achv.score >= 150 + ? VoucherType.GOLDEN + : achv.score >= 100 + ? VoucherType.PREMIUM + : achv.score >= 75 + ? VoucherType.PLUS + : VoucherType.REGULAR; + vouchers[achv.id] = new Voucher(voucherType, getAchievementDescription(achv.localizationKey)); + } - for (const achv of voucherAchvs) { - const voucherType = achv.score >= 150 - ? VoucherType.GOLDEN - : achv.score >= 100 - ? VoucherType.PREMIUM - : achv.score >= 75 - ? VoucherType.PLUS - : VoucherType.REGULAR; - vouchers[achv.id] = new Voucher(voucherType, getAchievementDescription(achv.localizationKey)); - } + const bossTrainerTypes = Object.keys(trainerConfigs) + .filter(tt => trainerConfigs[tt].isBoss && trainerConfigs[tt].getDerivedType() !== TrainerType.RIVAL && trainerConfigs[tt].hasVoucher); - const bossTrainerTypes = Object.keys(trainerConfigs) - .filter(tt => trainerConfigs[tt].isBoss && trainerConfigs[tt].getDerivedType() !== TrainerType.RIVAL && trainerConfigs[tt].hasVoucher); - - for (const trainerType of bossTrainerTypes) { - const voucherType = trainerConfigs[trainerType].moneyMultiplier < 10 - ? VoucherType.PLUS - : VoucherType.PREMIUM; - const key = TrainerType[trainerType]; - const trainerName = trainerConfigs[trainerType].name; - const trainer = trainerConfigs[trainerType]; - const title = trainer.title ? ` (${trainer.title})` : ""; - vouchers[key] = new Voucher( - voucherType, - `${i18next.t("voucher:defeatTrainer", { trainerName })} ${title}`, - ); - } - const voucherKeys = Object.keys(vouchers); - for (const k of voucherKeys) { - vouchers[k].id = k; - } - }); + for (const trainerType of bossTrainerTypes) { + const voucherType = trainerConfigs[trainerType].moneyMultiplier < 10 + ? VoucherType.PLUS + : VoucherType.PREMIUM; + const key = TrainerType[trainerType]; + const trainerName = trainerConfigs[trainerType].name; + const trainer = trainerConfigs[trainerType]; + const title = trainer.title ? ` (${trainer.title})` : ""; + vouchers[key] = new Voucher( + voucherType, + `${i18next.t("voucher:defeatTrainer", { trainerName })} ${title}`, + ); + } + const voucherKeys = Object.keys(vouchers); + for (const k of voucherKeys) { + vouchers[k].id = k; + } } diff --git a/src/test/abilities/ability_timing.test.ts b/src/test/abilities/ability_timing.test.ts index 3906233a7bf..3238f880992 100644 --- a/src/test/abilities/ability_timing.test.ts +++ b/src/test/abilities/ability_timing.test.ts @@ -1,13 +1,15 @@ -import { CommandPhase, MessagePhase, TurnInitPhase } from "#app/phases"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import i18next, { initI18n } from "#app/plugins/i18n"; -import GameManager from "#test/utils/gameManager"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Ability Timing", () => { diff --git a/src/test/abilities/aura_break.test.ts b/src/test/abilities/aura_break.test.ts index a34475cb1ad..7de300c157a 100644 --- a/src/test/abilities/aura_break.test.ts +++ b/src/test/abilities/aura_break.test.ts @@ -1,19 +1,18 @@ -import { allMoves } from "#app/data/move.js"; -import { MoveEffectPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Aura Break", () => { let phaserGame: Phaser.Game; let game: GameManager; - const auraBreakMultiplier = 9/16 * 4/3; + const auraBreakMultiplier = 9 / 16 * 4 / 3; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -42,7 +41,7 @@ describe("Abilities - Aura Break", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.MOONBLAST)); + game.move.select(Moves.MOONBLAST); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(expect.closeTo(basePower * auraBreakMultiplier)); @@ -56,7 +55,7 @@ describe("Abilities - Aura Break", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DARK_PULSE)); + game.move.select(Moves.DARK_PULSE); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(expect.closeTo(basePower * auraBreakMultiplier)); diff --git a/src/test/abilities/battery.test.ts b/src/test/abilities/battery.test.ts index 2345e63d987..020866509d6 100644 --- a/src/test/abilities/battery.test.ts +++ b/src/test/abilities/battery.test.ts @@ -1,13 +1,13 @@ -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { MoveEffectPhase, TurnEndPhase } from "#app/phases.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Battery", () => { let phaserGame: Phaser.Game; @@ -42,8 +42,8 @@ describe("Abilities - Battery", () => { await game.startBattle([Species.PIKACHU, Species.CHARJABUG]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DAZZLING_GLEAM)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.DAZZLING_GLEAM); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower * batteryMultiplier); @@ -57,8 +57,8 @@ describe("Abilities - Battery", () => { await game.startBattle([Species.PIKACHU, Species.CHARJABUG]); - game.doAttack(getMovePosition(game.scene, 0, Moves.BREAKING_SWIPE)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.BREAKING_SWIPE); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower); @@ -72,8 +72,8 @@ describe("Abilities - Battery", () => { await game.startBattle([Species.CHARJABUG, Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DAZZLING_GLEAM)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.DAZZLING_GLEAM); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower); diff --git a/src/test/abilities/battle_bond.test.ts b/src/test/abilities/battle_bond.test.ts index 1a5c71b4c15..71e9438db8f 100644 --- a/src/test/abilities/battle_bond.test.ts +++ b/src/test/abilities/battle_bond.test.ts @@ -1,11 +1,10 @@ -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { QuietFormChangePhase } from "#app/form-change-phase.js"; -import { TurnEndPhase } from "#app/phases.js"; +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 { getMovePosition } from "#test/utils/gameManagerUtils"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; const TIMEOUT = 20 * 1000; @@ -53,7 +52,7 @@ describe("Abilities - BATTLE BOND", () => { greninja!.status = new Status(StatusEffect.FAINT); expect(greninja!.isFainted()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to(TurnEndPhase); game.doSelectModifier(); diff --git a/src/test/abilities/costar.test.ts b/src/test/abilities/costar.test.ts index ef3fb3a2ab0..9a4baeef1fb 100644 --- a/src/test/abilities/costar.test.ts +++ b/src/test/abilities/costar.test.ts @@ -1,13 +1,13 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; -import { CommandPhase, MessagePhase } from "#app/phases.js"; +import { BattleStat } from "#app/data/battle-stat"; +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -43,15 +43,15 @@ describe("Abilities - COSTAR", () => { let [leftPokemon, rightPokemon] = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.NASTY_PLOT)); + game.move.select(Moves.NASTY_PLOT); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.toNextTurn(); expect(leftPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(+2); expect(rightPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(CommandPhase); game.doSwitchPokemon(2); await game.phaseInterceptor.to(MessagePhase); @@ -75,7 +75,7 @@ describe("Abilities - COSTAR", () => { expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2); expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(CommandPhase); game.doSwitchPokemon(2); await game.phaseInterceptor.to(MessagePhase); diff --git a/src/test/abilities/disguise.test.ts b/src/test/abilities/disguise.test.ts index 183295f6f41..58087b408a5 100644 --- a/src/test/abilities/disguise.test.ts +++ b/src/test/abilities/disguise.test.ts @@ -1,12 +1,16 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { StatusEffect } from "#app/data/status-effect"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { Mode } from "#app/ui/ui"; +import { toDmgValue } from "#app/utils"; import { Moves } from "#enums/moves"; -import { Abilities } from "#enums/abilities"; import { Species } from "#enums/species"; -import { StatusEffect } from "#app/data/status-effect.js"; -import { MoveEffectPhase, MoveEndPhase, TurnEndPhase, TurnInitPhase } from "#app/phases.js"; -import { BattleStat } from "#app/data/battle-stat.js"; +import GameManager from "#test/utils/gameManager"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { SPLASH_ONLY } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -38,16 +42,16 @@ describe("Abilities - Disguise", () => { game.override.moveset([Moves.SHADOW_SNEAK, Moves.VACUUM_WAVE, Moves.TOXIC_THREAD, Moves.SPLASH]); }, TIMEOUT); - it("takes no damage from attacking move and transforms to Busted form, taking 1/8 max HP damage from the disguise breaking", async () => { + it("takes no damage from attacking move and transforms to Busted form, takes 1/8 max HP damage from the disguise breaking", async () => { await game.startBattle(); const mimikyu = game.scene.getEnemyPokemon()!; const maxHp = mimikyu.getMaxHp(); - const disguiseDamage = Math.floor(maxHp / 8); + const disguiseDamage = toDmgValue(maxHp / 8); expect(mimikyu.formIndex).toBe(disguisedForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.SHADOW_SNEAK)); + game.move.select(Moves.SHADOW_SNEAK); await game.phaseInterceptor.to(MoveEndPhase); @@ -62,7 +66,7 @@ describe("Abilities - Disguise", () => { expect(mimikyu.formIndex).toBe(disguisedForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.VACUUM_WAVE)); + game.move.select(Moves.VACUUM_WAVE); await game.phaseInterceptor.to(MoveEndPhase); @@ -76,11 +80,11 @@ describe("Abilities - Disguise", () => { const mimikyu = game.scene.getEnemyPokemon()!; const maxHp = mimikyu.getMaxHp(); - const disguiseDamage = Math.floor(maxHp / 8); + const disguiseDamage = toDmgValue(maxHp / 8); expect(mimikyu.formIndex).toBe(disguisedForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURGING_STRIKES)); + game.move.select(Moves.SURGING_STRIKES); // First hit await game.phaseInterceptor.to(MoveEffectPhase); @@ -99,7 +103,7 @@ describe("Abilities - Disguise", () => { const mimikyu = game.scene.getEnemyPokemon()!; expect(mimikyu.hp).toBe(mimikyu.getMaxHp()); - game.doAttack(getMovePosition(game.scene, 0, Moves.TOXIC_THREAD)); + game.move.select(Moves.TOXIC_THREAD); await game.phaseInterceptor.to(TurnEndPhase); @@ -117,9 +121,9 @@ describe("Abilities - Disguise", () => { const mimikyu = game.scene.getPlayerPokemon()!; const maxHp = mimikyu.getMaxHp(); - const disguiseDamage = Math.floor(maxHp / 8); + const disguiseDamage = toDmgValue(maxHp / 8); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); @@ -134,29 +138,73 @@ describe("Abilities - Disguise", () => { expect(mimikyu.formIndex).toBe(bustedForm); }, TIMEOUT); - it("reverts to Disguised on arena reset", async () => { - game.override.startingWave(4); + it("persists form change when wave changes with no arena reset", async () => { + game.override.starterSpecies(0); + game.override.starterForms({ + [Species.MIMIKYU]: bustedForm + }); + await game.startBattle([Species.FURRET, Species.MIMIKYU]); + const mimikyu = game.scene.getParty()[1]!; + expect(mimikyu.formIndex).toBe(bustedForm); + + game.move.select(Moves.SPLASH); + await game.doKillOpponents(); + await game.toNextWave(); + + expect(mimikyu.formIndex).toBe(bustedForm); + }, TIMEOUT); + + it("reverts to Disguised form on arena reset", async () => { + game.override.startingWave(4); game.override.starterSpecies(Species.MIMIKYU); game.override.starterForms({ [Species.MIMIKYU]: bustedForm }); - game.override.enemySpecies(Species.MAGIKARP); - game.override.enemyAbility(Abilities.BALL_FETCH); - await game.startBattle(); const mimikyu = game.scene.getPlayerPokemon()!; expect(mimikyu.formIndex).toBe(bustedForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); - await game.phaseInterceptor.to(TurnEndPhase); - game.doSelectModifier(); - await game.phaseInterceptor.to(TurnInitPhase); + await game.toNextWave(); expect(mimikyu.formIndex).toBe(disguisedForm); }, TIMEOUT); + + it("reverts to Disguised form on biome change when fainted", async () => { + game.override.startingWave(10); + game.override.starterSpecies(0); + game.override.starterForms({ + [Species.MIMIKYU]: bustedForm + }); + + await game.startBattle([Species.MIMIKYU, Species.FURRET]); + + const mimikyu1 = game.scene.getPlayerPokemon()!; + + expect(mimikyu1.formIndex).toBe(bustedForm); + + game.move.select(Moves.SPLASH); + await game.killPokemon(mimikyu1); + game.doSelectPartyPokemon(1); + await game.toNextTurn(); + game.move.select(Moves.SPLASH); + await game.doKillOpponents(); + game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { // TODO: Make tests run in set mode instead of switch mode + game.setMode(Mode.MESSAGE); + game.endPhase(); + }, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)); + + game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { + game.setMode(Mode.MESSAGE); + game.endPhase(); + }, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)); + await game.phaseInterceptor.to("PartyHealPhase"); + + expect(mimikyu1.formIndex).toBe(disguisedForm); + }, TIMEOUT); }); diff --git a/src/test/abilities/dry_skin.test.ts b/src/test/abilities/dry_skin.test.ts index 20b85eab767..b337e4d96f7 100644 --- a/src/test/abilities/dry_skin.test.ts +++ b/src/test/abilities/dry_skin.test.ts @@ -1,12 +1,11 @@ -import { Species } from "#app/enums/species.js"; -import { TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { Species } from "#app/enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Dry Skin", () => { let phaserGame: Phaser.Game; @@ -43,13 +42,13 @@ describe("Abilities - Dry Skin", () => { // first turn let previousEnemyHp = enemy.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.SUNNY_DAY)); + game.move.select(Moves.SUNNY_DAY); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBeLessThan(previousEnemyHp); // second turn previousEnemyHp = enemy.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBeLessThan(previousEnemyHp); }); @@ -66,13 +65,13 @@ describe("Abilities - Dry Skin", () => { // first turn let previousEnemyHp = enemy.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.RAIN_DANCE)); + game.move.select(Moves.RAIN_DANCE); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBeGreaterThan(previousEnemyHp); // second turn previousEnemyHp = enemy.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBeGreaterThan(previousEnemyHp); }); @@ -87,7 +86,7 @@ describe("Abilities - Dry Skin", () => { enemy.hp = initialHP; // first turn - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAMETHROWER)); + game.move.select(Moves.FLAMETHROWER); await game.phaseInterceptor.to(TurnEndPhase); const fireDamageTakenWithDrySkin = initialHP - enemy.hp; @@ -96,7 +95,7 @@ describe("Abilities - Dry Skin", () => { game.override.enemyAbility(Abilities.NONE); // second turn - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAMETHROWER)); + game.move.select(Moves.FLAMETHROWER); await game.phaseInterceptor.to(TurnEndPhase); const fireDamageTakenWithoutDrySkin = initialHP - enemy.hp; @@ -113,7 +112,7 @@ describe("Abilities - Dry Skin", () => { enemy.hp = 1; - game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_GUN)); + game.move.select(Moves.WATER_GUN); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBeGreaterThan(1); }); @@ -129,7 +128,7 @@ describe("Abilities - Dry Skin", () => { enemy.hp = 1; game.override.enemyMoveset([Moves.PROTECT, Moves.PROTECT, Moves.PROTECT, Moves.PROTECT]); - game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_GUN)); + game.move.select(Moves.WATER_GUN); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBe(1); }); @@ -145,14 +144,14 @@ describe("Abilities - Dry Skin", () => { enemy.hp = 1; // first turn - game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_SHURIKEN)); + game.move.select(Moves.WATER_SHURIKEN); await game.phaseInterceptor.to(TurnEndPhase); const healthGainedFromWaterShuriken = enemy.hp - 1; enemy.hp = 1; // second turn - game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_GUN)); + game.move.select(Moves.WATER_GUN); await game.phaseInterceptor.to(TurnEndPhase); const healthGainedFromWaterGun = enemy.hp - 1; diff --git a/src/test/abilities/flash_fire.test.ts b/src/test/abilities/flash_fire.test.ts index b77b271b754..de40873998f 100644 --- a/src/test/abilities/flash_fire.test.ts +++ b/src/test/abilities/flash_fire.test.ts @@ -1,15 +1,15 @@ -import { Species } from "#app/enums/species.js"; -import GameManager from "#test/utils/gameManager"; +import { BattlerIndex } from "#app/battle"; +import { StatusEffect } from "#app/data/status-effect"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { Species } from "#app/enums/species"; +import { MovePhase } from "#app/phases/move-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { MovePhase, TurnEndPhase } from "#app/phases"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { StatusEffect } from "#app/data/status-effect.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { BattlerIndex } from "#app/battle.js"; describe("Abilities - Flash Fire", () => { let phaserGame: Phaser.Game; @@ -37,35 +37,35 @@ describe("Abilities - Flash Fire", () => { }); - it("immune to Fire-type moves", async() => { + it("immune to Fire-type moves", async () => { game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset(SPLASH_ONLY); await game.startBattle([Species.BLISSEY]); const blissey = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(blissey.hp).toBe(blissey.getMaxHp()); }, 20000); - it("not activate if the Pokémon is protected from the Fire-type move", async() => { + it("not activate if the Pokémon is protected from the Fire-type move", async () => { game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset([Moves.PROTECT]); await game.startBattle([Species.BLISSEY]); const blissey = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.PROTECT)); + game.move.select(Moves.PROTECT); await game.phaseInterceptor.to(TurnEndPhase); expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeUndefined(); }, 20000); - it("activated by Will-O-Wisp", async() => { + it("activated by Will-O-Wisp", async () => { game.override.enemyMoveset(Array(4).fill(Moves.WILL_O_WISP)).moveset(SPLASH_ONLY); await game.startBattle([Species.BLISSEY]); const blissey = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.move.forceHit(); await game.phaseInterceptor.to(MovePhase, false); await game.move.forceHit(); @@ -74,25 +74,25 @@ describe("Abilities - Flash Fire", () => { expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeDefined(); }, 20000); - it("activated after being frozen", async() => { + it("activated after being frozen", async () => { game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset(SPLASH_ONLY); game.override.statusEffect(StatusEffect.FREEZE); await game.startBattle([Species.BLISSEY]); const blissey = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeDefined(); }, 20000); - it("not passing with baton pass", async() => { + it("not passing with baton pass", async () => { game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset([Moves.BATON_PASS]); await game.startBattle([Species.BLISSEY, Species.CHANSEY]); // ensure use baton pass after enemy moved - game.doAttack(getMovePosition(game.scene, 0, Moves.BATON_PASS)); + game.move.select(Moves.BATON_PASS); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); game.doSelectPartyPokemon(1); @@ -103,7 +103,7 @@ describe("Abilities - Flash Fire", () => { expect(chansey!.getTag(BattlerTagType.FIRE_BOOST)).toBeUndefined(); }, 20000); - it("boosts Fire-type move when the ability is activated", async() => { + it("boosts Fire-type move when the ability is activated", async () => { game.override.enemyMoveset(Array(4).fill(Moves.FIRE_PLEDGE)).moveset([Moves.EMBER, Moves.SPLASH]); game.override.enemyAbility(Abilities.FLASH_FIRE).ability(Abilities.NONE); await game.startBattle([Species.BLISSEY]); @@ -112,7 +112,7 @@ describe("Abilities - Flash Fire", () => { blissey.hp = initialHP; // first turn - game.doAttack(getMovePosition(game.scene, 0, Moves.EMBER)); + game.move.select(Moves.EMBER); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to(TurnEndPhase); const originalDmg = initialHP - blissey.hp; @@ -121,7 +121,7 @@ describe("Abilities - Flash Fire", () => { blissey.hp = initialHP; // second turn - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); const flashFireDmg = initialHP - blissey.hp; diff --git a/src/test/abilities/gulp_missile.test.ts b/src/test/abilities/gulp_missile.test.ts index 2647f765f6e..a451d290906 100644 --- a/src/test/abilities/gulp_missile.test.ts +++ b/src/test/abilities/gulp_missile.test.ts @@ -1,21 +1,18 @@ -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { - BerryPhase, - MoveEndPhase, - TurnEndPhase, - TurnStartPhase, -} from "#app/phases"; +import { BattleStat } from "#app/data/battle-stat"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { StatusEffect } from "#app/enums/status-effect"; +import Pokemon from "#app/field/pokemon"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { SPLASH_ONLY } from "../utils/testUtils"; -import { BattleStat } from "#app/data/battle-stat.js"; -import { StatusEffect } from "#app/enums/status-effect.js"; -import Pokemon from "#app/field/pokemon.js"; describe("Abilities - Gulp Missile", () => { let phaserGame: Phaser.Game; @@ -32,7 +29,7 @@ describe("Abilities - Gulp Missile", () => { * @returns The effect damage of Gulp Missile */ const getEffectDamage = (pokemon: Pokemon): number => { - return Math.max(1, Math.floor(pokemon.getMaxHp() * 1/4)); + return Math.max(1, Math.floor(pokemon.getMaxHp() * 1 / 4)); }; beforeAll(() => { @@ -60,9 +57,9 @@ describe("Abilities - Gulp Missile", () => { await game.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); + game.move.select(Moves.DIVE); await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); + game.move.select(Moves.DIVE); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getHpRatio()).toBeGreaterThanOrEqual(.5); @@ -77,18 +74,33 @@ describe("Abilities - Gulp Missile", () => { vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.49); expect(cramorant.getHpRatio()).toBe(.49); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeDefined(); expect(cramorant.formIndex).toBe(GORGING_FORM); }); + it("changes to base form when switched out after Surf or Dive is used", async () => { + await game.startBattle([Species.CRAMORANT, Species.MAGIKARP]); + const cramorant = game.scene.getPlayerPokemon()!; + + game.move.select(Moves.SURF); + await game.toNextTurn(); + + game.doSwitchPokemon(1); + await game.toNextTurn(); // form change is delayed until after end of turn + + expect(cramorant.formIndex).toBe(NORMAL_FORM); + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeUndefined(); + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeUndefined(); + }); + it("changes form during Dive's charge turn", async () => { await game.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); + game.move.select(Moves.DIVE); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -102,7 +114,7 @@ describe("Abilities - Gulp Missile", () => { const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "damageAndUpdate"); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy)); @@ -115,7 +127,7 @@ describe("Abilities - Gulp Missile", () => { const cramorant = game.scene.getPlayerPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -137,7 +149,7 @@ describe("Abilities - Gulp Missile", () => { vi.spyOn(enemy, "damageAndUpdate"); vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -161,7 +173,7 @@ describe("Abilities - Gulp Missile", () => { vi.spyOn(enemy, "damageAndUpdate"); vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.45); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeDefined(); @@ -181,7 +193,7 @@ describe("Abilities - Gulp Missile", () => { const cramorant = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); + game.move.select(Moves.DIVE); await game.phaseInterceptor.to(BerryPhase, false); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -197,7 +209,7 @@ describe("Abilities - Gulp Missile", () => { vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(MoveEndPhase); const enemyHpPreEffect = enemy.hp; @@ -219,7 +231,7 @@ describe("Abilities - Gulp Missile", () => { const cramorant = game.scene.getPlayerPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -239,7 +251,7 @@ describe("Abilities - Gulp Missile", () => { const cramorant = game.scene.getPlayerPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -256,7 +268,7 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyAbility(Abilities.TRACE); await game.startBattle([Species.CRAMORANT]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnStartPhase); expect(game.scene.getEnemyPokemon()?.hasAbility(Abilities.GULP_MISSILE)).toBe(false); diff --git a/src/test/abilities/heatproof.test.ts b/src/test/abilities/heatproof.test.ts index 8249ba6996f..e2a558e6d99 100644 --- a/src/test/abilities/heatproof.test.ts +++ b/src/test/abilities/heatproof.test.ts @@ -1,13 +1,13 @@ -import { Species } from "#app/enums/species.js"; -import { TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { Species } from "#app/enums/species"; +import { StatusEffect } from "#app/enums/status-effect"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { toDmgValue } from "#app/utils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { StatusEffect } from "#app/enums/status-effect.js"; describe("Abilities - Heatproof", () => { let phaserGame: Phaser.Game; @@ -45,14 +45,14 @@ describe("Abilities - Heatproof", () => { const initialHP = 1000; enemy.hp = initialHP; - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAMETHROWER)); + game.move.select(Moves.FLAMETHROWER); await game.phaseInterceptor.to(TurnEndPhase); const heatproofDamage = initialHP - enemy.hp; enemy.hp = initialHP; game.override.enemyAbility(Abilities.BALL_FETCH); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAMETHROWER)); + game.move.select(Moves.FLAMETHROWER); await game.phaseInterceptor.to(TurnEndPhase); const regularDamage = initialHP - enemy.hp; @@ -68,10 +68,10 @@ describe("Abilities - Heatproof", () => { const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.toNextTurn(); // Normal burn damage is /16 - expect(enemy.hp).toBe(enemy.getMaxHp() - Math.floor(enemy.getMaxHp() / 32)); + expect(enemy.hp).toBe(enemy.getMaxHp() - toDmgValue(enemy.getMaxHp() / 32)); }); }); diff --git a/src/test/abilities/hustle.test.ts b/src/test/abilities/hustle.test.ts index dde310fda2a..b7c3b723c4b 100644 --- a/src/test/abilities/hustle.test.ts +++ b/src/test/abilities/hustle.test.ts @@ -1,14 +1,14 @@ -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { Stat } from "#app/enums/stat.js"; -import { DamagePhase, MoveEffectPhase } from "#app/phases.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { Stat } from "#app/enums/stat"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Hustle", () => { let phaserGame: Phaser.Game; @@ -43,7 +43,7 @@ describe("Abilities - Hustle", () => { vi.spyOn(pikachu, "getBattleStat"); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -56,7 +56,7 @@ describe("Abilities - Hustle", () => { vi.spyOn(pikachu, "getAccuracyMultiplier"); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MoveEffectPhase); expect(pikachu.getAccuracyMultiplier).toHaveReturnedWith(0.8); @@ -70,7 +70,7 @@ describe("Abilities - Hustle", () => { vi.spyOn(pikachu, "getBattleStat"); vi.spyOn(pikachu, "getAccuracyMultiplier"); - game.doAttack(getMovePosition(game.scene, 0, Moves.GIGA_DRAIN)); + game.move.select(Moves.GIGA_DRAIN); await game.phaseInterceptor.to(DamagePhase); expect(pikachu.getBattleStat).toHaveReturnedWith(spatk); @@ -88,7 +88,7 @@ describe("Abilities - Hustle", () => { vi.spyOn(pikachu, "getAccuracyMultiplier"); vi.spyOn(allMoves[Moves.FISSURE], "calculateBattleAccuracy"); - game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); + game.move.select(Moves.FISSURE); await game.phaseInterceptor.to(DamagePhase); expect(enemyPokemon.turnData.damageTaken).toBe(enemyPokemon.getMaxHp()); diff --git a/src/test/abilities/hyper_cutter.test.ts b/src/test/abilities/hyper_cutter.test.ts new file mode 100644 index 00000000000..28fcc2f6085 --- /dev/null +++ b/src/test/abilities/hyper_cutter.test.ts @@ -0,0 +1,57 @@ +import { BattleStat } from "#app/data/battle-stat"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Hyper Cutter", () => { + 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") + .moveset([Moves.SAND_ATTACK, Moves.NOBLE_ROAR, Moves.DEFOG, Moves.OCTOLOCK]) + .ability(Abilities.BALL_FETCH) + .enemySpecies(Species.SHUCKLE) + .enemyAbility(Abilities.HYPER_CUTTER) + .enemyMoveset(SPLASH_ONLY); + }); + + // Reference Link: https://bulbapedia.bulbagarden.net/wiki/Hyper_Cutter_(Ability) + + it("only prevents ATK drops", async () => { + await game.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.OCTOLOCK); + await game.toNextTurn(); + game.move.select(Moves.DEFOG); + await game.toNextTurn(); + game.move.select(Moves.NOBLE_ROAR); + await game.toNextTurn(); + game.move.select(Moves.SAND_ATTACK); + await game.toNextTurn(); + game.override.moveset([Moves.STRING_SHOT]); + game.move.select(Moves.STRING_SHOT); + await game.toNextTurn(); + + expect(enemy.summonData.battleStats[BattleStat.ATK]).toEqual(0); + [BattleStat.ACC, BattleStat.DEF, BattleStat.EVA, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD].forEach((stat: number) => expect(enemy.summonData.battleStats[stat]).toBeLessThan(0)); + }); +}); diff --git a/src/test/abilities/ice_face.test.ts b/src/test/abilities/ice_face.test.ts index cdf8d5928ee..fbc660c27c2 100644 --- a/src/test/abilities/ice_face.test.ts +++ b/src/test/abilities/ice_face.test.ts @@ -1,11 +1,13 @@ -import { QuietFormChangePhase } from "#app/form-change-phase"; -import { MoveEffectPhase, MoveEndPhase, TurnEndPhase, TurnInitPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +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"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-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"; @@ -36,7 +38,7 @@ describe("Abilities - Ice Face", () => { it("takes no damage from physical move and transforms to Noice", async () => { await game.startBattle([Species.HITMONLEE]); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MoveEndPhase); @@ -52,7 +54,7 @@ describe("Abilities - Ice Face", () => { game.override.enemyLevel(1); await game.startBattle([Species.HITMONLEE]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURGING_STRIKES)); + game.move.select(Moves.SURGING_STRIKES); const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined(); @@ -78,7 +80,7 @@ describe("Abilities - Ice Face", () => { it("takes damage from special moves", async () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); + game.move.select(Moves.ICE_BEAM); await game.phaseInterceptor.to(MoveEndPhase); @@ -92,7 +94,7 @@ describe("Abilities - Ice Face", () => { it("takes effects from status moves", async () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.TOXIC_THREAD)); + game.move.select(Moves.TOXIC_THREAD); await game.phaseInterceptor.to(MoveEndPhase); @@ -108,7 +110,7 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + game.move.select(Moves.QUICK_ATTACK); await game.phaseInterceptor.to(MoveEndPhase); @@ -130,7 +132,7 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.EISCUE, Species.NINJASK]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SNOWSCAPE)); + game.move.select(Moves.SNOWSCAPE); await game.phaseInterceptor.to(TurnEndPhase); let eiscue = game.scene.getPlayerPokemon()!; @@ -157,7 +159,7 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.EISCUE]); - game.doAttack(getMovePosition(game.scene, 0, Moves.HAIL)); + game.move.select(Moves.HAIL); const eiscue = game.scene.getPlayerPokemon()!; await game.phaseInterceptor.to(QuietFormChangePhase); @@ -176,7 +178,7 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.EISCUE, Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); + game.move.select(Moves.ICE_BEAM); await game.phaseInterceptor.to(TurnEndPhase); let eiscue = game.scene.getPlayerPokemon()!; @@ -210,7 +212,7 @@ describe("Abilities - Ice Face", () => { expect(eiscue.formIndex).toBe(noiceForm); expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); + game.move.select(Moves.ICE_BEAM); await game.doKillOpponents(); await game.phaseInterceptor.to(TurnEndPhase); game.doSelectModifier(); @@ -225,7 +227,7 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.GASTRO_ACID)); + game.move.select(Moves.GASTRO_ACID); await game.phaseInterceptor.to(TurnEndPhase); @@ -241,7 +243,7 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SKILL_SWAP)); + game.move.select(Moves.SKILL_SWAP); await game.phaseInterceptor.to(TurnEndPhase); @@ -257,7 +259,7 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SIMPLE_BEAM)); + game.move.select(Moves.SIMPLE_BEAM); await game.phaseInterceptor.to(TurnInitPhase); diff --git a/src/test/abilities/intimidate.test.ts b/src/test/abilities/intimidate.test.ts index 2b4c1041bfe..93b663d06da 100644 --- a/src/test/abilities/intimidate.test.ts +++ b/src/test/abilities/intimidate.test.ts @@ -1,17 +1,21 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Mode } from "#app/ui/ui"; import { BattleStat } from "#app/data/battle-stat"; -import { generateStarter, getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; import { Status, StatusEffect } from "#app/data/status-effect"; import { GameModes, getGameMode } from "#app/game-mode"; -import { CommandPhase, DamagePhase, EncounterPhase, EnemyCommandPhase, SelectStarterPhase, TurnInitPhase } from "#app/phases"; +import { CommandPhase } from "#app/phases/command-phase"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { generateStarter } from "#test/utils/gameManagerUtils"; import { SPLASH_ONLY } from "#test/utils/testUtils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Abilities - Intimidate", () => { let phaserGame: Phaser.Game; @@ -212,13 +216,7 @@ describe("Abilities - Intimidate", () => { let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.AERIAL_ACE); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(DamagePhase); await game.killPokemon(game.scene.currentBattle.enemyParty[0]); expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true); @@ -238,13 +236,7 @@ describe("Abilities - Intimidate", () => { let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.AERIAL_ACE); console.log("===to new turn==="); await game.toNextTurn(); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; @@ -263,13 +255,7 @@ describe("Abilities - Intimidate", () => { let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.AERIAL_ACE); console.log("===to new turn==="); await game.toNextTurn(); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; @@ -277,13 +263,7 @@ describe("Abilities - Intimidate", () => { battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.AERIAL_ACE); console.log("===to new turn==="); await game.toNextTurn(); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; @@ -302,13 +282,7 @@ describe("Abilities - Intimidate", () => { let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.AERIAL_ACE); console.log("===to new turn==="); await game.toNextTurn(); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; @@ -316,13 +290,7 @@ describe("Abilities - Intimidate", () => { battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.AERIAL_ACE); console.log("===to new turn==="); await game.toNextTurn(); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; diff --git a/src/test/abilities/intrepid_sword.test.ts b/src/test/abilities/intrepid_sword.test.ts index dcc91421165..18d6c04adbc 100644 --- a/src/test/abilities/intrepid_sword.test.ts +++ b/src/test/abilities/intrepid_sword.test.ts @@ -1,8 +1,8 @@ import { BattleStat } from "#app/data/battle-stat"; -import { CommandPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; +import { CommandPhase } from "#app/phases/command-phase"; 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"; diff --git a/src/test/abilities/libero.test.ts b/src/test/abilities/libero.test.ts index 2581eac068d..16597e90285 100644 --- a/src/test/abilities/libero.test.ts +++ b/src/test/abilities/libero.test.ts @@ -1,18 +1,17 @@ -import { allMoves } from "#app/data/move.js"; -import { Type } from "#app/data/type.js"; -import { Weather, WeatherType } from "#app/data/weather.js"; -import { PlayerPokemon } from "#app/field/pokemon.js"; -import { TurnEndPhase } from "#app/phases.js"; +import { allMoves } from "#app/data/move"; +import { Type } from "#app/data/type"; +import { Weather, WeatherType } from "#app/data/weather"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -49,7 +48,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); @@ -67,12 +66,12 @@ describe("Abilities - Libero", () => { let leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); - game.doAttack(getMovePosition(game.scene, 0, Moves.AGILITY)); + game.move.select(Moves.AGILITY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied.filter((a) => a === Abilities.LIBERO)).toHaveLength(1); @@ -89,7 +88,7 @@ describe("Abilities - Libero", () => { leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); @@ -108,7 +107,7 @@ describe("Abilities - Libero", () => { expect(leadPokemon).not.toBe(undefined); game.scene.arena.weather = new Weather(WeatherType.SUNNY); - game.doAttack(getMovePosition(game.scene, 0, Moves.WEATHER_BALL)); + game.move.select(Moves.WEATHER_BALL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).toContain(Abilities.LIBERO); @@ -131,7 +130,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).toContain(Abilities.LIBERO); @@ -154,7 +153,7 @@ describe("Abilities - Libero", () => { expect(leadPokemon).not.toBe(undefined); game.scene.arena.biomeType = Biome.MOUNTAIN; - game.doAttack(getMovePosition(game.scene, 0, Moves.NATURE_POWER)); + game.move.select(Moves.NATURE_POWER); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.AIR_SLASH); @@ -172,7 +171,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.DIG)); + game.move.select(Moves.DIG); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.DIG); @@ -191,7 +190,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.move.forceMiss(); await game.phaseInterceptor.to(TurnEndPhase); @@ -213,7 +212,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); @@ -232,7 +231,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); @@ -251,7 +250,7 @@ describe("Abilities - Libero", () => { expect(leadPokemon).not.toBe(undefined); leadPokemon.summonData.types = [allMoves[Moves.SPLASH].defaultType]; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); @@ -271,7 +270,7 @@ describe("Abilities - Libero", () => { vi.spyOn(leadPokemon, "isTerastallized").mockReturnValue(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); @@ -289,7 +288,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.STRUGGLE)); + game.move.select(Moves.STRUGGLE); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); @@ -307,7 +306,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.BURN_UP)); + game.move.select(Moves.BURN_UP); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); @@ -326,7 +325,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TRICK_OR_TREAT)); + game.move.select(Moves.TRICK_OR_TREAT); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TRICK_OR_TREAT); @@ -344,7 +343,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.CURSE)); + game.move.select(Moves.CURSE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.CURSE); diff --git a/src/test/abilities/magic_guard.test.ts b/src/test/abilities/magic_guard.test.ts index c86d65ca453..64c1746c7d9 100644 --- a/src/test/abilities/magic_guard.test.ts +++ b/src/test/abilities/magic_guard.test.ts @@ -1,17 +1,16 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; -import { TurnEndPhase } from "#app/phases"; -import { Moves } from "#enums/moves"; -import { ArenaTagType } from "#enums/arena-tag-type"; import { ArenaTagSide, getArenaTag } from "#app/data/arena-tag"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Abilities } from "#enums/abilities"; -import { WeatherType } from "#app/data/weather.js"; import { StatusEffect, getStatusEffectCatchRateMultiplier } from "#app/data/status-effect"; +import { WeatherType } from "#app/data/weather"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Abilities } from "#enums/abilities"; +import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import { SPLASH_ONLY } from "#test/utils/testUtils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; const TIMEOUT = 20 * 1000; // 20 sec timeout @@ -58,7 +57,7 @@ describe("Abilities - Magic Guard", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); @@ -82,7 +81,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); @@ -99,14 +98,14 @@ describe("Abilities - Magic Guard", () => { it( "ability effect should not persist when the ability is replaced", async () => { - game.override.enemyMoveset([Moves.WORRY_SEED,Moves.WORRY_SEED,Moves.WORRY_SEED,Moves.WORRY_SEED]); + game.override.enemyMoveset([Moves.WORRY_SEED, Moves.WORRY_SEED, Moves.WORRY_SEED, Moves.WORRY_SEED]); game.override.statusEffect(StatusEffect.POISON); await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); @@ -126,7 +125,7 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -150,7 +149,7 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -180,7 +179,7 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -206,7 +205,7 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -233,7 +232,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.CURSE)); + game.move.select(Moves.CURSE); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -257,7 +256,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.HIGH_JUMP_KICK)); + game.move.select(Moves.HIGH_JUMP_KICK); await game.move.forceMiss(); await game.phaseInterceptor.to(TurnEndPhase); @@ -276,7 +275,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.TAKE_DOWN)); + game.move.select(Moves.TAKE_DOWN); await game.phaseInterceptor.to(TurnEndPhase); @@ -294,7 +293,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.STRUGGLE)); + game.move.select(Moves.STRUGGLE); await game.phaseInterceptor.to(TurnEndPhase); @@ -313,7 +312,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.STEEL_BEAM)); + game.move.select(Moves.STEEL_BEAM); await game.phaseInterceptor.to(TurnEndPhase); @@ -329,7 +328,7 @@ describe("Abilities - Magic Guard", () => { it("Magic Guard does not prevent self-damage from confusion", async () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.CHARM)); + game.move.select(Moves.CHARM); await game.phaseInterceptor.to(TurnEndPhase); }); @@ -341,7 +340,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); + game.move.select(Moves.BELLY_DRUM); await game.phaseInterceptor.to(TurnEndPhase); @@ -353,7 +352,7 @@ describe("Abilities - Magic Guard", () => { }, TIMEOUT ); - it("Magic Guard prevents damage from abilities with PostTurnHurtIfSleepingAbAttr", async() => { + it("Magic Guard prevents damage from abilities with PostTurnHurtIfSleepingAbAttr", async () => { //Tests the ability Bad Dreams game.override.statusEffect(StatusEffect.SLEEP); //enemy pokemon is given Spore just in case player pokemon somehow awakens during test @@ -364,7 +363,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); @@ -378,7 +377,7 @@ describe("Abilities - Magic Guard", () => { }, TIMEOUT ); - it("Magic Guard prevents damage from abilities with PostFaintContactDamageAbAttr", async() => { + it("Magic Guard prevents damage from abilities with PostFaintContactDamageAbAttr", async () => { //Tests the abilities Innards Out/Aftermath game.override.moveset([Moves.TACKLE]); game.override.enemyAbility(Abilities.AFTERMATH); @@ -390,7 +389,7 @@ describe("Abilities - Magic Guard", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; enemyPokemon.hp = 1; - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); /** @@ -403,7 +402,7 @@ describe("Abilities - Magic Guard", () => { }, TIMEOUT ); - it("Magic Guard prevents damage from abilities with PostDefendContactDamageAbAttr", async() => { + it("Magic Guard prevents damage from abilities with PostDefendContactDamageAbAttr", async () => { //Tests the abilities Iron Barbs/Rough Skin game.override.moveset([Moves.TACKLE]); game.override.enemyAbility(Abilities.IRON_BARBS); @@ -414,7 +413,7 @@ describe("Abilities - Magic Guard", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); /** @@ -427,7 +426,7 @@ describe("Abilities - Magic Guard", () => { }, TIMEOUT ); - it("Magic Guard prevents damage from abilities with ReverseDrainAbAttr", async() => { + it("Magic Guard prevents damage from abilities with ReverseDrainAbAttr", async () => { //Tests the ability Liquid Ooze game.override.moveset([Moves.ABSORB]); game.override.enemyAbility(Abilities.LIQUID_OOZE); @@ -438,7 +437,7 @@ describe("Abilities - Magic Guard", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.ABSORB)); + game.move.select(Moves.ABSORB); await game.phaseInterceptor.to(TurnEndPhase); /** @@ -451,14 +450,14 @@ describe("Abilities - Magic Guard", () => { }, TIMEOUT ); - it("Magic Guard prevents HP loss from abilities with PostWeatherLapseDamageAbAttr", async() => { + it("Magic Guard prevents HP loss from abilities with PostWeatherLapseDamageAbAttr", async () => { //Tests the abilities Solar Power/Dry Skin game.override.passiveAbility(Abilities.SOLAR_POWER); game.override.weather(WeatherType.SUNNY); await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); /** diff --git a/src/test/abilities/moody.test.ts b/src/test/abilities/moody.test.ts new file mode 100644 index 00000000000..9e936e8100a --- /dev/null +++ b/src/test/abilities/moody.test.ts @@ -0,0 +1,93 @@ +import { BattleStat } from "#app/data/battle-stat"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Moody", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + const battleStatsArray = [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD]; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + + game.override + .battleType("single") + .enemySpecies(Species.RATTATA) + .enemyAbility(Abilities.BALL_FETCH) + .enemyPassiveAbility(Abilities.HYDRATION) + .ability(Abilities.MOODY) + .enemyMoveset(SPLASH_ONLY) + .moveset(SPLASH_ONLY); + }); + + it( + "should increase one BattleStat by 2 stages and decrease a different BattleStat by 1 stage", + async () => { + await game.startBattle(); + + const playerPokemon = game.scene.getPlayerPokemon()!; + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + // Find the increased and decreased stats, make sure they are different. + const statChanges = playerPokemon.summonData.battleStats; + const changedStats = battleStatsArray.filter(bs => statChanges[bs] === 2 || statChanges[bs] === -1); + + expect(changedStats).toBeTruthy(); + expect(changedStats.length).toBe(2); + expect(changedStats[0] !== changedStats[1]).toBeTruthy(); + }); + + it( + "should only increase one BattleStat by 2 stages if all BattleStats are at -6", + async () => { + await game.startBattle(); + + const playerPokemon = game.scene.getPlayerPokemon()!; + // Set all BattleStats to -6 + battleStatsArray.forEach(bs => playerPokemon.summonData.battleStats[bs] = -6); + + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + // Should increase one BattleStat by 2 (from -6, meaning it will be -4) + const increasedStat = battleStatsArray.filter(bs => playerPokemon.summonData.battleStats[bs] === -4); + + expect(increasedStat).toBeTruthy(); + expect(increasedStat.length).toBe(1); + }); + + it( + "should only decrease one BattleStat by 1 stage if all BattleStats are at 6", + async () => { + await game.startBattle(); + + const playerPokemon = game.scene.getPlayerPokemon()!; + // Set all BattleStats to 6 + battleStatsArray.forEach(bs => playerPokemon.summonData.battleStats[bs] = 6); + + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + // Should decrease one BattleStat by 1 (from 6, meaning it will be 5) + const decreasedStat = battleStatsArray.filter(bs => playerPokemon.summonData.battleStats[bs] === 5); + expect(decreasedStat).toBeTruthy(); + expect(decreasedStat.length).toBe(1); + }); +}); diff --git a/src/test/abilities/moxie.test.ts b/src/test/abilities/moxie.test.ts index f99068dea41..6a1838c9a98 100644 --- a/src/test/abilities/moxie.test.ts +++ b/src/test/abilities/moxie.test.ts @@ -1,13 +1,11 @@ import { BattleStat } from "#app/data/battle-stat"; import { Stat } from "#app/data/pokemon-stat"; -import { CommandPhase, EnemyCommandPhase, VictoryPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { VictoryPhase } from "#app/phases/victory-phase"; 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"; @@ -35,10 +33,10 @@ describe("Abilities - Moxie", () => { game.override.ability(Abilities.MOXIE); game.override.startingLevel(2000); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it("MOXIE", async() => { + it("MOXIE", async () => { const moveToUse = Moves.AERIAL_ACE; await game.startBattle([ Species.MIGHTYENA, @@ -48,13 +46,7 @@ describe("Abilities - Moxie", () => { let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[Stat.ATK]).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(VictoryPhase); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(1); diff --git a/src/test/abilities/mycelium_might.test.ts b/src/test/abilities/mycelium_might.test.ts index d519eb67626..83396f7950f 100644 --- a/src/test/abilities/mycelium_might.test.ts +++ b/src/test/abilities/mycelium_might.test.ts @@ -1,10 +1,10 @@ -import { MovePhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Abilities } from "#enums/abilities"; import { BattleStat } from "#app/data/battle-stat"; +import { MovePhase } from "#app/phases/move-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 Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -41,14 +41,14 @@ describe("Abilities - Mycelium Might", () => { * https://www.smogon.com/forums/threads/scarlet-violet-battle-mechanics-research.3709545/page-24 **/ - it("If a Pokemon with Mycelium Might uses a status move, it will always move last but the status move will ignore protective abilities", async() => { - await game.startBattle([ Species.REGIELEKI ]); + it("will move last in its priority bracket and ignore protective abilities", async () => { + await game.startBattle([Species.REGIELEKI]); const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyPokemon = game.scene.getEnemyPokemon(); const enemyIndex = enemyPokemon?.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.BABY_DOLL_EYES)); + game.move.select(Moves.BABY_DOLL_EYES); await game.phaseInterceptor.to(MovePhase, false); // The opponent Pokemon (without Mycelium Might) goes first despite having lower speed than the player Pokemon. @@ -63,15 +63,15 @@ describe("Abilities - Mycelium Might", () => { expect(enemyPokemon?.summonData.battleStats[BattleStat.ATK]).toBe(-1); }, 20000); - it("Pokemon with Mycelium Might will go first if a status move that is in a higher priority bracket than the opponent's move is used", async() => { + it("will still go first if a status move that is in a higher priority bracket than the opponent's move is used", async () => { game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); - await game.startBattle([ Species.REGIELEKI ]); + await game.startBattle([Species.REGIELEKI]); const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyPokemon = game.scene.getEnemyPokemon(); const enemyIndex = enemyPokemon?.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.BABY_DOLL_EYES)); + game.move.select(Moves.BABY_DOLL_EYES); await game.phaseInterceptor.to(MovePhase, false); // The player Pokemon (with M.M.) goes first because its move is still within a higher priority bracket than its opponent. @@ -85,13 +85,13 @@ describe("Abilities - Mycelium Might", () => { expect(enemyPokemon?.summonData.battleStats[BattleStat.ATK]).toBe(-1); }, 20000); - it("Order is established normally if the Pokemon uses a non-status move", async() => { - await game.startBattle([ Species.REGIELEKI ]); + it("will not affect non-status moves", async () => { + await game.startBattle([Species.REGIELEKI]); const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + game.move.select(Moves.QUICK_ATTACK); await game.phaseInterceptor.to(MovePhase, false); // The player Pokemon (with M.M.) goes first because it has a higher speed and did not use a status move. diff --git a/src/test/abilities/parental_bond.test.ts b/src/test/abilities/parental_bond.test.ts index 182f780763c..1404f597ccf 100644 --- a/src/test/abilities/parental_bond.test.ts +++ b/src/test/abilities/parental_bond.test.ts @@ -1,16 +1,21 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { StatusEffect } from "#app/data/status-effect.js"; -import { Type } from "#app/data/type.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { BerryPhase, CommandPhase, DamagePhase, MoveEffectPhase, MoveEndPhase, TurnEndPhase } from "#app/phases.js"; +import { BattleStat } from "#app/data/battle-stat"; +import { StatusEffect } from "#app/data/status-effect"; +import { Type } from "#app/data/type"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { toDmgValue } from "#app/utils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -55,7 +60,7 @@ describe("Abilities - Parental Bond", () => { let enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -68,7 +73,7 @@ describe("Abilities - Parental Bond", () => { const secondStrikeDamage = enemyStartingHp - enemyPokemon.hp; expect(leadPokemon.turnData.hitCount).toBe(2); - expect(secondStrikeDamage).toBe(Math.ceil(0.25 * firstStrikeDamage)); + expect(secondStrikeDamage).toBe(toDmgValue(0.25 * firstStrikeDamage)); }, TIMEOUT ); @@ -86,7 +91,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.POWER_UP_PUNCH)); + game.move.select(Moves.POWER_UP_PUNCH); await game.phaseInterceptor.to(BerryPhase, false); @@ -108,7 +113,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.BABY_DOLL_EYES)); + game.move.select(Moves.BABY_DOLL_EYES); await game.phaseInterceptor.to(BerryPhase, false); expect(enemyPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-1); @@ -128,7 +133,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.DOUBLE_HIT)); + game.move.select(Moves.DOUBLE_HIT); await game.move.forceHit(); await game.phaseInterceptor.to(BerryPhase, false); @@ -150,7 +155,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SELF_DESTRUCT)); + game.move.select(Moves.SELF_DESTRUCT); await game.phaseInterceptor.to(DamagePhase, false); @@ -171,7 +176,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.ROLLOUT)); + game.move.select(Moves.ROLLOUT); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase, false); @@ -195,7 +200,7 @@ describe("Abilities - Parental Bond", () => { const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(BerryPhase, false); expect(enemyPokemon.hp).toBe(enemyStartingHp - 80); @@ -206,7 +211,7 @@ describe("Abilities - Parental Bond", () => { "ability should not apply multiplier to counter moves", async () => { game.override.moveset([Moves.COUNTER]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); await game.startBattle([Species.CHARIZARD]); @@ -219,14 +224,14 @@ describe("Abilities - Parental Bond", () => { const playerStartingHp = leadPokemon.hp; const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.COUNTER)); + game.move.select(Moves.COUNTER); await game.phaseInterceptor.to(DamagePhase); const playerDamage = playerStartingHp - leadPokemon.hp; await game.phaseInterceptor.to(BerryPhase, false); - expect(enemyPokemon.hp).toBe(enemyStartingHp - 4*playerDamage); + expect(enemyPokemon.hp).toBe(enemyStartingHp - 4 * playerDamage); }, TIMEOUT ); @@ -246,10 +251,10 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon.length).toBe(2); enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); - game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE)); + game.move.select(Moves.EARTHQUAKE); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.EARTHQUAKE)); + game.move.select(Moves.EARTHQUAKE, 1); await game.phaseInterceptor.to(BerryPhase, false); playerPokemon.forEach(p => expect(p.turnData.hitCount).toBe(1)); @@ -269,7 +274,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE)); + game.move.select(Moves.EARTHQUAKE); await game.phaseInterceptor.to(DamagePhase, false); expect(leadPokemon.turnData.hitCount).toBe(2); @@ -289,7 +294,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.MIND_BLOWN)); + game.move.select(Moves.MIND_BLOWN); await game.phaseInterceptor.to(DamagePhase, false); @@ -298,7 +303,7 @@ describe("Abilities - Parental Bond", () => { // This test will time out if the user faints await game.phaseInterceptor.to(BerryPhase, false); - expect(leadPokemon.hp).toBe(Math.floor(leadPokemon.getMaxHp()/2)); + expect(leadPokemon.hp).toBe(toDmgValue(leadPokemon.getMaxHp() / 2)); }, TIMEOUT ); @@ -315,7 +320,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.BURN_UP)); + game.move.select(Moves.BURN_UP); await game.phaseInterceptor.to(DamagePhase); @@ -333,7 +338,7 @@ describe("Abilities - Parental Bond", () => { "Moves boosted by this ability and Multi-Lens should strike 4 times", async () => { game.override.moveset([Moves.TACKLE]); - game.override.startingHeldItems([{name: "MULTI_LENS", count: 1}]); + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); await game.startBattle([Species.CHARIZARD]); @@ -343,7 +348,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(DamagePhase); @@ -355,7 +360,7 @@ describe("Abilities - Parental Bond", () => { "Super Fang boosted by this ability and Multi-Lens should strike twice", async () => { game.override.moveset([Moves.SUPER_FANG]); - game.override.startingHeldItems([{name: "MULTI_LENS", count: 1}]); + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); await game.startBattle([Species.CHARIZARD]); @@ -367,7 +372,7 @@ describe("Abilities - Parental Bond", () => { const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.SUPER_FANG)); + game.move.select(Moves.SUPER_FANG); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -384,7 +389,7 @@ describe("Abilities - Parental Bond", () => { "Seismic Toss boosted by this ability and Multi-Lens should strike twice", async () => { game.override.moveset([Moves.SEISMIC_TOSS]); - game.override.startingHeldItems([{name: "MULTI_LENS", count: 1}]); + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); await game.startBattle([Species.CHARIZARD]); @@ -396,7 +401,7 @@ describe("Abilities - Parental Bond", () => { const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.SEISMIC_TOSS)); + game.move.select(Moves.SEISMIC_TOSS); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -422,7 +427,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.HYPER_BEAM)); + game.move.select(Moves.HYPER_BEAM); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -450,7 +455,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.ANCHOR_SHOT)); + game.move.select(Moves.ANCHOR_SHOT); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -480,7 +485,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SMACK_DOWN)); + game.move.select(Moves.SMACK_DOWN); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -507,7 +512,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); + game.move.select(Moves.U_TURN); await game.move.forceHit(); await game.phaseInterceptor.to(MoveEffectPhase); @@ -531,7 +536,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.WAKE_UP_SLAP)); + game.move.select(Moves.WAKE_UP_SLAP); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -549,7 +554,7 @@ describe("Abilities - Parental Bond", () => { "ability should not cause user to hit into King's Shield more than once", async () => { game.override.moveset([Moves.TACKLE]); - game.override.enemyMoveset([Moves.KINGS_SHIELD,Moves.KINGS_SHIELD,Moves.KINGS_SHIELD,Moves.KINGS_SHIELD]); + game.override.enemyMoveset([Moves.KINGS_SHIELD, Moves.KINGS_SHIELD, Moves.KINGS_SHIELD, Moves.KINGS_SHIELD]); await game.startBattle([Species.CHARIZARD]); @@ -559,7 +564,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(BerryPhase, false); @@ -581,7 +586,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_GUN)); + game.move.select(Moves.WATER_GUN); await game.phaseInterceptor.to(BerryPhase, false); @@ -594,7 +599,7 @@ describe("Abilities - Parental Bond", () => { async () => { game.override.battleType("double"); game.override.moveset([Moves.EARTHQUAKE, Moves.SPLASH]); - game.override.startingHeldItems([{name: "MULTI_LENS", count: 1}]); + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); await game.startBattle([Species.CHARIZARD, Species.PIDGEOT]); @@ -608,10 +613,10 @@ describe("Abilities - Parental Bond", () => { const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE)); + game.move.select(Moves.EARTHQUAKE); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -620,7 +625,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to(BerryPhase, false); - enemyPokemon.forEach((p, i) => expect(enemyStartingHp[i] - p.hp).toBe(2*enemyFirstHitDamage[i])); + enemyPokemon.forEach((p, i) => expect(enemyStartingHp[i] - p.hp).toBe(2 * enemyFirstHitDamage[i])); }, TIMEOUT ); diff --git a/src/test/abilities/pastel_veil.test.ts b/src/test/abilities/pastel_veil.test.ts index e3d52a720b3..ba90c7e3b3f 100644 --- a/src/test/abilities/pastel_veil.test.ts +++ b/src/test/abilities/pastel_veil.test.ts @@ -1,14 +1,14 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; -import { CommandPhase, TurnEndPhase } from "#app/phases"; +import { BattlerIndex } from "#app/battle"; +import { StatusEffect } from "#app/data/status-effect"; +import { Abilities } from "#app/enums/abilities"; +import { CommandPhase } from "#app/phases/command-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { StatusEffect } from "#app/data/status-effect.js"; -import { allAbilities } from "#app/data/ability.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { BattlerIndex } from "#app/battle.js"; +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 { SPLASH_ONLY } from "../utils/testUtils"; describe("Abilities - Pastel Veil", () => { let phaserGame: Phaser.Game; @@ -26,50 +26,49 @@ describe("Abilities - Pastel Veil", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); - game.override.moveset([Moves.SPLASH]); - game.override.enemyAbility(Abilities.BALL_FETCH); - game.override.enemySpecies(Species.MAGIKARP); - game.override.enemyMoveset([Moves.TOXIC_THREAD, Moves.TOXIC_THREAD, Moves.TOXIC_THREAD, Moves.TOXIC_THREAD]); + game.override + .battleType("double") + .moveset([Moves.TOXIC_THREAD, Moves.SPLASH]) + .enemyAbility(Abilities.BALL_FETCH) + .enemySpecies(Species.SUNKERN) + .enemyMoveset(SPLASH_ONLY); }); it("prevents the user and its allies from being afflicted by poison", async () => { - await game.startBattle([Species.GALAR_PONYTA, Species.MAGIKARP]); - const ponyta = game.scene.getPlayerField()[0]; - - vi.spyOn(ponyta, "getAbility").mockReturnValue(allAbilities[Abilities.PASTEL_VEIL]); + await game.startBattle([Species.MAGIKARP, Species.GALAR_PONYTA]); + const ponyta = game.scene.getPlayerField()[1]; + const magikarp = game.scene.getPlayerField()[0]; + ponyta.abilityIndex = 1; expect(ponyta.hasAbility(Abilities.PASTEL_VEIL)).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.TOXIC_THREAD, 1, BattlerIndex.PLAYER); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getPlayerField().every(p => p.status?.effect)).toBe(false); + expect(magikarp.status?.effect).toBeUndefined(); }); it("it heals the poisoned status condition of allies if user is sent out into battle", async () => { - await game.startBattle([Species.MAGIKARP, Species.MAGIKARP, Species.GALAR_PONYTA]); - const ponyta = game.scene.getParty().find(p => p.species.speciesId === Species.GALAR_PONYTA)!; - - vi.spyOn(ponyta, "getAbility").mockReturnValue(allAbilities[Abilities.PASTEL_VEIL]); + await game.startBattle([Species.MAGIKARP, Species.FEEBAS, Species.GALAR_PONYTA]); + const ponyta = game.scene.getParty()[2]; + const magikarp = game.scene.getPlayerField()[0]; + ponyta.abilityIndex = 1; expect(ponyta.hasAbility(Abilities.PASTEL_VEIL)).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.TOXIC_THREAD, 1, BattlerIndex.PLAYER); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getPlayerField().some(p => p.status?.effect === StatusEffect.POISON)).toBe(true); - - const poisonedMon = game.scene.getPlayerField().find(p => p.status?.effect === StatusEffect.POISON); + expect(magikarp.status?.effect).toBe(StatusEffect.POISON); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, (poisonedMon!.getBattlerIndex() as BattlerIndex.PLAYER | BattlerIndex.PLAYER_2), Moves.SPLASH)); + game.move.select(Moves.SPLASH); game.doSwitchPokemon(2); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getPlayerField().every(p => p.status?.effect)).toBe(false); + expect(magikarp.status?.effect).toBeUndefined(); }); }); diff --git a/src/test/abilities/power_construct.test.ts b/src/test/abilities/power_construct.test.ts index dd8fd836e51..ec37bc96c2f 100644 --- a/src/test/abilities/power_construct.test.ts +++ b/src/test/abilities/power_construct.test.ts @@ -1,11 +1,10 @@ -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { QuietFormChangePhase } from "#app/form-change-phase.js"; -import { TurnEndPhase } from "#app/phases.js"; +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 { getMovePosition } from "#test/utils/gameManagerUtils"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; const TIMEOUT = 20 * 1000; @@ -53,7 +52,7 @@ describe("Abilities - POWER CONSTRUCT", () => { zygarde!.status = new Status(StatusEffect.FAINT); expect(zygarde!.isFainted()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to(TurnEndPhase); game.doSelectModifier(); diff --git a/src/test/abilities/power_spot.test.ts b/src/test/abilities/power_spot.test.ts index 368f8a48110..b83284c0bac 100644 --- a/src/test/abilities/power_spot.test.ts +++ b/src/test/abilities/power_spot.test.ts @@ -1,13 +1,13 @@ -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { MoveEffectPhase, TurnEndPhase } from "#app/phases.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Power Spot", () => { let phaserGame: Phaser.Game; @@ -41,8 +41,8 @@ describe("Abilities - Power Spot", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); await game.startBattle([Species.REGIELEKI, Species.STONJOURNER]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DAZZLING_GLEAM)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.DAZZLING_GLEAM); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower * powerSpotMultiplier); @@ -55,8 +55,8 @@ describe("Abilities - Power Spot", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); await game.startBattle([Species.REGIELEKI, Species.STONJOURNER]); - game.doAttack(getMovePosition(game.scene, 0, Moves.BREAKING_SWIPE)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.BREAKING_SWIPE); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower * powerSpotMultiplier); @@ -69,8 +69,8 @@ describe("Abilities - Power Spot", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); await game.startBattle([Species.STONJOURNER, Species.REGIELEKI]); - game.doAttack(getMovePosition(game.scene, 0, Moves.BREAKING_SWIPE)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.BREAKING_SWIPE); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower); diff --git a/src/test/abilities/protean.test.ts b/src/test/abilities/protean.test.ts index 78768ce32db..a7c6799132f 100644 --- a/src/test/abilities/protean.test.ts +++ b/src/test/abilities/protean.test.ts @@ -1,18 +1,17 @@ -import { allMoves } from "#app/data/move.js"; -import { Type } from "#app/data/type.js"; -import { Weather, WeatherType } from "#app/data/weather.js"; -import { PlayerPokemon } from "#app/field/pokemon.js"; -import { TurnEndPhase } from "#app/phases.js"; +import { allMoves } from "#app/data/move"; +import { Type } from "#app/data/type"; +import { Weather, WeatherType } from "#app/data/weather"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -49,7 +48,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); @@ -67,12 +66,12 @@ describe("Abilities - Protean", () => { let leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); - game.doAttack(getMovePosition(game.scene, 0, Moves.AGILITY)); + game.move.select(Moves.AGILITY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied.filter((a) => a === Abilities.PROTEAN)).toHaveLength(1); @@ -89,7 +88,7 @@ describe("Abilities - Protean", () => { leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); @@ -108,7 +107,7 @@ describe("Abilities - Protean", () => { expect(leadPokemon).not.toBe(undefined); game.scene.arena.weather = new Weather(WeatherType.SUNNY); - game.doAttack(getMovePosition(game.scene, 0, Moves.WEATHER_BALL)); + game.move.select(Moves.WEATHER_BALL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).toContain(Abilities.PROTEAN); @@ -131,7 +130,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).toContain(Abilities.PROTEAN); @@ -154,7 +153,7 @@ describe("Abilities - Protean", () => { expect(leadPokemon).not.toBe(undefined); game.scene.arena.biomeType = Biome.MOUNTAIN; - game.doAttack(getMovePosition(game.scene, 0, Moves.NATURE_POWER)); + game.move.select(Moves.NATURE_POWER); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.AIR_SLASH); @@ -172,7 +171,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.DIG)); + game.move.select(Moves.DIG); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.DIG); @@ -191,7 +190,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.move.forceMiss(); await game.phaseInterceptor.to(TurnEndPhase); @@ -213,7 +212,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); @@ -232,7 +231,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); @@ -251,7 +250,7 @@ describe("Abilities - Protean", () => { expect(leadPokemon).not.toBe(undefined); leadPokemon.summonData.types = [allMoves[Moves.SPLASH].defaultType]; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.PROTEAN); @@ -271,7 +270,7 @@ describe("Abilities - Protean", () => { vi.spyOn(leadPokemon, "isTerastallized").mockReturnValue(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.PROTEAN); @@ -289,7 +288,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.STRUGGLE)); + game.move.select(Moves.STRUGGLE); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.PROTEAN); @@ -307,7 +306,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.BURN_UP)); + game.move.select(Moves.BURN_UP); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.PROTEAN); @@ -326,7 +325,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TRICK_OR_TREAT)); + game.move.select(Moves.TRICK_OR_TREAT); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TRICK_OR_TREAT); @@ -344,7 +343,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.CURSE)); + game.move.select(Moves.CURSE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.CURSE); diff --git a/src/test/abilities/quick_draw.test.ts b/src/test/abilities/quick_draw.test.ts index 75bb9ec6a0a..00d344ed333 100644 --- a/src/test/abilities/quick_draw.test.ts +++ b/src/test/abilities/quick_draw.test.ts @@ -1,10 +1,9 @@ import { allAbilities, BypassSpeedChanceAbAttr } from "#app/data/ability"; -import { FaintPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { FaintPhase } from "#app/phases/faint-phase"; 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, test, vi } from "vitest"; @@ -47,7 +46,7 @@ describe("Abilities - Quick Draw", () => { pokemon.hp = 1; enemy.hp = 1; - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(FaintPhase, false); expect(pokemon.isFainted()).toBe(false); @@ -67,7 +66,7 @@ describe("Abilities - Quick Draw", () => { pokemon.hp = 1; enemy.hp = 1; - game.doAttack(getMovePosition(game.scene, 0, Moves.TAIL_WHIP)); + game.move.select(Moves.TAIL_WHIP); await game.phaseInterceptor.to(FaintPhase, false); expect(pokemon.isFainted()).toBe(true); @@ -87,7 +86,7 @@ describe("Abilities - Quick Draw", () => { pokemon.hp = 1; enemy.hp = 1; - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(FaintPhase, false); expect(pokemon.isFainted()).toBe(true); diff --git a/src/test/abilities/sand_spit.test.ts b/src/test/abilities/sand_spit.test.ts index 59d311adb80..041e20faf7f 100644 --- a/src/test/abilities/sand_spit.test.ts +++ b/src/test/abilities/sand_spit.test.ts @@ -1,11 +1,10 @@ -import GameManager from "#test/utils/gameManager"; +import { WeatherType } from "#app/enums/weather-type"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { WeatherType } from "#app/enums/weather-type.js"; describe("Abilities - Sand Spit", () => { @@ -35,21 +34,21 @@ describe("Abilities - Sand Spit", () => { game.override.moveset([Moves.SPLASH, Moves.COIL]); }); - it("should trigger when hit with damaging move", async() => { + it("should trigger when hit with damaging move", async () => { game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.toNextTurn(); expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SANDSTORM); }, 20000); - it("should not trigger when targetted with status moves", async() => { + it("should not trigger when targetted with status moves", async () => { game.override.enemyMoveset(Array(4).fill(Moves.GROWL)); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.COIL)); + game.move.select(Moves.COIL); await game.toNextTurn(); expect(game.scene.arena.weather?.weatherType).not.toBe(WeatherType.SANDSTORM); diff --git a/src/test/abilities/sand_veil.test.ts b/src/test/abilities/sand_veil.test.ts index 6aab362634a..2336e2b50de 100644 --- a/src/test/abilities/sand_veil.test.ts +++ b/src/test/abilities/sand_veil.test.ts @@ -1,14 +1,15 @@ -import { BattleStatMultiplierAbAttr, allAbilities } from "#app/data/ability.js"; -import { BattleStat } from "#app/data/battle-stat.js"; -import { WeatherType } from "#app/data/weather.js"; -import { CommandPhase, MoveEffectPhase, MoveEndPhase } from "#app/phases.js"; +import { BattleStatMultiplierAbAttr, allAbilities } from "#app/data/ability"; +import { BattleStat } from "#app/data/battle-stat"; +import { WeatherType } from "#app/data/weather"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; 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, test, vi } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; const TIMEOUT = 20 * 1000; @@ -50,7 +51,7 @@ describe("Abilities - Sand Veil", () => { const sandVeilAttr = allAbilities[Abilities.SAND_VEIL].getAttrs(BattleStatMultiplierAbAttr)[0]; vi.spyOn(sandVeilAttr, "applyBattleStat").mockImplementation( - (pokemon, passive, battleStat, statValue, args) => { + (pokemon, passive, simulated, battleStat, statValue, args) => { if (battleStat === BattleStat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) { statValue.value *= -1; // will make all attacks miss return true; @@ -62,11 +63,11 @@ describe("Abilities - Sand Veil", () => { expect(leadPokemon[0].hasAbility(Abilities.SAND_VEIL)).toBe(true); expect(leadPokemon[1].hasAbility(Abilities.SAND_VEIL)).toBe(false); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase, false); diff --git a/src/test/abilities/sap_sipper.test.ts b/src/test/abilities/sap_sipper.test.ts index 6fbe57978e9..f9c20e85eab 100644 --- a/src/test/abilities/sap_sipper.test.ts +++ b/src/test/abilities/sap_sipper.test.ts @@ -1,12 +1,12 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { TerrainType } from "#app/data/terrain.js"; -import { MoveEndPhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { TerrainType } from "#app/data/terrain"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-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"; @@ -31,7 +31,7 @@ describe("Abilities - Sap Sipper", () => { game.override.disableCrits(); }); - it("raise attack 1 level and block effects when activated against a grass attack", async() => { + it("raise attack 1 level and block effects when activated against a grass attack", async () => { const moveToUse = Moves.LEAFAGE; const enemyAbility = Abilities.SAP_SIPPER; @@ -44,7 +44,7 @@ describe("Abilities - Sap Sipper", () => { const startingOppHp = game.scene.currentBattle.enemyParty[0].hp; - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); @@ -52,7 +52,7 @@ describe("Abilities - Sap Sipper", () => { expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(1); }); - it("raise attack 1 level and block effects when activated against a grass status move", async() => { + it("raise attack 1 level and block effects when activated against a grass status move", async () => { const moveToUse = Moves.SPORE; const enemyAbility = Abilities.SAP_SIPPER; @@ -63,7 +63,7 @@ describe("Abilities - Sap Sipper", () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); @@ -71,7 +71,7 @@ describe("Abilities - Sap Sipper", () => { expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(1); }); - it("do not activate against status moves that target the field", async() => { + it("do not activate against status moves that target the field", async () => { const moveToUse = Moves.GRASSY_TERRAIN; const enemyAbility = Abilities.SAP_SIPPER; @@ -82,7 +82,7 @@ describe("Abilities - Sap Sipper", () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); @@ -91,7 +91,7 @@ describe("Abilities - Sap Sipper", () => { expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(0); }); - it("activate once against multi-hit grass attacks", async() => { + it("activate once against multi-hit grass attacks", async () => { const moveToUse = Moves.BULLET_SEED; const enemyAbility = Abilities.SAP_SIPPER; @@ -104,7 +104,7 @@ describe("Abilities - Sap Sipper", () => { const startingOppHp = game.scene.currentBattle.enemyParty[0].hp; - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); @@ -112,7 +112,7 @@ describe("Abilities - Sap Sipper", () => { expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(1); }); - it("do not activate against status moves that target the user", async() => { + it("do not activate against status moves that target the user", async () => { const moveToUse = Moves.SPIKY_SHIELD; const ability = Abilities.SAP_SIPPER; @@ -124,7 +124,7 @@ describe("Abilities - Sap Sipper", () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(MoveEndPhase); @@ -138,7 +138,7 @@ describe("Abilities - Sap Sipper", () => { // TODO Add METRONOME outcome override // To run this testcase, manually modify the METRONOME move to always give SAP_SIPPER, then uncomment - it.todo("activate once against multi-hit grass attacks (metronome)", async() => { + it.todo("activate once against multi-hit grass attacks (metronome)", async () => { const moveToUse = Moves.METRONOME; const enemyAbility = Abilities.SAP_SIPPER; @@ -151,7 +151,7 @@ describe("Abilities - Sap Sipper", () => { const startingOppHp = game.scene.currentBattle.enemyParty[0].hp; - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/schooling.test.ts b/src/test/abilities/schooling.test.ts index e55b7795006..ad9663bf8e5 100644 --- a/src/test/abilities/schooling.test.ts +++ b/src/test/abilities/schooling.test.ts @@ -1,11 +1,10 @@ -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { QuietFormChangePhase } from "#app/form-change-phase.js"; -import { TurnEndPhase } from "#app/phases.js"; +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 { getMovePosition } from "#test/utils/gameManagerUtils"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; const TIMEOUT = 20 * 1000; @@ -53,7 +52,7 @@ describe("Abilities - SCHOOLING", () => { wishiwashi.status = new Status(StatusEffect.FAINT); expect(wishiwashi.isFainted()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to(TurnEndPhase); game.doSelectModifier(); diff --git a/src/test/abilities/screen_cleaner.test.ts b/src/test/abilities/screen_cleaner.test.ts index a73f56dd3eb..3c0d12a06ea 100644 --- a/src/test/abilities/screen_cleaner.test.ts +++ b/src/test/abilities/screen_cleaner.test.ts @@ -1,10 +1,10 @@ -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import { PostSummonPhase, TurnEndPhase, } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import { PostSummonPhase } from "#app/phases/post-summon-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 Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -35,7 +35,7 @@ describe("Abilities - Screen Cleaner", () => { await game.startBattle([Species.MAGIKARP, Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.HAIL)); + game.move.select(Moves.HAIL); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.AURORA_VEIL)).toBeDefined(); @@ -52,7 +52,7 @@ describe("Abilities - Screen Cleaner", () => { await game.startBattle([Species.MAGIKARP, Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.LIGHT_SCREEN)).toBeDefined(); @@ -69,7 +69,7 @@ describe("Abilities - Screen Cleaner", () => { await game.startBattle([Species.MAGIKARP, Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.REFLECT)).toBeDefined(); diff --git a/src/test/abilities/serene_grace.test.ts b/src/test/abilities/serene_grace.test.ts index d46587e45c7..7316b2ea920 100644 --- a/src/test/abilities/serene_grace.test.ts +++ b/src/test/abilities/serene_grace.test.ts @@ -1,17 +1,14 @@ +import { BattlerIndex } from "#app/battle"; import { applyAbAttrs, MoveEffectChanceMultiplierAbAttr } from "#app/data/ability"; import { Stat } from "#app/data/pokemon-stat"; -import { CommandPhase, MoveEffectPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import * as Utils from "#app/utils"; 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 { BattlerIndex } from "#app/battle.js"; describe("Abilities - Serene Grace", () => { @@ -35,10 +32,10 @@ describe("Abilities - Serene Grace", () => { game.override.enemySpecies(Species.ONIX); game.override.startingLevel(100); game.override.moveset(movesToUse); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it("Move chance without Serene Grace", async() => { + it("Move chance without Serene Grace", async () => { const moveToUse = Moves.AIR_SLASH; await game.startBattle([ Species.PIDGEOT @@ -48,13 +45,7 @@ describe("Abilities - Serene Grace", () => { game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -66,12 +57,12 @@ describe("Abilities - Serene Grace", () => { const chance = new Utils.IntegerHolder(move.chance); console.log(move.chance + " Their ability is " + phase.getUserPokemon()!.getAbility().name); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false); expect(chance.value).toBe(30); }, 20000); - it("Move chance with Serene Grace", async() => { + it("Move chance with Serene Grace", async () => { const moveToUse = Moves.AIR_SLASH; game.override.ability(Abilities.SERENE_GRACE); await game.startBattle([ @@ -81,13 +72,7 @@ describe("Abilities - Serene Grace", () => { game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -98,7 +83,7 @@ describe("Abilities - Serene Grace", () => { expect(move.id).toBe(Moves.AIR_SLASH); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false); expect(chance.value).toBe(60); }, 20000); diff --git a/src/test/abilities/sheer_force.test.ts b/src/test/abilities/sheer_force.test.ts index 50a0f0b63fb..f73b749dac2 100644 --- a/src/test/abilities/sheer_force.test.ts +++ b/src/test/abilities/sheer_force.test.ts @@ -1,17 +1,14 @@ +import { BattlerIndex } from "#app/battle"; import { applyAbAttrs, applyPostDefendAbAttrs, applyPreAttackAbAttrs, MoveEffectChanceMultiplierAbAttr, MovePowerBoostAbAttr, PostDefendTypeChangeAbAttr } from "#app/data/ability"; import { Stat } from "#app/data/pokemon-stat"; -import { CommandPhase, MoveEffectPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import * as Utils from "#app/utils"; 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 { BattlerIndex } from "#app/battle.js"; describe("Abilities - Sheer Force", () => { @@ -35,10 +32,10 @@ describe("Abilities - Sheer Force", () => { game.override.enemySpecies(Species.ONIX); game.override.startingLevel(100); game.override.moveset(movesToUse); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it("Sheer Force", async() => { + it("Sheer Force", async () => { const moveToUse = Moves.AIR_SLASH; game.override.ability(Abilities.SHEER_FORCE); await game.startBattle([ @@ -49,13 +46,7 @@ describe("Abilities - Sheer Force", () => { game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -68,16 +59,16 @@ describe("Abilities - Sheer Force", () => { const power = new Utils.IntegerHolder(move.power); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); - applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false); + applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power); expect(chance.value).toBe(0); - expect(power.value).toBe(move.power * 5461/4096); + expect(power.value).toBe(move.power * 5461 / 4096); }, 20000); - it("Sheer Force with exceptions including binding moves", async() => { + it("Sheer Force with exceptions including binding moves", async () => { const moveToUse = Moves.BIND; game.override.ability(Abilities.SHEER_FORCE); await game.startBattle([ @@ -88,13 +79,7 @@ describe("Abilities - Sheer Force", () => { game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -107,8 +92,8 @@ describe("Abilities - Sheer Force", () => { const power = new Utils.IntegerHolder(move.power); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); - applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false); + applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power); expect(chance.value).toBe(-1); expect(power.value).toBe(move.power); @@ -116,7 +101,7 @@ describe("Abilities - Sheer Force", () => { }, 20000); - it("Sheer Force with moves with no secondary effect", async() => { + it("Sheer Force with moves with no secondary effect", async () => { const moveToUse = Moves.TACKLE; game.override.ability(Abilities.SHEER_FORCE); await game.startBattle([ @@ -127,13 +112,7 @@ describe("Abilities - Sheer Force", () => { game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -146,8 +125,8 @@ describe("Abilities - Sheer Force", () => { const power = new Utils.IntegerHolder(move.power); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); - applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false); + applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power); expect(chance.value).toBe(-1); expect(power.value).toBe(move.power); @@ -155,10 +134,10 @@ describe("Abilities - Sheer Force", () => { }, 20000); - it("Sheer Force Disabling Specific Abilities", async() => { + it("Sheer Force Disabling Specific Abilities", async () => { const moveToUse = Moves.CRUSH_CLAW; game.override.enemyAbility(Abilities.COLOR_CHANGE); - game.override.startingHeldItems([{name: "KINGS_ROCK", count: 1}]); + game.override.startingHeldItems([{ name: "KINGS_ROCK", count: 1 }]); game.override.ability(Abilities.SHEER_FORCE); await game.startBattle([ Species.PIDGEOT @@ -168,13 +147,7 @@ describe("Abilities - Sheer Force", () => { game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -190,12 +163,12 @@ describe("Abilities - Sheer Force", () => { const target = phase.getTarget()!; const opponentType = target.getTypes()[0]; - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, chance, move, target, false); - applyPreAttackAbAttrs(MovePowerBoostAbAttr, user, target, move, power); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, chance, move, target, false); + applyPreAttackAbAttrs(MovePowerBoostAbAttr, user, target, move, false, power); applyPostDefendAbAttrs(PostDefendTypeChangeAbAttr, target, user, move, target.apply(user, move)); expect(chance.value).toBe(0); - expect(power.value).toBe(move.power * 5461/4096); + expect(power.value).toBe(move.power * 5461 / 4096); expect(target.getTypes().length).toBe(2); expect(target.getTypes()[0]).toBe(opponentType); diff --git a/src/test/abilities/shield_dust.test.ts b/src/test/abilities/shield_dust.test.ts index f1534551e92..14770c49427 100644 --- a/src/test/abilities/shield_dust.test.ts +++ b/src/test/abilities/shield_dust.test.ts @@ -1,17 +1,14 @@ +import { BattlerIndex } from "#app/battle"; import { applyAbAttrs, applyPreDefendAbAttrs, IgnoreMoveEffectsAbAttr, MoveEffectChanceMultiplierAbAttr } from "#app/data/ability"; import { Stat } from "#app/data/pokemon-stat"; -import { CommandPhase, MoveEffectPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import * as Utils from "#app/utils"; 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 { BattlerIndex } from "#app/battle.js"; describe("Abilities - Shield Dust", () => { @@ -36,10 +33,10 @@ describe("Abilities - Shield Dust", () => { game.override.enemyAbility(Abilities.SHIELD_DUST); game.override.startingLevel(100); game.override.moveset(movesToUse); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it("Shield Dust", async() => { + it("Shield Dust", async () => { const moveToUse = Moves.AIR_SLASH; await game.startBattle([ Species.PIDGEOT @@ -49,13 +46,7 @@ describe("Abilities - Shield Dust", () => { game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -66,8 +57,8 @@ describe("Abilities - Shield Dust", () => { expect(move.id).toBe(Moves.AIR_SLASH); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); - applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getTarget()!, phase.getUserPokemon()!, null!, null!, chance); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false); + applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getTarget()!, phase.getUserPokemon()!, null, null, false, chance); expect(chance.value).toBe(0); }, 20000); diff --git a/src/test/abilities/shields_down.test.ts b/src/test/abilities/shields_down.test.ts index 4d85e8aa47c..9bfec23ddf1 100644 --- a/src/test/abilities/shields_down.test.ts +++ b/src/test/abilities/shields_down.test.ts @@ -1,11 +1,10 @@ -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { QuietFormChangePhase } from "#app/form-change-phase.js"; -import { TurnEndPhase } from "#app/phases.js"; +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 { getMovePosition } from "#test/utils/gameManagerUtils"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; const TIMEOUT = 20 * 1000; @@ -53,7 +52,7 @@ describe("Abilities - SHIELDS DOWN", () => { minior.status = new Status(StatusEffect.FAINT); expect(minior.isFainted()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to(TurnEndPhase); game.doSelectModifier(); diff --git a/src/test/abilities/stall.test.ts b/src/test/abilities/stall.test.ts index 44519064300..d8dbe9d0e06 100644 --- a/src/test/abilities/stall.test.ts +++ b/src/test/abilities/stall.test.ts @@ -1,9 +1,8 @@ -import { MovePhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { MovePhase } from "#app/phases/move-phase"; 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"; @@ -38,13 +37,13 @@ describe("Abilities - Stall", () => { * https://bulbapedia.bulbagarden.net/wiki/Priority **/ - it("Pokemon with Stall should move last in its priority bracket regardless of speed", async() => { - await game.startBattle([ Species.SHUCKLE ]); + it("Pokemon with Stall should move last in its priority bracket regardless of speed", async () => { + await game.startBattle([Species.SHUCKLE]); const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + game.move.select(Moves.QUICK_ATTACK); await game.phaseInterceptor.to(MovePhase, false); // The player Pokemon (without Stall) goes first despite having lower speed than the opponent. @@ -56,13 +55,13 @@ describe("Abilities - Stall", () => { expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); }, 20000); - it("Pokemon with Stall will go first if a move that is in a higher priority bracket than the opponent's move is used", async() => { - await game.startBattle([ Species.SHUCKLE ]); + it("Pokemon with Stall will go first if a move that is in a higher priority bracket than the opponent's move is used", async () => { + await game.startBattle([Species.SHUCKLE]); const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MovePhase, false); // The opponent Pokemon (with Stall) goes first because its move is still within a higher priority bracket than its opponent. @@ -74,14 +73,14 @@ describe("Abilities - Stall", () => { expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); }, 20000); - it("If both Pokemon have stall and use the same move, speed is used to determine who goes first.", async() => { + it("If both Pokemon have stall and use the same move, speed is used to determine who goes first.", async () => { game.override.ability(Abilities.STALL); - await game.startBattle([ Species.SHUCKLE ]); + await game.startBattle([Species.SHUCKLE]); const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MovePhase, false); // The opponent Pokemon (with Stall) goes first because it has a higher speed. diff --git a/src/test/abilities/steely_spirit.test.ts b/src/test/abilities/steely_spirit.test.ts index 5d5514bc3a1..c632d0be777 100644 --- a/src/test/abilities/steely_spirit.test.ts +++ b/src/test/abilities/steely_spirit.test.ts @@ -1,14 +1,13 @@ -import { allAbilities } from "#app/data/ability.js"; -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { MoveEffectPhase, SelectTargetPhase } from "#app/phases.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allAbilities } from "#app/data/ability"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Steely Spirit", () => { let phaserGame: Phaser.Game; @@ -46,10 +45,8 @@ describe("Abilities - Steely Spirit", () => { expect(boostSource.hasAbility(Abilities.STEELY_SPIRIT)).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, moveToCheck)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(enemyToCheck.getBattlerIndex()); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex()); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase); expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower * steelySpiritMultiplier); @@ -65,12 +62,8 @@ describe("Abilities - Steely Spirit", () => { expect(game.scene.getPlayerField().every(p => p.hasAbility(Abilities.STEELY_SPIRIT))).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, moveToCheck)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(enemyToCheck.getBattlerIndex()); - game.doAttack(getMovePosition(game.scene, 1, moveToCheck)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(enemyToCheck.getBattlerIndex()); + game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex()); + game.move.select(moveToCheck, 1, enemyToCheck.getBattlerIndex()); await game.phaseInterceptor.to(MoveEffectPhase); expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower * Math.pow(steelySpiritMultiplier, 2)); @@ -89,10 +82,8 @@ describe("Abilities - Steely Spirit", () => { expect(boostSource.hasAbility(Abilities.STEELY_SPIRIT)).toBe(false); expect(boostSource.summonData.abilitySuppressed).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, moveToCheck)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(enemyToCheck.getBattlerIndex()); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex()); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase); expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower); diff --git a/src/test/abilities/sturdy.test.ts b/src/test/abilities/sturdy.test.ts index 4caa7b0bd14..dc9f774cc5b 100644 --- a/src/test/abilities/sturdy.test.ts +++ b/src/test/abilities/sturdy.test.ts @@ -1,10 +1,10 @@ -import { EnemyPokemon } from "#app/field/pokemon.js"; -import { DamagePhase, MoveEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { EnemyPokemon } from "#app/field/pokemon"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; 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, test } from "vitest"; @@ -41,7 +41,7 @@ describe("Abilities - Sturdy", () => { "Sturdy activates when user is at full HP", async () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CLOSE_COMBAT)); + game.move.select(Moves.CLOSE_COMBAT); await game.phaseInterceptor.to(MoveEndPhase); expect(game.scene.getEnemyParty()[0].hp).toBe(1); }, @@ -56,7 +56,7 @@ describe("Abilities - Sturdy", () => { const enemyPokemon: EnemyPokemon = game.scene.getEnemyParty()[0]; enemyPokemon.hp = enemyPokemon.getMaxHp() - 1; - game.doAttack(getMovePosition(game.scene, 0, Moves.CLOSE_COMBAT)); + game.move.select(Moves.CLOSE_COMBAT); await game.phaseInterceptor.to(DamagePhase); expect(enemyPokemon.hp).toBe(0); @@ -69,7 +69,7 @@ describe("Abilities - Sturdy", () => { "Sturdy pokemon should be immune to OHKO moves", async () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); + game.move.select(Moves.FISSURE); await game.phaseInterceptor.to(MoveEndPhase); const enemyPokemon: EnemyPokemon = game.scene.getEnemyParty()[0]; @@ -84,7 +84,7 @@ describe("Abilities - Sturdy", () => { game.override.ability(Abilities.MOLD_BREAKER); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CLOSE_COMBAT)); + game.move.select(Moves.CLOSE_COMBAT); await game.phaseInterceptor.to(DamagePhase); const enemyPokemon: EnemyPokemon = game.scene.getEnemyParty()[0]; diff --git a/src/test/abilities/sweet_veil.test.ts b/src/test/abilities/sweet_veil.test.ts index d650455664f..5de3c7285a9 100644 --- a/src/test/abilities/sweet_veil.test.ts +++ b/src/test/abilities/sweet_veil.test.ts @@ -1,14 +1,14 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; -import { CommandPhase, MovePhase, TurnEndPhase } from "#app/phases"; +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { CommandPhase } from "#app/phases/command-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { BattlerIndex } from "#app/battle.js"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import { SPLASH_ONLY } from "#test/utils/testUtils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Abilities - Sweet Veil", () => { let phaserGame: Phaser.Game; @@ -27,7 +27,7 @@ describe("Abilities - Sweet Veil", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override.battleType("double"); - game.override.moveset([Moves.SPLASH, Moves.REST]); + game.override.moveset([Moves.SPLASH, Moves.REST, Moves.YAWN]); game.override.enemySpecies(Species.MAGIKARP); game.override.enemyAbility(Abilities.BALL_FETCH); game.override.enemyMoveset([Moves.POWDER, Moves.POWDER, Moves.POWDER, Moves.POWDER]); @@ -36,8 +36,8 @@ describe("Abilities - Sweet Veil", () => { it("prevents the user and its allies from falling asleep", async () => { await game.startBattle([Species.SWIRLIX, Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); @@ -48,8 +48,8 @@ describe("Abilities - Sweet Veil", () => { game.override.enemyMoveset(SPLASH_ONLY); await game.startBattle([Species.SWIRLIX, Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.REST)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.REST, 1); await game.phaseInterceptor.to(TurnEndPhase); @@ -60,8 +60,8 @@ describe("Abilities - Sweet Veil", () => { game.override.enemyMoveset([Moves.YAWN, Moves.YAWN, Moves.YAWN, Moves.YAWN]); await game.startBattle([Species.SWIRLIX, Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); @@ -72,28 +72,19 @@ describe("Abilities - Sweet Veil", () => { game.override.enemySpecies(Species.PIKACHU); game.override.enemyLevel(5); game.override.startingLevel(5); - game.override.enemyMoveset([Moves.YAWN, Moves.YAWN, Moves.YAWN, Moves.YAWN]); + game.override.enemyMoveset(SPLASH_ONLY); await game.startBattle([Species.SHUCKLE, Species.SHUCKLE, Species.SWIRLIX]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.YAWN, 1, BattlerIndex.PLAYER); - // First pokemon move - await game.move.forceHit(); - - // Second pokemon move - await game.phaseInterceptor.to(MovePhase, false); - await game.move.forceHit(); + await game.phaseInterceptor.to("BerryPhase"); expect(game.scene.getPlayerField().some(p => !!p.getTag(BattlerTagType.DROWSY))).toBe(true); - await game.phaseInterceptor.to(TurnEndPhase); - - const drowsyMon = game.scene.getPlayerField().find(p => !!p.getTag(BattlerTagType.DROWSY))!; - await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, (drowsyMon.getBattlerIndex() as BattlerIndex.PLAYER | BattlerIndex.PLAYER_2), Moves.SPLASH)); + game.move.select(Moves.SPLASH); game.doSwitchPokemon(2); expect(game.scene.getPlayerField().every(p => p.status?.effect)).toBe(false); diff --git a/src/test/abilities/unseen_fist.test.ts b/src/test/abilities/unseen_fist.test.ts index a6cad8b03ce..ea1996ec66b 100644 --- a/src/test/abilities/unseen_fist.test.ts +++ b/src/test/abilities/unseen_fist.test.ts @@ -1,11 +1,10 @@ -import { TurnEndPhase } from "#app/phases.js"; +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 Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; const TIMEOUT = 20 * 1000; @@ -80,7 +79,7 @@ async function testUnseenFistHitResult(game: GameManager, attackMove: Moves, pro const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, attackMove)); + game.move.select(attackMove); await game.phaseInterceptor.to(TurnEndPhase, false); if (shouldSucceed) { diff --git a/src/test/abilities/volt_absorb.test.ts b/src/test/abilities/volt_absorb.test.ts index 985459e133b..d9c3fe34c24 100644 --- a/src/test/abilities/volt_absorb.test.ts +++ b/src/test/abilities/volt_absorb.test.ts @@ -1,11 +1,10 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-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"; @@ -42,7 +41,7 @@ describe("Abilities - Volt Absorb", () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/wind_power.test.ts b/src/test/abilities/wind_power.test.ts index 670544a89ef..c944e01b43a 100644 --- a/src/test/abilities/wind_power.test.ts +++ b/src/test/abilities/wind_power.test.ts @@ -1,13 +1,12 @@ -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +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 { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Wind Power", () => { let phaserGame: Phaser.Game; @@ -38,7 +37,7 @@ describe("Abilities - Wind Power", () => { expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.PETAL_BLIZZARD)); + game.move.select(Moves.PETAL_BLIZZARD); await game.phaseInterceptor.to(TurnEndPhase); expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeDefined(); @@ -53,7 +52,7 @@ describe("Abilities - Wind Power", () => { expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); + game.move.select(Moves.TAILWIND); await game.phaseInterceptor.to(TurnEndPhase); expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeDefined(); @@ -70,7 +69,7 @@ describe("Abilities - Wind Power", () => { expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); expect(magikarp.getTag(BattlerTagType.CHARGED)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); + game.move.select(Moves.TAILWIND); await game.phaseInterceptor.to(TurnEndPhase); @@ -86,7 +85,7 @@ describe("Abilities - Wind Power", () => { expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SANDSTORM)); + game.move.select(Moves.SANDSTORM); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/wind_rider.test.ts b/src/test/abilities/wind_rider.test.ts index e27349efe41..97e2e6456dc 100644 --- a/src/test/abilities/wind_rider.test.ts +++ b/src/test/abilities/wind_rider.test.ts @@ -1,13 +1,12 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +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 { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Wind Rider", () => { let phaserGame: Phaser.Game; @@ -38,7 +37,7 @@ describe("Abilities - Wind Rider", () => { expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.PETAL_BLIZZARD)); + game.move.select(Moves.PETAL_BLIZZARD); await game.phaseInterceptor.to(TurnEndPhase); @@ -55,7 +54,7 @@ describe("Abilities - Wind Rider", () => { expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); + game.move.select(Moves.TAILWIND); await game.phaseInterceptor.to(TurnEndPhase); @@ -73,7 +72,7 @@ describe("Abilities - Wind Rider", () => { expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); + game.move.select(Moves.TAILWIND); await game.phaseInterceptor.to(TurnEndPhase); @@ -91,7 +90,7 @@ describe("Abilities - Wind Rider", () => { expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); + game.move.select(Moves.TAILWIND); await game.phaseInterceptor.to(TurnEndPhase); @@ -108,7 +107,7 @@ describe("Abilities - Wind Rider", () => { expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(shiftry.isFullHp()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SANDSTORM)); + game.move.select(Moves.SANDSTORM); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/wonder_skin.test.ts b/src/test/abilities/wonder_skin.test.ts index a2815152df6..0c2aedc8ce8 100644 --- a/src/test/abilities/wonder_skin.test.ts +++ b/src/test/abilities/wonder_skin.test.ts @@ -1,14 +1,13 @@ -import { allAbilities } from "#app/data/ability.js"; -import { allMoves } from "#app/data/move.js"; -import { MoveEffectPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allAbilities } from "#app/data/ability"; +import { allMoves } from "#app/data/move"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Wonder Skin", () => { let phaserGame: Phaser.Game; @@ -40,7 +39,7 @@ describe("Abilities - Wonder Skin", () => { vi.spyOn(moveToCheck, "calculateBattleAccuracy"); await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.CHARM)); + game.move.select(Moves.CHARM); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(50); @@ -52,7 +51,7 @@ describe("Abilities - Wonder Skin", () => { vi.spyOn(moveToCheck, "calculateBattleAccuracy"); await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(100); @@ -68,7 +67,7 @@ describe("Abilities - Wonder Skin", () => { vi.spyOn(moveToCheck, "calculateBattleAccuracy"); await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.CHARM)); + game.move.select(Moves.CHARM); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(100); diff --git a/src/test/abilities/zen_mode.test.ts b/src/test/abilities/zen_mode.test.ts index 1bc7a6af4ce..677d998e876 100644 --- a/src/test/abilities/zen_mode.test.ts +++ b/src/test/abilities/zen_mode.test.ts @@ -1,17 +1,23 @@ +import { BattlerIndex } from "#app/battle"; import { Stat } from "#app/data/pokemon-stat"; -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { QuietFormChangePhase } from "#app/form-change-phase"; -import { CommandPhase, DamagePhase, EnemyCommandPhase, MessagePhase, PostSummonPhase, SwitchPhase, SwitchSummonPhase, TurnEndPhase, TurnInitPhase, TurnStartPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; +import { Status, StatusEffect } from "#app/data/status-effect"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import { PostSummonPhase } from "#app/phases/post-summon-phase"; +import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; +import { SwitchPhase } from "#app/phases/switch-phase"; +import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { Mode } from "#app/ui/ui"; 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, test } from "vitest"; -import { BattlerIndex } from "#app/battle.js"; const TIMEOUT = 20 * 1000; @@ -50,13 +56,7 @@ describe("Abilities - ZEN MODE", () => { game.scene.getParty()[0].hp = 100; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to(DamagePhase, false); @@ -79,13 +79,7 @@ describe("Abilities - ZEN MODE", () => { game.scene.getParty()[0].hp = 100; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to(QuietFormChangePhase); @@ -105,13 +99,7 @@ describe("Abilities - ZEN MODE", () => { game.scene.getParty()[0].hp = 100; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to(DamagePhase, false); @@ -160,7 +148,7 @@ describe("Abilities - ZEN MODE", () => { darmanitan.status = new Status(StatusEffect.FAINT); expect(darmanitan.isFainted()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to(TurnEndPhase); game.doSelectModifier(); diff --git a/src/test/abilities/zero_to_hero.test.ts b/src/test/abilities/zero_to_hero.test.ts index 7924b30eb76..1a9697f974e 100644 --- a/src/test/abilities/zero_to_hero.test.ts +++ b/src/test/abilities/zero_to_hero.test.ts @@ -1,11 +1,10 @@ -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { QuietFormChangePhase } from "#app/form-change-phase.js"; -import { TurnEndPhase } from "#app/phases.js"; +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 { getMovePosition } from "#test/utils/gameManagerUtils"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { SPLASH_ONLY } from "../utils/testUtils"; @@ -52,7 +51,7 @@ describe("Abilities - ZERO TO HERO", () => { palafin2.status = new Status(StatusEffect.FAINT); expect(palafin2.isFainted()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to(TurnEndPhase); game.doSelectModifier(); @@ -80,7 +79,7 @@ describe("Abilities - ZERO TO HERO", () => { const palafin = game.scene.getPlayerPokemon()!; expect(palafin.formIndex).toBe(baseForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.killPokemon(palafin); game.doSelectPartyPokemon(1); await game.toNextTurn(); @@ -97,7 +96,7 @@ describe("Abilities - ZERO TO HERO", () => { const palafin = game.scene.getPlayerPokemon()!; expect(palafin.formIndex).toBe(heroForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.killPokemon(palafin); game.doSelectPartyPokemon(1); await game.toNextTurn(); diff --git a/src/test/account.spec.ts b/src/test/account.spec.ts index d5d0458c7e8..eb6002f3cf2 100644 --- a/src/test/account.spec.ts +++ b/src/test/account.spec.ts @@ -1,4 +1,4 @@ -import * as battleScene from "#app/battle-scene.js"; +import * as battleScene from "#app/battle-scene"; import { describe, expect, it, vi } from "vitest"; import { initLoggedInUser, loggedInUser, updateUserInfo } from "../account"; import * as utils from "../utils"; diff --git a/src/test/achievements/achievement.test.ts b/src/test/achievements/achievement.test.ts index 5cd9c4d4094..36c20ae2248 100644 --- a/src/test/achievements/achievement.test.ts +++ b/src/test/achievements/achievement.test.ts @@ -1,7 +1,7 @@ -import { TurnHeldItemTransferModifier } from "#app/modifier/modifier.js"; +import { TurnHeldItemTransferModifier } from "#app/modifier/modifier"; import { Achv, AchvTier, DamageAchv, HealAchv, LevelAchv, ModifierAchv, MoneyAchv, RibbonAchv, achvs } from "#app/system/achv"; +import { IntegerHolder, NumberHolder } from "#app/utils"; import GameManager from "#test/utils/gameManager"; -import { IntegerHolder, NumberHolder } from "#app/utils.js"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import BattleScene from "../../battle-scene"; diff --git a/src/test/arena/arena_gravity.test.ts b/src/test/arena/arena_gravity.test.ts index 66d6994fb80..eda8c687ba1 100644 --- a/src/test/arena/arena_gravity.test.ts +++ b/src/test/arena/arena_gravity.test.ts @@ -1,13 +1,14 @@ -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import { MoveEffectPhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { SPLASH_ONLY } from "../utils/testUtils"; describe("Arena - Gravity", () => { let phaserGame: Phaser.Game; @@ -25,14 +26,17 @@ describe("Arena - Gravity", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); - game.override.moveset([Moves.TACKLE, Moves.GRAVITY, Moves.FISSURE]); - game.override.ability(Abilities.UNNERVE); - game.override.enemyAbility(Abilities.BALL_FETCH); - game.override.enemySpecies(Species.SHUCKLE); - game.override.enemyMoveset(new Array(4).fill(Moves.SPLASH)); + game.override + .battleType("single") + .moveset([Moves.TACKLE, Moves.GRAVITY, Moves.FISSURE]) + .ability(Abilities.UNNERVE) + .enemyAbility(Abilities.BALL_FETCH) + .enemySpecies(Species.SHUCKLE) + .enemyMoveset(SPLASH_ONLY); }); + // Reference: https://bulbapedia.bulbagarden.net/wiki/Gravity_(move) + it("non-OHKO move accuracy is multiplied by 1.67", async () => { const moveToCheck = allMoves[Moves.TACKLE]; @@ -40,14 +44,14 @@ describe("Arena - Gravity", () => { // Setup Gravity on first turn await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.GRAVITY)); + game.move.select(Moves.GRAVITY); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined(); // Use non-OHKO move on second turn await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(100 * 1.67); @@ -64,16 +68,77 @@ describe("Arena - Gravity", () => { // Setup Gravity on first turn await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.GRAVITY)); + game.move.select(Moves.GRAVITY); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined(); // Use OHKO move on second turn await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); + game.move.select(Moves.FISSURE); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(30); }); + + describe("Against flying types", () => { + it("can be hit by ground-type moves now", async () => { + game.override + .startingLevel(5) + .enemyLevel(5) + .enemySpecies(Species.PIDGEOT) + .moveset([Moves.GRAVITY, Moves.EARTHQUAKE]); + + await game.startBattle([Species.PIKACHU]); + + const pidgeot = game.scene.getEnemyPokemon()!; + vi.spyOn(pidgeot, "getAttackTypeEffectiveness"); + + // Try earthquake on 1st turn (fails!); + game.move.select(Moves.EARTHQUAKE); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(0); + + // Setup Gravity on 2nd turn + await game.toNextTurn(); + game.move.select(Moves.GRAVITY); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined(); + + // Use ground move on 3rd turn + await game.toNextTurn(); + game.move.select(Moves.EARTHQUAKE); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(1); + }); + + it("keeps super-effective moves super-effective after using gravity", async () => { + game.override + .startingLevel(5) + .enemyLevel(5) + .enemySpecies(Species.PIDGEOT) + .moveset([Moves.GRAVITY, Moves.THUNDERBOLT]); + + await game.startBattle([Species.PIKACHU]); + + const pidgeot = game.scene.getEnemyPokemon()!; + vi.spyOn(pidgeot, "getAttackTypeEffectiveness"); + + // Setup Gravity on 1st turn + game.move.select(Moves.GRAVITY); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined(); + + // Use electric move on 2nd turn + await game.toNextTurn(); + game.move.select(Moves.THUNDERBOLT); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(2); + }); + }); }); diff --git a/src/test/arena/weather_fog.test.ts b/src/test/arena/weather_fog.test.ts index e5718b73a3c..b36b0de2e06 100644 --- a/src/test/arena/weather_fog.test.ts +++ b/src/test/arena/weather_fog.test.ts @@ -1,11 +1,10 @@ -import { allMoves } from "#app/data/move.js"; -import { WeatherType } from "#app/data/weather.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { MoveEffectPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { WeatherType } from "#app/data/weather"; +import { Abilities } from "#app/enums/abilities"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -41,7 +40,7 @@ describe("Weather - Fog", () => { vi.spyOn(moveToCheck, "calculateBattleAccuracy"); await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(100 * 0.9); diff --git a/src/test/arena/weather_strong_winds.test.ts b/src/test/arena/weather_strong_winds.test.ts index d9f626cfb83..8b2d3e2547e 100644 --- a/src/test/arena/weather_strong_winds.test.ts +++ b/src/test/arena/weather_strong_winds.test.ts @@ -1,10 +1,9 @@ -import { allMoves } from "#app/data/move.js"; -import { TurnStartPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; 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"; @@ -38,7 +37,7 @@ describe("Weather - Strong Winds", () => { const pikachu = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT)); + game.move.select(Moves.THUNDERBOLT); await game.phaseInterceptor.to(TurnStartPhase); expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.THUNDERBOLT].type, pikachu)).toBe(0.5); @@ -49,7 +48,7 @@ describe("Weather - Strong Winds", () => { const pikachu = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT)); + game.move.select(Moves.THUNDERBOLT); await game.phaseInterceptor.to(TurnStartPhase); expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.THUNDERBOLT].type, pikachu)).toBe(1); @@ -60,7 +59,7 @@ describe("Weather - Strong Winds", () => { const pikachu = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); + game.move.select(Moves.ICE_BEAM); await game.phaseInterceptor.to(TurnStartPhase); expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.ICE_BEAM].type, pikachu)).toBe(1); @@ -71,7 +70,7 @@ describe("Weather - Strong Winds", () => { const pikachu = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.ROCK_SLIDE)); + game.move.select(Moves.ROCK_SLIDE); await game.phaseInterceptor.to(TurnStartPhase); expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.ROCK_SLIDE].type, pikachu)).toBe(1); diff --git a/src/test/battle-scene.test.ts b/src/test/battle-scene.test.ts index 21d3f689d1c..9e28ec99791 100644 --- a/src/test/battle-scene.test.ts +++ b/src/test/battle-scene.test.ts @@ -1,4 +1,4 @@ -import { LoadingScene } from "#app/loading-scene.js"; +import { LoadingScene } from "#app/loading-scene"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "./utils/gameManager"; diff --git a/src/test/battle-stat.spec.ts b/src/test/battle-stat.spec.ts index 775dd40ff34..16fce962838 100644 --- a/src/test/battle-stat.spec.ts +++ b/src/test/battle-stat.spec.ts @@ -1,4 +1,4 @@ -import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "#app/data/battle-stat.js"; +import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "#app/data/battle-stat"; import { describe, expect, it } from "vitest"; import { arrayOfRange, mockI18next } from "./utils/testUtils"; diff --git a/src/test/battle/battle-order.test.ts b/src/test/battle/battle-order.test.ts index 6aa919186b4..0129ecad254 100644 --- a/src/test/battle/battle-order.test.ts +++ b/src/test/battle/battle-order.test.ts @@ -1,14 +1,11 @@ import { Stat } from "#app/data/pokemon-stat"; -import { CommandPhase, EnemyCommandPhase, SelectTargetPhase, TurnStartPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import TargetSelectUiHandler from "#app/ui/target-select-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { SelectTargetPhase } from "#app/phases/select-target-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { Abilities } from "#enums/abilities"; -import { Button } from "#enums/buttons"; 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"; @@ -36,20 +33,14 @@ describe("Battle order", () => { game.override.moveset([Moves.TACKLE]); }); - it("opponent faster than player 50 vs 150", async() => { + it("opponent faster than player 50 vs 150", async () => { await game.startBattle([ Species.BULBASAUR, ]); game.scene.getParty()[0].stats[Stat.SPD] = 50; game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 150; - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.run(EnemyCommandPhase); const phase = game.scene.getCurrentPhase() as TurnStartPhase; const order = phase.getOrder(); @@ -57,20 +48,14 @@ describe("Battle order", () => { expect(order[1]).toBe(0); }, 20000); - it("Player faster than opponent 150 vs 50", async() => { + it("Player faster than opponent 150 vs 50", async () => { await game.startBattle([ Species.BULBASAUR, ]); game.scene.getParty()[0].stats[Stat.SPD] = 150; game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 50; - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.run(EnemyCommandPhase); const phase = game.scene.getCurrentPhase() as TurnStartPhase; const order = phase.getOrder(); @@ -78,7 +63,7 @@ describe("Battle order", () => { expect(order[1]).toBe(2); }, 20000); - it("double - both opponents faster than player 50/50 vs 150/150", async() => { + it("double - both opponents faster than player 50/50 vs 150/150", async () => { game.override.battleType("double"); await game.startBattle([ Species.BULBASAUR, @@ -89,28 +74,8 @@ describe("Battle order", () => { game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 150; game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150; - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); - game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { - const handler = game.scene.ui.getHandler() as TargetSelectUiHandler; - handler.processInput(Button.ACTION); - }); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); - game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { - const handler = game.scene.ui.getHandler() as TargetSelectUiHandler; - handler.processInput(Button.ACTION); - }); + game.move.select(Moves.TACKLE); + game.move.select(Moves.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); const phase = game.scene.getCurrentPhase() as TurnStartPhase; const order = phase.getOrder(); @@ -120,7 +85,7 @@ describe("Battle order", () => { expect(order.indexOf(1)).toBeGreaterThan(order.indexOf(3)); }, 20000); - it("double - speed tie except 1 - 100/100 vs 100/150", async() => { + it("double - speed tie except 1 - 100/100 vs 100/150", async () => { game.override.battleType("double"); await game.startBattle([ Species.BULBASAUR, @@ -131,28 +96,8 @@ describe("Battle order", () => { game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 100; game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150; - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); - game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { - const handler = game.scene.ui.getHandler() as TargetSelectUiHandler; - handler.processInput(Button.ACTION); - }); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); - game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { - const handler = game.scene.ui.getHandler() as TargetSelectUiHandler; - handler.processInput(Button.ACTION); - }); + game.move.select(Moves.TACKLE); + game.move.select(Moves.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); const phase = game.scene.getCurrentPhase() as TurnStartPhase; const order = phase.getOrder(); @@ -161,7 +106,7 @@ describe("Battle order", () => { expect(order.indexOf(3)).toBeLessThan(order.indexOf(2)); }, 20000); - it("double - speed tie 100/150 vs 100/150", async() => { + it("double - speed tie 100/150 vs 100/150", async () => { game.override.battleType("double"); await game.startBattle([ Species.BULBASAUR, @@ -172,28 +117,8 @@ describe("Battle order", () => { game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 100; game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150; - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); - game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { - const handler = game.scene.ui.getHandler() as TargetSelectUiHandler; - handler.processInput(Button.ACTION); - }); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); - game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { - const handler = game.scene.ui.getHandler() as TargetSelectUiHandler; - handler.processInput(Button.ACTION); - }); + game.move.select(Moves.TACKLE); + game.move.select(Moves.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); const phase = game.scene.getCurrentPhase() as TurnStartPhase; const order = phase.getOrder(); diff --git a/src/test/battle/battle.test.ts b/src/test/battle/battle.test.ts index a4713f90506..c79eee63a7c 100644 --- a/src/test/battle/battle.test.ts +++ b/src/test/battle/battle.test.ts @@ -1,25 +1,23 @@ import { allSpecies } from "#app/data/pokemon-species"; -import { TempBattleStat } from "#app/data/temp-battle-stat.js"; -import { GameModes } from "#app/game-mode"; -import { getGameMode } from "#app/game-mode.js"; -import { - BattleEndPhase, - CommandPhase, DamagePhase, - EncounterPhase, - EnemyCommandPhase, - LoginPhase, - NextEncounterPhase, - SelectGenderPhase, - SelectModifierPhase, - SelectStarterPhase, - SummonPhase, - SwitchPhase, - TitlePhase, - TurnInitPhase, VictoryPhase, -} from "#app/phases"; +import { TempBattleStat } from "#app/data/temp-battle-stat"; +import { GameModes, getGameMode } from "#app/game-mode"; +import { BattleEndPhase } from "#app/phases/battle-end-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { LoginPhase } from "#app/phases/login-phase"; +import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; +import { SelectGenderPhase } from "#app/phases/select-gender-phase"; +import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { SummonPhase } from "#app/phases/summon-phase"; +import { SwitchPhase } from "#app/phases/switch-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { VictoryPhase } from "#app/phases/victory-phase"; import GameManager from "#app/test/utils/gameManager"; -import { generateStarter, getMovePosition, } from "#app/test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; +import { generateStarter } from "#app/test/utils/gameManagerUtils"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; @@ -47,7 +45,7 @@ describe("Test Battle Phase", () => { game = new GameManager(phaserGame); }); - it("test phase interceptor with prompt", async() => { + it("test phase interceptor with prompt", async () => { await game.phaseInterceptor.run(LoginPhase); game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => { @@ -65,7 +63,7 @@ describe("Test Battle Phase", () => { expect(game.scene.gameData.gender).toBe(PlayerGender.MALE); }, 20000); - it("test phase interceptor with prompt with preparation for a future prompt", async() => { + it("test phase interceptor with prompt with preparation for a future prompt", async () => { await game.phaseInterceptor.run(LoginPhase); game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => { @@ -87,13 +85,13 @@ describe("Test Battle Phase", () => { expect(game.scene.gameData.gender).toBe(PlayerGender.MALE); }, 20000); - it("newGame one-liner", async() => { + it("newGame one-liner", async () => { await game.startBattle(); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); - it("do attack wave 3 - single battle - regular - OHKO", async() => { + it("do attack wave 3 - single battle - regular - OHKO", async () => { game.override.starterSpecies(Species.MEWTWO); game.override.enemySpecies(Species.RATTATA); game.override.startingLevel(2000); @@ -104,17 +102,11 @@ describe("Test Battle Phase", () => { game.override.enemyAbility(Abilities.HYDRATION); game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); await game.startBattle(); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(SelectModifierPhase, false); }, 20000); - it("do attack wave 3 - single battle - regular - NO OHKO with opponent using non damage attack", async() => { + it("do attack wave 3 - single battle - regular - NO OHKO with opponent using non damage attack", async () => { game.override.starterSpecies(Species.MEWTWO); game.override.enemySpecies(Species.RATTATA); game.override.startingLevel(5); @@ -124,17 +116,11 @@ describe("Test Battle Phase", () => { game.override.enemyMoveset([Moves.TAIL_WHIP, Moves.TAIL_WHIP, Moves.TAIL_WHIP, Moves.TAIL_WHIP]); game.override.battleType("single"); await game.startBattle(); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase, false); }, 20000); - it("load 100% data file", async() => { + it("load 100% data file", async () => { await game.importData("src/test/utils/saves/everything.prsv"); const caughtCount = Object.keys(game.scene.gameData.dexData).filter((key) => { const species = game.scene.gameData.dexData[key]; @@ -143,7 +129,7 @@ describe("Test Battle Phase", () => { expect(caughtCount).toBe(Object.keys(allSpecies).length); }, 20000); - it("start battle with selected team", async() => { + it("start battle with selected team", async () => { await game.startBattle([ Species.CHARIZARD, Species.CHANSEY, @@ -154,26 +140,26 @@ describe("Test Battle Phase", () => { expect(game.scene.getParty()[2].species.speciesId).toBe(Species.MEW); }, 20000); - it("test remove random battle seed int", async() => { - for (let i=0; i<10; i++) { + it("test remove random battle seed int", async () => { + for (let i = 0; i < 10; i++) { const rand = game.scene.randBattleSeedInt(16); expect(rand).toBe(15); } }); - it("wrong phase", async() => { + it("wrong phase", async () => { await game.phaseInterceptor.run(LoginPhase); await game.phaseInterceptor.run(LoginPhase).catch((e) => { expect(e).toBe("Wrong phase: this is SelectGenderPhase and not LoginPhase"); }); }, 20000); - it("wrong phase but skip", async() => { + it("wrong phase but skip", async () => { await game.phaseInterceptor.run(LoginPhase); await game.phaseInterceptor.run(LoginPhase, () => game.isCurrentPhase(SelectGenderPhase)); }, 20000); - it("good run", async() => { + it("good run", async () => { await game.phaseInterceptor.run(LoginPhase); game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => { game.scene.gameData.gender = PlayerGender.MALE; @@ -183,7 +169,7 @@ describe("Test Battle Phase", () => { await game.phaseInterceptor.run(TitlePhase); }, 20000); - it("good run from select gender to title", async() => { + it("good run from select gender to title", async () => { await game.phaseInterceptor.run(LoginPhase); game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => { game.scene.gameData.gender = PlayerGender.MALE; @@ -192,7 +178,7 @@ describe("Test Battle Phase", () => { await game.phaseInterceptor.runFrom(SelectGenderPhase).to(TitlePhase); }, 20000); - it("good run to SummonPhase phase", async() => { + it("good run to SummonPhase phase", async () => { await game.phaseInterceptor.run(LoginPhase); game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => { game.scene.gameData.gender = PlayerGender.MALE; @@ -208,7 +194,7 @@ describe("Test Battle Phase", () => { await game.phaseInterceptor.runFrom(SelectGenderPhase).to(SummonPhase); }, 20000); - it("2vs1", async() => { + it("2vs1", async () => { game.override.battleType("single"); game.override.enemySpecies(Species.MIGHTYENA); game.override.enemyAbility(Abilities.HYDRATION); @@ -221,7 +207,7 @@ describe("Test Battle Phase", () => { expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); - it("1vs1", async() => { + it("1vs1", async () => { game.override.battleType("single"); game.override.enemySpecies(Species.MIGHTYENA); game.override.enemyAbility(Abilities.HYDRATION); @@ -233,7 +219,7 @@ describe("Test Battle Phase", () => { expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); - it("2vs2", async() => { + it("2vs2", async () => { game.override.battleType("double"); game.override.enemySpecies(Species.MIGHTYENA); game.override.enemyAbility(Abilities.HYDRATION); @@ -247,7 +233,7 @@ describe("Test Battle Phase", () => { expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); - it("4vs2", async() => { + it("4vs2", async () => { game.override.battleType("double"); game.override.enemySpecies(Species.MIGHTYENA); game.override.enemyAbility(Abilities.HYDRATION); @@ -263,7 +249,7 @@ describe("Test Battle Phase", () => { expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); - it("kill opponent pokemon", async() => { + it("kill opponent pokemon", async () => { const moveToUse = Moves.SPLASH; game.override.battleType("single"); game.override.starterSpecies(Species.MEWTWO); @@ -273,26 +259,20 @@ describe("Test Battle Phase", () => { game.override.startingLevel(2000); game.override.startingWave(3); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); await game.startBattle([ Species.DARMANITAN, Species.CHARIZARD, ]); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.phaseInterceptor.to(DamagePhase, false); await game.killPokemon(game.scene.currentBattle.enemyParty[0]); expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true); await game.phaseInterceptor.to(VictoryPhase, false); }, 200000); - it("to next turn", async() => { + it("to next turn", async () => { const moveToUse = Moves.SPLASH; game.override.battleType("single"); game.override.starterSpecies(Species.MEWTWO); @@ -302,15 +282,15 @@ describe("Test Battle Phase", () => { game.override.startingLevel(2000); game.override.startingWave(3); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); await game.startBattle(); const turn = game.scene.currentBattle.turn; - game.doAttack(0); + game.move.select(moveToUse); await game.toNextTurn(); expect(game.scene.currentBattle.turn).toBeGreaterThan(turn); }, 20000); - it("to next wave with pokemon killed, single", async() => { + it("to next wave with pokemon killed, single", async () => { const moveToUse = Moves.SPLASH; game.override.battleType("single"); game.override.starterSpecies(Species.MEWTWO); @@ -320,10 +300,10 @@ describe("Test Battle Phase", () => { game.override.startingLevel(2000); game.override.startingWave(3); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); await game.startBattle(); const waveIndex = game.scene.currentBattle.waveIndex; - game.doAttack(0); + game.move.select(moveToUse); await game.doKillOpponents(); await game.toNextWave(); expect(game.scene.currentBattle.waveIndex).toBeGreaterThan(waveIndex); @@ -343,7 +323,7 @@ describe("Test Battle Phase", () => { await game.startBattle(); game.scene.getPlayerPokemon()!.hp = 1; - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(BattleEndPhase); game.doRevivePokemon(0); // pretend max revive was picked diff --git a/src/test/battle/damage_calculation.test.ts b/src/test/battle/damage_calculation.test.ts new file mode 100644 index 00000000000..665000450be --- /dev/null +++ b/src/test/battle/damage_calculation.test.ts @@ -0,0 +1,70 @@ +import { DamagePhase } from "#app/phases/damage-phase.js"; +import { toDmgValue } from "#app/utils"; +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 { SPLASH_ONLY } from "#test/utils/testUtils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Round Down and Minimun 1 test in Damage Calculation", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override.battleType("single"); + game.override.startingLevel(10); + }); + + it("When the user fails to use Jump Kick with Wonder Guard ability, the damage should be 1.", async () => { + game.override.enemySpecies(Species.GASTLY); + game.override.enemyMoveset(SPLASH_ONLY); + game.override.starterSpecies(Species.SHEDINJA); + game.override.moveset([Moves.JUMP_KICK]); + game.override.ability(Abilities.WONDER_GUARD); + + await game.startBattle(); + + const shedinja = game.scene.getPlayerPokemon()!; + + game.move.select(Moves.JUMP_KICK); + + await game.phaseInterceptor.to(DamagePhase); + + expect(shedinja.hp).toBe(shedinja.getMaxHp() - 1); + }); + + + it("Charizard with odd HP survives Stealth Rock damage twice", async () => { + game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0); + game.override.seed("Charizard Stealth Rock test"); + game.override.enemySpecies(Species.CHARIZARD); + game.override.enemyAbility(Abilities.BLAZE); + game.override.starterSpecies(Species.PIKACHU); + game.override.enemyLevel(100); + + await game.startBattle(); + + const charizard = game.scene.getEnemyPokemon()!; + + const maxHp = charizard.getMaxHp(); + const damage_prediction = toDmgValue(charizard.getMaxHp() / 2); + const currentHp = charizard.hp; + const expectedHP = maxHp - damage_prediction; + + expect(currentHp).toBe(expectedHP); + }); +}); diff --git a/src/test/battle/double_battle.test.ts b/src/test/battle/double_battle.test.ts index 76b7defe33d..d264a29ef9b 100644 --- a/src/test/battle/double_battle.test.ts +++ b/src/test/battle/double_battle.test.ts @@ -1,12 +1,12 @@ -import { BattleEndPhase, TurnInitPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition, } from "#test/utils/gameManagerUtils"; +import { Status, StatusEffect } from "#app/data/status-effect"; +import { BattleEndPhase } from "#app/phases/battle-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { Status, StatusEffect } from "#app/data/status-effect.js"; describe("Double Battles", () => { let phaserGame: Phaser.Game; @@ -28,7 +28,7 @@ describe("Double Battles", () => { // double-battle player's pokemon both fainted in same round, then revive one, and next double battle summons two player's pokemon successfully. // (There were bugs that either only summon one when can summon two, player stuck in switchPhase etc) - it("3v2 edge case: player summons 2 pokemon on the next battle after being fainted and revived", async() => { + it("3v2 edge case: player summons 2 pokemon on the next battle after being fainted and revived", async () => { game.override.battleType("double").enemyMoveset(SPLASH_ONLY).moveset(SPLASH_ONLY); await game.startBattle([ Species.BULBASAUR, @@ -36,8 +36,8 @@ describe("Double Battles", () => { Species.SQUIRTLE, ]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SPLASH, 1); for (const pokemon of game.scene.getPlayerField()) { pokemon.hp = 0; diff --git a/src/test/battle/error-handling.test.ts b/src/test/battle/error-handling.test.ts index a88d7cd8c18..da5cc4d1969 100644 --- a/src/test/battle/error-handling.test.ts +++ b/src/test/battle/error-handling.test.ts @@ -1,13 +1,14 @@ -import GameManager from "#test/utils/gameManager"; 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"; describe("Error Handling", () => { let phaserGame: Phaser.Game; let game: GameManager; + const moveToUse = Moves.SPLASH; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -21,7 +22,6 @@ describe("Error Handling", () => { beforeEach(() => { game = new GameManager(phaserGame); - const moveToUse = Moves.SPLASH; game.override .battleType("single") .startingWave(3); @@ -31,13 +31,13 @@ describe("Error Handling", () => { game.override.ability(Abilities.ZEN_MODE); game.override.startingLevel(2000); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it.skip("to next turn", async() => { + it.skip("to next turn", async () => { await game.startBattle(); const turn = game.scene.currentBattle.turn; - game.doAttack(0); + game.move.select(moveToUse); await game.toNextTurn(); expect(game.scene.currentBattle.turn).toBeGreaterThan(turn); }, 20000); diff --git a/src/test/battle/special_battle.test.ts b/src/test/battle/special_battle.test.ts index 6130df703f5..1d319bea372 100644 --- a/src/test/battle/special_battle.test.ts +++ b/src/test/battle/special_battle.test.ts @@ -1,9 +1,9 @@ -import { CommandPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; +import { CommandPhase } from "#app/phases/command-phase"; import { Mode } from "#app/ui/ui"; 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"; diff --git a/src/test/battlerTags/octolock.test.ts b/src/test/battlerTags/octolock.test.ts index 369a84e21fa..fa491589f09 100644 --- a/src/test/battlerTags/octolock.test.ts +++ b/src/test/battlerTags/octolock.test.ts @@ -1,10 +1,10 @@ +import BattleScene from "#app/battle-scene"; +import { BattleStat } from "#app/data/battle-stat"; +import { BattlerTag, BattlerTagLapseType, OctolockTag, TrappedTag } from "#app/data/battler-tags"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import Pokemon from "#app/field/pokemon"; +import { StatChangePhase } from "#app/phases/stat-change-phase"; import { describe, expect, it, vi } from "vitest"; -import Pokemon from "#app/field/pokemon.js"; -import BattleScene from "#app/battle-scene.js"; -import { BattlerTag, BattlerTagLapseType, OctolockTag, TrappedTag } from "#app/data/battler-tags.js"; -import { StatChangePhase } from "#app/phases.js"; -import { BattleStat } from "#app/data/battle-stat.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; vi.mock("#app/battle-scene.js"); diff --git a/src/test/battlerTags/stockpiling.test.ts b/src/test/battlerTags/stockpiling.test.ts index 005f1e1593c..fef1e938c09 100644 --- a/src/test/battlerTags/stockpiling.test.ts +++ b/src/test/battlerTags/stockpiling.test.ts @@ -1,10 +1,10 @@ +import BattleScene from "#app/battle-scene"; +import { BattleStat } from "#app/data/battle-stat"; +import { StockpilingTag } from "#app/data/battler-tags"; +import Pokemon, { PokemonSummonData } from "#app/field/pokemon"; +import * as messages from "#app/messages"; +import { StatChangePhase } from "#app/phases/stat-change-phase"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import Pokemon, { PokemonSummonData } from "#app/field/pokemon.js"; -import BattleScene from "#app/battle-scene.js"; -import { StockpilingTag } from "#app/data/battler-tags.js"; -import { StatChangePhase } from "#app/phases.js"; -import { BattleStat } from "#app/data/battle-stat.js"; -import * as messages from "#app/messages.js"; beforeEach(() => { vi.spyOn(messages, "getPokemonNameWithAffix").mockImplementation(() => ""); diff --git a/src/test/eggs/egg.test.ts b/src/test/eggs/egg.test.ts index 0bc2972e2dc..a01d2257099 100644 --- a/src/test/eggs/egg.test.ts +++ b/src/test/eggs/egg.test.ts @@ -1,14 +1,14 @@ +import { Egg, getLegendaryGachaSpeciesForTimestamp } from "#app/data/egg"; +import { EggSourceType } from "#app/enums/egg-source-types"; +import { EggTier } from "#app/enums/egg-type"; +import { VariantTier } from "#app/enums/variant-tiers"; +import EggData from "#app/system/egg-data"; +import * as Utils from "#app/utils"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import BattleScene from "../../battle-scene"; -import { Egg, getLegendaryGachaSpeciesForTimestamp } from "#app/data/egg.js"; -import { Species } from "#enums/species"; -import Phaser from "phaser"; -import { EggSourceType } from "#app/enums/egg-source-types.js"; -import { EggTier } from "#app/enums/egg-type.js"; -import { VariantTier } from "#app/enums/variant-tiers.js"; -import GameManager from "#test/utils/gameManager"; -import EggData from "#app/system/egg-data.js"; -import * as Utils from "#app/utils.js"; describe("Egg Generation Tests", () => { let phaserGame: Phaser.Game; diff --git a/src/test/evolution.test.ts b/src/test/evolution.test.ts index b54deaa4611..41088c17bcb 100644 --- a/src/test/evolution.test.ts +++ b/src/test/evolution.test.ts @@ -1,6 +1,6 @@ -import { pokemonEvolutions, SpeciesFormEvolution, SpeciesWildEvolutionDelay } from "#app/data/pokemon-evolutions.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { Species } from "#app/enums/species.js"; +import { pokemonEvolutions, SpeciesFormEvolution, SpeciesWildEvolutionDelay } from "#app/data/pokemon-evolutions"; +import { Abilities } from "#app/enums/abilities"; +import { Species } from "#app/enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/src/test/evolutions/evolutions.test.ts b/src/test/evolutions/evolutions.test.ts index af43e91b059..2028764115c 100644 --- a/src/test/evolutions/evolutions.test.ts +++ b/src/test/evolutions/evolutions.test.ts @@ -1,8 +1,8 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; import * as Utils from "#app/utils"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Evolution tests", () => { let phaserGame: Phaser.Game; diff --git a/src/test/field/pokemon.test.ts b/src/test/field/pokemon.test.ts index ee8e41e8b42..d597cd5219c 100644 --- a/src/test/field/pokemon.test.ts +++ b/src/test/field/pokemon.test.ts @@ -1,4 +1,4 @@ -import { Species } from "#app/enums/species.js"; +import { Species } from "#app/enums/species"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "../utils/gameManager"; diff --git a/src/test/final_boss.test.ts b/src/test/final_boss.test.ts index a57d71534a3..0f59572619b 100644 --- a/src/test/final_boss.test.ts +++ b/src/test/final_boss.test.ts @@ -1,8 +1,8 @@ -import { Biome } from "#app/enums/biome.js"; -import { Species } from "#app/enums/species.js"; +import { Biome } from "#app/enums/biome"; +import { Species } from "#app/enums/species"; +import { GameModes } from "#app/game-mode"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "./utils/gameManager"; -import { GameModes } from "#app/game-mode"; const FinalWave = { Classic: 200, diff --git a/src/test/game-mode.test.ts b/src/test/game-mode.test.ts index 4a1960a05ff..ccec3a3aa16 100644 --- a/src/test/game-mode.test.ts +++ b/src/test/game-mode.test.ts @@ -1,7 +1,7 @@ -import { GameMode, GameModes, getGameMode } from "#app/game-mode.js"; +import { GameMode, GameModes, getGameMode } from "#app/game-mode"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import GameManager from "./utils/gameManager"; import * as Utils from "../utils"; +import GameManager from "./utils/gameManager"; describe("game-mode", () => { let phaserGame: Phaser.Game; let game: GameManager; diff --git a/src/test/imports.test.ts b/src/test/imports.test.ts index 69c145236bc..305eccdc465 100644 --- a/src/test/imports.test.ts +++ b/src/test/imports.test.ts @@ -1,5 +1,5 @@ -import { describe, expect, it } from "vitest"; import { initStatsKeys } from "#app/ui/game-stats-ui-handler"; +import { describe, expect, it } from "vitest"; async function importModule() { try { diff --git a/src/test/inputs/inputs.test.ts b/src/test/inputs/inputs.test.ts index 7182ac2c02c..6306c1b9da6 100644 --- a/src/test/inputs/inputs.test.ts +++ b/src/test/inputs/inputs.test.ts @@ -1,9 +1,9 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import pad_xbox360 from "#app/configs/inputs/pad_xbox360"; import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty"; +import pad_xbox360 from "#app/configs/inputs/pad_xbox360"; +import GameManager from "#test/utils/gameManager"; import InputsHandler from "#test/utils/inputsHandler"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Inputs", () => { diff --git a/src/test/internals.test.ts b/src/test/internals.test.ts index 0ecd156431d..3c76b40e901 100644 --- a/src/test/internals.test.ts +++ b/src/test/internals.test.ts @@ -1,8 +1,8 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; +import { Abilities } from "#app/enums/abilities"; +import { Species } from "#app/enums/species"; import GameManager from "#test/utils/gameManager"; -import { Species } from "#app/enums/species.js"; -import { Abilities } from "#app/enums/abilities.js"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Internals", () => { let phaserGame: Phaser.Game; diff --git a/src/test/items/eviolite.test.ts b/src/test/items/eviolite.test.ts index 0fe90866de8..e491784acec 100644 --- a/src/test/items/eviolite.test.ts +++ b/src/test/items/eviolite.test.ts @@ -2,9 +2,9 @@ import { Stat } from "#app/data/pokemon-stat"; import { EvolutionStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; 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"; diff --git a/src/test/items/exp_booster.test.ts b/src/test/items/exp_booster.test.ts index 2b700c92086..9a7464e4866 100644 --- a/src/test/items/exp_booster.test.ts +++ b/src/test/items/exp_booster.test.ts @@ -1,7 +1,7 @@ -import { Abilities } from "#app/enums/abilities.js"; -import { PokemonExpBoosterModifier } from "#app/modifier/modifier.js"; -import GameManager from "#test/utils/gameManager"; +import { Abilities } from "#app/enums/abilities"; +import { PokemonExpBoosterModifier } from "#app/modifier/modifier"; import * as Utils from "#app/utils"; +import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/src/test/items/grip_claw.test.ts b/src/test/items/grip_claw.test.ts index 40ef81fed73..09afa9aea0b 100644 --- a/src/test/items/grip_claw.test.ts +++ b/src/test/items/grip_claw.test.ts @@ -1,14 +1,14 @@ -import { BattlerIndex } from "#app/battle.js"; -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { BerryType } from "#app/enums/berry-type.js"; -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; -import { CommandPhase, MoveEndPhase, SelectTargetPhase } from "#app/phases.js"; +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 GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { SPLASH_ONLY } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; // 20 seconds @@ -33,12 +33,12 @@ describe("Items - Grip Claw", () => { .battleType("double") .moveset([Moves.POPULATION_BOMB, Moves.SPLASH]) .startingHeldItems([ - { name: "GRIP_CLAW", count: 5 }, + { name: "GRIP_CLAW", count: 5 }, // TODO: Find a way to mock the steal chance of grip claw { name: "MULTI_LENS", count: 3 }, ]) .enemySpecies(Species.SNORLAX) .ability(Abilities.KLUTZ) - .enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]) + .enemyMoveset(SPLASH_ONLY) .enemyHeldItems([ { name: "BERRY", type: BerryType.SITRUS, count: 2 }, { name: "BERRY", type: BerryType.LUM, count: 2 }, @@ -52,19 +52,14 @@ describe("Items - Grip Claw", () => { it( "should only steal items from the attack target", async () => { - await game.startBattle([Species.PANSEAR, Species.ROWLET, Species.PANPOUR, Species.PANSAGE, Species.CHARMANDER, Species.SQUIRTLE]); + await game.startBattle([Species.PANSEAR, Species.ROWLET]); const enemyPokemon = game.scene.getEnemyField(); const enemyHeldItemCt = enemyPokemon.map(p => p.getHeldItems.length); - game.doAttack(getMovePosition(game.scene, 0, Moves.POPULATION_BOMB)); - - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY); - - await game.phaseInterceptor.to(CommandPhase, false); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.POPULATION_BOMB, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEndPhase, false); diff --git a/src/test/items/leek.test.ts b/src/test/items/leek.test.ts index 4abc470c6f0..7505b6374a0 100644 --- a/src/test/items/leek.test.ts +++ b/src/test/items/leek.test.ts @@ -1,11 +1,11 @@ import { BattlerIndex } from "#app/battle"; import { CritBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { MoveEffectPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import * as Utils from "#app/utils"; 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"; @@ -27,21 +27,21 @@ describe("Items - Leek", () => { game = new GameManager(phaserGame); game.override.enemySpecies(Species.MAGIKARP); - game.override.enemyMoveset([ Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH ]); + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); game.override.disableCrits(); game.override.battleType("single"); }); - it("LEEK activates in battle correctly", async() => { + it("LEEK activates in battle correctly", async () => { game.override.startingHeldItems([{ name: "LEEK" }]); - game.override.moveset([ Moves.POUND ]); + game.override.moveset([Moves.POUND]); const consoleSpy = vi.spyOn(console, "log"); await game.startBattle([ Species.FARFETCHD ]); - game.doAttack(0); + game.move.select(Moves.POUND); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -50,7 +50,7 @@ describe("Items - Leek", () => { expect(consoleSpy).toHaveBeenCalledWith("Applied", "Leek", ""); }, 20000); - it("LEEK held by FARFETCHD", async() => { + it("LEEK held by FARFETCHD", async () => { await game.startBattle([ Species.FARFETCHD ]); @@ -70,7 +70,7 @@ describe("Items - Leek", () => { expect(critLevel.value).toBe(2); }, 20000); - it("LEEK held by GALAR_FARFETCHD", async() => { + it("LEEK held by GALAR_FARFETCHD", async () => { await game.startBattle([ Species.GALAR_FARFETCHD ]); @@ -90,7 +90,7 @@ describe("Items - Leek", () => { expect(critLevel.value).toBe(2); }, 20000); - it("LEEK held by SIRFETCHD", async() => { + it("LEEK held by SIRFETCHD", async () => { await game.startBattle([ Species.SIRFETCHD ]); @@ -110,9 +110,9 @@ describe("Items - Leek", () => { expect(critLevel.value).toBe(2); }, 20000); - it("LEEK held by fused FARFETCHD line (base)", async() => { + it("LEEK held by fused FARFETCHD line (base)", async () => { // Randomly choose from the Farfetch'd line - const species = [ Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD ]; + const species = [Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD]; await game.startBattle([ species[Utils.randInt(species.length)], @@ -145,9 +145,9 @@ describe("Items - Leek", () => { expect(critLevel.value).toBe(2); }, 20000); - it("LEEK held by fused FARFETCHD line (part)", async() => { + it("LEEK held by fused FARFETCHD line (part)", async () => { // Randomly choose from the Farfetch'd line - const species = [ Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD ]; + const species = [Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD]; await game.startBattle([ Species.PIKACHU, @@ -180,7 +180,7 @@ describe("Items - Leek", () => { expect(critLevel.value).toBe(2); }, 20000); - it("LEEK not held by FARFETCHD line", async() => { + it("LEEK not held by FARFETCHD line", async () => { await game.startBattle([ Species.PIKACHU ]); diff --git a/src/test/items/leftovers.test.ts b/src/test/items/leftovers.test.ts index e791c4426a1..8e548542436 100644 --- a/src/test/items/leftovers.test.ts +++ b/src/test/items/leftovers.test.ts @@ -1,9 +1,9 @@ -import { DamagePhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { DamagePhase } from "#app/phases/damage-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 Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -31,10 +31,10 @@ describe("Items - Leftovers", () => { game.override.enemySpecies(Species.SHUCKLE); game.override.enemyAbility(Abilities.UNNERVE); game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); - game.override.startingHeldItems([{name: "LEFTOVERS", count: 1}]); + game.override.startingHeldItems([{ name: "LEFTOVERS", count: 1 }]); }); - it("leftovers works", async() => { + it("leftovers works", async () => { await game.startBattle([Species.ARCANINE]); // Make sure leftovers are there @@ -45,7 +45,7 @@ describe("Items - Leftovers", () => { // We should have full hp expect(leadPokemon.isFullHp()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); // We should have less hp after the attack await game.phaseInterceptor.to(DamagePhase, false); diff --git a/src/test/items/light_ball.test.ts b/src/test/items/light_ball.test.ts index ff7dfa4eba5..cf4f5c9e22f 100644 --- a/src/test/items/light_ball.test.ts +++ b/src/test/items/light_ball.test.ts @@ -2,9 +2,9 @@ import { Stat } from "#app/data/pokemon-stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; 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"; diff --git a/src/test/items/lock_capsule.test.ts b/src/test/items/lock_capsule.test.ts new file mode 100644 index 00000000000..bc4ca1cb014 --- /dev/null +++ b/src/test/items/lock_capsule.test.ts @@ -0,0 +1,46 @@ +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#app/enums/moves"; +import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type"; +import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import GameManager from "#test/utils/gameManager"; +import Phase from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Items - Lock Capsule", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phase.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + + game.override + .battleType("single") + .startingLevel(200) + .moveset([Moves.SURF]) + .enemyAbility(Abilities.BALL_FETCH) + .startingModifier([{ name: "LOCK_CAPSULE" }]); + }); + + it("doesn't set the cost of common tier items to 0", async () => { + await game.startBattle(); + + game.move.select(Moves.SURF); + await game.phaseInterceptor.to(SelectModifierPhase, false); + + const rewards = game.scene.getCurrentPhase() as SelectModifierPhase; + const potion = new ModifierTypeOption(modifierTypes.POTION(), 0, 40); // Common tier item + const rerollCost = rewards.getRerollCost([potion, potion, potion], true); + + expect(rerollCost).toBe(150); + }, 20000); +}); diff --git a/src/test/items/metal_powder.test.ts b/src/test/items/metal_powder.test.ts index 966762e4175..a3a4936532f 100644 --- a/src/test/items/metal_powder.test.ts +++ b/src/test/items/metal_powder.test.ts @@ -2,9 +2,9 @@ import { Stat } from "#app/data/pokemon-stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; 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"; diff --git a/src/test/items/quick_powder.test.ts b/src/test/items/quick_powder.test.ts index d2435dab431..53521ba78f1 100644 --- a/src/test/items/quick_powder.test.ts +++ b/src/test/items/quick_powder.test.ts @@ -2,9 +2,9 @@ import { Stat } from "#app/data/pokemon-stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; 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"; diff --git a/src/test/items/scope_lens.test.ts b/src/test/items/scope_lens.test.ts index 4efc7ab9d05..85673218762 100644 --- a/src/test/items/scope_lens.test.ts +++ b/src/test/items/scope_lens.test.ts @@ -1,11 +1,11 @@ import { BattlerIndex } from "#app/battle"; import { CritBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { MoveEffectPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import * as Utils from "#app/utils"; 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"; @@ -27,30 +27,30 @@ describe("Items - Scope Lens", () => { game = new GameManager(phaserGame); game.override.enemySpecies(Species.MAGIKARP); - game.override.enemyMoveset([ Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH ]); + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); game.override.disableCrits(); game.override.battleType("single"); }, 20000); - it("SCOPE_LENS activates in battle correctly", async() => { + it("SCOPE_LENS activates in battle correctly", async () => { game.override.startingHeldItems([{ name: "SCOPE_LENS" }]); - game.override.moveset([ Moves.POUND ]); + game.override.moveset([Moves.POUND]); const consoleSpy = vi.spyOn(console, "log"); await game.startBattle([ Species.GASTLY ]); - game.doAttack(0); + game.move.select(Moves.POUND); - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase); expect(consoleSpy).toHaveBeenCalledWith("Applied", "Scope Lens", ""); }, 20000); - it("SCOPE_LENS held by random pokemon", async() => { + it("SCOPE_LENS held by random pokemon", async () => { await game.startBattle([ Species.GASTLY ]); diff --git a/src/test/items/thick_club.test.ts b/src/test/items/thick_club.test.ts index 841cd7c90ac..347921446e6 100644 --- a/src/test/items/thick_club.test.ts +++ b/src/test/items/thick_club.test.ts @@ -2,9 +2,9 @@ import { Stat } from "#app/data/pokemon-stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; 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"; diff --git a/src/test/items/toxic_orb.test.ts b/src/test/items/toxic_orb.test.ts index 69f55cb2bbc..95336c0793e 100644 --- a/src/test/items/toxic_orb.test.ts +++ b/src/test/items/toxic_orb.test.ts @@ -1,13 +1,12 @@ import { StatusEffect } from "#app/data/status-effect"; -import { CommandPhase, EnemyCommandPhase, MessagePhase, TurnEndPhase } from "#app/phases"; +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 GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; 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"; @@ -42,7 +41,7 @@ describe("Items - Toxic orb", () => { }]); }); - it("TOXIC ORB", async() => { + it("TOXIC ORB", async () => { initI18n(); i18next.changeLanguage("en"); const moveToUse = Moves.GROWTH; @@ -52,15 +51,7 @@ describe("Items - Toxic orb", () => { ]); expect(game.scene.modifiers[0].type.id).toBe("TOXIC_ORB"); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - // Select Attack - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - // Select Move Growth - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); // will run the 13 phase from enemyCommandPhase to TurnEndPhase await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase); diff --git a/src/test/localization/battle-stat.test.ts b/src/test/localization/battle-stat.test.ts index b99ed2b7064..e8fc20ab5a4 100644 --- a/src/test/localization/battle-stat.test.ts +++ b/src/test/localization/battle-stat.test.ts @@ -1,26 +1,25 @@ -import { beforeAll, describe, expect, it } from "vitest"; -import { getBattleStatName, getBattleStatLevelChangeDescription } from "#app/data/battle-stat.js"; -import { BattleStat} from "#app/data/battle-stat.js"; -import { pokemonInfo as enPokemonInfo } from "#app/locales/en/pokemon-info.js"; -import { battle as enBattleStat } from "#app/locales/en/battle.js"; -import { pokemonInfo as dePokemonInfo } from "#app/locales/de/pokemon-info.js"; -import { battle as deBattleStat } from "#app/locales/de/battle.js"; -import { pokemonInfo as esPokemonInfo } from "#app/locales/es/pokemon-info.js"; -import { battle as esBattleStat } from "#app/locales/es/battle.js"; -import { pokemonInfo as frPokemonInfo } from "#app/locales/fr/pokemon-info.js"; -import { battle as frBattleStat } from "#app/locales/fr/battle.js"; -import { pokemonInfo as itPokemonInfo } from "#app/locales/it/pokemon-info.js"; -import { battle as itBattleStat } from "#app/locales/it/battle.js"; -import { pokemonInfo as koPokemonInfo } from "#app/locales/ko/pokemon-info.js"; -import { battle as koBattleStat } from "#app/locales/ko/battle.js"; -import { pokemonInfo as ptBrPokemonInfo } from "#app/locales/pt_BR/pokemon-info.js"; -import { battle as ptBrBattleStat } from "#app/locales/pt_BR/battle.js"; -import { pokemonInfo as zhCnPokemonInfo } from "#app/locales/zh_CN/pokemon-info.js"; -import { battle as zhCnBattleStat } from "#app/locales/zh_CN/battle.js"; -import { pokemonInfo as zhTwPokemonInfo } from "#app/locales/zh_TW/pokemon-info.js"; -import { battle as zhTwBattleStat } from "#app/locales/zh_TW/battle.js"; +import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "#app/data/battle-stat"; +import { battle as deBattleStat } from "#app/locales/de/battle"; +import { pokemonInfo as dePokemonInfo } from "#app/locales/de/pokemon-info"; +import { battle as enBattleStat } from "#app/locales/en/battle"; +import { pokemonInfo as enPokemonInfo } from "#app/locales/en/pokemon-info"; +import { battle as esBattleStat } from "#app/locales/es/battle"; +import { pokemonInfo as esPokemonInfo } from "#app/locales/es/pokemon-info"; +import { battle as frBattleStat } from "#app/locales/fr/battle"; +import { pokemonInfo as frPokemonInfo } from "#app/locales/fr/pokemon-info"; +import { battle as itBattleStat } from "#app/locales/it/battle"; +import { pokemonInfo as itPokemonInfo } from "#app/locales/it/pokemon-info"; +import { battle as koBattleStat } from "#app/locales/ko/battle"; +import { pokemonInfo as koPokemonInfo } from "#app/locales/ko/pokemon-info"; +import { battle as ptBrBattleStat } from "#app/locales/pt_BR/battle"; +import { pokemonInfo as ptBrPokemonInfo } from "#app/locales/pt_BR/pokemon-info"; +import { battle as zhCnBattleStat } from "#app/locales/zh_CN/battle"; +import { pokemonInfo as zhCnPokemonInfo } from "#app/locales/zh_CN/pokemon-info"; +import { battle as zhTwBattleStat } from "#app/locales/zh_TW/battle"; +import { pokemonInfo as zhTwPokemonInfo } from "#app/locales/zh_TW/pokemon-info"; import i18next, { initI18n } from "#app/plugins/i18n"; import { KoreanPostpositionProcessor } from "i18next-korean-postposition-processor"; +import { beforeAll, describe, expect, it } from "vitest"; interface BattleStatTestUnit { stat: BattleStat, diff --git a/src/test/localization/french.test.ts b/src/test/localization/french.test.ts index b03a8ee64e8..92b4c82d7cb 100644 --- a/src/test/localization/french.test.ts +++ b/src/test/localization/french.test.ts @@ -1,9 +1,9 @@ -import { afterEach, beforeAll, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; -import i18next from "i18next"; import { initI18n } from "#app/plugins/i18n"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import i18next from "i18next"; +import Phaser from "phaser"; +import { afterEach, beforeAll, describe, expect, it } from "vitest"; describe("Lokalization - french", () => { let phaserGame: Phaser.Game; diff --git a/src/test/localization/status-effect.test.ts b/src/test/localization/status-effect.test.ts index 8a9effe1672..9dcab5aeb5f 100644 --- a/src/test/localization/status-effect.test.ts +++ b/src/test/localization/status-effect.test.ts @@ -1,7 +1,7 @@ -import { beforeAll, describe, afterEach, expect, it, vi } from "vitest"; import { StatusEffect, getStatusEffectActivationText, getStatusEffectDescriptor, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText } from "#app/data/status-effect"; -import i18next from "i18next"; import { mockI18next } from "#test/utils/testUtils"; +import i18next from "i18next"; +import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; const pokemonName = "PKM"; const sourceText = "SOURCE"; diff --git a/src/test/localization/terrain.test.ts b/src/test/localization/terrain.test.ts index c072f9cc9ab..ed280177a06 100644 --- a/src/test/localization/terrain.test.ts +++ b/src/test/localization/terrain.test.ts @@ -1,11 +1,11 @@ import { TerrainType, getTerrainName } from "#app/data/terrain"; import { getTerrainBlockMessage, getTerrainClearMessage, getTerrainStartMessage } from "#app/data/weather"; -import GameManager from "#test/utils/gameManager"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { mockI18next } from "#test/utils/testUtils"; import i18next from "i18next"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { mockI18next } from "#test/utils/testUtils"; describe("terrain", () => { let phaserGame: Phaser.Game; diff --git a/src/test/moves/astonish.test.ts b/src/test/moves/astonish.test.ts index 358e4a9bec3..b21e2a06051 100644 --- a/src/test/moves/astonish.test.ts +++ b/src/test/moves/astonish.test.ts @@ -1,13 +1,15 @@ -import { allMoves } from "#app/data/move.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { BerryPhase, CommandPhase, MoveEndPhase, TurnEndPhase } from "#app/phases.js"; +import { allMoves } from "#app/data/move"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MoveEndPhase } from "#app/phases/move-end-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 Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; const TIMEOUT = 20 * 1000; @@ -47,7 +49,7 @@ describe("Moves - Astonish", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.ASTONISH)); + game.move.select(Moves.ASTONISH); await game.phaseInterceptor.to(MoveEndPhase, false); @@ -60,7 +62,7 @@ describe("Moves - Astonish", () => { await game.phaseInterceptor.to(CommandPhase, false); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/aurora_veil.test.ts b/src/test/moves/aurora_veil.test.ts index a10c9b6b60a..fec280debf4 100644 --- a/src/test/moves/aurora_veil.test.ts +++ b/src/test/moves/aurora_veil.test.ts @@ -1,15 +1,14 @@ -import { ArenaTagSide } from "#app/data/arena-tag.js"; -import Move, { allMoves } from "#app/data/move.js"; -import { WeatherType } from "#app/data/weather.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import Pokemon from "#app/field/pokemon.js"; -import { TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { NumberHolder } from "#app/utils.js"; +import { ArenaTagSide } from "#app/data/arena-tag"; +import Move, { allMoves } from "#app/data/move"; +import { WeatherType } from "#app/data/weather"; +import { Abilities } from "#app/enums/abilities"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import Pokemon from "#app/field/pokemon"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { NumberHolder } from "#app/utils"; 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"; @@ -18,7 +17,7 @@ describe("Moves - Aurora Veil", () => { let phaserGame: Phaser.Game; let game: GameManager; const singleBattleMultiplier = 0.5; - const doubleBattleMultiplier = 2732/4096; + const doubleBattleMultiplier = 2732 / 4096; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -42,11 +41,11 @@ describe("Moves - Aurora Veil", () => { game.override.weather(WeatherType.HAIL); }); - it("reduces damage of physical attacks by half in a single battle", async() => { + it("reduces damage of physical attacks by half in a single battle", async () => { const moveToUse = Moves.TACKLE; await game.startBattle([Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); @@ -54,14 +53,14 @@ describe("Moves - Aurora Veil", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); - it("reduces damage of physical attacks by a third in a double battle", async() => { + it("reduces damage of physical attacks by a third in a double battle", async () => { game.override.battleType("double"); const moveToUse = Moves.ROCK_SLIDE; await game.startBattle([Species.SHUCKLE, Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); - game.doAttack(getMovePosition(game.scene, 1, moveToUse)); + game.move.select(moveToUse); + game.move.select(moveToUse, 1); await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); @@ -69,11 +68,11 @@ describe("Moves - Aurora Veil", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); - it("reduces damage of special attacks by half in a single battle", async() => { + it("reduces damage of special attacks by half in a single battle", async () => { const moveToUse = Moves.ABSORB; await game.startBattle([Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); @@ -82,14 +81,14 @@ describe("Moves - Aurora Veil", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); - it("reduces damage of special attacks by a third in a double battle", async() => { + it("reduces damage of special attacks by a third in a double battle", async () => { game.override.battleType("double"); const moveToUse = Moves.DAZZLING_GLEAM; await game.startBattle([Species.SHUCKLE, Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); - game.doAttack(getMovePosition(game.scene, 1, moveToUse)); + game.move.select(moveToUse); + game.move.select(moveToUse, 1); await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); diff --git a/src/test/moves/baton_pass.test.ts b/src/test/moves/baton_pass.test.ts index 9f0cb3619b2..602da9e37f8 100644 --- a/src/test/moves/baton_pass.test.ts +++ b/src/test/moves/baton_pass.test.ts @@ -1,7 +1,7 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { PostSummonPhase, TurnEndPhase } from "#app/phases.js"; +import { BattleStat } from "#app/data/battle-stat"; +import { PostSummonPhase } from "#app/phases/post-summon-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; @@ -35,7 +35,7 @@ describe("Moves - Baton Pass", () => { .disableCrits(); }); - it("passes stat stage buffs when player uses it", async() => { + it("passes stat stage buffs when player uses it", async () => { // arrange await game.startBattle([ Species.RAICHU, @@ -43,12 +43,12 @@ describe("Moves - Baton Pass", () => { ]); // round 1 - buff - game.doAttack(getMovePosition(game.scene, 0, Moves.NASTY_PLOT)); + game.move.select(Moves.NASTY_PLOT); await game.toNextTurn(); expect(game.scene.getPlayerPokemon()!.summonData.battleStats[BattleStat.SPATK]).toEqual(2); // round 2 - baton pass - game.doAttack(getMovePosition(game.scene, 0, Moves.BATON_PASS)); + game.move.select(Moves.BATON_PASS); game.doSelectPartyPokemon(1); await game.phaseInterceptor.to(TurnEndPhase); @@ -58,7 +58,7 @@ describe("Moves - Baton Pass", () => { expect(playerPkm.summonData.battleStats[BattleStat.SPATK]).toEqual(2); }, 20000); - it("passes stat stage buffs when AI uses it", async() => { + it("passes stat stage buffs when AI uses it", async () => { // arrange game.override .startingWave(5) @@ -69,13 +69,13 @@ describe("Moves - Baton Pass", () => { ]); // round 1 - ai buffs - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.toNextTurn(); // round 2 - baton pass game.scene.getEnemyPokemon()!.hp = 100; game.override.enemyMoveset(new Array(4).fill(Moves.BATON_PASS)); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(PostSummonPhase, false); // assert diff --git a/src/test/moves/beak_blast.test.ts b/src/test/moves/beak_blast.test.ts index 61a022ac9eb..2a93dc00a54 100644 --- a/src/test/moves/beak_blast.test.ts +++ b/src/test/moves/beak_blast.test.ts @@ -1,13 +1,14 @@ -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { StatusEffect } from "#app/enums/status-effect"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { MovePhase } from "#app/phases/move-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { BerryPhase, MovePhase, TurnEndPhase } from "#app/phases"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { StatusEffect } from "#app/enums/status-effect.js"; +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; @@ -46,7 +47,7 @@ describe("Moves - Beak Blast", () => { const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAK_BLAST)); + game.move.select(Moves.BEAK_BLAST); await game.phaseInterceptor.to(MovePhase, false); expect(leadPokemon.getTag(BattlerTagType.BEAK_BLAST_CHARGING)).toBeDefined(); @@ -66,7 +67,7 @@ describe("Moves - Beak Blast", () => { const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAK_BLAST)); + game.move.select(Moves.BEAK_BLAST); await game.phaseInterceptor.to(MovePhase, false); expect(leadPokemon.getTag(BattlerTagType.BEAK_BLAST_CHARGING)).toBeDefined(); @@ -86,7 +87,7 @@ describe("Moves - Beak Blast", () => { const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAK_BLAST)); + game.move.select(Moves.BEAK_BLAST); await game.phaseInterceptor.to(MovePhase, false); expect(leadPokemon.getTag(BattlerTagType.BEAK_BLAST_CHARGING)).toBeDefined(); @@ -99,13 +100,13 @@ describe("Moves - Beak Blast", () => { it( "should only hit twice with Multi-Lens", async () => { - game.override.startingHeldItems([{name: "MULTI_LENS", count: 1}]); + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); await game.startBattle([Species.BLASTOISE]); const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAK_BLAST)); + game.move.select(Moves.BEAK_BLAST); await game.phaseInterceptor.to(BerryPhase, false); expect(leadPokemon.turnData.hitCount).toBe(2); @@ -122,7 +123,7 @@ describe("Moves - Beak Blast", () => { const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAK_BLAST)); + game.move.select(Moves.BEAK_BLAST); await game.phaseInterceptor.to(MovePhase, false); expect(leadPokemon.getTag(BattlerTagType.BEAK_BLAST_CHARGING)).toBeDefined(); diff --git a/src/test/moves/beat_up.test.ts b/src/test/moves/beat_up.test.ts index a5e4b3cbd34..ce1598a49b4 100644 --- a/src/test/moves/beat_up.test.ts +++ b/src/test/moves/beat_up.test.ts @@ -1,12 +1,11 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { StatusEffect } from "#app/enums/status-effect"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import GameManager from "#test/utils/gameManager"; -import { Species } from "#app/enums/species.js"; -import { Moves } from "#app/enums/moves.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { MoveEffectPhase } from "#app/phases.js"; -import { StatusEffect } from "#app/enums/status-effect.js"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; const TIMEOUT = 20 * 1000; // 20 sec timeout @@ -46,7 +45,7 @@ describe("Moves - Beat Up", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; let enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAT_UP)); + game.move.select(Moves.BEAT_UP); await game.phaseInterceptor.to(MoveEffectPhase); @@ -70,7 +69,7 @@ describe("Moves - Beat Up", () => { game.scene.getParty()[1].trySetStatus(StatusEffect.BURN); - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAT_UP)); + game.move.select(Moves.BEAT_UP); await game.phaseInterceptor.to(MoveEffectPhase); @@ -81,14 +80,14 @@ describe("Moves - Beat Up", () => { it( "should hit twice for each player Pokemon if the user has Multi-Lens", async () => { - game.override.startingHeldItems([{name: "MULTI_LENS", count: 1}]); + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); await game.startBattle([Species.MAGIKARP, Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, Species.PIKACHU, Species.EEVEE]); const playerPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; let enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAT_UP)); + game.move.select(Moves.BEAT_UP); await game.phaseInterceptor.to(MoveEffectPhase); diff --git a/src/test/moves/belly_drum.test.ts b/src/test/moves/belly_drum.test.ts index 74afc910faf..631de952a58 100644 --- a/src/test/moves/belly_drum.test.ts +++ b/src/test/moves/belly_drum.test.ts @@ -1,11 +1,11 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { TurnEndPhase } from "#app/phases"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { toDmgValue } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { BattleStat } from "#app/data/battle-stat"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; const TIMEOUT = 20 * 1000; // RATIO : HP Cost of Move @@ -40,13 +40,13 @@ describe("Moves - BELLY DRUM", () => { // Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Belly_Drum_(move) test("Belly Drum raises the user's Attack to its max, at the cost of 1/2 of its maximum HP", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); - game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); + game.move.select(Moves.BELLY_DRUM); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); @@ -55,17 +55,17 @@ describe("Moves - BELLY DRUM", () => { ); test("Belly Drum will still take effect if an uninvolved stat is at max", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); // Here - BattleStat.ATK -> -3 and BattleStat.SPATK -> 6 leadPokemon.summonData.battleStats[BattleStat.ATK] = -3; leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6; - game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); + game.move.select(Moves.BELLY_DRUM); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); @@ -75,14 +75,14 @@ describe("Moves - BELLY DRUM", () => { ); test("Belly Drum fails if the pokemon's attack stat is at its maximum", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; - game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); + game.move.select(Moves.BELLY_DRUM); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); @@ -91,14 +91,14 @@ describe("Moves - BELLY DRUM", () => { ); test("Belly Drum fails if the user's health is less than 1/2", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; - game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); + game.move.select(Moves.BELLY_DRUM); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(hpLost - PREDAMAGE); diff --git a/src/test/moves/ceaseless_edge.test.ts b/src/test/moves/ceaseless_edge.test.ts index c5ce8375102..34ecf8f39f6 100644 --- a/src/test/moves/ceaseless_edge.test.ts +++ b/src/test/moves/ceaseless_edge.test.ts @@ -2,11 +2,11 @@ import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; import { allMoves } from "#app/data/move"; import { Abilities } from "#app/enums/abilities"; import { ArenaTagType } from "#app/enums/arena-tag-type"; -import { MoveEffectPhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; 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, test, vi } from "vitest"; @@ -34,8 +34,8 @@ describe("Moves - Ceaseless Edge", () => { game.override.enemyPassiveAbility(Abilities.RUN_AWAY); game.override.startingLevel(100); game.override.enemyLevel(100); - game.override.moveset([ Moves.CEASELESS_EDGE, Moves.SPLASH, Moves.ROAR ]); - game.override.enemyMoveset([Moves.SPLASH,Moves.SPLASH,Moves.SPLASH,Moves.SPLASH]); + game.override.moveset([Moves.CEASELESS_EDGE, Moves.SPLASH, Moves.ROAR]); + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); vi.spyOn(allMoves[Moves.CEASELESS_EDGE], "accuracy", "get").mockReturnValue(100); }); @@ -43,13 +43,13 @@ describe("Moves - Ceaseless Edge", () => { test( "move should hit and apply spikes", async () => { - await game.startBattle([ Species.ILLUMISE ]); + await game.startBattle([Species.ILLUMISE]); const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.CEASELESS_EDGE)); + game.move.select(Moves.CEASELESS_EDGE); await game.phaseInterceptor.to(MoveEffectPhase, false); // Spikes should not have any layers before move effect is applied @@ -67,14 +67,14 @@ describe("Moves - Ceaseless Edge", () => { test( "move should hit twice with multi lens and apply two layers of spikes", async () => { - game.override.startingHeldItems([{name: "MULTI_LENS"}]); - await game.startBattle([ Species.ILLUMISE ]); + game.override.startingHeldItems([{ name: "MULTI_LENS" }]); + await game.startBattle([Species.ILLUMISE]); const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.CEASELESS_EDGE)); + game.move.select(Moves.CEASELESS_EDGE); await game.phaseInterceptor.to(MoveEffectPhase, false); // Spikes should not have any layers before move effect is applied @@ -92,12 +92,12 @@ describe("Moves - Ceaseless Edge", () => { test( "trainer - move should hit twice, apply two layers of spikes, force switch opponent - opponent takes damage", async () => { - game.override.startingHeldItems([{name: "MULTI_LENS"}]); + game.override.startingHeldItems([{ name: "MULTI_LENS" }]); game.override.startingWave(5); - await game.startBattle([ Species.ILLUMISE ]); + await game.startBattle([Species.ILLUMISE]); - game.doAttack(getMovePosition(game.scene, 0, Moves.CEASELESS_EDGE)); + game.move.select(Moves.CEASELESS_EDGE); await game.phaseInterceptor.to(MoveEffectPhase, false); // Spikes should not have any layers before move effect is applied const tagBefore = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag; @@ -111,7 +111,7 @@ describe("Moves - Ceaseless Edge", () => { const hpBeforeSpikes = game.scene.currentBattle.enemyParty[1].hp; // Check HP of pokemon that WILL BE switched in (index 1) game.forceOpponentToSwitch(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase, false); expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(hpBeforeSpikes); }, TIMEOUT diff --git a/src/test/moves/clangorous_soul.test.ts b/src/test/moves/clangorous_soul.test.ts index 5493466ba56..9ea6da91595 100644 --- a/src/test/moves/clangorous_soul.test.ts +++ b/src/test/moves/clangorous_soul.test.ts @@ -1,12 +1,12 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { TurnEndPhase } from "#app/phases"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { toDmgValue } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { BattleStat } from "#app/data/battle-stat"; +import GameManager from "#test/utils/gameManager"; import { SPLASH_ONLY } from "#test/utils/testUtils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; const TIMEOUT = 20 * 1000; /** HP Cost of Move */ @@ -41,13 +41,13 @@ describe("Moves - CLANGOROUS_SOUL", () => { //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Clangorous_Soul_(move) test("Clangorous Soul raises the user's Attack, Defense, Special Attack, Special Defense and Speed by one stage each, at the cost of 1/3 of its maximum HP", - async() => { - await game.startBattle([Species.MAGIKARP]); + async () => { + await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const leadPokemon = game.scene.getPlayerPokemon()!; + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); - game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL)); + game.move.select(Moves.CLANGOROUS_SOUL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); @@ -56,15 +56,15 @@ describe("Moves - CLANGOROUS_SOUL", () => { expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(1); expect(leadPokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(1); expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(1); - }, TIMEOUT + }, TIMEOUT ); test("Clangorous Soul will still take effect if one or more of the involved stats are not at max", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); //Here - BattleStat.SPD -> 0 and BattleStat.SPDEF -> 4 leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; @@ -72,7 +72,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6; leadPokemon.summonData.battleStats[BattleStat.SPDEF] = 4; - game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL)); + game.move.select(Moves.CLANGOROUS_SOUL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); @@ -85,7 +85,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { ); test("Clangorous Soul fails if all stats involved are at max", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; @@ -96,7 +96,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { leadPokemon.summonData.battleStats[BattleStat.SPDEF] = 6; leadPokemon.summonData.battleStats[BattleStat.SPD] = 6; - game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL)); + game.move.select(Moves.CLANGOROUS_SOUL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); @@ -109,14 +109,14 @@ describe("Moves - CLANGOROUS_SOUL", () => { ); test("Clangorous Soul fails if the user's health is less than 1/3", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; - game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL)); + game.move.select(Moves.CLANGOROUS_SOUL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(hpLost - PREDAMAGE); diff --git a/src/test/moves/crafty_shield.test.ts b/src/test/moves/crafty_shield.test.ts index de2829aacf6..a341a50b0b9 100644 --- a/src/test/moves/crafty_shield.test.ts +++ b/src/test/moves/crafty_shield.test.ts @@ -1,13 +1,13 @@ +import { BattleStat } from "#app/data/battle-stat"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "../utils/gameManager"; -import { Species } from "#enums/species"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BerryPhase, CommandPhase } from "#app/phases.js"; -import { BattleStat } from "#app/data/battle-stat.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; const TIMEOUT = 20 * 1000; @@ -47,11 +47,11 @@ describe("Moves - Crafty Shield", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CRAFTY_SHIELD)); + game.move.select(Moves.CRAFTY_SHIELD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -68,11 +68,11 @@ describe("Moves - Crafty Shield", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CRAFTY_SHIELD)); + game.move.select(Moves.CRAFTY_SHIELD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -90,11 +90,11 @@ describe("Moves - Crafty Shield", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CRAFTY_SHIELD)); + game.move.select(Moves.CRAFTY_SHIELD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -109,11 +109,11 @@ describe("Moves - Crafty Shield", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CRAFTY_SHIELD)); + game.move.select(Moves.CRAFTY_SHIELD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SWORDS_DANCE)); + game.move.select(Moves.SWORDS_DANCE, 1); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/double_team.test.ts b/src/test/moves/double_team.test.ts index 2153b856517..c45c8bd8516 100644 --- a/src/test/moves/double_team.test.ts +++ b/src/test/moves/double_team.test.ts @@ -1,10 +1,9 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { Abilities } from "#app/enums/abilities"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -42,7 +41,7 @@ describe("Moves - Double Team", () => { vi.spyOn(enemy, "getAccuracyMultiplier"); expect(ally.summonData.battleStats[BattleStat.EVA]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.DOUBLE_TEAM)); + game.move.select(Moves.DOUBLE_TEAM); await game.phaseInterceptor.to(TurnEndPhase); await game.toNextTurn(); diff --git a/src/test/moves/dragon_rage.test.ts b/src/test/moves/dragon_rage.test.ts index 6ec7521f678..223635575ab 100644 --- a/src/test/moves/dragon_rage.test.ts +++ b/src/test/moves/dragon_rage.test.ts @@ -1,17 +1,16 @@ import { BattleStat } from "#app/data/battle-stat"; import { Type } from "#app/data/type"; -import { Species } from "#app/enums/species.js"; +import { Species } from "#app/enums/species"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Dragon Rage", () => { let phaserGame: Phaser.Game; @@ -62,7 +61,7 @@ describe("Moves - Dragon Rage", () => { game.override.disableCrits(); vi.spyOn(enemyPokemon, "getTypes").mockReturnValue([Type.DRAGON]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; @@ -73,7 +72,7 @@ describe("Moves - Dragon Rage", () => { game.override.disableCrits(); vi.spyOn(enemyPokemon, "getTypes").mockReturnValue([Type.STEEL]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; @@ -84,7 +83,7 @@ describe("Moves - Dragon Rage", () => { game.override.disableCrits(); partyPokemon.summonData.battleStats[BattleStat.SPATK] = 2; - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; @@ -95,7 +94,7 @@ describe("Moves - Dragon Rage", () => { game.override.disableCrits(); vi.spyOn(partyPokemon, "getTypes").mockReturnValue([Type.DRAGON]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; @@ -105,7 +104,7 @@ describe("Moves - Dragon Rage", () => { it("ignores criticals", async () => { partyPokemon.addTag(BattlerTagType.ALWAYS_CRIT, 99); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; @@ -116,7 +115,7 @@ describe("Moves - Dragon Rage", () => { game.override.disableCrits(); game.override.enemyAbility(Abilities.ICE_SCALES); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; @@ -127,7 +126,7 @@ describe("Moves - Dragon Rage", () => { game.override.disableCrits(); game.scene.addModifier(modifierTypes.MULTI_LENS().newModifier(partyPokemon), false); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; diff --git a/src/test/moves/dragon_tail.test.ts b/src/test/moves/dragon_tail.test.ts index 7374451e643..362383e2fe3 100644 --- a/src/test/moves/dragon_tail.test.ts +++ b/src/test/moves/dragon_tail.test.ts @@ -1,14 +1,15 @@ -import { allMoves } from "#app/data/move.js"; -import { SPLASH_ONLY } from "../utils/testUtils"; -import { BattleEndPhase, BerryPhase, TurnEndPhase} from "#app/phases.js"; +import { BattlerIndex } from "#app/battle"; +import { allMoves } from "#app/data/move"; +import { BattleEndPhase } from "#app/phases/battle-end-phase"; +import { BerryPhase } from "#app/phases/berry-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 Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import GameManager from "../utils/gameManager"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BattlerIndex } from "#app/battle.js"; +import { SPLASH_ONLY } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -44,9 +45,8 @@ describe("Moves - Dragon Tail", () => { await game.startBattle([Species.DRATINI]); const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); + game.move.select(Moves.DRAGON_TAIL); await game.phaseInterceptor.to(BerryPhase); @@ -66,12 +66,9 @@ describe("Moves - Dragon Tail", () => { await game.startBattle([Species.DRATINI]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).toBeDefined(); - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); + game.move.select(Moves.DRAGON_TAIL); await game.phaseInterceptor.to(BerryPhase); @@ -83,7 +80,7 @@ describe("Moves - Dragon Tail", () => { ); test( - "Double battles should proceed without crashing" , + "Double battles should proceed without crashing", async () => { game.override.battleType("double").enemyMoveset(SPLASH_ONLY); game.override.moveset([Moves.DRAGON_TAIL, Moves.SPLASH, Moves.FLAMETHROWER]) @@ -91,19 +88,12 @@ describe("Moves - Dragon Tail", () => { await game.startBattle([Species.DRATINI, Species.DRATINI, Species.WAILORD, Species.WAILORD]); const leadPokemon = game.scene.getParty()[0]!; - const secPokemon = game.scene.getParty()[1]!; - expect(leadPokemon).toBeDefined(); - expect(secPokemon).toBeDefined(); - const enemyLeadPokemon = game.scene.currentBattle.enemyParty[0]!; - const enemySecPokemon = game.scene.currentBattle.enemyParty[1]!; - expect(enemyLeadPokemon).toBeDefined(); - expect(enemySecPokemon).toBeDefined(); + const enemyLeadPokemon = game.scene.getEnemyParty()[0]!; + const enemySecPokemon = game.scene.getEnemyParty()[1]!; - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); - game.doSelectTarget(BattlerIndex.ENEMY); - - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.DRAGON_TAIL, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); @@ -115,10 +105,8 @@ describe("Moves - Dragon Tail", () => { expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); // second turn - - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAMETHROWER)); - game.doSelectTarget(BattlerIndex.ENEMY_2); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.FLAMETHROWER, 0, BattlerIndex.ENEMY_2); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase); expect(enemySecPokemon.hp).toBeLessThan(enemySecPokemon.getMaxHp()); @@ -126,7 +114,7 @@ describe("Moves - Dragon Tail", () => { ); test( - "Flee move redirection works" , + "Flee move redirection works", async () => { game.override.battleType("double").enemyMoveset(SPLASH_ONLY); game.override.moveset([Moves.DRAGON_TAIL, Moves.SPLASH, Moves.FLAMETHROWER]); @@ -135,20 +123,13 @@ describe("Moves - Dragon Tail", () => { const leadPokemon = game.scene.getParty()[0]!; const secPokemon = game.scene.getParty()[1]!; - expect(leadPokemon).toBeDefined(); - expect(secPokemon).toBeDefined(); - const enemyLeadPokemon = game.scene.currentBattle.enemyParty[0]!; - const enemySecPokemon = game.scene.currentBattle.enemyParty[1]!; - expect(enemyLeadPokemon).toBeDefined(); - expect(enemySecPokemon).toBeDefined(); - - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); - game.doSelectTarget(BattlerIndex.ENEMY); + const enemyLeadPokemon = game.scene.getEnemyParty()[0]!; + const enemySecPokemon = game.scene.getEnemyParty()[1]!; + game.move.select(Moves.DRAGON_TAIL, 0, BattlerIndex.ENEMY); // target the same pokemon, second move should be redirected after first flees - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); - game.doSelectTarget(BattlerIndex.ENEMY); + game.move.select(Moves.DRAGON_TAIL, 1, BattlerIndex.ENEMY); await game.phaseInterceptor.to(BerryPhase); diff --git a/src/test/moves/dynamax_cannon.test.ts b/src/test/moves/dynamax_cannon.test.ts index 57846c1aef7..6ac0befdb36 100644 --- a/src/test/moves/dynamax_cannon.test.ts +++ b/src/test/moves/dynamax_cannon.test.ts @@ -1,10 +1,10 @@ import { BattlerIndex } from "#app/battle"; import { allMoves } from "#app/data/move"; -import { DamagePhase, MoveEffectPhase, TurnStartPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -27,7 +27,7 @@ describe("Moves - Dynamax Cannon", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.moveset([ dynamaxCannon.id ]); + game.override.moveset([dynamaxCannon.id]); game.override.startingLevel(200); // Note that, for Waves 1-10, the level cap is 10 @@ -36,18 +36,18 @@ describe("Moves - Dynamax Cannon", () => { game.override.disableCrits(); game.override.enemySpecies(Species.MAGIKARP); - game.override.enemyMoveset([ Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH ]); + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); vi.spyOn(dynamaxCannon, "calculateBattlePower"); }); - it("should return 100 power against an enemy below level cap", async() => { + it("should return 100 power against an enemy below level cap", async () => { game.override.enemyLevel(1); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); + game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(dynamaxCannon.id); @@ -55,13 +55,13 @@ describe("Moves - Dynamax Cannon", () => { expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(100); }, 20000); - it("should return 100 power against an enemy at level cap", async() => { + it("should return 100 power against an enemy at level cap", async () => { game.override.enemyLevel(10); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); + game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(dynamaxCannon.id); @@ -69,13 +69,13 @@ describe("Moves - Dynamax Cannon", () => { expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(100); }, 20000); - it("should return 120 power against an enemy 1% above level cap", async() => { + it("should return 120 power against an enemy 1% above level cap", async () => { game.override.enemyLevel(101); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); + game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; @@ -86,13 +86,13 @@ describe("Moves - Dynamax Cannon", () => { expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(120); }, 20000); - it("should return 140 power against an enemy 2% above level capp", async() => { + it("should return 140 power against an enemy 2% above level capp", async () => { game.override.enemyLevel(102); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); + game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; @@ -103,13 +103,13 @@ describe("Moves - Dynamax Cannon", () => { expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(140); }, 20000); - it("should return 160 power against an enemy 3% above level cap", async() => { + it("should return 160 power against an enemy 3% above level cap", async () => { game.override.enemyLevel(103); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); + game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; @@ -120,13 +120,13 @@ describe("Moves - Dynamax Cannon", () => { expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(160); }, 20000); - it("should return 180 power against an enemy 4% above level cap", async() => { + it("should return 180 power against an enemy 4% above level cap", async () => { game.override.enemyLevel(104); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); + game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; @@ -137,13 +137,13 @@ describe("Moves - Dynamax Cannon", () => { expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(180); }, 20000); - it("should return 200 power against an enemy 5% above level cap", async() => { + it("should return 200 power against an enemy 5% above level cap", async () => { game.override.enemyLevel(105); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); + game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; @@ -154,17 +154,14 @@ describe("Moves - Dynamax Cannon", () => { expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); - it("should return 200 power against an enemy way above level cap", async() => { + it("should return 200 power against an enemy way above level cap", async () => { game.override.enemyLevel(999); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); - - await game.phaseInterceptor.to(TurnStartPhase, false); - // Force user to act before enemy - vi.spyOn((game.scene.getCurrentPhase() as TurnStartPhase), "getOrder").mockReturnValue([ BattlerIndex.PLAYER, BattlerIndex. ENEMY]); + game.move.select(dynamaxCannon.id); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(dynamaxCannon.id); diff --git a/src/test/moves/fillet_away.test.ts b/src/test/moves/fillet_away.test.ts index 6965ced46d9..b2ff9e25dba 100644 --- a/src/test/moves/fillet_away.test.ts +++ b/src/test/moves/fillet_away.test.ts @@ -1,12 +1,12 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { TurnEndPhase } from "#app/phases"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { toDmgValue } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { BattleStat } from "#app/data/battle-stat"; +import GameManager from "#test/utils/gameManager"; import { SPLASH_ONLY } from "#test/utils/testUtils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; const TIMEOUT = 20 * 1000; /** HP Cost of Move */ @@ -41,13 +41,13 @@ describe("Moves - FILLET AWAY", () => { //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/fillet_away_(move) test("Fillet Away raises the user's Attack, Special Attack, and Speed by two stages each, at the cost of 1/2 of its maximum HP", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); - game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY)); + game.move.select(Moves.FILLET_AWAY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); @@ -58,17 +58,17 @@ describe("Moves - FILLET AWAY", () => { ); test("Fillet Away will still take effect if one or more of the involved stats are not at max", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); //Here - BattleStat.SPD -> 0 and BattleStat.SPATK -> 3 leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; leadPokemon.summonData.battleStats[BattleStat.SPATK] = 3; - game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY)); + game.move.select(Moves.FILLET_AWAY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); @@ -79,7 +79,7 @@ describe("Moves - FILLET AWAY", () => { ); test("Fillet Away fails if all stats involved are at max", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; @@ -88,7 +88,7 @@ describe("Moves - FILLET AWAY", () => { leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6; leadPokemon.summonData.battleStats[BattleStat.SPD] = 6; - game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY)); + game.move.select(Moves.FILLET_AWAY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); @@ -99,14 +99,14 @@ describe("Moves - FILLET AWAY", () => { ); test("Fillet Away fails if the user's health is less than 1/2", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; - game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY)); + game.move.select(Moves.FILLET_AWAY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(hpLost - PREDAMAGE); diff --git a/src/test/moves/fissure.test.ts b/src/test/moves/fissure.test.ts index 979bc40646c..51122b269b8 100644 --- a/src/test/moves/fissure.test.ts +++ b/src/test/moves/fissure.test.ts @@ -1,14 +1,14 @@ import { BattleStat } from "#app/data/battle-stat"; -import { Species } from "#app/enums/species.js"; +import { Species } from "#app/enums/species"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; -import { DamagePhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Fissure", () => { let phaserGame: Phaser.Game; @@ -56,7 +56,7 @@ describe("Moves - Fissure", () => { game.override.ability(Abilities.NO_GUARD); game.override.enemyAbility(Abilities.FUR_COAT); - game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); + game.move.select(Moves.FISSURE); await game.phaseInterceptor.to(DamagePhase, true); expect(enemyPokemon.isFainted()).toBe(true); @@ -67,7 +67,7 @@ describe("Moves - Fissure", () => { enemyPokemon.summonData.battleStats[BattleStat.ACC] = -6; - game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); + game.move.select(Moves.FISSURE); // wait for TurnEndPhase instead of DamagePhase as fissure might not actually inflict damage await game.phaseInterceptor.to(TurnEndPhase); @@ -80,7 +80,7 @@ describe("Moves - Fissure", () => { enemyPokemon.summonData.battleStats[BattleStat.EVA] = 6; - game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); + game.move.select(Moves.FISSURE); // wait for TurnEndPhase instead of DamagePhase as fissure might not actually inflict damage await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/moves/flame_burst.test.ts b/src/test/moves/flame_burst.test.ts index 0f9e311ca86..2777b8178b8 100644 --- a/src/test/moves/flame_burst.test.ts +++ b/src/test/moves/flame_burst.test.ts @@ -1,13 +1,12 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; -import { SelectTargetPhase, TurnEndPhase } from "#app/phases"; +import { allAbilities } from "#app/data/ability"; +import { Abilities } from "#app/enums/abilities"; +import Pokemon from "#app/field/pokemon"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Abilities } from "#app/enums/abilities.js"; -import { allAbilities } from "#app/data/ability.js"; -import Pokemon from "#app/field/pokemon.js"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Flame Burst", () => { let phaserGame: Phaser.Game; @@ -21,7 +20,7 @@ describe("Moves - Flame Burst", () => { * @returns Effect damage of Flame Burst */ const getEffectDamage = (pokemon: Pokemon): number => { - return Math.max(1, Math.floor(pokemon.getMaxHp() * 1/16)); + return Math.max(1, Math.floor(pokemon.getMaxHp() * 1 / 16)); }; beforeAll(() => { @@ -48,12 +47,10 @@ describe("Moves - Flame Burst", () => { it("inflicts damage to the target's ally equal to 1/16 of its max HP", async () => { await game.startBattle([Species.PIKACHU, Species.PIKACHU]); - const [ leftEnemy, rightEnemy ] = game.scene.getEnemyField(); + const [leftEnemy, rightEnemy] = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAME_BURST)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(leftEnemy.getBattlerIndex()); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.FLAME_BURST, 0, leftEnemy.getBattlerIndex()); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); expect(leftEnemy.hp).toBeLessThan(leftEnemy.getMaxHp()); @@ -64,12 +61,10 @@ describe("Moves - Flame Burst", () => { game.override.enemyAbility(Abilities.FLASH_FIRE); await game.startBattle([Species.PIKACHU, Species.PIKACHU]); - const [ leftEnemy, rightEnemy ] = game.scene.getEnemyField(); + const [leftEnemy, rightEnemy] = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAME_BURST)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(leftEnemy.getBattlerIndex()); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.FLAME_BURST, 0, leftEnemy.getBattlerIndex()); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); expect(leftEnemy.hp).toBe(leftEnemy.getMaxHp()); @@ -78,14 +73,12 @@ describe("Moves - Flame Burst", () => { it("does not interact with the target ally's abilities", async () => { await game.startBattle([Species.PIKACHU, Species.PIKACHU]); - const [ leftEnemy, rightEnemy ] = game.scene.getEnemyField(); + const [leftEnemy, rightEnemy] = game.scene.getEnemyField(); vi.spyOn(rightEnemy, "getAbility").mockReturnValue(allAbilities[Abilities.FLASH_FIRE]); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAME_BURST)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(leftEnemy.getBattlerIndex()); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.FLAME_BURST, 0, leftEnemy.getBattlerIndex()); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); expect(leftEnemy.hp).toBeLessThan(leftEnemy.getMaxHp()); @@ -94,14 +87,12 @@ describe("Moves - Flame Burst", () => { it("effect damage is prevented by Magic Guard", async () => { await game.startBattle([Species.PIKACHU, Species.PIKACHU]); - const [ leftEnemy, rightEnemy ] = game.scene.getEnemyField(); + const [leftEnemy, rightEnemy] = game.scene.getEnemyField(); vi.spyOn(rightEnemy, "getAbility").mockReturnValue(allAbilities[Abilities.MAGIC_GUARD]); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAME_BURST)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(leftEnemy.getBattlerIndex()); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.FLAME_BURST, 0, leftEnemy.getBattlerIndex()); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); expect(leftEnemy.hp).toBeLessThan(leftEnemy.getMaxHp()); diff --git a/src/test/moves/flower_shield.test.ts b/src/test/moves/flower_shield.test.ts index 7ca5fb8bc62..b3e50219aec 100644 --- a/src/test/moves/flower_shield.test.ts +++ b/src/test/moves/flower_shield.test.ts @@ -1,16 +1,15 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { SemiInvulnerableTag } from "#app/data/battler-tags.js"; -import { Type } from "#app/data/type.js"; -import { Biome } from "#app/enums/biome.js"; -import { TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { SemiInvulnerableTag } from "#app/data/battler-tags"; +import { Type } from "#app/data/type"; +import { Biome } from "#app/enums/biome"; +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 { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Flower Shield", () => { let phaserGame: Phaser.Game; @@ -45,7 +44,7 @@ describe("Moves - Flower Shield", () => { expect(magikarp.summonData.battleStats[BattleStat.DEF]).toBe(0); expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLOWER_SHIELD)); + game.move.select(Moves.FLOWER_SHIELD); await game.phaseInterceptor.to(TurnEndPhase); expect(magikarp.summonData.battleStats[BattleStat.DEF]).toBe(0); @@ -64,8 +63,8 @@ describe("Moves - Flower Shield", () => { grassPokemons.forEach(p => expect(p.summonData.battleStats[BattleStat.DEF]).toBe(0)); nonGrassPokemons.forEach(p => expect(p.summonData.battleStats[BattleStat.DEF]).toBe(0)); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLOWER_SHIELD)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.FLOWER_SHIELD); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); grassPokemons.forEach(p => expect(p.summonData.battleStats[BattleStat.DEF]).toBe(1)); @@ -88,7 +87,7 @@ describe("Moves - Flower Shield", () => { expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(0); expect(paras.getTag(SemiInvulnerableTag)).toBeUndefined; - game.doAttack(getMovePosition(game.scene, 0, Moves.FLOWER_SHIELD)); + game.move.select(Moves.FLOWER_SHIELD); await game.phaseInterceptor.to(TurnEndPhase); expect(paras.getTag(SemiInvulnerableTag)).toBeDefined(); @@ -106,7 +105,7 @@ describe("Moves - Flower Shield", () => { expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(0); expect(ally.summonData.battleStats[BattleStat.DEF]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLOWER_SHIELD)); + game.move.select(Moves.FLOWER_SHIELD); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(0); diff --git a/src/test/moves/focus_punch.test.ts b/src/test/moves/focus_punch.test.ts index f5cf85ffae0..99399623a1c 100644 --- a/src/test/moves/focus_punch.test.ts +++ b/src/test/moves/focus_punch.test.ts @@ -1,12 +1,15 @@ -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import { MoveHeaderPhase } from "#app/phases/move-header-phase"; +import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { BerryPhase, MessagePhase, MoveHeaderPhase, SwitchSummonPhase, TurnStartPhase } from "#app/phases"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import { SPLASH_ONLY } from "#test/utils/testUtils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; const TIMEOUT = 20 * 1000; @@ -47,7 +50,7 @@ describe("Moves - Focus Punch", () => { const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.FOCUS_PUNCH)); + game.move.select(Moves.FOCUS_PUNCH); await game.phaseInterceptor.to(MessagePhase); @@ -74,7 +77,7 @@ describe("Moves - Focus Punch", () => { const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.FOCUS_PUNCH)); + game.move.select(Moves.FOCUS_PUNCH); await game.phaseInterceptor.to(MessagePhase); @@ -99,7 +102,7 @@ describe("Moves - Focus Punch", () => { const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.FOCUS_PUNCH)); + game.move.select(Moves.FOCUS_PUNCH); await game.phaseInterceptor.to(MessagePhase); // Header message @@ -121,7 +124,7 @@ describe("Moves - Focus Punch", () => { await game.startBattle([Species.CHARIZARD]); game.forceOpponentToSwitch(); - game.doAttack(getMovePosition(game.scene, 0, Moves.FOCUS_PUNCH)); + game.move.select(Moves.FOCUS_PUNCH); await game.phaseInterceptor.to(TurnStartPhase); diff --git a/src/test/moves/follow_me.test.ts b/src/test/moves/follow_me.test.ts index 420dd7e0762..d7ef199df3e 100644 --- a/src/test/moves/follow_me.test.ts +++ b/src/test/moves/follow_me.test.ts @@ -1,11 +1,10 @@ -import { BattlerIndex } from "#app/battle.js"; +import { BattlerIndex } from "#app/battle"; import { Stat } from "#app/data/pokemon-stat"; -import { Abilities } from "#app/enums/abilities.js"; -import { CommandPhase, SelectTargetPhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { Abilities } from "#app/enums/abilities"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; 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, test } from "vitest"; @@ -32,32 +31,21 @@ describe("Moves - Follow Me", () => { game.override.enemySpecies(Species.SNORLAX); game.override.startingLevel(100); game.override.enemyLevel(100); - game.override.moveset([ Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK ]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); test( "move should redirect enemy attacks to the user", async () => { - await game.startBattle([ Species.AMOONGUSS, Species.CHARIZARD ]); + await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); - - const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const playerStartingHp = playerPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.FOLLOW_ME)); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - - game.doSelectTarget(BattlerIndex.ENEMY); + game.move.select(Moves.FOLLOW_ME); + game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY); await game.phaseInterceptor.to(TurnEndPhase, false); expect(playerPokemon[0].hp).toBeLessThan(playerStartingHp[0]); @@ -68,22 +56,14 @@ describe("Moves - Follow Me", () => { test( "move should redirect enemy attacks to the first ally that uses it", async () => { - await game.startBattle([ Species.AMOONGUSS, Species.CHARIZARD ]); + await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); - - const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const playerStartingHp = playerPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.FOLLOW_ME)); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.FOLLOW_ME)); + game.move.select(Moves.FOLLOW_ME); + game.move.select(Moves.FOLLOW_ME, 1); await game.phaseInterceptor.to(TurnEndPhase, false); playerPokemon.sort((a, b) => a.getBattleStat(Stat.SPD) - b.getBattleStat(Stat.SPD)); @@ -97,29 +77,17 @@ describe("Moves - Follow Me", () => { "move effect should be bypassed by Stalwart", async () => { game.override.ability(Abilities.STALWART); - game.override.moveset([ Moves.QUICK_ATTACK ]); - game.override.enemyMoveset([ Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME ]); + game.override.moveset([Moves.QUICK_ATTACK]); + game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]); - await game.startBattle([ Species.AMOONGUSS, Species.CHARIZARD ]); - - const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); + await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY_2); + game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY); + game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2); await game.phaseInterceptor.to(TurnEndPhase, false); // If redirection was bypassed, both enemies should be damaged @@ -131,29 +99,17 @@ describe("Moves - Follow Me", () => { test( "move effect should be bypassed by Snipe Shot", async () => { - game.override.moveset([ Moves.SNIPE_SHOT ]); - game.override.enemyMoveset([ Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME ]); + game.override.moveset([Moves.SNIPE_SHOT]); + game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]); - await game.startBattle([ Species.AMOONGUSS, Species.CHARIZARD ]); - - const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); + await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.SNIPE_SHOT)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.SNIPE_SHOT)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY_2); + game.move.select(Moves.SNIPE_SHOT, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SNIPE_SHOT, 1, BattlerIndex.ENEMY_2); await game.phaseInterceptor.to(TurnEndPhase, false); // If redirection was bypassed, both enemies should be damaged diff --git a/src/test/moves/foresight.test.ts b/src/test/moves/foresight.test.ts index 3fef1569eba..b856ec0f852 100644 --- a/src/test/moves/foresight.test.ts +++ b/src/test/moves/foresight.test.ts @@ -1,11 +1,10 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import GameManager from "#test/utils/gameManager"; -import { Species } from "#app/enums/species.js"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { SPLASH_ONLY } from "../utils/testUtils"; -import { Moves } from "#app/enums/moves.js"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { MoveEffectPhase } from "#app/phases.js"; describe("Moves - Foresight", () => { let phaserGame: Phaser.Game; @@ -37,19 +36,19 @@ describe("Moves - Foresight", () => { const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + game.move.select(Moves.QUICK_ATTACK); await game.toNextTurn(); expect(enemy.hp).toBe(enemy.getMaxHp()); - game.doAttack(getMovePosition(game.scene, 0, Moves.FORESIGHT)); + game.move.select(Moves.FORESIGHT); await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + game.move.select(Moves.QUICK_ATTACK); await game.toNextTurn(); expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); enemy.hp = enemy.getMaxHp(); - game.doAttack(getMovePosition(game.scene, 0, Moves.MACH_PUNCH)); + game.move.select(Moves.MACH_PUNCH); await game.phaseInterceptor.to(MoveEffectPhase); expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); @@ -62,9 +61,9 @@ describe("Moves - Foresight", () => { const pokemon = game.scene.getPlayerPokemon()!; vi.spyOn(pokemon, "getAccuracyMultiplier"); - game.doAttack(getMovePosition(game.scene, 0, Moves.FORESIGHT)); + game.move.select(Moves.FORESIGHT); await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + game.move.select(Moves.QUICK_ATTACK); await game.phaseInterceptor.to(MoveEffectPhase); expect(pokemon.getAccuracyMultiplier).toHaveReturnedWith(1); diff --git a/src/test/moves/freezy_frost.test.ts b/src/test/moves/freezy_frost.test.ts index 3ccd31bd29e..00d7104d373 100644 --- a/src/test/moves/freezy_frost.test.ts +++ b/src/test/moves/freezy_frost.test.ts @@ -1,14 +1,14 @@ import { BattleStat } from "#app/data/battle-stat"; -import { MoveEndPhase, TurnInitPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { allMoves } from "#app/data/move.js"; describe("Moves - Freezy Frost", () => { describe("integration tests", () => { @@ -46,17 +46,17 @@ describe("Moves - Freezy Frost", () => { expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.SWORDS_DANCE)); + game.move.select(Moves.SWORDS_DANCE); await game.phaseInterceptor.to(TurnInitPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.CHARM)); + game.move.select(Moves.CHARM); await game.phaseInterceptor.to(TurnInitPhase); const userAtkBefore = user.summonData.battleStats[BattleStat.ATK]; const enemyAtkBefore = enemy.summonData.battleStats[BattleStat.ATK]; expect(userAtkBefore).toBe(2); expect(enemyAtkBefore).toBe(-2); - game.doAttack(getMovePosition(game.scene, 0, Moves.FREEZY_FROST)); + game.move.select(Moves.FREEZY_FROST); await game.phaseInterceptor.to(TurnInitPhase); expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0); @@ -68,13 +68,13 @@ describe("Moves - Freezy Frost", () => { const user = game.scene.getPlayerPokemon()!; expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.SWORDS_DANCE)); + game.move.select(Moves.SWORDS_DANCE); await game.phaseInterceptor.to(TurnInitPhase); const userAtkBefore = user.summonData.battleStats[BattleStat.ATK]; expect(userAtkBefore).toBe(2); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(MoveEndPhase); expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); }); diff --git a/src/test/moves/fusion_bolt.test.ts b/src/test/moves/fusion_bolt.test.ts index c7a21e2c736..db31863ad03 100644 --- a/src/test/moves/fusion_bolt.test.ts +++ b/src/test/moves/fusion_bolt.test.ts @@ -1,10 +1,9 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Species } from "#enums/species"; -import { Moves } from "#enums/moves"; 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"; describe("Moves - Fusion Bolt", () => { let phaserGame: Phaser.Game; @@ -24,19 +23,19 @@ describe("Moves - Fusion Bolt", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.moveset([ fusionBolt ]); + game.override.moveset([fusionBolt]); game.override.startingLevel(1); game.override.enemySpecies(Species.RESHIRAM); game.override.enemyAbility(Abilities.ROUGH_SKIN); - game.override.enemyMoveset([ Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH ]); + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); game.override.battleType("single"); game.override.startingWave(97); game.override.disableCrits(); }); - it("should not make contact", async() => { + it("should not make contact", async () => { await game.startBattle([ Species.ZEKROM, ]); @@ -44,7 +43,7 @@ describe("Moves - Fusion Bolt", () => { const partyMember = game.scene.getPlayerPokemon()!; const initialHp = partyMember.hp; - game.doAttack(getMovePosition(game.scene, 0, fusionBolt)); + game.move.select(fusionBolt); await game.toNextTurn(); diff --git a/src/test/moves/fusion_flare.test.ts b/src/test/moves/fusion_flare.test.ts index 9ae42e7977f..471f6a2ac7b 100644 --- a/src/test/moves/fusion_flare.test.ts +++ b/src/test/moves/fusion_flare.test.ts @@ -1,11 +1,10 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { TurnStartPhase } from "#app/phases"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; import { StatusEffect } from "#app/data/status-effect"; -import { Species } from "#enums/species"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; 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"; describe("Moves - Fusion Flare", () => { let phaserGame: Phaser.Game; @@ -25,25 +24,25 @@ describe("Moves - Fusion Flare", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.moveset([ fusionFlare ]); + game.override.moveset([fusionFlare]); game.override.startingLevel(1); game.override.enemySpecies(Species.RESHIRAM); - game.override.enemyMoveset([ Moves.REST, Moves.REST, Moves.REST, Moves.REST ]); + game.override.enemyMoveset([Moves.REST, Moves.REST, Moves.REST, Moves.REST]); game.override.battleType("single"); game.override.startingWave(97); game.override.disableCrits(); }); - it("should thaw freeze status condition", async() => { + it("should thaw freeze status condition", async () => { await game.startBattle([ Species.RESHIRAM, ]); const partyMember = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, fusionFlare)); + game.move.select(fusionFlare); await game.phaseInterceptor.to(TurnStartPhase, false); diff --git a/src/test/moves/fusion_flare_bolt.test.ts b/src/test/moves/fusion_flare_bolt.test.ts index c2214d5442b..ebef5148778 100644 --- a/src/test/moves/fusion_flare_bolt.test.ts +++ b/src/test/moves/fusion_flare_bolt.test.ts @@ -1,13 +1,15 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { MoveEffectPhase, MovePhase, MoveEndPhase, DamagePhase } from "#app/phases"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Stat } from "#app/data/pokemon-stat"; -import { allMoves } from "#app/data/move"; import { BattlerIndex } from "#app/battle"; -import { Species } from "#enums/species"; +import { allMoves } from "#app/data/move"; +import { Stat } from "#app/data/pokemon-stat"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { MovePhase } from "#app/phases/move-phase"; import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Fusion Flare and Fusion Bolt", () => { let phaserGame: Phaser.Game; @@ -28,11 +30,11 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.moveset([ fusionFlare.id, fusionBolt.id ]); + game.override.moveset([fusionFlare.id, fusionBolt.id]); game.override.startingLevel(1); game.override.enemySpecies(Species.RESHIRAM); - game.override.enemyMoveset([ Moves.REST, Moves.REST, Moves.REST, Moves.REST ]); + game.override.enemyMoveset([Moves.REST, Moves.REST, Moves.REST, Moves.REST]); game.override.battleType("double"); game.override.startingWave(97); @@ -42,20 +44,17 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { vi.spyOn(fusionBolt, "calculateBattlePower"); }); - it("FUSION_FLARE should double power of subsequent FUSION_BOLT", async() => { + it("FUSION_FLARE should double power of subsequent FUSION_BOLT", async () => { await game.startBattle([ Species.ZEKROM, Species.ZEKROM ]); - game.doAttack(getMovePosition(game.scene, 0, fusionFlare.id)); - game.doSelectTarget(BattlerIndex.ENEMY); - - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.ENEMY); + game.move.select(fusionFlare.id, 0, BattlerIndex.ENEMY); + game.move.select(fusionBolt.id, 1, BattlerIndex.ENEMY); // Force user party to act before enemy party - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id); @@ -68,20 +67,17 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); - it("FUSION_BOLT should double power of subsequent FUSION_FLARE", async() => { + it("FUSION_BOLT should double power of subsequent FUSION_FLARE", async () => { await game.startBattle([ Species.ZEKROM, Species.ZEKROM ]); - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.ENEMY); - - game.doAttack(getMovePosition(game.scene, 0, fusionFlare.id)); - game.doSelectTarget(BattlerIndex.ENEMY); + game.move.select(fusionBolt.id, 0, BattlerIndex.ENEMY); + game.move.select(fusionFlare.id, 1, BattlerIndex.ENEMY); // Force user party to act before enemy party - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); @@ -94,20 +90,17 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); - it("FUSION_FLARE should double power of subsequent FUSION_BOLT if a move failed in between", async() => { + it("FUSION_FLARE should double power of subsequent FUSION_BOLT if a move failed in between", async () => { await game.startBattle([ Species.ZEKROM, Species.ZEKROM ]); - game.doAttack(getMovePosition(game.scene, 0, fusionFlare.id)); - game.doSelectTarget(0); - - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(0); + game.move.select(fusionFlare.id, 0, BattlerIndex.PLAYER); + game.move.select(fusionBolt.id, 1, BattlerIndex.PLAYER); // Force first enemy to act (and fail) in between party - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id); @@ -125,21 +118,18 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); - it("FUSION_FLARE should not double power of subsequent FUSION_BOLT if a move succeeded in between", async() => { - game.override.enemyMoveset([ Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH ]); + it("FUSION_FLARE should not double power of subsequent FUSION_BOLT if a move succeeded in between", async () => { + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); await game.startBattle([ Species.ZEKROM, Species.ZEKROM ]); - game.doAttack(getMovePosition(game.scene, 0, fusionFlare.id)); - game.doSelectTarget(BattlerIndex.ENEMY); - - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.ENEMY); + game.move.select(fusionFlare.id, 0, BattlerIndex.ENEMY); + game.move.select(fusionBolt.id, 1, BattlerIndex.ENEMY); // Force first enemy to act in between party - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id); @@ -156,20 +146,17 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100); }, 20000); - it("FUSION_FLARE should double power of subsequent FUSION_BOLT if moves are aimed at allies", async() => { + it("FUSION_FLARE should double power of subsequent FUSION_BOLT if moves are aimed at allies", async () => { await game.startBattle([ Species.ZEKROM, Species.RESHIRAM ]); - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.PLAYER_2); - - game.doAttack(getMovePosition(game.scene, 0, fusionFlare.id)); - game.doSelectTarget(BattlerIndex.PLAYER); + game.move.select(fusionBolt.id, 0, BattlerIndex.PLAYER_2); + game.move.select(fusionFlare.id, 1, BattlerIndex.PLAYER); // Force user party to act before enemy party - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); @@ -182,8 +169,8 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); - it("FUSION_FLARE and FUSION_BOLT alternating throughout turn should double power of subsequent moves", async() => { - game.override.enemyMoveset([ fusionFlare.id, fusionFlare.id, fusionFlare.id, fusionFlare.id ]); + it("FUSION_FLARE and FUSION_BOLT alternating throughout turn should double power of subsequent moves", async () => { + game.override.enemyMoveset([fusionFlare.id, fusionFlare.id, fusionFlare.id, fusionFlare.id]); await game.startBattle([ Species.ZEKROM, Species.ZEKROM @@ -214,14 +201,11 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { vi.spyOn(party[1], "stats", "get").mockReturnValue(stats.player[0].map((val, i) => (i === Stat.SPDEF ? 250 : val))); vi.spyOn(party[1], "stats", "get").mockReturnValue(stats.player[1].map((val, i) => (i === Stat.SPDEF ? 250 : val))); - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.ENEMY); - - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.ENEMY); + game.move.select(fusionBolt.id, 0, BattlerIndex.ENEMY); + game.move.select(fusionBolt.id, 1, BattlerIndex.ENEMY); // Force first enemy to act in between party - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); @@ -244,8 +228,8 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); - it("FUSION_FLARE and FUSION_BOLT alternating throughout turn should double power of subsequent moves if moves are aimed at allies", async() => { - game.override.enemyMoveset([ fusionFlare.id, fusionFlare.id, fusionFlare.id, fusionFlare.id ]); + it("FUSION_FLARE and FUSION_BOLT alternating throughout turn should double power of subsequent moves if moves are aimed at allies", async () => { + game.override.enemyMoveset([fusionFlare.id, fusionFlare.id, fusionFlare.id, fusionFlare.id]); await game.startBattle([ Species.ZEKROM, Species.ZEKROM @@ -276,14 +260,11 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { vi.spyOn(party[1], "stats", "get").mockReturnValue(stats.player[0].map((val, i) => (i === Stat.SPDEF ? 250 : val))); vi.spyOn(party[1], "stats", "get").mockReturnValue(stats.player[1].map((val, i) => (i === Stat.SPDEF ? 250 : val))); - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.PLAYER_2); - - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.PLAYER); + game.move.select(fusionBolt.id, 0, BattlerIndex.PLAYER_2); + game.move.select(fusionBolt.id, 1, BattlerIndex.PLAYER); // Force first enemy to act in between party - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); diff --git a/src/test/moves/gastro_acid.test.ts b/src/test/moves/gastro_acid.test.ts index cc247890754..67fd3464cf9 100644 --- a/src/test/moves/gastro_acid.test.ts +++ b/src/test/moves/gastro_acid.test.ts @@ -1,12 +1,11 @@ -import { BattlerIndex } from "#app/battle.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; -import { MoveResult } from "#app/field/pokemon.js"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { MoveResult } from "#app/field/pokemon"; import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; import { SPLASH_ONLY } from "#test/utils/testUtils"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; const TIMEOUT = 20 * 1000; @@ -46,10 +45,8 @@ describe("Moves - Gastro Acid", () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.GASTRO_ACID)); - game.doSelectTarget(BattlerIndex.ENEMY); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doSelectTarget(BattlerIndex.PLAYER_2); + game.move.select(Moves.GASTRO_ACID, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to("TurnInitPhase"); @@ -57,10 +54,8 @@ describe("Moves - Gastro Acid", () => { expect(enemyField[0].summonData.abilitySuppressed).toBe(true); expect(enemyField[1].summonData.abilitySuppressed).toBe(false); - game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_GUN)); - game.doSelectTarget(BattlerIndex.ENEMY); - game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_GUN)); - game.doSelectTarget(BattlerIndex.ENEMY_2); + game.move.select(Moves.WATER_GUN, 0, BattlerIndex.ENEMY); + game.move.select(Moves.WATER_GUN, 1, BattlerIndex.ENEMY_2); await game.phaseInterceptor.to("TurnEndPhase"); @@ -73,13 +68,13 @@ describe("Moves - Gastro Acid", () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CORE_ENFORCER)); + game.move.select(Moves.CORE_ENFORCER); // Force player to be slower to enable Core Enforcer to proc its suppression effect await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to("TurnInitPhase"); - game.doAttack(getMovePosition(game.scene, 0, Moves.GASTRO_ACID)); + game.move.select(Moves.GASTRO_ACID); await game.phaseInterceptor.to("TurnInitPhase"); diff --git a/src/test/moves/glaive_rush.test.ts b/src/test/moves/glaive_rush.test.ts index b9c9d2199d3..1eac3c32bb4 100644 --- a/src/test/moves/glaive_rush.test.ts +++ b/src/test/moves/glaive_rush.test.ts @@ -1,10 +1,10 @@ -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { DamagePhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -36,36 +36,36 @@ describe("Moves - Glaive Rush", () => { game.override.moveset([Moves.SHADOW_SNEAK, Moves.AVALANCHE, Moves.SPLASH, Moves.GLAIVE_RUSH]); }); - it("takes double damage from attacks", async() => { + it("takes double damage from attacks", async () => { await game.startBattle(); const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; vi.spyOn(game.scene, "randBattleSeedInt").mockReturnValue(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.SHADOW_SNEAK)); + game.move.select(Moves.SHADOW_SNEAK); await game.phaseInterceptor.to(DamagePhase); const damageDealt = 1000 - enemy.hp; await game.phaseInterceptor.to(TurnEndPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.SHADOW_SNEAK)); + game.move.select(Moves.SHADOW_SNEAK); await game.phaseInterceptor.to(DamagePhase); expect(enemy.hp).toBeLessThanOrEqual(1001 - (damageDealt * 3)); }, 5000); // TODO: revert back to 20s - it("always gets hit by attacks", async() => { + it("always gets hit by attacks", async () => { await game.startBattle(); const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; allMoves[Moves.AVALANCHE].accuracy = 0; - game.doAttack(getMovePosition(game.scene, 0, Moves.AVALANCHE)); + game.move.select(Moves.AVALANCHE); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBeLessThan(1000); }, 20000); - it("interacts properly with multi-lens", async() => { - game.override.startingHeldItems([{name: "MULTI_LENS", count: 2}]); + it("interacts properly with multi-lens", async () => { + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 2 }]); game.override.enemyMoveset(Array(4).fill(Moves.AVALANCHE)); await game.startBattle(); const player = game.scene.getPlayerPokemon()!; @@ -74,17 +74,17 @@ describe("Moves - Glaive Rush", () => { player.hp = 1000; allMoves[Moves.AVALANCHE].accuracy = 0; - game.doAttack(getMovePosition(game.scene, 0, Moves.GLAIVE_RUSH)); + game.move.select(Moves.GLAIVE_RUSH); await game.phaseInterceptor.to(TurnEndPhase); expect(player.hp).toBeLessThan(1000); player.hp = 1000; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(player.hp).toBe(1000); }, 20000); - it("secondary effects only last until next move", async() => { + it("secondary effects only last until next move", async () => { game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK)); await game.startBattle(); const player = game.scene.getPlayerPokemon()!; @@ -93,22 +93,22 @@ describe("Moves - Glaive Rush", () => { player.hp = 1000; allMoves[Moves.SHADOW_SNEAK].accuracy = 0; - game.doAttack(getMovePosition(game.scene, 0, Moves.GLAIVE_RUSH)); + game.move.select(Moves.GLAIVE_RUSH); await game.phaseInterceptor.to(TurnEndPhase); expect(player.hp).toBe(1000); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); const damagedHp = player.hp; expect(player.hp).toBeLessThan(1000); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(player.hp).toBe(damagedHp); }, 20000); - it("secondary effects are removed upon switching", async() => { + it("secondary effects are removed upon switching", async () => { game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK)); game.override.starterSpecies(0); await game.startBattle([Species.KLINK, Species.FEEBAS]); @@ -117,7 +117,7 @@ describe("Moves - Glaive Rush", () => { enemy.hp = 1000; allMoves[Moves.SHADOW_SNEAK].accuracy = 0; - game.doAttack(getMovePosition(game.scene, 0, Moves.GLAIVE_RUSH)); + game.move.select(Moves.GLAIVE_RUSH); await game.phaseInterceptor.to(TurnEndPhase); expect(player.hp).toBe(player.getMaxHp()); @@ -129,7 +129,7 @@ describe("Moves - Glaive Rush", () => { }, 20000); - it("secondary effects don't activate if move fails", async() => { + it("secondary effects don't activate if move fails", async () => { game.override.moveset([Moves.SHADOW_SNEAK, Moves.PROTECT, Moves.SPLASH, Moves.GLAIVE_RUSH]); await game.startBattle(); const player = game.scene.getPlayerPokemon()!; @@ -137,16 +137,16 @@ describe("Moves - Glaive Rush", () => { enemy.hp = 1000; player.hp = 1000; - game.doAttack(getMovePosition(game.scene, 0, Moves.PROTECT)); + game.move.select(Moves.PROTECT); await game.phaseInterceptor.to(TurnEndPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.SHADOW_SNEAK)); + game.move.select(Moves.SHADOW_SNEAK); await game.phaseInterceptor.to(TurnEndPhase); game.override.enemyMoveset(Array(4).fill(Moves.SPLASH)); const damagedHP1 = 1000 - enemy.hp; enemy.hp = 1000; - game.doAttack(getMovePosition(game.scene, 0, Moves.SHADOW_SNEAK)); + game.move.select(Moves.SHADOW_SNEAK); await game.phaseInterceptor.to(TurnEndPhase); const damagedHP2 = 1000 - enemy.hp; diff --git a/src/test/moves/growth.test.ts b/src/test/moves/growth.test.ts index bfa3cc54896..dfbf5406351 100644 --- a/src/test/moves/growth.test.ts +++ b/src/test/moves/growth.test.ts @@ -1,13 +1,11 @@ import { BattleStat } from "#app/data/battle-stat"; import { Stat } from "#app/data/pokemon-stat"; -import { CommandPhase, EnemyCommandPhase, TurnInitPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; 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"; @@ -35,10 +33,10 @@ describe("Moves - Growth", () => { game.override.ability(Abilities.INSOMNIA); game.override.startingLevel(2000); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it("GROWTH", async() => { + it("GROWTH", async () => { const moveToUse = Moves.GROWTH; await game.startBattle([ Species.MIGHTYENA, @@ -50,13 +48,7 @@ describe("Moves - Growth", () => { const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.SPATK]).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.SPATK]).toBe(1); diff --git a/src/test/moves/hard_press.test.ts b/src/test/moves/hard_press.test.ts index baf63a1ad23..70c78490269 100644 --- a/src/test/moves/hard_press.test.ts +++ b/src/test/moves/hard_press.test.ts @@ -1,13 +1,12 @@ -import { allMoves } from "#app/data/move.js"; -import { MoveEffectPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Hard Press", () => { let phaserGame: Phaser.Game; @@ -39,7 +38,7 @@ describe("Moves - Hard Press", () => { it("should return 100 power if target HP ratio is at 100%", async () => { await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.HARD_PRESS)); + game.move.select(Moves.HARD_PRESS); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(100); @@ -52,7 +51,7 @@ describe("Moves - Hard Press", () => { vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); - game.doAttack(getMovePosition(game.scene, 0, Moves.HARD_PRESS)); + game.move.select(Moves.HARD_PRESS); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(50); @@ -65,7 +64,7 @@ describe("Moves - Hard Press", () => { vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); - game.doAttack(getMovePosition(game.scene, 0, Moves.HARD_PRESS)); + game.move.select(Moves.HARD_PRESS); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(1); @@ -78,7 +77,7 @@ describe("Moves - Hard Press", () => { vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); - game.doAttack(getMovePosition(game.scene, 0, Moves.HARD_PRESS)); + game.move.select(Moves.HARD_PRESS); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(1); diff --git a/src/test/moves/haze.test.ts b/src/test/moves/haze.test.ts index 092575b8000..8a32a40cb32 100644 --- a/src/test/moves/haze.test.ts +++ b/src/test/moves/haze.test.ts @@ -1,13 +1,13 @@ import { BattleStat } from "#app/data/battle-stat"; -import { MoveEndPhase, TurnInitPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Haze", () => { describe("integration tests", () => { @@ -44,17 +44,17 @@ describe("Moves - Haze", () => { expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.SWORDS_DANCE)); + game.move.select(Moves.SWORDS_DANCE); await game.phaseInterceptor.to(TurnInitPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.CHARM)); + game.move.select(Moves.CHARM); await game.phaseInterceptor.to(TurnInitPhase); const userAtkBefore = user.summonData.battleStats[BattleStat.ATK]; const enemyAtkBefore = enemy.summonData.battleStats[BattleStat.ATK]; expect(userAtkBefore).toBe(2); expect(enemyAtkBefore).toBe(-2); - game.doAttack(getMovePosition(game.scene, 0, Moves.HAZE)); + game.move.select(Moves.HAZE); await game.phaseInterceptor.to(TurnInitPhase); expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0); @@ -66,13 +66,13 @@ describe("Moves - Haze", () => { const user = game.scene.getPlayerPokemon()!; expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.SWORDS_DANCE)); + game.move.select(Moves.SWORDS_DANCE); await game.phaseInterceptor.to(TurnInitPhase); const userAtkBefore = user.summonData.battleStats[BattleStat.ATK]; expect(userAtkBefore).toBe(2); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(MoveEndPhase); expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); }); diff --git a/src/test/moves/hyper_beam.test.ts b/src/test/moves/hyper_beam.test.ts index f33ce4f5478..1280d8b429a 100644 --- a/src/test/moves/hyper_beam.test.ts +++ b/src/test/moves/hyper_beam.test.ts @@ -1,13 +1,13 @@ -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; -import { BerryPhase, TurnEndPhase } from "#app/phases.js"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; const TIMEOUT = 20 * 1000; // 20 sec timeout for all tests @@ -47,7 +47,7 @@ describe("Moves - Hyper Beam", () => { const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.HYPER_BEAM)); + game.move.select(Moves.HYPER_BEAM); await game.phaseInterceptor.to(TurnEndPhase); @@ -62,7 +62,7 @@ describe("Moves - Hyper Beam", () => { expect(enemyPokemon.hp).toBe(enemyPostAttackHp); expect(leadPokemon.getTag(BattlerTagType.RECHARGING)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/jaw_lock.test.ts b/src/test/moves/jaw_lock.test.ts new file mode 100644 index 00000000000..42f7a244977 --- /dev/null +++ b/src/test/moves/jaw_lock.test.ts @@ -0,0 +1,171 @@ +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { FaintPhase } from "#app/phases/faint-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import GameManager from "#app/test/utils/gameManager"; +import { SPLASH_ONLY } from "#app/test/utils/testUtils"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Jaw Lock", () => { + 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") + .enemySpecies(Species.SNORLAX) + .enemyAbility(Abilities.INSOMNIA) + .enemyMoveset(SPLASH_ONLY) + .moveset([Moves.JAW_LOCK, Moves.SPLASH]) + .startingLevel(100) + .enemyLevel(100) + .disableCrits(); + }); + + it( + "should trap the move's user and target", + async () => { + await game.startBattle([Species.BULBASAUR]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.JAW_LOCK); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + + await game.phaseInterceptor.to(MoveEffectPhase, false); + + expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); + }, TIMEOUT + ); + + it( + "should not trap either pokemon if the target faints", + async () => { + game.override.enemyLevel(1); + await game.startBattle([Species.BULBASAUR]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.JAW_LOCK); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + + await game.phaseInterceptor.to(MoveEffectPhase, false); + + expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + + await game.phaseInterceptor.to(MoveEffectPhase); + + expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + + await game.phaseInterceptor.to(FaintPhase); + + expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + }, TIMEOUT + ); + + it( + "should only trap the user until the target faints", + async () => { + await game.startBattle([Species.BULBASAUR]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.JAW_LOCK); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + + await game.phaseInterceptor.to(MoveEffectPhase); + + expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); + + await game.phaseInterceptor.to(TurnEndPhase); + + await game.doKillOpponents(); + + expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + }, TIMEOUT + ); + + it( + "should not trap other targets after the first target is trapped", + async () => { + game.override.battleType("double"); + + await game.startBattle([Species.CHARMANDER, Species.BULBASAUR]); + + const playerPokemon = game.scene.getPlayerField(); + const enemyPokemon = game.scene.getEnemyField(); + + game.move.select(Moves.JAW_LOCK, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, 1); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); + + await game.phaseInterceptor.to(MoveEffectPhase); + + expect(playerPokemon[0].getTag(BattlerTagType.TRAPPED)).toBeDefined(); + expect(enemyPokemon[0].getTag(BattlerTagType.TRAPPED)).toBeDefined(); + + await game.toNextTurn(); + + game.move.select(Moves.JAW_LOCK, 0, BattlerIndex.ENEMY_2); + game.move.select(Moves.SPLASH, 1); + + await game.phaseInterceptor.to(MoveEffectPhase); + + expect(enemyPokemon[1].getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + expect(playerPokemon[0].getTag(BattlerTagType.TRAPPED)).toBeDefined(); + expect(playerPokemon[0].getTag(BattlerTagType.TRAPPED)?.sourceId).toBe(enemyPokemon[0].id); + }, TIMEOUT + ); + + it( + "should not trap either pokemon if the target is protected", + async () => { + game.override.enemyMoveset(Array(4).fill(Moves.PROTECT)); + + await game.startBattle([Species.BULBASAUR]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.JAW_LOCK); + + await game.phaseInterceptor.to(BerryPhase, false); + + expect(playerPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + }, TIMEOUT + ); +}); diff --git a/src/test/moves/light_screen.test.ts b/src/test/moves/light_screen.test.ts index 9de1f8c492b..e94dc4a299e 100644 --- a/src/test/moves/light_screen.test.ts +++ b/src/test/moves/light_screen.test.ts @@ -1,14 +1,13 @@ -import { ArenaTagSide } from "#app/data/arena-tag.js"; -import Move, { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import Pokemon from "#app/field/pokemon.js"; -import { TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { NumberHolder } from "#app/utils.js"; +import { ArenaTagSide } from "#app/data/arena-tag"; +import Move, { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import Pokemon from "#app/field/pokemon"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { NumberHolder } from "#app/utils"; 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"; @@ -17,7 +16,7 @@ describe("Moves - Light Screen", () => { let phaserGame: Phaser.Game; let game: GameManager; const singleBattleMultiplier = 0.5; - const doubleBattleMultiplier = 2732/4096; + const doubleBattleMultiplier = 2732 / 4096; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -40,11 +39,11 @@ describe("Moves - Light Screen", () => { game.override.disableCrits(); }); - it("reduces damage of special attacks by half in a single battle", async() => { + it("reduces damage of special attacks by half in a single battle", async () => { const moveToUse = Moves.ABSORB; await game.startBattle([Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); @@ -53,14 +52,14 @@ describe("Moves - Light Screen", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); - it("reduces damage of special attacks by a third in a double battle", async() => { + it("reduces damage of special attacks by a third in a double battle", async () => { game.override.battleType("double"); const moveToUse = Moves.DAZZLING_GLEAM; await game.startBattle([Species.SHUCKLE, Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); - game.doAttack(getMovePosition(game.scene, 1, moveToUse)); + game.move.select(moveToUse); + game.move.select(moveToUse, 1); await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); @@ -68,11 +67,11 @@ describe("Moves - Light Screen", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); - it("does not affect physical attacks", async() => { + it("does not affect physical attacks", async () => { const moveToUse = Moves.TACKLE; await game.startBattle([Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); diff --git a/src/test/moves/lucky_chant.test.ts b/src/test/moves/lucky_chant.test.ts index 1232ce9ffc3..7d5bfe02476 100644 --- a/src/test/moves/lucky_chant.test.ts +++ b/src/test/moves/lucky_chant.test.ts @@ -1,11 +1,11 @@ +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "../utils/gameManager"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { BerryPhase, TurnEndPhase } from "#app/phases.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; const TIMEOUT = 20 * 1000; @@ -43,13 +43,13 @@ describe("Moves - Lucky Chant", () => { const playerPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); const firstTurnDamage = playerPokemon.getMaxHp() - playerPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.LUCKY_CHANT)); + game.move.select(Moves.LUCKY_CHANT); await game.phaseInterceptor.to(BerryPhase, false); @@ -67,15 +67,15 @@ describe("Moves - Lucky Chant", () => { const playerPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.FOLLOW_ME)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.FOLLOW_ME); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); const firstTurnDamage = playerPokemon[0].getMaxHp() - playerPokemon[0].hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.FOLLOW_ME)); - game.doAttack(getMovePosition(game.scene, 1, Moves.LUCKY_CHANT)); + game.move.select(Moves.FOLLOW_ME); + game.move.select(Moves.LUCKY_CHANT, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -96,13 +96,13 @@ describe("Moves - Lucky Chant", () => { enemyPokemon.addTag(BattlerTagType.ALWAYS_CRIT, 2, Moves.NONE, 0); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); const firstTurnDamage = playerPokemon.getMaxHp() - playerPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.LUCKY_CHANT)); + game.move.select(Moves.LUCKY_CHANT); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/magnet_rise.test.ts b/src/test/moves/magnet_rise.test.ts index 9b3c6c457e2..9037e377090 100644 --- a/src/test/moves/magnet_rise.test.ts +++ b/src/test/moves/magnet_rise.test.ts @@ -1,13 +1,15 @@ -import { CommandPhase, TurnEndPhase } from "#app/phases.js"; -import GameManager from "#test/utils/gameManager"; +import { CommandPhase } from "#app/phases/command-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; 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"; describe("Moves - Magnet Rise", () => { let phaserGame: Phaser.Game; let game: GameManager; + const moveToUse = Moves.MAGNET_RISE; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -21,7 +23,6 @@ describe("Moves - Magnet Rise", () => { beforeEach(() => { game = new GameManager(phaserGame); - const moveToUse = Moves.MAGNET_RISE; game.override.battleType("single"); game.override.starterSpecies(Species.MAGNEZONE); game.override.enemySpecies(Species.RATTATA); @@ -35,7 +36,7 @@ describe("Moves - Magnet Rise", () => { await game.startBattle(); const startingHp = game.scene.getParty()[0].hp; - game.doAttack(0); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); const finalHp = game.scene.getParty()[0].hp; const hpLost = finalHp - startingHp; @@ -46,12 +47,12 @@ describe("Moves - Magnet Rise", () => { await game.startBattle(); const startingHp = game.scene.getParty()[0].hp; - game.doAttack(0); + game.move.select(moveToUse); await game.phaseInterceptor.to(CommandPhase); let finalHp = game.scene.getParty()[0].hp; let hpLost = finalHp - startingHp; expect(hpLost).toBe(0); - game.doAttack(2); + game.move.select(Moves.GRAVITY); await game.phaseInterceptor.to(TurnEndPhase); finalHp = game.scene.getParty()[0].hp; hpLost = finalHp - startingHp; diff --git a/src/test/moves/make_it_rain.test.ts b/src/test/moves/make_it_rain.test.ts index a4440401c4b..0af7763f175 100644 --- a/src/test/moves/make_it_rain.test.ts +++ b/src/test/moves/make_it_rain.test.ts @@ -1,13 +1,13 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { MoveEndPhase, StatChangePhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { StatChangePhase } from "#app/phases/stat-change-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -41,8 +41,8 @@ describe("Moves - Make It Rain", () => { const playerPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.MAKE_IT_RAIN); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEndPhase); @@ -58,7 +58,7 @@ describe("Moves - Make It Rain", () => { const playerPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); + game.move.select(Moves.MAKE_IT_RAIN); await game.phaseInterceptor.to(StatChangePhase); @@ -74,8 +74,8 @@ describe("Moves - Make It Rain", () => { const playerPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.MAKE_IT_RAIN); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(StatChangePhase); @@ -88,8 +88,8 @@ describe("Moves - Make It Rain", () => { const playerPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.MAKE_IT_RAIN); + game.move.select(Moves.SPLASH, 1); // Make Make It Rain miss the first target await game.move.forceMiss(true); diff --git a/src/test/moves/mat_block.test.ts b/src/test/moves/mat_block.test.ts index 3a4d23d1497..29a97806242 100644 --- a/src/test/moves/mat_block.test.ts +++ b/src/test/moves/mat_block.test.ts @@ -1,12 +1,13 @@ +import { BattleStat } from "#app/data/battle-stat"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-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 Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "../utils/gameManager"; -import { Species } from "#enums/species"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BerryPhase, CommandPhase, TurnEndPhase } from "#app/phases.js"; -import { BattleStat } from "#app/data/battle-stat.js"; const TIMEOUT = 20 * 1000; @@ -46,11 +47,11 @@ describe("Moves - Mat Block", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.MAT_BLOCK)); + game.move.select(Moves.MAT_BLOCK); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -67,11 +68,11 @@ describe("Moves - Mat Block", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.MAT_BLOCK)); + game.move.select(Moves.MAT_BLOCK); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -86,18 +87,18 @@ describe("Moves - Mat Block", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); const leadStartingHp = leadPokemon.map(p => p.hp); await game.phaseInterceptor.to(CommandPhase, false); - game.doAttack(getMovePosition(game.scene, 0, Moves.MAT_BLOCK)); + game.move.select(Moves.MAT_BLOCK); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.MAT_BLOCK)); + game.move.select(Moves.MAT_BLOCK, 1); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/miracle_eye.test.ts b/src/test/moves/miracle_eye.test.ts index 45de8b7e4fb..f47e4ce0c16 100644 --- a/src/test/moves/miracle_eye.test.ts +++ b/src/test/moves/miracle_eye.test.ts @@ -1,12 +1,11 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; +import { BattlerIndex } from "#app/battle"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import GameManager from "#test/utils/gameManager"; -import { Species } from "#app/enums/species.js"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { SPLASH_ONLY } from "../utils/testUtils"; -import { Moves } from "#app/enums/moves.js"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { MoveEffectPhase } from "#app/phases.js"; -import { BattlerIndex } from "#app/battle.js"; describe("Moves - Miracle Eye", () => { let phaserGame: Phaser.Game; @@ -38,14 +37,14 @@ describe("Moves - Miracle Eye", () => { const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.CONFUSION)); + game.move.select(Moves.CONFUSION); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.toNextTurn(); expect(enemy.hp).toBe(enemy.getMaxHp()); - game.doAttack(getMovePosition(game.scene, 0, Moves.MIRACLE_EYE)); + game.move.select(Moves.MIRACLE_EYE); await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CONFUSION)); + game.move.select(Moves.CONFUSION); await game.phaseInterceptor.to(MoveEffectPhase); expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); diff --git a/src/test/moves/multi_target.test.ts b/src/test/moves/multi_target.test.ts index 4cb2dfb764d..b8c1f67b3df 100644 --- a/src/test/moves/multi_target.test.ts +++ b/src/test/moves/multi_target.test.ts @@ -1,13 +1,12 @@ -import { getMoveTargets } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { Species } from "#app/enums/species.js"; -import { TurnEndPhase } from "#app/phases.js"; -import GameManager from "#test/utils/gameManager"; +import { getMoveTargets } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { Species } from "#app/enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -95,8 +94,8 @@ async function checkDamageDecrease(game: GameManager, attackMove: Moves, killAll game.scene.getEnemyField()[1].abilityIndex = ability; } - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); @@ -105,9 +104,9 @@ async function checkDamageDecrease(game: GameManager, attackMove: Moves, killAll await game.toNextTurn(); const initialHp = game.scene.getEnemyField()[0].hp; - game.doAttack(getMovePosition(game.scene, 0, attackMove)); + game.move.select(attackMove); if (!killAlly) { - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); } await game.phaseInterceptor.to(TurnEndPhase); @@ -119,7 +118,7 @@ async function checkDamageDecrease(game: GameManager, attackMove: Moves, killAll game.scene.getEnemyField()[0].hp = initialHp; const initialHp1v1 = game.scene.getEnemyField()[0].hp; - game.doAttack(getMovePosition(game.scene, 0, attackMove)); + game.move.select(attackMove); await game.phaseInterceptor.to(TurnEndPhase); const afterHp1v1 = game.scene.getEnemyField()[0].hp; diff --git a/src/test/moves/octolock.test.ts b/src/test/moves/octolock.test.ts index 8988109f431..389e4a4c4cf 100644 --- a/src/test/moves/octolock.test.ts +++ b/src/test/moves/octolock.test.ts @@ -1,14 +1,15 @@ import { BattleStat } from "#app/data/battle-stat"; -import { TrappedTag } from "#app/data/battler-tags.js"; -import { CommandPhase, MoveEndPhase, TurnInitPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { TrappedTag } from "#app/data/battler-tags"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Octolock", () => { describe("integration tests", () => { @@ -45,7 +46,7 @@ describe("Moves - Octolock", () => { const enemyPokemon = game.scene.getEnemyField(); // use Octolock and advance to init phase of next turn to check for stat changes - game.doAttack(getMovePosition(game.scene, 0, Moves.OCTOLOCK)); + game.move.select(Moves.OCTOLOCK); await game.phaseInterceptor.to(TurnInitPhase); expect(enemyPokemon[0].summonData.battleStats[BattleStat.DEF]).toBe(-1); @@ -53,7 +54,7 @@ describe("Moves - Octolock", () => { // take a second turn to make sure stat changes occur again await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnInitPhase); expect(enemyPokemon[0].summonData.battleStats[BattleStat.DEF]).toBe(-2); @@ -68,7 +69,7 @@ describe("Moves - Octolock", () => { // before Octolock - enemy should not be trapped expect(enemyPokemon[0].findTag(t => t instanceof TrappedTag)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.OCTOLOCK)); + game.move.select(Moves.OCTOLOCK); // after Octolock - enemy should be trapped await game.phaseInterceptor.to(MoveEndPhase); diff --git a/src/test/moves/parting_shot.test.ts b/src/test/moves/parting_shot.test.ts index b8b0faba4ce..7c2ca3f334c 100644 --- a/src/test/moves/parting_shot.test.ts +++ b/src/test/moves/parting_shot.test.ts @@ -1,13 +1,15 @@ -import { SPLASH_ONLY } from "../utils/testUtils"; -import { BerryPhase, MessagePhase, TurnInitPhase, FaintPhase } from "#app/phases"; +import { BattleStat } from "#app/data/battle-stat"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { FaintPhase } from "#app/phases/faint-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, test } from "vitest"; import GameManager from "../utils/gameManager"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BattleStat } from "#app/data/battle-stat"; +import { SPLASH_ONLY } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -46,7 +48,7 @@ describe("Moves - Parting Shot", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.PARTING_SHOT)); + game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); const battleStatsOpponent = enemyPokemon.summonData.battleStats; @@ -67,7 +69,7 @@ describe("Moves - Parting Shot", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.PARTING_SHOT)); + game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); const battleStatsOpponent = enemyPokemon.summonData.battleStats; @@ -84,19 +86,19 @@ describe("Moves - Parting Shot", () => { await game.startBattle([Species.MEOWTH, Species.MEOWTH, Species.MEOWTH, Species.MURKROW, Species.ABRA]); // use Memento 3 times to debuff enemy - game.doAttack(getMovePosition(game.scene, 0, Moves.MEMENTO)); + game.move.select(Moves.MEMENTO); await game.phaseInterceptor.to(FaintPhase); expect(game.scene.getParty()[0].isFainted()).toBe(true); game.doSelectPartyPokemon(1); await game.phaseInterceptor.to(TurnInitPhase, false); - game.doAttack(getMovePosition(game.scene, 0, Moves.MEMENTO)); + game.move.select(Moves.MEMENTO); await game.phaseInterceptor.to(FaintPhase); expect(game.scene.getParty()[0].isFainted()).toBe(true); game.doSelectPartyPokemon(2); await game.phaseInterceptor.to(TurnInitPhase, false); - game.doAttack(getMovePosition(game.scene, 0, Moves.MEMENTO)); + game.move.select(Moves.MEMENTO); await game.phaseInterceptor.to(FaintPhase); expect(game.scene.getParty()[0].isFainted()).toBe(true); game.doSelectPartyPokemon(3); @@ -111,7 +113,7 @@ describe("Moves - Parting Shot", () => { expect(battleStatsOpponent[BattleStat.SPATK]).toBe(-6); // now parting shot should fail - game.doAttack(getMovePosition(game.scene, 0, Moves.PARTING_SHOT)); + game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); expect(battleStatsOpponent[BattleStat.ATK]).toBe(-6); @@ -132,7 +134,7 @@ describe("Moves - Parting Shot", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.PARTING_SHOT)); + game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); const battleStatsOpponent = enemyPokemon.summonData.battleStats; @@ -153,7 +155,7 @@ describe("Moves - Parting Shot", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.PARTING_SHOT)); + game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); const battleStatsOpponent = enemyPokemon.summonData.battleStats; @@ -171,7 +173,7 @@ describe("Moves - Parting Shot", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.PARTING_SHOT)); + game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); const battleStatsOpponent = enemyPokemon.summonData.battleStats; @@ -185,7 +187,7 @@ describe("Moves - Parting Shot", () => { "Parting shot regularly not fail if no party available to switch - party fainted", async () => { await game.startBattle([Species.MURKROW, Species.MEOWTH]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); // intentionally kill party pokemon, switch to second slot (now 1 party mon is fainted) await game.killPokemon(game.scene.getParty()[0]); @@ -194,7 +196,7 @@ describe("Moves - Parting Shot", () => { game.doSelectPartyPokemon(1); await game.phaseInterceptor.to(TurnInitPhase, false); - game.doAttack(getMovePosition(game.scene, 0, Moves.PARTING_SHOT)); + game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; diff --git a/src/test/moves/protect.test.ts b/src/test/moves/protect.test.ts index 34e208e0914..3fd51f4bc93 100644 --- a/src/test/moves/protect.test.ts +++ b/src/test/moves/protect.test.ts @@ -1,14 +1,13 @@ +import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; +import { BattleStat } from "#app/data/battle-stat"; +import { allMoves } from "#app/data/move"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import GameManager from "../utils/gameManager"; -import { Species } from "#enums/species"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BerryPhase } from "#app/phases.js"; -import { BattleStat } from "#app/data/battle-stat.js"; -import { allMoves } from "#app/data/move.js"; -import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag.js"; const TIMEOUT = 20 * 1000; @@ -48,7 +47,7 @@ describe("Moves - Protect", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.PROTECT)); + game.move.select(Moves.PROTECT); await game.phaseInterceptor.to(BerryPhase, false); @@ -66,7 +65,7 @@ describe("Moves - Protect", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.PROTECT)); + game.move.select(Moves.PROTECT); await game.phaseInterceptor.to(BerryPhase, false); @@ -84,7 +83,7 @@ describe("Moves - Protect", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.PROTECT)); + game.move.select(Moves.PROTECT); await game.phaseInterceptor.to(BerryPhase, false); @@ -103,7 +102,7 @@ describe("Moves - Protect", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.PROTECT)); + game.move.select(Moves.PROTECT); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/purify.test.ts b/src/test/moves/purify.test.ts index 7959927d63f..15d684b2d60 100644 --- a/src/test/moves/purify.test.ts +++ b/src/test/moves/purify.test.ts @@ -1,13 +1,12 @@ -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon.js"; -import { MoveEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattlerIndex } from "#app/battle"; +import { Status, StatusEffect } from "#app/data/status-effect"; +import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; 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, test } from "vitest"; -import { BattlerIndex } from "#app/battle.js"; const TIMEOUT = 20 * 1000; @@ -49,7 +48,7 @@ describe("Moves - Purify", () => { playerPokemon.hp = playerPokemon.getMaxHp() - 1; enemyPokemon.status = new Status(StatusEffect.BURN); - game.doAttack(getMovePosition(game.scene, 0, Moves.PURIFY)); + game.move.select(Moves.PURIFY); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEndPhase); @@ -69,7 +68,7 @@ describe("Moves - Purify", () => { playerPokemon.hp = playerPokemon.getMaxHp() - 1; const playerInitialHp = playerPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.PURIFY)); + game.move.select(Moves.PURIFY); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEndPhase); diff --git a/src/test/moves/quick_guard.test.ts b/src/test/moves/quick_guard.test.ts index 58165f3d916..26d9a74e9fd 100644 --- a/src/test/moves/quick_guard.test.ts +++ b/src/test/moves/quick_guard.test.ts @@ -1,12 +1,12 @@ +import { BattleStat } from "#app/data/battle-stat"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "../utils/gameManager"; -import { Species } from "#enums/species"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BerryPhase, CommandPhase } from "#app/phases.js"; -import { BattleStat } from "#app/data/battle-stat.js"; const TIMEOUT = 20 * 1000; @@ -46,11 +46,11 @@ describe("Moves - Quick Guard", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_GUARD)); + game.move.select(Moves.QUICK_GUARD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -68,11 +68,11 @@ describe("Moves - Quick Guard", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_GUARD)); + game.move.select(Moves.QUICK_GUARD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -90,11 +90,11 @@ describe("Moves - Quick Guard", () => { const leadPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_GUARD)); + game.move.select(Moves.QUICK_GUARD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.FOLLOW_ME)); + game.move.select(Moves.FOLLOW_ME, 1); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/rage_powder.test.ts b/src/test/moves/rage_powder.test.ts index 92cdcc9b4f7..3e78c6fe0c9 100644 --- a/src/test/moves/rage_powder.test.ts +++ b/src/test/moves/rage_powder.test.ts @@ -1,10 +1,9 @@ -import { BattlerIndex } from "#app/battle.js"; -import { CommandPhase, SelectTargetPhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattlerIndex } from "#app/battle"; +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 Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; @@ -31,35 +30,23 @@ describe("Moves - Rage Powder", () => { game.override.enemySpecies(Species.SNORLAX); game.override.startingLevel(100); game.override.enemyLevel(100); - game.override.moveset([ Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK ]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); test( "move effect should be bypassed by Grass type", async () => { - game.override.enemyMoveset([ Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER ]); + game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER]); - await game.startBattle([ Species.AMOONGUSS, Species.VENUSAUR ]); - - const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); + await game.startBattle([Species.AMOONGUSS, Species.VENUSAUR]); const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY_2); + game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY); + game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2); await game.phaseInterceptor.to(TurnEndPhase, false); // If redirection was bypassed, both enemies should be damaged @@ -72,29 +59,17 @@ describe("Moves - Rage Powder", () => { "move effect should be bypassed by Overcoat", async () => { game.override.ability(Abilities.OVERCOAT); - game.override.enemyMoveset([ Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER ]); + game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER]); // Test with two non-Grass type player Pokemon - await game.startBattle([ Species.BLASTOISE, Species.CHARIZARD ]); - - const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); + await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY_2); + game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY); + game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2); await game.phaseInterceptor.to(TurnEndPhase, false); // If redirection was bypassed, both enemies should be damaged diff --git a/src/test/moves/reflect.test.ts b/src/test/moves/reflect.test.ts index f5ea489a75e..9780ede3c55 100644 --- a/src/test/moves/reflect.test.ts +++ b/src/test/moves/reflect.test.ts @@ -1,14 +1,13 @@ -import { ArenaTagSide } from "#app/data/arena-tag.js"; -import Move, { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import Pokemon from "#app/field/pokemon.js"; -import { TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { NumberHolder } from "#app/utils.js"; +import { ArenaTagSide } from "#app/data/arena-tag"; +import Move, { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import Pokemon from "#app/field/pokemon"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { NumberHolder } from "#app/utils"; 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"; @@ -17,7 +16,7 @@ describe("Moves - Reflect", () => { let phaserGame: Phaser.Game; let game: GameManager; const singleBattleMultiplier = 0.5; - const doubleBattleMultiplier = 2732/4096; + const doubleBattleMultiplier = 2732 / 4096; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -40,11 +39,11 @@ describe("Moves - Reflect", () => { game.override.disableCrits(); }); - it("reduces damage of physical attacks by half in a single battle", async() => { + it("reduces damage of physical attacks by half in a single battle", async () => { const moveToUse = Moves.TACKLE; await game.startBattle([Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); @@ -52,14 +51,14 @@ describe("Moves - Reflect", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); - it("reduces damage of physical attacks by a third in a double battle", async() => { + it("reduces damage of physical attacks by a third in a double battle", async () => { game.override.battleType("double"); const moveToUse = Moves.ROCK_SLIDE; await game.startBattle([Species.SHUCKLE, Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); - game.doAttack(getMovePosition(game.scene, 1, moveToUse)); + game.move.select(moveToUse); + game.move.select(moveToUse, 1); await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); @@ -67,11 +66,11 @@ describe("Moves - Reflect", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); - it("does not affect special attacks", async() => { + it("does not affect special attacks", async () => { const moveToUse = Moves.ABSORB; await game.startBattle([Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/moves/rollout.test.ts b/src/test/moves/rollout.test.ts index ad323c447f5..ddb0b22e642 100644 --- a/src/test/moves/rollout.test.ts +++ b/src/test/moves/rollout.test.ts @@ -1,13 +1,12 @@ -import { allMoves } from "#app/data/move.js"; -import { CommandPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { CommandPhase } from "#app/phases/command-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Rollout", () => { let phaserGame: Phaser.Game; @@ -28,9 +27,9 @@ describe("Moves - Rollout", () => { game.override.disableCrits(); game.override.battleType("single"); game.override.starterSpecies(Species.RATTATA); - game.override.ability(Abilities.NONE); + game.override.ability(Abilities.BALL_FETCH); game.override.enemySpecies(Species.BIDOOF); - game.override.enemyAbility(Abilities.NONE); + game.override.enemyAbility(Abilities.BALL_FETCH); game.override.startingLevel(100); game.override.enemyLevel(100); game.override.enemyMoveset(SPLASH_ONLY); @@ -57,7 +56,7 @@ describe("Moves - Rollout", () => { let previousHp = enemyPkm.hp; for (let i = 0; i < turns; i++) { - game.doAttack(getMovePosition(game.scene, 0, Moves.ROLLOUT)); + game.move.select(Moves.ROLLOUT); await game.phaseInterceptor.to(CommandPhase); dmgHistory.push(previousHp - enemyPkm.hp); diff --git a/src/test/moves/roost.test.ts b/src/test/moves/roost.test.ts index a9036dcb478..cf07a3485e7 100644 --- a/src/test/moves/roost.test.ts +++ b/src/test/moves/roost.test.ts @@ -1,12 +1,12 @@ -import { Abilities } from "#app/enums/abilities.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; -import { MoveEffectPhase, TurnEndPhase } from "#app/phases.js"; +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; const TIMEOUT = 20 * 1000; @@ -31,8 +31,8 @@ describe("Moves - Roost", () => { game.override.enemyAbility(Abilities.INSOMNIA); game.override.startingLevel(100); game.override.enemyLevel(100); - game.override.moveset([ Moves.STOMPING_TANTRUM ]); - game.override.enemyMoveset([Moves.ROOST,Moves.ROOST,Moves.ROOST,Moves.ROOST]); + game.override.moveset([Moves.STOMPING_TANTRUM]); + game.override.enemyMoveset([Moves.ROOST, Moves.ROOST, Moves.ROOST, Moves.ROOST]); }); test( @@ -44,7 +44,7 @@ describe("Moves - Roost", () => { const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.STOMPING_TANTRUM)); + game.move.select(Moves.STOMPING_TANTRUM); await game.phaseInterceptor.to(MoveEffectPhase); diff --git a/src/test/moves/shell_trap.test.ts b/src/test/moves/shell_trap.test.ts index b027541c252..4549a8b2b73 100644 --- a/src/test/moves/shell_trap.test.ts +++ b/src/test/moves/shell_trap.test.ts @@ -1,14 +1,15 @@ +import { BattlerIndex } from "#app/battle"; +import { allMoves } from "#app/data/move"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { MoveResult } from "#app/field/pokemon"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { MovePhase } from "#app/phases/move-phase"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; -import { allMoves } from "#app/data/move.js"; -import { BattlerIndex } from "#app/battle.js"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BerryPhase, MoveEndPhase, MovePhase } from "#app/phases.js"; import { SPLASH_ONLY } from "../utils/testUtils"; -import { MoveResult } from "#app/field/pokemon.js"; const TIMEOUT = 20 * 1000; @@ -47,8 +48,8 @@ describe("Moves - Shell Trap", () => { const playerPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SHELL_TRAP)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SHELL_TRAP, 1); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2]); @@ -73,8 +74,8 @@ describe("Moves - Shell Trap", () => { const playerPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SHELL_TRAP)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SHELL_TRAP, 1); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2]); @@ -99,8 +100,8 @@ describe("Moves - Shell Trap", () => { const playerPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SHELL_TRAP)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SHELL_TRAP, 1); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2]); @@ -125,8 +126,8 @@ describe("Moves - Shell Trap", () => { const playerPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SHELL_TRAP)); - game.doAttack(getMovePosition(game.scene, 1, Moves.BULLDOZE)); + game.move.select(Moves.SHELL_TRAP); + game.move.select(Moves.BULLDOZE, 1); await game.phaseInterceptor.to(MoveEndPhase); @@ -152,7 +153,7 @@ describe("Moves - Shell Trap", () => { const playerPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SHELL_TRAP)); + game.move.select(Moves.SHELL_TRAP); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/spikes.test.ts b/src/test/moves/spikes.test.ts index bbbb3347580..c4096111c6f 100644 --- a/src/test/moves/spikes.test.ts +++ b/src/test/moves/spikes.test.ts @@ -1,8 +1,8 @@ -import { CommandPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; +import { CommandPhase } from "#app/phases/command-phase"; 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"; @@ -31,14 +31,11 @@ describe("Moves - Spikes", () => { game.override.ability(Abilities.HYDRATION); game.override.passiveAbility(Abilities.HYDRATION); game.override.startingWave(3); - game.override.enemyMoveset([Moves.SPLASH,Moves.SPLASH,Moves.SPLASH,Moves.SPLASH]); - game.override.moveset([Moves.SPIKES,Moves.SPLASH, Moves.ROAR]); + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); + game.override.moveset([Moves.SPIKES, Moves.SPLASH, Moves.ROAR]); }); - it("single - wild - stay on field - no damage", async() => { - // player set spikes on the field and do splash for 3 turns - // opponent do splash for 4 turns - // nobody should take damage + it("single - wild - stay on field - no damage", async () => { await game.classicMode.runToSummon([ Species.MIGHTYENA, Species.POOCHYENA, @@ -46,21 +43,14 @@ describe("Moves - Spikes", () => { await game.phaseInterceptor.to(CommandPhase, true); const initialHp = game.scene.getParty()[0].hp; expect(game.scene.getParty()[0].hp).toBe(initialHp); - game.doAttack(0); + game.move.select(Moves.SPIKES); await game.toNextTurn(); - game.doAttack(1); - await game.toNextTurn(); - game.doAttack(1); - await game.toNextTurn(); - game.doAttack(1); - await game.toNextTurn(); - game.doAttack(1); + game.move.select(Moves.SPLASH); await game.toNextTurn(); expect(game.scene.getParty()[0].hp).toBe(initialHp); - console.log(game.textInterceptor.logs); }, 20000); - it("single - wild - take some damage", async() => { + it("single - wild - take some damage", async () => { // player set spikes on the field and switch back to back // opponent do splash for 2 turns // nobody should take damage @@ -82,7 +72,7 @@ describe("Moves - Spikes", () => { expect(game.scene.getParty()[0].hp).toBe(initialHp); }, 20000); - it("trainer - wild - force switch opponent - should take damage", async() => { + it("trainer - wild - force switch opponent - should take damage", async () => { game.override.startingWave(5); // player set spikes on the field and do splash for 3 turns // opponent do splash for 4 turns @@ -93,14 +83,14 @@ describe("Moves - Spikes", () => { ]); await game.phaseInterceptor.to(CommandPhase, true); const initialHpOpponent = game.scene.currentBattle.enemyParty[1].hp; - game.doAttack(0); + game.move.select(Moves.SPIKES); await game.toNextTurn(); - game.doAttack(2); + game.move.select(Moves.ROAR); await game.toNextTurn(); expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(initialHpOpponent); }, 20000); - it("trainer - wild - force switch by himself opponent - should take damage", async() => { + it("trainer - wild - force switch by himself opponent - should take damage", async () => { game.override.startingWave(5); game.override.startingLevel(5000); game.override.enemySpecies(0); @@ -113,11 +103,11 @@ describe("Moves - Spikes", () => { ]); await game.phaseInterceptor.to(CommandPhase, true); const initialHpOpponent = game.scene.currentBattle.enemyParty[1].hp; - game.doAttack(0); + game.move.select(Moves.SPIKES); await game.toNextTurn(); game.forceOpponentToSwitch(); - game.doAttack(1); + game.move.select(Moves.SPLASH); await game.toNextTurn(); expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(initialHpOpponent); }, 20000); diff --git a/src/test/moves/spit_up.test.ts b/src/test/moves/spit_up.test.ts index ec0a53028ff..ab47e65d653 100644 --- a/src/test/moves/spit_up.test.ts +++ b/src/test/moves/spit_up.test.ts @@ -1,16 +1,17 @@ import { BattleStat } from "#app/data/battle-stat"; -import { StockpilingTag } from "#app/data/battler-tags.js"; -import { allMoves } from "#app/data/move.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { MoveResult, TurnMove } from "#app/field/pokemon.js"; -import { MovePhase, TurnInitPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; +import { StockpilingTag } from "#app/data/battler-tags"; +import { allMoves } from "#app/data/move"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { MoveResult, TurnMove } from "#app/field/pokemon"; +import { MovePhase } from "#app/phases/move-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Spit Up", () => { let phaserGame: Phaser.Game; @@ -54,7 +55,7 @@ describe("Moves - Spit Up", () => { vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.doAttack(0); + game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce(); @@ -79,7 +80,7 @@ describe("Moves - Spit Up", () => { vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.doAttack(0); + game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce(); @@ -105,7 +106,7 @@ describe("Moves - Spit Up", () => { vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.doAttack(0); + game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce(); @@ -125,7 +126,7 @@ describe("Moves - Spit Up", () => { vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.doAttack(0); + game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: Moves.SPIT_UP, result: MoveResult.FAIL }); @@ -145,7 +146,7 @@ describe("Moves - Spit Up", () => { vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.doAttack(0); + game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(MovePhase); expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(1); @@ -185,7 +186,7 @@ describe("Moves - Spit Up", () => { vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.doAttack(0); + game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: Moves.SPIT_UP, result: MoveResult.SUCCESS }); diff --git a/src/test/moves/spotlight.test.ts b/src/test/moves/spotlight.test.ts index 0893ba975d7..e5f4719d1d3 100644 --- a/src/test/moves/spotlight.test.ts +++ b/src/test/moves/spotlight.test.ts @@ -1,10 +1,9 @@ -import { BattlerIndex } from "#app/battle.js"; +import { BattlerIndex } from "#app/battle"; import { Stat } from "#app/data/pokemon-stat"; -import { CommandPhase, SelectTargetPhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; 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, test } from "vitest"; @@ -31,33 +30,21 @@ describe("Moves - Spotlight", () => { game.override.enemySpecies(Species.SNORLAX); game.override.startingLevel(100); game.override.enemyLevel(100); - game.override.moveset([ Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK ]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); test( "move should redirect attacks to the target", async () => { - await game.startBattle([ Species.AMOONGUSS, Species.CHARIZARD ]); - - const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); + await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPOTLIGHT)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY_2); + game.move.select(Moves.SPOTLIGHT, 0, BattlerIndex.ENEMY); + game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2); await game.phaseInterceptor.to(TurnEndPhase, false); expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]); @@ -68,17 +55,11 @@ describe("Moves - Spotlight", () => { test( "move should cause other redirection moves to fail", async () => { - game.override.enemyMoveset([ Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME ]); + game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]); - await game.startBattle([ Species.AMOONGUSS, Species.CHARIZARD ]); - - const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); + await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); /** * Spotlight will target the slower enemy. In this situation without Spotlight being used, @@ -90,14 +71,8 @@ describe("Moves - Spotlight", () => { const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPOTLIGHT)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(spotTarget); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(attackTarget); + game.move.select(Moves.SPOTLIGHT, 0, spotTarget); + game.move.select(Moves.QUICK_ATTACK, 1, attackTarget); await game.phaseInterceptor.to(TurnEndPhase, false); expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]); diff --git a/src/test/moves/stockpile.test.ts b/src/test/moves/stockpile.test.ts index 375eeab3c95..b1941b9f9b3 100644 --- a/src/test/moves/stockpile.test.ts +++ b/src/test/moves/stockpile.test.ts @@ -1,15 +1,15 @@ import { BattleStat } from "#app/data/battle-stat"; -import { StockpilingTag } from "#app/data/battler-tags.js"; -import { MoveResult, TurnMove } from "#app/field/pokemon.js"; -import { CommandPhase, TurnInitPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { StockpilingTag } from "#app/data/battler-tags"; +import { MoveResult, TurnMove } from "#app/field/pokemon"; +import { CommandPhase } from "#app/phases/command-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Stockpile", () => { describe("integration tests", () => { @@ -56,7 +56,7 @@ describe("Moves - Stockpile", () => { await game.phaseInterceptor.to(CommandPhase); } - game.doAttack(getMovePosition(game.scene, 0, Moves.STOCKPILE)); + game.move.select(Moves.STOCKPILE); await game.phaseInterceptor.to(TurnInitPhase); const stockpilingTag = user.getTag(StockpilingTag)!; @@ -91,7 +91,7 @@ describe("Moves - Stockpile", () => { expect(user.summonData.battleStats[BattleStat.DEF]).toBe(6); expect(user.summonData.battleStats[BattleStat.SPDEF]).toBe(6); - game.doAttack(getMovePosition(game.scene, 0, Moves.STOCKPILE)); + game.move.select(Moves.STOCKPILE); await game.phaseInterceptor.to(TurnInitPhase); const stockpilingTag = user.getTag(StockpilingTag)!; @@ -103,7 +103,7 @@ describe("Moves - Stockpile", () => { // do it again, just for good measure await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.STOCKPILE)); + game.move.select(Moves.STOCKPILE); await game.phaseInterceptor.to(TurnInitPhase); const stockpilingTagAgain = user.getTag(StockpilingTag)!; diff --git a/src/test/moves/swallow.test.ts b/src/test/moves/swallow.test.ts index aed30445fd2..202f25fee74 100644 --- a/src/test/moves/swallow.test.ts +++ b/src/test/moves/swallow.test.ts @@ -1,15 +1,16 @@ import { BattleStat } from "#app/data/battle-stat"; -import { StockpilingTag } from "#app/data/battler-tags.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { MoveResult, TurnMove } from "#app/field/pokemon.js"; -import { MovePhase, TurnInitPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; +import { StockpilingTag } from "#app/data/battler-tags"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { MoveResult, TurnMove } from "#app/field/pokemon"; +import { MovePhase } from "#app/phases/move-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Swallow", () => { let phaserGame: Phaser.Game; @@ -56,7 +57,7 @@ describe("Moves - Swallow", () => { vi.spyOn(pokemon, "heal"); - game.doAttack(0); + game.move.select(Moves.SWALLOW); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.heal).toHaveBeenCalledOnce(); @@ -84,7 +85,7 @@ describe("Moves - Swallow", () => { vi.spyOn(pokemon, "heal"); - game.doAttack(0); + game.move.select(Moves.SWALLOW); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.heal).toHaveBeenCalledOnce(); @@ -113,7 +114,7 @@ describe("Moves - Swallow", () => { vi.spyOn(pokemon, "heal"); - game.doAttack(0); + game.move.select(Moves.SWALLOW); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.heal).toHaveBeenCalledOnce(); @@ -131,7 +132,7 @@ describe("Moves - Swallow", () => { const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeUndefined(); - game.doAttack(0); + game.move.select(Moves.SWALLOW); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: Moves.SWALLOW, result: MoveResult.FAIL }); @@ -147,7 +148,7 @@ describe("Moves - Swallow", () => { const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); - game.doAttack(0); + game.move.select(Moves.SWALLOW); await game.phaseInterceptor.to(MovePhase); expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(1); @@ -183,7 +184,7 @@ describe("Moves - Swallow", () => { [BattleStat.SPDEF]: 2, }); - game.doAttack(0); + game.move.select(Moves.SWALLOW); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: Moves.SWALLOW, result: MoveResult.SUCCESS }); diff --git a/src/test/moves/tackle.test.ts b/src/test/moves/tackle.test.ts index 512b23ae363..5eca9e344c8 100644 --- a/src/test/moves/tackle.test.ts +++ b/src/test/moves/tackle.test.ts @@ -1,11 +1,9 @@ import { Stat } from "#app/data/pokemon-stat"; -import { CommandPhase, EnemyCommandPhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; 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"; @@ -32,30 +30,24 @@ describe("Moves - Tackle", () => { game.override.startingLevel(1); game.override.startingWave(97); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.GROWTH,Moves.GROWTH,Moves.GROWTH,Moves.GROWTH]); + game.override.enemyMoveset([Moves.GROWTH, Moves.GROWTH, Moves.GROWTH, Moves.GROWTH]); game.override.disableCrits(); }); - it("TACKLE against ghost", async() => { + it("TACKLE against ghost", async () => { const moveToUse = Moves.TACKLE; game.override.enemySpecies(Species.GENGAR); await game.startBattle([ Species.MIGHTYENA, ]); const hpOpponent = game.scene.currentBattle.enemyParty[0].hp; - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase); const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp; expect(hpLost).toBe(0); }, 20000); - it("TACKLE against not resistant", async() => { + it("TACKLE against not resistant", async () => { const moveToUse = Moves.TACKLE; await game.startBattle([ Species.MIGHTYENA, @@ -66,16 +58,10 @@ describe("Moves - Tackle", () => { const hpOpponent = game.scene.currentBattle.enemyParty[0].hp; - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase); const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp; expect(hpLost).toBeGreaterThan(0); - expect(hpLost).toBe(4); + expect(hpLost).toBeLessThan(4); }, 20000); }); diff --git a/src/test/moves/tail_whip.test.ts b/src/test/moves/tail_whip.test.ts index 7630b31f7de..0a999fe1920 100644 --- a/src/test/moves/tail_whip.test.ts +++ b/src/test/moves/tail_whip.test.ts @@ -1,12 +1,10 @@ import { BattleStat } from "#app/data/battle-stat"; -import { CommandPhase, EnemyCommandPhase, TurnInitPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; 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"; @@ -34,10 +32,10 @@ describe("Moves - Tail whip", () => { game.override.ability(Abilities.INSOMNIA); game.override.startingLevel(2000); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it("TAIL_WHIP", async() => { + it("TAIL_WHIP", async () => { const moveToUse = Moves.TAIL_WHIP; await game.startBattle([ Species.MIGHTYENA, @@ -47,13 +45,7 @@ describe("Moves - Tail whip", () => { let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.DEF]).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase); battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.DEF]).toBe(-1); diff --git a/src/test/moves/tailwind.test.ts b/src/test/moves/tailwind.test.ts index e32e10a4290..6b70122d08d 100644 --- a/src/test/moves/tailwind.test.ts +++ b/src/test/moves/tailwind.test.ts @@ -1,14 +1,13 @@ -import { ArenaTagSide } from "#app/data/arena-tag.js"; -import { Stat } from "#app/data/pokemon-stat.js"; -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import { TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { ArenaTagSide } from "#app/data/arena-tag"; +import { Stat } from "#app/data/pokemon-stat"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Tailwind", () => { let phaserGame: Phaser.Game; @@ -42,8 +41,8 @@ describe("Moves - Tailwind", () => { expect(magikarp.getBattleStat(Stat.SPD)).equal(magikarpSpd); expect(meowth.getBattleStat(Stat.SPD)).equal(meowthSpd); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.TAILWIND); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); @@ -57,19 +56,19 @@ describe("Moves - Tailwind", () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); + game.move.select(Moves.TAILWIND); await game.toNextTurn(); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.toNextTurn(); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.toNextTurn(); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.toNextTurn(); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeUndefined(); @@ -92,7 +91,7 @@ describe("Moves - Tailwind", () => { expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeUndefined(); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.ENEMY)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); + game.move.select(Moves.TAILWIND); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/moves/tera_blast.test.ts b/src/test/moves/tera_blast.test.ts new file mode 100644 index 00000000000..d261d4b856b --- /dev/null +++ b/src/test/moves/tera_blast.test.ts @@ -0,0 +1,105 @@ +import { BattlerIndex } from "#app/battle"; +import { BattleStat } from "#app/data/battle-stat"; +import { allMoves } from "#app/data/move"; +import { Type } from "#app/data/type"; +import { Abilities } from "#app/enums/abilities"; +import { Stat } from "#app/enums/stat"; +import { HitResult } from "#app/field/pokemon"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { SPLASH_ONLY } from "../utils/testUtils"; + +describe("Moves - Tera Blast", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const moveToCheck = allMoves[Moves.TERA_BLAST]; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + + game.override + .battleType("single") + .disableCrits() + .starterSpecies(Species.FEEBAS) + .moveset([Moves.TERA_BLAST]) + .ability(Abilities.BALL_FETCH) + .startingHeldItems([{ name: "TERA_SHARD", type: Type.FIRE }]) + .enemySpecies(Species.MAGIKARP) + .enemyMoveset(SPLASH_ONLY) + .enemyAbility(Abilities.BALL_FETCH) + .enemyLevel(20); + + vi.spyOn(moveToCheck, "calculateBattlePower"); + }); + + it("changes type to match user's tera type", async () => { + game.override + .enemySpecies(Species.FURRET) + .startingHeldItems([{ name: "TERA_SHARD", type: Type.FIGHTING }]); + await game.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + vi.spyOn(enemyPokemon, "apply"); + + game.move.select(Moves.TERA_BLAST); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.SUPER_EFFECTIVE); + }, 20000); + + it("increases power if user is Stellar tera type", async () => { + game.override.startingHeldItems([{ name: "TERA_SHARD", type: Type.STELLAR }]); + const stellarTypeMultiplier = 2; + const stellarTypeDmgBonus = 20; + const basePower = moveToCheck.power; + + await game.startBattle(); + + game.move.select(Moves.TERA_BLAST); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(moveToCheck.calculateBattlePower).toHaveReturnedWith((basePower + stellarTypeDmgBonus) * stellarTypeMultiplier); + }, 20000); + + // Currently abilities are bugged and can't see when a move's category is changed + it.skip("uses the higher stat of the user's Atk and SpAtk for damage calculation", async () => { + game.override.enemyAbility(Abilities.TOXIC_DEBRIS); + await game.startBattle(); + + const playerPokemon = game.scene.getPlayerPokemon()!; + playerPokemon.stats[Stat.ATK] = 100; + playerPokemon.stats[Stat.SPATK] = 1; + + game.move.select(Moves.TERA_BLAST); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(game.scene.getEnemyPokemon()!.battleData.abilityRevealed).toBe(true); + }, 20000); + + it("causes stat drops if user is Stellar tera type", async () => { + game.override.startingHeldItems([{ name: "TERA_SHARD", type: Type.STELLAR }]); + await game.startBattle(); + + const playerPokemon = game.scene.getPlayerPokemon()!; + + game.move.select(Moves.TERA_BLAST); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEndPhase"); + + expect(playerPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(-1); + expect(playerPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-1); + }, 20000); +}); diff --git a/src/test/moves/thousand_arrows.test.ts b/src/test/moves/thousand_arrows.test.ts index 84a71ee5256..8d1d6ee5f4a 100644 --- a/src/test/moves/thousand_arrows.test.ts +++ b/src/test/moves/thousand_arrows.test.ts @@ -1,10 +1,10 @@ -import { Abilities } from "#app/enums/abilities.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { BerryPhase, MoveEffectPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; 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"; @@ -30,18 +30,18 @@ describe("Moves - Thousand Arrows", () => { game.override.enemySpecies(Species.TOGETIC); game.override.startingLevel(100); game.override.enemyLevel(100); - game.override.moveset([ Moves.THOUSAND_ARROWS ]); - game.override.enemyMoveset([Moves.SPLASH,Moves.SPLASH,Moves.SPLASH,Moves.SPLASH]); + game.override.moveset([Moves.THOUSAND_ARROWS]); + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); }); it( "move should hit and ground Flying-type targets", async () => { - await game.startBattle([ Species.ILLUMISE ]); + await game.startBattle([Species.ILLUMISE]); const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS)); + game.move.select(Moves.THOUSAND_ARROWS); await game.phaseInterceptor.to(MoveEffectPhase, false); // Enemy should not be grounded before move effect is applied @@ -60,11 +60,11 @@ describe("Moves - Thousand Arrows", () => { game.override.enemySpecies(Species.SNORLAX); game.override.enemyAbility(Abilities.LEVITATE); - await game.startBattle([ Species.ILLUMISE ]); + await game.startBattle([Species.ILLUMISE]); const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS)); + game.move.select(Moves.THOUSAND_ARROWS); await game.phaseInterceptor.to(MoveEffectPhase, false); // Enemy should not be grounded before move effect is applied @@ -82,13 +82,13 @@ describe("Moves - Thousand Arrows", () => { async () => { game.override.enemySpecies(Species.SNORLAX); - await game.startBattle([ Species.ILLUMISE ]); + await game.startBattle([Species.ILLUMISE]); const enemyPokemon = game.scene.getEnemyPokemon()!; enemyPokemon.addTag(BattlerTagType.MAGNET_RISEN, undefined, Moves.MAGNET_RISE); - game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS)); + game.move.select(Moves.THOUSAND_ARROWS); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/tidy_up.test.ts b/src/test/moves/tidy_up.test.ts index e35a438c562..1ef7933c114 100644 --- a/src/test/moves/tidy_up.test.ts +++ b/src/test/moves/tidy_up.test.ts @@ -1,14 +1,14 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import { MoveEndPhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import { MoveEndPhase } from "#app/phases/move-end-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 { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Tidy Up", () => { @@ -37,81 +37,81 @@ describe("Moves - Tidy Up", () => { game.override.startingLevel(50); }); - it("spikes are cleared", async() => { + it("spikes are cleared", async () => { game.override.moveset([Moves.SPIKES, Moves.TIDY_UP]); game.override.enemyMoveset([Moves.SPIKES, Moves.SPIKES, Moves.SPIKES, Moves.SPIKES]); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPIKES)); + game.move.select(Moves.SPIKES); await game.phaseInterceptor.to(TurnEndPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP)); + game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); expect(game.scene.arena.getTag(ArenaTagType.SPIKES)).toBeUndefined(); }, 20000); - it("stealth rocks are cleared", async() => { + it("stealth rocks are cleared", async () => { game.override.moveset([Moves.STEALTH_ROCK, Moves.TIDY_UP]); game.override.enemyMoveset([Moves.STEALTH_ROCK, Moves.STEALTH_ROCK, Moves.STEALTH_ROCK, Moves.STEALTH_ROCK]); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.STEALTH_ROCK)); + game.move.select(Moves.STEALTH_ROCK); await game.phaseInterceptor.to(TurnEndPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP)); + game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); expect(game.scene.arena.getTag(ArenaTagType.STEALTH_ROCK)).toBeUndefined(); }, 20000); - it("toxic spikes are cleared", async() => { + it("toxic spikes are cleared", async () => { game.override.moveset([Moves.TOXIC_SPIKES, Moves.TIDY_UP]); game.override.enemyMoveset([Moves.TOXIC_SPIKES, Moves.TOXIC_SPIKES, Moves.TOXIC_SPIKES, Moves.TOXIC_SPIKES]); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TOXIC_SPIKES)); + game.move.select(Moves.TOXIC_SPIKES); await game.phaseInterceptor.to(TurnEndPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP)); + game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); expect(game.scene.arena.getTag(ArenaTagType.TOXIC_SPIKES)).toBeUndefined(); }, 20000); - it("sticky webs are cleared", async() => { + it("sticky webs are cleared", async () => { game.override.moveset([Moves.STICKY_WEB, Moves.TIDY_UP]); game.override.enemyMoveset([Moves.STICKY_WEB, Moves.STICKY_WEB, Moves.STICKY_WEB, Moves.STICKY_WEB]); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.STICKY_WEB)); + game.move.select(Moves.STICKY_WEB); await game.phaseInterceptor.to(TurnEndPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP)); + game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); expect(game.scene.arena.getTag(ArenaTagType.STICKY_WEB)).toBeUndefined(); }, 20000); - it.skip("substitutes are cleared", async() => { + it.skip("substitutes are cleared", async () => { game.override.moveset([Moves.SUBSTITUTE, Moves.TIDY_UP]); game.override.enemyMoveset([Moves.SUBSTITUTE, Moves.SUBSTITUTE, Moves.SUBSTITUTE, Moves.SUBSTITUTE]); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SUBSTITUTE)); + game.move.select(Moves.SUBSTITUTE); await game.phaseInterceptor.to(TurnEndPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP)); + game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); // TODO: check for subs here once the move is implemented }, 20000); - it("user's stats are raised with no traps set", async() => { + it("user's stats are raised with no traps set", async () => { await game.startBattle(); const player = game.scene.getPlayerPokemon()!.summonData.battleStats; expect(player[BattleStat.ATK]).toBe(0); expect(player[BattleStat.SPD]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP)); + game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(TurnEndPhase); expect(player[BattleStat.ATK]).toBe(+1); diff --git a/src/test/moves/u_turn.test.ts b/src/test/moves/u_turn.test.ts index 2c12a4da43b..ae55302bb42 100644 --- a/src/test/moves/u_turn.test.ts +++ b/src/test/moves/u_turn.test.ts @@ -1,12 +1,12 @@ -import { Abilities } from "#app/enums/abilities.js"; -import { SwitchPhase, TurnEndPhase } from "#app/phases"; +import { Abilities } from "#app/enums/abilities"; +import { StatusEffect } from "#app/enums/status-effect"; +import { SwitchPhase } from "#app/phases/switch-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { StatusEffect } from "#app/enums/status-effect.js"; import { SPLASH_ONLY } from "../utils/testUtils"; describe("Moves - U-turn", () => { @@ -35,7 +35,7 @@ describe("Moves - U-turn", () => { .disableCrits(); }); - it("triggers regenerator a single time when a regenerator user switches out with u-turn", async() => { + it("triggers regenerator a single time when a regenerator user switches out with u-turn", async () => { // arrange const playerHp = 1; game.override.ability(Abilities.REGENERATOR); @@ -46,7 +46,7 @@ describe("Moves - U-turn", () => { game.scene.getPlayerPokemon()!.hp = playerHp; // act - game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); + game.move.select(Moves.U_TURN); game.doSelectPartyPokemon(1); await game.phaseInterceptor.to(TurnEndPhase); @@ -56,7 +56,7 @@ describe("Moves - U-turn", () => { expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.SHUCKLE); }, 20000); - it("triggers rough skin on the u-turn user before a new pokemon is switched in", async() => { + it("triggers rough skin on the u-turn user before a new pokemon is switched in", async () => { // arrange game.override.enemyAbility(Abilities.ROUGH_SKIN); await game.startBattle([ @@ -65,7 +65,7 @@ describe("Moves - U-turn", () => { ]); // act - game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); + game.move.select(Moves.U_TURN); game.doSelectPartyPokemon(1); await game.phaseInterceptor.to(SwitchPhase, false); @@ -77,7 +77,7 @@ describe("Moves - U-turn", () => { expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase"); }, 20000); - it("triggers contact abilities on the u-turn user (eg poison point) before a new pokemon is switched in", async() => { + it("triggers contact abilities on the u-turn user (eg poison point) before a new pokemon is switched in", async () => { // arrange game.override.enemyAbility(Abilities.POISON_POINT); await game.startBattle([ @@ -87,7 +87,7 @@ describe("Moves - U-turn", () => { vi.spyOn(game.scene.getEnemyPokemon()!, "randSeedInt").mockReturnValue(0); // act - game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); + game.move.select(Moves.U_TURN); await game.phaseInterceptor.to(SwitchPhase, false); // assert diff --git a/src/test/moves/wide_guard.test.ts b/src/test/moves/wide_guard.test.ts index 94f382022c2..616972de01b 100644 --- a/src/test/moves/wide_guard.test.ts +++ b/src/test/moves/wide_guard.test.ts @@ -1,12 +1,12 @@ +import { BattleStat } from "#app/data/battle-stat"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "../utils/gameManager"; -import { Species } from "#enums/species"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BerryPhase, CommandPhase } from "#app/phases.js"; -import { BattleStat } from "#app/data/battle-stat.js"; const TIMEOUT = 20 * 1000; @@ -46,11 +46,11 @@ describe("Moves - Wide Guard", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.WIDE_GUARD)); + game.move.select(Moves.WIDE_GUARD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -67,11 +67,11 @@ describe("Moves - Wide Guard", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.WIDE_GUARD)); + game.move.select(Moves.WIDE_GUARD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -88,11 +88,11 @@ describe("Moves - Wide Guard", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.WIDE_GUARD)); + game.move.select(Moves.WIDE_GUARD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -110,11 +110,11 @@ describe("Moves - Wide Guard", () => { const leadPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.WIDE_GUARD)); + game.move.select(Moves.WIDE_GUARD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SURF)); + game.move.select(Moves.SURF, 1); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/phases/phases.test.ts b/src/test/phases/phases.test.ts index c61eb1d41b8..5ef25361a3f 100644 --- a/src/test/phases/phases.test.ts +++ b/src/test/phases/phases.test.ts @@ -1,9 +1,11 @@ -import BattleScene from "#app/battle-scene.js"; -import { LoginPhase, TitlePhase, UnavailablePhase } from "#app/phases.js"; -import { Mode } from "#app/ui/ui.js"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; +import BattleScene from "#app/battle-scene"; +import { LoginPhase } from "#app/phases/login-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import { UnavailablePhase } from "#app/phases/unavailable-phase"; +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"; describe("Phases", () => { let phaserGame: Phaser.Game; diff --git a/src/test/settingMenu/helpers/inGameManip.ts b/src/test/settingMenu/helpers/inGameManip.ts index e18a82ca571..b81e577f5b9 100644 --- a/src/test/settingMenu/helpers/inGameManip.ts +++ b/src/test/settingMenu/helpers/inGameManip.ts @@ -1,6 +1,6 @@ import { getIconForLatestInput, getSettingNameWithKeycode } from "#app/configs/inputs/configHandler"; -import { expect } from "vitest"; import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; +import { expect } from "vitest"; export class InGameManip { private config; diff --git a/src/test/settingMenu/helpers/menuManip.ts b/src/test/settingMenu/helpers/menuManip.ts index 4fd5f526897..90b3f1e96e6 100644 --- a/src/test/settingMenu/helpers/menuManip.ts +++ b/src/test/settingMenu/helpers/menuManip.ts @@ -1,6 +1,6 @@ -import { expect } from "vitest"; -import { deleteBind, getIconWithKeycode, getIconWithSettingName, getKeyWithKeycode, getKeyWithSettingName, assign, getSettingNameWithKeycode, canIAssignThisKey, canIDeleteThisKey, canIOverrideThisSetting } from "#app/configs/inputs/configHandler"; +import { assign, canIAssignThisKey, canIDeleteThisKey, canIOverrideThisSetting, deleteBind, getIconWithKeycode, getIconWithSettingName, getKeyWithKeycode, getKeyWithSettingName, getSettingNameWithKeycode } from "#app/configs/inputs/configHandler"; import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; +import { expect } from "vitest"; export class MenuManip { private config; diff --git a/src/test/settingMenu/rebinding_setting.test.ts b/src/test/settingMenu/rebinding_setting.test.ts index eead23972c2..ec2343cfb41 100644 --- a/src/test/settingMenu/rebinding_setting.test.ts +++ b/src/test/settingMenu/rebinding_setting.test.ts @@ -1,13 +1,13 @@ -import { beforeEach, describe, expect, it } from "vitest"; -import { deepCopy } from "#app/utils"; -import { getKeyWithKeycode, getKeyWithSettingName } from "#app/configs/inputs/configHandler"; -import { MenuManip } from "#test/settingMenu/helpers/menuManip"; -import { InGameManip } from "#test/settingMenu/helpers/inGameManip"; -import { InterfaceConfig } from "#app/inputs-controller"; import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty"; +import { getKeyWithKeycode, getKeyWithSettingName } from "#app/configs/inputs/configHandler"; +import { InterfaceConfig } from "#app/inputs-controller"; import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; -import { Device } from "#enums/devices"; +import { deepCopy } from "#app/utils"; import { Button } from "#enums/buttons"; +import { Device } from "#enums/devices"; +import { InGameManip } from "#test/settingMenu/helpers/inGameManip"; +import { MenuManip } from "#test/settingMenu/helpers/menuManip"; +import { beforeEach, describe, expect, it } from "vitest"; describe("Test Rebinding", () => { diff --git a/src/test/sprites/pokemonSprite.test.ts b/src/test/sprites/pokemonSprite.test.ts index deb5844d677..faf0626b365 100644 --- a/src/test/sprites/pokemonSprite.test.ts +++ b/src/test/sprites/pokemonSprite.test.ts @@ -1,8 +1,8 @@ -import { beforeAll, describe, expect, it } from "vitest"; -import _masterlist from "../../../public/images/pokemon/variant/_masterlist.json"; +import { getAppRootDir } from "#test/sprites/spritesUtils"; import fs from "fs"; import path from "path"; -import { getAppRootDir } from "#test/sprites/spritesUtils"; +import { beforeAll, describe, expect, it } from "vitest"; +import _masterlist from "../../../public/images/pokemon/variant/_masterlist.json"; type PokemonVariantMasterlist = typeof _masterlist; diff --git a/src/test/ui/starter-select.test.ts b/src/test/ui/starter-select.test.ts index 020b26b7f66..8ef1ea16b4a 100644 --- a/src/test/ui/starter-select.test.ts +++ b/src/test/ui/starter-select.test.ts @@ -1,19 +1,21 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { EncounterPhase, SelectStarterPhase, TitlePhase } from "#app/phases"; -import { Mode } from "#app/ui/ui"; -import { GameModes } from "#app/game-mode"; -import StarterSelectUiHandler from "#app/ui/starter-select-ui-handler"; -import OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler"; -import SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler"; -import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { Gender } from "#app/data/gender"; +import { Nature } from "#app/data/nature"; import { allSpecies } from "#app/data/pokemon-species"; -import { Nature} from "#app/data/nature"; -import { Button } from "#enums/buttons"; +import { GameModes } from "#app/game-mode"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; +import SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler"; +import OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler"; +import StarterSelectUiHandler from "#app/ui/starter-select-ui-handler"; +import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; +import { Button } from "#enums/buttons"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("UI - Starter select", () => { diff --git a/src/test/ui/transfer-item.test.ts b/src/test/ui/transfer-item.test.ts index bbb9a823ad9..f7dea463574 100644 --- a/src/test/ui/transfer-item.test.ts +++ b/src/test/ui/transfer-item.test.ts @@ -2,15 +2,15 @@ import { BerryType } from "#app/enums/berry-type"; import { Button } from "#app/enums/buttons"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; -import { BattleEndPhase, SelectModifierPhase } from "#app/phases"; -import GameManager from "#test/utils/gameManager"; +import { BattleEndPhase } from "#app/phases/battle-end-phase"; +import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler"; import { Mode } from "#app/ui/ui"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; describe("UI - Transfer Items", () => { @@ -43,7 +43,7 @@ describe("UI - Transfer Items", () => { await game.startBattle([Species.RAYQUAZA, Species.RAYQUAZA, Species.RAYQUAZA]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_CLAW)); + game.move.select(Moves.DRAGON_CLAW); game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { expect(game.scene.ui.getHandler()).toBeInstanceOf(ModifierSelectUiHandler); diff --git a/src/test/ui/type-hints.test.ts b/src/test/ui/type-hints.test.ts new file mode 100644 index 00000000000..ccab02b82bf --- /dev/null +++ b/src/test/ui/type-hints.test.ts @@ -0,0 +1,89 @@ +import { Button } from "#app/enums/buttons"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { CommandPhase } from "#app/phases/command-phase"; +import FightUiHandler from "#app/ui/fight-ui-handler"; +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 { SPLASH_ONLY } from "../utils/testUtils"; + +describe("UI - Type Hints", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(async () => { + game = new GameManager(phaserGame); + game.settings.typeHints(true); //activate type hints + game.override.battleType("single").startingLevel(100).startingWave(1).enemyMoveset(SPLASH_ONLY); + }); + + it("check immunity color", async () => { + game.override + .battleType("single") + .startingLevel(100) + .startingWave(1) + .enemySpecies(Species.FLORGES) + .enemyMoveset(SPLASH_ONLY) + .moveset([Moves.DRAGON_CLAW]); + game.settings.typeHints(true); //activate type hints + + await game.startBattle([Species.RAYQUAZA]); + + game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + const { ui } = game.scene; + const handler = ui.getHandler(); + handler.processInput(Button.ACTION); // select "Fight" + game.phaseInterceptor.unlock(); + }); + + game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { + const { ui } = game.scene; + const movesContainer = ui.getByName(FightUiHandler.MOVES_CONTAINER_NAME); + const dragonClawText = movesContainer + .getAll() + .find((text) => text.text === "Dragon Claw")! as unknown as MockText; + + expect.soft(dragonClawText.color).toBe("#929292"); + ui.getHandler().processInput(Button.ACTION); + }); + await game.phaseInterceptor.to(CommandPhase); + }); + + it("check status move color", async () => { + game.override.enemySpecies(Species.FLORGES).moveset([Moves.GROWL]); + + await game.startBattle([Species.RAYQUAZA]); + + game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + const { ui } = game.scene; + const handler = ui.getHandler(); + handler.processInput(Button.ACTION); // select "Fight" + game.phaseInterceptor.unlock(); + }); + + game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { + const { ui } = game.scene; + const movesContainer = ui.getByName(FightUiHandler.MOVES_CONTAINER_NAME); + const growlText = movesContainer + .getAll() + .find((text) => text.text === "Growl")! as unknown as MockText; + + expect.soft(growlText.color).toBe(undefined); + ui.getHandler().processInput(Button.ACTION); + }); + await game.phaseInterceptor.to(CommandPhase); + }); +}); diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 27ba7a215eb..cb3c547744b 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -1,35 +1,47 @@ -import GameWrapper from "#test/utils/gameWrapper"; -import { Mode } from "#app/ui/ui"; -import { generateStarter, waitUntil } from "#test/utils/gameManagerUtils"; -import { CommandPhase, EncounterPhase, FaintPhase, LoginPhase, MovePhase, NewBattlePhase, SelectStarterPhase, SelectTargetPhase, TitlePhase, TurnEndPhase, TurnInitPhase, TurnStartPhase } from "#app/phases"; -import BattleScene from "#app/battle-scene.js"; -import PhaseInterceptor from "#test/utils/phaseInterceptor"; -import TextInterceptor from "#test/utils/TextInterceptor"; -import { GameModes, getGameMode } from "#app/game-mode"; -import fs from "fs"; -import { AES, enc } from "crypto-js"; import { updateUserInfo } from "#app/account"; -import InputsHandler from "#app/test/utils/inputsHandler"; -import ErrorInterceptor from "#app/test/utils/errorInterceptor"; +import { BattlerIndex } from "#app/battle"; +import BattleScene from "#app/battle-scene"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; -import { MockClock } from "#app/test/utils/mocks/mockClock"; -import PartyUiHandler from "#app/ui/party-ui-handler"; -import CommandUiHandler, { Command } from "#app/ui/command-ui-handler"; import Trainer from "#app/field/trainer"; +import { GameModes, getGameMode } from "#app/game-mode"; +import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type"; +import { CommandPhase } from "#app/phases/command-phase"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { FaintPhase } from "#app/phases/faint-phase"; +import { LoginPhase } from "#app/phases/login-phase"; +import { MovePhase } from "#app/phases/move-phase"; +import { NewBattlePhase } from "#app/phases/new-battle-phase"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { SelectTargetPhase } from "#app/phases/select-target-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; +import ErrorInterceptor from "#app/test/utils/errorInterceptor"; +import InputsHandler from "#app/test/utils/inputsHandler"; +import { MockClock } from "#app/test/utils/mocks/mockClock"; +import CommandUiHandler from "#app/ui/command-ui-handler"; +import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; +import PartyUiHandler from "#app/ui/party-ui-handler"; +import TargetSelectUiHandler from "#app/ui/target-select-ui-handler"; +import { Mode } from "#app/ui/ui"; +import { Button } from "#enums/buttons"; import { ExpNotification } from "#enums/exp-notification"; import { GameDataType } from "#enums/game-data-type"; import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; -import { Button } from "#enums/buttons"; -import { BattlerIndex } from "#app/battle.js"; -import TargetSelectUiHandler from "#app/ui/target-select-ui-handler.js"; -import { OverridesHelper } from "./helpers/overridesHelper"; -import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type.js"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler.js"; -import { MoveHelper } from "./helpers/moveHelper"; +import { generateStarter, waitUntil } from "#test/utils/gameManagerUtils"; +import GameWrapper from "#test/utils/gameWrapper"; +import PhaseInterceptor from "#test/utils/phaseInterceptor"; +import TextInterceptor from "#test/utils/TextInterceptor"; +import { AES, enc } from "crypto-js"; +import fs from "fs"; import { vi } from "vitest"; import { ClassicModeHelper } from "./helpers/classicModeHelper"; import { DailyModeHelper } from "./helpers/dailyModeHelper"; +import { MoveHelper } from "./helpers/moveHelper"; +import { OverridesHelper } from "./helpers/overridesHelper"; +import { SettingsHelper } from "./helpers/settingsHelper"; /** * Class to manage the game state and transitions between phases. @@ -44,6 +56,7 @@ export default class GameManager { public readonly move: MoveHelper; public readonly classicMode: ClassicModeHelper; public readonly dailyMode: DailyModeHelper; + public readonly settings: SettingsHelper; /** * Creates an instance of GameManager. @@ -63,6 +76,7 @@ export default class GameManager { this.move = new MoveHelper(this); this.classicMode = new ClassicModeHelper(this); this.dailyMode = new DailyModeHelper(this); + this.settings = new SettingsHelper(this); } /** @@ -178,38 +192,23 @@ export default class GameManager { } /** - * Emulate a player attack - * @param movePosition the index of the move in the pokemon's moveset array + * Emulate a player's target selection after a move is chosen, usually called automatically by {@linkcode MoveHelper.select}. + * Will trigger during the next {@linkcode SelectTargetPhase} + * @param {BattlerIndex} targetIndex The index of the attack target, or `undefined` for multi-target attacks + * @param movePosition The index of the move in the pokemon's moveset array */ - doAttack(movePosition: integer) { - this.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - this.scene.ui.setMode(Mode.FIGHT, (this.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - this.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - (this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); - - // Confirm target selection if move is multi-target + selectTarget(movePosition: integer, targetIndex?: BattlerIndex) { this.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { const handler = this.scene.ui.getHandler() as TargetSelectUiHandler; const move = (this.scene.getCurrentPhase() as SelectTargetPhase).getPokemon().getMoveset()[movePosition]!.getMove(); // TODO: is the bang correct? - if (move.isMultiTarget()) { - handler.processInput(Button.ACTION); + if (!move.isMultiTarget()) { + handler.setCursor(targetIndex !== undefined ? targetIndex : BattlerIndex.ENEMY); + } + if (move.isMultiTarget() && targetIndex !== undefined) { + throw new Error(`targetIndex was passed to selectMove() but move ("${move.name}") is not targetted`); } - }, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(MovePhase) || this.isCurrentPhase(TurnEndPhase)); - } - - /** - * Emulate a player's target selection after an attack is chosen, - * usually called after {@linkcode doAttack} in a double battle. - * @param {BattlerIndex} targetIndex the index of the attack target - */ - doSelectTarget(targetIndex: BattlerIndex) { - this.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { - const handler = this.scene.ui.getHandler() as TargetSelectUiHandler; - handler.setCursor(targetIndex); handler.processInput(Button.ACTION); - }, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(TurnStartPhase)); + }, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(MovePhase) || this.isCurrentPhase(TurnStartPhase) || this.isCurrentPhase(TurnEndPhase)); } /** Faint all opponents currently on the field */ @@ -307,7 +306,7 @@ export default class GameManager { */ async importData(path): Promise<[boolean, integer]> { const saveKey = "x0i2O7WRiANTqPmZ"; - const dataRaw = fs.readFileSync(path, {encoding: "utf8", flag: "r"}); + const dataRaw = fs.readFileSync(path, { encoding: "utf8", flag: "r" }); let dataStr = AES.decrypt(dataRaw, saveKey).toString(enc.Utf8); dataStr = this.scene.gameData.convertSystemDataStr(dataStr); const systemData = this.scene.gameData.parseSystemData(dataStr); @@ -321,7 +320,7 @@ export default class GameManager { async killPokemon(pokemon: PlayerPokemon | EnemyPokemon) { (this.scene.time as MockClock).overrideDelay = 0.01; - return new Promise(async(resolve, reject) => { + return new Promise(async (resolve, reject) => { pokemon.hp = 0; this.scene.pushPhase(new FaintPhase(this.scene, pokemon.getBattlerIndex(), true)); await this.phaseInterceptor.to(FaintPhase).catch((e) => reject(e)); diff --git a/src/test/utils/gameManagerUtils.ts b/src/test/utils/gameManagerUtils.ts index dfba55fc75c..20a3fd179fd 100644 --- a/src/test/utils/gameManagerUtils.ts +++ b/src/test/utils/gameManagerUtils.ts @@ -1,12 +1,12 @@ +import BattleScene from "#app/battle-scene"; import { getDailyRunStarters } from "#app/data/daily-run"; import { Gender } from "#app/data/gender"; -import { Species } from "#enums/species"; -import { Starter } from "#app/ui/starter-select-ui-handler"; -import { GameModes, getGameMode } from "#app/game-mode"; import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; -import { PlayerPokemon } from "#app/field/pokemon"; import { Moves } from "#app/enums/moves"; -import BattleScene from "#app/battle-scene"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { GameModes, getGameMode } from "#app/game-mode"; +import { Starter } from "#app/ui/starter-select-ui-handler"; +import { Species } from "#enums/species"; /** Function to convert Blob to string */ export function blobToString(blob) { diff --git a/src/test/utils/gameWrapper.ts b/src/test/utils/gameWrapper.ts index 49044c260fa..f3098fa9b71 100644 --- a/src/test/utils/gameWrapper.ts +++ b/src/test/utils/gameWrapper.ts @@ -1,31 +1,28 @@ /* eslint-disable */ // @ts-nocheck -import * as main from "#app/main"; +import BattleScene, * as battleScene from "#app/battle-scene"; +import { MoveAnim } from "#app/data/battle-anims"; +import Pokemon from "#app/field/pokemon"; +import * as Utils from "#app/utils"; +import { blobToString } from "#test/utils/gameManagerUtils"; +import { MockClock } from "#test/utils/mocks/mockClock"; +import mockConsoleLog from "#test/utils/mocks/mockConsoleLog"; +import { MockFetch } from "#test/utils/mocks/mockFetch"; +import MockLoader from "#test/utils/mocks/mockLoader"; +import mockLocalStorage from "#test/utils/mocks/mockLocalStorage"; +import MockImage from "#test/utils/mocks/mocksContainer/mockImage"; +import MockTextureManager from "#test/utils/mocks/mockTextureManager"; import fs from "fs"; +import Phaser from "phaser"; +import InputText from "phaser3-rex-plugins/plugins/inputtext"; +import { vi } from "vitest"; +import { MockGameObjectCreator } from "./mocks/mockGameObjectCreator"; import InputManager = Phaser.Input.InputManager; import KeyboardManager = Phaser.Input.Keyboard.KeyboardManager; import KeyboardPlugin = Phaser.Input.Keyboard.KeyboardPlugin; import GamepadPlugin = Phaser.Input.Gamepad.GamepadPlugin; import EventEmitter = Phaser.Events.EventEmitter; import UpdateList = Phaser.GameObjects.UpdateList; -import MockGraphics from "#test/utils/mocks/mocksContainer/mockGraphics"; -import MockTextureManager from "#test/utils/mocks/mockTextureManager"; -import Phaser from "phaser"; -import { blobToString } from "#test/utils/gameManagerUtils"; -import { vi } from "vitest"; -import mockLocalStorage from "#test/utils/mocks/mockLocalStorage"; -import mockConsoleLog from "#test/utils/mocks/mockConsoleLog"; -import MockLoader from "#test/utils/mocks/mockLoader"; -import { MockFetch } from "#test/utils/mocks/mockFetch"; -import * as Utils from "#app/utils"; -import InputText from "phaser3-rex-plugins/plugins/inputtext"; -import { MockClock } from "#test/utils/mocks/mockClock"; -import BattleScene from "#app/battle-scene.js"; -import { MoveAnim } from "#app/data/battle-anims"; -import Pokemon from "#app/field/pokemon"; -import * as battleScene from "#app/battle-scene"; -import MockImage from "#test/utils/mocks/mocksContainer/mockImage.js"; -import { MockGameObjectCreator } from "./mocks/mockGameObjectCreator"; Object.defineProperty(window, "localStorage", { value: mockLocalStorage(), diff --git a/src/test/utils/helpers/classicModeHelper.ts b/src/test/utils/helpers/classicModeHelper.ts index e6854d5bc79..f41472303b4 100644 --- a/src/test/utils/helpers/classicModeHelper.ts +++ b/src/test/utils/helpers/classicModeHelper.ts @@ -1,8 +1,9 @@ -import { Species } from "#app/enums/species.js"; -import { GameModes, getGameMode } from "#app/game-mode.js"; -import overrides from "#app/overrides.js"; -import { EncounterPhase, SelectStarterPhase } from "#app/phases.js"; -import { Mode } from "#app/ui/ui.js"; +import { Species } from "#app/enums/species"; +import { GameModes, getGameMode } from "#app/game-mode"; +import overrides from "#app/overrides"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { Mode } from "#app/ui/ui"; import { generateStarter } from "../gameManagerUtils"; import { GameManagerHelper } from "./gameManagerHelper"; diff --git a/src/test/utils/helpers/dailyModeHelper.ts b/src/test/utils/helpers/dailyModeHelper.ts index c83a2e587d9..8f60981f4d8 100644 --- a/src/test/utils/helpers/dailyModeHelper.ts +++ b/src/test/utils/helpers/dailyModeHelper.ts @@ -1,8 +1,9 @@ -import { Button } from "#app/enums/buttons.js"; -import overrides from "#app/overrides.js"; -import { EncounterPhase, TitlePhase } from "#app/phases.js"; -import SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import { Button } from "#app/enums/buttons"; +import overrides from "#app/overrides"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler"; +import { Mode } from "#app/ui/ui"; import { GameManagerHelper } from "./gameManagerHelper"; /** diff --git a/src/test/utils/helpers/moveHelper.ts b/src/test/utils/helpers/moveHelper.ts index 9438952aa92..a53fa521785 100644 --- a/src/test/utils/helpers/moveHelper.ts +++ b/src/test/utils/helpers/moveHelper.ts @@ -1,5 +1,11 @@ +import { BattlerIndex } from "#app/battle"; +import { Moves } from "#app/enums/moves"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { Command } from "#app/ui/command-ui-handler"; +import { Mode } from "#app/ui/ui"; import { vi } from "vitest"; -import { MoveEffectPhase } from "#app/phases.js"; +import { getMovePosition } from "../gameManagerUtils"; import { GameManagerHelper } from "./gameManagerHelper"; /** @@ -32,4 +38,25 @@ export class MoveHelper extends GameManagerHelper { hitCheck.mockReturnValue(false); } } + + /** + * Select the move to be used by the given Pokemon(-index). Triggers during the next {@linkcode CommandPhase} + * @param move the move to use + * @param pkmIndex the pokemon index. Relevant for double-battles only (defaults to 0) + * @param targetIndex The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves, or `null` if a manual call to `selectTarget()` is required + */ + select(move: Moves, pkmIndex: 0 | 1 = 0, targetIndex?: BattlerIndex | null) { + const movePosition = getMovePosition(this.game.scene, pkmIndex, move); + + this.game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + this.game.scene.ui.setMode(Mode.FIGHT, (this.game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); + }); + this.game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { + (this.game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); + }); + + if (targetIndex !== null) { + this.game.selectTarget(movePosition, targetIndex); + } + } } diff --git a/src/test/utils/helpers/overridesHelper.ts b/src/test/utils/helpers/overridesHelper.ts index dbcb02825f2..d5eaee003db 100644 --- a/src/test/utils/helpers/overridesHelper.ts +++ b/src/test/utils/helpers/overridesHelper.ts @@ -84,6 +84,17 @@ export class OverridesHelper extends GameManagerHelper { return this; } + /** + * Override the player's starting modifiers + * @param modifiers the modifiers to set + * @returns this + */ + startingModifier(modifiers: ModifierOverride[]): this { + vi.spyOn(Overrides, "STARTING_MODIFIER_OVERRIDE", "get").mockReturnValue(modifiers); + this.log(`Player starting modifiers set to: ${modifiers}`); + return this; + } + /** * Override the player (pokemon) {@linkcode Abilities | ability} * @param ability the (pokemon) {@linkcode Abilities | ability} to set diff --git a/src/test/utils/helpers/settingsHelper.ts b/src/test/utils/helpers/settingsHelper.ts new file mode 100644 index 00000000000..dec9e160d51 --- /dev/null +++ b/src/test/utils/helpers/settingsHelper.ts @@ -0,0 +1,15 @@ +import { GameManagerHelper } from "./gameManagerHelper"; + +/** + * Helper to handle settings for tests + */ +export class SettingsHelper extends GameManagerHelper { + + /** + * Disable/Enable type hints settings + * @param enable true to enabled, false to disabled + */ + typeHints(enable: boolean) { + this.game.scene.typeHints = enable; + } +} diff --git a/src/test/utils/inputsHandler.ts b/src/test/utils/inputsHandler.ts index 148329ada32..30dd101f43d 100644 --- a/src/test/utils/inputsHandler.ts +++ b/src/test/utils/inputsHandler.ts @@ -1,11 +1,11 @@ import BattleScene from "#app/battle-scene"; -import Phaser from "phaser"; -import { InputsController } from "#app/inputs-controller"; import pad_xbox360 from "#app/configs/inputs/pad_xbox360"; -import { holdOn } from "#test/utils/gameManagerUtils"; +import { InputsController } from "#app/inputs-controller"; import TouchControl from "#app/touch-controls"; -import { JSDOM } from "jsdom"; +import { holdOn } from "#test/utils/gameManagerUtils"; import fs from "fs"; +import { JSDOM } from "jsdom"; +import Phaser from "phaser"; interface LogEntry { type: string; diff --git a/src/test/utils/misc.test.ts b/src/test/utils/misc.test.ts index c1947dbe8a2..a49b2894ca2 100644 --- a/src/test/utils/misc.test.ts +++ b/src/test/utils/misc.test.ts @@ -1,8 +1,8 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; import { apiFetch } from "#app/utils"; +import GameManager from "#test/utils/gameManager"; import { waitUntil } from "#test/utils/gameManagerUtils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Test misc", () => { let phaserGame: Phaser.Game; diff --git a/src/test/utils/mocks/mockTextureManager.ts b/src/test/utils/mocks/mockTextureManager.ts index 330409e9776..16d94da28ad 100644 --- a/src/test/utils/mocks/mockTextureManager.ts +++ b/src/test/utils/mocks/mockTextureManager.ts @@ -1,12 +1,12 @@ import MockContainer from "#test/utils/mocks/mocksContainer/mockContainer"; -import MockSprite from "#test/utils/mocks/mocksContainer/mockSprite"; -import MockRectangle from "#test/utils/mocks/mocksContainer/mockRectangle"; -import MockNineslice from "#test/utils/mocks/mocksContainer/mockNineslice"; import MockImage from "#test/utils/mocks/mocksContainer/mockImage"; -import MockText from "#test/utils/mocks/mocksContainer/mockText"; +import MockNineslice from "#test/utils/mocks/mocksContainer/mockNineslice"; import MockPolygon from "#test/utils/mocks/mocksContainer/mockPolygon"; -import { MockGameObject } from "./mockGameObject"; +import MockRectangle from "#test/utils/mocks/mocksContainer/mockRectangle"; +import MockSprite from "#test/utils/mocks/mocksContainer/mockSprite"; +import MockText from "#test/utils/mocks/mocksContainer/mockText"; import MockTexture from "#test/utils/mocks/mocksContainer/mockTexture"; +import { MockGameObject } from "./mockGameObject"; /** * Stub class for Phaser.Textures.TextureManager diff --git a/src/test/utils/mocks/mocksContainer/mockContainer.ts b/src/test/utils/mocks/mocksContainer/mockContainer.ts index d3672cb5235..5babd9e71b2 100644 --- a/src/test/utils/mocks/mocksContainer/mockContainer.ts +++ b/src/test/utils/mocks/mocksContainer/mockContainer.ts @@ -1,4 +1,5 @@ import MockTextureManager from "#test/utils/mocks/mockTextureManager"; +import { vi } from "vitest"; import { MockGameObject } from "../mockGameObject"; export default class MockContainer implements MockGameObject { @@ -13,6 +14,7 @@ export default class MockContainer implements MockGameObject { public frame; protected textureManager; public list: MockGameObject[] = []; + private name?: string; constructor(textureManager: MockTextureManager, x, y) { this.x = x; @@ -159,9 +161,10 @@ export default class MockContainer implements MockGameObject { // Moves this Game Object to be below the given Game Object in the display list. } - setName(name) { + setName = vi.fn((name: string) => { + this.name = name; // return this.phaserSprite.setName(name); - } + }); bringToTop(obj) { // Brings this Game Object to the top of its parents display list. diff --git a/src/test/utils/mocks/mocksContainer/mockSprite.ts b/src/test/utils/mocks/mocksContainer/mockSprite.ts index 9c566fc4bcb..35cd2d5faab 100644 --- a/src/test/utils/mocks/mocksContainer/mockSprite.ts +++ b/src/test/utils/mocks/mocksContainer/mockSprite.ts @@ -1,7 +1,7 @@ +import Phaser from "phaser"; import { MockGameObject } from "../mockGameObject"; import Sprite = Phaser.GameObjects.Sprite; import Frame = Phaser.Textures.Frame; -import Phaser from "phaser"; export default class MockSprite implements MockGameObject { diff --git a/src/test/utils/mocks/mocksContainer/mockText.ts b/src/test/utils/mocks/mocksContainer/mockText.ts index 5d405efadfd..6b9ecf083fd 100644 --- a/src/test/utils/mocks/mocksContainer/mockText.ts +++ b/src/test/utils/mocks/mocksContainer/mockText.ts @@ -1,4 +1,5 @@ import UI from "#app/ui/ui"; +import { vi } from "vitest"; import { MockGameObject } from "../mockGameObject"; export default class MockText implements MockGameObject { @@ -10,6 +11,8 @@ export default class MockText implements MockGameObject { public list: MockGameObject[] = []; public style; public text = ""; + private name?: string; + public color?: string; constructor(textureManager, x, y, content, styleOptions) { this.scene = textureManager.scene; @@ -190,10 +193,9 @@ export default class MockText implements MockGameObject { }; } - setColor(color) { - // Sets the tint of this Game Object. - // return this.phaserText.setColor(color); - } + setColor = vi.fn((color: string) => { + this.color = color; + }); setShadowColor(color) { // Sets the shadow color. @@ -219,9 +221,9 @@ export default class MockText implements MockGameObject { // return this.phaserText.setAlpha(alpha); } - setName(name) { - // return this.phaserText.setName(name); - } + setName = vi.fn((name: string) => { + this.name = name; + }); setAlign(align) { // return this.phaserText.setAlign(align); diff --git a/src/test/utils/mocks/mocksContainer/mockTexture.ts b/src/test/utils/mocks/mocksContainer/mockTexture.ts index 03bedb4751b..cb31480cc60 100644 --- a/src/test/utils/mocks/mocksContainer/mockTexture.ts +++ b/src/test/utils/mocks/mocksContainer/mockTexture.ts @@ -1,5 +1,5 @@ -import { MockGameObject } from "../mockGameObject"; import MockTextureManager from "#test/utils/mocks/mockTextureManager"; +import { MockGameObject } from "../mockGameObject"; /** diff --git a/src/test/utils/phaseInterceptor.ts b/src/test/utils/phaseInterceptor.ts index 34f79f93b6e..ca3d55137fa 100644 --- a/src/test/utils/phaseInterceptor.ts +++ b/src/test/utils/phaseInterceptor.ts @@ -1,43 +1,42 @@ -import { - BattleEndPhase, - BerryPhase, - CheckSwitchPhase, - CommandPhase, - DamagePhase, - EggLapsePhase, - EncounterPhase, - EnemyCommandPhase, - FaintPhase, - LoginPhase, - MessagePhase, - MoveEffectPhase, - MoveEndPhase, - MovePhase, - NewBattlePhase, - NextEncounterPhase, - PostSummonPhase, - SelectGenderPhase, - SelectModifierPhase, - SelectStarterPhase, - SelectTargetPhase, - ShinySparklePhase, - ShowAbilityPhase, - StatChangePhase, - SummonPhase, - SwitchPhase, - SwitchSummonPhase, - TitlePhase, - ToggleDoublePositionPhase, - TurnEndPhase, - TurnInitPhase, - TurnStartPhase, - UnavailablePhase, - VictoryPhase -} from "#app/phases"; -import UI, { Mode } from "#app/ui/ui"; import { Phase } from "#app/phase"; +import { BattleEndPhase } from "#app/phases/battle-end-phase"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { EggLapsePhase } from "#app/phases/egg-lapse-phase"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { FaintPhase } from "#app/phases/faint-phase"; +import { LoginPhase } from "#app/phases/login-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { MovePhase } from "#app/phases/move-phase"; +import { NewBattlePhase } from "#app/phases/new-battle-phase"; +import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; +import { PartyHealPhase } from "#app/phases/party-heal-phase"; +import { PostSummonPhase } from "#app/phases/post-summon-phase"; +import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; +import { SelectGenderPhase } from "#app/phases/select-gender-phase"; +import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { SelectTargetPhase } from "#app/phases/select-target-phase"; +import { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase"; +import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; +import { StatChangePhase } from "#app/phases/stat-change-phase"; +import { SummonPhase } from "#app/phases/summon-phase"; +import { SwitchPhase } from "#app/phases/switch-phase"; +import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; +import { UnavailablePhase } from "#app/phases/unavailable-phase"; +import { VictoryPhase } from "#app/phases/victory-phase"; import ErrorInterceptor from "#app/test/utils/errorInterceptor"; -import { QuietFormChangePhase } from "#app/form-change-phase"; +import UI, { Mode } from "#app/ui/ui"; export default class PhaseInterceptor { public scene; @@ -92,6 +91,7 @@ export default class PhaseInterceptor { [QuietFormChangePhase, this.startPhase], [SwitchPhase, this.startPhase], [SwitchSummonPhase, this.startPhase], + [PartyHealPhase, this.startPhase], ]; private endBySetMode = [ diff --git a/src/test/vitest.setup.ts b/src/test/vitest.setup.ts index b2861b7071c..eaa987c1a66 100644 --- a/src/test/vitest.setup.ts +++ b/src/test/vitest.setup.ts @@ -9,8 +9,8 @@ import { initMoves } from "#app/data/move"; import { initPokemonPrevolutions } from "#app/data/pokemon-evolutions"; import { initPokemonForms } from "#app/data/pokemon-forms"; import { initSpecies } from "#app/data/pokemon-species"; -import { initAchievements } from "#app/system/achv.js"; -import { initVouchers } from "#app/system/voucher.js"; +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"; diff --git a/src/timed-event-manager.ts b/src/timed-event-manager.ts index e3370d31216..58847006e91 100644 --- a/src/timed-event-manager.ts +++ b/src/timed-event-manager.ts @@ -28,7 +28,7 @@ const timedEvents: TimedEvent[] = [ eventType: EventType.SHINY, shinyMultiplier: 2, startDate: new Date(Date.UTC(2024, 7, 16, 0)), - endDate: new Date(Date.UTC(2024, 7, 21, 0)), + endDate: new Date(Date.UTC(2024, 7, 22, 0)), bannerFilename: "august-variant-update" } ]; diff --git a/src/ui/ball-ui-handler.ts b/src/ui/ball-ui-handler.ts index d8b3e5e3ee8..04691886d9c 100644 --- a/src/ui/ball-ui-handler.ts +++ b/src/ui/ball-ui-handler.ts @@ -1,4 +1,3 @@ -import { CommandPhase } from "../phases"; import BattleScene from "../battle-scene"; import { getPokeballName } from "../data/pokeball"; import { addTextObject, getTextStyleOptions, TextStyle } from "./text"; @@ -7,6 +6,7 @@ import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; import {Button} from "#enums/buttons"; +import { CommandPhase } from "#app/phases/command-phase.js"; export default class BallUiHandler extends UiHandler { private pokeballSelectContainer: Phaser.GameObjects.Container; diff --git a/src/ui/battle-message-ui-handler.ts b/src/ui/battle-message-ui-handler.ts index 1c7dfb27630..7a30e2787df 100644 --- a/src/ui/battle-message-ui-handler.ts +++ b/src/ui/battle-message-ui-handler.ts @@ -226,7 +226,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler { highestIv = ivs[s]; } }); - if (shownStat) { + if (shownStat !== null && shownStat !== undefined) { shownStats.push(shownStat); statsPool.splice(statsPool.indexOf(shownStat), 1); } diff --git a/src/ui/challenges-select-ui-handler.ts b/src/ui/challenges-select-ui-handler.ts index 12211fa71cc..73c47da41fe 100644 --- a/src/ui/challenges-select-ui-handler.ts +++ b/src/ui/challenges-select-ui-handler.ts @@ -5,12 +5,13 @@ import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; import {Button} from "#enums/buttons"; import i18next from "i18next"; -import { SelectStarterPhase, TitlePhase } from "#app/phases.js"; import { Challenge } from "#app/data/challenge.js"; import * as Utils from "../utils"; import { Challenges } from "#app/enums/challenges.js"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { Color, ShadowColor } from "#app/enums/color.js"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase.js"; +import { TitlePhase } from "#app/phases/title-phase.js"; /** * Handles all the UI for choosing optional challenges. diff --git a/src/ui/command-ui-handler.ts b/src/ui/command-ui-handler.ts index 11814a25240..b22ea5d20fc 100644 --- a/src/ui/command-ui-handler.ts +++ b/src/ui/command-ui-handler.ts @@ -1,4 +1,3 @@ -import { CommandPhase } from "../phases"; import BattleScene from "../battle-scene"; import { addTextObject, TextStyle } from "./text"; import PartyUiHandler, { PartyUiMode } from "./party-ui-handler"; @@ -7,6 +6,7 @@ import UiHandler from "./ui-handler"; import i18next from "i18next"; import {Button} from "#enums/buttons"; import { getPokemonNameWithAffix } from "#app/messages.js"; +import { CommandPhase } from "#app/phases/command-phase.js"; export enum Command { FIGHT = 0, diff --git a/src/ui/egg-hatch-scene-handler.ts b/src/ui/egg-hatch-scene-handler.ts index f567861e0b7..733873b974e 100644 --- a/src/ui/egg-hatch-scene-handler.ts +++ b/src/ui/egg-hatch-scene-handler.ts @@ -1,8 +1,8 @@ import BattleScene from "../battle-scene"; -import { EggHatchPhase } from "../egg-hatch-phase"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import {Button} from "#enums/buttons"; +import { EggHatchPhase } from "#app/phases/egg-hatch-phase.js"; export default class EggHatchSceneHandler extends UiHandler { public eggHatchContainer: Phaser.GameObjects.Container; diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index 8279ab72a70..71d137fbfd7 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -5,13 +5,15 @@ import { Command } from "./command-ui-handler"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import * as Utils from "../utils"; -import { CommandPhase } from "../phases"; import { MoveCategory } from "#app/data/move.js"; import i18next from "i18next"; import {Button} from "#enums/buttons"; import Pokemon, { PokemonMove } from "#app/field/pokemon.js"; +import { CommandPhase } from "#app/phases/command-phase.js"; export default class FightUiHandler extends UiHandler { + public static readonly MOVES_CONTAINER_NAME = "moves"; + private movesContainer: Phaser.GameObjects.Container; private moveInfoContainer: Phaser.GameObjects.Container; private typeIcon: Phaser.GameObjects.Sprite; @@ -35,7 +37,7 @@ export default class FightUiHandler extends UiHandler { const ui = this.getUi(); this.movesContainer = this.scene.add.container(18, -38.7); - this.movesContainer.setName("moves"); + this.movesContainer.setName(FightUiHandler.MOVES_CONTAINER_NAME); ui.add(this.movesContainer); this.moveInfoContainer = this.scene.add.container(1, 0); @@ -271,11 +273,10 @@ export default class FightUiHandler extends UiHandler { return undefined; } - const moveColors = opponents.map((opponent) => { - return opponent.getMoveEffectiveness(pokemon, pokemonMove); - }).filter((eff) => !!eff).sort((a, b) => b - a).map((effectiveness) => { - return getTypeDamageMultiplierColor(effectiveness, "offense"); - }); + const moveColors = opponents + .map((opponent) => opponent.getMoveEffectiveness(pokemon, pokemonMove)) + .sort((a, b) => b - a) + .map((effectiveness) => getTypeDamageMultiplierColor(effectiveness ?? 0, "offense")); return moveColors[0]; } diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index 016708027ca..bb1f970fe1c 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -12,6 +12,7 @@ import { allMoves } from "../data/move"; import * as Utils from "./../utils"; import Overrides from "#app/overrides"; import i18next from "i18next"; +import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; export const SHOP_OPTIONS_ROW_LIMIT = 6; @@ -249,11 +250,22 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { duration: 250 }); - this.setCursor(0); - this.setRowCursor(1); + const updateCursorTarget = () => { + if (this.scene.shopCursorTarget === ShopCursorTarget.CHECK_TEAM) { + this.setRowCursor(0); + this.setCursor(2); + } else { + this.setRowCursor(this.scene.shopCursorTarget); + this.setCursor(0); + } + }; - handleTutorial(this.scene, Tutorial.Select_Item).then(() => { - this.setCursor(0); + updateCursorTarget(); + + handleTutorial(this.scene, Tutorial.Select_Item).then((res) => { + if (res) { + updateCursorTarget(); + } this.awaitingActionInput = true; this.onActionInput = args[2]; }); diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 5e1ca7ccbb0..66c777944d1 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -1,4 +1,3 @@ -import { CommandPhase, SelectModifierPhase } from "../phases"; import BattleScene from "../battle-scene"; import Pokemon, { MoveResult, PlayerPokemon, PokemonMove } from "../field/pokemon"; import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "./text"; @@ -23,6 +22,8 @@ import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { getPokemonNameWithAffix } from "#app/messages.js"; +import { CommandPhase } from "#app/phases/command-phase.js"; +import { SelectModifierPhase } from "#app/phases/select-modifier-phase.js"; const defaultMessage = i18next.t("partyUiHandler:choosePokemon"); diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 03a13e7661a..bc809d8c686 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -13,10 +13,9 @@ import { allMoves } from "../data/move"; import { Nature, getNatureName } from "../data/nature"; import { pokemonFormChanges } from "../data/pokemon-forms"; import { LevelMoves, pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "../data/pokemon-level-moves"; -import PokemonSpecies, { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species"; +import PokemonSpecies, { allSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities, getPokerusStarters } from "../data/pokemon-species"; import { Type } from "../data/type"; import { GameModes } from "../game-mode"; -import { SelectChallengePhase, TitlePhase } from "../phases"; import { AbilityAttr, DexAttr, DexAttrProps, DexEntry, StarterMoveset, StarterAttributes, StarterPreferences, StarterPrefs } from "../system/game-data"; import { Tutorial, handleTutorial } from "../tutorial"; import * as Utils from "../utils"; @@ -44,6 +43,8 @@ import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType } import { StarterContainer } from "./starter-container"; import { DropDownColumn, FilterBar } from "./filter-bar"; import { ScrollBar } from "./scroll-bar"; +import { SelectChallengePhase } from "#app/phases/select-challenge-phase.js"; +import { TitlePhase } from "#app/phases/title-phase.js"; export type StarterSelectCallback = (starters: Starter[]) => void; @@ -871,38 +872,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.message.setOrigin(0, 0); this.starterSelectMessageBoxContainer.add(this.message); - const date = new Date(); - date.setUTCHours(0, 0, 0, 0); - - this.scene.executeWithSeedOffset(() => { - for (let c = 0; c < 3; c++) { - let randomSpeciesId: Species; - let species: PokemonSpecies | undefined; - - const generateSpecies = () => { - randomSpeciesId = Utils.randSeedItem(starterSpecies); - species = getPokemonSpecies(randomSpeciesId); - }; - - let dupe = false; - - do { - dupe = false; - - generateSpecies(); - - for (let ps = 0; ps < c; ps++) { - if (this.pokerusSpecies[ps] === species) { - dupe = true; - break; - } - } - } while (dupe); - - this.pokerusSpecies.push(species!); // TODO: is the bang correct? - } - }, 0, date.getTime().toString()); - this.statsContainer = new StatsContainer(this.scene, 6, 16); this.scene.add.existing(this.statsContainer); @@ -933,6 +902,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.starterPreferences = StarterPrefs.load(); } this.moveInfoOverlay.clear(); // clear this when removing a menu; the cancel button doesn't seem to trigger this automatically on controllers + this.pokerusSpecies = getPokerusStarters(this.scene); + if (args.length >= 1 && args[0] instanceof Function) { super.show(args); this.starterSelectCallback = args[0] as StarterSelectCallback; @@ -2295,13 +2266,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { container.cost = this.scene.gameData.getSpeciesStarterValue(container.species.speciesId); // First, ensure you have the caught attributes for the species else default to bigint 0 - const caughtVariants = this.scene.gameData.dexData[container.species.speciesId]?.caughtAttr || BigInt(0); + const isCaught = this.scene.gameData.dexData[container.species.speciesId]?.caughtAttr || BigInt(0); // Define the variables based on whether their respective variants have been caught - const isVariant3Caught = !!(caughtVariants & DexAttr.VARIANT_3); - const isVariant2Caught = !!(caughtVariants & DexAttr.VARIANT_2); - const isVariantCaught = !!(caughtVariants & DexAttr.SHINY); - const isCaught = !!(caughtVariants & DexAttr.NON_SHINY); + const isVariant3Caught = !!(isCaught & DexAttr.VARIANT_3); + const isVariant2Caught = !!(isCaught & DexAttr.VARIANT_2); + const isVariantCaught = !!(isCaught & DexAttr.SHINY); const isUncaught = !isCaught && !isVariantCaught && !isVariant2Caught && !isVariant3Caught; const isPassiveUnlocked = this.scene.gameData.starterData[container.species.speciesId].passiveAttr > 0; const isPassiveUnlockable = this.isPassiveAvailable(container.species.speciesId) && !isPassiveUnlocked; @@ -2913,10 +2883,22 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (species) { const dexEntry = this.scene.gameData.dexData[species.speciesId]; const abilityAttr = this.scene.gameData.starterData[species.speciesId].abilityAttr; + + const isCaught = this.scene.gameData.dexData[species.speciesId]?.caughtAttr || BigInt(0); + const isVariant3Caught = !!(isCaught & DexAttr.VARIANT_3); + const isVariant2Caught = !!(isCaught & DexAttr.VARIANT_2); + const isDefaultVariantCaught = !!(isCaught & DexAttr.DEFAULT_VARIANT); + const isVariantCaught = !!(isCaught & DexAttr.SHINY); + const isMaleCaught = !!(isCaught & DexAttr.MALE); + const isFemaleCaught = !!(isCaught & DexAttr.FEMALE); + + const starterAttributes = this.starterPreferences[species.speciesId]; + + const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)); + const defaultAbilityIndex = this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species); + const defaultNature = this.scene.gameData.getSpeciesDefaultNature(species); + if (!dexEntry.caughtAttr) { - const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)); - const defaultAbilityIndex = this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species); - const defaultNature = this.scene.gameData.getSpeciesDefaultNature(species); if (shiny === undefined || shiny !== props.shiny) { shiny = props.shiny; } @@ -2935,6 +2917,83 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (natureIndex === undefined || natureIndex !== defaultNature) { natureIndex = defaultNature; } + } else { + // compare current shiny, formIndex, female, variant, abilityIndex, natureIndex with the caught ones + // if the current ones are not caught, we need to find the next caught ones + if (shiny) { + if (!(isVariantCaught || isVariant2Caught || isVariant3Caught)) { + shiny = false; + starterAttributes.shiny = false; + variant = 0; + starterAttributes.variant = 0; + } else { + shiny = true; + starterAttributes.shiny = true; + if (variant === 0 && !isDefaultVariantCaught) { + if (isVariant2Caught) { + variant = 1; + starterAttributes.variant = 1; + } else if (isVariant3Caught) { + variant = 2; + starterAttributes.variant = 2; + } else { + variant = 0; + starterAttributes.variant = 0; + } + } else if (variant === 1 && !isVariant2Caught) { + if (isVariantCaught) { + variant = 0; + starterAttributes.variant = 0; + } else if (isVariant3Caught) { + variant = 2; + starterAttributes.variant = 2; + } else { + variant = 0; + starterAttributes.variant = 0; + } + } else if (variant === 2 && !isVariant3Caught) { + if (isVariantCaught) { + variant = 0; + starterAttributes.variant = 0; + } else if (isVariant2Caught) { + variant = 1; + starterAttributes.variant = 1; + } else { + variant = 0; + starterAttributes.variant = 0; + } + } + } + } + if (female) { + if (!isFemaleCaught) { + female = false; + starterAttributes.female = false; + } + } else { + if (!isMaleCaught) { + female = true; + starterAttributes.female = true; + } + } + + if (species.forms) { + const formCount = species.forms.length; + let newFormIndex = formIndex??0; + if (species.forms[newFormIndex]) { + const isValidForm = species.forms[newFormIndex].isStarterSelectable && dexEntry.caughtAttr & this.scene.gameData.getFormAttr(newFormIndex); + if (!isValidForm) { + do { + newFormIndex = (newFormIndex + 1) % formCount; + if (species.forms[newFormIndex].isStarterSelectable && dexEntry.caughtAttr & this.scene.gameData.getFormAttr(newFormIndex)) { + break; + } + } while (newFormIndex !== props.formIndex); + formIndex = newFormIndex; + starterAttributes.form = formIndex; + } + } + } } this.shinyOverlay.setVisible(shiny ?? false); // TODO: is false the correct default? @@ -2975,8 +3034,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler { starterSprite.setTexture(species.getIconAtlasKey(formIndex, shiny, variant), species.getIconId(female!, formIndex, shiny, variant)); currentFilteredContainer.checkIconId(female, formIndex, shiny, variant); } - this.canCycleShiny = !!(dexEntry.caughtAttr & DexAttr.NON_SHINY && dexEntry.caughtAttr & DexAttr.SHINY); - this.canCycleGender = !!(dexEntry.caughtAttr & DexAttr.MALE && dexEntry.caughtAttr & DexAttr.FEMALE); + + this.canCycleShiny = isVariantCaught || isVariant2Caught || isVariant3Caught; + this.canCycleGender = isMaleCaught && isFemaleCaught; this.canCycleAbility = [ abilityAttr & AbilityAttr.ABILITY_1, (abilityAttr & AbilityAttr.ABILITY_2) && species.ability2, abilityAttr & AbilityAttr.ABILITY_HIDDEN ].filter(a => a).length > 1; this.canCycleForm = species.forms.filter(f => f.isStarterSelectable || !pokemonFormChanges[species.speciesId]?.find(fc => fc.formKey)) .map((_, f) => dexEntry.caughtAttr & this.scene.gameData.getFormAttr(f)).filter(f => f).length > 1; @@ -3466,7 +3526,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { checkIconId(icon: Phaser.GameObjects.Sprite, species: PokemonSpecies, female: boolean, formIndex: number, shiny: boolean, variant: number) { if (icon.frame.name !== species.getIconId(female, formIndex, shiny, variant)) { - console.log(`${species.name}'s variant icon does not exist. Replacing with default.`); + console.log(`${species.name}'s icon ${icon.frame.name} does not match getIconId with female: ${female}, formIndex: ${formIndex}, shiny: ${shiny}, variant: ${variant}`); icon.setTexture(species.getIconAtlasKey(formIndex, false, variant)); icon.setFrame(species.getIconId(female, formIndex, false, variant)); } diff --git a/src/ui/target-select-ui-handler.ts b/src/ui/target-select-ui-handler.ts index 42c7fef5660..6ca580dc2b2 100644 --- a/src/ui/target-select-ui-handler.ts +++ b/src/ui/target-select-ui-handler.ts @@ -118,7 +118,7 @@ export default class TargetSelectUiHandler extends UiHandler { this.targetFlashTween = this.scene.tweens.add({ targets: this.targetsHighlighted, - key: { start: 0.55, to: 1 }, + key: { start: 1, to: 0.25 }, loop: -1, loopDelay: 150, duration: Utils.fixedInt(450), diff --git a/src/utils.ts b/src/utils.ts index aa45c091286..a9bbc93d684 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -552,3 +552,25 @@ export function capitalizeString(str: string, sep: string, lowerFirstChar: boole } return null; } + +/** + * Returns if an object is null or undefined + * @param object + */ +export function isNullOrUndefined(object: any): boolean { + return null === object || undefined === object; +} + +/** + * This function is used in the context of a Pokémon battle game to calculate the actual integer damage value from a float result. + * Many damage calculation formulas involve various parameters and result in float values. + * The actual damage applied to a Pokémon's HP must be an integer. + * This function helps in ensuring that by flooring the float value and enforcing a minimum damage value. + * + * @param value - The float value to convert. + * @param minValue - The minimum integer value to return. Defaults to 1. + * @returns The converted value as an integer. + */ +export function toDmgValue(value: number, minValue: number = 1) { + return Math.max(Math.floor(value), minValue); +}