From f6770b83b589a6769a84a41ae4dadb57034f311e Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Fri, 12 Apr 2024 19:31:08 -0400 Subject: [PATCH 01/51] Add missing passive parameter to BonusCritAbAttr --- src/data/ability.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 66d286a8f36..7478026380c 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1180,7 +1180,7 @@ export class BlockCritAbAttr extends AbAttr { } export class BonusCritAbAttr extends AbAttr { - apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.BooleanHolder).value = true; return true; } From 40cdd6ace298c89b48f47b0c453df4f2095d6367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Santos?= Date: Sat, 13 Apr 2024 00:11:44 +0200 Subject: [PATCH 02/51] Fix PWA setup --- index.html | 6 ++++-- logo.png => public/logo128.png | Bin logo512.png => public/logo512.png | Bin manifest.json => public/manifest.webmanifest | 13 +++---------- 4 files changed, 7 insertions(+), 12 deletions(-) rename logo.png => public/logo128.png (100%) rename logo512.png => public/logo512.png (100%) rename manifest.json => public/manifest.webmanifest (68%) diff --git a/index.html b/index.html index 4d3fe513570..40b52ffe739 100644 --- a/index.html +++ b/index.html @@ -8,7 +8,9 @@ - + + + - + From 824a73ef7113cded8a2de005f2d88e40e581bebf Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sat, 13 Apr 2024 18:59:58 -0400 Subject: [PATCH 11/51] Add candy system for passives and value reductions --- public/{exp_sprites.json => exp-sprites.json} | 0 public/images/items.json | 3297 +++++++++-------- public/images/items.png | Bin 46843 -> 46890 bytes public/images/items/candy.png | Bin 0 -> 402 bytes public/images/items/candy_overlay.png | Bin 0 -> 263 bytes public/images/items/mystery_egg.png | Bin 0 -> 252 bytes public/starter-colors.json | 1 + src/battle-scene.ts | 70 +- src/data/daily-run.ts | 2 + src/data/pokemon-species.ts | 118 +- src/debug.js | 2 +- src/field/pokemon.ts | 16 +- src/phases.ts | 53 +- src/system/game-data.ts | 171 +- src/system/pokemon-data.ts | 4 + src/ui/abstact-option-select-ui-handler.ts | 53 +- src/ui/candy-bar.ts | 123 + src/ui/confirm-ui-handler.ts | 10 +- src/ui/menu-ui-handler.ts | 47 +- src/ui/starter-select-ui-handler.ts | 359 +- src/ui/text.ts | 9 +- src/utils.ts | 10 + 22 files changed, 2497 insertions(+), 1848 deletions(-) rename public/{exp_sprites.json => exp-sprites.json} (100%) create mode 100644 public/images/items/candy.png create mode 100644 public/images/items/candy_overlay.png create mode 100644 public/images/items/mystery_egg.png create mode 100644 public/starter-colors.json create mode 100644 src/ui/candy-bar.ts diff --git a/public/exp_sprites.json b/public/exp-sprites.json similarity index 100% rename from public/exp_sprites.json rename to public/exp-sprites.json diff --git a/public/images/items.json b/public/images/items.json index 40265ec6955..3d247c554f8 100644 --- a/public/images/items.json +++ b/public/images/items.json @@ -51,6 +51,27 @@ "h": 14 } }, + { + "filename": "candy_overlay", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 12, + "w": 16, + "h": 15 + }, + "frame": { + "x": 39, + "y": 0, + "w": 16, + "h": 15 + } + }, { "filename": "prism_scale", "rotated": false, @@ -66,7 +87,7 @@ "h": 15 }, "frame": { - "x": 39, + "x": 55, "y": 0, "w": 15, "h": 15 @@ -87,7 +108,7 @@ "h": 15 }, "frame": { - "x": 54, + "x": 70, "y": 0, "w": 24, "h": 15 @@ -108,7 +129,7 @@ "h": 16 }, "frame": { - "x": 78, + "x": 94, "y": 0, "w": 16, "h": 16 @@ -129,7 +150,7 @@ "h": 16 }, "frame": { - "x": 94, + "x": 110, "y": 0, "w": 16, "h": 16 @@ -150,7 +171,7 @@ "h": 16 }, "frame": { - "x": 110, + "x": 126, "y": 0, "w": 16, "h": 16 @@ -171,7 +192,7 @@ "h": 16 }, "frame": { - "x": 126, + "x": 142, "y": 0, "w": 16, "h": 16 @@ -192,7 +213,7 @@ "h": 16 }, "frame": { - "x": 142, + "x": 158, "y": 0, "w": 16, "h": 16 @@ -213,7 +234,7 @@ "h": 16 }, "frame": { - "x": 158, + "x": 174, "y": 0, "w": 16, "h": 16 @@ -234,7 +255,7 @@ "h": 16 }, "frame": { - "x": 174, + "x": 190, "y": 0, "w": 16, "h": 16 @@ -255,7 +276,7 @@ "h": 16 }, "frame": { - "x": 190, + "x": 206, "y": 0, "w": 16, "h": 16 @@ -276,7 +297,7 @@ "h": 16 }, "frame": { - "x": 206, + "x": 222, "y": 0, "w": 16, "h": 16 @@ -297,7 +318,7 @@ "h": 16 }, "frame": { - "x": 222, + "x": 238, "y": 0, "w": 16, "h": 16 @@ -318,7 +339,7 @@ "h": 16 }, "frame": { - "x": 238, + "x": 254, "y": 0, "w": 16, "h": 16 @@ -339,7 +360,7 @@ "h": 16 }, "frame": { - "x": 254, + "x": 270, "y": 0, "w": 16, "h": 16 @@ -360,7 +381,7 @@ "h": 16 }, "frame": { - "x": 270, + "x": 286, "y": 0, "w": 16, "h": 16 @@ -381,7 +402,7 @@ "h": 16 }, "frame": { - "x": 286, + "x": 302, "y": 0, "w": 16, "h": 16 @@ -402,7 +423,7 @@ "h": 16 }, "frame": { - "x": 302, + "x": 318, "y": 0, "w": 16, "h": 16 @@ -423,7 +444,7 @@ "h": 16 }, "frame": { - "x": 318, + "x": 334, "y": 0, "w": 16, "h": 16 @@ -443,27 +464,6 @@ "w": 16, "h": 16 }, - "frame": { - "x": 334, - "y": 0, - "w": 16, - "h": 16 - } - }, - { - "filename": "garchompite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, "frame": { "x": 350, "y": 0, @@ -472,7 +472,7 @@ } }, { - "filename": "gardevoirite", + "filename": "garchompite", "rotated": false, "trimmed": true, "sourceSize": { @@ -514,7 +514,7 @@ } }, { - "filename": "gengarite", + "filename": "gardevoirite", "rotated": false, "trimmed": true, "sourceSize": { @@ -535,7 +535,7 @@ } }, { - "filename": "glalitite", + "filename": "gengarite", "rotated": false, "trimmed": true, "sourceSize": { @@ -556,7 +556,7 @@ } }, { - "filename": "gyaradosite", + "filename": "glalitite", "rotated": false, "trimmed": true, "sourceSize": { @@ -576,6 +576,27 @@ "h": 16 } }, + { + "filename": "gyaradosite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 60, + "y": 15, + "w": 16, + "h": 16 + } + }, { "filename": "heracronite", "rotated": false, @@ -591,7 +612,7 @@ "h": 16 }, "frame": { - "x": 60, + "x": 76, "y": 15, "w": 16, "h": 16 @@ -612,7 +633,7 @@ "h": 16 }, "frame": { - "x": 76, + "x": 92, "y": 16, "w": 16, "h": 16 @@ -633,7 +654,7 @@ "h": 16 }, "frame": { - "x": 92, + "x": 108, "y": 16, "w": 16, "h": 16 @@ -654,7 +675,7 @@ "h": 16 }, "frame": { - "x": 108, + "x": 124, "y": 16, "w": 16, "h": 16 @@ -675,7 +696,7 @@ "h": 16 }, "frame": { - "x": 124, + "x": 140, "y": 16, "w": 16, "h": 16 @@ -696,7 +717,7 @@ "h": 16 }, "frame": { - "x": 140, + "x": 156, "y": 16, "w": 16, "h": 16 @@ -717,7 +738,7 @@ "h": 16 }, "frame": { - "x": 156, + "x": 172, "y": 16, "w": 16, "h": 16 @@ -738,7 +759,7 @@ "h": 16 }, "frame": { - "x": 172, + "x": 188, "y": 16, "w": 16, "h": 16 @@ -759,7 +780,7 @@ "h": 16 }, "frame": { - "x": 188, + "x": 204, "y": 16, "w": 16, "h": 16 @@ -780,7 +801,7 @@ "h": 16 }, "frame": { - "x": 204, + "x": 220, "y": 16, "w": 16, "h": 16 @@ -801,7 +822,7 @@ "h": 16 }, "frame": { - "x": 220, + "x": 236, "y": 16, "w": 20, "h": 16 @@ -822,7 +843,7 @@ "h": 16 }, "frame": { - "x": 240, + "x": 256, "y": 16, "w": 16, "h": 16 @@ -843,7 +864,7 @@ "h": 16 }, "frame": { - "x": 256, + "x": 272, "y": 16, "w": 16, "h": 16 @@ -864,7 +885,7 @@ "h": 16 }, "frame": { - "x": 272, + "x": 288, "y": 16, "w": 16, "h": 16 @@ -885,7 +906,7 @@ "h": 16 }, "frame": { - "x": 288, + "x": 304, "y": 16, "w": 16, "h": 16 @@ -906,7 +927,7 @@ "h": 16 }, "frame": { - "x": 304, + "x": 320, "y": 16, "w": 16, "h": 16 @@ -927,7 +948,7 @@ "h": 16 }, "frame": { - "x": 320, + "x": 336, "y": 16, "w": 16, "h": 16 @@ -948,7 +969,7 @@ "h": 16 }, "frame": { - "x": 336, + "x": 352, "y": 16, "w": 16, "h": 16 @@ -969,7 +990,7 @@ "h": 16 }, "frame": { - "x": 352, + "x": 368, "y": 16, "w": 17, "h": 16 @@ -989,27 +1010,6 @@ "w": 16, "h": 16 }, - "frame": { - "x": 369, - "y": 16, - "w": 16, - "h": 16 - } - }, - { - "filename": "salamencite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, "frame": { "x": 0, "y": 30, @@ -1018,7 +1018,7 @@ } }, { - "filename": "sceptilite", + "filename": "salamencite", "rotated": false, "trimmed": true, "sourceSize": { @@ -1039,7 +1039,7 @@ } }, { - "filename": "scizorite", + "filename": "sceptilite", "rotated": false, "trimmed": true, "sourceSize": { @@ -1060,7 +1060,7 @@ } }, { - "filename": "sharpedonite", + "filename": "scizorite", "rotated": false, "trimmed": true, "sourceSize": { @@ -1081,7 +1081,7 @@ } }, { - "filename": "slowbronite", + "filename": "sharpedonite", "rotated": false, "trimmed": true, "sourceSize": { @@ -1096,6 +1096,27 @@ }, "frame": { "x": 64, + "y": 31, + "w": 16, + "h": 16 + } + }, + { + "filename": "slowbronite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 80, "y": 32, "w": 16, "h": 16 @@ -1116,7 +1137,7 @@ "h": 16 }, "frame": { - "x": 80, + "x": 96, "y": 32, "w": 16, "h": 16 @@ -1137,7 +1158,7 @@ "h": 16 }, "frame": { - "x": 96, + "x": 112, "y": 32, "w": 16, "h": 16 @@ -1158,7 +1179,7 @@ "h": 16 }, "frame": { - "x": 112, + "x": 128, "y": 32, "w": 16, "h": 16 @@ -1179,7 +1200,7 @@ "h": 16 }, "frame": { - "x": 128, + "x": 144, "y": 32, "w": 16, "h": 16 @@ -1200,7 +1221,7 @@ "h": 16 }, "frame": { - "x": 144, + "x": 160, "y": 32, "w": 16, "h": 16 @@ -1221,7 +1242,7 @@ "h": 16 }, "frame": { - "x": 160, + "x": 176, "y": 32, "w": 16, "h": 16 @@ -1242,7 +1263,7 @@ "h": 17 }, "frame": { - "x": 176, + "x": 192, "y": 32, "w": 23, "h": 17 @@ -1263,7 +1284,7 @@ "h": 17 }, "frame": { - "x": 199, + "x": 215, "y": 32, "w": 20, "h": 17 @@ -1284,12 +1305,33 @@ "h": 17 }, "frame": { - "x": 219, + "x": 235, "y": 32, "w": 23, "h": 17 } }, + { + "filename": "candy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 11, + "w": 18, + "h": 18 + }, + "frame": { + "x": 258, + "y": 32, + "w": 18, + "h": 18 + } + }, { "filename": "choice_specs", "rotated": false, @@ -1305,7 +1347,7 @@ "h": 18 }, "frame": { - "x": 242, + "x": 276, "y": 32, "w": 24, "h": 18 @@ -1326,7 +1368,7 @@ "h": 18 }, "frame": { - "x": 266, + "x": 300, "y": 32, "w": 18, "h": 18 @@ -1347,7 +1389,7 @@ "h": 18 }, "frame": { - "x": 284, + "x": 318, "y": 32, "w": 24, "h": 18 @@ -1368,7 +1410,7 @@ "h": 18 }, "frame": { - "x": 308, + "x": 342, "y": 32, "w": 18, "h": 18 @@ -1389,12 +1431,33 @@ "h": 18 }, "frame": { - "x": 326, + "x": 360, "y": 32, "w": 21, "h": 18 } }, + { + "filename": "mystery_egg", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 18 + }, + "frame": { + "x": 0, + "y": 46, + "w": 16, + "h": 18 + } + }, { "filename": "relic_crown", "rotated": false, @@ -1410,33 +1473,12 @@ "h": 18 }, "frame": { - "x": 347, - "y": 32, + "x": 16, + "y": 47, "w": 23, "h": 18 } }, - { - "filename": "wl_ability_urge", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 370, - "y": 32, - "w": 20, - "h": 18 - } - }, { "filename": "unremarkable_teacup", "rotated": false, @@ -1452,12 +1494,33 @@ "h": 18 }, "frame": { - "x": 0, + "x": 39, "y": 47, "w": 21, "h": 18 } }, + { + "filename": "wl_ability_urge", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 60, + "y": 47, + "w": 20, + "h": 18 + } + }, { "filename": "wl_antidote", "rotated": false, @@ -1473,8 +1536,8 @@ "h": 18 }, "frame": { - "x": 21, - "y": 47, + "x": 80, + "y": 48, "w": 20, "h": 18 } @@ -1494,8 +1557,8 @@ "h": 18 }, "frame": { - "x": 41, - "y": 47, + "x": 100, + "y": 48, "w": 20, "h": 18 } @@ -1515,7 +1578,7 @@ "h": 18 }, "frame": { - "x": 61, + "x": 120, "y": 48, "w": 20, "h": 18 @@ -1536,7 +1599,7 @@ "h": 18 }, "frame": { - "x": 81, + "x": 140, "y": 48, "w": 20, "h": 18 @@ -1557,7 +1620,7 @@ "h": 18 }, "frame": { - "x": 101, + "x": 160, "y": 48, "w": 20, "h": 18 @@ -1578,8 +1641,8 @@ "h": 18 }, "frame": { - "x": 121, - "y": 48, + "x": 180, + "y": 49, "w": 20, "h": 18 } @@ -1599,8 +1662,8 @@ "h": 18 }, "frame": { - "x": 141, - "y": 48, + "x": 200, + "y": 49, "w": 20, "h": 18 } @@ -1620,12 +1683,33 @@ "h": 18 }, "frame": { - "x": 161, + "x": 220, "y": 49, "w": 20, "h": 18 } }, + { + "filename": "oval_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 19 + }, + "frame": { + "x": 240, + "y": 49, + "w": 18, + "h": 19 + } + }, { "filename": "wl_full_restore", "rotated": false, @@ -1641,8 +1725,8 @@ "h": 18 }, "frame": { - "x": 181, - "y": 49, + "x": 258, + "y": 50, "w": 20, "h": 18 } @@ -1662,8 +1746,8 @@ "h": 18 }, "frame": { - "x": 201, - "y": 49, + "x": 278, + "y": 50, "w": 20, "h": 18 } @@ -1683,8 +1767,8 @@ "h": 18 }, "frame": { - "x": 221, - "y": 49, + "x": 298, + "y": 50, "w": 20, "h": 18 } @@ -1704,7 +1788,7 @@ "h": 18 }, "frame": { - "x": 241, + "x": 318, "y": 50, "w": 20, "h": 18 @@ -1725,7 +1809,7 @@ "h": 18 }, "frame": { - "x": 261, + "x": 338, "y": 50, "w": 20, "h": 18 @@ -1746,7 +1830,7 @@ "h": 18 }, "frame": { - "x": 281, + "x": 358, "y": 50, "w": 20, "h": 18 @@ -1766,90 +1850,6 @@ "w": 20, "h": 18 }, - "frame": { - "x": 301, - "y": 50, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_max_ether", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 321, - "y": 50, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_max_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 341, - "y": 50, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_max_revive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 361, - "y": 50, - "w": 20, - "h": 18 - } - }, - { - "filename": "wl_paralyze_heal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, "frame": { "x": 0, "y": 65, @@ -1858,7 +1858,7 @@ } }, { - "filename": "wl_potion", + "filename": "wl_max_ether", "rotated": false, "trimmed": true, "sourceSize": { @@ -1879,7 +1879,7 @@ } }, { - "filename": "wl_reset_urge", + "filename": "wl_max_potion", "rotated": false, "trimmed": true, "sourceSize": { @@ -1900,7 +1900,7 @@ } }, { - "filename": "wl_revive", + "filename": "wl_max_revive", "rotated": false, "trimmed": true, "sourceSize": { @@ -1915,13 +1915,13 @@ }, "frame": { "x": 60, - "y": 66, + "y": 65, "w": 20, "h": 18 } }, { - "filename": "wl_super_potion", + "filename": "wl_paralyze_heal", "rotated": false, "trimmed": true, "sourceSize": { @@ -1941,6 +1941,90 @@ "h": 18 } }, + { + "filename": "wl_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 100, + "y": 66, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_reset_urge", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 120, + "y": 66, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_revive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 140, + "y": 66, + "w": 20, + "h": 18 + } + }, + { + "filename": "wl_super_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 160, + "y": 66, + "w": 20, + "h": 18 + } + }, { "filename": "big_mushroom", "rotated": false, @@ -1956,8 +2040,8 @@ "h": 19 }, "frame": { - "x": 100, - "y": 66, + "x": 180, + "y": 67, "w": 19, "h": 19 } @@ -1977,14 +2061,14 @@ "h": 19 }, "frame": { - "x": 119, - "y": 66, + "x": 199, + "y": 67, "w": 22, "h": 19 } }, { - "filename": "lum_berry", + "filename": "miracle_seed", "rotated": false, "trimmed": true, "sourceSize": { @@ -1994,13 +2078,13 @@ "spriteSourceSize": { "x": 6, "y": 7, - "w": 20, + "w": 19, "h": 19 }, "frame": { - "x": 141, - "y": 66, - "w": 20, + "x": 221, + "y": 67, + "w": 19, "h": 19 } }, @@ -2019,8 +2103,8 @@ "h": 19 }, "frame": { - "x": 161, - "y": 67, + "x": 240, + "y": 68, "w": 23, "h": 19 } @@ -2040,8 +2124,8 @@ "h": 19 }, "frame": { - "x": 184, - "y": 67, + "x": 263, + "y": 68, "w": 22, "h": 19 } @@ -2061,12 +2145,33 @@ "h": 19 }, "frame": { - "x": 206, - "y": 67, + "x": 285, + "y": 68, "w": 23, "h": 19 } }, + { + "filename": "lum_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 308, + "y": 68, + "w": 20, + "h": 19 + } + }, { "filename": "metal_alloy", "rotated": false, @@ -2082,33 +2187,12 @@ "h": 19 }, "frame": { - "x": 229, + "x": 328, "y": 68, "w": 21, "h": 19 } }, - { - "filename": "miracle_seed", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 19, - "h": 19 - }, - "frame": { - "x": 250, - "y": 68, - "w": 19, - "h": 19 - } - }, { "filename": "mystic_ticket", "rotated": false, @@ -2124,14 +2208,14 @@ "h": 19 }, "frame": { - "x": 269, + "x": 349, "y": 68, "w": 23, "h": 19 } }, { - "filename": "oval_stone", + "filename": "golden_egg", "rotated": false, "trimmed": true, "sourceSize": { @@ -2140,15 +2224,15 @@ }, "spriteSourceSize": { "x": 7, - "y": 7, - "w": 18, - "h": 19 + "y": 6, + "w": 17, + "h": 20 }, "frame": { - "x": 292, + "x": 372, "y": 68, - "w": 18, - "h": 19 + "w": 17, + "h": 20 } }, { @@ -2166,8 +2250,8 @@ "h": 19 }, "frame": { - "x": 310, - "y": 68, + "x": 0, + "y": 83, "w": 23, "h": 19 } @@ -2187,8 +2271,8 @@ "h": 19 }, "frame": { - "x": 333, - "y": 68, + "x": 23, + "y": 83, "w": 20, "h": 19 } @@ -2208,33 +2292,12 @@ "h": 19 }, "frame": { - "x": 353, - "y": 68, + "x": 43, + "y": 83, "w": 22, "h": 19 } }, - { - "filename": "leftovers", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 15, - "h": 22 - }, - "frame": { - "x": 375, - "y": 68, - "w": 15, - "h": 22 - } - }, { "filename": "apicot_berry", "rotated": false, @@ -2250,8 +2313,8 @@ "h": 20 }, "frame": { - "x": 0, - "y": 83, + "x": 65, + "y": 84, "w": 19, "h": 20 } @@ -2271,29 +2334,8 @@ "h": 20 }, "frame": { - "x": 19, - "y": 83, - "w": 20, - "h": 20 - } - }, - { - "filename": "blue_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 39, - "y": 83, + "x": 84, + "y": 84, "w": 20, "h": 20 } @@ -2313,14 +2355,14 @@ "h": 20 }, "frame": { - "x": 59, + "x": 104, "y": 84, "w": 23, "h": 20 } }, { - "filename": "golden_egg", + "filename": "blue_orb", "rotated": false, "trimmed": true, "sourceSize": { @@ -2328,15 +2370,15 @@ "h": 32 }, "spriteSourceSize": { - "x": 7, + "x": 6, "y": 6, - "w": 17, + "w": 20, "h": 20 }, "frame": { - "x": 82, + "x": 127, "y": 84, - "w": 17, + "w": 20, "h": 20 } }, @@ -2355,8 +2397,8 @@ "h": 20 }, "frame": { - "x": 99, - "y": 85, + "x": 147, + "y": 84, "w": 19, "h": 20 } @@ -2376,33 +2418,12 @@ "h": 20 }, "frame": { - "x": 118, - "y": 85, + "x": 166, + "y": 86, "w": 26, "h": 20 } }, - { - "filename": "lucky_egg", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 17, - "h": 20 - }, - "frame": { - "x": 144, - "y": 85, - "w": 17, - "h": 20 - } - }, { "filename": "cracked_pot", "rotated": false, @@ -2418,7 +2439,7 @@ "h": 20 }, "frame": { - "x": 161, + "x": 192, "y": 86, "w": 26, "h": 20 @@ -2439,12 +2460,33 @@ "h": 20 }, "frame": { - "x": 187, + "x": 218, "y": 86, "w": 22, "h": 20 } }, + { + "filename": "fairy_feather", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 22, + "h": 20 + }, + "frame": { + "x": 240, + "y": 87, + "w": 22, + "h": 20 + } + }, { "filename": "gb", "rotated": false, @@ -2460,30 +2502,9 @@ "h": 20 }, "frame": { - "x": 209, - "y": 86, - "w": 20, - "h": 20 - } - }, - { - "filename": "fairy_feather", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 22, - "h": 20 - }, - "frame": { - "x": 229, + "x": 262, "y": 87, - "w": 22, + "w": 20, "h": 20 } }, @@ -2502,7 +2523,7 @@ "h": 20 }, "frame": { - "x": 251, + "x": 282, "y": 87, "w": 19, "h": 20 @@ -2523,12 +2544,33 @@ "h": 20 }, "frame": { - "x": 270, + "x": 301, "y": 87, "w": 24, "h": 20 } }, + { + "filename": "lucky_egg", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 17, + "h": 20 + }, + "frame": { + "x": 325, + "y": 87, + "w": 17, + "h": 20 + } + }, { "filename": "magnet", "rotated": false, @@ -2544,7 +2586,7 @@ "h": 20 }, "frame": { - "x": 294, + "x": 342, "y": 87, "w": 20, "h": 20 @@ -2565,8 +2607,8 @@ "h": 20 }, "frame": { - "x": 314, - "y": 87, + "x": 362, + "y": 88, "w": 22, "h": 20 } @@ -2586,54 +2628,12 @@ "h": 20 }, "frame": { - "x": 336, - "y": 87, + "x": 0, + "y": 102, "w": 20, "h": 20 } }, - { - "filename": "razor_fang", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 18, - "h": 20 - }, - "frame": { - "x": 356, - "y": 87, - "w": 18, - "h": 20 - } - }, - { - "filename": "full_heal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 9, - "y": 4, - "w": 15, - "h": 23 - }, - "frame": { - "x": 374, - "y": 90, - "w": 15, - "h": 23 - } - }, { "filename": "pb", "rotated": false, @@ -2649,8 +2649,8 @@ "h": 20 }, "frame": { - "x": 0, - "y": 103, + "x": 20, + "y": 102, "w": 20, "h": 20 } @@ -2670,12 +2670,33 @@ "h": 20 }, "frame": { - "x": 20, - "y": 103, + "x": 40, + "y": 102, "w": 20, "h": 20 } }, + { + "filename": "razor_fang", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 18, + "h": 20 + }, + "frame": { + "x": 60, + "y": 104, + "w": 18, + "h": 20 + } + }, { "filename": "rb", "rotated": false, @@ -2691,7 +2712,7 @@ "h": 20 }, "frame": { - "x": 40, + "x": 78, "y": 104, "w": 20, "h": 20 @@ -2712,7 +2733,7 @@ "h": 20 }, "frame": { - "x": 60, + "x": 98, "y": 104, "w": 23, "h": 20 @@ -2733,12 +2754,33 @@ "h": 20 }, "frame": { - "x": 83, - "y": 105, + "x": 121, + "y": 104, "w": 24, "h": 20 } }, + { + "filename": "strange_ball", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 145, + "y": 104, + "w": 20, + "h": 20 + } + }, { "filename": "sacred_ash", "rotated": false, @@ -2754,8 +2796,8 @@ "h": 20 }, "frame": { - "x": 107, - "y": 105, + "x": 165, + "y": 106, "w": 24, "h": 20 } @@ -2775,8 +2817,8 @@ "h": 20 }, "frame": { - "x": 131, - "y": 105, + "x": 189, + "y": 106, "w": 24, "h": 20 } @@ -2796,7 +2838,7 @@ "h": 20 }, "frame": { - "x": 155, + "x": 213, "y": 106, "w": 23, "h": 20 @@ -2817,33 +2859,12 @@ "h": 20 }, "frame": { - "x": 178, - "y": 106, + "x": 236, + "y": 107, "w": 24, "h": 20 } }, - { - "filename": "strange_ball", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 202, - "y": 106, - "w": 20, - "h": 20 - } - }, { "filename": "tera_orb", "rotated": false, @@ -2859,7 +2880,7 @@ "h": 20 }, "frame": { - "x": 222, + "x": 260, "y": 107, "w": 22, "h": 20 @@ -2880,7 +2901,7 @@ "h": 20 }, "frame": { - "x": 244, + "x": 282, "y": 107, "w": 20, "h": 20 @@ -2901,7 +2922,7 @@ "h": 21 }, "frame": { - "x": 264, + "x": 302, "y": 107, "w": 23, "h": 21 @@ -2922,7 +2943,7 @@ "h": 21 }, "frame": { - "x": 287, + "x": 325, "y": 107, "w": 23, "h": 21 @@ -2943,12 +2964,33 @@ "h": 21 }, "frame": { - "x": 310, - "y": 107, + "x": 348, + "y": 108, "w": 23, "h": 21 } }, + { + "filename": "spell_tag", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 19, + "h": 21 + }, + "frame": { + "x": 371, + "y": 108, + "w": 19, + "h": 21 + } + }, { "filename": "dawn_stone", "rotated": false, @@ -2964,54 +3006,12 @@ "h": 21 }, "frame": { - "x": 333, - "y": 107, + "x": 0, + "y": 122, "w": 20, "h": 21 } }, - { - "filename": "dusk_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 21, - "h": 21 - }, - "frame": { - "x": 353, - "y": 107, - "w": 21, - "h": 21 - } - }, - { - "filename": "calcium", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 374, - "y": 113, - "w": 16, - "h": 24 - } - }, { "filename": "deep_sea_tooth", "rotated": false, @@ -3027,8 +3027,8 @@ "h": 21 }, "frame": { - "x": 0, - "y": 123, + "x": 20, + "y": 122, "w": 22, "h": 21 } @@ -3048,12 +3048,33 @@ "h": 22 }, "frame": { - "x": 22, - "y": 123, + "x": 42, + "y": 122, "w": 18, "h": 22 } }, + { + "filename": "dusk_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 21, + "h": 21 + }, + "frame": { + "x": 60, + "y": 124, + "w": 21, + "h": 21 + } + }, { "filename": "liechi_berry", "rotated": false, @@ -3069,33 +3090,12 @@ "h": 21 }, "frame": { - "x": 40, + "x": 81, "y": 124, "w": 22, "h": 21 } }, - { - "filename": "poison_barb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 21, - "h": 21 - }, - "frame": { - "x": 62, - "y": 124, - "w": 21, - "h": 21 - } - }, { "filename": "mint_atk", "rotated": false, @@ -3111,8 +3111,8 @@ "h": 21 }, "frame": { - "x": 83, - "y": 125, + "x": 103, + "y": 124, "w": 28, "h": 21 } @@ -3132,8 +3132,8 @@ "h": 21 }, "frame": { - "x": 111, - "y": 125, + "x": 131, + "y": 124, "w": 28, "h": 21 } @@ -3153,7 +3153,7 @@ "h": 21 }, "frame": { - "x": 139, + "x": 159, "y": 126, "w": 28, "h": 21 @@ -3174,12 +3174,75 @@ "h": 21 }, "frame": { - "x": 167, + "x": 187, "y": 126, "w": 28, "h": 21 } }, + { + "filename": "poison_barb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 21, + "h": 21 + }, + "frame": { + "x": 215, + "y": 126, + "w": 21, + "h": 21 + } + }, + { + "filename": "mint_spd", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 28, + "h": 21 + }, + "frame": { + "x": 236, + "y": 127, + "w": 28, + "h": 21 + } + }, + { + "filename": "mint_spdef", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 2, + "y": 5, + "w": 28, + "h": 21 + }, + "frame": { + "x": 264, + "y": 127, + "w": 28, + "h": 21 + } + }, { "filename": "moon_stone", "rotated": false, @@ -3195,72 +3258,9 @@ "h": 21 }, "frame": { - "x": 195, - "y": 126, - "w": 23, - "h": 21 - } - }, - { - "filename": "mint_spd", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 28, - "h": 21 - }, - "frame": { - "x": 218, - "y": 127, - "w": 28, - "h": 21 - } - }, - { - "filename": "metronome", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 17, - "h": 22 - }, - "frame": { - "x": 246, - "y": 127, - "w": 17, - "h": 22 - } - }, - { - "filename": "mint_spdef", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 28, - "h": 21 - }, - "frame": { - "x": 263, + "x": 292, "y": 128, - "w": 28, + "w": 23, "h": 21 } }, @@ -3279,7 +3279,7 @@ "h": 21 }, "frame": { - "x": 291, + "x": 315, "y": 128, "w": 23, "h": 21 @@ -3300,8 +3300,8 @@ "h": 21 }, "frame": { - "x": 314, - "y": 128, + "x": 338, + "y": 129, "w": 23, "h": 21 } @@ -3321,75 +3321,12 @@ "h": 21 }, "frame": { - "x": 337, - "y": 128, + "x": 361, + "y": 129, "w": 21, "h": 21 } }, - { - "filename": "carbos", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 358, - "y": 128, - "w": 16, - "h": 24 - } - }, - { - "filename": "hp_up", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 374, - "y": 137, - "w": 16, - "h": 24 - } - }, - { - "filename": "spell_tag", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 19, - "h": 21 - }, - "frame": { - "x": 0, - "y": 144, - "w": 19, - "h": 21 - } - }, { "filename": "sweet_apple", "rotated": false, @@ -3405,12 +3342,33 @@ "h": 21 }, "frame": { - "x": 19, - "y": 145, + "x": 0, + "y": 143, "w": 22, "h": 21 } }, + { + "filename": "leftovers", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 15, + "h": 22 + }, + "frame": { + "x": 22, + "y": 143, + "w": 15, + "h": 22 + } + }, { "filename": "syrupy_apple", "rotated": false, @@ -3426,33 +3384,12 @@ "h": 21 }, "frame": { - "x": 41, - "y": 145, + "x": 37, + "y": 144, "w": 22, "h": 21 } }, - { - "filename": "lock_capsule", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 19, - "h": 22 - }, - "frame": { - "x": 63, - "y": 145, - "w": 19, - "h": 22 - } - }, { "filename": "tart_apple", "rotated": false, @@ -3468,8 +3405,8 @@ "h": 21 }, "frame": { - "x": 82, - "y": 146, + "x": 59, + "y": 145, "w": 22, "h": 21 } @@ -3489,8 +3426,8 @@ "h": 21 }, "frame": { - "x": 104, - "y": 146, + "x": 81, + "y": 145, "w": 23, "h": 21 } @@ -3510,8 +3447,8 @@ "h": 21 }, "frame": { - "x": 127, - "y": 147, + "x": 104, + "y": 145, "w": 21, "h": 21 } @@ -3531,8 +3468,8 @@ "h": 22 }, "frame": { - "x": 148, - "y": 147, + "x": 125, + "y": 145, "w": 22, "h": 22 } @@ -3552,7 +3489,7 @@ "h": 22 }, "frame": { - "x": 170, + "x": 147, "y": 147, "w": 22, "h": 22 @@ -3573,7 +3510,7 @@ "h": 22 }, "frame": { - "x": 192, + "x": 169, "y": 147, "w": 22, "h": 22 @@ -3594,8 +3531,8 @@ "h": 22 }, "frame": { - "x": 214, - "y": 148, + "x": 191, + "y": 147, "w": 22, "h": 22 } @@ -3615,8 +3552,8 @@ "h": 22 }, "frame": { - "x": 236, - "y": 149, + "x": 213, + "y": 147, "w": 22, "h": 22 } @@ -3636,8 +3573,8 @@ "h": 22 }, "frame": { - "x": 258, - "y": 149, + "x": 235, + "y": 148, "w": 24, "h": 22 } @@ -3657,8 +3594,8 @@ "h": 22 }, "frame": { - "x": 282, - "y": 149, + "x": 259, + "y": 148, "w": 24, "h": 22 } @@ -3678,7 +3615,7 @@ "h": 22 }, "frame": { - "x": 306, + "x": 283, "y": 149, "w": 22, "h": 22 @@ -3699,7 +3636,7 @@ "h": 22 }, "frame": { - "x": 328, + "x": 305, "y": 149, "w": 22, "h": 22 @@ -3720,54 +3657,12 @@ "h": 22 }, "frame": { - "x": 350, - "y": 152, + "x": 327, + "y": 150, "w": 23, "h": 22 } }, - { - "filename": "soothe_bell", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 22 - }, - "frame": { - "x": 373, - "y": 161, - "w": 17, - "h": 22 - } - }, - { - "filename": "metal_coat", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 19, - "h": 22 - }, - "frame": { - "x": 0, - "y": 165, - "w": 19, - "h": 22 - } - }, { "filename": "ice_stone", "rotated": false, @@ -3783,12 +3678,54 @@ "h": 22 }, "frame": { - "x": 19, - "y": 166, + "x": 350, + "y": 150, "w": 22, "h": 22 } }, + { + "filename": "metronome", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 5, + "w": 17, + "h": 22 + }, + "frame": { + "x": 372, + "y": 150, + "w": 17, + "h": 22 + } + }, + { + "filename": "lock_capsule", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 5, + "w": 19, + "h": 22 + }, + "frame": { + "x": 0, + "y": 164, + "w": 19, + "h": 22 + } + }, { "filename": "magmarizer", "rotated": false, @@ -3804,12 +3741,33 @@ "h": 22 }, "frame": { - "x": 41, - "y": 166, + "x": 19, + "y": 165, "w": 22, "h": 22 } }, + { + "filename": "soothe_bell", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 17, + "h": 22 + }, + "frame": { + "x": 41, + "y": 165, + "w": 17, + "h": 22 + } + }, { "filename": "map", "rotated": false, @@ -3825,8 +3783,8 @@ "h": 22 }, "frame": { - "x": 63, - "y": 167, + "x": 58, + "y": 166, "w": 27, "h": 22 } @@ -3846,12 +3804,33 @@ "h": 22 }, "frame": { - "x": 90, - "y": 167, + "x": 85, + "y": 166, "w": 22, "h": 22 } }, + { + "filename": "full_heal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 9, + "y": 4, + "w": 15, + "h": 23 + }, + "frame": { + "x": 107, + "y": 166, + "w": 15, + "h": 23 + } + }, { "filename": "memory_dark", "rotated": false, @@ -3867,8 +3846,8 @@ "h": 22 }, "frame": { - "x": 112, - "y": 168, + "x": 122, + "y": 167, "w": 22, "h": 22 } @@ -3888,7 +3867,7 @@ "h": 22 }, "frame": { - "x": 134, + "x": 144, "y": 169, "w": 22, "h": 22 @@ -3909,7 +3888,7 @@ "h": 22 }, "frame": { - "x": 156, + "x": 166, "y": 169, "w": 22, "h": 22 @@ -3930,7 +3909,7 @@ "h": 22 }, "frame": { - "x": 178, + "x": 188, "y": 169, "w": 22, "h": 22 @@ -3951,8 +3930,8 @@ "h": 22 }, "frame": { - "x": 200, - "y": 170, + "x": 210, + "y": 169, "w": 22, "h": 22 } @@ -3972,8 +3951,8 @@ "h": 22 }, "frame": { - "x": 222, - "y": 171, + "x": 232, + "y": 170, "w": 22, "h": 22 } @@ -3993,8 +3972,8 @@ "h": 22 }, "frame": { - "x": 244, - "y": 171, + "x": 254, + "y": 170, "w": 22, "h": 22 } @@ -4014,7 +3993,7 @@ "h": 22 }, "frame": { - "x": 266, + "x": 276, "y": 171, "w": 22, "h": 22 @@ -4035,7 +4014,7 @@ "h": 22 }, "frame": { - "x": 288, + "x": 298, "y": 171, "w": 22, "h": 22 @@ -4056,33 +4035,12 @@ "h": 22 }, "frame": { - "x": 310, - "y": 171, + "x": 320, + "y": 172, "w": 22, "h": 22 } }, - { - "filename": "hyper_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 23 - }, - "frame": { - "x": 332, - "y": 171, - "w": 17, - "h": 23 - } - }, { "filename": "memory_ice", "rotated": false, @@ -4098,33 +4056,12 @@ "h": 22 }, "frame": { - "x": 349, - "y": 174, + "x": 342, + "y": 172, "w": 22, "h": 22 } }, - { - "filename": "potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 23 - }, - "frame": { - "x": 371, - "y": 183, - "w": 17, - "h": 23 - } - }, { "filename": "memory_normal", "rotated": false, @@ -4140,8 +4077,8 @@ "h": 22 }, "frame": { - "x": 0, - "y": 188, + "x": 364, + "y": 172, "w": 22, "h": 22 } @@ -4161,12 +4098,33 @@ "h": 22 }, "frame": { - "x": 0, - "y": 188, + "x": 364, + "y": 172, "w": 22, "h": 22 } }, + { + "filename": "metal_coat", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 19, + "h": 22 + }, + "frame": { + "x": 0, + "y": 186, + "w": 19, + "h": 22 + } + }, { "filename": "memory_poison", "rotated": false, @@ -4182,12 +4140,33 @@ "h": 22 }, "frame": { - "x": 22, - "y": 188, + "x": 19, + "y": 187, "w": 22, "h": 22 } }, + { + "filename": "hyper_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 17, + "h": 23 + }, + "frame": { + "x": 41, + "y": 187, + "w": 17, + "h": 23 + } + }, { "filename": "memory_psychic", "rotated": false, @@ -4203,8 +4182,8 @@ "h": 22 }, "frame": { - "x": 44, - "y": 189, + "x": 58, + "y": 188, "w": 22, "h": 22 } @@ -4224,8 +4203,8 @@ "h": 22 }, "frame": { - "x": 66, - "y": 189, + "x": 80, + "y": 188, "w": 22, "h": 22 } @@ -4245,12 +4224,33 @@ "h": 22 }, "frame": { - "x": 88, + "x": 102, "y": 189, "w": 22, "h": 22 } }, + { + "filename": "sitrus_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 20, + "h": 22 + }, + "frame": { + "x": 124, + "y": 189, + "w": 20, + "h": 22 + } + }, { "filename": "memory_water", "rotated": false, @@ -4266,8 +4266,8 @@ "h": 22 }, "frame": { - "x": 110, - "y": 190, + "x": 144, + "y": 191, "w": 22, "h": 22 } @@ -4287,7 +4287,7 @@ "h": 22 }, "frame": { - "x": 132, + "x": 166, "y": 191, "w": 22, "h": 22 @@ -4308,12 +4308,33 @@ "h": 22 }, "frame": { - "x": 154, + "x": 188, "y": 191, "w": 24, "h": 22 } }, + { + "filename": "mystic_water", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 20, + "h": 23 + }, + "frame": { + "x": 212, + "y": 191, + "w": 20, + "h": 23 + } + }, { "filename": "protector", "rotated": false, @@ -4329,8 +4350,8 @@ "h": 22 }, "frame": { - "x": 178, - "y": 191, + "x": 232, + "y": 192, "w": 22, "h": 22 } @@ -4350,7 +4371,7 @@ "h": 22 }, "frame": { - "x": 200, + "x": 254, "y": 192, "w": 22, "h": 22 @@ -4371,12 +4392,33 @@ "h": 22 }, "frame": { - "x": 222, + "x": 276, "y": 193, "w": 23, "h": 22 } }, + { + "filename": "dragon_fang", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 21, + "h": 23 + }, + "frame": { + "x": 299, + "y": 193, + "w": 21, + "h": 23 + } + }, { "filename": "scroll_of_waters", "rotated": false, @@ -4392,8 +4434,8 @@ "h": 22 }, "frame": { - "x": 245, - "y": 193, + "x": 320, + "y": 194, "w": 22, "h": 22 } @@ -4413,33 +4455,12 @@ "h": 22 }, "frame": { - "x": 267, - "y": 193, + "x": 342, + "y": 194, "w": 22, "h": 22 } }, - { - "filename": "sitrus_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 20, - "h": 22 - }, - "frame": { - "x": 289, - "y": 193, - "w": 20, - "h": 22 - } - }, { "filename": "starf_berry", "rotated": false, @@ -4455,14 +4476,14 @@ "h": 22 }, "frame": { - "x": 309, - "y": 193, + "x": 364, + "y": 194, "w": 22, "h": 22 } }, { - "filename": "sachet", + "filename": "potion", "rotated": false, "trimmed": true, "sourceSize": { @@ -4470,15 +4491,15 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 18, + "x": 8, + "y": 5, + "w": 17, "h": 23 }, "frame": { - "x": 331, - "y": 194, - "w": 18, + "x": 0, + "y": 208, + "w": 17, "h": 23 } }, @@ -4497,33 +4518,12 @@ "h": 22 }, "frame": { - "x": 349, - "y": 196, + "x": 17, + "y": 209, "w": 22, "h": 22 } }, - { - "filename": "super_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 23 - }, - "frame": { - "x": 371, - "y": 206, - "w": 17, - "h": 23 - } - }, { "filename": "tm_bug", "rotated": false, @@ -4539,7 +4539,7 @@ "h": 22 }, "frame": { - "x": 0, + "x": 39, "y": 210, "w": 22, "h": 22 @@ -4560,12 +4560,33 @@ "h": 22 }, "frame": { - "x": 22, + "x": 61, "y": 210, "w": 22, "h": 22 } }, + { + "filename": "sachet", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 18, + "h": 23 + }, + "frame": { + "x": 83, + "y": 210, + "w": 18, + "h": 23 + } + }, { "filename": "tm_dragon", "rotated": false, @@ -4581,12 +4602,33 @@ "h": 22 }, "frame": { - "x": 44, + "x": 101, "y": 211, "w": 22, "h": 22 } }, + { + "filename": "lansat_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 21, + "h": 23 + }, + "frame": { + "x": 123, + "y": 211, + "w": 21, + "h": 23 + } + }, { "filename": "tm_electric", "rotated": false, @@ -4602,8 +4644,8 @@ "h": 22 }, "frame": { - "x": 66, - "y": 211, + "x": 144, + "y": 213, "w": 22, "h": 22 } @@ -4623,8 +4665,8 @@ "h": 22 }, "frame": { - "x": 88, - "y": 211, + "x": 166, + "y": 213, "w": 22, "h": 22 } @@ -4644,8 +4686,8 @@ "h": 22 }, "frame": { - "x": 110, - "y": 212, + "x": 188, + "y": 213, "w": 22, "h": 22 } @@ -4665,8 +4707,8 @@ "h": 22 }, "frame": { - "x": 132, - "y": 213, + "x": 210, + "y": 214, "w": 22, "h": 22 } @@ -4686,8 +4728,8 @@ "h": 22 }, "frame": { - "x": 154, - "y": 213, + "x": 232, + "y": 214, "w": 22, "h": 22 } @@ -4707,8 +4749,8 @@ "h": 22 }, "frame": { - "x": 176, - "y": 213, + "x": 254, + "y": 214, "w": 22, "h": 22 } @@ -4728,8 +4770,8 @@ "h": 22 }, "frame": { - "x": 198, - "y": 214, + "x": 276, + "y": 215, "w": 22, "h": 22 } @@ -4749,8 +4791,8 @@ "h": 22 }, "frame": { - "x": 220, - "y": 215, + "x": 298, + "y": 216, "w": 22, "h": 22 } @@ -4770,8 +4812,8 @@ "h": 22 }, "frame": { - "x": 242, - "y": 215, + "x": 320, + "y": 216, "w": 22, "h": 22 } @@ -4791,8 +4833,8 @@ "h": 22 }, "frame": { - "x": 264, - "y": 215, + "x": 342, + "y": 216, "w": 22, "h": 22 } @@ -4812,8 +4854,8 @@ "h": 22 }, "frame": { - "x": 286, - "y": 215, + "x": 364, + "y": 216, "w": 22, "h": 22 } @@ -4833,12 +4875,33 @@ "h": 22 }, "frame": { - "x": 308, - "y": 215, + "x": 0, + "y": 231, "w": 22, "h": 22 } }, + { + "filename": "super_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 17, + "h": 23 + }, + "frame": { + "x": 22, + "y": 231, + "w": 17, + "h": 23 + } + }, { "filename": "tm_steel", "rotated": false, @@ -4854,54 +4917,12 @@ "h": 22 }, "frame": { - "x": 330, - "y": 218, + "x": 39, + "y": 232, "w": 22, "h": 22 } }, - { - "filename": "elixir", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 352, - "y": 218, - "w": 18, - "h": 24 - } - }, - { - "filename": "mystic_water", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 20, - "h": 23 - }, - "frame": { - "x": 370, - "y": 229, - "w": 20, - "h": 23 - } - }, { "filename": "tm_water", "rotated": false, @@ -4917,7 +4938,7 @@ "h": 22 }, "frame": { - "x": 0, + "x": 61, "y": 232, "w": 22, "h": 22 @@ -4938,8 +4959,8 @@ "h": 22 }, "frame": { - "x": 22, - "y": 232, + "x": 83, + "y": 233, "w": 22, "h": 22 } @@ -4959,8 +4980,8 @@ "h": 22 }, "frame": { - "x": 44, - "y": 233, + "x": 105, + "y": 234, "w": 22, "h": 22 } @@ -4980,8 +5001,8 @@ "h": 22 }, "frame": { - "x": 66, - "y": 233, + "x": 127, + "y": 235, "w": 22, "h": 22 } @@ -5001,8 +5022,8 @@ "h": 22 }, "frame": { - "x": 88, - "y": 233, + "x": 149, + "y": 235, "w": 22, "h": 22 } @@ -5022,8 +5043,8 @@ "h": 22 }, "frame": { - "x": 110, - "y": 234, + "x": 171, + "y": 235, "w": 22, "h": 22 } @@ -5043,8 +5064,8 @@ "h": 22 }, "frame": { - "x": 132, - "y": 235, + "x": 193, + "y": 236, "w": 22, "h": 22 } @@ -5064,12 +5085,33 @@ "h": 22 }, "frame": { - "x": 154, - "y": 235, + "x": 215, + "y": 236, "w": 22, "h": 22 } }, + { + "filename": "berry_pouch", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 23, + "h": 23 + }, + "frame": { + "x": 237, + "y": 236, + "w": 23, + "h": 23 + } + }, { "filename": "black_belt", "rotated": false, @@ -5085,8 +5127,8 @@ "h": 23 }, "frame": { - "x": 176, - "y": 235, + "x": 260, + "y": 237, "w": 22, "h": 23 } @@ -5106,33 +5148,12 @@ "h": 23 }, "frame": { - "x": 198, - "y": 236, + "x": 282, + "y": 238, "w": 22, "h": 23 } }, - { - "filename": "berry_pouch", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 220, - "y": 237, - "w": 23, - "h": 23 - } - }, { "filename": "clefairy_doll", "rotated": false, @@ -5148,8 +5169,8 @@ "h": 23 }, "frame": { - "x": 243, - "y": 237, + "x": 304, + "y": 238, "w": 24, "h": 23 } @@ -5169,8 +5190,8 @@ "h": 23 }, "frame": { - "x": 267, - "y": 237, + "x": 328, + "y": 238, "w": 24, "h": 23 } @@ -5190,14 +5211,14 @@ "h": 23 }, "frame": { - "x": 291, - "y": 237, + "x": 352, + "y": 238, "w": 22, "h": 23 } }, { - "filename": "iron", + "filename": "calcium", "rotated": false, "trimmed": true, "sourceSize": { @@ -5211,54 +5232,12 @@ "h": 24 }, "frame": { - "x": 313, - "y": 237, + "x": 374, + "y": 238, "w": 16, "h": 24 } }, - { - "filename": "dragon_fang", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 21, - "h": 23 - }, - "frame": { - "x": 329, - "y": 240, - "w": 21, - "h": 23 - } - }, - { - "filename": "ether", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 350, - "y": 242, - "w": 18, - "h": 24 - } - }, { "filename": "dragon_tera_shard", "rotated": false, @@ -5274,8 +5253,8 @@ "h": 23 }, "frame": { - "x": 368, - "y": 252, + "x": 0, + "y": 253, "w": 22, "h": 23 } @@ -5295,33 +5274,12 @@ "h": 23 }, "frame": { - "x": 0, + "x": 22, "y": 254, "w": 23, "h": 23 } }, - { - "filename": "lansat_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 21, - "h": 23 - }, - "frame": { - "x": 23, - "y": 254, - "w": 21, - "h": 23 - } - }, { "filename": "electric_tera_shard", "rotated": false, @@ -5337,14 +5295,14 @@ "h": 23 }, "frame": { - "x": 44, - "y": 255, + "x": 45, + "y": 254, "w": 22, "h": 23 } }, { - "filename": "expert_belt", + "filename": "carbos", "rotated": false, "trimmed": true, "sourceSize": { @@ -5352,16 +5310,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 4, + "x": 8, "y": 4, - "w": 24, - "h": 23 + "w": 16, + "h": 24 }, "frame": { - "x": 66, - "y": 255, - "w": 24, - "h": 23 + "x": 67, + "y": 254, + "w": 16, + "h": 24 } }, { @@ -5379,8 +5337,8 @@ "h": 23 }, "frame": { - "x": 90, - "y": 256, + "x": 83, + "y": 255, "w": 22, "h": 23 } @@ -5400,12 +5358,33 @@ "h": 23 }, "frame": { - "x": 112, - "y": 257, + "x": 105, + "y": 256, "w": 22, "h": 23 } }, + { + "filename": "expert_belt", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 23 + }, + "frame": { + "x": 127, + "y": 257, + "w": 24, + "h": 23 + } + }, { "filename": "fire_stone", "rotated": false, @@ -5421,12 +5400,33 @@ "h": 23 }, "frame": { - "x": 134, + "x": 151, "y": 257, "w": 22, "h": 23 } }, + { + "filename": "elixir", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 18, + "h": 24 + }, + "frame": { + "x": 173, + "y": 257, + "w": 18, + "h": 24 + } + }, { "filename": "fire_tera_shard", "rotated": false, @@ -5442,7 +5442,7 @@ "h": 23 }, "frame": { - "x": 156, + "x": 191, "y": 258, "w": 22, "h": 23 @@ -5463,8 +5463,8 @@ "h": 23 }, "frame": { - "x": 178, - "y": 259, + "x": 213, + "y": 258, "w": 22, "h": 23 } @@ -5484,8 +5484,8 @@ "h": 23 }, "frame": { - "x": 200, - "y": 260, + "x": 235, + "y": 259, "w": 22, "h": 23 } @@ -5505,7 +5505,7 @@ "h": 23 }, "frame": { - "x": 222, + "x": 257, "y": 260, "w": 22, "h": 23 @@ -5526,8 +5526,8 @@ "h": 23 }, "frame": { - "x": 244, - "y": 260, + "x": 279, + "y": 261, "w": 22, "h": 23 } @@ -5547,8 +5547,8 @@ "h": 23 }, "frame": { - "x": 266, - "y": 260, + "x": 301, + "y": 261, "w": 23, "h": 23 } @@ -5568,92 +5568,8 @@ "h": 23 }, "frame": { - "x": 289, - "y": 260, - "w": 22, - "h": 23 - } - }, - { - "filename": "full_restore", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 311, + "x": 324, "y": 261, - "w": 18, - "h": 24 - } - }, - { - "filename": "leaf_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 21, - "h": 23 - }, - "frame": { - "x": 329, - "y": 263, - "w": 21, - "h": 23 - } - }, - { - "filename": "lure", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 17, - "h": 24 - }, - "frame": { - "x": 350, - "y": 266, - "w": 17, - "h": 24 - } - }, - { - "filename": "ice_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 367, - "y": 275, "w": 22, "h": 23 } @@ -5673,12 +5589,75 @@ "h": 23 }, "frame": { - "x": 0, - "y": 277, + "x": 346, + "y": 261, "w": 24, "h": 23 } }, + { + "filename": "ether", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 18, + "h": 24 + }, + "frame": { + "x": 370, + "y": 262, + "w": 18, + "h": 24 + } + }, + { + "filename": "ice_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 0, + "y": 276, + "w": 22, + "h": 23 + } + }, + { + "filename": "leaf_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 21, + "h": 23 + }, + "frame": { + "x": 22, + "y": 277, + "w": 21, + "h": 23 + } + }, { "filename": "leppa_berry", "rotated": false, @@ -5694,8 +5673,8 @@ "h": 23 }, "frame": { - "x": 24, - "y": 278, + "x": 43, + "y": 277, "w": 24, "h": 23 } @@ -5715,14 +5694,14 @@ "h": 23 }, "frame": { - "x": 48, + "x": 67, "y": 278, "w": 22, "h": 23 } }, { - "filename": "max_elixir", + "filename": "hp_up", "rotated": false, "trimmed": true, "sourceSize": { @@ -5730,15 +5709,15 @@ "h": 32 }, "spriteSourceSize": { - "x": 7, + "x": 8, "y": 4, - "w": 18, + "w": 16, "h": 24 }, "frame": { - "x": 70, + "x": 89, "y": 278, - "w": 18, + "w": 16, "h": 24 } }, @@ -5757,7 +5736,7 @@ "h": 23 }, "frame": { - "x": 88, + "x": 105, "y": 279, "w": 22, "h": 23 @@ -5778,7 +5757,7 @@ "h": 23 }, "frame": { - "x": 110, + "x": 127, "y": 280, "w": 22, "h": 23 @@ -5799,7 +5778,7 @@ "h": 23 }, "frame": { - "x": 132, + "x": 149, "y": 280, "w": 22, "h": 23 @@ -5820,7 +5799,7 @@ "h": 23 }, "frame": { - "x": 154, + "x": 171, "y": 281, "w": 22, "h": 23 @@ -5841,12 +5820,33 @@ "h": 23 }, "frame": { - "x": 176, - "y": 282, + "x": 193, + "y": 281, "w": 23, "h": 23 } }, + { + "filename": "full_restore", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 18, + "h": 24 + }, + "frame": { + "x": 216, + "y": 281, + "w": 18, + "h": 24 + } + }, { "filename": "rarer_candy", "rotated": false, @@ -5862,8 +5862,8 @@ "h": 23 }, "frame": { - "x": 199, - "y": 283, + "x": 234, + "y": 282, "w": 23, "h": 23 } @@ -5883,7 +5883,7 @@ "h": 23 }, "frame": { - "x": 222, + "x": 257, "y": 283, "w": 22, "h": 23 @@ -5904,8 +5904,8 @@ "h": 23 }, "frame": { - "x": 244, - "y": 283, + "x": 279, + "y": 284, "w": 22, "h": 23 } @@ -5925,8 +5925,8 @@ "h": 23 }, "frame": { - "x": 266, - "y": 283, + "x": 301, + "y": 284, "w": 24, "h": 23 } @@ -5946,75 +5946,12 @@ "h": 23 }, "frame": { - "x": 290, - "y": 283, + "x": 325, + "y": 284, "w": 21, "h": 23 } }, - { - "filename": "max_ether", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 311, - "y": 285, - "w": 18, - "h": 24 - } - }, - { - "filename": "whipped_dream", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 21, - "h": 23 - }, - "frame": { - "x": 329, - "y": 286, - "w": 21, - "h": 23 - } - }, - { - "filename": "max_lure", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 17, - "h": 24 - }, - "frame": { - "x": 350, - "y": 290, - "w": 17, - "h": 24 - } - }, { "filename": "steel_tera_shard", "rotated": false, @@ -6030,8 +5967,8 @@ "h": 23 }, "frame": { - "x": 367, - "y": 298, + "x": 346, + "y": 284, "w": 22, "h": 23 } @@ -6050,9 +5987,30 @@ "w": 22, "h": 23 }, + "frame": { + "x": 368, + "y": 286, + "w": 22, + "h": 23 + } + }, + { + "filename": "water_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, "frame": { "x": 0, - "y": 300, + "y": 299, "w": 22, "h": 23 } @@ -6073,13 +6031,13 @@ }, "frame": { "x": 22, - "y": 301, + "y": 300, "w": 23, "h": 23 } }, { - "filename": "twisted_spoon", + "filename": "whipped_dream", "rotated": false, "trimmed": true, "sourceSize": { @@ -6087,60 +6045,18 @@ "h": 32 }, "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, + "x": 5, + "y": 4, + "w": 21, "h": 23 }, "frame": { "x": 45, - "y": 301, - "w": 24, + "y": 300, + "w": 21, "h": 23 } }, - { - "filename": "water_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 69, - "y": 302, - "w": 22, - "h": 23 - } - }, - { - "filename": "max_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 91, - "y": 302, - "w": 18, - "h": 24 - } - }, { "filename": "big_root", "rotated": false, @@ -6156,12 +6072,33 @@ "h": 24 }, "frame": { - "x": 109, - "y": 303, + "x": 66, + "y": 301, "w": 23, "h": 24 } }, + { + "filename": "twisted_spoon", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 24, + "h": 23 + }, + "frame": { + "x": 89, + "y": 302, + "w": 24, + "h": 23 + } + }, { "filename": "catching_charm", "rotated": false, @@ -6177,54 +6114,12 @@ "h": 24 }, "frame": { - "x": 132, + "x": 113, "y": 303, "w": 21, "h": 24 } }, - { - "filename": "kings_rock", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 23, - "h": 24 - }, - "frame": { - "x": 153, - "y": 304, - "w": 23, - "h": 24 - } - }, - { - "filename": "max_repel", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 176, - "y": 305, - "w": 16, - "h": 24 - } - }, { "filename": "choice_scarf", "rotated": false, @@ -6240,8 +6135,8 @@ "h": 24 }, "frame": { - "x": 192, - "y": 306, + "x": 134, + "y": 303, "w": 24, "h": 24 } @@ -6261,8 +6156,8 @@ "h": 24 }, "frame": { - "x": 216, - "y": 306, + "x": 158, + "y": 304, "w": 24, "h": 24 } @@ -6282,8 +6177,8 @@ "h": 24 }, "frame": { - "x": 240, - "y": 306, + "x": 182, + "y": 304, "w": 24, "h": 24 } @@ -6303,33 +6198,12 @@ "h": 24 }, "frame": { - "x": 264, - "y": 306, + "x": 206, + "y": 305, "w": 24, "h": 24 } }, - { - "filename": "max_revive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 22, - "h": 24 - }, - "frame": { - "x": 288, - "y": 306, - "w": 22, - "h": 24 - } - }, { "filename": "grip_claw", "rotated": false, @@ -6345,14 +6219,14 @@ "h": 24 }, "frame": { - "x": 310, - "y": 309, + "x": 230, + "y": 305, "w": 24, "h": 24 } }, { - "filename": "pp_max", + "filename": "iron", "rotated": false, "trimmed": true, "sourceSize": { @@ -6366,14 +6240,14 @@ "h": 24 }, "frame": { - "x": 334, - "y": 309, + "x": 254, + "y": 306, "w": 16, "h": 24 } }, { - "filename": "pp_up", + "filename": "kings_rock", "rotated": false, "trimmed": true, "sourceSize": { @@ -6381,15 +6255,15 @@ "h": 32 }, "spriteSourceSize": { - "x": 8, + "x": 5, "y": 4, - "w": 16, + "w": 23, "h": 24 }, "frame": { - "x": 350, - "y": 314, - "w": 16, + "x": 270, + "y": 307, + "w": 23, "h": 24 } }, @@ -6408,33 +6282,12 @@ "h": 24 }, "frame": { - "x": 366, - "y": 321, + "x": 293, + "y": 307, "w": 24, "h": 24 } }, - { - "filename": "oval_charm", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 21, - "h": 24 - }, - "frame": { - "x": 0, - "y": 323, - "w": 21, - "h": 24 - } - }, { "filename": "lucky_punch_great", "rotated": false, @@ -6450,8 +6303,8 @@ "h": 24 }, "frame": { - "x": 21, - "y": 324, + "x": 317, + "y": 307, "w": 24, "h": 24 } @@ -6471,33 +6324,12 @@ "h": 24 }, "frame": { - "x": 45, - "y": 324, + "x": 341, + "y": 307, "w": 24, "h": 24 } }, - { - "filename": "protein", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 69, - "y": 325, - "w": 16, - "h": 24 - } - }, { "filename": "lucky_punch_ultra", "rotated": false, @@ -6513,12 +6345,33 @@ "h": 24 }, "frame": { - "x": 85, - "y": 326, + "x": 365, + "y": 309, "w": 24, "h": 24 } }, + { + "filename": "lure", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 17, + "h": 24 + }, + "frame": { + "x": 0, + "y": 322, + "w": 17, + "h": 24 + } + }, { "filename": "lustrous_globe", "rotated": false, @@ -6534,12 +6387,243 @@ "h": 24 }, "frame": { - "x": 109, - "y": 327, + "x": 17, + "y": 323, "w": 24, "h": 24 } }, + { + "filename": "max_elixir", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 18, + "h": 24 + }, + "frame": { + "x": 41, + "y": 323, + "w": 18, + "h": 24 + } + }, + { + "filename": "max_ether", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 18, + "h": 24 + }, + "frame": { + "x": 59, + "y": 325, + "w": 18, + "h": 24 + } + }, + { + "filename": "max_lure", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 17, + "h": 24 + }, + "frame": { + "x": 77, + "y": 325, + "w": 17, + "h": 24 + } + }, + { + "filename": "max_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 18, + "h": 24 + }, + "frame": { + "x": 94, + "y": 325, + "w": 18, + "h": 24 + } + }, + { + "filename": "max_repel", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 112, + "y": 327, + "w": 16, + "h": 24 + } + }, + { + "filename": "max_revive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 22, + "h": 24 + }, + "frame": { + "x": 128, + "y": 327, + "w": 22, + "h": 24 + } + }, + { + "filename": "muscle_band", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 150, + "y": 328, + "w": 24, + "h": 24 + } + }, + { + "filename": "oval_charm", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 21, + "h": 24 + }, + "frame": { + "x": 174, + "y": 328, + "w": 21, + "h": 24 + } + }, + { + "filename": "pp_max", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 195, + "y": 329, + "w": 16, + "h": 24 + } + }, + { + "filename": "pp_up", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 211, + "y": 329, + "w": 16, + "h": 24 + } + }, + { + "filename": "protein", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 227, + "y": 329, + "w": 16, + "h": 24 + } + }, { "filename": "red_orb", "rotated": false, @@ -6555,8 +6639,8 @@ "h": 24 }, "frame": { - "x": 133, - "y": 327, + "x": 243, + "y": 330, "w": 20, "h": 24 } @@ -6576,8 +6660,8 @@ "h": 24 }, "frame": { - "x": 153, - "y": 328, + "x": 263, + "y": 331, "w": 16, "h": 24 } @@ -6597,33 +6681,12 @@ "h": 24 }, "frame": { - "x": 169, - "y": 329, + "x": 279, + "y": 331, "w": 23, "h": 24 } }, - { - "filename": "muscle_band", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 192, - "y": 330, - "w": 24, - "h": 24 - } - }, { "filename": "salac_berry", "rotated": false, @@ -6639,8 +6702,8 @@ "h": 24 }, "frame": { - "x": 216, - "y": 330, + "x": 302, + "y": 331, "w": 24, "h": 24 } @@ -6660,8 +6723,8 @@ "h": 24 }, "frame": { - "x": 240, - "y": 330, + "x": 326, + "y": 331, "w": 24, "h": 24 } @@ -6681,51 +6744,9 @@ "h": 24 }, "frame": { - "x": 264, - "y": 330, - "w": 21, - "h": 24 - } - }, - { - "filename": "silk_scarf", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 285, - "y": 330, - "w": 24, - "h": 24 - } - }, - { - "filename": "sun_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 309, + "x": 350, "y": 333, - "w": 24, + "w": 21, "h": 24 } }, @@ -6744,7 +6765,7 @@ "h": 24 }, "frame": { - "x": 333, + "x": 371, "y": 333, "w": 17, "h": 24 @@ -6765,12 +6786,33 @@ "h": 24 }, "frame": { - "x": 350, - "y": 338, + "x": 0, + "y": 346, "w": 16, "h": 24 } }, + { + "filename": "silk_scarf", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 16, + "y": 347, + "w": 24, + "h": 24 + } + }, { "filename": "unknown", "rotated": false, @@ -6786,12 +6828,33 @@ "h": 24 }, "frame": { - "x": 366, - "y": 345, + "x": 40, + "y": 347, "w": 16, "h": 24 } }, + { + "filename": "sun_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 56, + "y": 349, + "w": 24, + "h": 24 + } + }, { "filename": "zinc", "rotated": false, @@ -6807,8 +6870,8 @@ "h": 24 }, "frame": { - "x": 0, - "y": 347, + "x": 80, + "y": 349, "w": 16, "h": 24 } @@ -6828,8 +6891,8 @@ "h": 25 }, "frame": { - "x": 16, - "y": 348, + "x": 96, + "y": 351, "w": 22, "h": 25 } @@ -6849,8 +6912,8 @@ "h": 26 }, "frame": { - "x": 38, - "y": 348, + "x": 118, + "y": 351, "w": 23, "h": 26 } @@ -6870,8 +6933,8 @@ "h": 26 }, "frame": { - "x": 61, - "y": 349, + "x": 141, + "y": 352, "w": 24, "h": 26 } @@ -6891,8 +6954,8 @@ "h": 26 }, "frame": { - "x": 85, - "y": 351, + "x": 165, + "y": 352, "w": 27, "h": 26 } @@ -6912,8 +6975,8 @@ "h": 27 }, "frame": { - "x": 112, - "y": 351, + "x": 192, + "y": 353, "w": 32, "h": 27 } @@ -6933,33 +6996,12 @@ "h": 28 }, "frame": { - "x": 144, - "y": 353, + "x": 224, + "y": 354, "w": 29, "h": 28 } }, - { - "filename": "prison_bottle", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 1, - "w": 17, - "h": 30 - }, - "frame": { - "x": 173, - "y": 353, - "w": 17, - "h": 30 - } - }, { "filename": "galarica_cuff", "rotated": false, @@ -6975,12 +7017,33 @@ "h": 30 }, "frame": { - "x": 190, - "y": 354, + "x": 253, + "y": 355, "w": 29, "h": 30 } }, + { + "filename": "prison_bottle", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 1, + "w": 17, + "h": 30 + }, + "frame": { + "x": 282, + "y": 355, + "w": 17, + "h": 30 + } + }, { "filename": "exp_charm", "rotated": false, @@ -6996,8 +7059,8 @@ "h": 31 }, "frame": { - "x": 219, - "y": 354, + "x": 299, + "y": 355, "w": 17, "h": 31 } @@ -7017,8 +7080,8 @@ "h": 31 }, "frame": { - "x": 236, - "y": 354, + "x": 316, + "y": 355, "w": 17, "h": 31 } @@ -7038,8 +7101,8 @@ "h": 31 }, "frame": { - "x": 253, - "y": 354, + "x": 333, + "y": 355, "w": 17, "h": 31 } @@ -7050,6 +7113,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:b9141845043d06c1d7b9be442fde5e4a:247748ff51cffc6859aa94c8cf94099a:110e074689c9edd2c54833ce2e4d9270$" + "smartupdate": "$TexturePacker:SmartUpdate:6d684b34b1074ff970152bf5b6c304e3:7c1ae946dd8c91ed5dfd1e2c9a39e1b1:110e074689c9edd2c54833ce2e4d9270$" } } diff --git a/public/images/items.png b/public/images/items.png index 7ae01596372c65de88799afe8ddc56cbdeeb6425..1ecd17240f0cd7228f4a3d39cd1f7e4249c112e3 100644 GIT binary patch literal 46890 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

4)z_ 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 E0WG}A9RL6T diff --git a/public/images/items/candy.png b/public/images/items/candy.png new file mode 100644 index 0000000000000000000000000000000000000000..9a68bdab6063e4ec775495e94b921311aedbfdfb GIT binary patch literal 402 zcmV;D0d4+?P)U5Jf+$6zMHRxU`bSRg?;e3viK$L-+t3BH|9Nxd0L;phD?z6j4xtJEsCIA=!nH zY_tB73y(nm&3HTptgNi8tp2;4SAXQ3oA3KP&z(O3=#1ky9>kBw<4YjUT=O`Nv0ktH znAUm(G6&*oi8+ALK8m8z@hk?oH>Z?7h1hI1vmho{;BYwf=G}TG&-32b16}q2;QKxR zKq&>K)Zja&2>?I{@oPVdjJ3c literal 0 HcmV?d00001 diff --git a/public/images/items/candy_overlay.png b/public/images/items/candy_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..67df546a633c3365f9ba6bb551ad9f218103b9fc GIT binary patch literal 263 zcmV+i0r>ujP)C$#DEH~qFdJ~Mivvrk+_~Gfs8m(NtC2el0a8r)>lm+YPg#o zH<<)FO<-MhR7kcY8!gWvL96Y}l3dnIAOJW6I3*fyFa;`p6p>xcCJcHS8+m8joBKw* zcn?G-a$HKpr}jjM%9C`n?`@Lq(};~RGG-M!0Ef)uy@)*Q7i$^3MFz-jJ|xib=o zZvfIU;KKPnd<^BY<$0|OVF1=*p1-${5`b|9xZHXdq6`OYbpV93y{Zvxmh3?Zflv;% zF3J7^4(Dn)Tn50)=vi95_X?H(&0oczKF6U@sHWb@dA=k@ loadCommonAnimAssets(this, true)), - Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(m))).then(() => loadMoveAnimAssets(this, defaultMoves, true)) + Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(m))).then(() => loadMoveAnimAssets(this, defaultMoves, true)), + this.initStarterColors() ]).then(() => { this.pushPhase(new LoginPhase(this)); this.pushPhase(new TitlePhase(this)); @@ -472,12 +484,53 @@ export default class BattleScene extends SceneBase { this.updateScoreText(); } - initExpSprites(): void { - if (expSpriteKeys.length) - return; - fetch('./exp_sprites.json').then(res => res.json()).then(keys => { - if (Array.isArray(keys)) - expSpriteKeys.push(...keys); + initExpSprites(): Promise { + return new Promise(resolve => { + if (expSpriteKeys.length) + return resolve(); + fetch('./exp-sprites.json').then(res => res.json()).then(keys => { + if (Array.isArray(keys)) + expSpriteKeys.push(...keys); + resolve(); + }); + }); + } + + initStarterColors(): Promise { + return new Promise(resolve => { + if (starterColors) + return resolve(); + + fetch('./starter-colors.json').then(res => res.json()).then(sc => { + starterColors = {}; + Object.keys(sc).forEach(key => { + starterColors[key] = sc[key]; + }); + + /*const loadPokemonAssets: Promise[] = []; + + for (let s of Object.keys(speciesStarters)) { + const species = getPokemonSpecies(parseInt(s)); + loadPokemonAssets.push(species.loadAssets(this, false, 0, false)); + } + + Promise.all(loadPokemonAssets).then(() => { + const starterCandyColors = {}; + const rgbaToHexFunc = (r, g, b) => [r, g, b].map(x => x.toString(16).padStart(2, '0')).join(''); + + for (let s of Object.keys(speciesStarters)) { + const species = getPokemonSpecies(parseInt(s)); + + starterCandyColors[species.speciesId] = species.generateCandyColors(this).map(c => rgbaToHexFunc(c[0], c[1], c[2])); + } + + console.log(JSON.stringify(starterCandyColors)); + + resolve(); + });*/ + + resolve(); + }); }); } @@ -1075,6 +1128,7 @@ export default class BattleScene extends SceneBase { this.scoreText.setY(this.moneyText.y + 10); const offsetY = (this.scoreText.visible ? this.scoreText : this.moneyText).y + 15; this.partyExpBar.setY(offsetY); + this.candyBar.setY(offsetY + 15); this.ui?.achvBar.setY(this.game.canvas.height / 6 + offsetY); } diff --git a/src/data/daily-run.ts b/src/data/daily-run.ts index 14401d7bbd6..a5ea4e585a0 100644 --- a/src/data/daily-run.ts +++ b/src/data/daily-run.ts @@ -64,6 +64,8 @@ function getDailyRunStarter(scene: BattleScene, starterSpeciesForm: PokemonSpeci const starter: Starter = { species: starterSpecies, dexAttr: pokemon.getDexAttr(), + passive: false, + variant: 0, nature: pokemon.getNature(), pokerus: pokemon.pokerus }; diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 04cc620cf3a..4d19242b7f0 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -11,6 +11,7 @@ import { StarterMoveset } from '../system/game-data'; import { speciesEggMoves } from './egg-moves'; import { PartyMemberStrength } from "./enums/party-member-strength"; import { GameMode } from '../game-mode'; +import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities"; export enum Region { NORMAL, @@ -381,6 +382,55 @@ export abstract class PokemonSpeciesForm { cry.stop(); return cry; } + + generateCandyColors(scene: BattleScene): integer[][] { + const sourceTexture = scene.textures.get(this.getSpriteKey(false)); + + const sourceFrame = sourceTexture.frames[sourceTexture.firstFrame]; + const sourceImage = sourceTexture.getSourceImage() as HTMLImageElement; + + const canvas = document.createElement('canvas'); + + const spriteColors: integer[][] = []; + + const context = canvas.getContext('2d'); + const frame = sourceFrame; + canvas.width = frame.width; + canvas.height = frame.height; + context.drawImage(sourceImage, frame.cutX, frame.cutY, frame.width, frame.height, 0, 0, frame.width, frame.height); + const imageData = context.getImageData(frame.cutX, frame.cutY, frame.width, frame.height); + const pixelData = imageData.data; + + for (let i = 0; i < pixelData.length; i += 4) { + if (pixelData[i + 3]) { + const pixel = pixelData.slice(i, i + 4); + const [ r, g, b, a ] = pixel; + if (!spriteColors.find(c => c[0] === r && c[1] === g && c[2] === b)) + spriteColors.push([ r, g, b, a ]); + } + } + + const pixelColors = []; + for (let i = 0; i < pixelData.length; i += 4) { + const total = pixelData.slice(i, i + 3).reduce((total: integer, value: integer) => total + value, 0); + if (!total) + continue; + pixelColors.push(argbFromRgba({ r: pixelData[i], g: pixelData[i + 1], b: pixelData[i + 2], a: pixelData[i + 3] })); + } + + let paletteColors: Map; + + const originalRandom = Math.random; + Math.random = () => Phaser.Math.RND.realInRange(0, 1); + + scene.executeWithSeedOffset(() => { + paletteColors = QuantizerCelebi.quantize(pixelColors, 2); + }, 0, 'This result should not vary'); + + Math.random = originalRandom; + + return Array.from(paletteColors.keys()).map(c => Object.values(rgbaFromArgb(c)) as integer[]); + } } export default class PokemonSpecies extends PokemonSpeciesForm { @@ -3058,8 +3108,8 @@ export const starterPassiveAbilities = { [Species.CHARMANDER]: Abilities.INTIMIDATE, [Species.SQUIRTLE]: Abilities.DAUNTLESS_SHIELD, [Species.CATERPIE]: Abilities.MAGICIAN, - [Species.WEEDLE]: Abilities.POISON_TOUCH, - [Species.PIDGEY]: Abilities.TECHNICIAN, + [Species.WEEDLE]: Abilities.TECHNICIAN, + [Species.PIDGEY]: Abilities.GALE_WINGS, [Species.RATTATA]: Abilities.STRONG_JAW, [Species.SPEAROW]: Abilities.MOXIE, [Species.EKANS]: Abilities.ROUGH_SKIN, @@ -3070,14 +3120,14 @@ export const starterPassiveAbilities = { [Species.ZUBAT]: Abilities.WIND_RIDER, [Species.ODDISH]: Abilities.LINGERING_AROMA, [Species.PARAS]: Abilities.POISON_HEAL, - [Species.VENONAT]: Abilities.TECHNICIAN, + [Species.VENONAT]: Abilities.SWARM, [Species.DIGLETT]: Abilities.STURDY, [Species.MEOWTH]: Abilities.NORMALIZE, [Species.PSYDUCK]: Abilities.SIMPLE, - [Species.MANKEY]: Abilities.STAMINA, - [Species.GROWLITHE]: Abilities.BALL_FETCH, + [Species.MANKEY]: Abilities.IRON_FIST, + [Species.GROWLITHE]: Abilities.SPEED_BOOST, [Species.POLIWAG]: Abilities.WATER_BUBBLE, - [Species.ABRA]: Abilities.TECHNICIAN, + [Species.ABRA]: Abilities.OPPORTUNIST, [Species.MACHOP]: Abilities.IRON_FIST, [Species.BELLSPROUT]: Abilities.CORROSION, [Species.TENTACOOL]: Abilities.INNARDS_OUT, @@ -3103,7 +3153,7 @@ export const starterPassiveAbilities = { [Species.TANGELA]: Abilities.TANGLING_HAIR, [Species.KANGASKHAN]: Abilities.IRON_FIST, [Species.HORSEA]: Abilities.DRIZZLE, - [Species.GOLDEEN]: Abilities.VOLT_ABSORB, + [Species.GOLDEEN]: Abilities.MULTISCALE, [Species.STARYU]: Abilities.REGENERATOR, [Species.SCYTHER]: Abilities.SPEED_BOOST, [Species.PINSIR]: Abilities.SAP_SIPPER, @@ -3139,7 +3189,7 @@ export const starterPassiveAbilities = { [Species.HOPPIP]: Abilities.PRANKSTER, [Species.AIPOM]: Abilities.SCRAPPY, [Species.SUNKERN]: Abilities.DROUGHT, - [Species.YANMA]: Abilities.TECHNICIAN, + [Species.YANMA]: Abilities.INFILTRATOR, [Species.WOOPER]: Abilities.SIMPLE, [Species.MURKROW]: Abilities.DEFIANT, [Species.MISDREAVUS]: Abilities.DAZZLING, @@ -3154,7 +3204,7 @@ export const starterPassiveAbilities = { [Species.HERACROSS]: Abilities.QUICK_FEET, [Species.SNEASEL]: Abilities.MOXIE, [Species.TEDDIURSA]: Abilities.GLUTTONY, - [Species.SLUGMA]: Abilities.DESOLATE_LAND, + [Species.SLUGMA]: Abilities.DROUGHT, [Species.SWINUB]: Abilities.SLUSH_RUSH, [Species.CORSOLA]: Abilities.STORM_DRAIN, [Species.REMORAID]: Abilities.SKILL_LINK, @@ -3166,7 +3216,7 @@ export const starterPassiveAbilities = { [Species.SMEARGLE]: Abilities.TRACE, [Species.TYROGUE]: Abilities.STAMINA, [Species.SMOOCHUM]: Abilities.CUTE_CHARM, - [Species.ELEKID]: Abilities.ADAPTABILITY, + [Species.ELEKID]: Abilities.IRON_FIST, [Species.MAGBY]: Abilities.CONTRARY, [Species.MILTANK]: Abilities.GLUTTONY, [Species.RAIKOU]: Abilities.FLARE_BOOST, @@ -3192,7 +3242,7 @@ export const starterPassiveAbilities = { [Species.SLAKOTH]: Abilities.GUTS, [Species.NINCADA]: Abilities.OVERCOAT, [Species.WHISMUR]: Abilities.PUNK_ROCK, - [Species.MAKUHITA]: Abilities.CONTRARY, + [Species.MAKUHITA]: Abilities.STAMINA, [Species.AZURILL]: Abilities.UNNERVE, [Species.NOSEPASS]: Abilities.LEVITATE, [Species.SKITTY]: Abilities.SCRAPPY, @@ -3217,10 +3267,10 @@ export const starterPassiveAbilities = { [Species.SWABLU]: Abilities.WHITE_SMOKE, [Species.ZANGOOSE]: Abilities.SUPER_LUCK, [Species.SEVIPER]: Abilities.MOLD_BREAKER, - [Species.LUNATONE]: Abilities.SHADOW_SHIELD, - [Species.SOLROCK]: Abilities.FULL_METAL_BODY, + [Species.LUNATONE]: Abilities.FAIRY_AURA, + [Species.SOLROCK]: Abilities.DROUGHT, [Species.BARBOACH]: Abilities.BALL_FETCH, - [Species.CORPHISH]: Abilities.WATER_BUBBLE, + [Species.CORPHISH]: Abilities.TOUGH_CLAWS, [Species.BALTOY]: Abilities.OWN_TEMPO, [Species.LILEEP]: Abilities.WATER_ABSORB, [Species.ANORITH]: Abilities.WATER_ABSORB, @@ -3237,7 +3287,7 @@ export const starterPassiveAbilities = { [Species.CLAMPERL]: Abilities.SIMPLE, [Species.RELICANTH]: Abilities.SOLID_ROCK, [Species.LUVDISC]: Abilities.PICKUP, - [Species.BAGON]: Abilities.GALE_WINGS, + [Species.BAGON]: Abilities.BERSERK, [Species.BELDUM]: Abilities.IRON_FIST, [Species.REGIROCK]: Abilities.REGENERATOR, [Species.REGICE]: Abilities.ICE_SCALES, @@ -3251,7 +3301,7 @@ export const starterPassiveAbilities = { [Species.DEOXYS]: Abilities.STICKY_HOLD, [Species.TURTWIG]: Abilities.HARVEST, [Species.CHIMCHAR]: Abilities.DEFIANT, - [Species.PIPLUP]: Abilities.BATTLE_ARMOR, + [Species.PIPLUP]: Abilities.SLUSH_RUSH, [Species.STARLY]: Abilities.ROCK_HEAD, [Species.BIDOOF]: Abilities.NEUROFORCE, [Species.KRICKETOT]: Abilities.SOUNDPROOF, @@ -3304,7 +3354,7 @@ export const starterPassiveAbilities = { [Species.VICTINI]: Abilities.SUPER_LUCK, [Species.SNIVY]: Abilities.MULTISCALE, [Species.TEPIG]: Abilities.SAND_RUSH, - [Species.OSHAWOTT]: Abilities.LIGHTNING_ROD, + [Species.OSHAWOTT]: Abilities.MOLD_BREAKER, [Species.PATRAT]: Abilities.STAKEOUT, [Species.LILLIPUP]: Abilities.BALL_FETCH, [Species.PURRLOIN]: Abilities.DEFIANT, @@ -3325,8 +3375,8 @@ export const starterPassiveAbilities = { [Species.SEWADDLE]: Abilities.SHARPNESS, [Species.VENIPEDE]: Abilities.INTIMIDATE, [Species.COTTONEE]: Abilities.MISTY_SURGE, - [Species.PETILIL]: Abilities.ORICHALCUM_PULSE, - [Species.BASCULIN]: Abilities.ROCK_HEAD, + [Species.PETILIL]: Abilities.DROUGHT, + [Species.BASCULIN]: Abilities.OPPORTUNIST, [Species.SANDILE]: Abilities.STRONG_JAW, [Species.DARUMAKA]: Abilities.IRON_FIST, [Species.MARACTUS]: Abilities.IRON_BARBS, @@ -3346,14 +3396,14 @@ export const starterPassiveAbilities = { [Species.DEERLING]: Abilities.JUSTIFIED, [Species.EMOLGA]: Abilities.WIND_POWER, [Species.KARRABLAST]: Abilities.NO_GUARD, - [Species.FOONGUS]: Abilities.ADAPTABILITY, + [Species.FOONGUS]: Abilities.MIMICRY, [Species.FRILLISH]: Abilities.MUMMY, [Species.ALOMOMOLA]: Abilities.MULTISCALE, [Species.JOLTIK]: Abilities.VOLT_ABSORB, [Species.FERROSEED]: Abilities.SKILL_LINK, [Species.KLINK]: Abilities.STEELWORKER, [Species.TYNAMO]: Abilities.SWIFT_SWIM, - [Species.ELGYEM]: Abilities.COMMANDER, + [Species.ELGYEM]: Abilities.SHADOW_TAG, [Species.LITWICK]: Abilities.SOUL_HEART, [Species.AXEW]: Abilities.SHEER_FORCE, [Species.CUBCHOO]: Abilities.INTIMIDATE, @@ -3362,7 +3412,7 @@ export const starterPassiveAbilities = { [Species.STUNFISK]: Abilities.STORM_DRAIN, [Species.MIENFOO]: Abilities.NO_GUARD, [Species.DRUDDIGON]: Abilities.INTIMIDATE, - [Species.GOLETT]: Abilities.JUSTIFIED, + [Species.GOLETT]: Abilities.SHADOW_SHIELD, [Species.PAWNIARD]: Abilities.SHARPNESS, [Species.BOUFFALANT]: Abilities.THICK_FAT, [Species.RUFFLET]: Abilities.RECKLESS, @@ -3380,7 +3430,7 @@ export const starterPassiveAbilities = { [Species.ZEKROM]: Abilities.HADRON_ENGINE, [Species.LANDORUS]: Abilities.PRANKSTER, [Species.KYUREM]: Abilities.SNOW_WARNING, - [Species.KELDEO]: Abilities.HUGE_POWER, + [Species.KELDEO]: Abilities.SHARPNESS, [Species.MELOETTA]: Abilities.PUNK_ROCK, [Species.GENESECT]: Abilities.MEGA_LAUNCHER, [Species.CHESPIN]: Abilities.IRON_BARBS, @@ -3425,7 +3475,7 @@ export const starterPassiveAbilities = { [Species.POPPLIO]: Abilities.PUNK_ROCK, [Species.PIKIPEK]: Abilities.ANGER_POINT, [Species.YUNGOOS]: Abilities.HUGE_POWER, - [Species.GRUBBIN]: Abilities.GALVANIZE, + [Species.GRUBBIN]: Abilities.SPEED_BOOST, [Species.CRABRAWLER]: Abilities.REFRIGERATE, [Species.ORICORIO]: Abilities.ADAPTABILITY, [Species.CUTIEFLY]: Abilities.FRIEND_GUARD, @@ -3442,7 +3492,7 @@ export const starterPassiveAbilities = { [Species.COMFEY]: Abilities.FRIEND_GUARD, [Species.ORANGURU]: Abilities.HOSPITALITY, [Species.PASSIMIAN]: Abilities.COSTAR, - [Species.WIMPOD]: Abilities.BATTLE_ARMOR, + [Species.WIMPOD]: Abilities.TINTED_LENS, [Species.SANDYGAST]: Abilities.DAUNTLESS_SHIELD, [Species.PYUKUMUKU]: Abilities.IRON_BARBS, [Species.TYPE_NULL]: Abilities.ADAPTABILITY, @@ -3465,7 +3515,7 @@ export const starterPassiveAbilities = { [Species.PHEROMOSA]: Abilities.MOXIE, [Species.XURKITREE]: Abilities.LIGHTNING_ROD, [Species.CELESTEELA]: Abilities.CHLOROPHYLL, - [Species.KARTANA]: Abilities.INTREPID_SWORD, + [Species.KARTANA]: Abilities.SHARPNESS, [Species.GUZZLORD]: Abilities.GLUTTONY, [Species.NECROZMA]: Abilities.BEAST_BOOST, [Species.MAGEARNA]: Abilities.STEELY_SPIRIT, @@ -3479,7 +3529,7 @@ export const starterPassiveAbilities = { [Species.SCORBUNNY]: Abilities.RECKLESS, [Species.SOBBLE]: Abilities.MIMICRY, [Species.SKWOVET]: Abilities.HONEY_GATHER, - [Species.ROOKIDEE]: Abilities.JUSTIFIED, + [Species.ROOKIDEE]: Abilities.IRON_BARBS, [Species.BLIPBUG]: Abilities.TINTED_LENS, [Species.NICKIT]: Abilities.INTIMIDATE, [Species.GOSSIFLEUR]: Abilities.STORM_DRAIN, @@ -3512,7 +3562,7 @@ export const starterPassiveAbilities = { [Species.ARCTOVISH]: Abilities.STRONG_JAW, [Species.DURALUDON]: Abilities.MEGA_LAUNCHER, [Species.DREEPY]: Abilities.PARENTAL_BOND, - [Species.ZACIAN]: Abilities.SHARPNESS, + [Species.ZACIAN]: Abilities.GUARD_DOG, [Species.ZAMAZENTA]: Abilities.GUARD_DOG, [Species.ETERNATUS]: Abilities.SUPREME_OVERLORD, [Species.KUBFU]: Abilities.IRON_FIST, @@ -3535,7 +3585,7 @@ export const starterPassiveAbilities = { [Species.SMOLIV]: Abilities.RIPEN, [Species.SQUAWKABILLY]: Abilities.GALE_WINGS, [Species.NACLI]: Abilities.EARTH_EATER, - [Species.CHARCADET]: Abilities.CONTRARY, + [Species.CHARCADET]: Abilities.MIRROR_ARMOR, [Species.TADBULB]: Abilities.TRANSISTOR, [Species.WATTREL]: Abilities.GALE_WINGS, [Species.MASCHIFF]: Abilities.STRONG_JAW, @@ -3562,15 +3612,15 @@ export const starterPassiveAbilities = { [Species.TATSUGIRI]: Abilities.WATER_BUBBLE, [Species.GREAT_TUSK]: Abilities.INTIMIDATE, [Species.SCREAM_TAIL]: Abilities.PIXILATE, - [Species.BRUTE_BONNET]: Abilities.ADAPTABILITY, + [Species.BRUTE_BONNET]: Abilities.BEAST_BOOST, [Species.FLUTTER_MANE]: Abilities.DAZZLING, - [Species.SLITHER_WING]: Abilities.SCRAPPY, + [Species.SLITHER_WING]: Abilities.MOXIE, [Species.SANDY_SHOCKS]: Abilities.EARTH_EATER, - [Species.IRON_TREADS]: Abilities.STEAM_ENGINE, + [Species.IRON_TREADS]: Abilities.BULLETPROOF, [Species.IRON_BUNDLE]: Abilities.SNOW_WARNING, [Species.IRON_HANDS]: Abilities.IRON_FIST, [Species.IRON_JUGULIS]: Abilities.NO_GUARD, - [Species.IRON_MOTH]: Abilities.TINTED_LENS, + [Species.IRON_MOTH]: Abilities.LEVITATE, [Species.IRON_THORNS]: Abilities.SAND_STREAM, [Species.FRIGIBAX]: Abilities.THICK_FAT, [Species.GIMMIGHOUL]: Abilities.SUPER_LUCK, @@ -3585,7 +3635,7 @@ export const starterPassiveAbilities = { [Species.WALKING_WAKE]: Abilities.BEAST_BOOST, [Species.IRON_LEAVES]: Abilities.SHARPNESS, [Species.POLTCHAGEIST]: Abilities.FLAME_BODY, - [Species.OKIDOGI]: Abilities.STICKY_HOLD, + [Species.OKIDOGI]: Abilities.INTIMIDATE, [Species.MUNKIDORI]: Abilities.PRANKSTER, [Species.FEZANDIPITI]: Abilities.DAZZLING, [Species.OGERPON]: Abilities.DISGUISE, diff --git a/src/debug.js b/src/debug.js index 3abb27f41c9..68f95c41140 100644 --- a/src/debug.js +++ b/src/debug.js @@ -2,7 +2,7 @@ export function getData() { const dataStr = localStorage.getItem('data'); if (!dataStr) return null; - return JSON.parse(atob(dataStr), (k, v) => k.endsWith('Attr') ? BigInt(v) : v); + return JSON.parse(atob(dataStr), (k, v) => k.endsWith('Attr') && ![ 'natureAttr', 'passiveAttr', 'variantAttr' ].includes(k) ? BigInt(v) : v); } export function getSession() { diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index b4ce4f0a030..ac30befa003 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -55,7 +55,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public species: PokemonSpecies; public formIndex: integer; public abilityIndex: integer; + public passive: boolean; public shiny: boolean; + public variant: integer; public pokeball: PokeballType; protected battleInfo: BattleInfo; public level: integer; @@ -127,6 +129,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.hp = dataSource.hp; this.stats = dataSource.stats; this.ivs = dataSource.ivs; + this.passive = !!dataSource.passive; + this.variant = dataSource.variant || 0; this.nature = dataSource.nature || 0 as Nature; this.natureOverride = dataSource.natureOverride !== undefined ? dataSource.natureOverride : -1; this.moveset = dataSource.moveset; @@ -727,7 +731,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } canApplyPassive(): boolean { - return this.isBoss(); + return this.passive || this.isBoss(); } canApplyAbility(passive: boolean = false): boolean { @@ -1237,8 +1241,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.scene.arena.terrain?.terrainType === TerrainType.MISTY && this.isGrounded() && type === Type.DRAGON) damage.value = Math.floor(damage.value / 2); - applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage); - const fixedDamage = new Utils.IntegerHolder(0); applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage); if (!isTypeImmune && fixedDamage.value) { @@ -1246,8 +1248,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { isCritical = false; result = HitResult.EFFECTIVE; } - - console.log('damage', damage.value, move.name, power.value, sourceAtk, targetDef); if (!result) { if (!typeMultiplier.value) @@ -1275,6 +1275,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage); } + applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage); + + console.log('damage', damage.value, move.name, power.value, sourceAtk, targetDef); + if (damage.value) { if (this.getHpRatio() === 1) applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, battlerMove, cancelled, damage); @@ -2156,7 +2160,7 @@ export class PlayerPokemon extends Pokemon { } tryPopulateMoveset(moveset: StarterMoveset): boolean { - if (!this.getSpeciesForm().validateStarterMoveset(moveset, this.scene.gameData.starterEggMoveData[this.species.getRootSpeciesId()])) + if (!this.getSpeciesForm().validateStarterMoveset(moveset, this.scene.gameData.starterData[this.species.getRootSpeciesId()].eggMoves)) return false; this.moveset = moveset.map(m => new PokemonMove(m)); diff --git a/src/phases.ts b/src/phases.ts index 5b8b9cdc004..092fa7e5d47 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -44,7 +44,7 @@ import { EggHatchPhase } from "./egg-hatch-phase"; import { Egg } from "./data/egg"; import { vouchers } from "./system/voucher"; import { loggedInUser, updateUserInfo } from "./account"; -import { GameDataType, PlayerGender, SessionSaveData } from "./system/game-data"; +import { DexAttr, GameDataType, PlayerGender, SessionSaveData } from "./system/game-data"; import { addPokeballCaptureStars, addPokeballOpenParticles } from "./field/anims"; import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeManualTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangePreMoveTrigger } from "./data/pokemon-forms"; import { battleSpecDialogue, getCharVariantFromDialogue } from "./data/dialogue"; @@ -175,7 +175,10 @@ export class TitlePhase extends Phase { if (loggedInUser.lastSessionSlot > -1) { options.push({ label: i18next.t('menu:continue'), - handler: () => this.loadSaveSlot(this.lastSessionData ? -1 : loggedInUser.lastSessionSlot) + handler: () => { + this.loadSaveSlot(this.lastSessionData ? -1 : loggedInUser.lastSessionSlot); + return true; + } }); } options.push({ @@ -188,20 +191,29 @@ export class TitlePhase extends Phase { this.end(); }; if (this.scene.gameData.unlocks[Unlockables.ENDLESS_MODE]) { - const options = [ + const options: OptionSelectItem[] = [ { label: gameModes[GameModes.CLASSIC].getName(), - handler: () => setModeAndEnd(GameModes.CLASSIC) + handler: () => { + setModeAndEnd(GameModes.CLASSIC); + return true; + } }, { label: gameModes[GameModes.ENDLESS].getName(), - handler: () => setModeAndEnd(GameModes.ENDLESS) + handler: () => { + setModeAndEnd(GameModes.ENDLESS); + return true; + } } ]; if (this.scene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE]) { options.push({ label: gameModes[GameModes.SPLICED_ENDLESS].getName(), - handler: () => setModeAndEnd(GameModes.SPLICED_ENDLESS) + handler: () => { + setModeAndEnd(GameModes.SPLICED_ENDLESS); + return true; + } }); } options.push({ @@ -210,6 +222,7 @@ export class TitlePhase extends Phase { 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 })); @@ -219,21 +232,27 @@ export class TitlePhase extends Phase { 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); - } - ) + 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(), + handler: () => { + this.initDailyRun(); + return true; + }, keepOpen: true }); const config: OptionSelectConfig = { @@ -377,6 +396,7 @@ export class SelectGenderPhase extends Phase { this.scene.gameData.gender = PlayerGender.MALE; this.scene.gameData.saveSetting(Setting.Player_Gender, 0); this.scene.gameData.saveSystem().then(() => this.end()); + return true; } }, { @@ -385,6 +405,7 @@ export class SelectGenderPhase extends Phase { this.scene.gameData.gender = PlayerGender.FEMALE; this.scene.gameData.saveSetting(Setting.Player_Gender, 1); this.scene.gameData.saveSystem().then(() => this.end()); + return true; } } ] @@ -437,6 +458,10 @@ export class SelectStarterPhase extends Phase { const starterIvs = this.scene.gameData.dexData[starter.species.speciesId].ivs.slice(0); const starterPokemon = this.scene.addPlayerPokemon(starter.species, this.scene.gameMode.getStartingLevel(), starterProps.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, starterIvs, starter.nature); starterPokemon.tryPopulateMoveset(starter.moveset); + if (starter.passive) + starterPokemon.passive = true; + if (starter.variant && starter.dexAttr & DexAttr.SHINY) + starterPokemon.variant = starter.variant; if (starter.pokerus) starterPokemon.pokerus = true; if (this.scene.gameMode.isSplicedOnly) diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 1bf3852740a..78ed699e954 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -43,6 +43,11 @@ export enum PlayerGender { FEMALE } +export enum Passive { + UNLOCKED = 1, + ENABLED = 2 +} + export function getDataTypeKey(dataType: GameDataType, slotId: integer = 0): string { switch (dataType) { case GameDataType.SYSTEM: @@ -64,8 +69,7 @@ interface SystemSaveData { secretId: integer; gender: PlayerGender; dexData: DexData; - starterMoveData: StarterMoveData; - starterEggMoveData: StarterEggMoveData; + starterData: StarterData; gameStats: GameStats; unlocks: Unlocks; achvUnlocks: AchvUnlocks; @@ -145,16 +149,25 @@ export interface DexAttrProps { export type StarterMoveset = [ Moves ] | [ Moves, Moves ] | [ Moves, Moves, Moves ] | [ Moves, Moves, Moves, Moves ]; -export interface StarterMoveData { - [key: integer]: StarterMoveset | StarterFormMoveData -} - export interface StarterFormMoveData { [key: integer]: StarterMoveset } -export interface StarterEggMoveData { - [key: integer]: integer +export interface StarterMoveData { + [key: integer]: StarterMoveset | StarterFormMoveData +} + +export interface StarterDataEntry { + moveset: StarterMoveset | StarterFormMoveData; + eggMoves: integer; + candyCount: integer; + passiveAttr: integer; + variantAttr: integer; + valueReduction: integer; +} + +export interface StarterData { + [key: integer]: StarterDataEntry } export interface TutorialFlags { @@ -167,7 +180,12 @@ const systemShortKeys = { natureAttr: '$na', seenCount: '$s' , caughtCount: '$c', - ivs: '$i' + ivs: '$i', + moveset: '$m', + eggMoves: '$em', + candyCount: '$cc', + passive: '$p', + valueReduction: '$vr' }; export class GameData { @@ -181,9 +199,7 @@ export class GameData { public dexData: DexData; private defaultDexData: DexData; - public starterMoveData: StarterMoveData; - - public starterEggMoveData: StarterEggMoveData; + public starterData: StarterData; public gameStats: GameStats; @@ -200,8 +216,7 @@ export class GameData { this.loadSettings(); this.trainerId = Utils.randSeedInt(65536); this.secretId = Utils.randSeedInt(65536); - this.starterMoveData = {}; - this.starterEggMoveData = {}; + this.starterData = {}; this.gameStats = new GameStats(); this.unlocks = { [Unlockables.ENDLESS_MODE]: false, @@ -218,7 +233,7 @@ export class GameData { }; this.eggs = []; this.initDexData(); - this.initEggMoveData(); + this.initStarterData(); } public saveSystem(): Promise { @@ -234,8 +249,7 @@ export class GameData { secretId: this.secretId, gender: this.gender, dexData: this.dexData, - starterMoveData: this.starterMoveData, - starterEggMoveData: this.starterEggMoveData, + starterData: this.starterData, gameStats: this.gameStats, unlocks: this.unlocks, achvUnlocks: this.achvUnlocks, @@ -297,17 +311,24 @@ export class GameData { this.saveSetting(Setting.Player_Gender, systemData.gender === PlayerGender.FEMALE ? 1 : 0); - this.starterMoveData = systemData.starterMoveData || {}; - - this.starterEggMoveData = {}; - this.initEggMoveData(); + const initStarterData = !systemData.starterData; - if (systemData.starterEggMoveData) { - for (let key of Object.keys(systemData.starterEggMoveData)) { - if (this.starterEggMoveData.hasOwnProperty(key)) - this.starterEggMoveData[key] = systemData.starterEggMoveData[key]; + if (initStarterData) { + this.initStarterData(); + + if (systemData['starterMoveData']) { + const starterMoveData = systemData['starterMoveData']; + for (let s of Object.keys(starterMoveData)) + this.starterData[s].moveset = starterMoveData[s]; } - } + + if (systemData['starterEggMoveData']) { + const starterEggMoveData = systemData['starterEggMoveData']; + for (let s of Object.keys(starterEggMoveData)) + this.starterData[s].eggMoves = starterEggMoveData[s]; + } + } else + this.starterData = systemData.starterData; if (systemData.gameStats) this.gameStats = systemData.gameStats; @@ -348,6 +369,16 @@ export class GameData { this.consolidateDexData(this.dexData); this.defaultDexData = null; + if (initStarterData) { + const starterIds = Object.keys(this.starterData).map(s => parseInt(s) as Species); + for (let s of starterIds) { + this.starterData[s].candyCount += this.dexData[s].caughtCount; + this.starterData[s].candyCount += this.dexData[s].hatchedCount * 2; + if (this.dexData[s].caughtAttr & DexAttr.SHINY) + this.starterData[s].candyCount += 4; + } + } + resolve(true); } catch (err) { console.error(err); @@ -388,7 +419,7 @@ export class GameData { return ret; } - return k.endsWith('Attr') && k !== 'natureAttr' ? BigInt(v) : v; + return k.endsWith('Attr') && ![ 'natureAttr', 'passiveAttr', 'variantAttr' ].includes(k) ? BigInt(v) : v; }) as SystemSaveData; } @@ -906,15 +937,23 @@ export class GameData { this.dexData = data; } - private initEggMoveData(): void { - const data: StarterEggMoveData = {}; - - const starterSpeciesIds = Object.keys(speciesEggMoves).map(k => parseInt(k) as Species); + private initStarterData(): void { + const starterData: StarterData = {}; - for (let speciesId of starterSpeciesIds) - data[speciesId] = 0; + const starterSpeciesIds = Object.keys(speciesStarters).map(k => parseInt(k) as Species); - this.starterEggMoveData = data; + for (let speciesId of starterSpeciesIds) { + starterData[speciesId] = { + moveset: null, + eggMoves: 0, + candyCount: 0, + passiveAttr: 0, + variantAttr: 0, + valueReduction: 0 + }; + } + + this.starterData = starterData; } setPokemonSeen(pokemon: Pokemon, incrementCount: boolean = true): void { @@ -943,6 +982,10 @@ export class GameData { pokemon.formIndex = formIndex; dexEntry.caughtAttr |= dexAttr; dexEntry.natureAttr |= Math.pow(2, pokemon.nature + 1); + + const hasPrevolution = pokemonPrevolutions.hasOwnProperty(species.speciesId); + const newCatch = !caughtAttr; + if (incrementCount) { if (!fromEgg) { dexEntry.caughtCount++; @@ -963,11 +1006,11 @@ export class GameData { if (pokemon.isShiny()) this.gameStats.shinyPokemonHatched++; } + + if (!hasPrevolution) + this.addStarterCandy(species, (1 * (pokemon.isShiny() ? 5 : 1)) * (fromEgg || pokemon.isBoss() ? 2 : 1)); } - - const hasPrevolution = pokemonPrevolutions.hasOwnProperty(species.speciesId); - const newCatch = !caughtAttr; - + const checkPrevolution = () => { if (hasPrevolution) { const prevolutionSpecies = pokemonPrevolutions[species.speciesId]; @@ -984,6 +1027,11 @@ export class GameData { }); } + addStarterCandy(species: PokemonSpecies, count: integer): void { + this.scene.candyBar.showStarterSpeciesCandy(species.speciesId, count); + this.starterData[species.speciesId].candyCount += count; + } + setEggMoveUnlocked(species: PokemonSpecies, eggMoveIndex: integer): Promise { return new Promise(resolve => { const speciesId = species.speciesId; @@ -992,17 +1040,17 @@ export class GameData { return; } - if (!this.starterEggMoveData.hasOwnProperty(speciesId)) - this.starterEggMoveData[speciesId] = 0; + if (!this.starterData[speciesId].eggMoves) + this.starterData[speciesId].eggMoves = 0; const value = Math.pow(2, eggMoveIndex); - if (this.starterEggMoveData[speciesId] & value) { + if (this.starterData[speciesId].eggMoves & value) { resolve(false); return; } - this.starterEggMoveData[speciesId] |= value; + this.starterData[speciesId].eggMoves |= value; this.scene.playSound('level_up_fanfare'); this.scene.ui.showText(`${eggMoveIndex === 3 ? 'Rare ' : ''}Egg Move unlocked: ${allMoves[speciesEggMoves[speciesId][eggMoveIndex]].name}`, null, () => resolve(true), null, true); @@ -1094,7 +1142,6 @@ export class GameData { getSpeciesStarterValue(speciesId: Species): number { const baseValue = speciesStarters[speciesId]; let value = baseValue; - const caughtHatchedCount = this.dexData[speciesId].caughtCount + this.dexData[speciesId].hatchedCount; const decrementValue = (value: number) => { if (value > 1) @@ -1104,44 +1151,8 @@ export class GameData { return value; } - let thresholdA: integer; - let thresholdB: integer; - - switch (baseValue) { - case 1: - [ thresholdA, thresholdB ] = [ 25, 100 ]; - break; - case 2: - [ thresholdA, thresholdB ] = [ 20, 70 ]; - break; - case 3: - [ thresholdA, thresholdB ] = [ 15, 50 ]; - break; - case 4: - [ thresholdA, thresholdB ] = [ 10, 30 ]; - break; - case 5: - [ thresholdA, thresholdB ] = [ 8, 25 ]; - break; - case 6: - [ thresholdA, thresholdB ] = [ 5, 15 ]; - break; - case 7: - [ thresholdA, thresholdB ] = [ 4, 12 ]; - break; - case 8: - [ thresholdA, thresholdB ] = [ 3, 10 ]; - break; - default: - [ thresholdA, thresholdB ] = [ 2, 5 ]; - break; - } - - if (caughtHatchedCount >= thresholdA) { + for (let v = 0; v < this.starterData[speciesId].valueReduction; v++) value = decrementValue(value); - if (caughtHatchedCount >= thresholdB) - value = decrementValue(value); - } return value; } diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 0ad01dd0f36..d9cb8b5d44f 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -17,7 +17,9 @@ export default class PokemonData { public species: Species; public formIndex: integer; public abilityIndex: integer; + public passive: boolean; public shiny: boolean; + public variant: integer; public pokeball: PokeballType; public level: integer; public exp: integer; @@ -53,7 +55,9 @@ export default class PokemonData { this.species = sourcePokemon ? sourcePokemon.species.speciesId : source.species; this.formIndex = Math.max(Math.min(source.formIndex, getPokemonSpecies(this.species).forms.length - 1), 0); this.abilityIndex = source.abilityIndex; + this.passive = source.passive; this.shiny = source.shiny; + this.variant = source.variant; this.pokeball = source.pokeball; this.level = source.level; this.exp = source.exp; diff --git a/src/ui/abstact-option-select-ui-handler.ts b/src/ui/abstact-option-select-ui-handler.ts index a8d55bc9d12..2f2c4face89 100644 --- a/src/ui/abstact-option-select-ui-handler.ts +++ b/src/ui/abstact-option-select-ui-handler.ts @@ -4,6 +4,7 @@ import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; import * as Utils from "../utils"; +import { argbFromRgba } from "@material/material-color-utilities"; export interface OptionSelectConfig { xOffset?: number; @@ -16,9 +17,11 @@ export interface OptionSelectConfig { export interface OptionSelectItem { label: string; - handler: Function; + handler: () => boolean; keepOpen?: boolean; overrideSound?: boolean; + item?: string; + itemArgs?: any[] } const scrollUpLabel = '↑'; @@ -28,6 +31,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { protected optionSelectContainer: Phaser.GameObjects.Container; protected optionSelectBg: Phaser.GameObjects.NineSlice; protected optionSelectText: Phaser.GameObjects.Text; + protected optionSelectIcons: Phaser.GameObjects.Sprite[]; protected config: OptionSelectConfig; @@ -58,6 +62,8 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { this.optionSelectBg.setOrigin(1, 1); this.optionSelectContainer.add(this.optionSelectBg); + this.optionSelectIcons = []; + this.setCursor(0); } @@ -66,8 +72,12 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { if (this.optionSelectText) this.optionSelectText.destroy(); + if (this.optionSelectIcons?.length) { + this.optionSelectIcons.map(i => i.destroy()); + this.optionSelectIcons.splice(0, this.optionSelectIcons.length); + } - this.optionSelectText = addTextObject(this.scene, 0, 0, options.map(o => o.label).join('\n'), TextStyle.WINDOW, { maxLines: options.length }); + this.optionSelectText = addTextObject(this.scene, 0, 0, options.map(o => o.item ? ` ${o.label}` : o.label).join('\n'), TextStyle.WINDOW, { maxLines: options.length }); this.optionSelectText.setLineSpacing(12); this.optionSelectContainer.add(this.optionSelectText); this.optionSelectContainer.setPosition((this.scene.game.canvas.width / 6) - 1 - (this.config?.xOffset || 0), -48 + (this.config?.yOffset || 0)); @@ -80,6 +90,31 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { this.optionSelectBg.height = this.getWindowHeight(); this.optionSelectText.setPositionRelative(this.optionSelectBg, 16, 9); + + options.forEach((option: OptionSelectItem, i: integer) => { + if (option.item) { + const itemIcon = this.scene.add.sprite(0, 0, 'items', option.item); + itemIcon.setScale(0.5); + this.optionSelectIcons.push(itemIcon); + + this.optionSelectContainer.add(itemIcon); + + itemIcon.setPositionRelative(this.optionSelectText, 6, 7 + 16 * i); + + if (option.item === 'candy') { + const itemOverlayIcon = this.scene.add.sprite(0, 0, 'items', 'candy_overlay'); + itemOverlayIcon.setScale(0.5); + this.optionSelectIcons.push(itemOverlayIcon); + + this.optionSelectContainer.add(itemOverlayIcon); + + itemOverlayIcon.setPositionRelative(this.optionSelectText, 6, 7 + 16 * i); + + itemIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(option.itemArgs[0]))); + itemOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(option.itemArgs[1]))); + } + } + }); } show(args: any[]): boolean { @@ -132,10 +167,12 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { return false; } const option = this.config.options[this.cursor + (this.scrollCursor - (this.scrollCursor ? 1 : 0))]; - option.handler(); - if (!option.keepOpen) - this.clear(); - playSound = !option.overrideSound; + if (option.handler()) { + if (!option.keepOpen) + this.clear(); + playSound = !option.overrideSound; + } else + ui.playError(); } else { switch (button) { case Button.UP: @@ -182,12 +219,12 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { if (optionStartIndex) options.unshift({ label: scrollUpLabel, - handler: () => { } + handler: () => true }); if (optionEndIndex < optionsScrollTotal) options.push({ label: scrollDownLabel, - handler: () => { } + handler: () => true }); } diff --git a/src/ui/candy-bar.ts b/src/ui/candy-bar.ts new file mode 100644 index 00000000000..2219e07479d --- /dev/null +++ b/src/ui/candy-bar.ts @@ -0,0 +1,123 @@ +import BattleScene, { starterColors } from "../battle-scene"; +import { TextStyle, addTextObject } from "./text"; +import { argbFromRgba } from "@material/material-color-utilities"; +import * as Utils from "../utils"; +import { Species } from "#app/data/enums/species"; + +export default class CandyBar extends Phaser.GameObjects.Container { + private bg: Phaser.GameObjects.NineSlice; + private candyIcon: Phaser.GameObjects.Sprite; + private candyOverlayIcon: Phaser.GameObjects.Sprite; + private countText: Phaser.GameObjects.Text; + private speciesId: Species; + + private tween: Phaser.Tweens.Tween; + private autoHideTimer: number; + + public shown: boolean; + + constructor(scene: BattleScene) { + super(scene, (scene.game.canvas.width / 6), -((scene.game.canvas.height) / 6) + 15); + } + + setup(): void { + this.bg = this.scene.add.nineslice(0, 0, 'party_exp_bar', null, 8, 18, 21, 5, 6, 4); + this.bg.setOrigin(0, 0); + + this.add(this.bg); + + this.candyIcon = this.scene.add.sprite(14, 0, 'items', 'candy'); + this.candyIcon.setOrigin(0.5, 0); + this.candyIcon.setScale(0.5); + + this.add(this.candyIcon); + + this.candyOverlayIcon = this.scene.add.sprite(14, 0, 'items', 'candy_overlay'); + this.candyOverlayIcon.setOrigin(0.5, 0); + this.candyOverlayIcon.setScale(0.5); + + this.add(this.candyOverlayIcon); + + this.countText = addTextObject(this.scene, 22, 4, '', TextStyle.BATTLE_INFO); + this.countText.setOrigin(0, 0); + this.add(this.countText); + + this.setVisible(false); + this.shown = false; + } + + showStarterSpeciesCandy(starterSpeciesId: Species, count: integer): Promise { + return new Promise(resolve => { + if (this.shown) { + if (this.speciesId === starterSpeciesId) + return resolve(); + else + return this.hide().then(() => this.showStarterSpeciesCandy(starterSpeciesId, count)).then(() => resolve()); + } + + const colorScheme = starterColors[starterSpeciesId]; + + this.candyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0]))); + this.candyOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1]))); + + this.countText.setText(`${(this.scene as BattleScene).gameData.starterData[starterSpeciesId].candyCount + count} (+${count.toString()})`); + + this.bg.width = this.countText.displayWidth + 28; + + (this.scene as BattleScene).fieldUI.bringToTop(this); + + if (this.tween) + this.tween.stop(); + + this.tween = this.scene.tweens.add({ + targets: this, + x: (this.scene.game.canvas.width / 6) - (this.bg.width - 5), + duration: 500, + ease: 'Sine.easeOut', + onComplete: () => { + this.tween = null; + this.resetAutoHideTimer(); + resolve(); + } + }); + + this.setVisible(true); + this.shown = true; + }); + } + + hide(): Promise { + return new Promise(resolve => { + if (!this.shown) + return resolve(); + + if (this.autoHideTimer) + clearInterval(this.autoHideTimer); + + if (this.tween) + this.tween.stop(); + + this.tween = this.scene.tweens.add({ + targets: this, + x: (this.scene.game.canvas.width / 6), + duration: 500, + ease: 'Sine.easeIn', + onComplete: () => { + this.tween = null; + this.shown = false; + this.setVisible(false); + resolve(); + } + }); + }); + } + + resetAutoHideTimer(): void { + if (this.autoHideTimer) + clearInterval(this.autoHideTimer); + this.autoHideTimer = setTimeout(() => { + this.hide(); + this.autoHideTimer = null; + }, 2500); + } +} \ No newline at end of file diff --git a/src/ui/confirm-ui-handler.ts b/src/ui/confirm-ui-handler.ts index e4dfb622457..d9e7726d826 100644 --- a/src/ui/confirm-ui-handler.ts +++ b/src/ui/confirm-ui-handler.ts @@ -20,11 +20,17 @@ export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler { options: [ { label: 'Yes', - handler: args[0] + handler: () => { + args[0](); + return true; + } }, { label: 'No', - handler: args[1] + handler: () => { + args[1](); + return true; + } } ], delay: args.length >= 6 && args[5] !== null ? args[5] as integer : 0 diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index 7c50d9241b7..88a80f7d21d 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -5,7 +5,7 @@ import * as Utils from "../utils"; import { addWindow } from "./ui-theme"; import MessageUiHandler from "./message-ui-handler"; import { GameDataType } from "../system/game-data"; -import { OptionSelectConfig } from "./abstact-option-select-ui-handler"; +import { OptionSelectConfig, OptionSelectItem } from "./abstact-option-select-ui-handler"; import { Tutorial, handleTutorial } from "../tutorial"; import { updateUserInfo } from "../account"; @@ -99,6 +99,7 @@ export default class MenuUiHandler extends MessageUiHandler { callback(i); ui.revertMode(); ui.showText(null, 0); + return true; } }; }).concat([{ @@ -106,6 +107,7 @@ export default class MenuUiHandler extends MessageUiHandler { handler: () => { ui.revertMode(); ui.showText(null, 0); + return true; } }]), xOffset: 98 @@ -117,7 +119,10 @@ export default class MenuUiHandler extends MessageUiHandler { if (Utils.isLocal) { manageDataOptions.push({ label: 'Import Session', - handler: () => confirmSlot('Select a slot to import to.', () => true, slotId => this.scene.gameData.importData(GameDataType.SESSION, slotId)), + handler: () => { + confirmSlot('Select a slot to import to.', () => true, slotId => this.scene.gameData.importData(GameDataType.SESSION, slotId)); + return true; + }, keepOpen: true }); } @@ -137,25 +142,35 @@ export default class MenuUiHandler extends MessageUiHandler { i => dataSlots.indexOf(i) > -1, slotId => this.scene.gameData.tryExportData(GameDataType.SESSION, slotId)); }); + return true; }, keepOpen: true }); if (Utils.isLocal) { manageDataOptions.push({ label: 'Import Data', - handler: () => this.scene.gameData.importData(GameDataType.SYSTEM), + handler: () => { + this.scene.gameData.importData(GameDataType.SYSTEM); + return true; + }, keepOpen: true }); } manageDataOptions.push( { label: 'Export Data', - handler: () => this.scene.gameData.tryExportData(GameDataType.SYSTEM), + handler: () => { + this.scene.gameData.tryExportData(GameDataType.SYSTEM); + return true; + }, keepOpen: true }, { label: 'Cancel', - handler: () => this.scene.ui.revertMode() + handler: () => { + this.scene.ui.revertMode(); + return true; + } } ); @@ -164,25 +179,37 @@ export default class MenuUiHandler extends MessageUiHandler { options: manageDataOptions }; - const communityOptions = [ + const communityOptions: OptionSelectItem[] = [ { label: 'Wiki', - handler: () => window.open(wikiUrl, '_blank').focus(), + handler: () => { + window.open(wikiUrl, '_blank').focus(); + return true; + }, keepOpen: true }, { label: 'Discord', - handler: () => window.open(discordUrl, '_blank').focus(), + handler: () => { + window.open(discordUrl, '_blank').focus(); + return true; + }, keepOpen: true }, { label: 'GitHub', - handler: () => window.open(githubUrl, '_blank').focus(), + handler: () => { + window.open(githubUrl, '_blank').focus(); + return true; + }, keepOpen: true }, { label: 'Cancel', - handler: () => this.scene.ui.revertMode() + handler: () => { + this.scene.ui.revertMode(); + return true; + } } ]; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 24e9f11cbb7..1604de259de 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -1,15 +1,14 @@ -import BattleScene, { Button } from "../battle-scene"; -import PokemonSpecies, { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, speciesStarters } from "../data/pokemon-species"; +import BattleScene, { Button, starterColors } from "../battle-scene"; +import PokemonSpecies, { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species"; import { Species } from "../data/enums/species"; -import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text"; +import { TextStyle, addBBCodeTextObject, addTextObject } from "./text"; import { Mode } from "./ui"; import MessageUiHandler from "./message-ui-handler"; import { Gender, getGenderColor, getGenderSymbol } from "../data/gender"; import { allAbilities } from "../data/ability"; -import { GameMode, GameModes, gameModes } from "../game-mode"; -import { Unlockables } from "../system/unlockables"; +import { GameModes, gameModes } from "../game-mode"; import { GrowthRate, getGrowthRateColor } from "../data/exp"; -import { DexAttr, DexAttrProps, DexEntry, StarterFormMoveData, StarterMoveset } from "../system/game-data"; +import { DexAttr, DexAttrProps, DexEntry, Passive as PassiveAttr, StarterFormMoveData, StarterMoveset } from "../system/game-data"; import * as Utils from "../utils"; import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler"; import { StatsContainer } from "./stats-container"; @@ -24,17 +23,65 @@ import { Type } from "../data/type"; import { Moves } from "../data/enums/moves"; import { speciesEggMoves } from "../data/egg-moves"; import { TitlePhase } from "../phases"; +import { argbFromRgba } from "@material/material-color-utilities"; +import { OptionSelectItem } from "./abstact-option-select-ui-handler"; export type StarterSelectCallback = (starters: Starter[]) => void; export interface Starter { species: PokemonSpecies; dexAttr: bigint; + passive: boolean; + variant: integer; nature: Nature; moveset?: StarterMoveset; pokerus: boolean; } +function getPassiveCandyCount(baseValue: integer): integer { + switch (baseValue) { + case 1: + return 100; + case 2: + return 75; + case 3: + return 50; + case 4: + return 40 + case 5: + return 30; + case 6: + return 20; + case 7: + return 15; + default: + return 10; + } +} + +function getValueReductionCandyCounts(baseValue: integer): [integer, integer] { + switch (baseValue) { + case 1: + return [ 50, 150 ]; + case 2: + return [ 30, 100 ]; + case 3: + return [ 25, 75 ]; + case 4: + return [ 20, 60 ]; + case 5: + return [ 15, 50 ]; + case 6: + return [ 10, 30 ]; + case 7: + return [ 8, 20 ]; + case 8: + return [ 5, 15 ]; + default: + return [ 3, 10 ]; + } +} + const gens = [ 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX' ]; export default class StarterSelectUiHandler extends MessageUiHandler { @@ -52,10 +99,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private pokemonUncaughtText: Phaser.GameObjects.Text; private pokemonAbilityLabelText: Phaser.GameObjects.Text; private pokemonAbilityText: Phaser.GameObjects.Text; + private pokemonPassiveLabelText: Phaser.GameObjects.Text; + private pokemonPassiveText: Phaser.GameObjects.Text; private pokemonNatureLabelText: Phaser.GameObjects.Text; private pokemonNatureText: BBCodeText; - private pokemonCaughtCountLabelText: Phaser.GameObjects.Text; - private pokemonCaughtCountText: Phaser.GameObjects.Text; private pokemonMovesContainer: Phaser.GameObjects.Container; private pokemonMoveContainers: Phaser.GameObjects.Container[]; private pokemonMoveBgs: Phaser.GameObjects.NineSlice[]; @@ -65,6 +112,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private pokemonEggMoveContainers: Phaser.GameObjects.Container[]; private pokemonEggMoveBgs: Phaser.GameObjects.NineSlice[]; private pokemonEggMoveLabels: Phaser.GameObjects.Text[]; + private pokemonCandyIcon: Phaser.GameObjects.Sprite; + private pokemonCandyOverlayIcon: Phaser.GameObjects.Sprite; + private pokemonCandyCountText: Phaser.GameObjects.Text; + private pokemonCaughtHatchedContainer: Phaser.GameObjects.Container; + private pokemonCaughtCountText: Phaser.GameObjects.Text; + private pokemonHatchedCountText: Phaser.GameObjects.Text; private genOptionsText: Phaser.GameObjects.Text; private instructionsText: Phaser.GameObjects.Text; private starterSelectMessageBox: Phaser.GameObjects.NineSlice; @@ -179,41 +232,77 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.type2Icon.setOrigin(0, 0); this.starterSelectContainer.add(this.type2Icon); + this.pokemonCandyIcon = this.scene.add.sprite(1, 12, 'items', 'candy'); + this.pokemonCandyIcon.setScale(0.5); + this.pokemonCandyIcon.setOrigin(0, 0); + this.starterSelectContainer.add(this.pokemonCandyIcon); + + this.pokemonCandyOverlayIcon = this.scene.add.sprite(1, 12, 'items', 'candy_overlay'); + this.pokemonCandyOverlayIcon.setScale(0.5); + this.pokemonCandyOverlayIcon.setOrigin(0, 0); + this.starterSelectContainer.add(this.pokemonCandyOverlayIcon); + + this.pokemonCandyCountText = addTextObject(this.scene, 14, 18, 'x0', TextStyle.WINDOW_ALT, { fontSize: '56px' }); + this.pokemonCandyCountText.setOrigin(0, 0); + this.starterSelectContainer.add(this.pokemonCandyCountText); + + this.pokemonCaughtHatchedContainer = this.scene.add.container(2, 25); + this.pokemonCaughtHatchedContainer.setScale(0.5); + this.starterSelectContainer.add(this.pokemonCaughtHatchedContainer); + + const pokemonCaughtIcon = this.scene.add.sprite(1, 0, 'items', 'pb'); + pokemonCaughtIcon.setOrigin(0, 0); + pokemonCaughtIcon.setScale(0.75); + this.pokemonCaughtHatchedContainer.add(pokemonCaughtIcon); + + this.pokemonCaughtCountText = addTextObject(this.scene, 24, 4, '0', TextStyle.SUMMARY_ALT); + this.pokemonCaughtCountText.setOrigin(0, 0); + this.pokemonCaughtHatchedContainer.add(this.pokemonCaughtCountText); + + const pokemonHatchedIcon = this.scene.add.sprite(1, 14, 'items', 'mystery_egg'); + pokemonHatchedIcon.setOrigin(0, 0); + pokemonHatchedIcon.setScale(0.75); + this.pokemonCaughtHatchedContainer.add(pokemonHatchedIcon); + + this.pokemonHatchedCountText = addTextObject(this.scene, 24, 19, '0', TextStyle.SUMMARY_ALT); + this.pokemonHatchedCountText.setOrigin(0, 0); + this.pokemonCaughtHatchedContainer.add(this.pokemonHatchedCountText); + this.pokemonGenderText = addTextObject(this.scene, 96, 112, '', TextStyle.SUMMARY_ALT); this.pokemonGenderText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonGenderText); - this.pokemonUncaughtText = addTextObject(this.scene, 6, 126, 'Uncaught', TextStyle.SUMMARY_ALT, { fontSize: '56px' }); + this.pokemonUncaughtText = addTextObject(this.scene, 6, 127, 'Uncaught', TextStyle.SUMMARY_ALT, { fontSize: '56px' }); this.pokemonUncaughtText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonUncaughtText); - this.pokemonAbilityLabelText = addTextObject(this.scene, 6, 126, 'Ability:', TextStyle.SUMMARY_ALT, { fontSize: '56px' }); + this.pokemonAbilityLabelText = addTextObject(this.scene, 6, 127, 'Ability:', TextStyle.SUMMARY_ALT, { fontSize: '56px' }); this.pokemonAbilityLabelText.setOrigin(0, 0); this.pokemonAbilityLabelText.setVisible(false); this.starterSelectContainer.add(this.pokemonAbilityLabelText); - this.pokemonAbilityText = addTextObject(this.scene, 30, 126, '', TextStyle.SUMMARY_ALT, { fontSize: '56px' }); + this.pokemonAbilityText = addTextObject(this.scene, 31, 127, '', TextStyle.SUMMARY_ALT, { fontSize: '56px' }); this.pokemonAbilityText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonAbilityText); - this.pokemonNatureLabelText = addTextObject(this.scene, 6, 135, 'Nature:', TextStyle.SUMMARY_ALT, { fontSize: '56px' }); + this.pokemonPassiveLabelText = addTextObject(this.scene, 6, 136, 'Passive:', TextStyle.SUMMARY_ALT, { fontSize: '56px' }); + this.pokemonPassiveLabelText.setOrigin(0, 0); + this.pokemonPassiveLabelText.setVisible(false); + this.starterSelectContainer.add(this.pokemonPassiveLabelText); + + this.pokemonPassiveText = addTextObject(this.scene, 31, 136, '', TextStyle.SUMMARY_ALT, { fontSize: '56px' }); + this.pokemonPassiveText.setOrigin(0, 0); + this.starterSelectContainer.add(this.pokemonPassiveText); + + this.pokemonNatureLabelText = addTextObject(this.scene, 6, 145, 'Nature:', TextStyle.SUMMARY_ALT, { fontSize: '56px' }); this.pokemonNatureLabelText.setOrigin(0, 0); this.pokemonNatureLabelText.setVisible(false); this.starterSelectContainer.add(this.pokemonNatureLabelText); - this.pokemonNatureText = addBBCodeTextObject(this.scene, 30, 135, '', TextStyle.SUMMARY_ALT, { fontSize: '56px' }); + this.pokemonNatureText = addBBCodeTextObject(this.scene, 31, 145, '', TextStyle.SUMMARY_ALT, { fontSize: '56px' }); this.pokemonNatureText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonNatureText); - this.pokemonCaughtCountLabelText = addTextObject(this.scene, 6, 144, 'Caught/Hatched:', TextStyle.SUMMARY_ALT, { fontSize: '56px' }); - this.pokemonCaughtCountLabelText.setOrigin(0, 0); - this.pokemonCaughtCountLabelText.setVisible(false); - this.starterSelectContainer.add(this.pokemonCaughtCountLabelText); - - this.pokemonCaughtCountText = addTextObject(this.scene, 58, 144, '0/0 (0)', TextStyle.SUMMARY_ALT, { fontSize: '56px' }); - this.pokemonCaughtCountText.setOrigin(0, 0); - this.starterSelectContainer.add(this.pokemonCaughtCountText); - this.pokemonMoveContainers = []; this.pokemonMoveBgs = []; this.pokemonMoveLabels = []; @@ -373,7 +462,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonEggMovesContainer = this.scene.add.container(102, 85); this.pokemonEggMovesContainer.setScale(0.375); - const eggMovesLabel = addTextObject(this.scene, -46, 0, 'Egg Moves', TextStyle.SUMMARY_ALT); + const eggMovesLabel = addTextObject(this.scene, -46, 0, 'Egg Moves', TextStyle.WINDOW_ALT); eggMovesLabel.setOrigin(0.5, 0); this.pokemonEggMovesContainer.add(eggMovesLabel); @@ -622,6 +711,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { ui.playSelect(); } else ui.playError(); + return true; }, overrideSound: true }, @@ -630,6 +720,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { handler: () => { this.toggleStatsMode(); ui.setMode(Mode.STARTER_SELECT); + return true; } } ]; @@ -639,7 +730,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { ui.showText('Select a move to swap out.', null, () => { ui.setModeWithoutClear(Mode.OPTION_SELECT, { options: moveset.map((m: Moves, i: number) => { - return { + const option: OptionSelectItem = { label: allMoves[m].name, handler: () => { ui.setMode(Mode.STARTER_SELECT).then(() => { @@ -647,29 +738,37 @@ export default class StarterSelectUiHandler extends MessageUiHandler { ui.setModeWithoutClear(Mode.OPTION_SELECT, { options: this.speciesStarterMoves.filter((sm: Moves) => sm !== m).map(sm => { // make an option for each available starter move - return { + const option = { label: allMoves[sm].name, handler: () => { this.switchMoveHandler(i, sm, m) showSwapOptions(this.starterMoveset); + return true; } }; + return option; }).concat({ label: 'Cancel', - handler: () => showSwapOptions(this.starterMoveset) + handler: () => { + showSwapOptions(this.starterMoveset); + return true; + } }), maxOptions: 8, yOffset: 19 }); }); }); + return true; } - } + }; + return option; }).concat({ label: 'Cancel', handler: () => { this.clearText(); ui.setMode(Mode.STARTER_SELECT); + return true; } }), maxOptions: 8, @@ -682,6 +781,103 @@ export default class StarterSelectUiHandler extends MessageUiHandler { label: 'Manage Moves', handler: () => { showSwapOptions(this.starterMoveset); + return true; + } + }); + const starterData = this.scene.gameData.starterData[this.lastSpecies.speciesId]; + const candyCount = starterData.candyCount; + const passiveAttr = starterData.passiveAttr; + if (passiveAttr & PassiveAttr.UNLOCKED) { + if (!(passiveAttr & PassiveAttr.ENABLED)) { + options.push({ + label: 'Enable Passive', + handler: () => { + starterData.passiveAttr |= PassiveAttr.ENABLED; + ui.setMode(Mode.STARTER_SELECT); + this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined); + return true; + } + }); + } else { + options.push({ + label: 'Disable Passive', + handler: () => { + starterData.passiveAttr ^= PassiveAttr.ENABLED; + ui.setMode(Mode.STARTER_SELECT); + this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined); + return true; + } + }); + } + } + const showUseCandies = () => { + const options = []; + if (!(passiveAttr & PassiveAttr.UNLOCKED)) { + const passiveCost = getPassiveCandyCount(speciesStarters[this.lastSpecies.speciesId]); + options.push({ + label: `x${passiveCost} Unlock Passive (${allAbilities[starterPassiveAbilities[this.lastSpecies.speciesId]].name})`, + handler: () => { + if (candyCount >= passiveCost) { + starterData.passiveAttr |= PassiveAttr.UNLOCKED | PassiveAttr.ENABLED; + starterData.candyCount -= passiveCost; + this.pokemonCandyCountText.setText(`x${starterData.candyCount}`); + this.scene.gameData.saveSystem().then(success => { + if (!success) + return this.scene.reset(true); + }); + ui.setMode(Mode.STARTER_SELECT); + this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined); + return true; + } + return false; + }, + item: 'candy', + itemArgs: starterColors[this.lastSpecies.speciesId] + }); + } + const valueReduction = starterData.valueReduction; + if (valueReduction < 2) { + const reductionCost = getValueReductionCandyCounts(speciesStarters[this.lastSpecies.speciesId])[valueReduction]; + options.push({ + label: `x${reductionCost} Reduce Cost`, + handler: () => { + if (candyCount >= reductionCost) { + starterData.valueReduction++; + starterData.candyCount -= reductionCost; + this.pokemonCandyCountText.setText(`x${starterData.candyCount}`); + this.scene.gameData.saveSystem().then(success => { + if (!success) + return this.scene.reset(true); + }); + this.updateStarterValueLabel(this.cursor); + this.tryUpdateValue(0); + ui.setMode(Mode.STARTER_SELECT); + this.scene.playSound('buy'); + return true; + } + return false; + }, + item: 'candy', + itemArgs: starterColors[this.lastSpecies.speciesId] + }); + } + options.push({ + label: 'Cancel', + handler: () => { + ui.setMode(Mode.STARTER_SELECT); + return true; + } + }); + ui.setModeWithoutClear(Mode.OPTION_SELECT, { + options: options, + yOffset: 47 + }); + }; + options.push({ + label: 'Use Candies', + handler: () => { + ui.setMode(Mode.STARTER_SELECT).then(() => showUseCandies()); + return true; } }); } @@ -689,6 +885,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { label: 'Cancel', handler: () => { ui.setMode(Mode.STARTER_SELECT); + return true; } }); ui.setModeWithoutClear(Mode.OPTION_SELECT, { @@ -809,22 +1006,22 @@ export default class StarterSelectUiHandler extends MessageUiHandler { // species has different forms if (pokemonFormLevelMoves.hasOwnProperty(speciesId)) { // starterMoveData doesn't have base form moves or is using the single form format - if (!this.scene.gameData.starterMoveData.hasOwnProperty(speciesId) || Array.isArray(this.scene.gameData.starterMoveData[speciesId])) - this.scene.gameData.starterMoveData[speciesId] = { [props.formIndex]: this.starterMoveset.slice(0) as StarterMoveset }; - const starterMoveData = this.scene.gameData.starterMoveData[speciesId][props.formIndex]; + if (!this.scene.gameData.starterData[speciesId].moveset || Array.isArray(this.scene.gameData.starterData[speciesId].moveset)) + this.scene.gameData.starterData[speciesId].moveset = { [props.formIndex]: this.starterMoveset.slice(0) as StarterMoveset }; + const starterMoveData = this.scene.gameData.starterData[speciesId].moveset[props.formIndex]; // starterMoveData doesn't have active form moves if (!starterMoveData.hasOwnProperty(props.formIndex)) - this.scene.gameData.starterMoveData[speciesId][props.formIndex] = this.starterMoveset.slice(0) as StarterMoveset; + this.scene.gameData.starterData[speciesId].moveset[props.formIndex] = this.starterMoveset.slice(0) as StarterMoveset; // does the species' starter move data have its form's starter moves and has it been updated if (starterMoveData.hasOwnProperty(props.formIndex)) { // active form move hasn't been updated if (starterMoveData[props.formIndex][existingMoveIndex] !== newMove) - this.scene.gameData.starterMoveData[speciesId][props.formIndex] = this.starterMoveset.slice(0) as StarterMoveset; + this.scene.gameData.starterData[speciesId].moveset[props.formIndex] = this.starterMoveset.slice(0) as StarterMoveset; } } else - this.scene.gameData.starterMoveData[speciesId] = this.starterMoveset.slice(0) as StarterMoveset; + this.scene.gameData.starterData[speciesId].moveset = this.starterMoveset.slice(0) as StarterMoveset; this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined, false); } @@ -902,29 +1099,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { for (let s = 0; s < 81; s++) { const speciesId = s < genLimit ? this.genSpecies[genCursorWithScroll][s].speciesId : 0 as Species; const slotVisible = !!speciesId; - if (slotVisible) { - const baseStarterValue = speciesStarters[speciesId]; - const starterValue = slotVisible ? this.scene.gameData.getSpeciesStarterValue(speciesId) : 0; - let valueStr = starterValue.toString(); - if (valueStr.startsWith('0.')) - valueStr = valueStr.slice(1); - this.starterValueLabels[s].setText(valueStr); - let textStyle: TextStyle; - switch (baseStarterValue - starterValue) { - case 0: - textStyle = TextStyle.WINDOW; - break; - case 1: - case 0.5: - textStyle = TextStyle.SUMMARY_BLUE; - break; - default: - textStyle = TextStyle.SUMMARY_GOLD; - break; - } - this.starterValueLabels[s].setColor(this.getTextColor(textStyle)); - this.starterValueLabels[s].setShadowColor(this.getTextColor(textStyle, true)); - } + if (slotVisible) + this.updateStarterValueLabel(s); this.starterValueLabels[s].setVisible(slotVisible); this.shinyIcons[s].setVisible(slotVisible && !!this.scene.gameData.dexData[speciesId].caughtAttr && !!(this.scene.gameData.dexData[speciesId].caughtAttr & DexAttr.SHINY)); } @@ -1007,16 +1183,25 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonNameText.setText(species.name); if (this.speciesStarterDexEntry?.caughtAttr) { + const colorScheme = starterColors[species.speciesId]; + this.pokemonGrowthRateText.setText(Utils.toReadableString(GrowthRate[species.growthRate])); this.pokemonGrowthRateText.setColor(getGrowthRateColor(species.growthRate)); this.pokemonGrowthRateText.setShadowColor(getGrowthRateColor(species.growthRate, true)); this.pokemonGrowthRateLabelText.setVisible(true); this.pokemonUncaughtText.setVisible(false); this.pokemonAbilityLabelText.setVisible(true); + this.pokemonPassiveLabelText.setVisible(true); this.pokemonNatureLabelText.setVisible(true); - this.pokemonCaughtCountLabelText.setVisible(true); - this.pokemonCaughtCountText.setText(`${this.speciesStarterDexEntry.caughtCount}/${this.speciesStarterDexEntry.hatchedCount} (${this.speciesStarterDexEntry.caughtCount + this.speciesStarterDexEntry.hatchedCount})`); - this.pokemonCaughtCountText.setVisible(true); + this.pokemonCaughtCountText.setText(`${this.speciesStarterDexEntry.caughtCount}`); + this.pokemonHatchedCountText.setText(`${this.speciesStarterDexEntry.hatchedCount}`); + this.pokemonCaughtHatchedContainer.setVisible(true); + this.pokemonCandyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0]))); + this.pokemonCandyIcon.setVisible(true); + this.pokemonCandyOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1]))); + this.pokemonCandyOverlayIcon.setVisible(true); + this.pokemonCandyCountText.setText(`x${this.scene.gameData.starterData[species.speciesId].candyCount}`); + this.pokemonCandyCountText.setVisible(true); this.iconAnimHandler.addOrUpdate(this.starterSelectGenIconContainers[species.generation - 1].getAt(this.genSpecies[species.generation - 1].indexOf(species)) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.PASSIVE); let starterIndex = -1; @@ -1056,9 +1241,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.type2Icon.setVisible(false); this.pokemonUncaughtText.setVisible(true); this.pokemonAbilityLabelText.setVisible(false); + this.pokemonPassiveLabelText.setVisible(false); this.pokemonNatureLabelText.setVisible(false); - this.pokemonCaughtCountLabelText.setVisible(false); - this.pokemonCaughtCountText.setVisible(false); + this.pokemonCaughtHatchedContainer.setVisible(false); + this.pokemonCandyIcon.setVisible(false); + this.pokemonCandyOverlayIcon.setVisible(false); + this.pokemonCandyCountText.setVisible(false); const defaultDexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(species, true); const defaultNature = this.scene.gameData.getSpeciesDefaultNature(species); @@ -1076,9 +1264,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.type2Icon.setVisible(false); this.pokemonUncaughtText.setVisible(!!species); this.pokemonAbilityLabelText.setVisible(false); + this.pokemonPassiveLabelText.setVisible(false); this.pokemonNatureLabelText.setVisible(false); - this.pokemonCaughtCountLabelText.setVisible(false); - this.pokemonCaughtCountText.setVisible(false); + this.pokemonCaughtHatchedContainer.setVisible(false); + this.pokemonCandyIcon.setVisible(false); + this.pokemonCandyOverlayIcon.setVisible(false); + this.pokemonCandyCountText.setVisible(false); this.setSpeciesDetails(species, false, 0, false, 0, 0); this.pokemonSprite.clearTint(); @@ -1185,6 +1376,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonAbilityText.setColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD)); this.pokemonAbilityText.setShadowColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD, true)); + const passiveAttr = this.scene.gameData.starterData[species.speciesId].passiveAttr; + this.pokemonPassiveText.setText(passiveAttr & PassiveAttr.UNLOCKED ? passiveAttr & PassiveAttr.ENABLED ? allAbilities[starterPassiveAbilities[this.lastSpecies.speciesId]].name : 'Disabled' : 'Locked'); + this.pokemonPassiveText.setColor(this.getTextColor(passiveAttr === (PassiveAttr.UNLOCKED | PassiveAttr.ENABLED) ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GRAY)); + this.pokemonPassiveText.setShadowColor(this.getTextColor(passiveAttr === (PassiveAttr.UNLOCKED | PassiveAttr.ENABLED) ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GRAY, true)); + this.pokemonNatureText.setText(getNatureName(natureIndex as unknown as Nature, true, true, false, this.scene.uiTheme)); let levelMoves: LevelMoves; @@ -1195,18 +1391,18 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.speciesStarterMoves.push(...levelMoves.filter(lm => lm[0] <= 5).map(lm => lm[1])); if (speciesEggMoves.hasOwnProperty(species.speciesId)) { for (let em = 0; em < 4; em++) { - if (this.scene.gameData.starterEggMoveData[species.speciesId] & Math.pow(2, em)) + if (this.scene.gameData.starterData[species.speciesId].eggMoves & Math.pow(2, em)) this.speciesStarterMoves.push(speciesEggMoves[species.speciesId][em]); } } - const speciesMoveData = this.scene.gameData.starterMoveData[species.speciesId]; + const speciesMoveData = this.scene.gameData.starterData[species.speciesId].moveset; let moveData: StarterMoveset = speciesMoveData ? Array.isArray(speciesMoveData) ? speciesMoveData as StarterMoveset : (speciesMoveData as StarterFormMoveData)[formIndex] : null; - const availableStarterMoves = this.speciesStarterMoves.concat(speciesEggMoves.hasOwnProperty(species.speciesId) ? speciesEggMoves[species.speciesId].filter((_, em: integer) => this.scene.gameData.starterEggMoveData[species.speciesId] & Math.pow(2, em)) : []); + const availableStarterMoves = this.speciesStarterMoves.concat(speciesEggMoves.hasOwnProperty(species.speciesId) ? speciesEggMoves[species.speciesId].filter((_, em: integer) => this.scene.gameData.starterData[species.speciesId].eggMoves & Math.pow(2, em)) : []); this.starterMoveset = (moveData || (this.speciesStarterMoves.slice(0, 4) as StarterMoveset)).filter(m => availableStarterMoves.find(sm => sm === m)) as StarterMoveset; // Consolidate move data if it contains an incompatible move if (this.starterMoveset.length < 4 && this.starterMoveset.length < availableStarterMoves.length) @@ -1216,6 +1412,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.setTypeIcons(speciesForm.type1, speciesForm.type2); } else { this.pokemonAbilityText.setText(''); + this.pokemonPassiveText.setText(''); this.pokemonNatureText.setText(''); this.setTypeIcons(null, null); } @@ -1225,6 +1422,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonNumberText.setShadowColor(this.getTextColor(TextStyle.SUMMARY, true)); this.pokemonGenderText.setText(''); this.pokemonAbilityText.setText(''); + this.pokemonPassiveText.setText(''); this.pokemonNatureText.setText(''); this.setTypeIcons(null, null); } @@ -1243,7 +1441,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { for (let em = 0; em < 4; em++) { const eggMove = hasEggMoves ? allMoves[speciesEggMoves[species.speciesId][em]] : null; - const eggMoveUnlocked = eggMove && this.scene.gameData.starterEggMoveData.hasOwnProperty(species.speciesId) && this.scene.gameData.starterEggMoveData[species.speciesId] & Math.pow(2, em); + const eggMoveUnlocked = eggMove && this.scene.gameData.starterData[species.speciesId].eggMoves & Math.pow(2, em); this.pokemonEggMoveBgs[em].setFrame(Type[eggMove ? eggMove.type : Type.UNKNOWN].toString().toLowerCase()); this.pokemonEggMoveLabels[em].setText(eggMove && eggMoveUnlocked ? eggMove.name : '???'); } @@ -1281,6 +1479,31 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.tryUpdateValue(); } + updateStarterValueLabel(cursor: integer): void { + const speciesId = this.genSpecies[this.genCursor][cursor].speciesId; + const baseStarterValue = speciesStarters[speciesId]; + const starterValue = this.scene.gameData.getSpeciesStarterValue(speciesId); + let valueStr = starterValue.toString(); + if (valueStr.startsWith('0.')) + valueStr = valueStr.slice(1); + this.starterValueLabels[cursor].setText(valueStr); + let textStyle: TextStyle; + switch (baseStarterValue - starterValue) { + case 0: + textStyle = TextStyle.WINDOW; + break; + case 1: + case 0.5: + textStyle = TextStyle.SUMMARY_BLUE; + break; + default: + textStyle = TextStyle.SUMMARY_GOLD; + break; + } + this.starterValueLabels[cursor].setColor(this.getTextColor(textStyle)); + this.starterValueLabels[cursor].setShadowColor(this.getTextColor(textStyle, true)); + } + tryUpdateValue(add?: integer): boolean { const value = this.starterGens.reduce((total: integer, gen: integer, i: integer) => total += this.scene.gameData.getSpeciesStarterValue(this.genSpecies[gen][this.starterCursors[i]].speciesId), 0); const newValue = value + (add || 0); @@ -1331,6 +1554,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { return { species: starterSpecies, dexAttr: thisObj.starterAttr[i], + passive: !(thisObj.scene.gameData.starterData[starterSpecies.speciesId].passiveAttr ^ (PassiveAttr.ENABLED | PassiveAttr.UNLOCKED)), + variant: 0, nature: thisObj.starterNatures[i] as Nature, moveset: thisObj.starterMovesets[i], pokerus: !![ 0, 1, 2 ].filter(n => thisObj.pokerusGens[n] === starterSpecies.generation - 1 && thisObj.pokerusCursors[n] === thisObj.genSpecies[starterSpecies.generation - 1].indexOf(starterSpecies)).length diff --git a/src/ui/text.ts b/src/ui/text.ts index 6162d21b717..bfb02d06e27 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -18,6 +18,7 @@ export enum TextStyle { SUMMARY_BLUE, SUMMARY_PINK, SUMMARY_GOLD, + SUMMARY_GRAY, MONEY, SETTINGS_LABEL, SETTINGS_SELECTED, @@ -75,10 +76,14 @@ function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraStyleOptio switch (style) { case TextStyle.SUMMARY: + case TextStyle.SUMMARY_ALT: + case TextStyle.SUMMARY_BLUE: case TextStyle.SUMMARY_RED: case TextStyle.SUMMARY_PINK: case TextStyle.SUMMARY_GOLD: + case TextStyle.SUMMARY_GRAY: case TextStyle.WINDOW: + case TextStyle.WINDOW_ALT: case TextStyle.MESSAGE: case TextStyle.SETTINGS_LABEL: case TextStyle.SETTINGS_SELECTED: @@ -152,7 +157,9 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui return !shadow ? '#f89890' : '#984038'; case TextStyle.SUMMARY_GOLD: case TextStyle.MONEY: - return !shadow ? '#e8e8a8' : '#a0a060' + return !shadow ? '#e8e8a8' : '#a0a060'; + case TextStyle.SUMMARY_GRAY: + return !shadow ? '#a0a0a0' : '#636363'; case TextStyle.SETTINGS_LABEL: return !shadow ? '#f8b050' : '#c07800'; case TextStyle.SETTINGS_SELECTED: diff --git a/src/utils.ts b/src/utils.ts index 714cc5b0872..13b06714032 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -311,4 +311,14 @@ export function deltaRgb(rgb1: integer[], rgb2: integer[]): integer { const t = (r1 + r2) / 2; return Math.ceil(Math.sqrt(2 * drp2 + 4 * dgp2 + 3 * dbp2 + t * (drp2 - dbp2) / 256)); +} + +export function rgbHexToRgba(hex: string) { + const color = hex.match(/^([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i); + return { + r: parseInt(color[1], 16), + g: parseInt(color[2], 16), + b: parseInt(color[3], 16), + a: 255 + }; } \ No newline at end of file From 608c02474913ac18e1af8f05901f59cd2c2ace5e Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sat, 13 Apr 2024 19:06:33 -0400 Subject: [PATCH 12/51] Fix crash on starter select when scrolling generations --- src/ui/starter-select-ui-handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 1604de259de..66d58c7d17e 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -1480,7 +1480,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } updateStarterValueLabel(cursor: integer): void { - const speciesId = this.genSpecies[this.genCursor][cursor].speciesId; + const speciesId = this.genSpecies[this.getGenCursorWithScroll()][cursor].speciesId; const baseStarterValue = speciesStarters[speciesId]; const starterValue = this.scene.gameData.getSpeciesStarterValue(speciesId); let valueStr = starterValue.toString(); From 61ce0887ef93c816be99cab898560d46ef4daace Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sat, 13 Apr 2024 19:21:59 -0400 Subject: [PATCH 13/51] Fix candy menu not showing for species with only 1 starter move --- src/ui/starter-select-ui-handler.ts | 170 ++++++++++++++-------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 66d58c7d17e..85aa0892d2c 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -784,103 +784,103 @@ export default class StarterSelectUiHandler extends MessageUiHandler { return true; } }); - const starterData = this.scene.gameData.starterData[this.lastSpecies.speciesId]; - const candyCount = starterData.candyCount; - const passiveAttr = starterData.passiveAttr; - if (passiveAttr & PassiveAttr.UNLOCKED) { - if (!(passiveAttr & PassiveAttr.ENABLED)) { - options.push({ - label: 'Enable Passive', - handler: () => { - starterData.passiveAttr |= PassiveAttr.ENABLED; - ui.setMode(Mode.STARTER_SELECT); - this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined); - return true; - } - }); - } else { - options.push({ - label: 'Disable Passive', - handler: () => { - starterData.passiveAttr ^= PassiveAttr.ENABLED; - ui.setMode(Mode.STARTER_SELECT); - this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined); - return true; - } - }); - } - } - const showUseCandies = () => { - const options = []; - if (!(passiveAttr & PassiveAttr.UNLOCKED)) { - const passiveCost = getPassiveCandyCount(speciesStarters[this.lastSpecies.speciesId]); - options.push({ - label: `x${passiveCost} Unlock Passive (${allAbilities[starterPassiveAbilities[this.lastSpecies.speciesId]].name})`, - handler: () => { - if (candyCount >= passiveCost) { - starterData.passiveAttr |= PassiveAttr.UNLOCKED | PassiveAttr.ENABLED; - starterData.candyCount -= passiveCost; - this.pokemonCandyCountText.setText(`x${starterData.candyCount}`); - this.scene.gameData.saveSystem().then(success => { - if (!success) - return this.scene.reset(true); - }); - ui.setMode(Mode.STARTER_SELECT); - this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined); - return true; - } - return false; - }, - item: 'candy', - itemArgs: starterColors[this.lastSpecies.speciesId] - }); - } - const valueReduction = starterData.valueReduction; - if (valueReduction < 2) { - const reductionCost = getValueReductionCandyCounts(speciesStarters[this.lastSpecies.speciesId])[valueReduction]; - options.push({ - label: `x${reductionCost} Reduce Cost`, - handler: () => { - if (candyCount >= reductionCost) { - starterData.valueReduction++; - starterData.candyCount -= reductionCost; - this.pokemonCandyCountText.setText(`x${starterData.candyCount}`); - this.scene.gameData.saveSystem().then(success => { - if (!success) - return this.scene.reset(true); - }); - this.updateStarterValueLabel(this.cursor); - this.tryUpdateValue(0); - ui.setMode(Mode.STARTER_SELECT); - this.scene.playSound('buy'); - return true; - } - return false; - }, - item: 'candy', - itemArgs: starterColors[this.lastSpecies.speciesId] - }); - } + } + const starterData = this.scene.gameData.starterData[this.lastSpecies.speciesId]; + const candyCount = starterData.candyCount; + const passiveAttr = starterData.passiveAttr; + if (passiveAttr & PassiveAttr.UNLOCKED) { + if (!(passiveAttr & PassiveAttr.ENABLED)) { options.push({ - label: 'Cancel', + label: 'Enable Passive', handler: () => { + starterData.passiveAttr |= PassiveAttr.ENABLED; ui.setMode(Mode.STARTER_SELECT); + this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined); return true; } }); - ui.setModeWithoutClear(Mode.OPTION_SELECT, { - options: options, - yOffset: 47 + } else { + options.push({ + label: 'Disable Passive', + handler: () => { + starterData.passiveAttr ^= PassiveAttr.ENABLED; + ui.setMode(Mode.STARTER_SELECT); + this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined); + return true; + } }); - }; + } + } + const showUseCandies = () => { + const options = []; + if (!(passiveAttr & PassiveAttr.UNLOCKED)) { + const passiveCost = getPassiveCandyCount(speciesStarters[this.lastSpecies.speciesId]); + options.push({ + label: `x${passiveCost} Unlock Passive (${allAbilities[starterPassiveAbilities[this.lastSpecies.speciesId]].name})`, + handler: () => { + if (candyCount >= passiveCost) { + starterData.passiveAttr |= PassiveAttr.UNLOCKED | PassiveAttr.ENABLED; + starterData.candyCount -= passiveCost; + this.pokemonCandyCountText.setText(`x${starterData.candyCount}`); + this.scene.gameData.saveSystem().then(success => { + if (!success) + return this.scene.reset(true); + }); + ui.setMode(Mode.STARTER_SELECT); + this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined); + return true; + } + return false; + }, + item: 'candy', + itemArgs: starterColors[this.lastSpecies.speciesId] + }); + } + const valueReduction = starterData.valueReduction; + if (valueReduction < 2) { + const reductionCost = getValueReductionCandyCounts(speciesStarters[this.lastSpecies.speciesId])[valueReduction]; + options.push({ + label: `x${reductionCost} Reduce Cost`, + handler: () => { + if (candyCount >= reductionCost) { + starterData.valueReduction++; + starterData.candyCount -= reductionCost; + this.pokemonCandyCountText.setText(`x${starterData.candyCount}`); + this.scene.gameData.saveSystem().then(success => { + if (!success) + return this.scene.reset(true); + }); + this.updateStarterValueLabel(this.cursor); + this.tryUpdateValue(0); + ui.setMode(Mode.STARTER_SELECT); + this.scene.playSound('buy'); + return true; + } + return false; + }, + item: 'candy', + itemArgs: starterColors[this.lastSpecies.speciesId] + }); + } options.push({ - label: 'Use Candies', + label: 'Cancel', handler: () => { - ui.setMode(Mode.STARTER_SELECT).then(() => showUseCandies()); + ui.setMode(Mode.STARTER_SELECT); return true; } }); - } + ui.setModeWithoutClear(Mode.OPTION_SELECT, { + options: options, + yOffset: 47 + }); + }; + options.push({ + label: 'Use Candies', + handler: () => { + ui.setMode(Mode.STARTER_SELECT).then(() => showUseCandies()); + return true; + } + }); options.push({ label: 'Cancel', handler: () => { From c369e9ed85a3a7504e8d7dc2cb1b3e0e2ca7c389 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sat, 13 Apr 2024 19:28:32 -0400 Subject: [PATCH 14/51] Change layering on certain starter select ui elements --- src/ui/starter-select-ui-handler.ts | 92 ++++++++++++++--------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 85aa0892d2c..c0bcb549741 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -222,52 +222,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonGrowthRateText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonGrowthRateText); - this.type1Icon = this.scene.add.sprite(8, 98, 'types'); - this.type1Icon.setScale(0.5); - this.type1Icon.setOrigin(0, 0); - this.starterSelectContainer.add(this.type1Icon); - - this.type2Icon = this.scene.add.sprite(26, 98, 'types'); - this.type2Icon.setScale(0.5); - this.type2Icon.setOrigin(0, 0); - this.starterSelectContainer.add(this.type2Icon); - - this.pokemonCandyIcon = this.scene.add.sprite(1, 12, 'items', 'candy'); - this.pokemonCandyIcon.setScale(0.5); - this.pokemonCandyIcon.setOrigin(0, 0); - this.starterSelectContainer.add(this.pokemonCandyIcon); - - this.pokemonCandyOverlayIcon = this.scene.add.sprite(1, 12, 'items', 'candy_overlay'); - this.pokemonCandyOverlayIcon.setScale(0.5); - this.pokemonCandyOverlayIcon.setOrigin(0, 0); - this.starterSelectContainer.add(this.pokemonCandyOverlayIcon); - - this.pokemonCandyCountText = addTextObject(this.scene, 14, 18, 'x0', TextStyle.WINDOW_ALT, { fontSize: '56px' }); - this.pokemonCandyCountText.setOrigin(0, 0); - this.starterSelectContainer.add(this.pokemonCandyCountText); - - this.pokemonCaughtHatchedContainer = this.scene.add.container(2, 25); - this.pokemonCaughtHatchedContainer.setScale(0.5); - this.starterSelectContainer.add(this.pokemonCaughtHatchedContainer); - - const pokemonCaughtIcon = this.scene.add.sprite(1, 0, 'items', 'pb'); - pokemonCaughtIcon.setOrigin(0, 0); - pokemonCaughtIcon.setScale(0.75); - this.pokemonCaughtHatchedContainer.add(pokemonCaughtIcon); - - this.pokemonCaughtCountText = addTextObject(this.scene, 24, 4, '0', TextStyle.SUMMARY_ALT); - this.pokemonCaughtCountText.setOrigin(0, 0); - this.pokemonCaughtHatchedContainer.add(this.pokemonCaughtCountText); - - const pokemonHatchedIcon = this.scene.add.sprite(1, 14, 'items', 'mystery_egg'); - pokemonHatchedIcon.setOrigin(0, 0); - pokemonHatchedIcon.setScale(0.75); - this.pokemonCaughtHatchedContainer.add(pokemonHatchedIcon); - - this.pokemonHatchedCountText = addTextObject(this.scene, 24, 19, '0', TextStyle.SUMMARY_ALT); - this.pokemonHatchedCountText.setOrigin(0, 0); - this.pokemonCaughtHatchedContainer.add(this.pokemonHatchedCountText); - this.pokemonGenderText = addTextObject(this.scene, 96, 112, '', TextStyle.SUMMARY_ALT); this.pokemonGenderText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonGenderText); @@ -430,6 +384,52 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonSprite = this.scene.add.sprite(53, 63, `pkmn__sub`); this.starterSelectContainer.add(this.pokemonSprite); + this.type1Icon = this.scene.add.sprite(8, 98, 'types'); + this.type1Icon.setScale(0.5); + this.type1Icon.setOrigin(0, 0); + this.starterSelectContainer.add(this.type1Icon); + + this.type2Icon = this.scene.add.sprite(26, 98, 'types'); + this.type2Icon.setScale(0.5); + this.type2Icon.setOrigin(0, 0); + this.starterSelectContainer.add(this.type2Icon); + + this.pokemonCandyIcon = this.scene.add.sprite(1, 12, 'items', 'candy'); + this.pokemonCandyIcon.setScale(0.5); + this.pokemonCandyIcon.setOrigin(0, 0); + this.starterSelectContainer.add(this.pokemonCandyIcon); + + this.pokemonCandyOverlayIcon = this.scene.add.sprite(1, 12, 'items', 'candy_overlay'); + this.pokemonCandyOverlayIcon.setScale(0.5); + this.pokemonCandyOverlayIcon.setOrigin(0, 0); + this.starterSelectContainer.add(this.pokemonCandyOverlayIcon); + + this.pokemonCandyCountText = addTextObject(this.scene, 14, 18, 'x0', TextStyle.WINDOW_ALT, { fontSize: '56px' }); + this.pokemonCandyCountText.setOrigin(0, 0); + this.starterSelectContainer.add(this.pokemonCandyCountText); + + this.pokemonCaughtHatchedContainer = this.scene.add.container(2, 25); + this.pokemonCaughtHatchedContainer.setScale(0.5); + this.starterSelectContainer.add(this.pokemonCaughtHatchedContainer); + + const pokemonCaughtIcon = this.scene.add.sprite(1, 0, 'items', 'pb'); + pokemonCaughtIcon.setOrigin(0, 0); + pokemonCaughtIcon.setScale(0.75); + this.pokemonCaughtHatchedContainer.add(pokemonCaughtIcon); + + this.pokemonCaughtCountText = addTextObject(this.scene, 24, 4, '0', TextStyle.SUMMARY_ALT); + this.pokemonCaughtCountText.setOrigin(0, 0); + this.pokemonCaughtHatchedContainer.add(this.pokemonCaughtCountText); + + const pokemonHatchedIcon = this.scene.add.sprite(1, 14, 'items', 'mystery_egg'); + pokemonHatchedIcon.setOrigin(0, 0); + pokemonHatchedIcon.setScale(0.75); + this.pokemonCaughtHatchedContainer.add(pokemonHatchedIcon); + + this.pokemonHatchedCountText = addTextObject(this.scene, 24, 19, '0', TextStyle.SUMMARY_ALT); + this.pokemonHatchedCountText.setOrigin(0, 0); + this.pokemonCaughtHatchedContainer.add(this.pokemonHatchedCountText); + this.pokemonMovesContainer = this.scene.add.container(102, 16); this.pokemonMovesContainer.setScale(0.5); From 000d31f663e8801b4587acb07dec246170a42cc1 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sat, 13 Apr 2024 19:36:43 -0400 Subject: [PATCH 15/51] Fix use candy menu showing up for evolved starters --- src/ui/starter-select-ui-handler.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index c0bcb549741..dbe22516e70 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -25,6 +25,7 @@ import { speciesEggMoves } from "../data/egg-moves"; import { TitlePhase } from "../phases"; import { argbFromRgba } from "@material/material-color-utilities"; import { OptionSelectItem } from "./abstact-option-select-ui-handler"; +import { pokemonPrevolutions } from "#app/data/pokemon-evolutions"; export type StarterSelectCallback = (starters: Starter[]) => void; @@ -874,13 +875,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler { yOffset: 47 }); }; - options.push({ - label: 'Use Candies', - handler: () => { - ui.setMode(Mode.STARTER_SELECT).then(() => showUseCandies()); - return true; - } - }); + if (!pokemonPrevolutions.hasOwnProperty(this.lastSpecies.speciesId)) { + options.push({ + label: 'Use Candies', + handler: () => { + ui.setMode(Mode.STARTER_SELECT).then(() => showUseCandies()); + return true; + } + }); + } options.push({ label: 'Cancel', handler: () => { From 77368986fe55faaab5737159761f201e0aec208c Mon Sep 17 00:00:00 2001 From: Madmadness65 Date: Sat, 13 Apr 2024 19:37:42 -0500 Subject: [PATCH 16/51] Change Rotom and Carbink's passive abilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rotom naturally cannot take advantage of Electric Surge as it's not grounded, and Oblivious is pointless on a genderless Pokémon like Carbink. --- src/data/pokemon-species.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 4d19242b7f0..f1fc1827dbb 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -3336,7 +3336,7 @@ export const starterPassiveAbilities = { [Species.FINNEON]: Abilities.DRIZZLE, [Species.MANTYKE]: Abilities.STORM_DRAIN, [Species.SNOVER]: Abilities.SNOW_CLOAK, - [Species.ROTOM]: Abilities.ELECTRIC_SURGE, + [Species.ROTOM]: Abilities.MOTOR_DRIVE, [Species.UXIE]: Abilities.ILLUSION, [Species.MESPRIT]: Abilities.MOODY, [Species.AZELF]: Abilities.NEUROFORCE, @@ -3457,7 +3457,7 @@ export const starterPassiveAbilities = { [Species.AMAURA]: Abilities.SERENE_GRACE, [Species.HAWLUCHA]: Abilities.RECKLESS, [Species.DEDENNE]: Abilities.SIMPLE, - [Species.CARBINK]: Abilities.OBLIVIOUS, + [Species.CARBINK]: Abilities.SOLID_ROCK, [Species.GOOMY]: Abilities.POISON_HEAL, [Species.KLEFKI]: Abilities.TRIAGE, [Species.PHANTUMP]: Abilities.UNNERVE, From 796f69daeb1876da10be164e8d10e85a9a2b2ddf Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sat, 13 Apr 2024 20:41:51 -0400 Subject: [PATCH 17/51] Lower candy costs for lower-cost starters --- src/ui/starter-select-ui-handler.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index dbe22516e70..479ae21e566 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -42,15 +42,15 @@ export interface Starter { function getPassiveCandyCount(baseValue: integer): integer { switch (baseValue) { case 1: - return 100; - case 2: - return 75; - case 3: return 50; + case 2: + return 45; + case 3: + return 40; case 4: - return 40 - case 5: return 30; + case 5: + return 25; case 6: return 20; case 7: @@ -63,15 +63,15 @@ function getPassiveCandyCount(baseValue: integer): integer { function getValueReductionCandyCounts(baseValue: integer): [integer, integer] { switch (baseValue) { case 1: - return [ 50, 150 ]; + return [ 30, 75]; case 2: - return [ 30, 100 ]; + return [ 25, 60 ]; case 3: - return [ 25, 75 ]; + return [ 20, 50 ]; case 4: - return [ 20, 60 ]; + return [ 15, 40 ]; case 5: - return [ 15, 50 ]; + return [ 12, 35 ]; case 6: return [ 10, 30 ]; case 7: From cdab38abda838f03c7fa39eecf86c477473f597f Mon Sep 17 00:00:00 2001 From: neverblde Date: Sat, 13 Apr 2024 19:59:12 -0700 Subject: [PATCH 18/51] Don't escape interpolation values --- src/plugins/i18n.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index 95ba54007e1..a2a1283eafe 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -20,6 +20,9 @@ i18next.init({ lng: DEFAULT_LANGUAGE_OVERRIDE ? DEFAULT_LANGUAGE_OVERRIDE : 'en', fallbackLng: 'en', debug: true, + interpolation: { + escapeValue: false, + }, resources: { en: { menu: enMenu, From f068b53d44b540aa9be630a7db51fec05cce5769 Mon Sep 17 00:00:00 2001 From: shayebeadling Date: Sat, 13 Apr 2024 23:20:54 -0400 Subject: [PATCH 19/51] Show icon in dex for HA mons, like shiny (#117) * Show icon in dex for HA mons, like shiny * Swaps the star with the HA capsule * Removes the unwanted icon --- public/images/ui/ha_capsule.png | Bin 0 -> 297 bytes src/loading-scene.ts | 1 + src/ui/starter-select-ui-handler.ts | 13 +++++++++++++ 3 files changed, 14 insertions(+) create mode 100644 public/images/ui/ha_capsule.png diff --git a/public/images/ui/ha_capsule.png b/public/images/ui/ha_capsule.png new file mode 100644 index 0000000000000000000000000000000000000000..cccc9cf51d3054351193c5fbd53fa8a8e2be5fff GIT binary patch literal 297 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)H!3HEvS)PI@&H|6fVg?3oVGw3ym^DWND0tA* z#W93qW^#f9^P4p}U3wQER_;+!dZcqg=;q7Hzk28FzP(SW;GJ(*^G8-M@!f;_HLq^n z|3BS-?~nQ)2ma=N`gE%C3=as%%vtq!x&HcjMZcsEf85+IG3DQ%guNwCznHNZO8S1c zyqmsXe$LnT_0>-g|Myn^^MAd@db9S%#>TlPuh|&gns>Xy`Om@dHXBFFHfi?fHT(a6 z2;*XAKJ3pHmS^|1r^8eI|Jncl>%G6e{SUNilY|{d(fQX8L`3*q#8?=X8@g{xJvR9b i&@+6g5uRzjz6@GGHU|)cz@^~HAj;F#&t;ucLK6V`mUt8Z literal 0 HcmV?d00001 diff --git a/src/loading-scene.ts b/src/loading-scene.ts index 5c8f879ebce..9a3b7fdd5bd 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -58,6 +58,7 @@ export class LoadingScene extends SceneBase { this.loadImage('achv_bar_3', 'ui'); this.loadImage('achv_bar_4', 'ui'); this.loadImage('shiny_star', 'ui', 'shiny.png'); + this.loadImage('ha_capsule', 'ui', 'ha_capsule.png'); this.loadImage('icon_spliced', 'ui'); this.loadImage('icon_tera', 'ui'); this.loadImage('type_tera', 'ui'); diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 479ae21e566..e8791482168 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -163,6 +163,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private startCursorObj: Phaser.GameObjects.NineSlice; private starterValueLabels: Phaser.GameObjects.Text[]; private shinyIcons: Phaser.GameObjects.Image[]; + private hiddenAbilityIcons: Phaser.GameObjects.Image[]; private iconAnimHandler: PokemonIconAnimHandler; @@ -382,6 +383,17 @@ export default class StarterSelectUiHandler extends MessageUiHandler { return ret; }); + this.hiddenAbilityIcons = new Array(81).fill(null).map((_, i) => { + const x = (i % 9) * 18; + const y = Math.floor(i / 9) * 18; + const ret = this.scene.add.image(x + 163, y + 16, 'ha_capsule'); + ret.setOrigin(0, 0); + ret.setScale(0.5); + ret.setVisible(false); + this.starterSelectContainer.add(ret); + return ret; + }); + this.pokemonSprite = this.scene.add.sprite(53, 63, `pkmn__sub`); this.starterSelectContainer.add(this.pokemonSprite); @@ -1106,6 +1118,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.updateStarterValueLabel(s); this.starterValueLabels[s].setVisible(slotVisible); this.shinyIcons[s].setVisible(slotVisible && !!this.scene.gameData.dexData[speciesId].caughtAttr && !!(this.scene.gameData.dexData[speciesId].caughtAttr & DexAttr.SHINY)); + this.hiddenAbilityIcons[s].setVisible(slotVisible && !!this.scene.gameData.dexData[speciesId].caughtAttr && !!(this.scene.gameData.dexData[speciesId].caughtAttr & DexAttr.ABILITY_HIDDEN)); } } else { changed = super.setCursor(cursor); From 4078518c5f36e96116c1b7f68b5c4c689ecca640 Mon Sep 17 00:00:00 2001 From: Xavion3 Date: Sun, 14 Apr 2024 13:21:34 +1000 Subject: [PATCH 20/51] Add support for ability changing effects (#113) * Add support for ability changing effects * Fix doodle with a fainted ally in doubles --- src/data/ability.ts | 107 +++++++++++++++++++++++++++++++++++++++----- src/data/move.ts | 105 ++++++++++++++++++++++++++++++++++++++++--- src/phases.ts | 2 +- 3 files changed, 196 insertions(+), 18 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 072a6aa155d..eb83d53e237 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -572,6 +572,47 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { } } +export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { + constructor() { + super(); + } + + applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean { + if (move.getMove().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; + return true; + } + + return false; + } + + getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + return getPokemonMessage(pokemon, ` swapped\nabilities with its target!`); + } +} + +export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { + constructor() { + super(); + } + + applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean { + if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr)) { + attacker.summonData.ability = pokemon.getAbility().id; + + return true; + } + + return false; + } + + getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + return getPokemonMessage(pokemon, ` gave its target\n${abilityName}!`); + } +} + export class PreAttackAbAttr extends AbAttr { applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean | Promise { return false; @@ -829,7 +870,7 @@ class PostVictoryStatChangeAbAttr extends PostVictoryAbAttr { } export class PostKnockOutAbAttr extends AbAttr { - applyPostKnockOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { return false; } } @@ -845,7 +886,7 @@ export class PostKnockOutStatChangeAbAttr extends PostKnockOutAbAttr { this.levels = levels; } - applyPostKnockOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { const stat = typeof this.stat === 'function' ? this.stat(pokemon) : this.stat; @@ -855,6 +896,22 @@ export class PostKnockOutStatChangeAbAttr extends PostKnockOutAbAttr { } } +export class CopyFaintedAllyAbilityAbAttr extends PostKnockOutAbAttr { + constructor() { + super(); + } + + applyPostKnockOut(pokemon: Pokemon, passive: 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(getPokemonMessage(knockedOut, `'s ${allAbilities[knockedOut.getAbility().id].name} was taken over!`)); + return true; + } + + return false; + } +} + export class IgnoreOpponentStatChangesAbAttr extends AbAttr { constructor() { super(false); @@ -1018,6 +1075,27 @@ export class PostSummonFormChangeAbAttr extends PostSummonAbAttr { } } +export class TraceAbAttr extends PostSummonAbAttr { + applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + const targets = pokemon.getOpponents(); + let target: Pokemon; + if (targets.length > 1) + pokemon.scene.executeWithSeedOffset(() => target = Utils.randSeedItem(targets), pokemon.scene.currentBattle.waveIndex); + else + target = targets[0]; + + // Wonder Guard is normally uncopiable so has the attribute, but trace specifically can copy it + if (target.getAbility().hasAttr(UncopiableAbilityAbAttr) && target.getAbility().id !== Abilities.WONDER_GUARD) + return false; + + pokemon.summonData.ability = target.getAbility().id; + + pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` traced ${target.name}'s\n${allAbilities[target.getAbility().id].name}!`)); + + return true; + } +} + export class PostSummonTransformAbAttr extends PostSummonAbAttr { constructor() { super(true); @@ -1900,8 +1978,8 @@ export function applyPostAttackAbAttrs(attrType: { new(...args: any[]): PostAtta } export function applyPostKnockOutAbAttrs(attrType: { new(...args: any[]): PostKnockOutAbAttr }, - pokemon: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostKnockOut(pokemon, passive, args), args); + pokemon: Pokemon, knockedOut: Pokemon, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostKnockOut(pokemon, passive, knockedOut, args), args); } export function applyPostVictoryAbAttrs(attrType: { new(...args: any[]): PostVictoryAbAttr }, @@ -2085,7 +2163,8 @@ export function initAbilities() { .attr(ProtectStatAbAttr, BattleStat.ACC) .attr(DoubleBattleChanceAbAttr) .ignorable(), - new Ability(Abilities.TRACE, "Trace (N)", "When it enters a battle, the Pokémon copies an opposing Pokémon's Ability.", 3) + new Ability(Abilities.TRACE, "Trace", "When it enters a battle, the Pokémon copies an opposing Pokémon's Ability.", 3) + .attr(TraceAbAttr) .attr(UncopiableAbilityAbAttr), new Ability(Abilities.HUGE_POWER, "Huge Power", "Doubles the Pokémon's Attack stat.", 3) .attr(BattleStatMultiplierAbAttr, BattleStat.ATK, 2), @@ -2356,7 +2435,9 @@ export function initAbilities() { .attr(PostSummonTransformAbAttr) .attr(UncopiableAbilityAbAttr), new Ability(Abilities.INFILTRATOR, "Infiltrator (N)", "Passes through the opposing Pokémon's barrier, substitute, and the like and strikes.", 5), - new Ability(Abilities.MUMMY, "Mummy (N)", "Contact with the Pokémon changes the attacker's Ability to Mummy.", 5), + new Ability(Abilities.MUMMY, "Mummy", "Contact with the Pokémon changes the attacker's Ability to Mummy.", 5) + .attr(PostDefendAbilityGiveAbAttr) + .bypassFaint(), new Ability(Abilities.MOXIE, "Moxie", "The Pokémon shows moxie, and that boosts the Attack stat after knocking out any Pokémon.", 5) .attr(PostVictoryStatChangeAbAttr, BattleStat.ATK, 1), new Ability(Abilities.JUSTIFIED, "Justified", "Being hit by a Dark-type move boosts the Attack stat of the Pokémon, for justice.", 5) @@ -2524,9 +2605,11 @@ export function initAbilities() { .attr(PostKnockOutStatChangeAbAttr, BattleStat.SPATK, 1), new Ability(Abilities.TANGLING_HAIR, "Tangling Hair", "Contact with the Pokémon lowers the attacker's Speed stat.", 7) .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), BattleStat.SPD, -1, false), - new Ability(Abilities.RECEIVER, "Receiver (N)", "The Pokémon copies the Ability of a defeated ally.", 7) + new Ability(Abilities.RECEIVER, "Receiver", "The Pokémon copies the Ability of a defeated ally.", 7) + .attr(CopyFaintedAllyAbilityAbAttr) .attr(UncopiableAbilityAbAttr), - new Ability(Abilities.POWER_OF_ALCHEMY, "Power of Alchemy (N)", "The Pokémon copies the Ability of a defeated ally.", 7) + new Ability(Abilities.POWER_OF_ALCHEMY, "Power of Alchemy", "The Pokémon copies the Ability of a defeated ally.", 7) + .attr(CopyFaintedAllyAbilityAbAttr) .attr(UncopiableAbilityAbAttr), new Ability(Abilities.BEAST_BOOST, "Beast Boost", "The Pokémon boosts its most proficient stat each time it knocks out a Pokémon.", 7) .attr(PostVictoryStatChangeAbAttr, p => { @@ -2604,7 +2687,9 @@ export function initAbilities() { new Ability(Abilities.SCREEN_CLEANER, "Screen Cleaner (N)", "When the Pokémon enters a battle, the effects of Light Screen, Reflect, and Aurora Veil are nullified for both opposing and ally Pokémon.", 8), new Ability(Abilities.STEELY_SPIRIT, "Steely Spirit (N)", "Powers up ally Pokémon's Steel-type moves.", 8), new Ability(Abilities.PERISH_BODY, "Perish Body (N)", "When hit by a move that makes direct contact, the Pokémon and the attacker will faint after three turns unless they switch out of battle.", 8), - new Ability(Abilities.WANDERING_SPIRIT, "Wandering Spirit (N)", "The Pokémon exchanges Abilities with a Pokémon that hits it with a move that makes direct contact.", 8), + new Ability(Abilities.WANDERING_SPIRIT, "Wandering Spirit (P)", "The Pokémon exchanges Abilities with a Pokémon that hits it with a move that makes direct contact.", 8) + .attr(PostDefendAbilitySwapAbAttr) + .bypassFaint(), new Ability(Abilities.GORILLA_TACTICS, "Gorilla Tactics (N)", "Boosts the Pokémon's Attack stat but only allows the use of the first selected move.", 8), new Ability(Abilities.NEUTRALIZING_GAS, "Neutralizing Gas (N)", "If the Pokémon with Neutralizing Gas is in the battle, the effects of all Pokémon's Abilities will be nullified or will not be triggered.", 8) .attr(UncopiableAbilityAbAttr) @@ -2642,7 +2727,9 @@ export function initAbilities() { .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr), - new Ability(Abilities.LINGERING_AROMA, "Lingering Aroma (N)", "Contact with the Pokémon changes the attacker's Ability to Lingering Aroma.", 9), + new Ability(Abilities.LINGERING_AROMA, "Lingering Aroma", "Contact with the Pokémon changes the attacker's Ability to Lingering Aroma.", 9) + .attr(PostDefendAbilityGiveAbAttr) + .bypassFaint(), new Ability(Abilities.SEED_SOWER, "Seed Sower", "Turns the ground into Grassy Terrain when the Pokémon is hit by an attack.", 9) .attr(PostDefendTerrainChangeAbAttr, TerrainType.GRASSY), new Ability(Abilities.THERMAL_EXCHANGE, "Thermal Exchange (P)", "Boosts the Attack stat when the Pokémon is hit by a Fire-type move. The Pokémon also cannot be burned.", 9) diff --git a/src/data/move.ts b/src/data/move.ts index 35687050b08..8ea1df3cfd7 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -12,8 +12,9 @@ import * as Utils from "../utils"; import { WeatherType } from "./weather"; import { ArenaTagSide, ArenaTrapTag } from "./arena-tag"; import { ArenaTagType } from "./enums/arena-tag-type"; -import { UnswappableAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr } from "./ability"; +import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, NoTransformAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr } from "./ability"; import { Abilities } from "./enums/abilities"; +import { allAbilities } from './ability'; import { PokemonHeldItemModifier } from "../modifier/modifier"; import { BattlerIndex } from "../battle"; import { Stat } from "./pokemon-stat"; @@ -2914,6 +2915,91 @@ export class SketchAttr extends MoveEffectAttr { } } +export class AbilityChangeAttr extends MoveEffectAttr { + public ability: Abilities; + + constructor(ability: Abilities, selfTarget?: boolean) { + super(selfTarget, MoveEffectTrigger.HIT); + + this.ability = ability; + } + + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!super.apply(user, target, move, args)) + return false; + + (this.selfTarget ? user : target).summonData.ability = this.ability; + + user.scene.queueMessage('The ' + getPokemonMessage((this.selfTarget ? user : target), ` acquired\n${allAbilities[this.ability].name}!`)); + + return true; + } + + getCondition(): MoveConditionFunc { + return (user, target, move) => !(this.selfTarget ? user : target).getAbility().hasAttr(UnsuppressableAbilityAbAttr) && (this.selfTarget ? user : target).getAbility().id !== this.ability; + } +} + +export class AbilityCopyAttr extends MoveEffectAttr { + public copyToPartner: boolean; + + constructor(copyToPartner: boolean = false) { + super(false, MoveEffectTrigger.HIT); + + this.copyToPartner = copyToPartner; + } + + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!super.apply(user, target, move, args)) + return false; + + user.summonData.ability = target.getAbility().id; + + user.scene.queueMessage(getPokemonMessage(user, ` copied the `) + getPokemonMessage(target, `'s\n${allAbilities[target.getAbility().id].name}!`)); + + if (this.copyToPartner && user.scene.currentBattle?.double && user.getAlly().hp) { + user.getAlly().summonData.ability = target.getAbility().id; + user.getAlly().scene.queueMessage(getPokemonMessage(user.getAlly(), ` copied the `) + getPokemonMessage(target, `'s\n${allAbilities[target.getAbility().id].name}!`)); + } + + return true; + } + + getCondition(): MoveConditionFunc { + return (user, target, move) => { + let ret = !target.getAbility().hasAttr(UncopiableAbilityAbAttr) && !user.getAbility().hasAttr(UnsuppressableAbilityAbAttr); + if (this.copyToPartner && user.scene.currentBattle?.double) + ret = ret && (!user.getAlly().hp || !user.getAlly().getAbility().hasAttr(UnsuppressableAbilityAbAttr)); + else + ret = ret && user.getAbility().id !== target.getAbility().id; + return ret; + }; + } +} + +export class AbilityGiveAttr extends MoveEffectAttr { + public copyToPartner: boolean; + + constructor() { + super(false, MoveEffectTrigger.HIT); + } + + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!super.apply(user, target, move, args)) + return false; + + target.summonData.ability = user.getAbility().id; + + user.scene.queueMessage('The' + getPokemonMessage(target, `\nacquired ${allAbilities[user.getAbility().id].name}!`)); + + return true; + } + + getCondition(): MoveConditionFunc { + return (user, target, move) => !user.getAbility().hasAttr(UncopiableAbilityAbAttr) && !target.getAbility().hasAttr(UnsuppressableAbilityAbAttr) && user.getAbility().id !== target.getAbility().id; + } +} + export class SwitchAbilitiesAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!super.apply(user, target, move, args)) @@ -2923,7 +3009,7 @@ export class SwitchAbilitiesAttr extends MoveEffectAttr { user.summonData.ability = target.getAbility().id; target.summonData.ability = tempAbilityId; - user.scene.queueMessage(getPokemonMessage(user, ` swapped\nAbilities with its target!`)); + user.scene.queueMessage(getPokemonMessage(user, ` swapped\nabilities with its target!`)); return true; } @@ -3794,7 +3880,8 @@ export function initMoves() { .attr(AddBattlerTagAttr, BattlerTagType.HELPING_HAND) .target(MoveTarget.NEAR_ALLY), new StatusMove(Moves.TRICK, "Trick (N)", Type.PSYCHIC, 100, 10, "The user catches the target off guard and swaps its held item with its own.", -1, 0, 3), - new StatusMove(Moves.ROLE_PLAY, "Role Play (N)", Type.PSYCHIC, -1, 10, "The user mimics the target completely, copying the target's Ability.", -1, 0, 3), + new StatusMove(Moves.ROLE_PLAY, "Role Play", Type.PSYCHIC, -1, 10, "The user mimics the target completely, copying the target's Ability.", -1, 0, 3) + .attr(AbilityCopyAttr), new SelfStatusMove(Moves.WISH, "Wish (N)", Type.NORMAL, -1, 10, "One turn after this move is used, the user's or its replacement's HP is restored by half the user's max HP.", -1, 0, 3) .triageMove(), new SelfStatusMove(Moves.ASSIST, "Assist", Type.NORMAL, -1, 20, "The user hurriedly and randomly uses a move among those known by ally Pokémon.", -1, 0, 3) @@ -4076,7 +4163,8 @@ export function initMoves() { }); return uniqueUsedMoveIds.size >= movesetMoveIds.length - 1; }), - new StatusMove(Moves.WORRY_SEED, "Worry Seed (N)", Type.GRASS, 100, 10, "A seed that causes worry is planted on the target. It prevents sleep by making the target's Ability Insomnia.", -1, 0, 4), + new StatusMove(Moves.WORRY_SEED, "Worry Seed", Type.GRASS, 100, 10, "A seed that causes worry is planted on the target. It prevents sleep by making the target's Ability Insomnia.", -1, 0, 4) + .attr(AbilityChangeAttr, Abilities.INSOMNIA), new AttackMove(Moves.SUCKER_PUNCH, "Sucker Punch (P)", Type.DARK, MoveCategory.PHYSICAL, 70, 100, 5, "This move enables the user to attack first. This move fails if the target is not readying an attack.", -1, 1, 4), new StatusMove(Moves.TOXIC_SPIKES, "Toxic Spikes", Type.POISON, -1, 20, "The user lays a trap of poison spikes at the feet of the opposing team. The spikes will poison opposing Pokémon that switch into battle.", -1, 0, 4) .attr(AddArenaTrapTagAttr, ArenaTagType.TOXIC_SPIKES) @@ -4325,8 +4413,10 @@ export function initMoves() { .ballBombMove(), new AttackMove(Moves.FOUL_PLAY, "Foul Play", Type.DARK, MoveCategory.PHYSICAL, 95, 100, 15, "The user turns the target's power against it. The higher the target's Attack stat, the greater the damage it deals.", -1, 0, 5) .attr(TargetAtkUserAtkAttr), - new StatusMove(Moves.SIMPLE_BEAM, "Simple Beam (N)", Type.NORMAL, 100, 15, "The user's mysterious psychic wave changes the target's Ability to Simple.", -1, 0, 5), - new StatusMove(Moves.ENTRAINMENT, "Entrainment (N)", Type.NORMAL, 100, 15, "The user dances with an odd rhythm that compels the target to mimic it, making the target's Ability the same as the user's.", -1, 0, 5), + new StatusMove(Moves.SIMPLE_BEAM, "Simple Beam", Type.NORMAL, 100, 15, "The user's mysterious psychic wave changes the target's Ability to Simple.", -1, 0, 5) + .attr(AbilityChangeAttr, Abilities.SIMPLE), + new StatusMove(Moves.ENTRAINMENT, "Entrainment", Type.NORMAL, 100, 15, "The user dances with an odd rhythm that compels the target to mimic it, making the target's Ability the same as the user's.", -1, 0, 5) + .attr(AbilityGiveAttr), new StatusMove(Moves.AFTER_YOU, "After You (N)", Type.NORMAL, -1, 15, "The user helps the target and makes it use its move right after the user.", -1, 0, 5) .ignoresProtect(), new AttackMove(Moves.ROUND, "Round (P)", Type.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, "The user attacks the target with a song. Others can join in the Round to increase the power of the attack.", -1, 0, 5) @@ -5237,7 +5327,8 @@ export function initMoves() { .attr(LapseBattlerTagAttr, [ BattlerTagType.BIND, BattlerTagType.WRAP, BattlerTagType.FIRE_SPIN, BattlerTagType.WHIRLPOOL, BattlerTagType.CLAMP, BattlerTagType.SAND_TOMB, BattlerTagType.MAGMA_STORM, BattlerTagType.THUNDER_CAGE, BattlerTagType.SEEDED ], true) .attr(StatusEffectAttr, StatusEffect.POISON) .target(MoveTarget.ALL_NEAR_ENEMIES), - new StatusMove(Moves.DOODLE, "Doodle (N)", Type.NORMAL, 100, 10, "The user captures the very essence of the target in a sketch. This changes the Abilities of the user and its ally Pokémon to that of the target.", -1, 0, 9), + new StatusMove(Moves.DOODLE, "Doodle", Type.NORMAL, 100, 10, "The user captures the very essence of the target in a sketch. This changes the Abilities of the user and its ally Pokémon to that of the target.", -1, 0, 9) + .attr(AbilityCopyAttr, true), new SelfStatusMove(Moves.FILLET_AWAY, "Fillet Away", Type.NORMAL, -1, 10, "The user sharply boosts its Attack, Sp. Atk, and Speed stats by using its own HP.", -1, 0, 9) .attr(CutHpStatBoostAttr, [ BattleStat.ATK, BattleStat.SPATK, BattleStat.SPD ], 2, 2), new AttackMove(Moves.KOWTOW_CLEAVE, "Kowtow Cleave", Type.DARK, MoveCategory.PHYSICAL, 85, -1, 10, "The user slashes at the target after kowtowing to make the target let down its guard. This attack never misses.", -1, 0, 9) diff --git a/src/phases.ts b/src/phases.ts index 092fa7e5d47..4e065265c20 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2975,7 +2975,7 @@ export class FaintPhase extends PokemonPhase { } const alivePlayField = this.scene.getField(true); - alivePlayField.forEach(p => applyPostKnockOutAbAttrs(PostKnockOutAbAttr, p)); + 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()) From af571dbfcf079d111488bbcc6b09dc0042dfcf41 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sat, 13 Apr 2024 23:44:10 -0400 Subject: [PATCH 21/51] Copy hidden ability capsule to legacy UI --- public/images/ui/legacy/ha_capsule.png | Bin 0 -> 297 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/images/ui/legacy/ha_capsule.png diff --git a/public/images/ui/legacy/ha_capsule.png b/public/images/ui/legacy/ha_capsule.png new file mode 100644 index 0000000000000000000000000000000000000000..cccc9cf51d3054351193c5fbd53fa8a8e2be5fff GIT binary patch literal 297 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)H!3HEvS)PI@&H|6fVg?3oVGw3ym^DWND0tA* z#W93qW^#f9^P4p}U3wQER_;+!dZcqg=;q7Hzk28FzP(SW;GJ(*^G8-M@!f;_HLq^n z|3BS-?~nQ)2ma=N`gE%C3=as%%vtq!x&HcjMZcsEf85+IG3DQ%guNwCznHNZO8S1c zyqmsXe$LnT_0>-g|Myn^^MAd@db9S%#>TlPuh|&gns>Xy`Om@dHXBFFHfi?fHT(a6 z2;*XAKJ3pHmS^|1r^8eI|Jncl>%G6e{SUNilY|{d(fQX8L`3*q#8?=X8@g{xJvR9b i&@+6g5uRzjz6@GGHU|)cz@^~HAj;F#&t;ucLK6V`mUt8Z literal 0 HcmV?d00001 From 7e3ad88e4dca08ae5187fcf57bc9afda436dbb23 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 14 Apr 2024 00:14:23 -0400 Subject: [PATCH 22/51] Hide candies for evolved starters --- src/ui/starter-select-ui-handler.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index e8791482168..7521cbbc6bb 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -1212,12 +1212,18 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonCaughtCountText.setText(`${this.speciesStarterDexEntry.caughtCount}`); this.pokemonHatchedCountText.setText(`${this.speciesStarterDexEntry.hatchedCount}`); this.pokemonCaughtHatchedContainer.setVisible(true); - this.pokemonCandyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0]))); - this.pokemonCandyIcon.setVisible(true); - this.pokemonCandyOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1]))); - this.pokemonCandyOverlayIcon.setVisible(true); - this.pokemonCandyCountText.setText(`x${this.scene.gameData.starterData[species.speciesId].candyCount}`); - this.pokemonCandyCountText.setVisible(true); + if (pokemonPrevolutions.hasOwnProperty(species.speciesId)) { + this.pokemonCaughtHatchedContainer.setY(16); + [ this.pokemonCandyIcon, this.pokemonCandyOverlayIcon, this.pokemonCandyCountText ].map(c => c.setVisible(false)); + } else { + this.pokemonCaughtHatchedContainer.setY(25); + this.pokemonCandyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0]))); + this.pokemonCandyIcon.setVisible(true); + this.pokemonCandyOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1]))); + this.pokemonCandyOverlayIcon.setVisible(true); + this.pokemonCandyCountText.setText(`x${this.scene.gameData.starterData[species.speciesId].candyCount}`); + this.pokemonCandyCountText.setVisible(true); + } this.iconAnimHandler.addOrUpdate(this.starterSelectGenIconContainers[species.generation - 1].getAt(this.genSpecies[species.generation - 1].indexOf(species)) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.PASSIVE); let starterIndex = -1; From 65961d5cc7c50af0e0bcf600ce4ad49a5e70cf97 Mon Sep 17 00:00:00 2001 From: Temps Ray Date: Sun, 14 Apr 2024 00:51:47 -0400 Subject: [PATCH 23/51] Fix bug in legendary gacha that reduced epic egg rates --- src/ui/egg-gacha-ui-handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index e2ec457a091..f0e32dbc2e9 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -339,7 +339,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { const tierValueOffset = this.gachaCursor === GachaType.LEGENDARY ? 1 : 0; const tiers = new Array(pullCount).fill(null).map(() => { const tierValue = Utils.randInt(256); - return tierValue >= 52 + tierValueOffset ? EggTier.COMMON : tierValue + tierValueOffset >= 8 ? EggTier.GREAT : tierValue >= 1 + tierValueOffset ? EggTier.ULTRA : EggTier.MASTER; + return tierValue >= 52 + tierValueOffset ? EggTier.COMMON : tierValue >= 8 + tierValueOffset ? EggTier.GREAT : tierValue >= 1 + tierValueOffset ? EggTier.ULTRA : EggTier.MASTER; }); if (pullCount >= 25 && !tiers.filter(t => t >= EggTier.ULTRA).length) tiers[Utils.randInt(tiers.length)] = EggTier.ULTRA; From 1941939931cc8766329467c3ab807a478f1a4cca Mon Sep 17 00:00:00 2001 From: Madmadness65 Date: Sun, 14 Apr 2024 08:19:31 -0500 Subject: [PATCH 24/51] Fix Jungle Healing healing the user twice It still does not have the status condition healing bit yet. --- src/data/move.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/move.ts b/src/data/move.ts index 8ea1df3cfd7..a1e7a75f3c1 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -5117,7 +5117,7 @@ export function initMoves() { .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) .attr(StatusEffectAttr, StatusEffect.BURN), new StatusMove(Moves.JUNGLE_HEALING, "Jungle Healing (P)", Type.GRASS, -1, 10, "The user becomes one with the jungle, restoring HP and healing any status conditions of itself and its ally Pokémon in battle.", -1, 0, 8) - .attr(HealAttr, 0.25) + .attr(HealAttr, 0.25, true, false) .target(MoveTarget.USER_AND_ALLIES), new AttackMove(Moves.WICKED_BLOW, "Wicked Blow", Type.DARK, MoveCategory.PHYSICAL, 75, 100, 5, "The user, having mastered the Dark style, strikes the target with a fierce blow. This attack always results in a critical hit.", -1, 0, 8) .attr(CritOnlyAttr) From cc20d24afa262a1763028842b7e45ff07fbe6e04 Mon Sep 17 00:00:00 2001 From: Xavion3 Date: Mon, 15 Apr 2024 01:54:46 +1000 Subject: [PATCH 25/51] Implement sucker punch (#121) * Implement sucker punch * Fix variable name --- src/data/move.ts | 3 ++- src/field/pokemon.ts | 1 + src/phases.ts | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/data/move.ts b/src/data/move.ts index a1e7a75f3c1..bac19611fdf 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -4165,7 +4165,8 @@ export function initMoves() { }), new StatusMove(Moves.WORRY_SEED, "Worry Seed", Type.GRASS, 100, 10, "A seed that causes worry is planted on the target. It prevents sleep by making the target's Ability Insomnia.", -1, 0, 4) .attr(AbilityChangeAttr, Abilities.INSOMNIA), - new AttackMove(Moves.SUCKER_PUNCH, "Sucker Punch (P)", Type.DARK, MoveCategory.PHYSICAL, 70, 100, 5, "This move enables the user to attack first. This move fails if the target is not readying an attack.", -1, 1, 4), + new AttackMove(Moves.SUCKER_PUNCH, "Sucker Punch", Type.DARK, MoveCategory.PHYSICAL, 70, 100, 5, "This move enables the user to attack first. This move fails if the target is not readying an attack.", -1, 1, 4) + .condition((user, target, move) => !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS), new StatusMove(Moves.TOXIC_SPIKES, "Toxic Spikes", Type.POISON, -1, 20, "The user lays a trap of poison spikes at the feet of the opposing team. The spikes will poison opposing Pokémon that switch into battle.", -1, 0, 4) .attr(AddArenaTrapTagAttr, ArenaTagType.TOXIC_SPIKES) .target(MoveTarget.ENEMY_SIDE), diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index ac30befa003..03019054001 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2818,6 +2818,7 @@ export class PokemonBattleSummonData { export class PokemonTurnData { public flinched: boolean; + public acted: boolean; public hitCount: integer; public hitsLeft: integer; public damageDealt: integer = 0; diff --git a/src/phases.ts b/src/phases.ts index 4e065265c20..c29821daf46 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2155,6 +2155,8 @@ export class MovePhase extends BattlePhase { }); const doMove = () => { + this.pokemon.turnData.acted = true; // Record that the move was attempted, even if it fails + this.pokemon.lapseTags(BattlerTagLapseType.PRE_MOVE); if (!this.followUp && this.canMove() && !this.cancelled) { From 598e6953483e1e8d5eca287b05b7b3c387a7ccc7 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 14 Apr 2024 12:02:44 -0400 Subject: [PATCH 26/51] Fixed damage moves are unaffected by the multi lens --- src/phases.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/phases.ts b/src/phases.ts index c29821daf46..659e30df9a7 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2,7 +2,7 @@ import BattleScene, { STARTER_FORM_OVERRIDE, STARTER_SPECIES_OVERRIDE, bypassLog import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon"; import * as Utils from './utils'; import { Moves } from "./data/enums/moves"; -import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr, RechargeAttr, PreMoveMessageAttr, HealStatusEffectAttr, IgnoreOpponentStatChangesAttr, NoEffectAttr } from "./data/move"; +import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr, RechargeAttr, PreMoveMessageAttr, HealStatusEffectAttr, IgnoreOpponentStatChangesAttr, NoEffectAttr, FixedDamageAttr } from "./data/move"; import { Mode } from './ui/ui'; import { Command } from "./ui/command-ui-handler"; import { Stat } from "./data/pokemon-stat"; @@ -2321,7 +2321,7 @@ export class MoveEffectPhase extends PokemonPhase { const hitCount = new Utils.IntegerHolder(1); // Assume single target for multi hit applyMoveAttrs(MultiHitAttr, user, this.getTarget(), this.move.getMove(), hitCount); - if (this.move.getMove() instanceof AttackMove) + if (this.move.getMove() instanceof AttackMove && !this.move.getMove().getAttrs(FixedDamageAttr).length) this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0)); user.turnData.hitsLeft = user.turnData.hitCount = hitCount.value; } From 705ba0659ee6c58805acf2a1d05cc502375b6dda Mon Sep 17 00:00:00 2001 From: Xavion3 Date: Mon, 15 Apr 2024 02:05:32 +1000 Subject: [PATCH 27/51] Fix crash bug with sucker punch when not using a move --- src/data/move.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/data/move.ts b/src/data/move.ts index bac19611fdf..0fe4a87868f 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -22,6 +22,7 @@ import { TerrainType } from "./terrain"; import { SpeciesFormChangeActiveTrigger } from "./pokemon-forms"; import { Species } from "./enums/species"; import { ModifierPoolType } from "#app/modifier/modifier-type"; +import { Command } from "../ui/command-ui-handler"; export enum MoveCategory { PHYSICAL, @@ -4166,7 +4167,7 @@ export function initMoves() { new StatusMove(Moves.WORRY_SEED, "Worry Seed", Type.GRASS, 100, 10, "A seed that causes worry is planted on the target. It prevents sleep by making the target's Ability Insomnia.", -1, 0, 4) .attr(AbilityChangeAttr, Abilities.INSOMNIA), new AttackMove(Moves.SUCKER_PUNCH, "Sucker Punch", Type.DARK, MoveCategory.PHYSICAL, 70, 100, 5, "This move enables the user to attack first. This move fails if the target is not readying an attack.", -1, 1, 4) - .condition((user, target, move) => !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS), + .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS), new StatusMove(Moves.TOXIC_SPIKES, "Toxic Spikes", Type.POISON, -1, 20, "The user lays a trap of poison spikes at the feet of the opposing team. The spikes will poison opposing Pokémon that switch into battle.", -1, 0, 4) .attr(AddArenaTrapTagAttr, ArenaTagType.TOXIC_SPIKES) .target(MoveTarget.ENEMY_SIDE), From 75ce0e8f73988c40ae4cf3162fdfece8fad807a2 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 14 Apr 2024 12:23:49 -0400 Subject: [PATCH 28/51] Fix transform and imposter not copying ability --- src/data/ability.ts | 1 + src/data/move.ts | 1 + src/field/pokemon.ts | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index eb83d53e237..e122a4bd76b 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1111,6 +1111,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { pokemon.summonData.speciesForm = target.getSpeciesForm(); pokemon.summonData.fusionSpeciesForm = target.getFusionSpeciesForm(); + pokemon.summonData.ability = target.getAbility().id; pokemon.summonData.gender = target.getGender(); pokemon.summonData.fusionGender = target.getFusionGender(); pokemon.summonData.stats = [ pokemon.stats[Stat.HP] ].concat(target.stats.slice(1)); diff --git a/src/data/move.ts b/src/data/move.ts index 0fe4a87868f..e2a7e3cd39c 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -3028,6 +3028,7 @@ export class TransformAttr extends MoveEffectAttr { user.summonData.speciesForm = target.getSpeciesForm(); user.summonData.fusionSpeciesForm = target.getFusionSpeciesForm(); + user.summonData.ability = target.getAbility().id; user.summonData.gender = target.getGender(); user.summonData.fusionGender = target.getFusionGender(); user.summonData.stats = [ user.stats[Stat.HP] ].concat(target.stats.slice(1)); diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 03019054001..86355b17a9e 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -716,8 +716,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (OPP_ABILITY_OVERRIDE && !this.isPlayer()) return allAbilities[OPP_ABILITY_OVERRIDE]; if (this.isFusion()) - return allAbilities[this.getFusionSpeciesForm().getAbility(this.fusionAbilityIndex)]; - let abilityId = this.getSpeciesForm().getAbility(this.abilityIndex); + return allAbilities[this.getFusionSpeciesForm(ignoreOverride).getAbility(this.fusionAbilityIndex)]; + let abilityId = this.getSpeciesForm(ignoreOverride).getAbility(this.abilityIndex); if (abilityId === Abilities.NONE) abilityId = this.species.ability1; return allAbilities[abilityId]; From c25cb50b31ab15df0292a82ceb709754734be510 Mon Sep 17 00:00:00 2001 From: cornfish <166959318+poppedcornfish@users.noreply.github.com> Date: Sun, 14 Apr 2024 11:13:17 -0600 Subject: [PATCH 29/51] Implement toxic chain (#126) * implement toxic chain * fix for self target like roost --- src/data/ability.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index e122a4bd76b..396f703b472 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -788,19 +788,21 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { } } -export class PostAttackContactApplyStatusEffectAbAttr extends PostAttackAbAttr { +export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr { + private contactRequired: boolean; private chance: integer; private effects: StatusEffect[]; - constructor(chance: integer, ...effects: StatusEffect[]) { + constructor(contactRequired: boolean, chance: integer, ...effects: StatusEffect[]) { super(); + this.contactRequired = contactRequired; this.chance = chance; this.effects = effects; } applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean { - if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) { + if (pokemon != attacker && (!this.contactRequired || move.getMove().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); } @@ -809,6 +811,12 @@ export class PostAttackContactApplyStatusEffectAbAttr extends PostAttackAbAttr { } } +export class PostAttackContactApplyStatusEffectAbAttr extends PostAttackApplyStatusEffectAbAttr { + constructor(chance: integer, ...effects: StatusEffect[]) { + super(true, chance, ...effects); + } +} + export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { private condition: PokemonDefendCondition; @@ -2808,7 +2816,8 @@ export function initAbilities() { .ignorable(), new Ability(Abilities.SUPERSWEET_SYRUP, "Supersweet Syrup (N)", "A sickly sweet scent spreads across the field the first time the Pokémon enters a battle, lowering the evasiveness of opposing Pokémon.", 9), new Ability(Abilities.HOSPITALITY, "Hospitality (N)", "When the Pokémon enters a battle, it showers its ally with hospitality, restoring a small amount of the ally's HP.", 9), - new Ability(Abilities.TOXIC_CHAIN, "Toxic Chain (N)", "The power of the Pokémon's toxic chain may badly poison any target the Pokémon hits with a move.", 9), + new Ability(Abilities.TOXIC_CHAIN, "Toxic Chain", "The power of the Pokémon's toxic chain may badly poison any target the Pokémon hits with a move.", 9) + .attr(PostAttackApplyStatusEffectAbAttr, false, 30, StatusEffect.TOXIC), new Ability(Abilities.EMBODY_ASPECT_TEAL, "Embody Aspect", "The Pokémon's heart fills with memories, causing the Teal Mask to shine and the Pokémon's Speed stat to be boosted.", 9) .attr(PostBattleInitStatChangeAbAttr, BattleStat.SPD, 1, true) .attr(UncopiableAbilityAbAttr) From 946e6d8c5c41f6e78ae9caaf696884f23d35500f Mon Sep 17 00:00:00 2001 From: Tempoanon <163687446+TempsRay@users.noreply.github.com> Date: Sun, 14 Apr 2024 13:15:01 -0400 Subject: [PATCH 30/51] Implement Rivalry, Quick Feet, Liquid Voice, and Normalize (#108) * Implement Rivalry * Implement Quick Feet, Liquid Voice, and Normalize * Forgot paralysis is half speed instead of a quarter * Remove log statements * Fix minor edge case in rivalry for gendered vs genderless --- src/data/ability.ts | 40 ++++++++++++++++++++++++++++++++++++---- src/field/pokemon.ts | 3 ++- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 396f703b472..ded9d0a438e 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -8,6 +8,7 @@ import { Weather, WeatherType } from "./weather"; import { BattlerTag } from "./battler-tags"; import { BattlerTagType } from "./enums/battler-tag-type"; import { StatusEffect, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect"; +import { Gender } from "./gender"; import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, RecoilAttr, StatusMoveTypeImmunityAttr, allMoves } from "./move"; import { ArenaTagType } from "./enums/arena-tag-type"; import { Stat } from "./pokemon-stat"; @@ -657,6 +658,30 @@ export class MoveTypeChangePowerMultiplierAbAttr extends VariableMoveTypeAbAttr } } +export class MoveTypeChangeAttr extends PreAttackAbAttr { + private newType: Type; + private powerMultiplier: number; + private condition: PokemonAttackCondition; + + constructor(newType: Type, powerMultiplier: number, condition: PokemonAttackCondition){ + super(true); + this.newType = newType; + this.powerMultiplier = powerMultiplier; + this.condition = condition; + } + + applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean { + if (this.condition(pokemon, defender, move.getMove())) { + const type = (args[0] as Utils.IntegerHolder); + type.value = this.newType; + (args[1] as Utils.NumberHolder).value *= this.powerMultiplier; + return true; + } + + return false; + } +} + export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr { private condition: PokemonAttackCondition; private powerMultiplier: number; @@ -2278,7 +2303,9 @@ export function initAbilities() { new Ability(Abilities.MOTOR_DRIVE, "Motor Drive", "Boosts its Speed stat if hit by an Electric-type move instead of taking damage.", 4) .attr(TypeImmunityStatChangeAbAttr, Type.ELECTRIC, BattleStat.SPD, 1) .ignorable(), - new Ability(Abilities.RIVALRY, "Rivalry (N)", "Becomes competitive and deals more damage to Pokémon of the same gender, but deals less to Pokémon of the opposite gender.", 4), + new Ability(Abilities.RIVALRY, "Rivalry", "Becomes competitive and deals more damage to Pokémon of the same gender, but deals less to Pokémon of the opposite gender.", 4) + .attr(MovePowerBoostAbAttr, (user, target, move) => user.gender !== Gender.GENDERLESS && target.gender !== Gender.GENDERLESS && user.gender === target.gender, 1.25) + .attr(MovePowerBoostAbAttr, (user, target, move) => user.gender !== Gender.GENDERLESS && target.gender !== Gender.GENDERLESS && user.gender !== target.gender, 0.75), new Ability(Abilities.STEADFAST, "Steadfast", "The Pokémon's determination boosts the Speed stat each time the Pokémon flinches.", 4) .attr(FlinchStatChangeAbAttr, BattleStat.SPD, 1), new Ability(Abilities.SNOW_CLOAK, "Snow Cloak", "Boosts evasiveness in a hailstorm.", 4) @@ -2319,8 +2346,12 @@ export function initAbilities() { .attr(PostWeatherLapseDamageAbAttr, 2, WeatherType.SUNNY, WeatherType.HARSH_SUN) .attr(BattleStatMultiplierAbAttr, BattleStat.SPATK, 1.5) .condition(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)), - new Ability(Abilities.QUICK_FEET, "Quick Feet (N)", "Boosts the Speed stat if the Pokémon has a status condition.", 4), - new Ability(Abilities.NORMALIZE, "Normalize (N)", "All the Pokémon's moves become Normal type. The power of those moves is boosted a little.", 4), + new Ability(Abilities.QUICK_FEET, "Quick Feet", "Boosts the Speed stat if the Pokémon has a status condition.", 4) + .conditionalAttr(pokemon => pokemon.status.effect === StatusEffect.PARALYSIS, BattleStatMultiplierAbAttr, BattleStat.SPD, 2) + .conditionalAttr(pokemon => !!pokemon.status, BattleStatMultiplierAbAttr, BattleStat.SPD, 1.5), + new Ability(Abilities.NORMALIZE, "Normalize", "All the Pokémon's moves become Normal type. The power of those moves is boosted a little.", 4) + .attr(MoveTypeChangeAttr, Type.NORMAL, 1.2, (user, target, move) => move.id !== Moves.HIDDEN_POWER && move.id !== Moves.WEATHER_BALL && + move.id !== Moves.NATURAL_GIFT && move.id !== Moves.JUDGMENT && move.id !== Moves.TECHNO_BLAST), new Ability(Abilities.SNIPER, "Sniper (N)", "Powers up moves if they become critical hits when attacking.", 4), new Ability(Abilities.MAGIC_GUARD, "Magic Guard", "The Pokémon only takes damage from attacks.", 4) .attr(BlockNonDirectDamageAbAttr), @@ -2566,7 +2597,8 @@ export function initAbilities() { .condition(getWeatherCondition(WeatherType.HAIL)), new Ability(Abilities.LONG_REACH, "Long Reach", "The Pokémon uses its moves without making contact with the target.", 7) .attr(IgnoreContactAbAttr), - new Ability(Abilities.LIQUID_VOICE, "Liquid Voice (N)", "All sound-based moves become Water-type moves.", 7), + new Ability(Abilities.LIQUID_VOICE, "Liquid Voice", "All sound-based moves become Water-type moves.", 7) + .attr(MoveTypeChangeAttr, Type.WATER, 1, (user, target, move) => move.hasFlag(MoveFlags.SOUND_BASED)), new Ability(Abilities.TRIAGE, "Triage", "Gives priority to a healing move.", 7) .attr(IncrementMovePriorityAbAttr, (pokemon, move) => move.hasFlag(MoveFlags.TRIAGE_MOVE), 3), new Ability(Abilities.GALVANIZE, "Galvanize", "Normal-type moves become Electric-type moves. The power of those moves is boosted a little.", 7) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 86355b17a9e..c396ca37bcc 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -25,7 +25,7 @@ import { TempBattleStat } from '../data/temp-battle-stat'; import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag'; import { ArenaTagType } from "../data/enums/arena-tag-type"; import { Biome } from "../data/enums/biome"; -import { Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from '../data/ability'; +import { Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from '../data/ability'; import { Abilities } from "#app/data/enums/abilities"; import PokemonData from '../system/pokemon-data'; import { BattlerIndex } from '../battle'; @@ -1124,6 +1124,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs(VariableMoveTypeAttr, source, this, move, variableType); // 2nd argument is for MoveTypeChangePowerMultiplierAbAttr applyAbAttrs(VariableMoveTypeAbAttr, source, null, variableType, typeChangeMovePowerMultiplier); + applyPreAttackAbAttrs(MoveTypeChangeAttr, source, this, battlerMove, variableType, typeChangeMovePowerMultiplier); const type = variableType.value as Type; const types = this.getTypes(true, true); From 605e16fe35fc279eca2bad51acf777a1f1e868e1 Mon Sep 17 00:00:00 2001 From: Xavion3 Date: Mon, 15 Apr 2024 03:11:22 +1000 Subject: [PATCH 31/51] Fix a bunch of moves --- src/data/move.ts | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index e2a7e3cd39c..50fcbaa3786 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -3867,9 +3867,10 @@ export function initMoves() { new AttackMove(Moves.FACADE, "Facade", Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, "This attack move doubles its power if the user is poisoned, burned, or paralyzed.", -1, 0, 3) .attr(MovePowerMultiplierAttr, (user, target, move) => user.status && (user.status.effect === StatusEffect.BURN || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.PARALYSIS) ? 2 : 1), - new AttackMove(Moves.FOCUS_PUNCH, "Focus Punch (P)", Type.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 20, "The user focuses its mind before launching a punch. This move fails if the user is hit before it is used.", -1, -3, 3) + new AttackMove(Moves.FOCUS_PUNCH, "Focus Punch", Type.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 20, "The user focuses its mind before launching a punch. This move fails if the user is hit before it is used.", -1, -3, 3) .punchingMove() - .ignoresVirtual(), + .ignoresVirtual() + .condition((user, target, move) => !user.turnData.attacksReceived.find(r => r.damage)), new AttackMove(Moves.SMELLING_SALTS, "Smelling Salts", Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 10, "This attack's power is doubled when used on a target with paralysis. This also cures the target's paralysis, however.", -1, 0, 3) .attr(MovePowerMultiplierAttr, (user, target, move) => target.status?.effect === StatusEffect.PARALYSIS ? 2 : 1) .attr(HealStatusEffectAttr, true, StatusEffect.PARALYSIS), @@ -3895,7 +3896,8 @@ export function initMoves() { .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], -1, true), new SelfStatusMove(Moves.MAGIC_COAT, "Magic Coat (N)", Type.PSYCHIC, -1, 15, "Moves like Leech Seed and moves that inflict status conditions are blocked by a barrier and reflected back to the user of those moves.", -1, 4, 3), new SelfStatusMove(Moves.RECYCLE, "Recycle (N)", Type.NORMAL, -1, 10, "The user recycles a held item that has been used in battle so it can be used again.", -1, 0, 3), - new AttackMove(Moves.REVENGE, "Revenge (P)", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, "This attack move's power is doubled if the user has been hurt by the opponent in the same turn.", -1, -4, 3), + new AttackMove(Moves.REVENGE, "Revenge", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, "This attack move's power is doubled if the user has been hurt by the opponent in the same turn.", -1, -4, 3) + .attr(TurnDamagedDoublePowerAttr), new AttackMove(Moves.BRICK_BREAK, "Brick Break (P)", Type.FIGHTING, MoveCategory.PHYSICAL, 75, 100, 15, "The user attacks with a swift chop. It can also break barriers, such as Light Screen and Reflect.", -1, 0, 3), new StatusMove(Moves.YAWN, "Yawn", Type.NORMAL, -1, 10, "The user lets loose a huge yawn that lulls the target into falling asleep on the next turn.", -1, 0, 3) .attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true) @@ -4863,7 +4865,8 @@ export function initMoves() { new AttackMove(Moves.PULVERIZING_PANCAKE, "Pulverizing Pancake (P)", Type.NORMAL, MoveCategory.PHYSICAL, 210, -1, 1, "Z-Power brings out the true capabilities of the user, Snorlax. The Pokémon moves its enormous body energetically and attacks the target with full force.", -1, 0, 7), new SelfStatusMove(Moves.EXTREME_EVOBOOST, "Extreme Evoboost", Type.NORMAL, -1, 1, "After obtaining Z-Power, the user, Eevee, gets energy from its evolved friends and boosts its stats sharply.", 100, 0, 7) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 2, true), - new AttackMove(Moves.GENESIS_SUPERNOVA, "Genesis Supernova (P)", Type.PSYCHIC, MoveCategory.SPECIAL, 185, -1, 1, "After obtaining Z-Power, the user, Mew, attacks the target with full force. The terrain will be charged with psychic energy.", -1, 0, 7), + new AttackMove(Moves.GENESIS_SUPERNOVA, "Genesis Supernova", Type.PSYCHIC, MoveCategory.SPECIAL, 185, -1, 1, "After obtaining Z-Power, the user, Mew, attacks the target with full force. The terrain will be charged with psychic energy.", -1, 0, 7) + .attr(TerrainChangeAttr, TerrainType.PSYCHIC), /* End Unused */ new AttackMove(Moves.SHELL_TRAP, "Shell Trap (P)", Type.FIRE, MoveCategory.SPECIAL, 150, 100, 5, "The user sets a shell trap. If the user is hit by a physical move, the trap will explode and inflict damage on opposing Pokémon.", -1, -3, 7) .target(MoveTarget.ALL_NEAR_ENEMIES), @@ -4911,6 +4914,7 @@ export function initMoves() { .attr(ClearTerrainAttr) .makesContact(false), new AttackMove(Moves.CLANGOROUS_SOULBLAZE, "Clangorous Soulblaze (P)", Type.DRAGON, MoveCategory.SPECIAL, 185, -1, 1, "After obtaining Z-Power, the user, Kommo-o, attacks the opposing Pokémon with full force. This move boosts the user's stats.", 100, 0, 7) + .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 1, true) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES), /* End Unused */ @@ -5051,8 +5055,9 @@ export function initMoves() { .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.APPLE_ACID, "Apple Acid", Type.GRASS, MoveCategory.SPECIAL, 80, 100, 10, "The user attacks the target with an acidic liquid created from tart apples. This also lowers the target's Sp. Def stat.", 100, 0, 8) .attr(StatChangeAttr, BattleStat.SPDEF, -1), - new AttackMove(Moves.GRAV_APPLE, "Grav Apple (P)", Type.GRASS, MoveCategory.PHYSICAL, 80, 100, 10, "The user inflicts damage by dropping an apple from high above. This also lowers the target's Defense stat.", 100, 0, 8) + new AttackMove(Moves.GRAV_APPLE, "Grav Apple", Type.GRASS, MoveCategory.PHYSICAL, 80, 100, 10, "The user inflicts damage by dropping an apple from high above. This also lowers the target's Defense stat.", 100, 0, 8) .attr(StatChangeAttr, BattleStat.DEF, -1) + .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTag(ArenaTagType.GRAVITY) ? 1.5 : 1) .makesContact(false), new AttackMove(Moves.SPIRIT_BREAK, "Spirit Break", Type.FAIRY, MoveCategory.PHYSICAL, 75, 100, 15, "The user attacks the target with so much force that it could break the target's spirit. This also lowers the target's Sp. Atk stat.", 100, 0, 8) .attr(StatChangeAttr, BattleStat.SPATK, -1), @@ -5357,8 +5362,10 @@ export function initMoves() { new AttackMove(Moves.HYDRO_STEAM, "Hydro Steam (P)", Type.WATER, MoveCategory.SPECIAL, 80, 100, 15, "The user blasts the target with boiling-hot water. This move's power is not lowered in harsh sunlight but rather boosted by 50 percent.", -1, 0, 9), new AttackMove(Moves.RUINATION, "Ruination", Type.DARK, MoveCategory.SPECIAL, 1, 90, 10, "The user summons a ruinous disaster. This cuts the target's HP in half.", -1, 0, 9) .attr(TargetHalfHpDamageAttr), - new AttackMove(Moves.COLLISION_COURSE, "Collision Course (P)", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 100, 5, "The user transforms and crashes to the ground, causing a massive prehistoric explosion. This move's power is boosted more than usual if it's a supereffective hit.", -1, 0, 9), - new AttackMove(Moves.ELECTRO_DRIFT, "Electro Drift (P)", Type.ELECTRIC, MoveCategory.SPECIAL, 100, 100, 5, "The user races forward at ultrafast speeds, piercing its target with futuristic electricity. This move's power is boosted more than usual if it's a supereffective hit.", -1, 0, 9) + new AttackMove(Moves.COLLISION_COURSE, "Collision Course", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 100, 5, "The user transforms and crashes to the ground, causing a massive prehistoric explosion. This move's power is boosted more than usual if it's a supereffective hit.", -1, 0, 9) + .attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type) >= 2 ? 5461/4096 : 1), + new AttackMove(Moves.ELECTRO_DRIFT, "Electro Drift", Type.ELECTRIC, MoveCategory.SPECIAL, 100, 100, 5, "The user races forward at ultrafast speeds, piercing its target with futuristic electricity. This move's power is boosted more than usual if it's a supereffective hit.", -1, 0, 9) + .attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type) >= 2 ? 5461/4096 : 1) .makesContact(), new SelfStatusMove(Moves.SHED_TAIL, "Shed Tail (N)", Type.NORMAL, -1, 10, "The user creates a substitute for itself using its own HP before switching places with a party Pokémon in waiting.", -1, 0, 9), new StatusMove(Moves.CHILLY_RECEPTION, "Chilly Reception", Type.ICE, -1, 10, "The user tells a chillingly bad joke before switching places with a party Pokémon in waiting. This summons a snowstorm lasting five turns.", -1, 0, 9) @@ -5391,7 +5398,11 @@ export function initMoves() { .triageMove(), new AttackMove(Moves.DOUBLE_SHOCK, "Double Shock (P)", Type.ELECTRIC, MoveCategory.PHYSICAL, 120, 100, 5, "The user discharges all the electricity from its body to perform a high-damage attack. After using this move, the user will no longer be Electric type.", -1, 0, 9), new AttackMove(Moves.GIGATON_HAMMER, "Gigaton Hammer (P)", Type.STEEL, MoveCategory.PHYSICAL, 160, 100, 5, "The user swings its whole body around to attack with its huge hammer. This move can't be used twice in a row.", -1, 0, 9) - .makesContact(false), + .makesContact(false) + .condition((user, target, move) => { + const turnMove = user.getLastXMoves(1); + return !turnMove.length || turnMove[0].move !== move.id || turnMove[0].result !== MoveResult.SUCCESS; + }), // TODO Add Instruct/Encore interaction new AttackMove(Moves.COMEUPPANCE, "Comeuppance", Type.DARK, MoveCategory.PHYSICAL, 1, 100, 10, "The user retaliates with much greater force against the opponent that last inflicted damage on it.", -1, 0, 9) .attr(CounterDamageAttr, (move: Move) => (move.category === MoveCategory.PHYSICAL || move.category === MoveCategory.SPECIAL), 1.5) .target(MoveTarget.ATTACKER), @@ -5414,7 +5425,11 @@ export function initMoves() { new AttackMove(Moves.MAGICAL_TORQUE, "Magical Torque", Type.FAIRY, MoveCategory.PHYSICAL, 100, 100, 10, "The user revs their fae-like engine into the target. This may also confuse the target.", 30, 0, 9) .attr(ConfuseAttr) .makesContact(false), - new AttackMove(Moves.BLOOD_MOON, "Blood Moon (P)", Type.NORMAL, MoveCategory.SPECIAL, 140, 100, 5, "The user unleashes the full brunt of its spirit from a full moon that shines as red as blood. This move can't be used twice in a row.", -1, 0, 9), + new AttackMove(Moves.BLOOD_MOON, "Blood Moon (P)", Type.NORMAL, MoveCategory.SPECIAL, 140, 100, 5, "The user unleashes the full brunt of its spirit from a full moon that shines as red as blood. This move can't be used twice in a row.", -1, 0, 9) + .condition((user, target, move) => { + const turnMove = user.getLastXMoves(1); + return !turnMove.length || turnMove[0].move !== move.id || turnMove[0].result !== MoveResult.SUCCESS; + }), // TODO Add Instruct/Encore interaction new AttackMove(Moves.MATCHA_GOTCHA, "Matcha Gotcha", Type.GRASS, MoveCategory.SPECIAL, 80, 90, 15, "The user fires a blast of tea that it mixed. The user's HP is restored by up to half the damage taken by the target. This may also leave the target with a burn.", 20, 0, 9) .attr(HitHealAttr) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) @@ -5442,7 +5457,8 @@ export function initMoves() { new AttackMove(Moves.MIGHTY_CLEAVE, "Mighty Cleave", Type.ROCK, MoveCategory.PHYSICAL, 95, 100, 5, "The user wields the light that has accumulated atop its head to cleave the target. This move hits even if the target protects itself.", -1, 0, 9) .ignoresProtect(), new AttackMove(Moves.TACHYON_CUTTER, "Tachyon Cutter", Type.STEEL, MoveCategory.SPECIAL, 50, -1, 10, "The user attacks by launching particle blades at the target twice in a row. This attack never misses.", -1, 0, 9) - .attr(MultiHitAttr, MultiHitType._2), + .attr(MultiHitAttr, MultiHitType._2) + .slicingMove(), new AttackMove(Moves.HARD_PRESS, "Hard Press", Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, "The target is crushed with an arm, a claw, or the like to inflict damage. The more HP the target has left, the greater the move's power.", -1, 0, 9) .attr(OpponentHighHpPowerAttr), new StatusMove(Moves.DRAGON_CHEER, "Dragon Cheer (P)", Type.DRAGON, -1, 15, "The user raises its allies' morale with a draconic cry so that their future attacks have a heightened chance of landing critical hits. This rouses Dragon types more.", 100, 0, 9) From 73cf4e9f5c4341bbc0b96bcf54f7501f1ccbee67 Mon Sep 17 00:00:00 2001 From: Madmadness65 Date: Sun, 14 Apr 2024 12:52:59 -0500 Subject: [PATCH 32/51] Implement Thunderclap It's just a copy of Sucker Punch, so no additional work needed. --- src/data/move.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/data/move.ts b/src/data/move.ts index 50fcbaa3786..e5ff01c638f 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -5453,7 +5453,8 @@ export function initMoves() { .attr(DoublePowerChanceAttr), new StatusMove(Moves.BURNING_BULWARK, "Burning Bulwark", Type.FIRE, -1, 10, "The user's intensely hot fur protects it from attacks and also burns any attacker that makes direct contact with it.", 100, 4, 9) .attr(ProtectAttr, BattlerTagType.BURNING_BULWARK), - new AttackMove(Moves.THUNDERCLAP, "Thunderclap (P)", Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 5, "This move enables the user to attack first with a jolt of electricity. This move fails if the target is not readying an attack.", -1, 1, 9), + new AttackMove(Moves.THUNDERCLAP, "Thunderclap", Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 5, "This move enables the user to attack first with a jolt of electricity. This move fails if the target is not readying an attack.", -1, 1, 9) + .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS), new AttackMove(Moves.MIGHTY_CLEAVE, "Mighty Cleave", Type.ROCK, MoveCategory.PHYSICAL, 95, 100, 5, "The user wields the light that has accumulated atop its head to cleave the target. This move hits even if the target protects itself.", -1, 0, 9) .ignoresProtect(), new AttackMove(Moves.TACHYON_CUTTER, "Tachyon Cutter", Type.STEEL, MoveCategory.SPECIAL, 50, -1, 10, "The user attacks by launching particle blades at the target twice in a row. This attack never misses.", -1, 0, 9) From 90ef58d7a4144fb50dab62716df82f77e015b969 Mon Sep 17 00:00:00 2001 From: shayebeadling Date: Sun, 14 Apr 2024 14:20:00 -0400 Subject: [PATCH 33/51] Defiant implementation (#128) * Defiant works, but self inflicted stat changes still proc the stat raise. * Prevents proc from self-targeted stat reductions, like superpower --- src/data/ability.ts | 38 +++++++++++++++++++++++++++++++++++++- src/phases.ts | 3 ++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index ded9d0a438e..2ad6959cbd7 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -82,6 +82,7 @@ type AbAttrCondition = (pokemon: Pokemon) => boolean; type PokemonAttackCondition = (user: Pokemon, target: Pokemon, move: Move) => boolean; type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean; +type PokemonStatChangeCondition = (target: Pokemon, statsChanged: BattleStat[], levels: integer) => boolean; export abstract class AbAttr { public showAbility: boolean; @@ -385,6 +386,12 @@ export class PostDefendAbAttr extends AbAttr { } } +export class PostStatChangeAbAttr extends AbAttr { + applyPostStatChange(pokemon: Pokemon, statsChanged: BattleStat[], levelChanged: integer, selfTarget: boolean, args: any[]): boolean | Promise { + return false; + } +} + export class MoveImmunityAbAttr extends PreDefendAbAttr { private immuneCondition: PreDefendAbAttrCondition; @@ -614,6 +621,29 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { } } +export class PostStatChangeStatChangeAbAttr extends PostStatChangeAbAttr { + private condition: PokemonStatChangeCondition; + private statsToChange: BattleStat[]; + private levels: integer; + + constructor(condition: PokemonStatChangeCondition, statsToChange: BattleStat[], levels: integer) { + super(true); + + this.condition = condition; + this.statsToChange = statsToChange; + this.levels = levels; + } + + applyPostStatChange(pokemon: Pokemon, 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)); + return true; + } + + return false; + } +} + export class PreAttackAbAttr extends AbAttr { applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean | Promise { return false; @@ -2036,6 +2066,11 @@ export function applyPreStatChangeAbAttrs(attrType: { new(...args: any[]): PreSt return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreStatChange(pokemon, passive, stat, cancelled, args), args); } +export function applyPostStatChangeAbAttrs(attrType: { new(...args: any[]): PostStatChangeAbAttr }, + 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); +} + export function applyPreSetStatusAbAttrs(attrType: { new(...args: any[]): PreSetStatusAbAttr }, pokemon: Pokemon, effect: StatusEffect, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { const simulated = args.length > 1 && args[1]; @@ -2419,7 +2454,8 @@ export function initAbilities() { .ignorable(), new Ability(Abilities.UNNERVE, "Unnerve", "Unnerves opposing Pokémon and makes them unable to eat Berries.", 5) .attr(PreventBerryUseAbAttr), - new Ability(Abilities.DEFIANT, "Defiant (N)", "Boosts the Pokémon's Attack stat sharply when its stats are lowered.", 5), + new Ability(Abilities.DEFIANT, "Defiant", "Boosts the Pokémon's Attack stat sharply when its stats are lowered.", 5) + .attr(PostStatChangeStatChangeAbAttr, (target, statsChanged, levels) => levels < 0, [BattleStat.ATK], 2), new Ability(Abilities.DEFEATIST, "Defeatist", "Halves the Pokémon's Attack and Sp. Atk stats when its HP becomes half or less.", 5) .attr(BattleStatMultiplierAbAttr, BattleStat.ATK, 0.5) .attr(BattleStatMultiplierAbAttr, BattleStat.SPATK, 0.5) diff --git a/src/phases.ts b/src/phases.ts index 659e30df9a7..7e5a3aab1d6 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -30,7 +30,7 @@ import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, get import { TempBattleStat } from "./data/temp-battle-stat"; import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag"; import { ArenaTagType } from "./data/enums/arena-tag-type"; -import { CheckTrappedAbAttr, MoveAbilityBypassAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr } from "./data/ability"; +import { CheckTrappedAbAttr, MoveAbilityBypassAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs } from "./data/ability"; import { Abilities } from "./data/enums/abilities"; import { Unlockables, getUnlockableName } from "./system/unlockables"; import { getBiomeKey } from "./field/arena"; @@ -2632,6 +2632,7 @@ export class StatChangePhase extends PokemonPhase { for (let stat of filteredStats) pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + levels.value, 6), -6); + applyPostStatChangeAbAttrs(PostStatChangeAbAttr, pokemon, filteredStats, this.levels, this.selfTarget) this.end(); }; From f358e302e9a9a2659615f100a71f05c05ff10d13 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 14 Apr 2024 14:36:44 -0400 Subject: [PATCH 34/51] Give Ferroseed duplicate ability as hidden --- src/data/pokemon-species.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index f1fc1827dbb..e582fdfce1d 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -1617,7 +1617,7 @@ export function initSpecies() { new PokemonSpecies(Species.ALOMOMOLA, "Alomomola", 5, false, false, false, "Caring Pokémon", Type.WATER, null, 1.2, 31.6, Abilities.HEALER, Abilities.HYDRATION, Abilities.REGENERATOR, 470, 165, 75, 80, 40, 45, 65, 75, 70, 165, GrowthRate.FAST, 50, false), new PokemonSpecies(Species.JOLTIK, "Joltik", 5, false, false, false, "Attaching Pokémon", Type.BUG, Type.ELECTRIC, 0.1, 0.6, Abilities.COMPOUND_EYES, Abilities.UNNERVE, Abilities.SWARM, 319, 50, 47, 50, 57, 50, 65, 190, 50, 64, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.GALVANTULA, "Galvantula", 5, false, false, false, "EleSpider Pokémon", Type.BUG, Type.ELECTRIC, 0.8, 14.3, Abilities.COMPOUND_EYES, Abilities.UNNERVE, Abilities.SWARM, 472, 70, 77, 60, 97, 60, 108, 75, 50, 165, GrowthRate.MEDIUM_FAST, 50, false), - new PokemonSpecies(Species.FERROSEED, "Ferroseed", 5, false, false, false, "Thorn Seed Pokémon", Type.GRASS, Type.STEEL, 0.6, 18.8, Abilities.IRON_BARBS, Abilities.NONE, Abilities.NONE, 305, 44, 50, 91, 24, 86, 10, 255, 50, 61, GrowthRate.MEDIUM_FAST, 50, false), + new PokemonSpecies(Species.FERROSEED, "Ferroseed", 5, false, false, false, "Thorn Seed Pokémon", Type.GRASS, Type.STEEL, 0.6, 18.8, Abilities.IRON_BARBS, Abilities.NONE, Abilities.IRON_BARBS, 305, 44, 50, 91, 24, 86, 10, 255, 50, 61, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.FERROTHORN, "Ferrothorn", 5, false, false, false, "Thorn Pod Pokémon", Type.GRASS, Type.STEEL, 1, 110, Abilities.IRON_BARBS, Abilities.NONE, Abilities.ANTICIPATION, 489, 74, 94, 131, 54, 116, 20, 90, 50, 171, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.KLINK, "Klink", 5, false, false, false, "Gear Pokémon", Type.STEEL, null, 0.3, 21, Abilities.PLUS, Abilities.MINUS, Abilities.CLEAR_BODY, 300, 40, 55, 70, 45, 60, 30, 130, 50, 60, GrowthRate.MEDIUM_SLOW, null, false), new PokemonSpecies(Species.KLANG, "Klang", 5, false, false, false, "Gear Pokémon", Type.STEEL, null, 0.6, 51, Abilities.PLUS, Abilities.MINUS, Abilities.CLEAR_BODY, 440, 60, 80, 95, 70, 85, 50, 60, 50, 154, GrowthRate.MEDIUM_SLOW, null, false), From 8ccdf6d55463961a836ff49b8c3f0aaffb11d1a1 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 14 Apr 2024 15:47:00 -0400 Subject: [PATCH 35/51] Fix cachebusting --- src/battle-scene.ts | 3 --- src/loading-scene.ts | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 35c7951c344..0efc4dc8ddb 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -55,7 +55,6 @@ import PokemonInfoContainer from './ui/pokemon-info-container'; import { biomeDepths } from './data/biomes'; import { initTouchControls } from './touch-controls'; import { UiTheme } from './enums/ui-theme'; -import CacheBustedLoaderPlugin from './plugins/cache-busted-loader-plugin'; import { SceneBase } from './scene-base'; import CandyBar from './ui/candy-bar'; @@ -214,8 +213,6 @@ export default class BattleScene extends SceneBase { this.phaseQueuePrepend = []; this.phaseQueuePrependSpliceIndex = -1; this.nextCommandPhaseQueue = []; - - Phaser.Plugins.PluginCache.register('Loader', CacheBustedLoaderPlugin, 'load'); } loadPokemonAtlas(key: string, atlasPath: string, experimental?: boolean) { diff --git a/src/loading-scene.ts b/src/loading-scene.ts index 9a3b7fdd5bd..a3b92112990 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -3,6 +3,7 @@ import { Biome } from "./data/enums/biome"; import { TrainerType } from "./data/enums/trainer-type"; import { trainerConfigs } from "./data/trainer-config"; import { getBiomeHasProps } from "./field/arena"; +import CacheBustedLoaderPlugin from "./plugins/cache-busted-loader-plugin"; import { SceneBase } from "./scene-base"; import { WindowVariant, getWindowVariantSuffix } from "./ui/ui-theme"; import * as Utils from "./utils"; @@ -10,6 +11,8 @@ import * as Utils from "./utils"; export class LoadingScene extends SceneBase { constructor() { super('loading'); + + Phaser.Plugins.PluginCache.register('Loader', CacheBustedLoaderPlugin, 'load'); } preload() { From c8445ba8dd5dfca18807e5e2f2226bb4ea53c9ba Mon Sep 17 00:00:00 2001 From: Appo <72869654+AppleOfTheDark@users.noreply.github.com> Date: Sun, 14 Apr 2024 21:50:26 +0100 Subject: [PATCH 36/51] Implement Competitive Since Competitive is just Defiant but for SPATK, the code for Defiant was reused with the buff changed to special attack. This should mean the ability works the exact same way as Defiant for it's respective stat. --- src/data/ability.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 2ad6959cbd7..a0645d4af9c 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -2563,7 +2563,8 @@ export function initAbilities() { new Ability(Abilities.BULLETPROOF, "Bulletproof", "Protects the Pokémon from some ball and bomb moves.", 6) .attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.BALLBOMB_MOVE)) .ignorable(), - new Ability(Abilities.COMPETITIVE, "Competitive (N)", "Boosts the Sp. Atk stat sharply when a stat is lowered.", 6), + new Ability(Abilities.COMPETITIVE, "Competitive", "Boosts the Sp. Atk stat sharply when a stat is lowered.", 6) + .attr(PostStatChangeStatChangeAbAttr, (target, statsChanged, levels) => levels < 0, [BattleStat.SPATK], 2), new Ability(Abilities.STRONG_JAW, "Strong Jaw", "The Pokémon's strong jaw boosts the power of its biting moves.", 6) .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.BITING_MOVE), 1.5), new Ability(Abilities.REFRIGERATE, "Refrigerate", "Normal-type moves become Ice-type moves. The power of those moves is boosted a little.", 6) From 8932a0b6f9e08ea4825eec7d058ae638ef93c195 Mon Sep 17 00:00:00 2001 From: shayebeadlingkl Date: Sun, 14 Apr 2024 17:43:03 -0400 Subject: [PATCH 37/51] Implements Nature Power --- src/data/move.ts | 107 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/src/data/move.ts b/src/data/move.ts index e5ff01c638f..37f63abc2cf 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -23,6 +23,7 @@ import { SpeciesFormChangeActiveTrigger } from "./pokemon-forms"; import { Species } from "./enums/species"; import { ModifierPoolType } from "#app/modifier/modifier-type"; import { Command } from "../ui/command-ui-handler"; +import { Biome } from "./enums/biome"; export enum MoveCategory { PHYSICAL, @@ -2745,6 +2746,108 @@ export class RandomMoveAttr extends OverrideMoveEffectAttr { } } +export class NaturePowerAttr extends OverrideMoveEffectAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { + return new Promise(resolve => { + var moveId; + switch (user.scene.arena.getTerrainType()) { + // this allows terrains to 'override' the biome move + case TerrainType.NONE: + switch (user.scene.arena.biomeType) { + case Biome.TOWN: + moveId = Moves.TRI_ATTACK; + break; + case Biome.PLAINS + || Biome.GRASS + || Biome.TALL_GRASS + || Biome.MEADOW + || Biome.FOREST + || Biome.JUNGLE: + moveId = Moves.ENERGY_BALL; + break; + case Biome.SEA + || Biome.SWAMP + || Biome.BEACH + || Biome.LAKE + || Biome.SEABED + || Biome.ISLAND: + moveId = Moves.HYDRO_PUMP; + break; + case Biome.MOUNTAIN: + moveId = Moves.AIR_SLASH; + break; + case Biome.BADLANDS + || Biome.DESERT + || Biome.WASTELAND + || Biome.CONSTRUCTION_SITE: + moveId = Moves.EARTH_POWER; + break; + case Biome.CAVE: + moveId = Moves.POWER_GEM; + break; + case Biome.ICE_CAVE + || Biome.SNOWY_FOREST: + moveId = Moves.ICE_BEAM; + break; + case Biome.VOLCANO: + moveId = Moves.LAVA_PLUME; + break; + case Biome.GRAVEYARD + || Biome.RUINS + || Biome.TEMPLE: + moveId = Moves.SHADOW_BALL; + break; + case Biome.DOJO: + moveId = Moves.AURA_SPHERE; + break; + case Biome.FAIRY_CAVE: + moveId = Moves.MOONBLAST; + break; + case Biome.ABYSS + || Biome.SPACE + || Biome.END: + moveId = Moves.DARK_PULSE; + break; + } + break; + case TerrainType.MISTY: + moveId = Moves.MOONBLAST; + break; + case TerrainType.ELECTRIC: + moveId = Moves.THUNDERBOLT; + break; + case TerrainType.GRASSY: + moveId = Moves.ENERGY_BALL; + break; + case TerrainType.PSYCHIC: + moveId = Moves.PSYCHIC; + break; + default: + // Just in case there's no match + moveId = Moves.TRI_ATTACK; + break; + } + + const moveTargets = getMoveTargets(user, moveId); + if (!moveTargets.targets.length) { + resolve(false); + return; + } + const targets = moveTargets.multiple || moveTargets.targets.length === 1 + ? moveTargets.targets + : moveTargets.targets.indexOf(target.getBattlerIndex()) > -1 + ? [ target.getBattlerIndex() ] + : [ moveTargets.targets[user.randSeedInt(moveTargets.targets.length)] ]; + user.getMoveQueue().push({ move: moveId, targets: targets, ignorePP: true }); + user.scene.unshiftPhase(new MovePhase(user.scene, user, targets, new PokemonMove(moveId, 0, 0, true), true)); + initMoveAnim(moveId).then(() => { + loadMoveAnimAssets(user.scene, [ moveId ], true) + .then(() => resolve(true)); + }); + }); + } +} + const lastMoveCopiableCondition: MoveConditionFunc = (user, target, move) => { const copiableMove = user.scene.currentBattle.lastMove; @@ -3875,7 +3978,9 @@ export function initMoves() { .attr(MovePowerMultiplierAttr, (user, target, move) => target.status?.effect === StatusEffect.PARALYSIS ? 2 : 1) .attr(HealStatusEffectAttr, true, StatusEffect.PARALYSIS), new SelfStatusMove(Moves.FOLLOW_ME, "Follow Me (N)", Type.NORMAL, -1, 20, "The user draws attention to itself, making all targets take aim only at the user.", -1, 2, 3), - new StatusMove(Moves.NATURE_POWER, "Nature Power (N)", Type.NORMAL, -1, 20, "This attack makes use of nature's power. Its effects vary depending on the user's environment.", -1, 0, 3), + new StatusMove(Moves.NATURE_POWER, "Nature Power", Type.NORMAL, -1, 20, "This attack makes use of nature's power. Its effects vary depending on the user's environment.", -1, 0, 3) + .attr(NaturePowerAttr) + .ignoresVirtual(), new SelfStatusMove(Moves.CHARGE, "Charge (P)", Type.ELECTRIC, -1, 20, "The user boosts the power of the Electric move it uses on the next turn. This also raises the user's Sp. Def stat.", -1, 0, 3) .attr(StatChangeAttr, BattleStat.SPDEF, 1, true), new StatusMove(Moves.TAUNT, "Taunt (N)", Type.DARK, 100, 20, "The target is taunted into a rage that allows it to use only attack moves for three turns.", -1, 0, 3), From 669bc367e5d003919616e242e78290e8e532fbbf Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 14 Apr 2024 18:19:35 -0400 Subject: [PATCH 38/51] Fix Super Luck working on the wrong side --- src/field/pokemon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index c396ca37bcc..f7832d612d0 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1186,7 +1186,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs(HighCritAttr, source, this, move, critLevel); this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel); const bonusCrit = new Utils.BooleanHolder(false); - if (applyAbAttrs(BonusCritAbAttr, this, null, bonusCrit)) { + if (applyAbAttrs(BonusCritAbAttr, source, null, bonusCrit)) { if (bonusCrit.value) critLevel.value += 1; } From be9d07ab1a2bb43585395da30e94629e2ac5daca Mon Sep 17 00:00:00 2001 From: shayebeadlingkl Date: Sun, 14 Apr 2024 18:25:15 -0400 Subject: [PATCH 39/51] fixes targeting --- src/data/move.ts | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index 37f63abc2cf..e4eed04b8ae 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -2790,7 +2790,7 @@ export class NaturePowerAttr extends OverrideMoveEffectAttr { moveId = Moves.ICE_BEAM; break; case Biome.VOLCANO: - moveId = Moves.LAVA_PLUME; + moveId = Moves.FLAMETHROWER; break; case Biome.GRAVEYARD || Biome.RUINS @@ -2828,18 +2828,8 @@ export class NaturePowerAttr extends OverrideMoveEffectAttr { break; } - const moveTargets = getMoveTargets(user, moveId); - if (!moveTargets.targets.length) { - resolve(false); - return; - } - const targets = moveTargets.multiple || moveTargets.targets.length === 1 - ? moveTargets.targets - : moveTargets.targets.indexOf(target.getBattlerIndex()) > -1 - ? [ target.getBattlerIndex() ] - : [ moveTargets.targets[user.randSeedInt(moveTargets.targets.length)] ]; - user.getMoveQueue().push({ move: moveId, targets: targets, ignorePP: true }); - user.scene.unshiftPhase(new MovePhase(user.scene, user, targets, new PokemonMove(moveId, 0, 0, true), true)); + user.getMoveQueue().push({ move: moveId, targets: [target.getBattlerIndex()], ignorePP: true }); + user.scene.unshiftPhase(new MovePhase(user.scene, user, [target.getBattlerIndex()], new PokemonMove(moveId, 0, 0, true), true)); initMoveAnim(moveId).then(() => { loadMoveAnimAssets(user.scene, [ moveId ], true) .then(() => resolve(true)); From 208279ce772a8dd43feddf221b04feb925f8424e Mon Sep 17 00:00:00 2001 From: surniki Date: Sun, 14 Apr 2024 17:00:50 -0500 Subject: [PATCH 40/51] Copies the moveset of the evolving Nincada to the newly generated pokemon for Shedinja. --- src/field/pokemon.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index f7832d612d0..9fbe01f53e1 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2260,6 +2260,7 @@ export class PlayerPokemon extends Pokemon { if (newEvolution.condition.predicate(this)) { const newPokemon = this.scene.addPlayerPokemon(this.species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, this.ivs, this.nature); newPokemon.natureOverride = this.natureOverride; + newPokemon.moveset = this.moveset.slice(); newPokemon.fusionSpecies = this.fusionSpecies; newPokemon.fusionFormIndex = this.fusionFormIndex; newPokemon.fusionAbilityIndex = this.fusionAbilityIndex; From 99aa7854c0bdceca7fb07d9bd42b96c164c366de Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 14 Apr 2024 20:23:51 -0400 Subject: [PATCH 41/51] Prevent replacing learnset when species is overridden --- src/field/pokemon.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 9fbe01f53e1..16e695769e7 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -850,10 +850,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return true; }); } else - levelMoves = this.getSpeciesForm().getLevelMoves(); + levelMoves = this.getSpeciesForm(true).getLevelMoves(); if (this.fusionSpecies) { const evolutionLevelMoves = levelMoves.slice(0, Math.max(levelMoves.findIndex(lm => !!lm[0]), 0)); - const fusionLevelMoves = this.getFusionSpeciesForm().getLevelMoves(); + const fusionLevelMoves = this.getFusionSpeciesForm(true).getLevelMoves(); const newLevelMoves: LevelMoves = []; while (levelMoves.length && levelMoves[0][0] < startingLevel) levelMoves.shift(); From e0dd7d6d48fe2f59c875cf0edfb671b6f1b43267 Mon Sep 17 00:00:00 2001 From: Madmadness65 Date: Sun, 14 Apr 2024 20:30:54 -0500 Subject: [PATCH 42/51] Implement Shields Down's form change; Fix wild Rotom forms not appearing Shields Down is only partially implemented; the form changing aspect is implemented, but the immunity to statuses is not yet. Minior's alternate colored forms can now be encountered in the wild. Rotom's appliance forms should *finally* appear in the wild correctly now. And a minor fix for Zen Mode activating incorrectly. --- src/battle-scene.ts | 2 ++ src/data/ability.ts | 11 +++++++---- src/data/pokemon-forms.ts | 16 ++++++++++++++++ src/field/arena.ts | 14 ++++++++++++++ 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 0efc4dc8ddb..30c6c560368 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -936,6 +936,8 @@ export default class BattleScene extends SceneBase { case Species.TATSUGIRI: case Species.PALDEA_TAUROS: return Utils.randSeedInt(species.forms.length); + case Species.MINIOR: + return Utils.randSeedInt(6); case Species.MEOWSTIC: case Species.INDEEDEE: case Species.BASCULEGION: diff --git a/src/data/ability.ts b/src/data/ability.ts index a0645d4af9c..7cb03d7e7e3 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -2536,9 +2536,9 @@ export function initAbilities() { .attr(PostDefendContactDamageAbAttr, 8) .bypassFaint(), new Ability(Abilities.ZEN_MODE, "Zen Mode", "Changes the Pokémon's shape when HP is half or less.", 5) - .attr(PostBattleInitFormChangeAbAttr, p => p.getHpRatio() >= 0.5 ? 0 : 1) - .attr(PostSummonFormChangeAbAttr, p => p.getHpRatio() >= 0.5 ? 0 : 1) - .attr(PostTurnFormChangeAbAttr, p => p.getHpRatio() >= 0.5 ? 0 : 1) + .attr(PostBattleInitFormChangeAbAttr, p => p.getHpRatio() <= 0.5 ? 1 : 0) + .attr(PostSummonFormChangeAbAttr, p => p.getHpRatio() <= 0.5 ? 1 : 0) + .attr(PostTurnFormChangeAbAttr, p => p.getHpRatio() <= 0.5 ? 1 : 0) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr), @@ -2616,7 +2616,10 @@ export function initAbilities() { new Ability(Abilities.WATER_COMPACTION, "Water Compaction", "Boosts the Pokémon's Defense stat sharply when hit by a Water-type move.", 7) .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.WATER, BattleStat.DEF, 2), new Ability(Abilities.MERCILESS, "Merciless (N)", "The Pokémon's attacks become critical hits if the target is poisoned.", 7), - new Ability(Abilities.SHIELDS_DOWN, "Shields Down (N)", "When its HP becomes half or less, the Pokémon's shell breaks and it becomes aggressive.", 7) + new Ability(Abilities.SHIELDS_DOWN, "Shields Down (P)", "When its HP becomes half or less, the Pokémon's shell breaks and it becomes aggressive.", 7) + .attr(PostBattleInitFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) + .attr(PostSummonFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) + .attr(PostTurnFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr), diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index e5bcf25a4fe..2d2d491bf31 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -549,6 +549,22 @@ export const pokemonFormChanges: PokemonFormChanges = { new SpeciesFormChange(Species.WISHIWASHI, '', 'school', new SpeciesFormChangeManualTrigger(), true), new SpeciesFormChange(Species.WISHIWASHI, 'school', '', new SpeciesFormChangeManualTrigger(), true) ], + [Species.MINIOR]: [ + new SpeciesFormChange(Species.MINIOR, 'red-meteor', 'red', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'red', 'red-meteor', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'orange-meteor', 'orange', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'orange', 'orange-meteor', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'yellow-meteor', 'yellow', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'yellow', 'yellow-meteor', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'green-meteor', 'green', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'green', 'green-meteor', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'blue-meteor', 'blue', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'blue', 'blue-meteor', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'indigo-meteor', 'indigo', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'indigo', 'indigo-meteor', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'violet-meteor', 'violet', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'violet', 'violet-meteor', new SpeciesFormChangeManualTrigger(), true) + ], [Species.NECROZMA]: [ new SpeciesFormChange(Species.NECROZMA, '', 'dawn-wings', new SpeciesFormChangeItemTrigger(FormChangeItem.N_LUNARIZER)), new SpeciesFormChange(Species.NECROZMA, '', 'dusk-mane', new SpeciesFormChangeItemTrigger(FormChangeItem.N_SOLARIZER)) diff --git a/src/field/arena.ts b/src/field/arena.ts index 11677dc490e..a3ba8cb69ce 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -166,6 +166,20 @@ export class Arena { return 2; } break; + case Species.ROTOM: + switch (this.biomeType) { + case Biome.VOLCANO: + return 1; + case Biome.SEA: + return 2; + case Biome.ICE_CAVE: + return 3; + case Biome.MOUNTAIN: + return 4; + case Biome.TALL_GRASS: + return 5; + } + break; case Species.SCATTERBUG: case Species.SPEWPA: case Species.VIVILLON: From d699a0f7d4f2422495733fd7ea2b45c08e263cb5 Mon Sep 17 00:00:00 2001 From: lucfd <83493765+lucfd@users.noreply.github.com> Date: Sun, 14 Apr 2024 22:26:58 -0400 Subject: [PATCH 43/51] Implemented barrier-breaking moves (#130) * Implemented barrier-breaking moves * removed (P) from move names * refactored to support defog --- src/data/move.ts | 50 ++++++++++++++++++++++++++++++++++++++++------ src/field/arena.ts | 10 ++++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index e4eed04b8ae..db563c9f425 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -2531,6 +2531,40 @@ export class AddArenaTrapTagAttr extends AddArenaTagAttr { } } +export class RemoveScreensAttr extends MoveEffectAttr { + + private targetBothSides: boolean; + + constructor(targetBothSides: boolean = false) { + super(true, MoveEffectTrigger.PRE_APPLY); + this.targetBothSides = targetBothSides; + } + + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + + if (!super.apply(user, target, move, args)) + return false; + + if(this.targetBothSides){ + user.scene.arena.removeTagOnSide(ArenaTagType.REFLECT, ArenaTagSide.PLAYER); + user.scene.arena.removeTagOnSide(ArenaTagType.LIGHT_SCREEN, ArenaTagSide.PLAYER); + user.scene.arena.removeTagOnSide(ArenaTagType.AURORA_VEIL, ArenaTagSide.PLAYER); + + user.scene.arena.removeTagOnSide(ArenaTagType.REFLECT, ArenaTagSide.ENEMY); + user.scene.arena.removeTagOnSide(ArenaTagType.LIGHT_SCREEN, ArenaTagSide.ENEMY); + user.scene.arena.removeTagOnSide(ArenaTagType.AURORA_VEIL, ArenaTagSide.ENEMY); + } + else{ + user.scene.arena.removeTagOnSide(ArenaTagType.REFLECT, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY); + user.scene.arena.removeTagOnSide(ArenaTagType.LIGHT_SCREEN, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY); + user.scene.arena.removeTagOnSide(ArenaTagType.AURORA_VEIL, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY); + } + + return true; + + } +} + export class ForceSwitchOutAttr extends MoveEffectAttr { private user: boolean; private batonPass: boolean; @@ -3993,7 +4027,8 @@ export function initMoves() { new SelfStatusMove(Moves.RECYCLE, "Recycle (N)", Type.NORMAL, -1, 10, "The user recycles a held item that has been used in battle so it can be used again.", -1, 0, 3), new AttackMove(Moves.REVENGE, "Revenge", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, "This attack move's power is doubled if the user has been hurt by the opponent in the same turn.", -1, -4, 3) .attr(TurnDamagedDoublePowerAttr), - new AttackMove(Moves.BRICK_BREAK, "Brick Break (P)", Type.FIGHTING, MoveCategory.PHYSICAL, 75, 100, 15, "The user attacks with a swift chop. It can also break barriers, such as Light Screen and Reflect.", -1, 0, 3), + new AttackMove(Moves.BRICK_BREAK, "Brick Break", Type.FIGHTING, MoveCategory.PHYSICAL, 75, 100, 15, "The user attacks with a swift chop. It can also break barriers, such as Light Screen and Reflect.", -1, 0, 3) + .attr(RemoveScreensAttr), new StatusMove(Moves.YAWN, "Yawn", Type.NORMAL, -1, 10, "The user lets loose a huge yawn that lulls the target into falling asleep on the next turn.", -1, 0, 3) .attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true) .condition((user, target, move) => !target.status), @@ -4369,7 +4404,8 @@ export function initMoves() { new StatusMove(Moves.DEFOG, "Defog", Type.FLYING, -1, 15, "A strong wind blows away the target's barriers such as Reflect or Light Screen. This also lowers the target's evasiveness.", -1, 0, 4) .attr(StatChangeAttr, BattleStat.EVA, -1) .attr(ClearWeatherAttr, WeatherType.FOG) - .attr(ClearTerrainAttr), + .attr(ClearTerrainAttr) + .attr(RemoveScreensAttr, true), new StatusMove(Moves.TRICK_ROOM, "Trick Room", Type.PSYCHIC, -1, 5, "The user creates a bizarre area in which slower Pokémon get to move first for five turns.", -1, -7, 4) .attr(AddArenaTagAttr, ArenaTagType.TRICK_ROOM, 5) .ignoresProtect() @@ -4967,8 +5003,9 @@ export function initMoves() { .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.FLEUR_CANNON, "Fleur Cannon", Type.FAIRY, MoveCategory.SPECIAL, 130, 90, 5, "The user unleashes a strong beam. The attack's recoil harshly lowers the user's Sp. Atk stat.", 100, 0, 7) .attr(StatChangeAttr, BattleStat.SPATK, -2, true), - new AttackMove(Moves.PSYCHIC_FANGS, "Psychic Fangs (P)", Type.PSYCHIC, MoveCategory.PHYSICAL, 85, 100, 10, "The user bites the target with its psychic capabilities. This can also destroy Light Screen and Reflect.", -1, 0, 7) - .bitingMove(), + new AttackMove(Moves.PSYCHIC_FANGS, "Psychic Fangs", Type.PSYCHIC, MoveCategory.PHYSICAL, 85, 100, 10, "The user bites the target with its psychic capabilities. This can also destroy Light Screen and Reflect.", -1, 0, 7) + .bitingMove() + .attr(RemoveScreensAttr), new AttackMove(Moves.STOMPING_TANTRUM, "Stomping Tantrum (P)", Type.GROUND, MoveCategory.PHYSICAL, 75, 100, 10, "Driven by frustration, the user attacks the target. If the user's previous move has failed, the power of this move doubles.", -1, 0, 7), new AttackMove(Moves.SHADOW_BONE, "Shadow Bone", Type.GHOST, MoveCategory.PHYSICAL, 85, 100, 10, "The user attacks by beating the target with a bone that contains a spirit. This may also lower the target's Defense stat.", 20, 0, 7) .attr(StatChangeAttr, BattleStat.DEF, -1) @@ -5445,8 +5482,9 @@ export function initMoves() { new AttackMove(Moves.AQUA_STEP, "Aqua Step", Type.WATER, MoveCategory.PHYSICAL, 80, 100, 10, "The user toys with the target and attacks it using light and fluid dance steps. This also boosts the user's Speed stat.", 100, 0, 9) .attr(StatChangeAttr, BattleStat.SPD, 1, true) .danceMove(), - new AttackMove(Moves.RAGING_BULL, "Raging Bull (P)", Type.NORMAL, MoveCategory.PHYSICAL, 90, 100, 10, "The user performs a tackle like a raging bull. This move's type depends on the user's form. It can also break barriers, such as Light Screen and Reflect.", -1, 0, 9) - .attr(RagingBullTypeAttr), + new AttackMove(Moves.RAGING_BULL, "Raging Bull", Type.NORMAL, MoveCategory.PHYSICAL, 90, 100, 10, "The user performs a tackle like a raging bull. This move's type depends on the user's form. It can also break barriers, such as Light Screen and Reflect.", -1, 0, 9) + .attr(RagingBullTypeAttr) + .attr(RemoveScreensAttr), new AttackMove(Moves.MAKE_IT_RAIN, "Make It Rain", Type.STEEL, MoveCategory.SPECIAL, 120, 100, 5, "The user attacks by throwing out a mass of coins. This also lowers the user's Sp. Atk stat. Money is earned after the battle.", -1, 0, 9) .attr(MoneyAttr) .attr(StatChangeAttr, BattleStat.SPATK, -1, true, null, true, true) diff --git a/src/field/arena.ts b/src/field/arena.ts index a3ba8cb69ce..910d998d68e 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -541,6 +541,16 @@ export class Arena { return !!tag; } + removeTagOnSide(tagType: ArenaTagType, side: ArenaTagSide): boolean { + const tag = this.getTagOnSide(tagType, side); + if (tag) { + tag.onRemove(this); + this.tags.splice(this.tags.indexOf(tag), 1); + } + return !!tag; + } + + removeAllTags(): void { while (this.tags.length) { this.tags[0].onRemove(this); From 032ab95756d96fb43a0f358085068f209f2b5115 Mon Sep 17 00:00:00 2001 From: Matthew Ross Date: Sun, 14 Apr 2024 19:26:35 -0700 Subject: [PATCH 44/51] Additional check for can apply ability after move used --- src/phases.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phases.ts b/src/phases.ts index 7e5a3aab1d6..ff2f62c3eb9 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2389,7 +2389,7 @@ export class MoveEffectPhase extends PokemonPhase { } Utils.executeIf(!isProtected && !chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT && (!attr.firstHitOnly || firstHit), user, target, this.move.getMove()).then(() => { - return Utils.executeIf(!target.isFainted(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, hitResult).then(() => { + return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, hitResult).then(() => { if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) user.scene.applyModifiers(EnemyAttackStatusEffectChanceModifier, false, target); })).then(() => { From e7f1969099d36887766f6051db18307e6cc8d7b7 Mon Sep 17 00:00:00 2001 From: Madmadness65 Date: Sun, 14 Apr 2024 21:41:12 -0500 Subject: [PATCH 45/51] Implement Ivy Cudgel's type changing property Basically a copy/paste of Raging Bull's code. --- src/data/move.ts | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/data/move.ts b/src/data/move.ts index db563c9f425..841327b42f3 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -2017,6 +2017,45 @@ export class RagingBullTypeAttr extends VariableMoveTypeAttr { } } +export class IvyCudgelTypeAttr extends VariableMoveTypeAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.OGERPON)) { + const form = user.species.speciesId === Species.OGERPON ? user.formIndex : user.fusionSpecies.formIndex; + const type = (args[0] as Utils.IntegerHolder); + + switch (form) { + case 1: // Wellspring Mask + type.value = Type.WATER; + break; + case 2: // Hearthflame Mask + type.value = Type.FIRE; + break; + case 3: // Cornerstone Mask + type.value = Type.ROCK; + break; + case 4: // Teal Mask Tera + type.value = Type.GRASS; + break; + case 5: // Wellspring Mask Tera + type.value = Type.WATER; + break; + case 6: // Hearthflame Mask Tera + type.value = Type.FIRE; + break; + case 7: // Cornerstone Mask Tera + type.value = Type.ROCK; + break; + default: + type.value = Type.GRASS; + break; + } + return true; + } + + return false; + } +} + export class WeatherBallTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!user.scene.arena.weather?.isEffectSuppressed(user.scene)) { @@ -5572,7 +5611,8 @@ export function initMoves() { new AttackMove(Moves.SYRUP_BOMB, "Syrup Bomb (P)", Type.GRASS, MoveCategory.SPECIAL, 60, 85, 10, "The user sets off an explosion of sticky candy syrup, which coats the target and causes the target's Speed stat to drop each turn for three turns.", -1, 0, 9) .attr(StatChangeAttr, BattleStat.SPD, -1) //Temporary .ballBombMove(), - new AttackMove(Moves.IVY_CUDGEL, "Ivy Cudgel (P)", Type.GRASS, MoveCategory.PHYSICAL, 100, 100, 10, "The user strikes with an ivy-wrapped cudgel. This move's type changes depending on the mask worn by the user, and it has a heightened chance of landing a critical hit.", -1, 0, 9) + new AttackMove(Moves.IVY_CUDGEL, "Ivy Cudgel", Type.GRASS, MoveCategory.PHYSICAL, 100, 100, 10, "The user strikes with an ivy-wrapped cudgel. This move's type changes depending on the mask worn by the user, and it has a heightened chance of landing a critical hit.", -1, 0, 9) + .attr(IvyCudgelTypeAttr) .attr(HighCritAttr) .makesContact(false), new AttackMove(Moves.ELECTRO_SHOT, "Electro Shot", Type.ELECTRIC, MoveCategory.SPECIAL, 130, 100, 10, "The user gathers electricity on the first turn, boosting its Sp. Atk stat, then fires a high-voltage shot on the next turn. The shot will be fired immediately in rain.", 100, 0, 9) From 8dd0aa538435ab97fe5585cf1cf09f4c8b342338 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 14 Apr 2024 22:56:25 -0400 Subject: [PATCH 46/51] Fix some female breeder names showing as male names --- src/data/trainer-names.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/trainer-names.ts b/src/data/trainer-names.ts index 8d7f2e4fdce..a1c83af0779 100644 --- a/src/data/trainer-names.ts +++ b/src/data/trainer-names.ts @@ -79,7 +79,7 @@ export const trainerNamePools = { [TrainerType.BEAUTY]: ["Cassie","Julia","Olivia","Samantha","Valerie","Victoria","Bridget","Connie","Jessica","Johanna","Melissa","Sheila","Shirley","Tiffany","Namiko","Thalia","Grace","Lola","Lori","Maura","Tamia","Cyndy","Devon","Gabriella","Harley","Lindsay","Nicola","Callie","Charlotte","Kassandra","December","Fleming","Nikola","Aimee","Anais","Brigitte","Cassandra","Andrea","Brittney","Carolyn","Krystal","Alexis","Alice","Aina","Anya","Arianna","Aubrey","Beverly","Camille","Beauty","Evette","Hansol","Haruka","Jill","Jo","Lana","Lois","Lucy","Mai","Nickie","Nicole","Prita","Rose","Shelly","Suzy","Tessa","Anita","Alissa","Rita","Cudsy","Eloff","Miru","Minot","Nevah","Niven","Ogoin"], [TrainerType.BIKER]: ["Charles","Dwayne","Glenn","Harris","Joel","Riley","Zeke","Alex","Billy","Ernest","Gerald","Hideo","Isaac","Jared","Jaren","Jaxon","Jordy","Lao","Lukas","Malik","Nikolas","Ricardo","Ruben","Virgil","William","Aiden","Dale","Dan","Jacob","Markey","Reese","Teddy","Theron","Jeremy","Morgann","Phillip","Philip","Stanley","Dillon"], [TrainerType.BLACK_BELT]: [["Kenji","Lao","Lung","Nob","Wai","Yoshi","Atsushi","Daisuke","Hideki","Hitoshi","Kiyo","Koichi","Koji","Yuji","Cristian","Rhett","Takao","Theodore","Zander","Aaron","Hugh","Mike","Nicolas","Shea","Takashi","Adam","Carl","Colby","Darren","David","Davon","Derek","Eddie","Gregory","Griffin","Jarrett","Jeffery","Kendal","Kyle","Luke","Miles","Nathaniel","Philip","Rafael","Ray","Ricky","Sean","Willie","Ander","Manford","Benjamin","Corey","Edward","Grant","Jay","Kendrew","Kentaro","Ryder","Teppei","Thomas","Tyrone","Andrey","Donny","Drago","Gordon","Grigor","Jeriel","Kenneth","Martell","Mathis","Rich","Rocky","Rodrigo","Wesley","Zachery","Alonzo","Cadoc","Gunnar","Igor","Killian","Markus","Ricardo","Yanis","Banting","Clayton","Duane","Earl","Greg","Roy","Terry","Tracy","Walter","Alvaro","Curtis","Francis","Ross","Brice","Cheng","Dudley","Eric","Kano","Masahiro","Randy","Ryuji","Steve","Tadashi","Wong","Yuen","Brian","Carter","Reece","Nick","Yang"],["Cora","Cyndy","Jill","Laura","Sadie","Tessa","Vivian","Aisha","Callie","Danielle","Helene","Jocelyn","Lilith","Paula","Reyna","Helen","Kelsey","Tyler","Amy","Chandra","Hillary","Janie","Lee","Maggie","Mikiko","Miriam","Sharon","Susie","Xiao","Alize","Azra","Brenda","Chalina","Chan","Glinda","Maki","Tia","Tiffany","Wendy","Andrea","Gabrielle","Gerardine","Hailey","Hedvig","Justine","Kinsey","Sigrid","Veronique","Tess"]], - [TrainerType.BREEDER]: [["Isaac","Myles","Salvadore","Allison","Alize","Bethany","Lily","Albert","Kahlil","Eustace","Galen","Owen","Addison","Marcus","Foster","Cory","Glenn","Jay","Wesley","William","Adrian","Bradley","Jaime"],["Lydia","Gabrielle","Jayden","Pat","Veronica","Amber","Jennifer","Kaylee","Adelaide","Brooke","Ethel","April","Irene","Magnolia","Amala","Mercy","Amanda","Ikue","Savannah","Yuka","Chloe","Debra","Denise","Elena"]], + [TrainerType.BREEDER]: [["Isaac","Myles","Salvadore","Albert","Kahlil","Eustace","Galen","Owen","Addison","Marcus","Foster","Cory","Glenn","Jay","Wesley","William","Adrian","Bradley","Jaime"],["Allison","Alize","Bethany","Lily","Lydia","Gabrielle","Jayden","Pat","Veronica","Amber","Jennifer","Kaylee","Adelaide","Brooke","Ethel","April","Irene","Magnolia","Amala","Mercy","Amanda","Ikue","Savannah","Yuka","Chloe","Debra","Denise","Elena"]], [TrainerType.CLERK]: [["Chaz","Clemens","Doug","Fredric","Ivan","Isaac","Nelson","Wade","Warren","Augustin","Gilligan","Cody","Jeremy","Shane","Dugal","Royce","Ronald"],["Alberta","Ingrid","Katie","Piper","Trisha","Wren","Britney","Lana","Jessica","Kristen","Michelle","Gabrielle"]], [TrainerType.CYCLIST]: [["Axel","James","John","Ryan","Hector","Jeremiah"],["Kayla","Megan","Nicole","Rachel","Krissa","Adelaide"]], [TrainerType.DANCER]: ["Brian","Davey","Dirk","Edmond","Mickey","Raymond","Cara","Julia","Maika","Mireille","Ronda","Zoe"], From 8819473dced5a9c3d6c1e471e520143c573e5578 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 14 Apr 2024 23:46:20 -0400 Subject: [PATCH 47/51] Lower price of Sacred Ash --- src/modifier/modifier-type.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 258e79e3651..8078a7c06b9 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -1341,7 +1341,7 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: integer, base new ModifierTypeOption(modifierTypes.FULL_RESTORE(), 0, baseCost * 2.25) ], [ - new ModifierTypeOption(modifierTypes.SACRED_ASH(), 0, baseCost * 12) + new ModifierTypeOption(modifierTypes.SACRED_ASH(), 0, baseCost * 10) ] ]; return options.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)).flat(); From 6881246d17c352a4201eb3ec4b9469eee7774efb Mon Sep 17 00:00:00 2001 From: Xavion3 Date: Mon, 15 Apr 2024 15:03:50 +1000 Subject: [PATCH 48/51] Implement check functions for abilities --- src/data/battler-tags.ts | 4 ++-- src/data/move.ts | 10 +++++----- src/data/pokemon-forms.ts | 4 ++-- src/data/weather.ts | 2 +- src/field/pokemon.ts | 24 ++++++++++++++++++++---- src/phases.ts | 2 +- 6 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 20946c784cb..a45522b5e1e 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -799,7 +799,7 @@ export class TruantTag extends AbilityBattlerTag { } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { - if ((!pokemon.canApplyAbility() || pokemon.getAbility().id !== Abilities.TRUANT) && (!pokemon.canApplyAbility(true) || pokemon.getPassiveAbility().id !== Abilities.TRUANT)) + if (!pokemon.hasAbility(Abilities.TRUANT)) return super.lapse(pokemon, lapseType); const passive = pokemon.getAbility().id !== Abilities.TRUANT; @@ -827,7 +827,7 @@ export class SlowStartTag extends AbilityBattlerTag { } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { - if ((!pokemon.canApplyAbility() || pokemon.getAbility().id !== this.ability) && (!pokemon.canApplyAbility(true) || pokemon.getPassiveAbility().id !== this.ability)) + if (!pokemon.hasAbility(this.ability)) this.turnCount = 1; return super.lapse(pokemon, lapseType); diff --git a/src/data/move.ts b/src/data/move.ts index 841327b42f3..de749ba3ad8 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -275,7 +275,7 @@ export default class Move { checkFlag(flag: MoveFlags, user: Pokemon, target: Pokemon): boolean { switch (flag) { case MoveFlags.MAKES_CONTACT: - if ((user.canApplyAbility() && user.getAbility().hasAttr(IgnoreContactAbAttr)) || (user.canApplyAbility(true) && user.getPassiveAbility().hasAttr(IgnoreContactAbAttr))) + if (user.hasAbilityWithAttr(IgnoreContactAbAttr)) return false; break; } @@ -4849,9 +4849,9 @@ export function initMoves() { .attr(StatChangeAttr, [ BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 2, true) .ignoresVirtual(), new StatusMove(Moves.MAGNETIC_FLUX, "Magnetic Flux", Type.ELECTRIC, -1, 20, "The user manipulates magnetic fields, which raises the Defense and Sp. Def stats of ally Pokémon with the Plus or Minus Ability.", -1, 0, 6) - .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], 1, false, (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS].find(a => a === user.getAbility().id || (user.canApplyPassive() && a === user.getPassiveAbility().id))) + .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], 1, false, (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS].find(a => target.hasAbility(a, false))) .target(MoveTarget.USER_AND_ALLIES) - .condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS].find(a => a === p.getAbility().id || (user.canApplyPassive() && a === user.getPassiveAbility().id)))), + .condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS].find(a => p.hasAbility(a, false)))), new StatusMove(Moves.HAPPY_HOUR, "Happy Hour (N)", Type.NORMAL, -1, 30, "Using Happy Hour doubles the amount of prize money received after battle.", -1, 0, 6) // No animation .target(MoveTarget.USER_SIDE), new StatusMove(Moves.ELECTRIC_TERRAIN, "Electric Terrain", Type.ELECTRIC, -1, 10, "The user electrifies the ground for five turns, powering up Electric-type moves. Pokémon on the ground no longer fall asleep.", -1, 0, 6) @@ -4979,9 +4979,9 @@ export function initMoves() { new SelfStatusMove(Moves.LASER_FOCUS, "Laser Focus", Type.NORMAL, -1, 30, "The user concentrates intensely. The attack on the next turn always results in a critical hit.", -1, 0, 7) .attr(AddBattlerTagAttr, BattlerTagType.ALWAYS_CRIT, true, false), new StatusMove(Moves.GEAR_UP, "Gear Up", Type.STEEL, -1, 20, "The user engages its gears to raise the Attack and Sp. Atk stats of ally Pokémon with the Plus or Minus Ability.", -1, 0, 7) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], 1, false, (user, target, move) => [ Abilities.PLUS, Abilities.MINUS ].includes(target.getAbility().id) || (target.canApplyPassive() && [ Abilities.PLUS, Abilities.MINUS ].includes(target.getPassiveAbility().id))) + .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], 1, false, (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS].find(a => target.hasAbility(a, false))) .target(MoveTarget.USER_AND_ALLIES) - .condition((user, target, move) => !![ user, user.getAlly() ].find(p => p && [ Abilities.PLUS, Abilities.MINUS ].includes(p.getAbility().id) || (target.canApplyPassive() && [ Abilities.PLUS, Abilities.MINUS ].includes(target.getPassiveAbility().id)))), + .condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS].find(a => p.hasAbility(a, false)))), new AttackMove(Moves.THROAT_CHOP, "Throat Chop (P)", Type.DARK, MoveCategory.PHYSICAL, 80, 100, 15, "The user attacks the target's throat, and the resultant suffering prevents the target from using moves that emit sound for two turns.", 100, 0, 7), new AttackMove(Moves.POLLEN_PUFF, "Pollen Puff (P)", Type.BUG, MoveCategory.SPECIAL, 90, 100, 15, "The user attacks the enemy with a pollen puff that explodes. If the target is an ally, it gives the ally a pollen puff that restores its HP instead.", -1, 0, 7) .ballBombMove(), diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index 2d2d491bf31..bb22f985976 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -535,8 +535,8 @@ export const pokemonFormChanges: PokemonFormChanges = { new SpeciesFormChange(Species.MELOETTA, 'pirouette', 'aria', new SpeciesFormChangeActiveTrigger(false), true) ], [Species.AEGISLASH]: [ - new SpeciesFormChange(Species.AEGISLASH, 'blade', 'shield', new SpeciesFormChangePreMoveTrigger(Moves.KINGS_SHIELD), true, new SpeciesFormChangeCondition(p => p.getAbility().id === Abilities.STANCE_CHANGE)), - new SpeciesFormChange(Species.AEGISLASH, 'shield', 'blade', new SpeciesFormChangePreMoveTrigger(m => allMoves[m].category !== MoveCategory.STATUS), true, new SpeciesFormChangeCondition(p => p.getAbility().id === Abilities.STANCE_CHANGE)), + new SpeciesFormChange(Species.AEGISLASH, 'blade', 'shield', new SpeciesFormChangePreMoveTrigger(Moves.KINGS_SHIELD), true, new SpeciesFormChangeCondition(p => p.hasAbility(Abilities.STANCE_CHANGE))), + new SpeciesFormChange(Species.AEGISLASH, 'shield', 'blade', new SpeciesFormChangePreMoveTrigger(m => allMoves[m].category !== MoveCategory.STATUS), true, new SpeciesFormChangeCondition(p => p.hasAbility(Abilities.STANCE_CHANGE))), new SpeciesFormChange(Species.AEGISLASH, 'blade', 'shield', new SpeciesFormChangeActiveTrigger(false), true) ], [Species.DIANCIE]: [ diff --git a/src/data/weather.ts b/src/data/weather.ts index b3a59f34b98..b2881598450 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -108,7 +108,7 @@ export class Weather { for (let pokemon of field) { let suppressWeatherEffectAbAttr = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr; if (!suppressWeatherEffectAbAttr) - suppressWeatherEffectAbAttr = pokemon.canApplyPassive() ? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr : null; + suppressWeatherEffectAbAttr = pokemon.hasPassive() ? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr : null; if (suppressWeatherEffectAbAttr && (!this.isImmutable() || suppressWeatherEffectAbAttr.affectsImmutable)) return true; } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 16e695769e7..9181be78edf 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -25,7 +25,7 @@ import { TempBattleStat } from '../data/temp-battle-stat'; import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag'; import { ArenaTagType } from "../data/enums/arena-tag-type"; import { Biome } from "../data/enums/biome"; -import { Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from '../data/ability'; +import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from '../data/ability'; import { Abilities } from "#app/data/enums/abilities"; import PokemonData from '../system/pokemon-data'; import { BattlerIndex } from '../battle'; @@ -561,7 +561,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let value = Math.floor(((2 * baseStat + this.ivs[s]) * this.level) * 0.01); if (isHp) { value = value + this.level + 10; - if ((this.canApplyAbility() && this.getAbility().hasAttr(NonSuperEffectiveImmunityAbAttr)) || (this.canApplyAbility(true) && this.getPassiveAbility().hasAttr(NonSuperEffectiveImmunityAbAttr))) + if (this.hasAbility(Abilities.WONDER_GUARD, false, true)) value = 1; if (this.hp > value || this.hp === undefined) this.hp = value; @@ -730,12 +730,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return allAbilities[starterPassiveAbilities[starterSpeciesId]]; } - canApplyPassive(): boolean { + hasPassive(): boolean { return this.passive || this.isBoss(); } canApplyAbility(passive: boolean = false): boolean { - if (passive && !this.canApplyPassive()) + if (passive && !this.hasPassive()) return false; const ability = (!passive ? this.getAbility() : this.getPassiveAbility()); if (ability.isIgnorable && this.scene.arena.ignoreAbilities) @@ -743,6 +743,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return (this.hp || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this)); } + hasAbility(ability: Abilities, canApply: boolean = true, ignoreOverride?: boolean): boolean { + if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).id === ability) + return true; + if (this.hasPassive() && (!canApply || this.canApplyAbility(true)) && this.getPassiveAbility().id === ability) + return true; + return false; + } + + hasAbilityWithAttr(attrType: { new(...args: any[]): AbAttr }, canApply: boolean = true, ignoreOverride?: boolean): boolean { + if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).hasAttr(attrType)) + return true; + if (this.hasPassive() && (!canApply || this.canApplyAbility(true)) && this.getPassiveAbility().hasAttr(attrType)) + return true; + return false; + } + getWeight(): number { const weight = new Utils.NumberHolder(this.species.weight); // This will trigger the ability overlay so only call this function when necessary diff --git a/src/phases.ts b/src/phases.ts index ff2f62c3eb9..58490e24cc8 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2190,7 +2190,7 @@ export class MovePhase extends BattlePhase { for (let opponent of targetedOpponents) { if (this.move.ppUsed === this.move.getMove().pp) break; - if ((opponent.canApplyAbility() && opponent.getAbility().hasAttr(IncreasePpAbAttr)) || (opponent.canApplyAbility(true) && opponent.getPassiveAbility().hasAttr(IncreasePpAbAttr))) + if (opponent.hasAbilityWithAttr(IncreasePpAbAttr)) this.move.ppUsed = Math.min(this.move.ppUsed + 1, this.move.getMovePp()); } } From 6e59b4dd77e6e1451d80de8c002083032f0307e3 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Mon, 15 Apr 2024 10:09:51 -0400 Subject: [PATCH 49/51] Prevent saving on an outdated client --- package.json | 2 +- src/phases.ts | 10 ++++++ src/system/game-data.ts | 5 +++ src/ui/outdated-modal-ui-handler.ts | 47 +++++++++++++++++++++++++++++ src/ui/ui.ts | 10 ++++-- 5 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 src/ui/outdated-modal-ui-handler.ts diff --git a/package.json b/package.json index fd189fd4d89..0765a180c7e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pokemon-rogue-battle", "private": true, - "version": "1.0.0", + "version": "1.0.1", "type": "module", "scripts": { "start": "vite", diff --git a/src/phases.ts b/src/phases.ts index 58490e24cc8..f668278d273 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -379,6 +379,16 @@ export class UnavailablePhase extends Phase { } } +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); diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 78ed699e954..e267608e8f8 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -27,6 +27,7 @@ import { Moves } from "../data/enums/moves"; import { speciesEggMoves } from "../data/egg-moves"; import { allMoves } from "../data/move"; import { TrainerVariant } from "../field/trainer"; +import { OutdatedPhase, UnavailablePhase } from "#app/phases"; const saveKey = 'x0i2O7WRiANTqPmZ'; // Temporary; secure encryption is not yet necessary @@ -269,6 +270,10 @@ export class GameData { .then(error => { this.scene.ui.savingIcon.hide(); if (error) { + if (error.startsWith('client version out of date')) { + this.scene.clearPhaseQueue(); + this.scene.unshiftPhase(new OutdatedPhase(this.scene)); + } console.error(error); return resolve(false); } diff --git a/src/ui/outdated-modal-ui-handler.ts b/src/ui/outdated-modal-ui-handler.ts new file mode 100644 index 00000000000..53243c42bbe --- /dev/null +++ b/src/ui/outdated-modal-ui-handler.ts @@ -0,0 +1,47 @@ +import BattleScene from "../battle-scene"; +import { ModalConfig, ModalUiHandler } from "./modal-ui-handler"; +import { addTextObject, TextStyle } from "./text"; +import { Mode } from "./ui"; + +export default class OutdatedModalUiHandler extends ModalUiHandler { + constructor(scene: BattleScene, mode?: Mode) { + super(scene, mode); + } + + getModalTitle(): string { + return ''; + } + + getWidth(): number { + return 160; + } + + getHeight(): number { + return 64; + } + + getMargin(): [number, number, number, number] { + return [ 0, 0, 48, 0 ]; + } + + getButtonLabels(): string[] { + return [ ]; + } + + setup(): void { + super.setup(); + + const label = addTextObject(this.scene, this.getWidth() / 2, this.getHeight() / 2, 'Your client is currently outdated.\nPlease reload to update the game.\n\nIf this error persists, please clear your browser cache.', TextStyle.WINDOW, { fontSize: '48px', align: 'center' }); + label.setOrigin(0.5, 0.5); + + this.modalContainer.add(label); + } + + show(args: any[]): boolean { + const config: ModalConfig = { + buttonActions: [] + }; + + return super.show([ config ]); + } +} \ No newline at end of file diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 422a0ab3e10..82eead45eb4 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -34,6 +34,7 @@ import SaveSlotSelectUiHandler from './save-slot-select-ui-handler'; import TitleUiHandler from './title-ui-handler'; import SavingIconHandler from './saving-icon-handler'; import UnavailableModalUiHandler from './unavailable-modal-ui-handler'; +import OutdatedModalUiHandler from './outdated-modal-ui-handler'; export enum Mode { MESSAGE, @@ -63,7 +64,8 @@ export enum Mode { LOGIN_FORM, REGISTRATION_FORM, LOADING, - UNAVAILABLE + UNAVAILABLE, + OUTDATED }; const transitionModes = [ @@ -90,7 +92,8 @@ const noTransitionModes = [ Mode.LOGIN_FORM, Mode.REGISTRATION_FORM, Mode.LOADING, - Mode.UNAVAILABLE + Mode.UNAVAILABLE, + Mode.OUTDATED ]; export default class UI extends Phaser.GameObjects.Container { @@ -141,7 +144,8 @@ export default class UI extends Phaser.GameObjects.Container { new LoginFormUiHandler(scene), new RegistrationFormUiHandler(scene), new LoadingModalUiHandler(scene), - new UnavailableModalUiHandler(scene) + new UnavailableModalUiHandler(scene), + new OutdatedModalUiHandler(scene) ]; } From 398f2c5be067a903429401b40d191302b226155f Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Mon, 15 Apr 2024 10:35:57 -0400 Subject: [PATCH 50/51] Fix being able to overwrite data without a confirmation --- src/ui/save-slot-select-ui-handler.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index 054bb1ab9f9..21865045141 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -114,8 +114,10 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { ui.showText(null, 0); }, false, 0, 19, 2000); }); - } else + } else if (this.sessionSlots[this.cursor].hasData === false) saveAndCallback(); + else + return false; break; } success = true; @@ -210,7 +212,6 @@ class SessionSlot extends Phaser.GameObjects.Container { super(scene, 0, slotId * 56); this.slotId = slotId; - this.hasData = false; this.setup(); } @@ -282,6 +283,7 @@ class SessionSlot extends Phaser.GameObjects.Container { return new Promise(resolve => { this.scene.gameData.getSession(this.slotId).then(async sessionData => { if (!sessionData) { + this.hasData = false; this.loadingLabel.setText('Empty'); resolve(false); return; From 9d92ec2a72661e7e6b22826185b6b68637c70527 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Mon, 15 Apr 2024 10:45:40 -0400 Subject: [PATCH 51/51] Fix nature power using wrong syntax for switch case --- src/data/move.ts | 50 +++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index de749ba3ad8..db0c062e2e8 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -2830,44 +2830,44 @@ export class NaturePowerAttr extends OverrideMoveEffectAttr { case Biome.TOWN: moveId = Moves.TRI_ATTACK; break; - case Biome.PLAINS - || Biome.GRASS - || Biome.TALL_GRASS - || Biome.MEADOW - || Biome.FOREST - || Biome.JUNGLE: + case Biome.PLAINS: + case Biome.GRASS: + case Biome.TALL_GRASS: + case Biome.MEADOW: + case Biome.FOREST: + case Biome.JUNGLE: moveId = Moves.ENERGY_BALL; break; - case Biome.SEA - || Biome.SWAMP - || Biome.BEACH - || Biome.LAKE - || Biome.SEABED - || Biome.ISLAND: + case Biome.SEA: + case Biome.SWAMP: + case Biome.BEACH: + case Biome.LAKE: + case Biome.SEABED: + case Biome.ISLAND: moveId = Moves.HYDRO_PUMP; break; case Biome.MOUNTAIN: moveId = Moves.AIR_SLASH; break; - case Biome.BADLANDS - || Biome.DESERT - || Biome.WASTELAND - || Biome.CONSTRUCTION_SITE: + case Biome.BADLANDS: + case Biome.DESERT: + case Biome.WASTELAND: + case Biome.CONSTRUCTION_SITE: moveId = Moves.EARTH_POWER; break; case Biome.CAVE: moveId = Moves.POWER_GEM; break; - case Biome.ICE_CAVE - || Biome.SNOWY_FOREST: + case Biome.ICE_CAVE: + case Biome.SNOWY_FOREST: moveId = Moves.ICE_BEAM; break; case Biome.VOLCANO: moveId = Moves.FLAMETHROWER; break; - case Biome.GRAVEYARD - || Biome.RUINS - || Biome.TEMPLE: + case Biome.GRAVEYARD: + case Biome.RUINS: + case Biome.TEMPLE: moveId = Moves.SHADOW_BALL; break; case Biome.DOJO: @@ -2876,11 +2876,13 @@ export class NaturePowerAttr extends OverrideMoveEffectAttr { case Biome.FAIRY_CAVE: moveId = Moves.MOONBLAST; break; - case Biome.ABYSS - || Biome.SPACE - || Biome.END: + case Biome.ABYSS: + case Biome.SPACE: moveId = Moves.DARK_PULSE; break; + case Biome.END: + moveId = Moves.ETERNABEAM; + break; } break; case TerrainType.MISTY: