From 39c6c375f8a40d61707b5905ff5372ca9fda2ab6 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 22 Aug 2024 13:03:05 -0400 Subject: [PATCH] fix ME null checks and unit tests with beta update --- public/images/items.json | 7750 +++++++++-------- public/images/items.png | Bin 56828 -> 57799 bytes src/battle-scene.ts | 53 +- src/data/battle-anims.ts | 42 +- src/data/egg.ts | 4 +- .../encounters/a-trainers-test-encounter.ts | 4 +- .../encounters/absolute-avarice-encounter.ts | 26 +- .../an-offer-you-cant-refuse-encounter.ts | 2 +- .../encounters/berries-abound-encounter.ts | 14 +- .../encounters/clowning-around-encounter.ts | 22 +- .../encounters/dancing-lessons-encounter.ts | 14 +- .../encounters/delibirdy-encounter.ts | 28 +- .../department-store-sale-encounter.ts | 14 +- .../encounters/field-trip-encounter.ts | 12 +- .../encounters/fiery-fallout-encounter.ts | 22 +- .../encounters/fight-or-flight-encounter.ts | 8 +- .../mysterious-challengers-encounter.ts | 1 + .../encounters/mysterious-chest-encounter.ts | 2 +- .../encounters/part-timer-encounter.ts | 28 +- .../encounters/safari-zone-encounter.ts | 14 +- .../shady-vitamin-dealer-encounter.ts | 8 +- .../slumbering-snorlax-encounter.ts | 2 +- .../the-pokemon-salesman-encounter.ts | 12 +- .../encounters/the-strong-stuff-encounter.ts | 4 +- .../the-winstrate-challenge-encounter.ts | 54 +- .../encounters/training-session-encounter.ts | 78 +- .../encounters/trash-to-treasure-encounter.ts | 4 +- .../encounters/weird-dream-encounter.ts | 22 +- .../mystery-encounter-option.ts | 80 +- .../mystery-encounter-requirements.ts | 66 +- .../mystery-encounters/mystery-encounter.ts | 153 +- .../mystery-encounters/mystery-encounters.ts | 8 +- .../can-learn-move-requirement.ts | 6 +- .../utils/encounter-dialogue-utils.ts | 31 +- .../utils/encounter-phase-utils.ts | 110 +- .../utils/encounter-pokemon-utils.ts | 74 +- src/field/arena.ts | 2 +- src/field/mystery-encounter-intro.ts | 13 +- src/field/pokemon.ts | 2 +- .../mystery-encounters/delibirdy-dialogue.ts | 2 +- src/modifier/modifier-type.ts | 27 +- src/modifier/modifier.ts | 4 +- src/phases.ts | 53 +- src/phases/mystery-encounter-phases.ts | 53 +- .../mystery-encounter/encounterTestUtils.ts | 13 +- .../a-trainers-test-encounter.test.ts | 38 +- .../absolute-avarice-encounter.test.ts | 30 +- ...an-offer-you-cant-refuse-encounter.test.ts | 16 +- .../berries-abound-encounter.test.ts | 37 +- .../clowning-around-encounter.test.ts | 47 +- .../dancing-lessons-encounter.test.ts | 24 +- .../encounters/delibirdy-encounter.test.ts | 52 +- .../department-store-sale-encounter.test.ts | 16 +- .../fiery-fallout-encounter.test.ts | 28 +- .../fight-or-flight-encounter.test.ts | 28 +- .../encounters/lost-at-sea-encounter.test.ts | 32 +- .../mysterious-challengers-encounter.test.ts | 38 +- .../encounters/part-timer-encounter.test.ts | 22 +- .../the-pokemon-salesman-encounter.test.ts | 14 +- .../the-strong-stuff-encounter.test.ts | 31 +- .../the-winstrate-challenge-encounter.test.ts | 28 +- .../trash-to-treasure-encounter.test.ts | 26 +- .../encounters/weird-dream-encounter.test.ts | 16 +- .../mystery-encounter-utils.test.ts | 2 +- .../mystery-encounter.test.ts | 4 +- .../phases/mystery-encounter-phase.test.ts | 4 +- src/test/phases/select-modifier-phase.test.ts | 43 +- src/test/utils/gameManager.ts | 4 +- src/test/utils/gameManagerUtils.ts | 2 +- src/test/utils/helpers/overridesHelper.ts | 39 +- src/test/utils/mocks/mockGameObject.ts | 2 +- .../mocks/mocksContainer/mockContainer.ts | 2 +- .../mocks/mocksContainer/mockGraphics.ts | 1 + .../mocks/mocksContainer/mockRectangle.ts | 1 + .../utils/mocks/mocksContainer/mockSprite.ts | 1 + .../utils/mocks/mocksContainer/mockText.ts | 2 +- .../utils/mocks/mocksContainer/mockTexture.ts | 1 + src/test/utils/phaseInterceptor.ts | 2 +- src/ui/mystery-encounter-ui-handler.ts | 44 +- src/ui/text.ts | 2 +- 80 files changed, 4874 insertions(+), 4746 deletions(-) diff --git a/public/images/items.json b/public/images/items.json index c347790b92f..b318f79b0a0 100644 --- a/public/images/items.json +++ b/public/images/items.json @@ -4,8 +4,8 @@ "image": "items.png", "format": "RGBA8888", "size": { - "w": 425, - "h": 425 + "w": 428, + "h": 428 }, "scale": 1, "frames": [ @@ -387,6 +387,27 @@ "h": 28 } }, + { + "filename": "mega_bracelet", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 16 + }, + "frame": { + "x": 0, + "y": 412, + "w": 20, + "h": 16 + } + }, { "filename": "ability_charm", "rotated": false, @@ -619,7 +640,7 @@ } }, { - "filename": "elixir", + "filename": "catching_charm", "rotated": false, "trimmed": true, "sourceSize": { @@ -627,15 +648,15 @@ "h": 32 }, "spriteSourceSize": { - "x": 7, + "x": 5, "y": 4, - "w": 18, + "w": 21, "h": 24 }, "frame": { "x": 407, "y": 0, - "w": 18, + "w": 21, "h": 24 } }, @@ -892,7 +913,7 @@ } }, { - "filename": "coupon", + "filename": "exp_balance", "rotated": false, "trimmed": true, "sourceSize": { @@ -901,36 +922,15 @@ }, "spriteSourceSize": { "x": 4, - "y": 7, - "w": 23, - "h": 19 + "y": 5, + "w": 24, + "h": 22 }, "frame": { "x": 22, "y": 406, - "w": 23, - "h": 19 - } - }, - { - "filename": "golden_mystic_ticket", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 23, - "h": 19 - }, - "frame": { - "x": 45, - "y": 406, - "w": 23, - "h": 19 + "w": 24, + "h": 22 } }, { @@ -955,7 +955,7 @@ } }, { - "filename": "mega_bracelet", + "filename": "relic_band", "rotated": false, "trimmed": true, "sourceSize": { @@ -963,20 +963,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, + "x": 7, + "y": 9, + "w": 17, "h": 16 }, "frame": { "x": 28, "y": 70, - "w": 20, + "w": 17, "h": 16 } }, { - "filename": "calcium", + "filename": "abomasite", "rotated": false, "trimmed": true, "sourceSize": { @@ -985,57 +985,15 @@ }, "spriteSourceSize": { "x": 8, - "y": 4, + "y": 8, "w": 16, - "h": 24 + "h": 16 }, "frame": { - "x": 39, - "y": 86, + "x": 45, + "y": 70, "w": 16, - "h": 24 - } - }, - { - "filename": "carbos", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 39, - "y": 110, - "w": 16, - "h": 24 - } - }, - { - "filename": "catching_charm", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 21, - "h": 24 - }, - "frame": { - "x": 39, - "y": 134, - "w": 21, - "h": 24 + "h": 16 } }, { @@ -1054,7 +1012,7 @@ }, "frame": { "x": 39, - "y": 158, + "y": 86, "w": 24, "h": 24 } @@ -1075,7 +1033,7 @@ }, "frame": { "x": 39, - "y": 182, + "y": 110, "w": 24, "h": 24 } @@ -1094,6 +1052,69 @@ "w": 24, "h": 24 }, + "frame": { + "x": 39, + "y": 134, + "w": 24, + "h": 24 + } + }, + { + "filename": "golden_punch", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 39, + "y": 158, + "w": 24, + "h": 24 + } + }, + { + "filename": "gracidea", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 39, + "y": 182, + "w": 24, + "h": 24 + } + }, + { + "filename": "grip_claw", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, "frame": { "x": 44, "y": 206, @@ -1102,7 +1123,7 @@ } }, { - "filename": "golden_punch", + "filename": "icicle_plate", "rotated": false, "trimmed": true, "sourceSize": { @@ -1123,7 +1144,7 @@ } }, { - "filename": "gracidea", + "filename": "insect_plate", "rotated": false, "trimmed": true, "sourceSize": { @@ -1143,69 +1164,6 @@ "h": 24 } }, - { - "filename": "grip_claw", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 46, - "y": 278, - "w": 24, - "h": 24 - } - }, - { - "filename": "icicle_plate", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 46, - "y": 302, - "w": 24, - "h": 24 - } - }, - { - "filename": "insect_plate", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 46, - "y": 326, - "w": 24, - "h": 24 - } - }, { "filename": "iron_plate", "rotated": false, @@ -1222,7 +1180,7 @@ }, "frame": { "x": 46, - "y": 350, + "y": 278, "w": 24, "h": 24 } @@ -1243,137 +1201,11 @@ }, "frame": { "x": 46, - "y": 374, + "y": 302, "w": 24, "h": 24 } }, - { - "filename": "abomasite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 48, - "y": 70, - "w": 16, - "h": 16 - } - }, - { - "filename": "ether", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 55, - "y": 86, - "w": 18, - "h": 24 - } - }, - { - "filename": "full_restore", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 55, - "y": 110, - "w": 18, - "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": 60, - "y": 134, - "w": 16, - "h": 24 - } - }, - { - "filename": "iron", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 63, - "y": 158, - "w": 16, - "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": 63, - "y": 182, - "w": 23, - "h": 24 - } - }, { "filename": "lucky_punch_great", "rotated": false, @@ -1389,8 +1221,8 @@ "h": 24 }, "frame": { - "x": 68, - "y": 206, + "x": 46, + "y": 326, "w": 24, "h": 24 } @@ -1410,8 +1242,8 @@ "h": 24 }, "frame": { - "x": 68, - "y": 230, + "x": 46, + "y": 350, "w": 24, "h": 24 } @@ -1431,8 +1263,8 @@ "h": 24 }, "frame": { - "x": 69, - "y": 254, + "x": 46, + "y": 374, "w": 24, "h": 24 } @@ -1452,112 +1284,7 @@ "h": 24 }, "frame": { - "x": 70, - "y": 278, - "w": 24, - "h": 24 - } - }, - { - "filename": "meadow_plate", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 70, - "y": 302, - "w": 24, - "h": 24 - } - }, - { - "filename": "mind_plate", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 70, - "y": 326, - "w": 24, - "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": 70, - "y": 350, - "w": 24, - "h": 24 - } - }, - { - "filename": "pixie_plate", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 70, - "y": 374, - "w": 24, - "h": 24 - } - }, - { - "filename": "salac_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 24, - "h": 24 - }, - "frame": { - "x": 68, + "x": 46, "y": 398, "w": 24, "h": 24 @@ -1585,7 +1312,7 @@ } }, { - "filename": "lure", + "filename": "calcium", "rotated": false, "trimmed": true, "sourceSize": { @@ -1595,39 +1322,18 @@ "spriteSourceSize": { "x": 8, "y": 4, - "w": 17, - "h": 24 - }, - "frame": { - "x": 92, - "y": 398, - "w": 17, - "h": 24 - } - }, - { - "filename": "max_elixir", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, + "w": 16, "h": 24 }, "frame": { "x": 59, "y": 27, - "w": 18, + "w": 16, "h": 24 } }, { - "filename": "scanner", + "filename": "meadow_plate", "rotated": false, "trimmed": true, "sourceSize": { @@ -1641,14 +1347,14 @@ "h": 24 }, "frame": { - "x": 77, + "x": 75, "y": 26, "w": 24, "h": 24 } }, { - "filename": "silk_scarf", + "filename": "mind_plate", "rotated": false, "trimmed": true, "sourceSize": { @@ -1662,12 +1368,33 @@ "h": 24 }, "frame": { - "x": 101, + "x": 99, "y": 26, "w": 24, "h": 24 } }, + { + "filename": "revive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 10, + "y": 8, + "w": 12, + "h": 17 + }, + "frame": { + "x": 123, + "y": 26, + "w": 12, + "h": 17 + } + }, { "filename": "big_mushroom", "rotated": false, @@ -1732,7 +1459,7 @@ } }, { - "filename": "sky_plate", + "filename": "absolite", "rotated": false, "trimmed": true, "sourceSize": { @@ -1740,20 +1467,41 @@ "h": 32 }, "spriteSourceSize": { - "x": 4, + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 61, + "y": 70, + "w": 16, + "h": 16 + } + }, + { + "filename": "carbos", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, "y": 4, - "w": 24, + "w": 16, "h": 24 }, "frame": { - "x": 125, - "y": 36, - "w": 24, + "x": 63, + "y": 86, + "w": 16, "h": 24 } }, { - "filename": "choice_specs", + "filename": "elixir", "rotated": false, "trimmed": true, "sourceSize": { @@ -1761,16 +1509,100 @@ "h": 32 }, "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 24, - "h": 18 + "x": 7, + "y": 4, + "w": 18, + "h": 24 }, "frame": { - "x": 125, - "y": 60, - "w": 24, - "h": 18 + "x": 63, + "y": 110, + "w": 18, + "h": 24 + } + }, + { + "filename": "ether", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 18, + "h": 24 + }, + "frame": { + "x": 63, + "y": 134, + "w": 18, + "h": 24 + } + }, + { + "filename": "full_restore", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 4, + "w": 18, + "h": 24 + }, + "frame": { + "x": 63, + "y": 158, + "w": 18, + "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": 63, + "y": 182, + "w": 23, + "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": 68, + "y": 206, + "w": 18, + "h": 24 } }, { @@ -1788,14 +1620,140 @@ "h": 24 }, "frame": { - "x": 149, - "y": 36, + "x": 68, + "y": 230, "w": 18, "h": 24 } }, { - "filename": "adamant_crystal", + "filename": "lure", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 17, + "h": 24 + }, + "frame": { + "x": 69, + "y": 254, + "w": 17, + "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": 70, + "y": 278, + "w": 16, + "h": 24 + } + }, + { + "filename": "iron", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 70, + "y": 302, + "w": 16, + "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": 70, + "y": 326, + "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": 70, + "y": 350, + "w": 18, + "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": 70, + "y": 374, + "w": 22, + "h": 24 + } + }, + { + "filename": "muscle_band", "rotated": false, "trimmed": true, "sourceSize": { @@ -1804,15 +1762,183 @@ }, "spriteSourceSize": { "x": 4, - "y": 6, - "w": 23, - "h": 21 + "y": 4, + "w": 24, + "h": 24 }, "frame": { - "x": 149, - "y": 60, + "x": 70, + "y": 398, + "w": 24, + "h": 24 + } + }, + { + "filename": "pixie_plate", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 79, + "y": 73, + "w": 24, + "h": 24 + } + }, + { + "filename": "reveal_glass", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, "w": 23, - "h": 21 + "h": 24 + }, + "frame": { + "x": 103, + "y": 73, + "w": 23, + "h": 24 + } + }, + { + "filename": "salac_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 81, + "y": 97, + "w": 24, + "h": 24 + } + }, + { + "filename": "scanner", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 81, + "y": 121, + "w": 24, + "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": 81, + "y": 145, + "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": 105, + "y": 97, + "w": 21, + "h": 24 + } + }, + { + "filename": "shiny_charm", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 21, + "h": 24 + }, + "frame": { + "x": 105, + "y": 121, + "w": 21, + "h": 24 + } + }, + { + "filename": "sky_plate", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 4, + "w": 24, + "h": 24 + }, + "frame": { + "x": 105, + "y": 145, + "w": 24, + "h": 24 } }, { @@ -1830,8 +1956,8 @@ "h": 24 }, "frame": { - "x": 167, - "y": 21, + "x": 86, + "y": 169, "w": 24, "h": 24 } @@ -1851,8 +1977,8 @@ "h": 24 }, "frame": { - "x": 191, - "y": 21, + "x": 86, + "y": 193, "w": 24, "h": 24 } @@ -1872,8 +1998,8 @@ "h": 24 }, "frame": { - "x": 215, - "y": 21, + "x": 86, + "y": 217, "w": 24, "h": 24 } @@ -1893,8 +2019,8 @@ "h": 24 }, "frame": { - "x": 239, - "y": 21, + "x": 86, + "y": 241, "w": 24, "h": 24 } @@ -1914,8 +2040,8 @@ "h": 24 }, "frame": { - "x": 263, - "y": 21, + "x": 86, + "y": 265, "w": 24, "h": 24 } @@ -1935,14 +2061,14 @@ "h": 24 }, "frame": { - "x": 287, - "y": 21, + "x": 86, + "y": 289, "w": 24, "h": 24 } }, { - "filename": "silver_powder", + "filename": "red_orb", "rotated": false, "trimmed": true, "sourceSize": { @@ -1950,20 +2076,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 24, - "h": 15 + "x": 6, + "y": 4, + "w": 20, + "h": 24 }, "frame": { - "x": 167, - "y": 45, - "w": 24, - "h": 15 + "x": 110, + "y": 169, + "w": 20, + "h": 24 } }, { - "filename": "max_revive", + "filename": "black_belt", "rotated": false, "trimmed": true, "sourceSize": { @@ -1974,13 +2100,34 @@ "x": 5, "y": 4, "w": 22, - "h": 24 + "h": 23 }, "frame": { - "x": 311, - "y": 21, + "x": 110, + "y": 193, "w": 22, - "h": 24 + "h": 23 + } + }, + { + "filename": "bug_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 110, + "y": 216, + "w": 22, + "h": 23 } }, { @@ -1998,8 +2145,8 @@ "h": 23 }, "frame": { - "x": 333, - "y": 20, + "x": 110, + "y": 239, "w": 24, "h": 23 } @@ -2019,8 +2166,8 @@ "h": 23 }, "frame": { - "x": 357, - "y": 20, + "x": 110, + "y": 262, "w": 24, "h": 23 } @@ -2040,281 +2187,8 @@ "h": 23 }, "frame": { - "x": 381, - "y": 20, - "w": 24, - "h": 23 - } - }, - { - "filename": "red_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 20, - "h": 24 - }, - "frame": { - "x": 405, - "y": 24, - "w": 20, - "h": 24 - } - }, - { - "filename": "amulet_coin", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 23, - "h": 21 - }, - "frame": { - "x": 172, - "y": 60, - "w": 23, - "h": 21 - } - }, - { - "filename": "candy_overlay", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 12, - "w": 16, - "h": 15 - }, - "frame": { - "x": 191, - "y": 45, - "w": 16, - "h": 15 - } - }, - { - "filename": "dragon_scale", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 24, - "h": 18 - }, - "frame": { - "x": 207, - "y": 45, - "w": 24, - "h": 18 - } - }, - { - "filename": "exp_balance", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, - "h": 22 - }, - "frame": { - "x": 231, - "y": 45, - "w": 24, - "h": 22 - } - }, - { - "filename": "exp_share", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, - "h": 22 - }, - "frame": { - "x": 255, - "y": 45, - "w": 24, - "h": 22 - } - }, - { - "filename": "leppa_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, - "h": 23 - }, - "frame": { - "x": 279, - "y": 45, - "w": 24, - "h": 23 - } - }, - { - "filename": "scope_lens", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, - "h": 23 - }, - "frame": { - "x": 303, - "y": 45, - "w": 24, - "h": 23 - } - }, - { - "filename": "revive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 10, - "y": 8, - "w": 12, - "h": 17 - }, - "frame": { - "x": 195, - "y": 60, - "w": 12, - "h": 17 - } - }, - { - "filename": "icy_reins_of_unity", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 24, - "h": 20 - }, - "frame": { - "x": 207, - "y": 63, - "w": 24, - "h": 20 - } - }, - { - "filename": "metal_powder", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 6, - "w": 24, - "h": 20 - }, - "frame": { - "x": 231, - "y": 67, - "w": 24, - "h": 20 - } - }, - { - "filename": "peat_block", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, - "h": 22 - }, - "frame": { - "x": 255, - "y": 67, - "w": 24, - "h": 22 - } - }, - { - "filename": "twisted_spoon", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 24, - "h": 23 - }, - "frame": { - "x": 279, - "y": 68, + "x": 110, + "y": 285, "w": 24, "h": 23 } @@ -2334,77 +2208,14 @@ "h": 23 }, "frame": { - "x": 303, - "y": 68, + "x": 87, + "y": 313, "w": 23, "h": 23 } }, { - "filename": "black_belt", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 327, - "y": 45, - "w": 22, - "h": 23 - } - }, - { - "filename": "griseous_core", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 326, - "y": 68, - "w": 23, - "h": 23 - } - }, - { - "filename": "reveal_glass", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 4, - "w": 23, - "h": 24 - }, - "frame": { - "x": 349, - "y": 43, - "w": 23, - "h": 24 - } - }, - { - "filename": "leek", + "filename": "leppa_berry", "rotated": false, "trimmed": true, "sourceSize": { @@ -2414,144 +2225,18 @@ "spriteSourceSize": { "x": 4, "y": 5, - "w": 23, + "w": 24, "h": 23 }, "frame": { - "x": 372, - "y": 43, - "w": 23, + "x": 110, + "y": 308, + "w": 24, "h": 23 } }, { - "filename": "rare_candy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 349, - "y": 67, - "w": 23, - "h": 23 - } - }, - { - "filename": "rarer_candy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 23, - "h": 23 - }, - "frame": { - "x": 372, - "y": 66, - "w": 23, - "h": 23 - } - }, - { - "filename": "bug_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 395, - "y": 48, - "w": 22, - "h": 23 - } - }, - { - "filename": "auspicious_armor", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 23, - "h": 21 - }, - "frame": { - "x": 395, - "y": 71, - "w": 23, - "h": 21 - } - }, - { - "filename": "binding_band", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 23, - "h": 20 - }, - "frame": { - "x": 372, - "y": 89, - "w": 23, - "h": 20 - } - }, - { - "filename": "healing_charm", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 23, - "h": 22 - }, - "frame": { - "x": 349, - "y": 90, - "w": 23, - "h": 22 - } - }, - { - "filename": "black_glasses", + "filename": "choice_specs", "rotated": false, "trimmed": true, "sourceSize": { @@ -2561,14 +2246,35 @@ "spriteSourceSize": { "x": 4, "y": 8, - "w": 23, - "h": 17 + "w": 24, + "h": 18 }, "frame": { - "x": 395, - "y": 92, + "x": 135, + "y": 36, + "w": 24, + "h": 18 + } + }, + { + "filename": "coupon", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, "w": 23, - "h": 17 + "h": 19 + }, + "frame": { + "x": 125, + "y": 54, + "w": 23, + "h": 19 } }, { @@ -2586,7 +2292,7 @@ "h": 23 }, "frame": { - "x": 73, + "x": 126, "y": 73, "w": 22, "h": 23 @@ -2607,8 +2313,8 @@ "h": 23 }, "frame": { - "x": 95, - "y": 73, + "x": 126, + "y": 96, "w": 22, "h": 23 } @@ -2628,12 +2334,33 @@ "h": 23 }, "frame": { - "x": 73, - "y": 96, + "x": 126, + "y": 119, "w": 22, "h": 23 } }, + { + "filename": "dragon_fang", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 21, + "h": 23 + }, + "frame": { + "x": 129, + "y": 142, + "w": 21, + "h": 23 + } + }, { "filename": "fairy_tera_shard", "rotated": false, @@ -2649,8 +2376,8 @@ "h": 23 }, "frame": { - "x": 95, - "y": 96, + "x": 130, + "y": 165, "w": 22, "h": 23 } @@ -2670,33 +2397,12 @@ "h": 23 }, "frame": { - "x": 117, - "y": 78, + "x": 132, + "y": 188, "w": 22, "h": 23 } }, - { - "filename": "blank_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 117, - "y": 101, - "w": 22, - "h": 22 - } - }, { "filename": "fire_stone", "rotated": false, @@ -2712,8 +2418,8 @@ "h": 23 }, "frame": { - "x": 139, - "y": 81, + "x": 132, + "y": 211, "w": 22, "h": 23 } @@ -2733,180 +2439,12 @@ "h": 23 }, "frame": { - "x": 161, - "y": 81, + "x": 134, + "y": 234, "w": 22, "h": 23 } }, - { - "filename": "quick_powder", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 6, - "w": 24, - "h": 20 - }, - "frame": { - "x": 139, - "y": 104, - "w": 24, - "h": 20 - } - }, - { - "filename": "big_nugget", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 163, - "y": 104, - "w": 20, - "h": 20 - } - }, - { - "filename": "max_lure", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 17, - "h": 24 - }, - "frame": { - "x": 183, - "y": 81, - "w": 17, - "h": 24 - } - }, - { - "filename": "rusted_sword", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 23, - "h": 22 - }, - "frame": { - "x": 200, - "y": 83, - "w": 23, - "h": 22 - } - }, - { - "filename": "rusted_shield", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 6, - "w": 24, - "h": 20 - }, - "frame": { - "x": 183, - "y": 105, - "w": 24, - "h": 20 - } - }, - { - "filename": "apicot_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 20 - }, - "frame": { - "x": 207, - "y": 105, - "w": 19, - "h": 20 - } - }, - { - "filename": "relic_crown", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 23, - "h": 18 - }, - "frame": { - "x": 223, - "y": 87, - "w": 23, - "h": 18 - } - }, - { - "filename": "blue_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 226, - "y": 105, - "w": 20, - "h": 20 - } - }, { "filename": "flying_tera_shard", "rotated": false, @@ -2922,33 +2460,12 @@ "h": 23 }, "frame": { - "x": 246, - "y": 89, + "x": 134, + "y": 257, "w": 22, "h": 23 } }, - { - "filename": "blunder_policy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 19 - }, - "frame": { - "x": 246, - "y": 112, - "w": 22, - "h": 19 - } - }, { "filename": "focus_sash", "rotated": false, @@ -2964,8 +2481,8 @@ "h": 23 }, "frame": { - "x": 268, - "y": 91, + "x": 134, + "y": 280, "w": 22, "h": 23 } @@ -2985,159 +2502,12 @@ "h": 23 }, "frame": { - "x": 290, - "y": 91, + "x": 134, + "y": 303, "w": 22, "h": 23 } }, - { - "filename": "grass_tera_shard", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 312, - "y": 91, - "w": 22, - "h": 23 - } - }, - { - "filename": "full_heal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 9, - "y": 4, - "w": 15, - "h": 23 - }, - "frame": { - "x": 334, - "y": 91, - "w": 15, - "h": 23 - } - }, - { - "filename": "sacred_ash", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 24, - "h": 20 - }, - "frame": { - "x": 268, - "y": 114, - "w": 24, - "h": 20 - } - }, - { - "filename": "shadow_reins_of_unity", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 24, - "h": 20 - }, - "frame": { - "x": 292, - "y": 114, - "w": 24, - "h": 20 - } - }, - { - "filename": "soft_sand", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 24, - "h": 20 - }, - "frame": { - "x": 316, - "y": 114, - "w": 24, - "h": 20 - } - }, - { - "filename": "eviolite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 15, - "h": 15 - }, - "frame": { - "x": 73, - "y": 119, - "w": 15, - "h": 15 - } - }, - { - "filename": "max_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 4, - "w": 18, - "h": 24 - }, - "frame": { - "x": 76, - "y": 134, - "w": 18, - "h": 24 - } - }, { "filename": "max_repel", "rotated": false, @@ -3153,33 +2523,12 @@ "h": 24 }, "frame": { - "x": 79, - "y": 158, + "x": 148, + "y": 54, "w": 16, "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": 86, - "y": 182, - "w": 21, - "h": 24 - } - }, { "filename": "pp_max", "rotated": false, @@ -3195,8 +2544,8 @@ "h": 24 }, "frame": { - "x": 92, - "y": 206, + "x": 148, + "y": 78, "w": 16, "h": 24 } @@ -3216,14 +2565,14 @@ "h": 24 }, "frame": { - "x": 92, - "y": 230, + "x": 148, + "y": 102, "w": 16, "h": 24 } }, { - "filename": "protein", + "filename": "aerodactylite", "rotated": false, "trimmed": true, "sourceSize": { @@ -3232,19 +2581,19 @@ }, "spriteSourceSize": { "x": 8, - "y": 4, + "y": 8, "w": 16, - "h": 24 + "h": 16 }, "frame": { - "x": 93, - "y": 254, + "x": 148, + "y": 126, "w": 16, - "h": 24 + "h": 16 } }, { - "filename": "repel", + "filename": "full_heal", "rotated": false, "trimmed": true, "sourceSize": { @@ -3252,20 +2601,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 8, + "x": 9, "y": 4, - "w": 16, - "h": 24 + "w": 15, + "h": 23 }, "frame": { - "x": 94, - "y": 278, - "w": 16, - "h": 24 + "x": 150, + "y": 142, + "w": 15, + "h": 23 } }, { - "filename": "shiny_charm", + "filename": "grass_tera_shard", "rotated": false, "trimmed": true, "sourceSize": { @@ -3275,18 +2624,18 @@ "spriteSourceSize": { "x": 6, "y": 4, - "w": 21, - "h": 24 + "w": 22, + "h": 23 }, "frame": { - "x": 94, - "y": 302, - "w": 21, - "h": 24 + "x": 152, + "y": 165, + "w": 22, + "h": 23 } }, { - "filename": "dragon_fang", + "filename": "griseous_core", "rotated": false, "trimmed": true, "sourceSize": { @@ -3296,13 +2645,34 @@ "spriteSourceSize": { "x": 5, "y": 5, - "w": 21, + "w": 23, "h": 23 }, "frame": { - "x": 94, - "y": 326, - "w": 21, + "x": 154, + "y": 188, + "w": 23, + "h": 23 + } + }, + { + "filename": "leek", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 23, + "h": 23 + }, + "frame": { + "x": 154, + "y": 211, + "w": 23, "h": 23 } }, @@ -3321,8 +2691,8 @@ "h": 23 }, "frame": { - "x": 94, - "y": 349, + "x": 156, + "y": 234, "w": 22, "h": 23 } @@ -3342,14 +2712,14 @@ "h": 23 }, "frame": { - "x": 94, - "y": 372, + "x": 156, + "y": 257, "w": 22, "h": 23 } }, { - "filename": "prism_scale", + "filename": "macho_brace", "rotated": false, "trimmed": true, "sourceSize": { @@ -3357,83 +2727,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 9, - "y": 8, - "w": 15, - "h": 15 - }, - "frame": { - "x": 88, - "y": 119, - "w": 15, - "h": 15 - } - }, - { - "filename": "super_lure", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 17, - "h": 24 - }, - "frame": { - "x": 94, - "y": 134, - "w": 17, - "h": 24 - } - }, - { - "filename": "super_repel", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 95, - "y": 158, - "w": 16, - "h": 24 - } - }, - { - "filename": "berry_pot", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, + "x": 4, "y": 5, - "w": 18, - "h": 22 + "w": 23, + "h": 23 }, "frame": { - "x": 340, - "y": 114, - "w": 18, - "h": 22 + "x": 156, + "y": 280, + "w": 23, + "h": 23 } }, { - "filename": "unknown", + "filename": "rare_candy", "rotated": false, "trimmed": true, "sourceSize": { @@ -3441,16 +2748,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 + "x": 4, + "y": 5, + "w": 23, + "h": 23 }, "frame": { - "x": 358, - "y": 112, - "w": 16, - "h": 24 + "x": 156, + "y": 303, + "w": 23, + "h": 23 } }, { @@ -3468,14 +2775,14 @@ "h": 23 }, "frame": { - "x": 374, - "y": 109, + "x": 88, + "y": 336, "w": 22, "h": 23 } }, { - "filename": "normal_tera_shard", + "filename": "scope_lens", "rotated": false, "trimmed": true, "sourceSize": { @@ -3483,57 +2790,36 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 - }, - "frame": { - "x": 396, - "y": 109, - "w": 22, - "h": 23 - } - }, - { - "filename": "zinc", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 4, - "w": 16, - "h": 24 - }, - "frame": { - "x": 107, - "y": 182, - "w": 16, - "h": 24 - } - }, - { - "filename": "hyper_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, + "x": 4, "y": 5, - "w": 17, + "w": 24, "h": 23 }, "frame": { - "x": 108, - "y": 206, - "w": 17, + "x": 110, + "y": 331, + "w": 24, + "h": 23 + } + }, + { + "filename": "twisted_spoon", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 24, + "h": 23 + }, + "frame": { + "x": 134, + "y": 326, + "w": 24, "h": 23 } }, @@ -3552,12 +2838,33 @@ "h": 23 }, "frame": { - "x": 108, - "y": 229, + "x": 158, + "y": 326, "w": 21, "h": 23 } }, + { + "filename": "silver_powder", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 11, + "w": 24, + "h": 15 + }, + "frame": { + "x": 88, + "y": 359, + "w": 24, + "h": 15 + } + }, { "filename": "leaf_stone", "rotated": false, @@ -3573,8 +2880,8 @@ "h": 23 }, "frame": { - "x": 109, - "y": 252, + "x": 92, + "y": 374, "w": 21, "h": 23 } @@ -3594,12 +2901,54 @@ "h": 23 }, "frame": { - "x": 110, - "y": 275, + "x": 94, + "y": 397, "w": 20, "h": 23 } }, + { + "filename": "binding_band", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 23, + "h": 20 + }, + "frame": { + "x": 112, + "y": 354, + "w": 23, + "h": 20 + } + }, + { + "filename": "normal_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 113, + "y": 374, + "w": 22, + "h": 23 + } + }, { "filename": "petaya_berry", "rotated": false, @@ -3615,12 +2964,54 @@ "h": 23 }, "frame": { - "x": 115, - "y": 298, + "x": 114, + "y": 397, "w": 22, "h": 23 } }, + { + "filename": "exp_share", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 24, + "h": 22 + }, + "frame": { + "x": 135, + "y": 349, + "w": 24, + "h": 22 + } + }, + { + "filename": "peat_block", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 24, + "h": 22 + }, + "frame": { + "x": 135, + "y": 371, + "w": 24, + "h": 22 + } + }, { "filename": "poison_tera_shard", "rotated": false, @@ -3636,12 +3027,96 @@ "h": 23 }, "frame": { - "x": 115, - "y": 321, + "x": 159, + "y": 349, "w": 22, "h": 23 } }, + { + "filename": "adamant_crystal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 6, + "w": 23, + "h": 21 + }, + "frame": { + "x": 159, + "y": 372, + "w": 23, + "h": 21 + } + }, + { + "filename": "rarer_candy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 23, + "h": 23 + }, + "frame": { + "x": 136, + "y": 393, + "w": 23, + "h": 23 + } + }, + { + "filename": "healing_charm", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 23, + "h": 22 + }, + "frame": { + "x": 159, + "y": 393, + "w": 23, + "h": 22 + } + }, + { + "filename": "protein", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 159, + "y": 22, + "w": 16, + "h": 24 + } + }, { "filename": "psychic_tera_shard", "rotated": false, @@ -3657,8 +3132,8 @@ "h": 23 }, "frame": { - "x": 116, - "y": 344, + "x": 175, + "y": 21, "w": 22, "h": 23 } @@ -3678,8 +3153,8 @@ "h": 23 }, "frame": { - "x": 116, - "y": 367, + "x": 197, + "y": 21, "w": 22, "h": 23 } @@ -3699,14 +3174,35 @@ "h": 23 }, "frame": { - "x": 111, - "y": 123, + "x": 219, + "y": 21, "w": 22, "h": 23 } }, { - "filename": "steel_tera_shard", + "filename": "rusted_sword", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 23, + "h": 22 + }, + "frame": { + "x": 241, + "y": 21, + "w": 23, + "h": 22 + } + }, + { + "filename": "amulet_coin", "rotated": false, "trimmed": true, "sourceSize": { @@ -3715,19 +3211,19 @@ }, "spriteSourceSize": { "x": 6, - "y": 4, - "w": 22, - "h": 23 + "y": 5, + "w": 23, + "h": 21 }, "frame": { - "x": 111, - "y": 146, - "w": 22, - "h": 23 + "x": 264, + "y": 21, + "w": 23, + "h": 21 } }, { - "filename": "stellar_tera_shard", + "filename": "auspicious_armor", "rotated": false, "trimmed": true, "sourceSize": { @@ -3735,20 +3231,41 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 4, - "w": 22, - "h": 23 + "x": 4, + "y": 5, + "w": 23, + "h": 21 }, "frame": { - "x": 133, - "y": 124, - "w": 22, - "h": 23 + "x": 287, + "y": 21, + "w": 23, + "h": 21 } }, { - "filename": "water_tera_shard", + "filename": "berry_juice", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 24, + "h": 23 + }, + "spriteSourceSize": { + "x": 1, + "y": 1, + "w": 22, + "h": 21 + }, + "frame": { + "x": 310, + "y": 21, + "w": 22, + "h": 21 + } + }, + { + "filename": "blank_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -3756,16 +3273,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 4, + "x": 5, + "y": 5, "w": 22, - "h": 23 + "h": 22 }, "frame": { - "x": 155, - "y": 124, + "x": 332, + "y": 20, "w": 22, - "h": 23 + "h": 22 } }, { @@ -3783,8 +3300,8 @@ "h": 22 }, "frame": { - "x": 133, - "y": 147, + "x": 354, + "y": 20, "w": 22, "h": 22 } @@ -3804,12 +3321,159 @@ "h": 22 }, "frame": { - "x": 155, - "y": 147, + "x": 376, + "y": 20, "w": 22, "h": 22 } }, + { + "filename": "dragon_scale", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 24, + "h": 18 + }, + "frame": { + "x": 398, + "y": 24, + "w": 24, + "h": 18 + } + }, + { + "filename": "repel", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 164, + "y": 46, + "w": 16, + "h": 24 + } + }, + { + "filename": "steel_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 180, + "y": 44, + "w": 22, + "h": 23 + } + }, + { + "filename": "stellar_tera_shard", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 4, + "w": 22, + "h": 23 + }, + "frame": { + "x": 202, + "y": 44, + "w": 22, + "h": 23 + } + }, + { + "filename": "hyper_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 17, + "h": 23 + }, + "frame": { + "x": 224, + "y": 44, + "w": 17, + "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": 241, + "y": 43, + "w": 22, + "h": 23 + } + }, + { + "filename": "super_lure", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 17, + "h": 24 + }, + "frame": { + "x": 164, + "y": 70, + "w": 17, + "h": 24 + } + }, { "filename": "wide_lens", "rotated": false, @@ -3825,8 +3489,8 @@ "h": 23 }, "frame": { - "x": 177, - "y": 125, + "x": 181, + "y": 67, "w": 22, "h": 23 } @@ -3846,1247 +3510,8 @@ "h": 22 }, "frame": { - "x": 199, - "y": 125, - "w": 22, - "h": 22 - } - }, - { - "filename": "dire_hit", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 221, - "y": 125, - "w": 22, - "h": 22 - } - }, - { - "filename": "deep_sea_tooth", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 177, - "y": 148, - "w": 22, - "h": 21 - } - }, - { - "filename": "dna_splicers", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 199, - "y": 147, - "w": 22, - "h": 22 - } - }, - { - "filename": "dragon_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 221, - "y": 147, - "w": 22, - "h": 22 - } - }, - { - "filename": "electirizer", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 243, - "y": 131, - "w": 22, - "h": 22 - } - }, - { - "filename": "moon_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 6, - "w": 23, - "h": 21 - }, - "frame": { - "x": 265, - "y": 134, - "w": 23, - "h": 21 - } - }, - { - "filename": "n_lunarizer", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 6, - "w": 23, - "h": 21 - }, - "frame": { - "x": 288, - "y": 134, - "w": 23, - "h": 21 - } - }, - { - "filename": "n_solarizer", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 6, - "w": 23, - "h": 21 - }, - "frame": { - "x": 311, - "y": 134, - "w": 23, - "h": 21 - } - }, - { - "filename": "deep_sea_scale", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 20 - }, - "frame": { - "x": 243, - "y": 153, - "w": 22, - "h": 20 - } - }, - { - "filename": "mystic_ticket", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 23, - "h": 19 - }, - "frame": { - "x": 265, - "y": 155, - "w": 23, - "h": 19 - } - }, - { - "filename": "pair_of_tickets", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 23, - "h": 19 - }, - "frame": { - "x": 288, - "y": 155, - "w": 23, - "h": 19 - } - }, - { - "filename": "reviver_seed", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 8, - "w": 23, - "h": 20 - }, - "frame": { - "x": 311, - "y": 155, - "w": 23, - "h": 20 - } - }, - { - "filename": "electric_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 334, - "y": 136, - "w": 22, - "h": 22 - } - }, - { - "filename": "enigma_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 356, - "y": 136, - "w": 22, - "h": 22 - } - }, - { - "filename": "burn_drive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 23, - "h": 17 - }, - "frame": { - "x": 334, - "y": 158, - "w": 23, - "h": 17 - } - }, - { - "filename": "fairy_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 378, - "y": 132, - "w": 22, - "h": 22 - } - }, - { - "filename": "fighting_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 400, - "y": 132, - "w": 22, - "h": 22 - } - }, - { - "filename": "chill_drive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 23, - "h": 17 - }, - "frame": { - "x": 357, - "y": 158, - "w": 23, - "h": 17 - } - }, - { - "filename": "wellspring_mask", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 23, - "h": 21 - }, - "frame": { - "x": 380, - "y": 154, - "w": 23, - "h": 21 - } - }, - { - "filename": "fire_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 403, - "y": 154, - "w": 22, - "h": 22 - } - }, - { - "filename": "flying_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 123, - "y": 169, - "w": 22, - "h": 22 - } - }, - { - "filename": "ganlon_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 145, - "y": 169, - "w": 22, - "h": 22 - } - }, - { - "filename": "ghost_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 167, - "y": 169, - "w": 22, - "h": 22 - } - }, - { - "filename": "grass_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 189, - "y": 169, - "w": 22, - "h": 22 - } - }, - { - "filename": "ground_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 211, - "y": 169, - "w": 22, - "h": 22 - } - }, - { - "filename": "shell_bell", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 23, - "h": 20 - }, - "frame": { - "x": 233, - "y": 173, - "w": 23, - "h": 20 - } - }, - { - "filename": "dubious_disc", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 22, - "h": 19 - }, - "frame": { - "x": 256, - "y": 174, - "w": 22, - "h": 19 - } - }, - { - "filename": "fairy_feather", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 22, - "h": 20 - }, - "frame": { - "x": 278, - "y": 174, - "w": 22, - "h": 20 - } - }, - { - "filename": "guard_spec", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 300, - "y": 175, - "w": 22, - "h": 22 - } - }, - { - "filename": "ice_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 322, - "y": 175, - "w": 22, - "h": 22 - } - }, - { - "filename": "ice_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 344, - "y": 175, - "w": 22, - "h": 22 - } - }, - { - "filename": "magmarizer", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 366, - "y": 175, - "w": 22, - "h": 22 - } - }, - { - "filename": "leftovers", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 15, - "h": 22 - }, - "frame": { - "x": 388, - "y": 175, - "w": 15, - "h": 22 - } - }, - { - "filename": "mini_black_hole", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 403, - "y": 176, - "w": 22, - "h": 22 - } - }, - { - "filename": "poison_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 125, - "y": 191, - "w": 22, - "h": 22 - } - }, - { - "filename": "protector", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 147, - "y": 191, - "w": 22, - "h": 22 - } - }, - { - "filename": "psychic_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 169, - "y": 191, - "w": 22, - "h": 22 - } - }, - { - "filename": "rock_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 191, - "y": 191, - "w": 22, - "h": 22 - } - }, - { - "filename": "hard_meteorite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 20, - "h": 22 - }, - "frame": { - "x": 213, - "y": 191, - "w": 20, - "h": 22 - } - }, - { - "filename": "liechi_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 233, - "y": 193, - "w": 22, - "h": 21 - } - }, - { - "filename": "scroll_of_darkness", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 255, - "y": 193, - "w": 22, - "h": 22 - } - }, - { - "filename": "douse_drive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 23, - "h": 17 - }, - "frame": { - "x": 277, - "y": 194, - "w": 23, - "h": 17 - } - }, - { - "filename": "relic_band", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 9, - "w": 17, - "h": 16 - }, - "frame": { - "x": 125, - "y": 213, - "w": 17, - "h": 16 - } - }, - { - "filename": "shock_drive", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 23, - "h": 17 - }, - "frame": { - "x": 142, - "y": 213, - "w": 23, - "h": 17 - } - }, - { - "filename": "wise_glasses", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 23, - "h": 17 - }, - "frame": { - "x": 165, - "y": 213, - "w": 23, - "h": 17 - } - }, - { - "filename": "malicious_armor", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 20 - }, - "frame": { - "x": 188, - "y": 213, - "w": 22, - "h": 20 - } - }, - { - "filename": "scroll_of_waters", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 210, - "y": 213, - "w": 22, - "h": 22 - } - }, - { - "filename": "shed_shell", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 232, - "y": 214, - "w": 22, - "h": 22 - } - }, - { - "filename": "starf_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 254, - "y": 215, - "w": 22, - "h": 22 - } - }, - { - "filename": "steel_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 300, - "y": 197, - "w": 22, - "h": 22 - } - }, - { - "filename": "thick_club", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 322, - "y": 197, - "w": 22, - "h": 22 - } - }, - { - "filename": "thunder_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 344, - "y": 197, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_bug", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 366, - "y": 197, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_dark", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 129, - "y": 230, - "w": 22, - "h": 22 - } - }, - { - "filename": "sharp_beak", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 21, - "h": 23 - }, - "frame": { - "x": 130, - "y": 252, - "w": 21, - "h": 23 - } - }, - { - "filename": "whipped_dream", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 21, - "h": 23 - }, - "frame": { - "x": 130, - "y": 275, - "w": 21, - "h": 23 - } - }, - { - "filename": "tm_dragon", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 151, - "y": 230, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_electric", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 151, - "y": 252, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_fairy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 151, - "y": 274, + "x": 203, + "y": 67, "w": 22, "h": 22 } @@ -5106,12 +3531,54 @@ "h": 23 }, "frame": { - "x": 137, - "y": 298, + "x": 164, + "y": 94, "w": 17, "h": 23 } }, + { + "filename": "dire_hit", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 181, + "y": 90, + "w": 22, + "h": 22 + } + }, + { + "filename": "dna_splicers", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 203, + "y": 89, + "w": 22, + "h": 22 + } + }, { "filename": "sachet", "rotated": false, @@ -5127,12 +3594,138 @@ "h": 23 }, "frame": { - "x": 137, - "y": 321, + "x": 164, + "y": 117, "w": 18, "h": 23 } }, + { + "filename": "super_repel", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 225, + "y": 67, + "w": 16, + "h": 24 + } + }, + { + "filename": "dragon_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 241, + "y": 66, + "w": 22, + "h": 22 + } + }, + { + "filename": "sharp_beak", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 21, + "h": 23 + }, + "frame": { + "x": 182, + "y": 112, + "w": 21, + "h": 23 + } + }, + { + "filename": "electirizer", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 203, + "y": 111, + "w": 22, + "h": 22 + } + }, + { + "filename": "unknown", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 225, + "y": 91, + "w": 16, + "h": 24 + } + }, + { + "filename": "electric_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 241, + "y": 88, + "w": 22, + "h": 22 + } + }, { "filename": "super_potion", "rotated": false, @@ -5148,12 +3741,1188 @@ "h": 23 }, "frame": { - "x": 138, - "y": 344, + "x": 165, + "y": 140, "w": 17, "h": 23 } }, + { + "filename": "whipped_dream", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 4, + "w": 21, + "h": 23 + }, + "frame": { + "x": 182, + "y": 135, + "w": 21, + "h": 23 + } + }, + { + "filename": "enigma_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 203, + "y": 133, + "w": 22, + "h": 22 + } + }, + { + "filename": "zinc", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 4, + "w": 16, + "h": 24 + }, + "frame": { + "x": 225, + "y": 115, + "w": 16, + "h": 24 + } + }, + { + "filename": "fairy_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 241, + "y": 110, + "w": 22, + "h": 22 + } + }, + { + "filename": "aggronite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 225, + "y": 139, + "w": 16, + "h": 16 + } + }, + { + "filename": "fighting_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 241, + "y": 132, + "w": 22, + "h": 22 + } + }, + { + "filename": "berry_pot", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 5, + "w": 18, + "h": 22 + }, + "frame": { + "x": 174, + "y": 163, + "w": 18, + "h": 22 + } + }, + { + "filename": "fire_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 177, + "y": 185, + "w": 22, + "h": 22 + } + }, + { + "filename": "flying_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 177, + "y": 207, + "w": 22, + "h": 22 + } + }, + { + "filename": "ganlon_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 178, + "y": 229, + "w": 22, + "h": 22 + } + }, + { + "filename": "ghost_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 178, + "y": 251, + "w": 22, + "h": 22 + } + }, + { + "filename": "grass_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 179, + "y": 273, + "w": 22, + "h": 22 + } + }, + { + "filename": "ground_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 179, + "y": 295, + "w": 22, + "h": 22 + } + }, + { + "filename": "guard_spec", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 179, + "y": 317, + "w": 22, + "h": 22 + } + }, + { + "filename": "hard_meteorite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 5, + "w": 20, + "h": 22 + }, + "frame": { + "x": 181, + "y": 339, + "w": 20, + "h": 22 + } + }, + { + "filename": "ice_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 182, + "y": 361, + "w": 22, + "h": 22 + } + }, + { + "filename": "ice_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 182, + "y": 383, + "w": 22, + "h": 22 + } + }, + { + "filename": "black_glasses", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 23, + "h": 17 + }, + "frame": { + "x": 182, + "y": 405, + "w": 23, + "h": 17 + } + }, + { + "filename": "leftovers", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 15, + "h": 22 + }, + "frame": { + "x": 192, + "y": 158, + "w": 15, + "h": 22 + } + }, + { + "filename": "icy_reins_of_unity", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 24, + "h": 20 + }, + "frame": { + "x": 207, + "y": 155, + "w": 24, + "h": 20 + } + }, + { + "filename": "apicot_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 20 + }, + "frame": { + "x": 231, + "y": 155, + "w": 19, + "h": 20 + } + }, + { + "filename": "dawn_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 21 + }, + "frame": { + "x": 250, + "y": 154, + "w": 20, + "h": 21 + } + }, + { + "filename": "metal_powder", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 6, + "w": 24, + "h": 20 + }, + "frame": { + "x": 207, + "y": 175, + "w": 24, + "h": 20 + } + }, + { + "filename": "quick_powder", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 6, + "w": 24, + "h": 20 + }, + "frame": { + "x": 231, + "y": 175, + "w": 24, + "h": 20 + } + }, + { + "filename": "magmarizer", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 199, + "y": 195, + "w": 22, + "h": 22 + } + }, + { + "filename": "mini_black_hole", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 221, + "y": 195, + "w": 22, + "h": 22 + } + }, + { + "filename": "big_nugget", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 255, + "y": 175, + "w": 20, + "h": 20 + } + }, + { + "filename": "moon_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 6, + "w": 23, + "h": 21 + }, + "frame": { + "x": 243, + "y": 195, + "w": 23, + "h": 21 + } + }, + { + "filename": "n_lunarizer", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 6, + "w": 23, + "h": 21 + }, + "frame": { + "x": 200, + "y": 217, + "w": 23, + "h": 21 + } + }, + { + "filename": "n_solarizer", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 6, + "w": 23, + "h": 21 + }, + "frame": { + "x": 200, + "y": 238, + "w": 23, + "h": 21 + } + }, + { + "filename": "poison_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 223, + "y": 217, + "w": 22, + "h": 22 + } + }, + { + "filename": "deep_sea_scale", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 20 + }, + "frame": { + "x": 223, + "y": 239, + "w": 22, + "h": 20 + } + }, + { + "filename": "protector", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 245, + "y": 216, + "w": 22, + "h": 22 + } + }, + { + "filename": "deep_sea_tooth", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 245, + "y": 238, + "w": 22, + "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": 266, + "y": 195, + "w": 21, + "h": 21 + } + }, + { + "filename": "psychic_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 267, + "y": 216, + "w": 22, + "h": 22 + } + }, + { + "filename": "liechi_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 267, + "y": 238, + "w": 22, + "h": 21 + } + }, + { + "filename": "rock_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 201, + "y": 259, + "w": 22, + "h": 22 + } + }, + { + "filename": "rusted_shield", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 6, + "w": 24, + "h": 20 + }, + "frame": { + "x": 223, + "y": 259, + "w": 24, + "h": 20 + } + }, + { + "filename": "sacred_ash", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 24, + "h": 20 + }, + "frame": { + "x": 247, + "y": 259, + "w": 24, + "h": 20 + } + }, + { + "filename": "scroll_of_darkness", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 201, + "y": 281, + "w": 22, + "h": 22 + } + }, + { + "filename": "scroll_of_waters", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 223, + "y": 279, + "w": 22, + "h": 22 + } + }, + { + "filename": "shadow_reins_of_unity", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 24, + "h": 20 + }, + "frame": { + "x": 245, + "y": 279, + "w": 24, + "h": 20 + } + }, + { + "filename": "shed_shell", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 201, + "y": 303, + "w": 22, + "h": 22 + } + }, + { + "filename": "starf_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 223, + "y": 301, + "w": 22, + "h": 22 + } + }, + { + "filename": "soft_sand", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 24, + "h": 20 + }, + "frame": { + "x": 245, + "y": 299, + "w": 24, + "h": 20 + } + }, + { + "filename": "steel_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 201, + "y": 325, + "w": 22, + "h": 22 + } + }, + { + "filename": "thick_club", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 223, + "y": 323, + "w": 22, + "h": 22 + } + }, + { + "filename": "thunder_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 245, + "y": 319, + "w": 22, + "h": 22 + } + }, + { + "filename": "blue_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 271, + "y": 259, + "w": 20, + "h": 20 + } + }, + { + "filename": "tm_bug", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 269, + "y": 279, + "w": 22, + "h": 22 + } + }, + { + "filename": "black_sludge", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 24, + "h": 24 + }, + "spriteSourceSize": { + "x": 1, + "y": 2, + "w": 22, + "h": 19 + }, + "frame": { + "x": 269, + "y": 301, + "w": 22, + "h": 19 + } + }, + { + "filename": "wellspring_mask", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 5, + "w": 23, + "h": 21 + }, + "frame": { + "x": 267, + "y": 320, + "w": 23, + "h": 21 + } + }, + { + "filename": "burn_drive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 23, + "h": 17 + }, + "frame": { + "x": 245, + "y": 341, + "w": 23, + "h": 17 + } + }, + { + "filename": "blunder_policy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 19 + }, + "frame": { + "x": 268, + "y": 341, + "w": 22, + "h": 19 + } + }, + { + "filename": "dubious_disc", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 22, + "h": 19 + }, + "frame": { + "x": 223, + "y": 345, + "w": 22, + "h": 19 + } + }, { "filename": "lock_capsule", "rotated": false, @@ -5169,8 +4938,8 @@ "h": 22 }, "frame": { - "x": 154, - "y": 296, + "x": 204, + "y": 347, "w": 19, "h": 22 } @@ -5190,14 +4959,203 @@ "h": 22 }, "frame": { - "x": 138, - "y": 367, + "x": 204, + "y": 369, "w": 19, "h": 22 } }, { - "filename": "sitrus_berry", + "filename": "tm_dark", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 223, + "y": 364, + "w": 22, + "h": 22 + } + }, + { + "filename": "reviver_seed", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 8, + "w": 23, + "h": 20 + }, + "frame": { + "x": 245, + "y": 358, + "w": 23, + "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": 268, + "y": 360, + "w": 22, + "h": 20 + } + }, + { + "filename": "chill_drive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 23, + "h": 17 + }, + "frame": { + "x": 245, + "y": 378, + "w": 23, + "h": 17 + } + }, + { + "filename": "douse_drive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 23, + "h": 17 + }, + "frame": { + "x": 268, + "y": 380, + "w": 23, + "h": 17 + } + }, + { + "filename": "malicious_armor", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 20 + }, + "frame": { + "x": 223, + "y": 386, + "w": 22, + "h": 20 + } + }, + { + "filename": "tm_dragon", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 205, + "y": 406, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_electric", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 227, + "y": 406, + "w": 22, + "h": 22 + } + }, + { + "filename": "candy_overlay", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 12, + "w": 16, + "h": 15 + }, + "frame": { + "x": 205, + "y": 391, + "w": 16, + "h": 15 + } + }, + { + "filename": "quick_claw", "rotated": false, "trimmed": true, "sourceSize": { @@ -5206,14 +5164,98 @@ }, "spriteSourceSize": { "x": 6, + "y": 6, + "w": 19, + "h": 21 + }, + "frame": { + "x": 249, + "y": 395, + "w": 19, + "h": 21 + } + }, + { + "filename": "golden_mystic_ticket", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 23, + "h": 19 + }, + "frame": { + "x": 268, + "y": 397, + "w": 23, + "h": 19 + } + }, + { + "filename": "relic_gold", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 9, + "y": 11, + "w": 15, + "h": 11 + }, + "frame": { + "x": 249, + "y": 416, + "w": 15, + "h": 11 + } + }, + { + "filename": "mystic_ticket", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 23, + "h": 19 + }, + "frame": { + "x": 264, + "y": 42, + "w": 23, + "h": 19 + } + }, + { + "filename": "tm_fairy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, "y": 5, - "w": 20, + "w": 22, "h": 22 }, "frame": { - "x": 155, - "y": 318, - "w": 20, + "x": 263, + "y": 61, + "w": 22, "h": 22 } }, @@ -5232,8 +5274,8 @@ "h": 22 }, "frame": { - "x": 155, - "y": 340, + "x": 263, + "y": 83, "w": 22, "h": 22 } @@ -5253,75 +5295,12 @@ "h": 22 }, "frame": { - "x": 157, - "y": 362, + "x": 263, + "y": 105, "w": 22, "h": 22 } }, - { - "filename": "relic_gold", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 9, - "y": 11, - "w": 15, - "h": 11 - }, - "frame": { - "x": 173, - "y": 230, - "w": 15, - "h": 11 - } - }, - { - "filename": "metronome", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 17, - "h": 22 - }, - "frame": { - "x": 173, - "y": 241, - "w": 17, - "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": 173, - "y": 263, - "w": 17, - "h": 22 - } - }, { "filename": "tm_flying", "rotated": false, @@ -5337,14 +5316,14 @@ "h": 22 }, "frame": { - "x": 173, - "y": 285, + "x": 263, + "y": 127, "w": 22, "h": 22 } }, { - "filename": "dawn_stone", + "filename": "pair_of_tickets", "rotated": false, "trimmed": true, "sourceSize": { @@ -5352,121 +5331,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 21 + "x": 4, + "y": 7, + "w": 23, + "h": 19 }, "frame": { - "x": 190, - "y": 233, - "w": 20, - "h": 21 - } - }, - { - "filename": "sweet_apple", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 210, - "y": 235, - "w": 22, - "h": 21 - } - }, - { - "filename": "syrupy_apple", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 232, - "y": 236, - "w": 22, - "h": 21 - } - }, - { - "filename": "gb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 190, - "y": 254, - "w": 20, - "h": 20 - } - }, - { - "filename": "tart_apple", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 254, - "y": 237, - "w": 22, - "h": 21 - } - }, - { - "filename": "tera_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 20 - }, - "frame": { - "x": 210, - "y": 256, - "w": 22, - "h": 20 + "x": 287, + "y": 42, + "w": 23, + "h": 19 } }, { @@ -5484,8 +5358,8 @@ "h": 22 }, "frame": { - "x": 232, - "y": 257, + "x": 285, + "y": 61, "w": 22, "h": 22 } @@ -5505,8 +5379,8 @@ "h": 22 }, "frame": { - "x": 254, - "y": 258, + "x": 285, + "y": 83, "w": 22, "h": 22 } @@ -5526,8 +5400,8 @@ "h": 22 }, "frame": { - "x": 175, - "y": 307, + "x": 285, + "y": 105, "w": 22, "h": 22 } @@ -5547,12 +5421,96 @@ "h": 22 }, "frame": { - "x": 177, - "y": 329, + "x": 285, + "y": 127, "w": 22, "h": 22 } }, + { + "filename": "shell_bell", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 23, + "h": 20 + }, + "frame": { + "x": 310, + "y": 42, + "w": 23, + "h": 20 + } + }, + { + "filename": "sweet_apple", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 333, + "y": 42, + "w": 22, + "h": 21 + } + }, + { + "filename": "syrupy_apple", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 355, + "y": 42, + "w": 22, + "h": 21 + } + }, + { + "filename": "tart_apple", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 377, + "y": 42, + "w": 22, + "h": 21 + } + }, { "filename": "tm_normal", "rotated": false, @@ -5568,8 +5526,8 @@ "h": 22 }, "frame": { - "x": 179, - "y": 351, + "x": 399, + "y": 42, "w": 22, "h": 22 } @@ -5589,8 +5547,8 @@ "h": 22 }, "frame": { - "x": 179, - "y": 373, + "x": 307, + "y": 62, "w": 22, "h": 22 } @@ -5610,138 +5568,12 @@ "h": 22 }, "frame": { - "x": 157, - "y": 384, + "x": 307, + "y": 84, "w": 22, "h": 22 } }, - { - "filename": "upgrade", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 22, - "h": 19 - }, - "frame": { - "x": 109, - "y": 406, - "w": 22, - "h": 19 - } - }, - { - "filename": "metal_alloy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 21, - "h": 19 - }, - "frame": { - "x": 131, - "y": 406, - "w": 21, - "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": 152, - "y": 406, - "w": 20, - "h": 19 - } - }, - { - "filename": "power_herb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 20, - "h": 19 - }, - "frame": { - "x": 172, - "y": 406, - "w": 20, - "h": 19 - } - }, - { - "filename": "absolite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 116, - "y": 390, - "w": 16, - "h": 16 - } - }, - { - "filename": "aerodactylite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 132, - "y": 390, - "w": 16, - "h": 16 - } - }, { "filename": "tm_rock", "rotated": false, @@ -5757,8 +5589,8 @@ "h": 22 }, "frame": { - "x": 388, - "y": 198, + "x": 307, + "y": 106, "w": 22, "h": 22 } @@ -5778,8 +5610,8 @@ "h": 22 }, "frame": { - "x": 277, - "y": 211, + "x": 307, + "y": 128, "w": 22, "h": 22 } @@ -5799,8 +5631,8 @@ "h": 22 }, "frame": { - "x": 276, - "y": 233, + "x": 329, + "y": 63, "w": 22, "h": 22 } @@ -5820,8 +5652,8 @@ "h": 22 }, "frame": { - "x": 276, - "y": 255, + "x": 329, + "y": 85, "w": 22, "h": 22 } @@ -5841,8 +5673,8 @@ "h": 22 }, "frame": { - "x": 299, - "y": 219, + "x": 351, + "y": 63, "w": 22, "h": 22 } @@ -5862,8 +5694,8 @@ "h": 22 }, "frame": { - "x": 321, - "y": 219, + "x": 329, + "y": 107, "w": 22, "h": 22 } @@ -5883,8 +5715,8 @@ "h": 22 }, "frame": { - "x": 343, - "y": 219, + "x": 351, + "y": 85, "w": 22, "h": 22 } @@ -5904,8 +5736,8 @@ "h": 22 }, "frame": { - "x": 365, - "y": 219, + "x": 373, + "y": 63, "w": 22, "h": 22 } @@ -5925,8 +5757,8 @@ "h": 22 }, "frame": { - "x": 298, - "y": 241, + "x": 351, + "y": 107, "w": 22, "h": 22 } @@ -5946,8 +5778,8 @@ "h": 22 }, "frame": { - "x": 320, - "y": 241, + "x": 373, + "y": 85, "w": 22, "h": 22 } @@ -5967,33 +5799,12 @@ "h": 22 }, "frame": { - "x": 342, - "y": 241, + "x": 373, + "y": 107, "w": 22, "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": 364, - "y": 241, - "w": 21, - "h": 21 - } - }, { "filename": "poison_barb", "rotated": false, @@ -6009,33 +5820,12 @@ "h": 21 }, "frame": { - "x": 387, - "y": 220, + "x": 329, + "y": 129, "w": 21, "h": 21 } }, - { - "filename": "golden_egg", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 17, - "h": 20 - }, - "frame": { - "x": 408, - "y": 220, - "w": 17, - "h": 20 - } - }, { "filename": "shiny_stone", "rotated": false, @@ -6051,14 +5841,77 @@ "h": 21 }, "frame": { - "x": 385, - "y": 241, + "x": 350, + "y": 129, "w": 21, "h": 21 } }, { - "filename": "quick_claw", + "filename": "tera_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 20 + }, + "frame": { + "x": 371, + "y": 129, + "w": 22, + "h": 20 + } + }, + { + "filename": "sitrus_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 20, + "h": 22 + }, + "frame": { + "x": 395, + "y": 64, + "w": 20, + "h": 22 + } + }, + { + "filename": "zoom_lens", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 21, + "h": 21 + }, + "frame": { + "x": 395, + "y": 86, + "w": 21, + "h": 21 + } + }, + { + "filename": "gb", "rotated": false, "trimmed": true, "sourceSize": { @@ -6068,14 +5921,140 @@ "spriteSourceSize": { "x": 6, "y": 6, - "w": 19, - "h": 21 + "w": 20, + "h": 20 }, "frame": { - "x": 406, - "y": 241, - "w": 19, - "h": 21 + "x": 395, + "y": 107, + "w": 20, + "h": 20 + } + }, + { + "filename": "relic_crown", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 23, + "h": 18 + }, + "frame": { + "x": 270, + "y": 149, + "w": 23, + "h": 18 + } + }, + { + "filename": "metronome", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 5, + "w": 17, + "h": 22 + }, + "frame": { + "x": 275, + "y": 167, + "w": 17, + "h": 22 + } + }, + { + "filename": "shock_drive", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 23, + "h": 17 + }, + "frame": { + "x": 293, + "y": 150, + "w": 23, + "h": 17 + } + }, + { + "filename": "upgrade", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 22, + "h": 19 + }, + "frame": { + "x": 292, + "y": 167, + "w": 22, + "h": 19 + } + }, + { + "filename": "wise_glasses", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 8, + "w": 23, + "h": 17 + }, + "frame": { + "x": 316, + "y": 150, + "w": 23, + "h": 17 + } + }, + { + "filename": "metal_alloy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 21, + "h": 19 + }, + "frame": { + "x": 314, + "y": 167, + "w": 21, + "h": 19 } }, { @@ -6093,12 +6072,54 @@ "h": 18 }, "frame": { - "x": 298, - "y": 263, + "x": 339, + "y": 150, "w": 21, "h": 18 } }, + { + "filename": "old_gateau", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 21, + "h": 18 + }, + "frame": { + "x": 335, + "y": 168, + "w": 21, + "h": 18 + } + }, + { + "filename": "baton", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 360, + "y": 150, + "w": 18, + "h": 18 + } + }, { "filename": "sharp_meteorite", "rotated": false, @@ -6114,117 +6135,12 @@ "h": 18 }, "frame": { - "x": 319, - "y": 263, + "x": 356, + "y": 168, "w": 21, "h": 18 } }, - { - "filename": "unremarkable_teacup", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 21, - "h": 18 - }, - "frame": { - "x": 340, - "y": 263, - "w": 21, - "h": 18 - } - }, - { - "filename": "zoom_lens", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 21, - "h": 21 - }, - "frame": { - "x": 276, - "y": 277, - "w": 21, - "h": 21 - } - }, - { - "filename": "everstone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 17 - }, - "frame": { - "x": 297, - "y": 281, - "w": 20, - "h": 17 - } - }, - { - "filename": "magnet", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 317, - "y": 281, - "w": 20, - "h": 20 - } - }, - { - "filename": "mb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 337, - "y": 281, - "w": 20, - "h": 20 - } - }, { "filename": "candy_jar", "rotated": false, @@ -6240,14 +6156,77 @@ "h": 20 }, "frame": { - "x": 357, - "y": 281, + "x": 378, + "y": 149, "w": 19, "h": 20 } }, { - "filename": "baton", + "filename": "everstone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 17 + }, + "frame": { + "x": 377, + "y": 169, + "w": 20, + "h": 17 + } + }, + { + "filename": "golden_egg", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 17, + "h": 20 + }, + "frame": { + "x": 393, + "y": 129, + "w": 17, + "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": 410, + "y": 127, + "w": 18, + "h": 20 + } + }, + { + "filename": "oval_stone", "rotated": false, "trimmed": true, "sourceSize": { @@ -6258,15 +6237,99 @@ "x": 7, "y": 7, "w": 18, + "h": 19 + }, + "frame": { + "x": 410, + "y": 147, + "w": 18, + "h": 19 + } + }, + { + "filename": "magnet", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 397, + "y": 166, + "w": 20, + "h": 20 + } + }, + { + "filename": "unremarkable_teacup", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 21, "h": 18 }, "frame": { - "x": 361, - "y": 263, - "w": 18, + "x": 292, + "y": 186, + "w": 21, "h": 18 } }, + { + "filename": "lum_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 313, + "y": 186, + "w": 20, + "h": 19 + } + }, + { + "filename": "mb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 333, + "y": 186, + "w": 20, + "h": 20 + } + }, { "filename": "pb", "rotated": false, @@ -6282,8 +6345,8 @@ "h": 20 }, "frame": { - "x": 379, - "y": 262, + "x": 353, + "y": 186, "w": 20, "h": 20 } @@ -6303,33 +6366,12 @@ "h": 20 }, "frame": { - "x": 399, - "y": 262, + "x": 373, + "y": 186, "w": 20, "h": 20 } }, - { - "filename": "razor_claw", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 20, - "h": 19 - }, - "frame": { - "x": 376, - "y": 282, - "w": 20, - "h": 19 - } - }, { "filename": "rb", "rotated": false, @@ -6345,8 +6387,71 @@ "h": 20 }, "frame": { - "x": 396, - "y": 282, + "x": 393, + "y": 186, + "w": 20, + "h": 20 + } + }, + { + "filename": "eviolite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 15, + "h": 15 + }, + "frame": { + "x": 413, + "y": 186, + "w": 15, + "h": 15 + } + }, + { + "filename": "prism_scale", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 9, + "y": 8, + "w": 15, + "h": 15 + }, + "frame": { + "x": 413, + "y": 201, + "w": 15, + "h": 15 + } + }, + { + "filename": "smooth_meteorite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 289, + "y": 204, "w": 20, "h": 20 } @@ -6366,14 +6471,14 @@ "h": 21 }, "frame": { - "x": 192, - "y": 395, + "x": 289, + "y": 224, "w": 19, "h": 21 } }, { - "filename": "smooth_meteorite", + "filename": "power_herb", "rotated": false, "trimmed": true, "sourceSize": { @@ -6381,16 +6486,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 7, - "y": 6, + "x": 6, + "y": 7, "w": 20, - "h": 20 + "h": 19 }, "frame": { - "x": 211, - "y": 276, + "x": 309, + "y": 205, "w": 20, - "h": 20 + "h": 19 } }, { @@ -6408,12 +6513,33 @@ "h": 20 }, "frame": { - "x": 231, - "y": 279, + "x": 308, + "y": 224, "w": 20, "h": 20 } }, + { + "filename": "razor_claw", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 329, + "y": 206, + "w": 20, + "h": 19 + } + }, { "filename": "ub", "rotated": false, @@ -6429,14 +6555,14 @@ "h": 20 }, "frame": { - "x": 251, - "y": 280, + "x": 349, + "y": 206, "w": 20, "h": 20 } }, { - "filename": "mystery_egg", + "filename": "hard_stone", "rotated": false, "trimmed": true, "sourceSize": { @@ -6444,16 +6570,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 18 + "x": 6, + "y": 6, + "w": 19, + "h": 20 }, "frame": { - "x": 195, - "y": 276, - "w": 16, - "h": 18 + "x": 369, + "y": 206, + "w": 19, + "h": 20 } }, { @@ -6471,8 +6597,8 @@ "h": 19 }, "frame": { - "x": 211, - "y": 296, + "x": 388, + "y": 206, "w": 20, "h": 19 } @@ -6492,54 +6618,12 @@ "h": 18 }, "frame": { - "x": 231, - "y": 299, + "x": 408, + "y": 216, "w": 20, "h": 18 } }, - { - "filename": "wl_antidote", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 251, - "y": 300, - "w": 20, - "h": 18 - } - }, - { - "filename": "hard_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 20 - }, - "frame": { - "x": 271, - "y": 298, - "w": 19, - "h": 20 - } - }, { "filename": "miracle_seed", "rotated": false, @@ -6555,12 +6639,33 @@ "h": 19 }, "frame": { - "x": 290, - "y": 298, + "x": 328, + "y": 225, "w": 19, "h": 19 } }, + { + "filename": "wl_antidote", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 18 + }, + "frame": { + "x": 347, + "y": 226, + "w": 20, + "h": 18 + } + }, { "filename": "wl_awakening", "rotated": false, @@ -6576,8 +6681,8 @@ "h": 18 }, "frame": { - "x": 309, - "y": 301, + "x": 367, + "y": 226, "w": 20, "h": 18 } @@ -6597,8 +6702,8 @@ "h": 18 }, "frame": { - "x": 329, - "y": 301, + "x": 388, + "y": 225, "w": 20, "h": 18 } @@ -6618,12 +6723,33 @@ "h": 18 }, "frame": { - "x": 349, - "y": 301, + "x": 408, + "y": 234, "w": 20, "h": 18 } }, + { + "filename": "soothe_bell", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 17, + "h": 22 + }, + "frame": { + "x": 291, + "y": 245, + "w": 17, + "h": 22 + } + }, { "filename": "wl_custom_thief", "rotated": false, @@ -6639,8 +6765,8 @@ "h": 18 }, "frame": { - "x": 369, - "y": 301, + "x": 308, + "y": 244, "w": 20, "h": 18 } @@ -6660,54 +6786,12 @@ "h": 18 }, "frame": { - "x": 389, - "y": 302, + "x": 328, + "y": 244, "w": 20, "h": 18 } }, - { - "filename": "aggronite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 409, - "y": 302, - "w": 16, - "h": 16 - } - }, - { - "filename": "alakazite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 409, - "y": 318, - "w": 16, - "h": 16 - } - }, { "filename": "wl_ether", "rotated": false, @@ -6723,12 +6807,33 @@ "h": 18 }, "frame": { - "x": 211, - "y": 315, + "x": 348, + "y": 244, "w": 20, "h": 18 } }, + { + "filename": "lucky_egg", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 17, + "h": 20 + }, + "frame": { + "x": 291, + "y": 267, + "w": 17, + "h": 20 + } + }, { "filename": "wl_full_heal", "rotated": false, @@ -6744,8 +6849,8 @@ "h": 18 }, "frame": { - "x": 231, - "y": 317, + "x": 308, + "y": 262, "w": 20, "h": 18 } @@ -6765,8 +6870,8 @@ "h": 18 }, "frame": { - "x": 251, - "y": 318, + "x": 328, + "y": 262, "w": 20, "h": 18 } @@ -6786,33 +6891,12 @@ "h": 18 }, "frame": { - "x": 271, - "y": 318, + "x": 348, + "y": 262, "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": 291, - "y": 317, - "w": 18, - "h": 19 - } - }, { "filename": "wl_hyper_potion", "rotated": false, @@ -6828,8 +6912,8 @@ "h": 18 }, "frame": { - "x": 309, - "y": 319, + "x": 368, + "y": 244, "w": 20, "h": 18 } @@ -6849,8 +6933,8 @@ "h": 18 }, "frame": { - "x": 329, - "y": 319, + "x": 388, + "y": 243, "w": 20, "h": 18 } @@ -6870,8 +6954,8 @@ "h": 18 }, "frame": { - "x": 349, - "y": 319, + "x": 408, + "y": 252, "w": 20, "h": 18 } @@ -6891,8 +6975,8 @@ "h": 18 }, "frame": { - "x": 369, - "y": 319, + "x": 368, + "y": 262, "w": 20, "h": 18 } @@ -6912,33 +6996,12 @@ "h": 18 }, "frame": { - "x": 389, - "y": 320, + "x": 388, + "y": 261, "w": 20, "h": 18 } }, - { - "filename": "altarianite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 409, - "y": 334, - "w": 16, - "h": 16 - } - }, { "filename": "wl_max_ether", "rotated": false, @@ -6954,197 +7017,8 @@ "h": 18 }, "frame": { - "x": 199, - "y": 333, - "w": 20, - "h": 18 - } - }, - { - "filename": "razor_fang", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 18, - "h": 20 - }, - "frame": { - "x": 201, - "y": 351, - "w": 18, - "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": 201, - "y": 371, - "w": 17, - "h": 20 - } - }, - { - "filename": "wl_max_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 18 - }, - "frame": { - "x": 219, - "y": 335, - "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": 219, - "y": 353, - "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": 218, - "y": 371, - "w": 20, - "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": 239, - "y": 336, - "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": 259, - "y": 336, - "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": 279, - "y": 336, - "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": 239, - "y": 354, + "x": 408, + "y": 270, "w": 20, "h": 18 } @@ -7164,8 +7038,8 @@ "h": 18 }, "frame": { - "x": 259, - "y": 354, + "x": 291, + "y": 287, "w": 18, "h": 18 } @@ -7185,12 +7059,159 @@ "h": 18 }, "frame": { - "x": 277, - "y": 354, + "x": 291, + "y": 305, "w": 18, "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": 290, + "y": 323, + "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": 290, + "y": 341, + "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": 290, + "y": 359, + "w": 20, + "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": 291, + "y": 377, + "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": 291, + "y": 395, + "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": 309, + "y": 280, + "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": 309, + "y": 298, + "w": 20, + "h": 18 + } + }, { "filename": "flame_orb", "rotated": false, @@ -7206,8 +7227,8 @@ "h": 18 }, "frame": { - "x": 238, - "y": 372, + "x": 329, + "y": 280, "w": 18, "h": 18 } @@ -7227,8 +7248,8 @@ "h": 18 }, "frame": { - "x": 256, - "y": 372, + "x": 329, + "y": 298, "w": 18, "h": 18 } @@ -7248,8 +7269,8 @@ "h": 18 }, "frame": { - "x": 274, - "y": 372, + "x": 347, + "y": 280, "w": 18, "h": 18 } @@ -7269,12 +7290,75 @@ "h": 18 }, "frame": { - "x": 299, - "y": 337, + "x": 347, + "y": 298, "w": 18, "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": 365, + "y": 280, + "w": 16, + "h": 18 + } + }, + { + "filename": "alakazite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 365, + "y": 298, + "w": 16, + "h": 16 + } + }, + { + "filename": "altarianite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 310, + "y": 316, + "w": 16, + "h": 16 + } + }, { "filename": "ampharosite", "rotated": false, @@ -7290,8 +7374,8 @@ "h": 16 }, "frame": { - "x": 317, - "y": 337, + "x": 310, + "y": 332, "w": 16, "h": 16 } @@ -7311,8 +7395,8 @@ "h": 16 }, "frame": { - "x": 333, - "y": 337, + "x": 326, + "y": 316, "w": 16, "h": 16 } @@ -7332,8 +7416,8 @@ "h": 16 }, "frame": { - "x": 349, - "y": 337, + "x": 310, + "y": 348, "w": 16, "h": 16 } @@ -7353,8 +7437,8 @@ "h": 16 }, "frame": { - "x": 365, - "y": 337, + "x": 326, + "y": 332, "w": 16, "h": 16 } @@ -7374,8 +7458,8 @@ "h": 16 }, "frame": { - "x": 295, - "y": 355, + "x": 342, + "y": 316, "w": 16, "h": 16 } @@ -7395,8 +7479,8 @@ "h": 16 }, "frame": { - "x": 381, - "y": 338, + "x": 326, + "y": 348, "w": 16, "h": 16 } @@ -7416,8 +7500,8 @@ "h": 16 }, "frame": { - "x": 311, - "y": 355, + "x": 342, + "y": 332, "w": 16, "h": 16 } @@ -7437,8 +7521,8 @@ "h": 16 }, "frame": { - "x": 327, - "y": 353, + "x": 342, + "y": 348, "w": 16, "h": 16 } @@ -7458,8 +7542,8 @@ "h": 16 }, "frame": { - "x": 343, - "y": 353, + "x": 381, + "y": 280, "w": 16, "h": 16 } @@ -7479,8 +7563,8 @@ "h": 16 }, "frame": { - "x": 359, - "y": 353, + "x": 381, + "y": 296, "w": 16, "h": 16 } @@ -7500,8 +7584,8 @@ "h": 16 }, "frame": { - "x": 375, - "y": 354, + "x": 358, + "y": 316, "w": 16, "h": 16 } @@ -7521,8 +7605,8 @@ "h": 16 }, "frame": { - "x": 391, - "y": 354, + "x": 358, + "y": 332, "w": 16, "h": 16 } @@ -7542,8 +7626,8 @@ "h": 16 }, "frame": { - "x": 407, - "y": 350, + "x": 358, + "y": 348, "w": 16, "h": 16 } @@ -7563,8 +7647,8 @@ "h": 16 }, "frame": { - "x": 407, - "y": 366, + "x": 397, + "y": 288, "w": 16, "h": 16 } @@ -7584,8 +7668,8 @@ "h": 16 }, "frame": { - "x": 211, - "y": 391, + "x": 397, + "y": 304, "w": 16, "h": 16 } @@ -7605,8 +7689,8 @@ "h": 16 }, "frame": { - "x": 211, - "y": 407, + "x": 381, + "y": 312, "w": 16, "h": 16 } @@ -7626,8 +7710,8 @@ "h": 16 }, "frame": { - "x": 227, - "y": 390, + "x": 374, + "y": 328, "w": 16, "h": 16 } @@ -7647,8 +7731,8 @@ "h": 16 }, "frame": { - "x": 227, - "y": 406, + "x": 374, + "y": 344, "w": 16, "h": 16 } @@ -7668,8 +7752,8 @@ "h": 16 }, "frame": { - "x": 243, - "y": 390, + "x": 397, + "y": 320, "w": 16, "h": 16 } @@ -7689,8 +7773,8 @@ "h": 16 }, "frame": { - "x": 243, - "y": 406, + "x": 390, + "y": 336, "w": 16, "h": 16 } @@ -7710,8 +7794,8 @@ "h": 16 }, "frame": { - "x": 259, - "y": 390, + "x": 390, + "y": 352, "w": 16, "h": 16 } @@ -7731,8 +7815,8 @@ "h": 16 }, "frame": { - "x": 259, - "y": 406, + "x": 374, + "y": 360, "w": 16, "h": 16 } @@ -7752,8 +7836,8 @@ "h": 16 }, "frame": { - "x": 275, - "y": 390, + "x": 390, + "y": 368, "w": 16, "h": 16 } @@ -7773,8 +7857,8 @@ "h": 16 }, "frame": { - "x": 275, - "y": 406, + "x": 406, + "y": 336, "w": 16, "h": 16 } @@ -7794,8 +7878,8 @@ "h": 16 }, "frame": { - "x": 292, - "y": 372, + "x": 406, + "y": 352, "w": 16, "h": 16 } @@ -7815,8 +7899,8 @@ "h": 16 }, "frame": { - "x": 308, - "y": 371, + "x": 406, + "y": 368, "w": 16, "h": 16 } @@ -7836,8 +7920,8 @@ "h": 16 }, "frame": { - "x": 324, - "y": 371, + "x": 311, + "y": 364, "w": 16, "h": 16 } @@ -7857,8 +7941,8 @@ "h": 16 }, "frame": { - "x": 340, - "y": 369, + "x": 327, + "y": 364, "w": 16, "h": 16 } @@ -7878,8 +7962,8 @@ "h": 16 }, "frame": { - "x": 356, - "y": 369, + "x": 311, + "y": 380, "w": 16, "h": 16 } @@ -7899,8 +7983,8 @@ "h": 16 }, "frame": { - "x": 372, - "y": 370, + "x": 343, + "y": 364, "w": 16, "h": 16 } @@ -7920,8 +8004,8 @@ "h": 16 }, "frame": { - "x": 388, - "y": 370, + "x": 311, + "y": 396, "w": 16, "h": 16 } @@ -7941,8 +8025,8 @@ "h": 16 }, "frame": { - "x": 292, - "y": 388, + "x": 311, + "y": 412, "w": 16, "h": 16 } @@ -7962,8 +8046,8 @@ "h": 16 }, "frame": { - "x": 308, - "y": 387, + "x": 327, + "y": 380, "w": 16, "h": 16 } @@ -7983,8 +8067,8 @@ "h": 16 }, "frame": { - "x": 324, - "y": 387, + "x": 327, + "y": 396, "w": 16, "h": 16 } @@ -8004,8 +8088,8 @@ "h": 16 }, "frame": { - "x": 340, - "y": 385, + "x": 327, + "y": 412, "w": 16, "h": 16 } @@ -8025,8 +8109,8 @@ "h": 16 }, "frame": { - "x": 356, - "y": 385, + "x": 343, + "y": 380, "w": 16, "h": 16 } @@ -8046,8 +8130,8 @@ "h": 16 }, "frame": { - "x": 291, - "y": 404, + "x": 343, + "y": 396, "w": 16, "h": 16 } @@ -8067,8 +8151,8 @@ "h": 16 }, "frame": { - "x": 372, - "y": 386, + "x": 343, + "y": 412, "w": 16, "h": 16 } @@ -8088,8 +8172,8 @@ "h": 16 }, "frame": { - "x": 388, - "y": 386, + "x": 359, + "y": 376, "w": 16, "h": 16 } @@ -8109,8 +8193,8 @@ "h": 16 }, "frame": { - "x": 404, - "y": 382, + "x": 359, + "y": 392, "w": 16, "h": 16 } @@ -8130,8 +8214,8 @@ "h": 16 }, "frame": { - "x": 404, - "y": 398, + "x": 359, + "y": 408, "w": 16, "h": 16 } @@ -8151,8 +8235,8 @@ "h": 16 }, "frame": { - "x": 340, - "y": 401, + "x": 375, + "y": 384, "w": 16, "h": 16 } @@ -8172,8 +8256,8 @@ "h": 16 }, "frame": { - "x": 356, - "y": 401, + "x": 375, + "y": 400, "w": 16, "h": 16 } @@ -8193,8 +8277,8 @@ "h": 16 }, "frame": { - "x": 372, - "y": 402, + "x": 391, + "y": 384, "w": 16, "h": 16 } @@ -8214,8 +8298,8 @@ "h": 16 }, "frame": { - "x": 388, - "y": 402, + "x": 391, + "y": 400, "w": 16, "h": 16 } @@ -8226,6 +8310,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:4669e332ee400e355936594c14e7221c:1a1f5a801c94e8eb8589e13bc50105a1:110e074689c9edd2c54833ce2e4d9270$" + "smartupdate": "$TexturePacker:SmartUpdate:ae06b70c6800597c7ac11beefc1d8aad:b9f30512e12737247c8cb2691252baac:110e074689c9edd2c54833ce2e4d9270$" } } diff --git a/public/images/items.png b/public/images/items.png index 9de02d9e0e95643ae8f183cbaf08f9e55acbbe6c..a031b00ceb0af308f89835efab44d95fc6237b42 100644 GIT binary patch literal 57799 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⪙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 E1JgBQu>b%7 literal 56828 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 nRA*pg-6VRbq!a^DJKgGXoru- mt.newModifier(enemyPokemon).add(this.enemyModifiers, false, this)); } - if (isBoss) { - count = Math.max(count, Math.floor(chances / 2)); - } - getEnemyModifierTypesForWave(difficultyWaveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD, upgradeChance) - .map(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false, this)); - return true; }); this.updateModifiers(false).then(() => resolve()); @@ -2786,7 +2785,7 @@ export default class BattleScene extends SceneBase { } // Check for queued encounters first - if (!encounter && this.mysteryEncounterData?.nextEncounterQueue?.length > 0) { + if (!encounter && this.mysteryEncounterData?.nextEncounterQueue && this.mysteryEncounterData.nextEncounterQueue.length > 0) { let i = 0; while (i < this.mysteryEncounterData.nextEncounterQueue.length && !!encounter) { const candidate = this.mysteryEncounterData.nextEncounterQueue[i]; @@ -2801,7 +2800,7 @@ export default class BattleScene extends SceneBase { if (encounter) { encounter = new MysteryEncounter(encounter); - encounter.populateDialogueTokensFromRequirements!(this); + encounter.populateDialogueTokensFromRequirements(this); return encounter; } diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 4dbab6666f0..9f5b85801fe 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -440,7 +440,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent { scene.field.add(moveAnim.bgSprite); const fieldPokemon = scene.getEnemyPokemon() || scene.getPlayerPokemon(); if (!isNullOrUndefined(priority)) { - scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority); + scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority!); } else if (fieldPokemon?.isOnField()) { scene.field.moveBelow(moveAnim.bgSprite as Phaser.GameObjects.GameObject, fieldPokemon); } @@ -540,7 +540,7 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise { export async function initEncounterAnims(scene: BattleScene, encounterAnim: EncounterAnim | EncounterAnim[]): Promise { const anims = Array.isArray(encounterAnim) ? encounterAnim : [encounterAnim]; const encounterAnimNames = Utils.getEnumKeys(EncounterAnim); - const encounterAnimFetches = []; + const encounterAnimFetches: Promise>[] = []; for (const anim of anims) { if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) { continue; @@ -806,7 +806,7 @@ export abstract class BattleAnim { play(scene: BattleScene, callback?: Function) { const isOppAnim = this.isOppAnim(); const user = !isOppAnim ? this.user! : this.target!; // TODO: are those bangs correct? - const target = !isOppAnim ? this.target : this.user; + const target = !isOppAnim ? this.target! : this.user!; if (!target?.isOnField() && !this.playOnEmptyField) { if (callback) { @@ -1052,7 +1052,7 @@ export abstract class BattleAnim { y += targetInitialY; const angle = -frame.angle; const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++; - ret.get(frame.target).set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle }); + ret.get(frame.target)?.set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle }); } return ret; @@ -1102,17 +1102,17 @@ export abstract class BattleAnim { this.srcLine = [ userFocusX, userFocusY, targetFocusX, targetFocusY ]; this.dstLine = [ 150, 75, targetInitialX, targetInitialY ]; - let r = anim.frames.length; + let r = anim!.frames.length; let f = 0; const existingFieldSprites = [...scene.field.getAll()]; scene.tweens.addCounter({ duration: Utils.getFrameMs(3) * frameTimeMult, - repeat: anim.frames.length, + repeat: anim!.frames.length, onRepeat: () => { - const spriteFrames = anim.frames[f]; - const frameData = this.getGraphicFrameDataWithoutTarget(anim.frames[f], targetInitialX, targetInitialY); + const spriteFrames = anim!.frames[f]; + const frameData = this.getGraphicFrameDataWithoutTarget(anim!.frames[f], targetInitialX, targetInitialY); const u = 0; const t = 0; let g = 0; @@ -1124,7 +1124,7 @@ export abstract class BattleAnim { const sprites = spriteCache[AnimFrameTarget.GRAPHIC]; if (g === sprites.length) { - const newSprite: Phaser.GameObjects.Sprite = scene.addFieldSprite(0, 0, anim.graphic, 1); + const newSprite: Phaser.GameObjects.Sprite = scene.addFieldSprite(0, 0, anim!.graphic, 1); sprites.push(newSprite); scene.field.add(newSprite); spritePriorities.push(1); @@ -1147,17 +1147,19 @@ export abstract class BattleAnim { } moveSprite.setFrame(frame.graphicFrame); - const graphicFrameData = frameData.get(frame.target).get(graphicIndex); - moveSprite.setPosition(graphicFrameData.x, graphicFrameData.y); - moveSprite.setAngle(graphicFrameData.angle); - moveSprite.setScale(graphicFrameData.scaleX, graphicFrameData.scaleY); + const graphicFrameData = frameData.get(frame.target)?.get(graphicIndex); + if (graphicFrameData) { + moveSprite.setPosition(graphicFrameData.x, graphicFrameData.y); + moveSprite.setAngle(graphicFrameData.angle); + moveSprite.setScale(graphicFrameData.scaleX, graphicFrameData.scaleY); - moveSprite.setAlpha(frame.opacity / 255); - moveSprite.setVisible(frame.visible); - moveSprite.setBlendMode(frame.blendType === AnimBlendType.NORMAL ? Phaser.BlendModes.NORMAL : frame.blendType === AnimBlendType.ADD ? Phaser.BlendModes.ADD : Phaser.BlendModes.DIFFERENCE); + moveSprite.setAlpha(frame.opacity / 255); + moveSprite.setVisible(frame.visible); + moveSprite.setBlendMode(frame.blendType === AnimBlendType.NORMAL ? Phaser.BlendModes.NORMAL : frame.blendType === AnimBlendType.ADD ? Phaser.BlendModes.ADD : Phaser.BlendModes.DIFFERENCE); + } } - if (anim.frameTimedEvents.has(f)) { - for (const event of anim.frameTimedEvents.get(f)) { + if (anim?.frameTimedEvents.get(f)) { + for (const event of anim.frameTimedEvents.get(f)!) { r = Math.max((anim.frames.length - f) + event.execute(scene, this, frameTimedEventPriority), r); } } @@ -1277,8 +1279,8 @@ export class EncounterBattleAnim extends BattleAnim { this.oppAnim = oppAnim ?? false; } - getAnim(): AnimConfig { - return encounterAnims.get(this.encounterAnim); + getAnim(): AnimConfig | null { + return encounterAnims.get(this.encounterAnim) ?? null; } isOppAnim(): boolean { diff --git a/src/data/egg.ts b/src/data/egg.ts index 9f5e8cb1edf..1406ff9d869 100644 --- a/src/data/egg.ts +++ b/src/data/egg.ts @@ -86,7 +86,7 @@ export class Egg { private _overrideHiddenAbility: boolean; - private _eventEggTypeDescriptor: string; + private _eventEggTypeDescriptor?: string; //// // #endregion @@ -186,7 +186,7 @@ export class Egg { this.addEggToGameData(eggOptions.scene!); // TODO: is this bang correct? } - this._eventEggTypeDescriptor = eggOptions.eventEggTypeDescriptor ?? null; + this._eventEggTypeDescriptor = eggOptions?.eventEggTypeDescriptor; } //// diff --git a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts index d42d4051aa6..6cd0e8fe0ea 100644 --- a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts +++ b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts @@ -76,13 +76,13 @@ export const ATrainersTestEncounter: MysteryEncounter = text: `${namespace}.${trainerNameKey}.intro_dialogue` } ]; - encounter.options[0].dialogue.selected = [ + encounter.options[0].dialogue!.selected = [ { speaker: `trainerNames:${trainerNameKey}`, text: `${namespace}.${trainerNameKey}.accept` } ]; - encounter.options[1].dialogue.selected = [ + encounter.options[1].dialogue!.selected = [ { speaker: `trainerNames:${trainerNameKey}`, text: `${namespace}.${trainerNameKey}.decline` diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index 4cc3648beae..c8706d18d31 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -22,6 +22,7 @@ import { applyModifierTypeToPlayerPokemon, catchPokemon, getHighestLevelPlayerPo import { TrainerSlot } from "#app/data/trainer-config"; import { PokeballType } from "#app/data/pokeball"; import HeldModifierConfig from "#app/interfaces/held-modifier-config"; +import { BerryType } from "#enums/berry-type"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounter:absoluteAvarice"; @@ -39,8 +40,8 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = .withIntroSpriteConfigs([ { // This sprite has the shadow - spriteKey: null, - fileRoot: null, + spriteKey: "", + fileRoot: "", species: Species.GREEDENT, hasShadow: true, alpha: 0.001, @@ -48,8 +49,8 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = x: -5 }, { - spriteKey: null, - fileRoot: null, + spriteKey: "", + fileRoot: "", species: Species.GREEDENT, hasShadow: false, repeat: true, @@ -254,7 +255,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = queueEncounterMessage(scene, `${namespace}.option.1.food_stash`); }; - setEncounterRewards(scene, { fillRemaining: true }, null, givePartyPokemonReviverSeeds); + setEncounterRewards(scene, { fillRemaining: true }, undefined, givePartyPokemonReviverSeeds); encounter.startOfBattleEffects.push({ sourceBattlerIndex: BattlerIndex.ENEMY, targets: [BattlerIndex.ENEMY], @@ -287,7 +288,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = const party = scene.getParty(); party.forEach(pokemon => { const stolenBerries: BerryModifier[] = berryMap.get(pokemon.id); - const berryTypesAsArray = []; + const berryTypesAsArray: BerryType[] = []; stolenBerries?.forEach(bMod => berryTypesAsArray.push(...new Array(bMod.stackCount).fill(bMod.berryType))); const returnedBerryCount = Math.floor((berryTypesAsArray.length ?? 0) * 2 / 5); @@ -330,7 +331,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = // Let it have the food // Greedent joins the team, level equal to 2 below highest party member const level = getHighestLevelPlayerPokemon(scene).level - 2; - const greedent = new EnemyPokemon(scene, getPokemonSpecies(Species.GREEDENT), level, TrainerSlot.NONE, false, null); + const greedent = new EnemyPokemon(scene, getPokemonSpecies(Species.GREEDENT), level, TrainerSlot.NONE, false); greedent.moveset = [new PokemonMove(Moves.THRASH), new PokemonMove(Moves.BODY_PRESS), new PokemonMove(Moves.STUFF_CHEEKS), new PokemonMove(Moves.SLACK_OFF)]; greedent.passive = true; @@ -346,7 +347,7 @@ function doGreedentSpriteSteal(scene: BattleScene) { const shakeDelay = 50; const slideDelay = 500; - const greedentSprites = scene.currentBattle.mysteryEncounter.introVisuals.getSpriteAtIndex(1); + const greedentSprites = scene.currentBattle.mysteryEncounter.introVisuals?.getSpriteAtIndex(1); scene.playSound("Follow Me"); scene.tweens.chain({ @@ -420,7 +421,7 @@ function doGreedentSpriteSteal(scene: BattleScene) { } function doGreedentEatBerries(scene: BattleScene) { - const greedentSprites = scene.currentBattle.mysteryEncounter.introVisuals.getSpriteAtIndex(1); + const greedentSprites = scene.currentBattle.mysteryEncounter.introVisuals?.getSpriteAtIndex(1); let index = 1; scene.tweens.add({ targets: greedentSprites, @@ -455,7 +456,12 @@ function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) { const encounter = scene.currentBattle.mysteryEncounter; animationOrder.forEach((berry, i) => { const introVisualsIndex = encounter.spriteConfigs.findIndex(config => config.spriteKey?.includes(berry)); - const [ sprite, tintSprite ] = encounter.introVisuals.getSpriteAtIndex(introVisualsIndex); + let sprite: Phaser.GameObjects.Sprite, tintSprite: Phaser.GameObjects.Sprite; + const sprites = encounter.introVisuals?.getSpriteAtIndex(introVisualsIndex); + if (sprites) { + sprite = sprites[0]; + tintSprite = sprites[1]; + } scene.time.delayedCall(berryAddDelay * i + 400, () => { if (sprite) { sprite.setVisible(!isEat); diff --git a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts index 4e96b9f52d4..1ee1704c8b4 100644 --- a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts +++ b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts @@ -136,7 +136,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = // Update money and remove pokemon from party updatePlayerMoney(scene, encounter.misc.price); - setEncounterExp(scene, encounter.options[1].primaryPokemon.id, getPokemonSpecies(Species.LIEPARD).baseExp, true); + setEncounterExp(scene, encounter.options[1].primaryPokemon!.id, getPokemonSpecies(Species.LIEPARD).baseExp, true); leaveEncounterWithoutBattle(scene, true); }) diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index 6e75526076a..7e8279f383b 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -146,7 +146,7 @@ export const BerriesAboundEncounter: MysteryEncounter = // Calculate boss mon const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, scene.currentBattle.waveIndex, 0, getPartyLuckValue(scene.getParty()), true); - const bossPokemon = new EnemyPokemon(scene, bossSpecies, scene.currentBattle.waveIndex, TrainerSlot.NONE, true, null); + const bossPokemon = new EnemyPokemon(scene, bossSpecies, scene.currentBattle.waveIndex, TrainerSlot.NONE, true); encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon)); const config: EnemyPartyConfig = { levelAdditiveMultiplier: 1, @@ -222,7 +222,7 @@ export const BerriesAboundEncounter: MysteryEncounter = shopOptions.push(generateModifierTypeOption(scene, modifierTypes.BERRY)); } - setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, null, doBerryRewards); + setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards); await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]); } ) @@ -262,12 +262,12 @@ export const BerriesAboundEncounter: MysteryEncounter = }; const config = scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]; - config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON]; - config.pokemonConfigs[0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { + config.pokemonConfigs![0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON]; + config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { queueEncounterMessage(pokemon.scene, `${namespace}.option.2.boss_enraged`); pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1)); }; - setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, null, doBerryRewards); + setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards); await showEncounterText(scene, `${namespace}.option.2.selected_bad`); await initBattleWithEnemyConfig(scene, config); return; @@ -287,8 +287,8 @@ export const BerriesAboundEncounter: MysteryEncounter = } }; - setEncounterExp(scene, fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs[0].species.baseExp); - setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, null, doFasterBerryRewards); + setEncounterExp(scene, fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp); + setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doFasterBerryRewards); await showEncounterText(scene, `${namespace}.option.2.selected`); leaveEncounterWithoutBattle(scene); } diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index 85fbc2d2f18..c9c1f0dd335 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -1,4 +1,4 @@ -import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { trainerConfigs, TrainerPartyCompoundTemplate, TrainerPartyTemplate, } from "#app/data/trainer-config"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; @@ -28,7 +28,6 @@ import { BattlerIndex } from "#app/battle"; import { Moves } from "#enums/moves"; import { EncounterAnim, EncounterBattleAnim } from "#app/data/battle-anims"; import { MoveCategory } from "#app/data/move"; -import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:clowningAround"; @@ -129,7 +128,7 @@ export const ClowningAroundEncounter: MysteryEncounter = }, { // Blacephalon has the random ability from pool, and 2 entirely random types to fit with the theme of the encounter species: getPokemonSpecies(Species.BLACEPHALON), - mysteryEncounterData: new MysteryEncounterPokemonData(null, ability, null, [randSeedInt(18), randSeedInt(18)]), + mysteryEncounterData: new MysteryEncounterPokemonData(undefined, ability, undefined, [randSeedInt(18), randSeedInt(18)]), isBoss: true, moveSet: [Moves.TRICK, Moves.HYPNOSIS, Moves.SHADOW_BALL, Moves.MIND_BLOWN] }, @@ -206,7 +205,7 @@ export const ClowningAroundEncounter: MysteryEncounter = ease: "Sine.easeInOut", duration: 250 }); - const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon(), scene.getPlayerPokemon()); + const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); background.playWithoutTargets(scene, 230, 40, 2); return true; }) @@ -292,7 +291,7 @@ export const ClowningAroundEncounter: MysteryEncounter = }) .withPostOptionPhase(async (scene: BattleScene) => { // Play animations - const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon(), scene.getPlayerPokemon()); + const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); background.playWithoutTargets(scene, 230, 40, 2); await transitionMysteryEncounterIntroVisuals(scene); }) @@ -327,8 +326,8 @@ export const ClowningAroundEncounter: MysteryEncounter = // If the Pokemon has non-status moves that don't match the Pokemon's type, prioritizes those as the new type // Makes the "randomness" of the shuffle slightly less punishing let priorityTypes = pokemon.moveset - .filter(move => !originalTypes.includes(move.getMove().type) && move.getMove().category !== MoveCategory.STATUS) - .map(move => move.getMove().type); + .filter(move => move && !originalTypes.includes(move.getMove().type) && move.getMove().category !== MoveCategory.STATUS) + .map(move => move!.getMove().type); if (priorityTypes?.length > 0) { priorityTypes = [...new Set(priorityTypes)]; randSeedShuffle(priorityTypes); @@ -350,7 +349,7 @@ export const ClowningAroundEncounter: MysteryEncounter = } if (!pokemon.mysteryEncounterData) { - pokemon.mysteryEncounterData = new MysteryEncounterPokemonData(null, null, null, newTypes); + pokemon.mysteryEncounterData = new MysteryEncounterPokemonData(undefined, undefined, undefined, newTypes); } else { pokemon.mysteryEncounterData.types = newTypes; } @@ -361,7 +360,7 @@ export const ClowningAroundEncounter: MysteryEncounter = }) .withPostOptionPhase(async (scene: BattleScene) => { // Play animations - const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon(), scene.getPlayerPokemon()); + const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); background.playWithoutTargets(scene, 230, 40, 2); await transitionMysteryEncounterIntroVisuals(scene); }) @@ -441,8 +440,7 @@ function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItem [modifierTypes.GOLDEN_PUNCH, 5], [modifierTypes.ATTACK_TYPE_BOOSTER, 99], [modifierTypes.QUICK_CLAW, 3], - [modifierTypes.WIDE_LENS, 3], - [modifierTypes.WHITE_HERB, 2] + [modifierTypes.WIDE_LENS, 3] ]; const roguePool = [ @@ -450,7 +448,7 @@ function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItem [modifierTypes.SHELL_BELL, 4], [modifierTypes.SOUL_DEW, 10], [modifierTypes.SOOTHE_BELL, 3], - [modifierTypes.SCOPE_LENS, 5], + [modifierTypes.SCOPE_LENS, 1], [modifierTypes.BATON, 1], [modifierTypes.FOCUS_BAND, 5], [modifierTypes.KINGS_ROCK, 3], diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index ede6ecf5462..35b52c15c5a 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -87,7 +87,7 @@ export const DancingLessonsEncounter: MysteryEncounter = .withAutoHideIntroVisuals(false) .withCatchAllowed(true) .withOnVisualsStart((scene: BattleScene) => { - const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon(), scene.getPlayerPokemon()); + const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getPlayerPokemon()); danceAnim.play(scene); return true; @@ -104,8 +104,8 @@ export const DancingLessonsEncounter: MysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter; const species = getPokemonSpecies(Species.ORICORIO); - const enemyPokemon = scene.addEnemyPokemon(species, scene.currentBattle.enemyLevels[0], TrainerSlot.NONE, false); - if (!enemyPokemon.moveset.some(m => m.getMove().id === Moves.REVELATION_DANCE)) { + const enemyPokemon = scene.addEnemyPokemon(species, scene.currentBattle.enemyLevels![0], TrainerSlot.NONE, false); + if (!enemyPokemon.moveset.some(m => m && m.getMove().id === Moves.REVELATION_DANCE)) { if (enemyPokemon.moveset.length < 4) { enemyPokemon.moveset.push(new PokemonMove(Moves.REVELATION_DANCE)); } else { @@ -206,7 +206,7 @@ export const DancingLessonsEncounter: MysteryEncounter = scene.unshiftPhase(new LearnMovePhase(scene, scene.getParty().indexOf(pokemon), Moves.REVELATION_DANCE)); // Play animation again to "learn" the dance - const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon(), scene.getPlayerPokemon()); + const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getPlayerPokemon()); danceAnim.play(scene); }; @@ -239,7 +239,7 @@ export const DancingLessonsEncounter: MysteryEncounter = const onPokemonSelected = (pokemon: PlayerPokemon) => { // Return the options for nature selection return pokemon.moveset - .filter(move => DANCING_MOVES.includes(move.getMove().id)) + .filter(move => move && DANCING_MOVES.includes(move.getMove().id)) .map((move: PokemonMove) => { const option: OptionSelectItem = { label: move.getName(), @@ -261,13 +261,13 @@ export const DancingLessonsEncounter: MysteryEncounter = // If pokemon meets primary pokemon reqs, it can be selected const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon); if (!meetsReqs) { - return getEncounterText(scene, `${namespace}.invalid_selection`); + return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null; } return null; }; - return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); + return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); }) .withOptionPhase(async (scene: BattleScene) => { // Show the Oricorio a dance, and recruit it diff --git a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts index 336d95bd156..bcb742f01d0 100644 --- a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts +++ b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts @@ -47,8 +47,8 @@ export const DelibirdyEncounter: MysteryEncounter = )) .withIntroSpriteConfigs([ { - spriteKey: null, - fileRoot: null, + spriteKey: "", + fileRoot: "", species: Species.DELIBIRD, hasShadow: true, repeat: true, @@ -56,16 +56,16 @@ export const DelibirdyEncounter: MysteryEncounter = scale: 0.94 }, { - spriteKey: null, - fileRoot: null, + spriteKey: "", + fileRoot: "", species: Species.DELIBIRD, hasShadow: true, repeat: true, scale: 1.06 }, { - spriteKey: null, - fileRoot: null, + spriteKey: "", + fileRoot: "", species: Species.DELIBIRD, hasShadow: true, repeat: true, @@ -116,7 +116,7 @@ export const DelibirdyEncounter: MysteryEncounter = const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType; await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell); scene.playSound("item_fanfare"); - await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, true); + await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), undefined, true); } else { scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ABILITY_CHARM)); } @@ -169,13 +169,13 @@ export const DelibirdyEncounter: MysteryEncounter = // If pokemon meets primary pokemon reqs, it can be selected const meetsReqs = encounter.options[1].pokemonMeetsPrimaryRequirements(scene, pokemon); if (!meetsReqs) { - return getEncounterText(scene, `${namespace}.invalid_selection`); + return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null; } return null; }; - return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); + return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); }) .withOptionPhase(async (scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter; @@ -191,7 +191,7 @@ export const DelibirdyEncounter: MysteryEncounter = const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType; await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell); scene.playSound("item_fanfare"); - await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, true); + await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), undefined, true); } else { scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.CANDY_JAR)); } @@ -204,7 +204,7 @@ export const DelibirdyEncounter: MysteryEncounter = const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType; await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell); scene.playSound("item_fanfare"); - await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, true); + await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), undefined, true); } else { scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.HEALING_CHARM)); } @@ -264,13 +264,13 @@ export const DelibirdyEncounter: MysteryEncounter = // If pokemon meets primary pokemon reqs, it can be selected const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon); if (!meetsReqs) { - return getEncounterText(scene, `${namespace}.invalid_selection`); + return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null; } return null; }; - return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); + return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); }) .withOptionPhase(async (scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter; @@ -284,7 +284,7 @@ export const DelibirdyEncounter: MysteryEncounter = const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType; await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell); scene.playSound("item_fanfare"); - await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, true); + await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), undefined, true); } else { scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.BERRY_POUCH)); } diff --git a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts index 9a11a2e8888..09c991ee4a3 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -2,7 +2,7 @@ import { leaveEncounterWithoutBattle, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { modifierTypes } from "#app/modifier/modifier-type"; +import { ModifierTypeFunc, modifierTypes } from "#app/modifier/modifier-type"; import { randSeedInt } from "#app/utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; @@ -32,8 +32,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = x: -20, }, { - spriteKey: null, - fileRoot: null, + spriteKey: "", + fileRoot: "", species: Species.FURFROU, hasShadow: true, repeat: true, @@ -60,7 +60,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = }, async (scene: BattleScene) => { // Choose TMs - const modifiers = []; + const modifiers: ModifierTypeFunc[] = []; let i = 0; while (i < 4) { // 2/2/1 weight on TM rarity @@ -86,7 +86,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = }, async (scene: BattleScene) => { // Choose Vitamins - const modifiers = []; + const modifiers: ModifierTypeFunc[] = []; let i = 0; while (i < 3) { // 2/1 weight on base stat booster vs PP Up @@ -110,7 +110,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = }, async (scene: BattleScene) => { // Choose X Items - const modifiers = []; + const modifiers: ModifierTypeFunc[] = []; let i = 0; while (i < 5) { // 4/1 weight on base stat booster vs Dire Hit @@ -134,7 +134,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = }, async (scene: BattleScene) => { // Choose Pokeballs - const modifiers = []; + const modifiers: ModifierTypeFunc[] = []; let i = 0; while (i < 4) { // 10/30/20/5 weight on pokeballs diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index 5024ba3c361..ca6f7424dc8 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -78,7 +78,7 @@ export const FieldTripEncounter: MysteryEncounter = const correctMove = move.getMove().category === MoveCategory.PHYSICAL; encounter.setDialogueToken("moveCategory", "Physical"); if (!correctMove) { - encounter.options[0].dialogue.selected = [ + encounter.options[0].dialogue!.selected = [ { text: `${namespace}.option.incorrect`, speaker: `${namespace}.speaker`, @@ -97,7 +97,7 @@ export const FieldTripEncounter: MysteryEncounter = } else { encounter.setDialogueToken("pokeName", pokemon.getNameToRender()); encounter.setDialogueToken("move", move.getName()); - encounter.options[0].dialogue.selected = [ + encounter.options[0].dialogue!.selected = [ { text: `${namespace}.option.selected`, }, @@ -164,7 +164,7 @@ export const FieldTripEncounter: MysteryEncounter = const correctMove = move.getMove().category === MoveCategory.SPECIAL; encounter.setDialogueToken("moveCategory", "Special"); if (!correctMove) { - encounter.options[1].dialogue.selected = [ + encounter.options[1].dialogue!.selected = [ { text: `${namespace}.option.incorrect`, speaker: `${namespace}.speaker`, @@ -189,7 +189,7 @@ export const FieldTripEncounter: MysteryEncounter = } else { encounter.setDialogueToken("pokeName", pokemon.getNameToRender()); encounter.setDialogueToken("move", move.getName()); - encounter.options[1].dialogue.selected = [ + encounter.options[1].dialogue!.selected = [ { text: `${namespace}.option.selected`, }, @@ -256,7 +256,7 @@ export const FieldTripEncounter: MysteryEncounter = const correctMove = move.getMove().category === MoveCategory.STATUS; encounter.setDialogueToken("moveCategory", "Status"); if (!correctMove) { - encounter.options[2].dialogue.selected = [ + encounter.options[2].dialogue!.selected = [ { text: `${namespace}.option.incorrect`, speaker: `${namespace}.speaker`, @@ -275,7 +275,7 @@ export const FieldTripEncounter: MysteryEncounter = } else { encounter.setDialogueToken("pokeName", pokemon.getNameToRender()); encounter.setDialogueToken("move", move.getName()); - encounter.options[2].dialogue.selected = [ + encounter.options[2].dialogue!.selected = [ { text: `${namespace}.option.selected`, }, diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 99919b09e3c..1eea328927b 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -75,8 +75,8 @@ export const FieryFalloutEncounter: MysteryEncounter = // Load hidden Volcarona sprites encounter.spriteConfigs = [ { - spriteKey: null, - fileRoot: null, + spriteKey: "", + fileRoot: "", species: Species.VOLCARONA, repeat: true, hidden: true, @@ -85,8 +85,8 @@ export const FieryFalloutEncounter: MysteryEncounter = startFrame: 20 }, { - spriteKey: null, - fileRoot: null, + spriteKey: "", + fileRoot: "", species: Species.VOLCARONA, repeat: true, hidden: true, @@ -104,9 +104,9 @@ export const FieryFalloutEncounter: MysteryEncounter = }) .withOnVisualsStart((scene: BattleScene) => { // Play animations - const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, scene.getPlayerPokemon(), scene.getPlayerPokemon()); + const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); background.playWithoutTargets(scene, 200, 70, 2, 3); - const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, scene.getPlayerPokemon(), scene.getPlayerPokemon()); + const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); animation.playWithoutTargets(scene, 80, 100, 2); scene.time.delayedCall(600, () => { animation.playWithoutTargets(scene, -20, 100, 2); @@ -133,7 +133,7 @@ export const FieryFalloutEncounter: MysteryEncounter = async (scene: BattleScene) => { // Pick battle const encounter = scene.currentBattle.mysteryEncounter; - setEncounterRewards(scene, { fillRemaining: true }, null, () => giveLeadPokemonCharcoal(scene)); + setEncounterRewards(scene, { fillRemaining: true }, undefined, () => giveLeadPokemonCharcoal(scene)); encounter.startOfBattleEffects.push( { @@ -185,7 +185,7 @@ export const FieryFalloutEncounter: MysteryEncounter = } // Burn random member - const burnable = nonFireTypes.filter(p => isNullOrUndefined(p.status) || isNullOrUndefined(p.status.effect) || p.status?.effect === StatusEffect.BURN); + const burnable = nonFireTypes.filter(p => isNullOrUndefined(p.status) || isNullOrUndefined(p.status!.effect) || p.status?.effect === StatusEffect.BURN); if (burnable?.length > 0) { const roll = randSeedInt(burnable.length); const chosenPokemon = burnable[roll]; @@ -224,13 +224,13 @@ export const FieryFalloutEncounter: MysteryEncounter = transitionMysteryEncounterIntroVisuals(scene); setEncounterRewards(scene, { fillRemaining: true }, - null, + undefined, () => { giveLeadPokemonCharcoal(scene); }); - const primary = encounter.options[2].primaryPokemon; - const secondary = encounter.options[2].secondaryPokemon[0]; + const primary = encounter.options[2].primaryPokemon!; + const secondary = encounter.options[2].secondaryPokemon![0]; setEncounterExp(scene, [primary.id, secondary.id], getPokemonSpecies(Species.VOLCARONA).baseExp * 2); leaveEncounterWithoutBattle(scene); diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index 10d9f21e8bb..616c81880df 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -50,7 +50,7 @@ export const FightOrFlightEncounter: MysteryEncounter = // Calculate boss mon const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, scene.currentBattle.waveIndex, 0, getPartyLuckValue(scene.getParty()), true); - const bossPokemon = new EnemyPokemon(scene, bossSpecies, scene.currentBattle.waveIndex, TrainerSlot.NONE, true, null); + const bossPokemon = new EnemyPokemon(scene, bossSpecies, scene.currentBattle.waveIndex, TrainerSlot.NONE, true); const config: EnemyPartyConfig = { levelAdditiveMultiplier: 1, pokemonConfigs: [{ @@ -72,7 +72,7 @@ export const FightOrFlightEncounter: MysteryEncounter = ? ModifierTier.ULTRA : ModifierTier.GREAT; regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0); - let item: ModifierTypeOption; + let item: ModifierTypeOption | null = null; // TMs excluded from possible rewards as they're too swingy in value for a singular item reward while (!item || item.type.id.includes("TM_")) { item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier], allowLuckUpgrades: false })[0]; @@ -147,8 +147,8 @@ export const FightOrFlightEncounter: MysteryEncounter = setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false }); // Use primaryPokemon to execute the thievery - const primaryPokemon = encounter.options[1].primaryPokemon; - setEncounterExp(scene, primaryPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs[0].species.baseExp); + const primaryPokemon = encounter.options[1].primaryPokemon!; + setEncounterExp(scene, primaryPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp); leaveEncounterWithoutBattle(scene); }) .build() diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index f3738059b0b..1695466c1cd 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -87,6 +87,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = const e4Template = trainerPartyTemplates.ELITE_FOUR; const brutalConfig = trainerConfigs[brutalTrainerType].copy(); brutalConfig.setPartyTemplates(e4Template); + // @ts-ignore brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func female = false; if (brutalConfig.hasGenders) { diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index e4616c6f169..303beb57aae 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -57,7 +57,7 @@ export const MysteriousChestEncounter: MysteryEncounter = .withPreOptionPhase(async (scene: BattleScene) => { // Play animation const introVisuals = - scene.currentBattle.mysteryEncounter.introVisuals; + scene.currentBattle.mysteryEncounter.introVisuals!; introVisuals.spriteConfigs[0].disableAnimation = false; introVisuals.playAnim(); }) diff --git a/src/data/mystery-encounters/encounters/part-timer-encounter.ts b/src/data/mystery-encounters/encounters/part-timer-encounter.ts index 861e0bc0e8f..a17a47f23fe 100644 --- a/src/data/mystery-encounters/encounters/part-timer-encounter.ts +++ b/src/data/mystery-encounters/encounters/part-timer-encounter.ts @@ -100,8 +100,10 @@ export const PartTimerEncounter: MysteryEncounter = // Reduce all PP to 2 (if they started at greater than 2) pokemon.moveset.forEach(move => { - const newPpUsed = move.getMovePp() - 2; - move.ppUsed = move.ppUsed < newPpUsed ? newPpUsed : move.ppUsed; + if (move) { + const newPpUsed = move.getMovePp() - 2; + move.ppUsed = move.ppUsed < newPpUsed ? newPpUsed : move.ppUsed; + } }); setEncounterExp(scene, pokemon.id, 100); @@ -115,13 +117,13 @@ export const PartTimerEncounter: MysteryEncounter = // Only Pokemon non-KOd pokemon can be selected const selectableFilter = (pokemon: Pokemon) => { if (!pokemon.isAllowedInBattle()) { - return getEncounterText(scene, `${namespace}.invalid_selection`); + return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null; } return null; }; - return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); + return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); }) .withOptionPhase(async (scene: BattleScene) => { // Pick Deliveries @@ -179,8 +181,10 @@ export const PartTimerEncounter: MysteryEncounter = // Reduce all PP to 2 (if they started at greater than 2) pokemon.moveset.forEach(move => { - const newPpUsed = move.getMovePp() - 2; - move.ppUsed = move.ppUsed < newPpUsed ? newPpUsed : move.ppUsed; + if (move) { + const newPpUsed = move.getMovePp() - 2; + move.ppUsed = move.ppUsed < newPpUsed ? newPpUsed : move.ppUsed; + } }); setEncounterExp(scene, pokemon.id, 100); @@ -194,13 +198,13 @@ export const PartTimerEncounter: MysteryEncounter = // Only Pokemon non-KOd pokemon can be selected const selectableFilter = (pokemon: Pokemon) => { if (!pokemon.isAllowedInBattle()) { - return getEncounterText(scene, `${namespace}.invalid_selection`); + return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null; } return null; }; - return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); + return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); }) .withOptionPhase(async (scene: BattleScene) => { // Pick Move Warehouse items @@ -241,13 +245,15 @@ export const PartTimerEncounter: MysteryEncounter = }) .withPreOptionPhase(async (scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter; - const selectedPokemon = encounter.selectedOption.primaryPokemon; + const selectedPokemon = encounter.selectedOption?.primaryPokemon!; encounter.setDialogueToken("selectedPokemon", selectedPokemon.getNameToRender()); // Reduce all PP to 2 (if they started at greater than 2) selectedPokemon.moveset.forEach(move => { - const newPpUsed = move.getMovePp() - 2; - move.ppUsed = move.ppUsed < newPpUsed ? newPpUsed : move.ppUsed; + if (move) { + const newPpUsed = move.getMovePp() - 2; + move.ppUsed = move.ppUsed < newPpUsed ? newPpUsed : move.ppUsed; + } }); setEncounterExp(scene, selectedPokemon.id, 100); diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 66f286653b9..a83d76a7cfa 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -172,9 +172,9 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [ // 80% chance to increase flee stage +1 const fleeChangeResult = tryChangeFleeStage(scene, 1, 8); if (!fleeChangeResult) { - await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.busy_eating`), 1000, false ); + await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.busy_eating`) ?? "", 1000, false ); } else { - await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.eating`), 1000, false); + await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.eating`) ?? "", 1000, false); } await doEndTurn(scene, 1); @@ -201,9 +201,9 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [ // 80% chance to decrease catch stage -1 const catchChangeResult = tryChangeCatchStage(scene, -1, 8); if (!catchChangeResult) { - await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.beside_itself_angry`), 1000, false ); + await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.beside_itself_angry`) ?? "", 1000, false ); } else { - await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.angry`), 1000, false ); + await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.angry`) ?? "", 1000, false ); } await doEndTurn(scene, 2); @@ -239,7 +239,7 @@ async function summonSafariPokemon(scene: BattleScene) { const encounter = scene.currentBattle.mysteryEncounter; // Message pokemon remaining encounter.setDialogueToken("remainingCount", encounter.misc.safariPokemonRemaining); - scene.queueMessage(getEncounterText(scene, `${namespace}.safari.remaining_count`), null, true); + scene.queueMessage(getEncounterText(scene, `${namespace}.safari.remaining_count`) ?? "", null, true); // Generate pokemon using safariPokemonRemaining so they are always the same pokemon no matter how many turns are taken // Safari pokemon roll twice on shiny and HA chances, but are otherwise normal @@ -288,7 +288,7 @@ async function summonSafariPokemon(scene: BattleScene) { scene.unshiftPhase(new SummonPhase(scene, 0, false)); encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon)); - showEncounterText(scene, getEncounterText(scene, "battle:singleWildAppeared"), 1500, false) + showEncounterText(scene, getEncounterText(scene, "battle:singleWildAppeared") ?? "", 1500, false) .then(() => { const ivScannerModifier = scene.findModifier(m => m instanceof IvScannerModifier); if (ivScannerModifier) { @@ -494,7 +494,7 @@ async function doEndTurn(scene: BattleScene, cursorIndex: number) { leaveEncounterWithoutBattle(scene, true); } } else { - scene.queueMessage(getEncounterText(scene, `${namespace}.safari.watching`), 0, null, 1000); + scene.queueMessage(getEncounterText(scene, `${namespace}.safari.watching`) ?? "", 0, null, 1000); initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true }); } } diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts index 3e4fe49da43..2876ce64c8f 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -95,13 +95,13 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = // If pokemon meets primary pokemon reqs, it can be selected const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon); if (!meetsReqs) { - return getEncounterText(scene, `${namespace}.invalid_selection`); + return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null; } return null; }; - return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); + return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); }) .withOptionPhase(async (scene: BattleScene) => { // Choose Cheap Option @@ -178,13 +178,13 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = // If pokemon meets primary pokemon reqs, it can be selected const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon); if (!meetsReqs) { - return getEncounterText(scene, `${namespace}.invalid_selection`); + return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null; } return null; }; - return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); + return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); }) .withOptionPhase(async (scene: BattleScene) => { // Choose Expensive Option diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index 35ba52cdce7..f56823a3445 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -140,7 +140,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = const instance = scene.currentBattle.mysteryEncounter; setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: false }); // Snorlax exp to Pokemon that did the stealing - setEncounterExp(scene, instance.primaryPokemon.id, getPokemonSpecies(Species.SNORLAX).baseExp); + setEncounterExp(scene, instance.primaryPokemon!.id, getPokemonSpecies(Species.SNORLAX).baseExp); leaveEncounterWithoutBattle(scene); }) .build() diff --git a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts index e44d03da8f5..9604783d3ff 100644 --- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts @@ -29,7 +29,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_POKEMON_SALESMAN) .withEncounterTier(MysteryEncounterTier.ULTRA) .withSceneWaveRangeRequirement(10, 180) - .withSceneRequirement(new MoneyRequirement(null, MAX_POKEMON_PRICE_MULTIPLIER)) // Some costs may not be as significant, this is the max you'd pay + .withSceneRequirement(new MoneyRequirement(undefined, MAX_POKEMON_PRICE_MULTIPLIER)) // Some costs may not be as significant, this is the max you'd pay .withAutoHideIntroVisuals(false) .withIntroSpriteConfigs([ { @@ -66,10 +66,10 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = // If no HA mon found or you roll 1%, give shiny Magikarp species = getPokemonSpecies(Species.MAGIKARP); const hiddenIndex = species.ability2 ? 2 : 1; - pokemon = new PlayerPokemon(scene, species, 5, hiddenIndex, species.formIndex, null, true, null, null, null, null); + pokemon = new PlayerPokemon(scene, species, 5, hiddenIndex, species.formIndex, undefined, true); } else { const hiddenIndex = species.ability2 ? 2 : 1; - pokemon = new PlayerPokemon(scene, species, 5, hiddenIndex, species.formIndex, null, null, null, null, null, null); + pokemon = new PlayerPokemon(scene, species, 5, hiddenIndex, species.formIndex); } const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(pokemon); @@ -88,8 +88,8 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = // Always max price for shiny (flip HA back to normal), and add special messaging priceMultiplier = MAX_POKEMON_PRICE_MULTIPLIER; pokemon.abilityIndex = 0; - encounter.dialogue.encounterOptionsDialogue.description = `${namespace}.description_shiny`; - encounter.options[0].dialogue.buttonTooltip = `${namespace}.option.1.tooltip_shiny`; + encounter.dialogue.encounterOptionsDialogue!.description = `${namespace}.description_shiny`; + encounter.options[0].dialogue!.buttonTooltip = `${namespace}.option.1.tooltip_shiny`; } const price = scene.getWaveMoneyAmount(priceMultiplier); encounter.setDialogueToken("purchasePokemon", pokemon.getNameToRender()); @@ -107,7 +107,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterOptionBuilder .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) .withHasDexProgress(true) - .withSceneMoneyRequirement(null, MAX_POKEMON_PRICE_MULTIPLIER) // Wave scaling money multiplier of 2 + .withSceneMoneyRequirement(undefined, MAX_POKEMON_PRICE_MULTIPLIER) // Wave scaling money multiplier of 2 .withDialogue({ buttonLabel: `${namespace}.option.1.label`, buttonTooltip: `${namespace}.option.1.tooltip`, diff --git a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts index fd67d2a0234..4eaec2fdca2 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -127,7 +127,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = // -20 to all base stats of highest BST, +10 to all base stats of rest of party // Get highest BST mon const party = scene.getParty(); - let highestBst: PlayerPokemon = null; + let highestBst: PlayerPokemon | null = null; let statTotal = 0; for (const pokemon of party) { if (!highestBst) { @@ -157,7 +157,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = } encounter.setDialogueToken("highBstPokemon", highestBst.getNameToRender()); - await showEncounterText(scene, `${namespace}.option.1.selected_2`, null, true); + await showEncounterText(scene, `${namespace}.option.1.selected_2`, undefined, true); setEncounterRewards(scene, { fillRemaining: true }); leaveEncounterWithoutBattle(scene, true); diff --git a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts index c0886ed9f6b..5ae6d86ecdb 100644 --- a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts @@ -140,7 +140,7 @@ async function spawnNextTrainerOrEndEncounter(scene: BattleScene) { await transitionMysteryEncounterIntroVisuals(scene, false, false); await showEncounterDialogue(scene, `${namespace}.victory`, `${namespace}.speaker`); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.MYSTERY_ENCOUNTER_MACHO_BRACE], fillRemaining: false }); - encounter.doContinueEncounter = null; + encounter.doContinueEncounter = undefined; leaveEncounterWithoutBattle(scene, false, MysteryEncounterMode.TRAINER_BATTLE); } else { await initBattleWithEnemyConfig(scene, nextConfig); @@ -151,17 +151,21 @@ function endTrainerBattleAndShowDialogue(scene: BattleScene): Promise { return new Promise(async resolve => { if (scene.currentBattle.mysteryEncounter.enemyPartyConfigs.length === 0) { // Battle is over - scene.tweens.add({ - targets: scene.currentBattle.trainer, - x: "+=16", - y: "-=16", - alpha: 0, - ease: "Sine.easeInOut", - duration: 750, - onComplete: () => { - scene.field.remove(scene.currentBattle.trainer, true); - } - }); + const trainer = scene.currentBattle.trainer; + if (trainer) { + scene.tweens.add({ + targets: trainer, + x: "+=16", + y: "-=16", + alpha: 0, + ease: "Sine.easeInOut", + duration: 750, + onComplete: () => { + scene.field.remove(trainer, true); + } + }); + } + await spawnNextTrainerOrEndEncounter(scene); resolve(); // Wait for all dialogue/post battle stuff to complete before resolving } else { @@ -186,18 +190,20 @@ function endTrainerBattleAndShowDialogue(scene: BattleScene): Promise { // Unassign previous trainer from battle so it isn't destroyed before animation completes scene.currentBattle.trainer = null; await spawnNextTrainerOrEndEncounter(scene); - scene.tweens.add({ - targets: trainer, - x: "+=16", - y: "-=16", - alpha: 0, - ease: "Sine.easeInOut", - duration: 750, - onComplete: () => { - scene.field.remove(trainer, true); - resolve(); - } - }); + if (trainer) { + scene.tweens.add({ + targets: trainer, + x: "+=16", + y: "-=16", + alpha: 0, + ease: "Sine.easeInOut", + duration: 750, + onComplete: () => { + scene.field.remove(trainer, true); + resolve(); + } + }); + } } }); } diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index e680268bc8f..54d48d4dcc5 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -6,11 +6,10 @@ import { Stat } from "#app/data/pokemon-stat"; import Pokemon, { PlayerPokemon } from "#app/field/pokemon"; import { pokemonInfo } from "#app/locales/en/pokemon-info"; import { PokemonHeldItemModifier } from "#app/modifier/modifier"; -import { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { AbilityAttr } from "#app/system/game-data"; import PokemonData from "#app/system/pokemon-data"; import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import { randSeedShuffle } from "#app/utils"; +import { isNullOrUndefined, randSeedShuffle } from "#app/utils"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; @@ -19,6 +18,7 @@ import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; +import HeldModifierConfig from "#app/interfaces/held-modifier-config"; /** The i18n namespace for the encounter */ const namespace = "mysteryEncounter:trainingSession"; @@ -77,13 +77,13 @@ export const TrainingSessionEncounter: MysteryEncounter = const selectableFilter = (pokemon: Pokemon) => { const meetsReqs = pokemon.isAllowedInBattle(); if (!meetsReqs) { - return getEncounterText(scene, `${namespace}.invalid_selection`); + return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null; } return null; }; - return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); + return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); }) .withOptionPhase(async (scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter; @@ -125,7 +125,7 @@ export const TrainingSessionEncounter: MysteryEncounter = encounter.setDialogueToken("stat1", "-"); encounter.setDialogueToken("stat2", "-"); // Add the pokemon back to party with IV boost - const ivIndexes = []; + const ivIndexes: any[] = []; playerPokemon.ivs.forEach((iv, index) => { if (iv < 31) { ivIndexes.push({ iv: iv, index: index }); @@ -147,12 +147,12 @@ export const TrainingSessionEncounter: MysteryEncounter = if (improvedCount === 0) { encounter.setDialogueToken( "stat1", - getIvName(ivToChange.index) + getIvName(ivToChange.index) ?? "" ); } else { encounter.setDialogueToken( "stat2", - getIvName(ivToChange.index) + getIvName(ivToChange.index) ?? "" ); } @@ -185,12 +185,7 @@ export const TrainingSessionEncounter: MysteryEncounter = queueEncounterMessage(scene, `${namespace}.option.1.finished`); }; - setEncounterRewards( - scene, - { fillRemaining: true }, - null, - onBeforeRewardsPhase - ); + setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase); return initBattleWithEnemyConfig(scene, config); }) @@ -237,13 +232,13 @@ export const TrainingSessionEncounter: MysteryEncounter = const selectableFilter = (pokemon: Pokemon) => { const meetsReqs = pokemon.isAllowedInBattle(); if (!meetsReqs) { - return getEncounterText(scene, `${namespace}.invalid_selection`); + return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null; } return null; }; - return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); + return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); }) .withOptionPhase(async (scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter; @@ -278,12 +273,7 @@ export const TrainingSessionEncounter: MysteryEncounter = scene.updateModifiers(true); }; - setEncounterRewards( - scene, - { fillRemaining: true }, - null, - onBeforeRewardsPhase - ); + setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase); return initBattleWithEnemyConfig(scene, config); }) @@ -339,13 +329,13 @@ export const TrainingSessionEncounter: MysteryEncounter = const selectableFilter = (pokemon: Pokemon) => { const meetsReqs = pokemon.isAllowedInBattle(); if (!meetsReqs) { - return getEncounterText(scene, `${namespace}.invalid_selection`); + return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null; } return null; }; - return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); + return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); }) .withOptionPhase(async (scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter; @@ -354,18 +344,10 @@ export const TrainingSessionEncounter: MysteryEncounter = // Spawn hard training session with chosen pokemon // Every 30 waves, add +1 boss segment, capping at 6 // Also starts with +1 to all stats - const segments = Math.min( - 2 + Math.floor(scene.currentBattle.waveIndex / 30), - 6 - ); + const segments = Math.min(2 + Math.floor(scene.currentBattle.waveIndex / 30), 6); const modifiers = new ModifiersHolder(); - const config = getEnemyConfig( - scene, - playerPokemon, - segments, - modifiers - ); - config.pokemonConfigs[0].tags = [ + const config = getEnemyConfig(scene, playerPokemon, segments, modifiers); + config.pokemonConfigs![0].tags = [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON, ]; scene.removePokemonFromPlayerParty(playerPokemon, false); @@ -376,15 +358,10 @@ export const TrainingSessionEncounter: MysteryEncounter = const abilityIndex = encounter.misc.abilityIndex; if (!!playerPokemon.getFusionSpeciesForm()) { playerPokemon.fusionAbilityIndex = abilityIndex; - if ( - speciesStarters.hasOwnProperty( - playerPokemon.fusionSpecies.speciesId - ) - ) { - scene.gameData.starterData[ - playerPokemon.fusionSpecies.speciesId - ].abilityAttr |= - abilityIndex !== 1 || playerPokemon.fusionSpecies.ability2 + if (!isNullOrUndefined(playerPokemon.fusionSpecies?.speciesId) && speciesStarters.hasOwnProperty(playerPokemon.fusionSpecies!.speciesId)) { + scene.gameData.starterData[playerPokemon.fusionSpecies!.speciesId] + .abilityAttr |= + abilityIndex !== 1 || playerPokemon.fusionSpecies!.ability2 ? Math.pow(2, playerPokemon.fusionAbilityIndex) : AbilityAttr.ABILITY_HIDDEN; } @@ -414,12 +391,7 @@ export const TrainingSessionEncounter: MysteryEncounter = scene.updateModifiers(true); }; - setEncounterRewards( - scene, - { fillRemaining: true }, - null, - onBeforeRewardsPhase - ); + setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase); return initBattleWithEnemyConfig(scene, config); }) @@ -432,7 +404,11 @@ function getEnemyConfig(scene: BattleScene, playerPokemon: PlayerPokemon,segment // Passes modifiers by reference modifiers.value = scene.findModifiers((m) => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).pokemonId === playerPokemon.id) as PokemonHeldItemModifier[]; - const modifierTypes = modifiers.value.map((mod) => mod.type) as PokemonHeldItemModifierType[]; + const modifierConfigs = modifiers.value.map((mod) => { + return { + modifierType: mod.type + }; + }) as HeldModifierConfig[]; const data = new PokemonData(playerPokemon); return { @@ -444,7 +420,7 @@ function getEnemyConfig(scene: BattleScene, playerPokemon: PlayerPokemon,segment formIndex: playerPokemon.formIndex, level: playerPokemon.level, dataSource: data, - modifierConfigs: modifierTypes, + modifierConfigs: modifierConfigs, }, ], }; diff --git a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts index 50de002e053..d8c95761952 100644 --- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts +++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts @@ -178,7 +178,7 @@ async function tryApplyDigRewardItems(scene: BattleScene) { } scene.playSound("item_fanfare"); - await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: "2 " + leftovers.name }), null, true); + await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: "2 " + leftovers.name }), undefined, true); // First Shell bell for (const pokemon of party) { @@ -205,7 +205,7 @@ async function tryApplyDigRewardItems(scene: BattleScene) { } scene.playSound("item_fanfare"); - await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: "2 " + shellBell.name }), null, true); + await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: "2 " + shellBell.name }), undefined, true); } async function doGarbageDig(scene: BattleScene) { diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index 99d732f3107..d4ec3ab3c04 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -8,7 +8,7 @@ import { leaveEncounterWithoutBattle, setEncounterRewards, } from "../utils/enco import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; -import { IntegerHolder, randSeedInt, randSeedShuffle } from "#app/utils"; +import { IntegerHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils"; import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species"; import { HiddenAbilityRateBoosterModifier, PokemonBaseStatTotalModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/modifier/modifier"; import { achvs } from "#app/system/achv"; @@ -362,7 +362,7 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon } newTypes.push(newType); if (!newPokemon.mysteryEncounterData) { - newPokemon.mysteryEncounterData = new MysteryEncounterPokemonData(null, null, null, newTypes); + newPokemon.mysteryEncounterData = new MysteryEncounterPokemonData(undefined, undefined, undefined, newTypes); } else { newPokemon.mysteryEncounterData.types = newTypes; } @@ -381,9 +381,11 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon // Def or SpDef stats.push(baseStats[Stat.DEF] < baseStats[Stat.SPDEF] ? Stat.DEF : Stat.SPDEF); // const mod = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU().newModifier(newPokemon, 20, stats); - const modType = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU().generateType(null, [20, stats]); - const modifier = modType.newModifier(newPokemon); - scene.addModifier(modifier); + const modType = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU().generateType(scene.getParty(), [20, stats]); + const modifier = modType?.newModifier(newPokemon); + if (modifier) { + scene.addModifier(modifier); + } } // Enable passive if previous had it @@ -422,8 +424,8 @@ function getOriginalBst(scene: BattleScene, pokemon: Pokemon) { } function getTransformedSpecies(originalBst: number, bstSearchRange: [number, number], hasPokemonBstHigherThan600: boolean, hasPokemonBstBetween570And600: boolean, alreadyUsedSpecies: PokemonSpecies[]): PokemonSpecies { - let newSpecies: PokemonSpecies; - while (!newSpecies) { + let newSpecies: PokemonSpecies | undefined; + while (isNullOrUndefined(newSpecies)) { const bstCap = originalBst + bstSearchRange[1]; const bstMin = Math.max(originalBst + bstSearchRange[0], 0); @@ -442,7 +444,7 @@ function getTransformedSpecies(originalBst: number, bstSearchRange: [number, num if (validSpecies?.length > 20) { validSpecies = randSeedShuffle(validSpecies); newSpecies = validSpecies.pop(); - while (alreadyUsedSpecies.includes(newSpecies)) { + while (isNullOrUndefined(newSpecies) || alreadyUsedSpecies.includes(newSpecies!)) { newSpecies = validSpecies.pop(); } } else { @@ -452,7 +454,7 @@ function getTransformedSpecies(originalBst: number, bstSearchRange: [number, num } } - return newSpecies; + return newSpecies!; } function doShowDreamBackground(scene: BattleScene) { @@ -503,7 +505,7 @@ function doHideDreamBackground(scene: BattleScene) { function doSideBySideTransformations(scene: BattleScene, transformations: PokemonTransformation[]) { return new Promise(resolve => { - const allTransformationPromises = []; + const allTransformationPromises: Promise[] = []; for (let i = 0; i < 3; i++) { const delay = i * 4000; scene.time.delayedCall(delay, () => { diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index d4cf56ae17c..086706075e7 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -8,16 +8,32 @@ import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequiremen import { CanLearnMoveRequirement, CanLearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement"; import { isNullOrUndefined } from "#app/utils"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; export type OptionPhaseCallback = (scene: BattleScene) => Promise; -// export default interface MysteryEncounterOption { -// -// } +/** + * Used by {@link MysteryEncounterOptionBuilder} class to define required/optional properties on the {@link MysteryEncounterOption} class when building. + * + * Should ONLY contain properties that are necessary for {@link MysteryEncounterOption} construction. + * Post-construct and flag data properties are defined in the {@link MysteryEncounterOption} class itself. + */ +export interface IMysteryEncounterOption { + optionMode: MysteryEncounterOptionMode; + hasDexProgress: boolean; + requirements: EncounterSceneRequirement[]; + primaryPokemonRequirements: EncounterPokemonRequirement[]; + secondaryPokemonRequirements: EncounterPokemonRequirement[]; + excludePrimaryFromSecondaryRequirements: boolean; -export default class MysteryEncounterOption { + dialogue?: OptionTextDisplay; + + onPreOptionPhase?: OptionPhaseCallback; + onOptionPhase: OptionPhaseCallback; + onPostOptionPhase?: OptionPhaseCallback; +} + +export default class MysteryEncounterOption implements IMysteryEncounterOption { optionMode: MysteryEncounterOptionMode; hasDexProgress: boolean; requirements: EncounterSceneRequirement[]; @@ -40,12 +56,14 @@ export default class MysteryEncounterOption { /** Executes after the encounter is over. Usually this will be for calculating dialogueTokens or performing data updates */ onPostOptionPhase?: OptionPhaseCallback; - constructor(option: MysteryEncounterOption | null) { - Object.assign(this, option); - this.hasDexProgress = !isNullOrUndefined(this.hasDexProgress) ? this.hasDexProgress : false; - this.requirements = this.requirements ? this.requirements : []; - this.primaryPokemonRequirements = this.primaryPokemonRequirements ? this.primaryPokemonRequirements : []; - this.secondaryPokemonRequirements = this.secondaryPokemonRequirements ? this.secondaryPokemonRequirements : []; + constructor(option: IMysteryEncounterOption | null) { + if (!isNullOrUndefined(option)) { + Object.assign(this, option); + } + this.hasDexProgress = this.hasDexProgress ?? false; + this.requirements = this.requirements ?? []; + this.primaryPokemonRequirements = this.primaryPokemonRequirements ?? []; + this.secondaryPokemonRequirements = this.secondaryPokemonRequirements ?? []; } hasRequirements() { @@ -70,7 +88,8 @@ export default class MysteryEncounterOption { for (const req of this.primaryPokemonRequirements) { if (req.meetsRequirement(scene)) { if (req instanceof EncounterPokemonRequirement) { - qualified = qualified.filter(pkmn => req.queryParty(scene.getParty()).includes(pkmn)); + const queryParty = req.queryParty(scene.getParty()); + qualified = qualified.filter(pkmn => queryParty.includes(pkmn)); } } else { this.primaryPokemon = undefined; @@ -125,8 +144,8 @@ export default class MysteryEncounterOption { for (const req of this.secondaryPokemonRequirements) { if (req.meetsRequirement(scene)) { if (req instanceof EncounterPokemonRequirement) { - qualified = qualified.filter(pkmn => req.queryParty(scene.getParty()).includes(pkmn)); - + const queryParty = req.queryParty(scene.getParty()); + qualified = qualified.filter(pkmn => queryParty.includes(pkmn)); } } else { this.secondaryPokemon = []; @@ -138,7 +157,7 @@ export default class MysteryEncounterOption { } } -export class MysteryEncounterOptionBuilder implements Partial { +export class MysteryEncounterOptionBuilder implements Partial { optionMode: MysteryEncounterOptionMode = MysteryEncounterOptionMode.DEFAULT; requirements: EncounterSceneRequirement[] = []; primaryPokemonRequirements: EncounterPokemonRequirement[] = []; @@ -146,26 +165,17 @@ export class MysteryEncounterOptionBuilder implements Partial { + static newOptionWithMode(optionMode: MysteryEncounterOptionMode): MysteryEncounterOptionBuilder & Pick { return Object.assign(new MysteryEncounterOptionBuilder(), { optionMode }); } - withHasDexProgress(hasDexProgress: boolean): this & Required> { + withHasDexProgress(hasDexProgress: boolean): this & Required> { return Object.assign(this, { hasDexProgress: hasDexProgress }); } - withSceneRequirement(requirement: EncounterSceneRequirement): this & Required> { + withSceneRequirement(requirement: EncounterSceneRequirement): this & Required> { if (requirement instanceof EncounterPokemonRequirement) { Error("Incorrectly added pokemon requirement as scene requirement."); } @@ -174,23 +184,23 @@ export class MysteryEncounterOptionBuilder implements Partial> { + withPreOptionPhase(onPreOptionPhase: OptionPhaseCallback): this & Required> { return Object.assign(this, { onPreOptionPhase: onPreOptionPhase }); } - withOptionPhase(onOptionPhase: OptionPhaseCallback): this & Required> { + withOptionPhase(onOptionPhase: OptionPhaseCallback): this & Required> { return Object.assign(this, { onOptionPhase: onOptionPhase }); } - withPostOptionPhase(onPostOptionPhase: OptionPhaseCallback): this & Required> { + withPostOptionPhase(onPostOptionPhase: OptionPhaseCallback): this & Required> { return Object.assign(this, { onPostOptionPhase: onPostOptionPhase }); } - withPrimaryPokemonRequirement(requirement: EncounterPokemonRequirement): this & Required> { + withPrimaryPokemonRequirement(requirement: EncounterPokemonRequirement): this & Required> { if (requirement instanceof EncounterSceneRequirement) { Error("Incorrectly added scene requirement as pokemon requirement."); } @@ -223,7 +233,7 @@ export class MysteryEncounterOptionBuilder implements Partial> { + withSecondaryPokemonRequirement(requirement: EncounterPokemonRequirement, excludePrimaryFromSecondaryRequirements: boolean = true): this & Required> { if (requirement instanceof EncounterSceneRequirement) { Error("Incorrectly added scene requirement as pokemon requirement."); } @@ -244,7 +254,7 @@ export class MysteryEncounterOptionBuilder implements Partial 0) { + if (this.scalingMultiplier > 0) { this.requiredMoney = scene.getWaveMoneyAmount(this.scalingMultiplier); } - return !(this?.requiredMoney > 0 && this.requiredMoney > money); + return !(this.requiredMoney > 0 && this.requiredMoney > money); } getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { - const value = this?.scalingMultiplier > 0 ? scene.getWaveMoneyAmount(this.scalingMultiplier).toString() : this.requiredMoney.toString(); + const value = this.scalingMultiplier > 0 ? scene.getWaveMoneyAmount(this.scalingMultiplier).toString() : this.requiredMoney.toString(); return ["money", value]; } } @@ -367,10 +367,10 @@ export class NatureRequirement extends EncounterPokemonRequirement { } getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { - if (this.requiredNature.includes(pokemon.nature)) { - return ["nature", Nature[pokemon.nature]]; + if (!isNullOrUndefined(pokemon?.nature) && this.requiredNature.includes(pokemon!.nature)) { + return ["nature", Nature[pokemon!.nature]]; } - return null; + return ["nature", ""]; } } @@ -537,9 +537,9 @@ export class EvolutionTargetSpeciesRequirement extends EncounterPokemonRequireme getMatchingDialogueToken(str:string, pokemon: PlayerPokemon): [RegExp, string] { const evos = this.requiredEvolutionTargetSpecies.filter((evolutionTargetSpecies) => pokemon.getEvolution().speciesId === evolutionTargetSpecies); if (evos.length > 0) { - return ["Evolution", Species[evos[0]]]; + return ["evolution", Species[evos[0]]]; } - return null; + return ["evolution", ""]; } }*/ @@ -622,7 +622,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement { return !this.requiredStatusEffect.some((statusEffect) => { if (statusEffect === StatusEffect.NONE) { // StatusEffect.NONE also checks for null or undefined status - return isNullOrUndefined(pokemon.status) || isNullOrUndefined(pokemon.status.effect) || pokemon.status?.effect === statusEffect; + return isNullOrUndefined(pokemon.status) || isNullOrUndefined(pokemon.status!.effect) || pokemon.status?.effect === statusEffect; } else { return pokemon.status?.effect === statusEffect; } @@ -634,9 +634,9 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement { getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { const reqStatus = this.requiredStatusEffect.filter((a) => { if (a === StatusEffect.NONE) { - return isNullOrUndefined(pokemon.status) || isNullOrUndefined(pokemon.status.effect) || pokemon.status?.effect === a; + return isNullOrUndefined(pokemon?.status) || isNullOrUndefined(pokemon!.status!.effect) || pokemon!.status!.effect === a; } - return pokemon.status?.effect === a; + return pokemon!.status?.effect === a; }); if (reqStatus.length > 0) { return ["status", StatusEffect[reqStatus[0]]]; @@ -698,7 +698,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen if (requiredItems.length > 0) { return ["formChangeItem", FormChangeItem[requiredItems[0]]]; } - return null; + return ["formChangeItem", ""]; } } @@ -748,7 +748,7 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement { if (requiredItems.length > 0) { return ["evolutionItem", EvolutionItem[requiredItems[0]]]; } - return null; + return ["evolutionItem", ""]; } } @@ -789,18 +789,18 @@ export class HeldItemRequirement extends EncounterPokemonRequirement { } getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { - const requiredItems = pokemon.getHeldItems().filter((it) => { + const requiredItems = pokemon?.getHeldItems().filter((it) => { return this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem); }); - if (requiredItems.length > 0) { + if (requiredItems && requiredItems.length > 0) { return ["heldItem", requiredItems[0].type.name]; } - return null; + return ["heldItem", ""]; } } export class LevelRequirement extends EncounterPokemonRequirement { - requiredLevelRange?: [number, number]; + requiredLevelRange: [number, number]; minNumberOfPokemon: number; invertQuery: boolean; @@ -809,12 +809,11 @@ export class LevelRequirement extends EncounterPokemonRequirement { this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; this.requiredLevelRange = requiredLevelRange; - } meetsRequirement(scene: BattleScene): boolean { // Party Pokemon inside required level range - if (!isNullOrUndefined(this?.requiredLevelRange) && this.requiredLevelRange?.[0] <= this.requiredLevelRange?.[1]) { + if (!isNullOrUndefined(this.requiredLevelRange) && this.requiredLevelRange[0] <= this.requiredLevelRange[1]) { const partyPokemon = scene.getParty(); const pokemonInRange = this.queryParty(partyPokemon); if (pokemonInRange.length < this.minNumberOfPokemon) { @@ -834,12 +833,12 @@ export class LevelRequirement extends EncounterPokemonRequirement { } getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { - return ["level", pokemon.level.toString()]; + return ["level", pokemon?.level.toString() ?? ""]; } } export class FriendshipRequirement extends EncounterPokemonRequirement { - requiredFriendshipRange?: [number, number]; + requiredFriendshipRange: [number, number]; minNumberOfPokemon: number; invertQuery: boolean; @@ -852,7 +851,7 @@ export class FriendshipRequirement extends EncounterPokemonRequirement { meetsRequirement(scene: BattleScene): boolean { // Party Pokemon inside required friendship range - if (!isNullOrUndefined(this?.requiredFriendshipRange) && this.requiredFriendshipRange?.[0] <= this.requiredFriendshipRange?.[1]) { + if (!isNullOrUndefined(this.requiredFriendshipRange) && this.requiredFriendshipRange[0] <= this.requiredFriendshipRange[1]) { const partyPokemon = scene.getParty(); const pokemonInRange = this.queryParty(partyPokemon); if (pokemonInRange.length < this.minNumberOfPokemon) { @@ -872,7 +871,7 @@ export class FriendshipRequirement extends EncounterPokemonRequirement { } getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { - return ["friendship", pokemon.friendship.toString()]; + return ["friendship", pokemon?.friendship.toString() ?? ""]; } } @@ -882,7 +881,7 @@ export class FriendshipRequirement extends EncounterPokemonRequirement { * 1 -> 100% hp */ export class HealthRatioRequirement extends EncounterPokemonRequirement { - requiredHealthRange?: [number, number]; + requiredHealthRange: [number, number]; minNumberOfPokemon: number; invertQuery: boolean; @@ -895,7 +894,7 @@ export class HealthRatioRequirement extends EncounterPokemonRequirement { meetsRequirement(scene: BattleScene): boolean { // Party Pokemon inside required level range - if (!isNullOrUndefined(this?.requiredHealthRange) && this.requiredHealthRange?.[0] <= this.requiredHealthRange?.[1]) { + if (!isNullOrUndefined(this.requiredHealthRange) && this.requiredHealthRange[0] <= this.requiredHealthRange[1]) { const partyPokemon = scene.getParty(); const pokemonInRange = this.queryParty(partyPokemon); if (pokemonInRange.length < this.minNumberOfPokemon) { @@ -917,12 +916,15 @@ export class HealthRatioRequirement extends EncounterPokemonRequirement { } getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { - return ["healthRatio", Math.floor(pokemon.getHpRatio() * 100).toString() + "%"]; + if (!isNullOrUndefined(pokemon?.getHpRatio())) { + return ["healthRatio", Math.floor(pokemon!.getHpRatio() * 100).toString() + "%"]; + } + return ["healthRatio", ""]; } } export class WeightRequirement extends EncounterPokemonRequirement { - requiredWeightRange?: [number, number]; + requiredWeightRange: [number, number]; minNumberOfPokemon: number; invertQuery: boolean; @@ -935,7 +937,7 @@ export class WeightRequirement extends EncounterPokemonRequirement { meetsRequirement(scene: BattleScene): boolean { // Party Pokemon inside required friendship range - if (!isNullOrUndefined(this?.requiredWeightRange) && this.requiredWeightRange?.[0] <= this.requiredWeightRange?.[1]) { + if (!isNullOrUndefined(this.requiredWeightRange) && this.requiredWeightRange[0] <= this.requiredWeightRange[1]) { const partyPokemon = scene.getParty(); const pokemonInRange = this.queryParty(partyPokemon); if (pokemonInRange.length < this.minNumberOfPokemon) { @@ -955,7 +957,7 @@ export class WeightRequirement extends EncounterPokemonRequirement { } getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { - return ["weight", pokemon.getWeight().toString()]; + return ["weight", pokemon?.getWeight().toString() ?? ""]; } } diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index cae68150255..3db97bf1f98 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -24,14 +24,41 @@ export interface EncounterStartOfBattleEffect { followUp?: boolean; } -interface IMysteryEncounter { - meetsRequirements(scene: BattleScene): boolean; - pokemonMeetsPrimaryRequirements(scene: BattleScene, pokemon: Pokemon): boolean; - initIntroVisuals(scene: BattleScene): void; - populateDialogueTokensFromRequirements(scene: BattleScene): void; - setDialogueToken(key: string, value: string): void; - getSeedOffset(): number; - updateSeedOffset(scene: BattleScene): void; +/** + * Used by {@link MysteryEncounterBuilder} class to define required/optional properties on the {@link MysteryEncounter} class when building. + * + * Should ONLY contain properties that are necessary for {@link MysteryEncounter} construction. + * Post-construct and flag data properties are defined in the {@link MysteryEncounter} class itself. + */ +export interface IMysteryEncounter { + encounterType: MysteryEncounterType; + options: [MysteryEncounterOption, MysteryEncounterOption, ...MysteryEncounterOption[]]; + spriteConfigs: MysteryEncounterSpriteConfig[]; + encounterTier: MysteryEncounterTier; + encounterAnimations?: EncounterAnim[]; + hideBattleIntroMessage: boolean; + autoHideIntroVisuals: boolean; + enterIntroVisualsFromRight: boolean; + catchAllowed: boolean; + continuousEncounter: boolean; + maxAllowedEncounters: number; + + onInit?: (scene: BattleScene) => boolean; + onVisualsStart?: (scene: BattleScene) => boolean; + doEncounterExp?: (scene: BattleScene) => boolean; + doEncounterRewards?: (scene: BattleScene) => boolean; + doContinueEncounter?: (scene: BattleScene) => Promise; + + requirements: EncounterSceneRequirement[]; + primaryPokemonRequirements: EncounterPokemonRequirement[]; + secondaryPokemonRequirements: EncounterPokemonRequirement[]; + excludePrimaryFromSupportRequirements: boolean; + + dialogue: MysteryEncounterDialogue; + enemyPartyConfigs: EnemyPartyConfig[]; + + dialogueTokens: Record; + expMultiplier: number; } /** @@ -147,7 +174,7 @@ export default class MysteryEncounter implements IMysteryEncounter { * Can be set for uses programatic dialogue during an encounter (storing the name of one of the party's pokemon, etc.) * Example use: see MYSTERIOUS_CHEST */ - dialogueTokens: Map; + dialogueTokens: Record; /** * Should be set depending upon option selected as part of an encounter * For example, if there is no battle as part of the encounter/selected option, should be set to NO_BATTLE @@ -171,7 +198,7 @@ export default class MysteryEncounter implements IMysteryEncounter { /** * Will be set by option select handlers automatically, and can be used to refer to which option was chosen by later phases */ - startOfBattleEffects?: EncounterStartOfBattleEffect[]; + startOfBattleEffects: EncounterStartOfBattleEffect[] = []; /** * Can be set higher or lower based on the type of battle or exp gained for an option/encounter * Defaults to 1 @@ -188,7 +215,7 @@ export default class MysteryEncounter implements IMysteryEncounter { */ private seedOffset?: any; - constructor(encounter: MysteryEncounter | null) { + constructor(encounter: IMysteryEncounter | null) { if (!isNullOrUndefined(encounter)) { Object.assign(this, encounter); } @@ -207,9 +234,9 @@ export default class MysteryEncounter implements IMysteryEncounter { // Reset any dirty flags or encounter data this.startOfBattleEffectsComplete = false; this.lockEncounterRewardTiers = true; - this.dialogueTokens = new Map(); + this.dialogueTokens = {}; this.enemyPartyConfigs = []; - this.startOfBattleEffects = []; + // this.startOfBattleEffects = []; this.introVisuals = undefined; this.misc = null; this.expMultiplier = 1; @@ -361,7 +388,7 @@ export default class MysteryEncounter implements IMysteryEncounter { const opt = this.options[i]; opt.meetsRequirements(scene); const j = i + 1; - if (opt.requirements?.length > 0) { + if (opt.requirements.length > 0) { for (const req of opt.requirements) { const dialogueToken = req.getDialogueToken(scene); if (dialogueToken?.length === 2) { @@ -369,7 +396,7 @@ export default class MysteryEncounter implements IMysteryEncounter { } } } - if (opt.primaryPokemonRequirements?.length > 0 && opt.primaryPokemon && opt.primaryPokemon.length > 0) { + if (opt.primaryPokemonRequirements.length > 0 && opt.primaryPokemon) { this.setDialogueToken("option" + j + "PrimaryName", opt.primaryPokemon.getNameToRender()); for (const req of opt.primaryPokemonRequirements) { if (!req.invertQuery) { @@ -424,26 +451,16 @@ export default class MysteryEncounter implements IMysteryEncounter { * Builder class for creating a MysteryEncounter * must call `build()` at the end after specifying all params for the MysteryEncounter */ -export class MysteryEncounterBuilder implements Partial { - encounterType: MysteryEncounterType; - encounterMode: MysteryEncounterMode; - options: [MysteryEncounterOption, MysteryEncounterOption, ...MysteryEncounterOption[]] = [new MysteryEncounterOption(null), new MysteryEncounterOption(null)]; - spriteConfigs: MysteryEncounterSpriteConfig[]; +export class MysteryEncounterBuilder implements Partial { + options: [MysteryEncounterOption, MysteryEncounterOption, ...MysteryEncounterOption[]]; enemyPartyConfigs: EnemyPartyConfig[] = []; dialogue: MysteryEncounterDialogue = {}; - encounterTier: MysteryEncounterTier; - encounterAnimations: EncounterAnim[]; requirements: EncounterSceneRequirement[] = []; primaryPokemonRequirements: EncounterPokemonRequirement[] = []; secondaryPokemonRequirements: EncounterPokemonRequirement[] = []; - excludePrimaryFromSupportRequirements: boolean; - dialogueTokens: Map = new Map(); - - doEncounterExp?: (scene: BattleScene) => boolean; - doEncounterRewards?: (scene: BattleScene) => boolean; - onInit?: (scene: BattleScene) => boolean; - onVisualsStart?: (scene: BattleScene) => boolean; + excludePrimaryFromSupportRequirements: boolean = true; + dialogueTokens: Record = {}; hideBattleIntroMessage: boolean = false; autoHideIntroVisuals: boolean = true; @@ -455,21 +472,6 @@ export class MysteryEncounterBuilder implements Partial { maxAllowedEncounters: number = 3; expMultiplier: number = 1; - /** - * Builder class has to re-declare the {@link MysteryEncounter} class functions so - * the compiler does not yell about user not defining a non-optional property - */ - - meetsRequirements = MysteryEncounter.prototype["meetsRequirements"]; - pokemonMeetsPrimaryRequirements = MysteryEncounter.prototype["pokemonMeetsPrimaryRequirements"]; - initIntroVisuals = MysteryEncounter.prototype["initIntroVisuals"]; - populateDialogueTokensFromRequirements = MysteryEncounter.prototype["populateDialogueTokensFromRequirements"]; - setDialogueToken = MysteryEncounter.prototype["setDialogueToken"]; - getSeedOffset = MysteryEncounter.prototype["getSeedOffset"]; - updateSeedOffset = MysteryEncounter.prototype["updateSeedOffset"]; - meetsPrimaryRequirementAndPrimaryPokemonSelected = MysteryEncounter.prototype["meetsPrimaryRequirementAndPrimaryPokemonSelected"]; - meetsSecondaryRequirementAndSecondaryPokemonSelected = MysteryEncounter.prototype["meetsSecondaryRequirementAndSecondaryPokemonSelected"]; - /** * REQUIRED */ @@ -480,7 +482,7 @@ export class MysteryEncounterBuilder implements Partial { * @param encounterType * @returns this */ - static withEncounterType(encounterType: MysteryEncounterType): MysteryEncounterBuilder & Pick { + static withEncounterType(encounterType: MysteryEncounterType): MysteryEncounterBuilder & Pick { return Object.assign(new MysteryEncounterBuilder(), { encounterType }); } @@ -492,14 +494,13 @@ export class MysteryEncounterBuilder implements Partial { * @param option - MysteryEncounterOption to add, can use MysteryEncounterOptionBuilder to create instance * @returns */ - withOption(option: MysteryEncounterOption): this & Pick { - if (this.options[0] === null) { - return Object.assign(this, { options: [option, this.options[0]] }); - } else if (this.options[1] === null) { - return Object.assign(this, { options: [this.options[0], option] }); + withOption(option: MysteryEncounterOption): this & Pick { + if (!this.options) { + const options = [option]; + return Object.assign(this, { options }); } else { this.options.push(option); - return Object.assign(this, { options: this.options }); + return this; } } @@ -514,7 +515,7 @@ export class MysteryEncounterBuilder implements Partial { * @param callback - {@linkcode OptionPhaseCallback} * @returns */ - withSimpleOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback): this & Pick { + withSimpleOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback): this & Pick { return this.withOption(MysteryEncounterOptionBuilder.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT).withDialogue(dialogue).withOptionPhase(callback).build()); } @@ -528,7 +529,7 @@ export class MysteryEncounterBuilder implements Partial { * @param callback - {@linkcode OptionPhaseCallback} * @returns */ - withSimpleDexProgressOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback): this & Pick { + withSimpleDexProgressOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback): this & Pick { return this.withOption(MysteryEncounterOptionBuilder .newOptionWithMode(MysteryEncounterOptionMode.DEFAULT) .withHasDexProgress(true) @@ -542,7 +543,7 @@ export class MysteryEncounterBuilder implements Partial { * @param spriteConfigs * @returns */ - withIntroSpriteConfigs(spriteConfigs: MysteryEncounterSpriteConfig[]): this & Pick { + withIntroSpriteConfigs(spriteConfigs: MysteryEncounterSpriteConfig[]): this & Pick { return Object.assign(this, { spriteConfigs: spriteConfigs }); } @@ -571,7 +572,7 @@ export class MysteryEncounterBuilder implements Partial { * @param encounterTier * @returns */ - withEncounterTier(encounterTier: MysteryEncounterTier): this & Pick { + withEncounterTier(encounterTier: MysteryEncounterTier): this & Pick { return Object.assign(this, { encounterTier: encounterTier }); } @@ -582,7 +583,7 @@ export class MysteryEncounterBuilder implements Partial { * @param encounterAnimations * @returns */ - withAnimations(...encounterAnimations: EncounterAnim[]): this & Required> { + withAnimations(...encounterAnimations: EncounterAnim[]): this & Required> { const animations = Array.isArray(encounterAnimations) ? encounterAnimations : [encounterAnimations]; return Object.assign(this, { encounterAnimations: animations }); } @@ -593,7 +594,7 @@ export class MysteryEncounterBuilder implements Partial { * Default false * @param continuousEncounter */ - withContinuousEncounter(continuousEncounter: boolean): this & Required> { + withContinuousEncounter(continuousEncounter: boolean): this & Required> { return Object.assign(this, { continuousEncounter: continuousEncounter }); } @@ -602,7 +603,7 @@ export class MysteryEncounterBuilder implements Partial { * @param maxAllowedEncounters * @returns */ - withMaxAllowedEncounters(maxAllowedEncounters: number): this & Required> { + withMaxAllowedEncounters(maxAllowedEncounters: number): this & Required> { return Object.assign(this, { maxAllowedEncounters: maxAllowedEncounters }); } @@ -613,12 +614,12 @@ export class MysteryEncounterBuilder implements Partial { * @param requirement * @returns */ - withSceneRequirement(requirement: EncounterSceneRequirement): this & Required> { + withSceneRequirement(requirement: EncounterSceneRequirement): this & Required> { if (requirement instanceof EncounterPokemonRequirement) { Error("Incorrectly added pokemon requirement as scene requirement."); } this.requirements.push(requirement); - return Object.assign(this, { requirements: this.requirements }); + return this; } /** @@ -628,7 +629,7 @@ export class MysteryEncounterBuilder implements Partial { * @param max optional max wave. If not given, defaults to min => exact wave * @returns */ - withSceneWaveRangeRequirement(min: number, max?: number): this & Required> { + withSceneWaveRangeRequirement(min: number, max?: number): this & Required> { return this.withSceneRequirement(new WaveRangeRequirement([min, max ?? min])); } @@ -640,7 +641,7 @@ export class MysteryEncounterBuilder implements Partial { * @param excludeFainted - if true, only counts unfainted mons * @returns */ - withScenePartySizeRequirement(min: number, max?: number, excludeFainted: boolean = false): this & Required> { + withScenePartySizeRequirement(min: number, max?: number, excludeFainted: boolean = false): this & Required> { return this.withSceneRequirement(new PartySizeRequirement([min, max ?? min], excludeFainted)); } @@ -650,7 +651,7 @@ export class MysteryEncounterBuilder implements Partial { * @param requirement {@linkcode EncounterPokemonRequirement} * @returns */ - withPrimaryPokemonRequirement(requirement: EncounterPokemonRequirement): this & Required> { + withPrimaryPokemonRequirement(requirement: EncounterPokemonRequirement): this & Required> { if (requirement instanceof EncounterSceneRequirement) { Error("Incorrectly added scene requirement as pokemon requirement."); } @@ -667,7 +668,7 @@ export class MysteryEncounterBuilder implements Partial { * @param invertQuery if true will invert the query * @returns */ - withPrimaryPokemonStatusEffectRequirement(statusEffect: StatusEffect | StatusEffect[], minNumberOfPokemon: number = 1, invertQuery: boolean = false): this & Required> { + withPrimaryPokemonStatusEffectRequirement(statusEffect: StatusEffect | StatusEffect[], minNumberOfPokemon: number = 1, invertQuery: boolean = false): this & Required> { return this.withPrimaryPokemonRequirement(new StatusEffectRequirement(statusEffect, minNumberOfPokemon, invertQuery)); } @@ -679,14 +680,14 @@ export class MysteryEncounterBuilder implements Partial { * @param invertQuery if true will invert the query * @returns */ - withPrimaryPokemonHealthRatioRequirement(requiredHealthRange: [number, number], minNumberOfPokemon: number = 1, invertQuery: boolean = false): this & Required> { + withPrimaryPokemonHealthRatioRequirement(requiredHealthRange: [number, number], minNumberOfPokemon: number = 1, invertQuery: boolean = false): this & Required> { return this.withPrimaryPokemonRequirement(new HealthRatioRequirement(requiredHealthRange, minNumberOfPokemon, invertQuery)); } // TODO: Maybe add an optional parameter for excluding primary pokemon from the support cast? // ex. if your only grass type pokemon, a snivy, is chosen as primary, if the support pokemon requires a grass type, the event won't trigger because // it's already been - withSecondaryPokemonRequirement(requirement: EncounterPokemonRequirement, excludePrimaryFromSecondaryRequirements: boolean = false): this & Required> { + withSecondaryPokemonRequirement(requirement: EncounterPokemonRequirement, excludePrimaryFromSecondaryRequirements: boolean = false): this & Required> { if (requirement instanceof EncounterSceneRequirement) { Error("Incorrectly added scene requirement as pokemon requirement."); } @@ -706,7 +707,7 @@ export class MysteryEncounterBuilder implements Partial { * @param doEncounterRewards - synchronous callback function to perform during rewards phase of the encounter * @returns */ - withRewards(doEncounterRewards: (scene: BattleScene) => boolean): this & Required> { + withRewards(doEncounterRewards: (scene: BattleScene) => boolean): this & Required> { return Object.assign(this, { doEncounterRewards: doEncounterRewards }); } @@ -720,7 +721,7 @@ export class MysteryEncounterBuilder implements Partial { * @param doEncounterExp - synchronous callback function to perform during rewards phase of the encounter * @returns */ - withExp(doEncounterExp: (scene: BattleScene) => boolean): this & Required> { + withExp(doEncounterExp: (scene: BattleScene) => boolean): this & Required> { return Object.assign(this, { doEncounterExp: doEncounterExp }); } @@ -731,8 +732,8 @@ export class MysteryEncounterBuilder implements Partial { * @param onInit - synchronous callback function to perform as soon as the encounter is selected for the next phase * @returns */ - withOnInit(onInit: (scene: BattleScene) => boolean): this & Required> { - return Object.assign(this, { onInit: onInit }); + withOnInit(onInit: (scene: BattleScene) => boolean): this & Required> { + return Object.assign(this, { onInit }); } /** @@ -741,7 +742,7 @@ export class MysteryEncounterBuilder implements Partial { * @param onVisualsStart - synchronous callback function to perform as soon as the enemy field finishes sliding in * @returns */ - withOnVisualsStart(onVisualsStart: (scene: BattleScene) => boolean): this & Required> { + withOnVisualsStart(onVisualsStart: (scene: BattleScene) => boolean): this & Required> { return Object.assign(this, { onVisualsStart: onVisualsStart }); } @@ -751,7 +752,7 @@ export class MysteryEncounterBuilder implements Partial { * @param catchAllowed - if true, allows enemy pokemon to be caught during the encounter * @returns */ - withCatchAllowed(catchAllowed: boolean): this & Required> { + withCatchAllowed(catchAllowed: boolean): this & Required> { return Object.assign(this, { catchAllowed: catchAllowed }); } @@ -759,7 +760,7 @@ export class MysteryEncounterBuilder implements Partial { * @param hideBattleIntroMessage - if true, will not show the trainerAppeared/wildAppeared/bossAppeared message for an encounter * @returns */ - withHideWildIntroMessage(hideBattleIntroMessage: boolean): this & Required> { + withHideWildIntroMessage(hideBattleIntroMessage: boolean): this & Required> { return Object.assign(this, { hideBattleIntroMessage: hideBattleIntroMessage }); } @@ -767,7 +768,7 @@ export class MysteryEncounterBuilder implements Partial { * @param autoHideIntroVisuals - if false, will not hide the intro visuals that are displayed at the beginning of encounter * @returns */ - withAutoHideIntroVisuals(autoHideIntroVisuals: boolean): this & Required> { + withAutoHideIntroVisuals(autoHideIntroVisuals: boolean): this & Required> { return Object.assign(this, { autoHideIntroVisuals: autoHideIntroVisuals }); } @@ -776,7 +777,7 @@ export class MysteryEncounterBuilder implements Partial { * Default false * @returns */ - withEnterIntroVisualsFromRight(enterIntroVisualsFromRight: boolean): this & Required> { + withEnterIntroVisualsFromRight(enterIntroVisualsFromRight: boolean): this & Required> { return Object.assign(this, { enterIntroVisualsFromRight: enterIntroVisualsFromRight }); } @@ -856,7 +857,7 @@ export class MysteryEncounterBuilder implements Partial { * * @returns */ - build(this: MysteryEncounter): MysteryEncounter { + build(this: IMysteryEncounter): MysteryEncounter { return new MysteryEncounter(this); } } diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index 44f4ecf2420..b9e15302fe4 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -278,7 +278,7 @@ export function initMysteryEncounters() { extremeBiomeEncounters.forEach(encounter => { EXTREME_ENCOUNTER_BIOMES.forEach(biome => { const encountersForBiome = mysteryEncountersByBiome.get(biome); - if (!encountersForBiome.includes(encounter)) { + if (encountersForBiome && !encountersForBiome.includes(encounter)) { encountersForBiome.push(encounter); } }); @@ -287,7 +287,7 @@ export function initMysteryEncounters() { nonExtremeBiomeEncounters.forEach(encounter => { NON_EXTREME_ENCOUNTER_BIOMES.forEach(biome => { const encountersForBiome = mysteryEncountersByBiome.get(biome); - if (!encountersForBiome.includes(encounter)) { + if (encountersForBiome && !encountersForBiome.includes(encounter)) { encountersForBiome.push(encounter); } }); @@ -296,7 +296,7 @@ export function initMysteryEncounters() { humanTransitableBiomeEncounters.forEach(encounter => { HUMAN_TRANSITABLE_BIOMES.forEach(biome => { const encountersForBiome = mysteryEncountersByBiome.get(biome); - if (!encountersForBiome.includes(encounter)) { + if (encountersForBiome && !encountersForBiome.includes(encounter)) { encountersForBiome.push(encounter); } }); @@ -305,7 +305,7 @@ export function initMysteryEncounters() { civilizationBiomeEncounters.forEach(encounter => { CIVILIZATION_ENCOUNTER_BIOMES.forEach(biome => { const encountersForBiome = mysteryEncountersByBiome.get(biome); - if (!encountersForBiome.includes(encounter)) { + if (encountersForBiome && !encountersForBiome.includes(encounter)) { encountersForBiome.push(encounter); } }); diff --git a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts index fb1d65342c1..9a0447d816d 100644 --- a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts +++ b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts @@ -1,6 +1,6 @@ import BattleScene from "#app/battle-scene"; import { Moves } from "#app/enums/moves"; -import { PlayerPokemon } from "#app/field/pokemon"; +import { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import { isNullOrUndefined } from "#app/utils"; import { EncounterPokemonRequirement } from "../mystery-encounter-requirements"; @@ -37,7 +37,7 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement { this.excludeEggMoves = excludeEggMoves ?? false; this.includeFainted = includeFainted ?? false; this.minNumberOfPokemon = minNumberOfPokemon ?? 1; - this.invertQuery = invertQuery; + this.invertQuery = invertQuery ?? false; } override meetsRequirement(scene: BattleScene): boolean { @@ -66,7 +66,7 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement { } override getDialogueToken(_scene: BattleScene, _pokemon?: PlayerPokemon): [string, string] { - return ["requiredMoves", this.requiredMoves.join(", ")]; + return ["requiredMoves", this.requiredMoves.map(m => new PokemonMove(m).getName()).join(", ")]; } private getPokemonLevelMoves(pkm: PlayerPokemon): Moves[] { diff --git a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts index 382b2c7dde8..2ed2696182b 100644 --- a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts @@ -4,34 +4,37 @@ import { UiTheme } from "#enums/ui-theme"; import { isNullOrUndefined } from "#app/utils"; import i18next from "i18next"; -export function getEncounterText(scene: BattleScene, keyOrString: string, primaryStyle?: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string { +export function getEncounterText(scene: BattleScene, keyOrString?: string, primaryStyle?: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string | null { if (isNullOrUndefined(keyOrString)) { return null; } - let textString: string = getTextWithDialogueTokens(scene, keyOrString); + let textString: string | null = getTextWithDialogueTokens(scene, keyOrString); // Can only color the text if a Primary Style is defined // primaryStyle is applied to all text that does not have its own specified style - if (primaryStyle) { + if (primaryStyle && textString) { textString = getTextWithColors(textString, primaryStyle, uiTheme); } return textString; } -function getTextWithDialogueTokens(scene: BattleScene, keyOrString: string): string { +function getTextWithDialogueTokens(scene: BattleScene, keyOrString?: string): string | null { if (isNullOrUndefined(keyOrString)) { return null; } - if (i18next.exists(keyOrString, scene.currentBattle?.mysteryEncounter?.dialogueTokens)) { + const tokens = scene.currentBattle?.mysteryEncounter?.dialogueTokens; + // @ts-ignore + if (i18next.exists(keyOrString, tokens)) { const stringArray = [`${keyOrString}`] as any; stringArray.raw = [`${keyOrString}`]; - return i18next.t(stringArray, scene.currentBattle?.mysteryEncounter?.dialogueTokens); + // @ts-ignore + return i18next.t(stringArray, tokens) as string; } - return keyOrString; + return keyOrString ?? null; } /** @@ -40,8 +43,8 @@ function getTextWithDialogueTokens(scene: BattleScene, keyOrString: string): str * @param contentKey */ export function queueEncounterMessage(scene: BattleScene, contentKey: string): void { - const text: string = getEncounterText(scene, contentKey); - scene.queueMessage(text, null, true); + const text: string | null = getEncounterText(scene, contentKey); + scene.queueMessage(text ?? "", null, true); } /** @@ -53,8 +56,8 @@ export function queueEncounterMessage(scene: BattleScene, contentKey: string): v */ export function showEncounterText(scene: BattleScene, contentKey: string, callbackDelay: number = 0, prompt: boolean = true): Promise { return new Promise(resolve => { - const text: string = getEncounterText(scene, contentKey); - scene.ui.showText(text, null, () => resolve(), callbackDelay, prompt); + const text: string | null = getEncounterText(scene, contentKey); + scene.ui.showText(text ?? "", null, () => resolve(), callbackDelay, prompt); }); } @@ -67,8 +70,8 @@ export function showEncounterText(scene: BattleScene, contentKey: string, callba */ export function showEncounterDialogue(scene: BattleScene, textContentKey: string, speakerContentKey: string, callbackDelay: number = 0): Promise { return new Promise(resolve => { - const text: string = getEncounterText(scene, textContentKey); - const speaker: string = getEncounterText(scene, speakerContentKey); - scene.ui.showDialogue(text, speaker, null, () => resolve(), callbackDelay); + const text: string | null = getEncounterText(scene, textContentKey); + const speaker: string | null = getEncounterText(scene, speakerContentKey); + scene.ui.showDialogue(text ?? "", speaker ?? "", null, () => resolve(), callbackDelay); }); } diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index c58e0fbad1f..9d74fa79ff0 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -1,4 +1,4 @@ -import { BattlerIndex, BattleType } from "#app/battle"; +import Battle, { BattlerIndex, BattleType } from "#app/battle"; import { biomeLinks, BiomePoolTier } from "#app/data/biomes"; import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters"; @@ -10,7 +10,7 @@ import { BattleEndPhase, EggLapsePhase, ExpPhase, GameOverPhase, MovePhase, Sele import { MysteryEncounterBattlePhase, MysteryEncounterBattleStartCleanupPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases"; import PokemonData from "#app/system/pokemon-data"; import { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler"; +import { PartyOption, PartyUiMode, PokemonSelectFilter } from "#app/ui/party-ui-handler"; import { Mode } from "#app/ui/ui"; import * as Utils from "#app/utils"; import { isNullOrUndefined } from "#app/utils"; @@ -101,29 +101,30 @@ export interface EnemyPartyConfig { * @param partyConfig - Can pass various customizable attributes for the enemy party, see EnemyPartyConfig */ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: EnemyPartyConfig): Promise { - const loaded = false; - const loadEnemyAssets = []; + const loaded: boolean = false; + const loadEnemyAssets: Promise[] = []; - const battle = scene.currentBattle; + const battle: Battle = scene.currentBattle; - let doubleBattle = partyConfig?.doubleBattle; + let doubleBattle: boolean = partyConfig?.doubleBattle ?? false; // Trainer const trainerType = partyConfig?.trainerType; - let trainerConfig = partyConfig?.trainerConfig; - if (trainerType || trainerConfig) { + const partyTrainerConfig = partyConfig?.trainerConfig; + let trainerConfig: TrainerConfig; + if (!isNullOrUndefined(trainerType) || partyTrainerConfig) { scene.currentBattle.mysteryEncounter.encounterMode = MysteryEncounterMode.TRAINER_BATTLE; if (scene.currentBattle.trainer) { scene.currentBattle.trainer.setVisible(false); scene.currentBattle.trainer.destroy(); } - trainerConfig = partyConfig?.trainerConfig ? partyConfig?.trainerConfig : trainerConfigs[trainerType]; + trainerConfig = partyConfig?.trainerConfig ? partyConfig?.trainerConfig : trainerConfigs[trainerType!]; - const doubleTrainer = trainerConfig.doubleOnly || (trainerConfig.hasDouble && partyConfig.doubleBattle); + const doubleTrainer = trainerConfig.doubleOnly || (trainerConfig.hasDouble && !!partyConfig.doubleBattle); doubleBattle = doubleTrainer; const trainerFemale = isNullOrUndefined(partyConfig.female) ? !!(Utils.randSeedInt(2)) : partyConfig.female; - const newTrainer = new Trainer(scene, trainerConfig.trainerType, doubleTrainer ? TrainerVariant.DOUBLE : trainerFemale ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, null, null, null, trainerConfig); + const newTrainer = new Trainer(scene, trainerConfig.trainerType, doubleTrainer ? TrainerVariant.DOUBLE : trainerFemale ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, undefined, undefined, undefined, trainerConfig); newTrainer.x += 300; newTrainer.setVisible(false); scene.field.add(newTrainer); @@ -134,7 +135,8 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: } else { // Wild scene.currentBattle.mysteryEncounter.encounterMode = MysteryEncounterMode.WILD_BATTLE; - battle.enemyLevels = new Array(partyConfig?.pokemonConfigs?.length > 0 ? partyConfig?.pokemonConfigs?.length : doubleBattle ? 2 : 1).fill(null).map(() => scene.currentBattle.getLevelForWave()); + const numEnemies = partyConfig?.pokemonConfigs && partyConfig.pokemonConfigs.length > 0 ? partyConfig?.pokemonConfigs?.length : doubleBattle ? 2 : 1; + battle.enemyLevels = new Array(numEnemies).fill(null).map(() => scene.currentBattle.getLevelForWave()); } scene.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy()); @@ -146,7 +148,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: // This can be amplified or counteracted by setting levelAdditiveMultiplier in config // levelAdditiveMultiplier value of 0.5 will halve the modifier scaling, 2 will double it, etc. // Leaving null/undefined will disable level scaling - const mult = !isNullOrUndefined(partyConfig.levelAdditiveMultiplier) ? partyConfig.levelAdditiveMultiplier : 0; + const mult: number = !isNullOrUndefined(partyConfig.levelAdditiveMultiplier) ? partyConfig.levelAdditiveMultiplier! : 0; const additive = Math.max(Math.round((scene.currentBattle.waveIndex / 10) * mult), 0); battle.enemyLevels = battle.enemyLevels.map(level => level + additive); @@ -155,10 +157,10 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: let dataSource; let isBoss = false; if (!loaded) { - if (trainerType || trainerConfig) { + if ((!isNullOrUndefined(trainerType) || trainerConfig) && battle.trainer) { // Allows overriding a trainer's pokemon to use specific species/data - if (e < partyConfig?.pokemonConfigs?.length) { - const config = partyConfig?.pokemonConfigs?.[e]; + if (partyConfig?.pokemonConfigs && e < partyConfig.pokemonConfigs.length) { + const config = partyConfig.pokemonConfigs[e]; level = config.level ? config.level : level; dataSource = config.dataSource; enemySpecies = config.species; @@ -168,8 +170,8 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: battle.enemyParty[e] = battle.trainer.genPartyMember(e); } } else { - if (e < partyConfig?.pokemonConfigs?.length) { - const config = partyConfig?.pokemonConfigs?.[e]; + if (partyConfig?.pokemonConfigs && e < partyConfig.pokemonConfigs.length) { + const config = partyConfig.pokemonConfigs[e]; level = config.level ? config.level : level; dataSource = config.dataSource; enemySpecies = config.species; @@ -201,8 +203,8 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: scene.gameData.setPokemonSeen(enemyPokemon, true, !!(trainerType || trainerConfig)); } - if (e < partyConfig?.pokemonConfigs?.length) { - const config = partyConfig?.pokemonConfigs?.[e]; + if (partyConfig?.pokemonConfigs && e < partyConfig.pokemonConfigs.length) { + const config = partyConfig.pokemonConfigs[e]; // Generate new id, reset status and HP in case using data source if (config.dataSource) { @@ -211,24 +213,24 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: // Set form if (!isNullOrUndefined(config.formIndex)) { - enemyPokemon.formIndex = config.formIndex; + enemyPokemon.formIndex = config.formIndex!; } // Set shiny if (!isNullOrUndefined(config.shiny)) { - enemyPokemon.shiny = config.shiny; + enemyPokemon.shiny = config.shiny!; } // Set custom mystery encounter data fields (such as sprite scale, custom abilities, types, etc.) if (!isNullOrUndefined(config.mysteryEncounterData)) { - enemyPokemon.mysteryEncounterData = config.mysteryEncounterData; + enemyPokemon.mysteryEncounterData = config.mysteryEncounterData!; } // Set Boss if (config.isBoss) { - let segments = !isNullOrUndefined(config.bossSegments) ? config.bossSegments : scene.getEncounterBossSegments(scene.currentBattle.waveIndex, level, enemySpecies, true); + let segments = !isNullOrUndefined(config.bossSegments) ? config.bossSegments! : scene.getEncounterBossSegments(scene.currentBattle.waveIndex, level, enemySpecies, true); if (!isNullOrUndefined(config.bossSegmentModifier)) { - segments += config.bossSegmentModifier; + segments += config.bossSegmentModifier!; } enemyPokemon.setBoss(true, segments); } @@ -253,7 +255,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: if (statusEffects) { // Default to cureturn 3 for sleep const status = Array.isArray(statusEffects) ? statusEffects[0] : statusEffects; - const cureTurn = Array.isArray(statusEffects) ? statusEffects[1] : statusEffects === StatusEffect.SLEEP ? 3 : null; + const cureTurn = Array.isArray(statusEffects) ? statusEffects[1] : statusEffects === StatusEffect.SLEEP ? 3 : undefined; enemyPokemon.status = new Status(status, 0, cureTurn); } @@ -264,24 +266,24 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: // Set ability if (!isNullOrUndefined(config.abilityIndex)) { - enemyPokemon.abilityIndex = config.abilityIndex; + enemyPokemon.abilityIndex = config.abilityIndex!; } // Set gender if (!isNullOrUndefined(config.gender)) { - enemyPokemon.gender = config.gender; - enemyPokemon.summonData.gender = config.gender; + enemyPokemon.gender = config.gender!; + enemyPokemon.summonData.gender = config.gender!; } // Set moves - if (config?.moveSet?.length > 0) { + if (config?.moveSet && config.moveSet.length > 0) { const moves = config.moveSet.map(m => new PokemonMove(m)); enemyPokemon.moveset = moves; enemyPokemon.summonData.moveset = moves; } // Set tags - if (config.tags?.length > 0) { + if (config.tags && config.tags.length > 0) { const tags = config.tags; tags.forEach(tag => enemyPokemon.addTag(tag)); } @@ -319,8 +321,12 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: }); if (!loaded) { regenerateModifierPoolThresholds(scene.getEnemyField(), battle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD); - const customModifierTypes = partyConfig?.pokemonConfigs?.map(config => config?.modifierConfigs); - scene.generateEnemyModifiers(customModifierTypes); + const customModifierTypes = partyConfig?.pokemonConfigs + ?.filter(config => config?.modifierConfigs) + .map(config => config.modifierConfigs!); + if (customModifierTypes) { + scene.generateEnemyModifiers(customModifierTypes); + } } } @@ -368,7 +374,7 @@ export function updatePlayerMoney(scene: BattleScene, changeValue: number, playS * @param pregenArgs - can specify BerryType for berries, TM for TMs, AttackBoostType for item, etc. */ export function generateModifierType(scene: BattleScene, modifier: () => ModifierType, pregenArgs?: any[]): ModifierType { - const modifierId = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifier); + const modifierId = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifier)!; let result: ModifierType = modifierTypes[modifierId]?.(); // Populates item id and tier (order matters) @@ -376,8 +382,8 @@ export function generateModifierType(scene: BattleScene, modifier: () => Modifie .withIdFromFunc(modifierTypes[modifierId]) .withTierFromPool(); - result = result instanceof ModifierTypeGenerator ? result.generateType(scene.getParty(), pregenArgs) : result; - return result; + const generatedResult = result instanceof ModifierTypeGenerator ? result.generateType(scene.getParty(), pregenArgs) : result; + return generatedResult ?? result; } /** @@ -399,7 +405,7 @@ export function generateModifierTypeOption(scene: BattleScene, modifier: () => M * @param onPokemonNotSelected - Any logic that needs to be performed if no Pokemon is chosen * @param selectablePokemonFilter */ -export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (pokemon: PlayerPokemon) => void | OptionSelectItem[], onPokemonNotSelected?: () => void, selectablePokemonFilter?: (pokemon: PlayerPokemon) => string): Promise { +export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (pokemon: PlayerPokemon) => void | OptionSelectItem[], onPokemonNotSelected?: () => void, selectablePokemonFilter?: PokemonSelectFilter): Promise { return new Promise(resolve => { const modeToSetOnExit = scene.ui.getMode(); @@ -451,7 +457,7 @@ export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (p scene.ui.setModeWithoutClear(Mode.OPTION_SELECT, config, null, true); }; - const textPromptKey = scene.currentBattle.mysteryEncounter.selectedOption.dialogue.secondOptionPrompt; + const textPromptKey = scene.currentBattle.mysteryEncounter?.selectedOption?.dialogue?.secondOptionPrompt; if (!textPromptKey) { displayOptions(); } else { @@ -486,7 +492,7 @@ export function setEncounterRewards(scene: BattleScene, customShopRewards?: Cust } if (customShopRewards) { - scene.unshiftPhase(new SelectModifierPhase(scene, 0, null, customShopRewards)); + scene.unshiftPhase(new SelectModifierPhase(scene, 0, undefined, customShopRewards)); } else { scene.tryRemovePhase(p => p instanceof SelectModifierPhase); } @@ -530,7 +536,7 @@ export function setEncounterExp(scene: BattleScene, participantId: integer | int const multipleParticipantExpBonusModifier = scene.findModifier(m => m instanceof MultipleParticipantExpBonusModifier) as MultipleParticipantExpBonusModifier; const nonFaintedPartyMembers = party.filter(p => p.hp); const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < scene.getMaxExpLevel()); - const partyMemberExp = []; + const partyMemberExp: number[] = []; // EXP value calculation is based off Pokemon.getExpValue let expValue = Math.floor(baseExpValue * (useWaveIndex ? scene.currentBattle.waveIndex : 1) / 5 + 1); @@ -581,7 +587,7 @@ export function setEncounterExp(scene: BattleScene, participantId: integer | int const medianLevel = Math.floor(totalLevel / expPartyMembers.length); - const recipientExpPartyMemberIndexes = []; + const recipientExpPartyMemberIndexes: number[] = []; expPartyMembers.forEach((expPartyMember, epm) => { if (expPartyMember.level <= medianLevel) { recipientExpPartyMemberIndexes.push(epm); @@ -720,7 +726,7 @@ export function transitionMysteryEncounterIntroVisuals(scene: BattleScene, hide: scene.field.remove(pokemon, true); }); - scene.currentBattle.mysteryEncounter.introVisuals = null; + scene.currentBattle.mysteryEncounter.introVisuals = undefined; } resolve(true); } @@ -808,11 +814,13 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n }) .map(b => !Array.isArray(b) ? b : b[0]); }, i * 100); - const specialBiomes = biomes.filter(b => alwaysPickTheseBiomes.includes(b)); - if (specialBiomes.length > 0) { - currentBiome = specialBiomes[Utils.randSeedInt(specialBiomes.length)]; - } else { - currentBiome = biomes[Utils.randSeedInt(biomes.length)]; + if (biomes! && biomes.length > 0) { + const specialBiomes = biomes.filter(b => alwaysPickTheseBiomes.includes(b)); + if (specialBiomes.length > 0) { + currentBiome = specialBiomes[Utils.randSeedInt(specialBiomes.length)]; + } else { + currentBiome = biomes[Utils.randSeedInt(biomes.length)]; + } } } else if (biomeLinks.hasOwnProperty(currentBiome)) { currentBiome = (biomeLinks[currentBiome] as Biome); @@ -840,7 +848,7 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n // Otherwise, roll encounter const roll = Utils.randSeedInt(256); - validMEfloorsByBiome.set(Biome[currentBiome], validMEfloorsByBiome.get(Biome[currentBiome]) + 1); + validMEfloorsByBiome.set(Biome[currentBiome], (validMEfloorsByBiome.get(Biome[currentBiome]) ?? 0) + 1); // If total number of encounters is lower than expected for the run, slightly favor a new encounter // Do the reverse as well @@ -866,7 +874,7 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n const rareThreshold = totalWeight - tierWeights[0] - tierWeights[1] - tierWeights[2]; // 64 - 32 - 16 - 10 = 6 tierValue > commonThreshold ? ++numEncounters[0] : tierValue > uncommonThreshold ? ++numEncounters[1] : tierValue > rareThreshold ? ++numEncounters[2] : ++numEncounters[3]; - encountersByBiome.set(Biome[currentBiome], encountersByBiome.get(Biome[currentBiome]) + 1); + encountersByBiome.set(Biome[currentBiome], (encountersByBiome.get(Biome[currentBiome]) ?? 0) + 1); } else { encounterRate += WEIGHT_INCREMENT_ON_SPAWN_MISS; } @@ -899,7 +907,7 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n const encountersPerRunPerBiome = encountersByBiomeRuns.reduce((a, b) => { for (const biome of a.keys()) { - a.set(biome, a.get(biome) + b.get(biome)); + a.set(biome, a.get(biome)! + b.get(biome)!); } return a; }); @@ -910,7 +918,7 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n const validMEFloorsPerRunPerBiome = validFloorsByBiome.reduce((a, b) => { for (const biome of a.keys()) { - a.set(biome, a.get(biome) + b.get(biome)); + a.set(biome, a.get(biome)! + b.get(biome)!); } return a; }); diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index 307e0a095ff..81dff39da74 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -52,13 +52,13 @@ export function getRandomPlayerPokemon(scene: BattleScene, isAllowedInBattle: bo if (doNotReturnLastAbleMon && unfaintedMons.length === 1) { chosenIndex = randSeedInt(faintedMons.length); - chosenPokemon = faintedMons.at(chosenIndex); + chosenPokemon = faintedMons[chosenIndex]; } else if (isAllowedInBattle) { chosenIndex = randSeedInt(unfaintedMons.length); - chosenPokemon = unfaintedMons.at(chosenIndex); + chosenPokemon = unfaintedMons[chosenIndex]; } else { chosenIndex = randSeedInt(party.length); - chosenPokemon = party.at(chosenIndex); + chosenPokemon = party[chosenIndex]; } return chosenPokemon; @@ -72,17 +72,17 @@ export function getRandomPlayerPokemon(scene: BattleScene, isAllowedInBattle: bo */ export function getHighestLevelPlayerPokemon(scene: BattleScene, unfainted: boolean = false): PlayerPokemon { const party = scene.getParty(); - let pokemon: PlayerPokemon; - party.every(p => { + let pokemon: PlayerPokemon | null = null; + + for (const p of party) { if (unfainted && p.isFainted()) { - return true; + continue; } pokemon = pokemon ? pokemon?.level < p?.level ? p : pokemon : p; - return true; - }); + } - return pokemon; + return pokemon!; } /** @@ -94,17 +94,17 @@ export function getHighestLevelPlayerPokemon(scene: BattleScene, unfainted: bool */ export function getHighestStatPlayerPokemon(scene: BattleScene, stat: Stat, unfainted: boolean = false): PlayerPokemon { const party = scene.getParty(); - let pokemon: PlayerPokemon; - party.every(p => { + let pokemon: PlayerPokemon | null = null; + + for (const p of party) { if (unfainted && p.isFainted()) { - return true; + continue; } pokemon = pokemon ? pokemon.getStat(stat) < p?.getStat(stat) ? p : pokemon : p; - return true; - }); + } - return pokemon; + return pokemon!; } /** @@ -115,17 +115,17 @@ export function getHighestStatPlayerPokemon(scene: BattleScene, stat: Stat, unfa */ export function getLowestLevelPlayerPokemon(scene: BattleScene, unfainted: boolean = false): PlayerPokemon { const party = scene.getParty(); - let pokemon: PlayerPokemon; - party.every(p => { + let pokemon: PlayerPokemon | null = null; + + for (const p of party) { if (unfainted && p.isFainted()) { - return true; + continue; } pokemon = pokemon ? pokemon?.level > p?.level ? p : pokemon : p; - return true; - }); + } - return pokemon; + return pokemon!; } /** @@ -136,17 +136,17 @@ export function getLowestLevelPlayerPokemon(scene: BattleScene, unfainted: boole */ export function getHighestStatTotalPlayerPokemon(scene: BattleScene, unfainted: boolean = false): PlayerPokemon { const party = scene.getParty(); - let pokemon: PlayerPokemon; - party.every(p => { + let pokemon: PlayerPokemon | null = null; + + for (const p of party) { if (unfainted && p.isFainted()) { - return true; + continue; } pokemon = pokemon ? pokemon?.stats.reduce((a, b) => a + b) < p?.stats.reduce((a, b) => a + b) ? p : pokemon : p; - return true; - }); + } - return pokemon; + return pokemon!; } /** @@ -166,8 +166,8 @@ export function getRandomSpeciesByStarterTier(starterTiers: number | [number, nu .filter(s => getPokemonSpecies(s[0]) && (!excludedSpecies || !excludedSpecies.includes(s[0]))) .map(s => [getPokemonSpecies(s[0]), s[1]]); - if (!isNullOrUndefined(types) && types.length > 0) { - filteredSpecies = filteredSpecies.filter(s => types.includes(s[0].type1) || types.includes(s[0].type2)); + if (types && types.length > 0) { + filteredSpecies = filteredSpecies.filter(s => types.includes(s[0].type1) || (!isNullOrUndefined(s[0].type2) && types.includes(s[0].type2!))); } // If no filtered mons exist at specified starter tiers, will expand starter search range until there are @@ -260,10 +260,12 @@ export function applyHealToPokemon(scene: BattleScene, pokemon: PlayerPokemon, h * @param value */ export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: number) { - const modType = modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE().generateType(null, [value]); - const modifier = modType.newModifier(pokemon); - await pokemon.scene.addModifier(modifier, false, false, false, true); - pokemon.calculateStats(); + const modType = modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE().generateType(pokemon.scene.getParty(), [value]); + const modifier = modType?.newModifier(pokemon); + if (modifier) { + await pokemon.scene.addModifier(modifier, false, false, false, true); + pokemon.calculateStats(); + } } /** @@ -479,7 +481,7 @@ function failCatch(scene: BattleScene, pokemon: EnemyPokemon, originalY: number, * @param showCatchObtainMessage * @param isObtain */ -export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, pokeball: Phaser.GameObjects.Sprite, pokeballType: PokeballType, showCatchObtainMessage: boolean = true, isObtain: boolean = false): Promise { +export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, pokeball: Phaser.GameObjects.Sprite | null, pokeballType: PokeballType, showCatchObtainMessage: boolean = true, isObtain: boolean = false): Promise { scene.unshiftPhase(new VictoryPhase(scene, BattlerIndex.ENEMY, true)); const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm(); @@ -508,7 +510,9 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po const doPokemonCatchMenu = () => { const end = () => { scene.pokemonInfoContainer.hide(); - removePb(scene, pokeball); + if (pokeball) { + removePb(scene, pokeball); + } resolve(); }; const removePokemon = () => { diff --git a/src/field/arena.ts b/src/field/arena.ts index cf4e919abf5..da35f3f3281 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -146,7 +146,7 @@ export class Arena { return this.randomSpecies(waveIndex, level, (attempt || 0) + 1); } - const newSpeciesId = ret.getWildSpeciesForLevel(level, true, isBoss, this.scene.gameMode); + const newSpeciesId = ret.getWildSpeciesForLevel(level, true, !!isBoss, this.scene.gameMode); if (newSpeciesId !== ret.speciesId) { console.log("Replaced", Species[ret.speciesId], "with", Species[newSpeciesId]); ret = getPokemonSpecies(newSpeciesId); diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index 1192e1b5369..2adf26bb2b3 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -4,6 +4,7 @@ import MysteryEncounter from "../data/mystery-encounters/mystery-encounter"; import { Species } from "#enums/species"; import { isNullOrUndefined } from "#app/utils"; import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; +import PlayAnimationConfig = Phaser.Types.Animations.PlayAnimationConfig; type KnownFileRoot = | "arenas" @@ -85,7 +86,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con }; if (!isNullOrUndefined(result.species)) { - const keys = getSpriteKeysFromSpecies(result.species); + const keys = getSpriteKeysFromSpecies(result.species!); result.spriteKey = keys.spriteKey; result.fileRoot = keys.fileRoot; result.isPokemon = true; @@ -164,7 +165,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con } } - if (!isNaN(alpha)) { + if (!isNullOrUndefined(alpha)) { sprite.setAlpha(alpha); tintSprite.setAlpha(alpha); } @@ -290,7 +291,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con const tintSprites = this.getTintSprites(); this.spriteConfigs.forEach((config, i) => { if (!config.disableAnimation) { - const trainerAnimConfig = { + const trainerAnimConfig: PlayAnimationConfig = { key: config.spriteKey, repeat: config?.repeat ? -1 : 0, startFrame: config?.startFrame ?? 0 @@ -307,7 +308,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con */ getSpriteAtIndex(index: number): Phaser.GameObjects.Sprite[] { if (!this.spriteConfigs) { - return; + return []; } const ret: Phaser.GameObjects.Sprite[] = []; @@ -319,7 +320,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con getSprites(): Phaser.GameObjects.Sprite[] { if (!this.spriteConfigs) { - return; + return []; } const ret: Phaser.GameObjects.Sprite[] = []; @@ -331,7 +332,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con getTintSprites(): Phaser.GameObjects.Sprite[] { if (!this.spriteConfigs) { - return; + return []; } const ret: Phaser.GameObjects.Sprite[] = []; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 7a6b26fcd3c..46b4fba06af 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -108,7 +108,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public mysteryEncounterData: MysteryEncounterPokemonData; /** Used by Mystery Encounters to execute pokemon-specific logic (such as stat boosts) at start of battle */ - public mysteryEncounterBattleEffects: (pokemon: Pokemon) => void = null; + public mysteryEncounterBattleEffects?: (pokemon: Pokemon) => void; public fieldPosition: FieldPosition; diff --git a/src/locales/en/mystery-encounters/delibirdy-dialogue.ts b/src/locales/en/mystery-encounters/delibirdy-dialogue.ts index a77261573a3..2cca9416382 100644 --- a/src/locales/en/mystery-encounters/delibirdy-dialogue.ts +++ b/src/locales/en/mystery-encounters/delibirdy-dialogue.ts @@ -1,6 +1,6 @@ export const delibirdyDialogue = { intro: "A pack of Delibird have appeared!", - title: "Delibird-y", + title: "Delibir-dy", description: "The Delibirds are looking at you expectantly, as if they want something. Perhaps giving them an item or some money would satisfy them?", query: "What will you give them?", invalid_selection: "Pokémon doesn't have that kind of item.", diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 8da6d701a9a..6a70b4c7737 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -28,7 +28,6 @@ import { BerryType } from "#enums/berry-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { getPokemonNameWithAffix } from "#app/messages.js"; -import { isNullOrUndefined } from "../utils"; const outputModifierData = false; const useMaxWeightForOutput = false; @@ -120,26 +119,6 @@ export class ModifierType { return this; } - /** - * Populates the tier field by performing a reverse lookup on the modifier pool specified by {@linkcode poolType} using the - * {@linkcode ModifierType}'s id. - * @param poolType the {@linkcode ModifierPoolType} to look into to derive the item's tier; defaults to {@linkcode ModifierPoolType.PLAYER} - */ - withTierFromPool(poolType: ModifierPoolType = ModifierPoolType.PLAYER): ModifierType { - for (const tier of Object.values(getModifierPoolForType(poolType))) { - for (const modifier of tier) { - if (this.id === modifier.modifierType.id) { - this.tier = modifier.modifierType.tier; - break; - } - } - if (this.tier) { - break; - } - } - return this; - } - /** * Populates item tier for ModifierType instance * Tier is a necessary field for items that appear in player shop (determines the Pokeball visual they use) @@ -2113,10 +2092,10 @@ function getModifierTypeOptionWithRetry(existingOptions: ModifierTypeOption[], r allowLuckUpgrades = allowLuckUpgrades ?? true; let candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, tier, undefined, 0, allowLuckUpgrades); let r = 0; - while (existingOptions.length && ++r < retryCount && existingOptions.filter(o => o.type.name === candidate.type.name || o.type.group === candidate.type.group).length) { - candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, candidate.type.tier, candidate.upgradeCount, 0, allowLuckUpgrades); + while (existingOptions.length && ++r < retryCount && existingOptions.filter(o => o.type.name === candidate?.type.name || o.type.group === candidate?.type.group).length) { + candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, candidate?.type.tier ?? tier, candidate?.upgradeCount, 0, allowLuckUpgrades); } - return candidate; + return candidate!; } /** diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index c1d379b97ea..538f34aded7 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -826,7 +826,7 @@ export class PokemonIncrementingStatModifier extends PokemonHeldItemModifier { args[1].forEach((v, i) => { const isHp = i === 0; let mult = 1; - if (this.stackCount === this.getMaxHeldItemCount(null)) { + if (this.stackCount === this.getMaxHeldItemCount()) { mult = isHp ? 1.05 : 1.1; } const newVal = Math.floor((v + this.stackCount * (isHp ? 1 : 2)) * mult); @@ -840,7 +840,7 @@ export class PokemonIncrementingStatModifier extends PokemonHeldItemModifier { return 1.2; } - getMaxHeldItemCount(pokemon: Pokemon): integer { + getMaxHeldItemCount(pokemon?: Pokemon): integer { return 50; } } diff --git a/src/phases.ts b/src/phases.ts index fff69caac7b..d525cd8f95b 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -5,7 +5,7 @@ import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMov import { Mode } from "./ui/ui"; import { Command } from "./ui/command-ui-handler"; import { Stat } from "./data/pokemon-stat"; -import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, Modifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TurnHealModifier, TurnHeldItemTransferModifier, MoneyMultiplierModifier, MoneyInterestModifier, IvScannerModifier, LapsingPokemonHeldItemModifier, PokemonMultiHitModifier, overrideModifiers, overrideHeldItems, BypassSpeedChanceModifier, TurnStatusEffectModifier, PokemonResetNegativeStatStageModifier } from "./modifier/modifier"; +import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, Modifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TurnHealModifier, TurnHeldItemTransferModifier, MoneyMultiplierModifier, MoneyInterestModifier, IvScannerModifier, LapsingPokemonHeldItemModifier, PokemonMultiHitModifier, overrideModifiers, overrideHeldItems, BypassSpeedChanceModifier, TurnStatusEffectModifier, PokemonResetNegativeStatStageModifier, PokemonIncrementingStatModifier } from "./modifier/modifier"; import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler"; import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball"; import { CommonAnim, CommonBattleAnim, initEncounterAnims, initMoveAnim, loadEncounterAnimAssets, loadMoveAnimAssets, MoveAnim } from "./data/battle-anims"; @@ -19,7 +19,7 @@ import { biomeLinks, getBiomeName } from "./data/biomes"; import { ModifierTier } from "./modifier/modifier-tier"; import { CustomModifierSettings, FusePokemonModifierType, getDailyRunStarterModifiers, getEnemyBuffModifierForWave, getModifierType, getPlayerModifierTypeOptions, getPlayerShopModifierTypeOptionsForWave, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, modifierTypes, PokemonModifierType, PokemonMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, regenerateModifierPoolThresholds, RememberMoveModifierType, TmModifierType } from "./modifier/modifier-type"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import { BattlerTagLapseType, CenterOfAttentionTag, EncoreTag, ProtectedTag, SemiInvulnerableTag, TrappedTag } from "./data/battler-tags"; +import { BattlerTagLapseType, CenterOfAttentionTag, EncoreTag, MysteryEncounterPostSummonTag, ProtectedTag, SemiInvulnerableTag, TrappedTag } from "./data/battler-tags"; import { getPokemonNameWithAffix } from "./messages"; import { Starter } from "./ui/starter-select-ui-handler"; import { Gender } from "./data/gender"; @@ -70,6 +70,7 @@ import { doTrainerExclamation, handleMysteryEncounterBattleStartEffects, handleM import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; +import { isNullOrUndefined } from "./utils"; const { t } = i18next; @@ -865,7 +866,7 @@ export class EncounterPhase extends BattlePhase { // Add intro visuals for mystery encounter mysteryEncounter.initIntroVisuals(this.scene); - this.scene.field.add(mysteryEncounter.introVisuals); + this.scene.field.add(mysteryEncounter.introVisuals!); } let totalBst = 0; @@ -930,7 +931,7 @@ export class EncounterPhase extends BattlePhase { const newEncounter = this.scene.getMysteryEncounter(mysteryEncounter); battle.mysteryEncounter = newEncounter; } - loadEnemyAssets.push(battle.mysteryEncounter.introVisuals.loadAssets().then(() => battle.mysteryEncounter.introVisuals.initSprite())); + loadEnemyAssets.push(battle.mysteryEncounter.introVisuals!.loadAssets().then(() => battle.mysteryEncounter.introVisuals!.initSprite())); // Load Mystery Encounter Exclamation bubble and sfx loadEnemyAssets.push(new Promise(resolve => { this.scene.loadSe("GEN8- Exclaim", "battle_anims", "GEN8- Exclaim.wav"); @@ -1130,7 +1131,7 @@ export class EncounterPhase extends BattlePhase { } } } else if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER) { - const introVisuals = this.scene.currentBattle.mysteryEncounter.introVisuals; + const introVisuals = this.scene.currentBattle.mysteryEncounter.introVisuals!; introVisuals.playAnim(); if (this.scene.currentBattle.mysteryEncounter.onVisualsStart) { @@ -1148,23 +1149,27 @@ export class EncounterPhase extends BattlePhase { if (showEncounterMessage) { const introDialogue = this.scene.currentBattle.mysteryEncounter.dialogue.intro; - const FIRST_DIALOGUE_PROMPT_DELAY = 750; - let i = 0; - const showNextDialogue = () => { - const nextAction = i === introDialogue.length - 1 ? doShowEncounterOptions : showNextDialogue; - const dialogue = introDialogue[i]; - const title = getEncounterText(this.scene, dialogue.speaker); - const text = getEncounterText(this.scene, dialogue.text); - i++; - if (title) { - this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0); - } else { - this.scene.ui.showText(text, null, nextAction, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0, true); - } - }; + if (!introDialogue) { + doShowEncounterOptions(); + } else { + const FIRST_DIALOGUE_PROMPT_DELAY = 750; + let i = 0; + const showNextDialogue = () => { + const nextAction = i === introDialogue.length - 1 ? doShowEncounterOptions : showNextDialogue; + const dialogue = introDialogue[i]; + const title = getEncounterText(this.scene, dialogue?.speaker); + const text = getEncounterText(this.scene, dialogue.text)!; + i++; + if (title) { + this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0); + } else { + this.scene.ui.showText(text, null, nextAction, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0, true); + } + }; - if (introDialogue.length > 0) { - showNextDialogue(); + if (introDialogue.length > 0) { + showNextDialogue(); + } } } else { doShowEncounterOptions(); @@ -1330,7 +1335,7 @@ export class NextEncounterPhase extends EncounterPhase { } if (lastEncounterVisuals) { this.scene.field.remove(lastEncounterVisuals, true); - this.scene.lastMysteryEncounter.introVisuals = null; + this.scene.lastMysteryEncounter.introVisuals = undefined; } if (!this.tryOverrideForBattleSpec()) { @@ -4378,7 +4383,7 @@ export class VictoryPhase extends PokemonPhase { if (participated) { partyMember.addFriendship(2); const machoBraceModifier = partyMember.getHeldItems().find(m => m instanceof PokemonIncrementingStatModifier); - if (!isNullOrUndefined(machoBraceModifier) && machoBraceModifier.stackCount < machoBraceModifier.getMaxStackCount(this.scene)) { + if (machoBraceModifier && machoBraceModifier.stackCount < machoBraceModifier.getMaxStackCount(this.scene)) { machoBraceModifier.stackCount++; this.scene.updateModifiers(true, true); partyMember.updateInfo(); @@ -5857,7 +5862,7 @@ export class SelectModifierPhase extends BattlePhase { } else { baseValue = 250; } - const multiplier = !isNullOrUndefined(this.customModifierSettings?.rerollMultiplier) ? this.customModifierSettings.rerollMultiplier : 1; + const multiplier = !isNullOrUndefined(this.customModifierSettings?.rerollMultiplier) ? this.customModifierSettings!.rerollMultiplier! : 1; return Math.min(Math.ceil(this.scene.currentBattle.waveIndex / 10) * baseValue * Math.pow(2, this.rerollCount) * multiplier, Number.MAX_SAFE_INTEGER); } diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index 160a081fda1..bb31ca999f8 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -27,7 +27,7 @@ import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; */ export class MysteryEncounterPhase extends Phase { private readonly FIRST_DIALOGUE_PROMPT_DELAY = 300; - optionSelectSettings: OptionSelectSettings; + optionSelectSettings?: OptionSelectSettings; /** * @@ -72,7 +72,7 @@ export class MysteryEncounterPhase extends Phase { if (option.onPreOptionPhase) { this.scene.executeWithSeedOffset(async () => { - return await option.onPreOptionPhase(this.scene) + return await option.onPreOptionPhase!(this.scene) .then((result) => { if (isNullOrUndefined(result) || result) { this.continueEncounter(); @@ -93,7 +93,7 @@ export class MysteryEncounterPhase extends Phase { }; const optionSelectDialogue = this.scene.currentBattle?.mysteryEncounter?.selectedOption?.dialogue; - if (optionSelectDialogue?.selected?.length > 0) { + if (optionSelectDialogue?.selected && optionSelectDialogue.selected.length > 0) { // Handle intermediate dialogue (between player selection event and the onOptionSelect logic) this.scene.ui.setMode(Mode.MESSAGE); const selectedDialogue = optionSelectDialogue.selected; @@ -101,17 +101,17 @@ export class MysteryEncounterPhase extends Phase { const showNextDialogue = () => { const nextAction = i === selectedDialogue.length - 1 ? endDialogueAndContinueEncounter : showNextDialogue; const dialogue = selectedDialogue[i]; - let title: string = null; - const text: string = getEncounterText(this.scene, dialogue.text); + let title: string | null = null; + const text: string | null = getEncounterText(this.scene, dialogue.text); if (dialogue.speaker) { title = getEncounterText(this.scene, dialogue.speaker); } i++; if (title) { - this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0); + this.scene.ui.showDialogue(text ?? "", title, null, nextAction, 0, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0); } else { - this.scene.ui.showText(text, null, nextAction, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0, true); + this.scene.ui.showText(text ?? "", null, nextAction, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0, true); } }; @@ -142,7 +142,7 @@ export class MysteryEncounterOptionSelectedPhase extends Phase { constructor(scene: BattleScene) { super(scene); - this.onOptionSelect = this.scene.currentBattle.mysteryEncounter.selectedOption.onOptionPhase; + this.onOptionSelect = this.scene.currentBattle.mysteryEncounter.selectedOption!.onOptionPhase; } start() { @@ -223,10 +223,10 @@ export class MysteryEncounterBattlePhase extends Phase { if (encounterMode === MysteryEncounterMode.TRAINER_BATTLE) { if (scene.currentBattle.double) { - return i18next.t("battle:trainerAppearedDouble", { trainerName: scene.currentBattle.trainer.getName(TrainerSlot.NONE, true) }); + return i18next.t("battle:trainerAppearedDouble", { trainerName: scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }); } else { - return i18next.t("battle:trainerAppeared", { trainerName: scene.currentBattle.trainer.getName(TrainerSlot.NONE, true) }); + return i18next.t("battle:trainerAppeared", { trainerName: scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }); } } @@ -276,22 +276,22 @@ export class MysteryEncounterBattlePhase extends Phase { } }; - const encounterMessages = scene.currentBattle.trainer.getEncounterMessages(); + const encounterMessages = scene.currentBattle.trainer?.getEncounterMessages(); - if (!encounterMessages?.length) { + if (!encounterMessages || !encounterMessages.length) { doSummon(); } else { const trainer = this.scene.currentBattle.trainer; let message: string; scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.mysteryEncounter.getSeedOffset()); - + message = message!; // tell TS compiler it's defined now const showDialogueAndSummon = () => { - scene.ui.showDialogue(message, trainer.getName(TrainerSlot.NONE, true), null, () => { + scene.ui.showDialogue(message, trainer?.getName(TrainerSlot.NONE, true), null, () => { scene.charSprite.hide().then(() => scene.hideFieldOverlay(250).then(() => doSummon())); }); }; - if (scene.currentBattle.trainer.config.hasCharSprite && !scene.ui.shouldSkipDialogue(message)) { - scene.showFieldOverlay(500).then(() => scene.charSprite.showCharacter(trainer.getKey(), getCharVariantFromDialogue(encounterMessages[0])).then(() => showDialogueAndSummon())); + if (this.scene.currentBattle.trainer?.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { + this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(trainer?.getKey()!, getCharVariantFromDialogue(encounterMessages[0])).then(() => showDialogueAndSummon())); // TODO: is this bang correct? } else { showDialogueAndSummon(); } @@ -349,6 +349,9 @@ export class MysteryEncounterBattlePhase extends Phase { showEnemyTrainer(): void { // Show enemy trainer const trainer = this.scene.currentBattle.trainer; + if (!trainer) { + return; + } trainer.alpha = 0; trainer.x += 16; trainer.y -= 16; @@ -416,7 +419,7 @@ export class MysteryEncounterRewardsPhase extends Phase { this.scene.currentBattle.mysteryEncounter.doEncounterRewards(this.scene); } else if (this.addHealPhase) { this.scene.tryRemovePhase(p => p instanceof SelectModifierPhase); - this.scene.unshiftPhase(new SelectModifierPhase(this.scene, 0, null, { fillRemaining: false, rerollMultiplier: 0 })); + this.scene.unshiftPhase(new SelectModifierPhase(this.scene, 0, undefined, { fillRemaining: false, rerollMultiplier: 0 })); } // Do not use ME's seedOffset for rewards, these should always be consistent with waveIndex (once per wave) }, this.scene.currentBattle.waveIndex * 1000); @@ -436,11 +439,11 @@ export class MysteryEncounterRewardsPhase extends Phase { */ export class PostMysteryEncounterPhase extends Phase { private readonly FIRST_DIALOGUE_PROMPT_DELAY = 750; - onPostOptionSelect: OptionPhaseCallback; + onPostOptionSelect?: OptionPhaseCallback; constructor(scene: BattleScene) { super(scene); - this.onPostOptionSelect = this.scene.currentBattle.mysteryEncounter.selectedOption.onPostOptionPhase; + this.onPostOptionSelect = this.scene.currentBattle.mysteryEncounter.selectedOption?.onPostOptionPhase; } start() { @@ -448,7 +451,7 @@ export class PostMysteryEncounterPhase extends Phase { if (this.onPostOptionSelect) { this.scene.executeWithSeedOffset(async () => { - return await this.onPostOptionSelect(this.scene) + return await this.onPostOptionSelect!(this.scene) .then((result) => { if (isNullOrUndefined(result) || result) { this.continueEncounter(); @@ -467,13 +470,13 @@ export class PostMysteryEncounterPhase extends Phase { }; const outroDialogue = this.scene.currentBattle?.mysteryEncounter?.dialogue?.outro; - if (outroDialogue?.length > 0) { + if (outroDialogue && outroDialogue.length > 0) { let i = 0; const showNextDialogue = () => { const nextAction = i === outroDialogue.length - 1 ? endPhase : showNextDialogue; const dialogue = outroDialogue[i]; - let title: string = null; - const text: string = getEncounterText(this.scene, dialogue.text); + let title: string | null = null; + const text: string | null = getEncounterText(this.scene, dialogue.text); if (dialogue.speaker) { title = getEncounterText(this.scene, dialogue.speaker); } @@ -481,9 +484,9 @@ export class PostMysteryEncounterPhase extends Phase { i++; this.scene.ui.setMode(Mode.MESSAGE); if (title) { - this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0); + this.scene.ui.showDialogue(text ?? "", title, null, nextAction, 0, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0); } else { - this.scene.ui.showText(text, null, nextAction, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0, true); + this.scene.ui.showText(text ?? "", null, nextAction, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0, true); } }; diff --git a/src/test/mystery-encounter/encounterTestUtils.ts b/src/test/mystery-encounter/encounterTestUtils.ts index 791c96b3437..3756d24df24 100644 --- a/src/test/mystery-encounter/encounterTestUtils.ts +++ b/src/test/mystery-encounter/encounterTestUtils.ts @@ -10,6 +10,7 @@ import { expect, vi } from "vitest"; import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import PartyUiHandler from "#app/ui/party-ui-handler"; import OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler"; +import { isNullOrUndefined } from "#app/utils"; /** * Runs a MysteryEncounter to either the start of a battle, or to the MysteryEncounterRewardsPhase, depending on the option selected @@ -18,7 +19,7 @@ import OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler"; * @param secondaryOptionSelect - * @param isBattle - if selecting option should lead to battle, set to true */ -export async function runMysteryEncounterToEnd(game: GameManager, optionNo: number, secondaryOptionSelect: { pokemonNo: number, optionNo?: number } = null, isBattle: boolean = false) { +export async function runMysteryEncounterToEnd(game: GameManager, optionNo: number, secondaryOptionSelect?: { pokemonNo: number, optionNo?: number }, isBattle: boolean = false) { vi.spyOn(EncounterPhaseUtils, "selectPokemonForOption"); await runSelectMysteryEncounterOption(game, optionNo, secondaryOptionSelect); @@ -65,7 +66,7 @@ export async function runMysteryEncounterToEnd(game: GameManager, optionNo: numb } } -export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number, secondaryOptionSelect: { pokemonNo: number, optionNo?: number } = null) { +export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number, secondaryOptionSelect?: { pokemonNo: number, optionNo?: number }) { // Handle any eventual queued messages (e.g. weather phase, etc.) game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { const uiHandler = game.scene.ui.getHandler(); @@ -107,8 +108,8 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN uiHandler.processInput(Button.ACTION); - if (!isNaN(secondaryOptionSelect?.pokemonNo)) { - await handleSecondaryOptionSelect(game, secondaryOptionSelect.pokemonNo, secondaryOptionSelect.optionNo); + if (!isNullOrUndefined(secondaryOptionSelect?.pokemonNo)) { + await handleSecondaryOptionSelect(game, secondaryOptionSelect!.pokemonNo, secondaryOptionSelect!.optionNo); } } @@ -128,14 +129,14 @@ async function handleSecondaryOptionSelect(game: GameManager, pokemonNo: number, partyUiHandler.processInput(Button.ACTION); // If there is a second choice to make after selecting a Pokemon - if (!isNaN(optionNo)) { + if (!isNullOrUndefined(optionNo)) { // Wait for Summary menu to close and second options to spawn const secondOptionUiHandler = game.scene.ui.handlers[Mode.OPTION_SELECT] as OptionSelectUiHandler; vi.spyOn(secondOptionUiHandler, "show"); await vi.waitFor(() => expect(secondOptionUiHandler.show).toHaveBeenCalled()); // Navigate down to the correct option - for (let i = 1; i < optionNo; i++) { + for (let i = 1; i < optionNo!; i++) { secondOptionUiHandler.processInput(Button.DOWN); } diff --git a/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts b/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts index 74effa3b29d..fc8460f1325 100644 --- a/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts @@ -35,7 +35,7 @@ describe("A Trainer's Test - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); const biomeMap = new Map([ [Biome.VOLCANO, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]], @@ -59,11 +59,11 @@ describe("A Trainer's Test - Mystery Encounter", () => { expect(ATrainersTestEncounter.encounterTier).toBe(MysteryEncounterTier.ROGUE); expect(ATrainersTestEncounter.dialogue).toBeDefined(); expect(ATrainersTestEncounter.dialogue.intro).toBeDefined(); - expect(ATrainersTestEncounter.dialogue.intro[0].speaker).toBeDefined(); - expect(ATrainersTestEncounter.dialogue.intro[0].text).toBeDefined(); - expect(ATrainersTestEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(ATrainersTestEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(ATrainersTestEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(ATrainersTestEncounter.dialogue.intro?.[0].speaker).toBeDefined(); + expect(ATrainersTestEncounter.dialogue.intro?.[0].text).toBeDefined(); + expect(ATrainersTestEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(ATrainersTestEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(ATrainersTestEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(ATrainersTestEncounter.options.length).toBe(2); }); @@ -92,14 +92,14 @@ describe("A Trainer's Test - Mystery Encounter", () => { expect(ATrainersTestEncounter.onInit).toBeDefined(); ATrainersTestEncounter.populateDialogueTokensFromRequirements(scene); - const onInitResult = onInit(scene); + const onInitResult = onInit!(scene); expect(ATrainersTestEncounter.dialogueTokens?.statTrainerName).toBeDefined(); expect(ATrainersTestEncounter.misc.trainerType).toBeDefined(); expect(ATrainersTestEncounter.misc.trainerNameKey).toBeDefined(); expect(ATrainersTestEncounter.misc.trainerEggDescription).toBeDefined(); expect(ATrainersTestEncounter.dialogue.intro).toBeDefined(); - expect(ATrainersTestEncounter.options[1].dialogue.selected).toBeDefined(); + expect(ATrainersTestEncounter.options[1].dialogue?.selected).toBeDefined(); expect(onInitResult).toBe(true); }); @@ -108,19 +108,19 @@ describe("A Trainer's Test - Mystery Encounter", () => { const option = ATrainersTestEncounter.options[0]; expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT); expect(option.dialogue).toBeDefined(); - expect(option.dialogue.buttonLabel).toStrictEqual(`${namespace}.option.1.label`); - expect(option.dialogue.buttonTooltip).toStrictEqual(`${namespace}.option.1.tooltip`); + expect(option.dialogue!.buttonLabel).toStrictEqual(`${namespace}.option.1.label`); + expect(option.dialogue!.buttonTooltip).toStrictEqual(`${namespace}.option.1.tooltip`); }); it("Should start battle against the trainer", async () => { await game.runToMysteryEncounter(MysteryEncounterType.A_TRAINERS_TEST, defaultParty); - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(scene.currentBattle.trainer).toBeDefined(); - expect(["buck", "cheryl", "marley", "mira", "riley"].includes(scene.currentBattle.trainer.config.name.toLowerCase())).toBeTruthy(); + expect(["buck", "cheryl", "marley", "mira", "riley"].includes(scene.currentBattle.trainer!.config.name.toLowerCase())).toBeTruthy(); expect(enemyField[0]).toBeDefined(); }); @@ -131,10 +131,10 @@ describe("A Trainer's Test - Mystery Encounter", () => { expect(eggsBefore).toBeDefined(); const eggsBeforeLength = eggsBefore.length; - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const eggsAfter = scene.gameData.eggs; expect(eggsAfter).toBeDefined(); @@ -151,7 +151,7 @@ describe("A Trainer's Test - Mystery Encounter", () => { return { totalDuration: 1, destroy: () => null - } as Phaser.Sound.NoAudioSound; + } as any; }); }); @@ -159,8 +159,8 @@ describe("A Trainer's Test - Mystery Encounter", () => { const option = ATrainersTestEncounter.options[1]; expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT); expect(option.dialogue).toBeDefined(); - expect(option.dialogue.buttonLabel).toStrictEqual(`${namespace}.option.2.label`); - expect(option.dialogue.buttonTooltip).toStrictEqual(`${namespace}.option.2.tooltip`); + expect(option.dialogue?.buttonLabel).toStrictEqual(`${namespace}.option.2.label`); + expect(option.dialogue?.buttonTooltip).toStrictEqual(`${namespace}.option.2.tooltip`); }); it("Should fully heal the party", async () => { @@ -182,7 +182,7 @@ describe("A Trainer's Test - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const eggsAfter = scene.gameData.eggs; expect(eggsAfter).toBeDefined(); diff --git a/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts b/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts index d21421cde2b..0c5495ba924 100644 --- a/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts @@ -35,7 +35,7 @@ describe("Absolute Avarice - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -58,9 +58,9 @@ describe("Absolute Avarice - Mystery Encounter", () => { expect(AbsoluteAvariceEncounter.encounterTier).toBe(MysteryEncounterTier.GREAT); expect(AbsoluteAvariceEncounter.dialogue).toBeDefined(); expect(AbsoluteAvariceEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]); - expect(AbsoluteAvariceEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(AbsoluteAvariceEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(AbsoluteAvariceEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(AbsoluteAvariceEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(AbsoluteAvariceEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(AbsoluteAvariceEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(AbsoluteAvariceEncounter.options.length).toBe(3); }); @@ -98,7 +98,7 @@ describe("Absolute Avarice - Mystery Encounter", () => { it("should spawn if player has enough berries", async () => { game.override.mysteryEncounterTier(MysteryEncounterTier.GREAT); - game.override.starterHeldItems([{name: "BERRY", count: 2, type: BerryType.SITRUS}, {name: "BERRY", count: 3, type: BerryType.GANLON}]); + game.override.startingHeldItems([{name: "BERRY", count: 2, type: BerryType.SITRUS}, {name: "BERRY", count: 3, type: BerryType.GANLON}]); await game.runToMysteryEncounter(); @@ -106,7 +106,7 @@ describe("Absolute Avarice - Mystery Encounter", () => { }); it("should remove all player's berries at the start of the encounter", async () => { - game.override.starterHeldItems([{name: "BERRY", count: 2, type: BerryType.SITRUS}, {name: "BERRY", count: 3, type: BerryType.GANLON}]); + game.override.startingHeldItems([{name: "BERRY", count: 2, type: BerryType.SITRUS}, {name: "BERRY", count: 3, type: BerryType.GANLON}]); await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty); @@ -134,13 +134,13 @@ describe("Absolute Avarice - Mystery Encounter", () => { const phaseSpy = vi.spyOn(scene, "pushPhase"); await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty); - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(Species.GREEDENT); - const moveset = enemyField[0].moveset.map(m => m.moveId); + const moveset = enemyField[0].moveset.map(m => m?.moveId); expect(moveset?.length).toBe(4); expect(moveset).toEqual([Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.SLACK_OFF]); @@ -151,10 +151,10 @@ describe("Absolute Avarice - Mystery Encounter", () => { it("should give reviver seed to each pokemon after battle", async () => { await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty); - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); for (const partyPokemon of scene.getParty()) { const pokemonId = partyPokemon.id; @@ -162,7 +162,7 @@ describe("Absolute Avarice - Mystery Encounter", () => { && (m as PokemonHeldItemModifier).pokemonId === pokemonId, true) as PokemonHeldItemModifier[]; const revSeed = pokemonItems.find(i => i.type.name === "Reviver Seed"); expect(revSeed).toBeDefined; - expect(revSeed.stackCount).toBe(1); + expect(revSeed?.stackCount).toBe(1); } }); }); @@ -184,7 +184,7 @@ describe("Absolute Avarice - Mystery Encounter", () => { }); it("Should return 3 (2/5ths floored) berries if 8 were stolen", async () => { - game.override.starterHeldItems([{name: "BERRY", count: 2, type: BerryType.SITRUS}, {name: "BERRY", count: 3, type: BerryType.GANLON}, {name: "BERRY", count: 3, type: BerryType.APICOT}]); + game.override.startingHeldItems([{name: "BERRY", count: 2, type: BerryType.SITRUS}, {name: "BERRY", count: 3, type: BerryType.GANLON}, {name: "BERRY", count: 3, type: BerryType.APICOT}]); await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty); @@ -200,7 +200,7 @@ describe("Absolute Avarice - Mystery Encounter", () => { }); it("Should return 2 (2/5ths floored) berries if 7 were stolen", async () => { - game.override.starterHeldItems([{name: "BERRY", count: 2, type: BerryType.SITRUS}, {name: "BERRY", count: 3, type: BerryType.GANLON}, {name: "BERRY", count: 2, type: BerryType.APICOT}]); + game.override.startingHeldItems([{name: "BERRY", count: 2, type: BerryType.SITRUS}, {name: "BERRY", count: 3, type: BerryType.GANLON}, {name: "BERRY", count: 2, type: BerryType.APICOT}]); await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty); @@ -251,7 +251,7 @@ describe("Absolute Avarice - Mystery Encounter", () => { expect(partyCountBefore + 1).toBe(partyCountAfter); const greedent = scene.getParty()[scene.getParty().length - 1]; expect(greedent.species.speciesId).toBe(Species.GREEDENT); - const moveset = greedent.moveset.map(m => m.moveId); + const moveset = greedent.moveset.map(m => m?.moveId); expect(moveset?.length).toBe(4); expect(moveset).toEqual([Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.SLACK_OFF]); }); diff --git a/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts b/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts index 128a4c109dc..bbc1a968b5c 100644 --- a/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts @@ -38,7 +38,7 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); const biomeMap = new Map([ [Biome.VOLCANO, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]], @@ -65,9 +65,9 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => { { text: `${namespace}.intro` }, { speaker: `${namespace}.speaker`, text: `${namespace}.intro_dialogue` } ]); - expect(AnOfferYouCantRefuseEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(AnOfferYouCantRefuseEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(AnOfferYouCantRefuseEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(AnOfferYouCantRefuseEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(AnOfferYouCantRefuseEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(AnOfferYouCantRefuseEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(AnOfferYouCantRefuseEncounter.options.length).toBe(3); }); @@ -104,7 +104,7 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => { expect(AnOfferYouCantRefuseEncounter.onInit).toBeDefined(); AnOfferYouCantRefuseEncounter.populateDialogueTokensFromRequirements(scene); - const onInitResult = onInit(scene); + const onInitResult = onInit!(scene); expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.strongestPokemon).toBeDefined(); expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.price).toBeDefined(); @@ -153,7 +153,7 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => { const itemModifier = scene.findModifier(m => m instanceof ShinyRateBoosterModifier) as ShinyRateBoosterModifier; expect(itemModifier).toBeDefined(); - expect(itemModifier.stackCount).toBe(1); + expect(itemModifier?.stackCount).toBe(1); }); it("Should remove the Pokemon from the party", async () => { @@ -199,7 +199,7 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => { it("should award EXP to a pokemon with an ability in EXTORTION_ABILITIES", async () => { await game.runToMysteryEncounter(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE, defaultParty); const party = scene.getParty(); - const gyarados = party.find((pkm) => pkm.species.speciesId === Species.GYARADOS); + const gyarados = party.find((pkm) => pkm.species.speciesId === Species.GYARADOS)!; const expBefore = gyarados.exp; await runMysteryEncounterToEnd(game, 2); @@ -210,7 +210,7 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => { it("should award EXP to a pokemon with a move in EXTORTION_MOVES", async () => { await game.runToMysteryEncounter(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE, [Species.ABRA]); const party = scene.getParty(); - const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA); + const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA)!; abra.moveset = [new PokemonMove(Moves.BEAT_UP)]; const expBefore = abra.exp; diff --git a/src/test/mystery-encounter/encounters/berries-abound-encounter.test.ts b/src/test/mystery-encounter/encounters/berries-abound-encounter.test.ts index 435f890b857..0086cc869ef 100644 --- a/src/test/mystery-encounter/encounters/berries-abound-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/berries-abound-encounter.test.ts @@ -37,7 +37,7 @@ describe("Berries Abound - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -59,9 +59,9 @@ describe("Berries Abound - Mystery Encounter", () => { expect(BerriesAboundEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON); expect(BerriesAboundEncounter.dialogue).toBeDefined(); expect(BerriesAboundEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]); - expect(BerriesAboundEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(BerriesAboundEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(BerriesAboundEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(BerriesAboundEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(BerriesAboundEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(BerriesAboundEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(BerriesAboundEncounter.options.length).toBe(3); }); @@ -90,12 +90,12 @@ describe("Berries Abound - Mystery Encounter", () => { expect(BerriesAboundEncounter.onInit).toBeDefined(); BerriesAboundEncounter.populateDialogueTokensFromRequirements(scene); - const onInitResult = onInit(scene); + const onInitResult = onInit!(scene); const config = BerriesAboundEncounter.enemyPartyConfigs[0]; expect(config).toBeDefined(); expect(config.levelAdditiveMultiplier).toBe(1); - expect(config.pokemonConfigs[0].isBoss).toBe(true); + expect(config.pokemonConfigs?.[0].isBoss).toBe(true); expect(onInitResult).toBe(true); }); @@ -119,12 +119,12 @@ describe("Berries Abound - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty); const config = game.scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]; - const speciesToSpawn = config.pokemonConfigs[0].species.speciesId; + const speciesToSpawn = config.pokemonConfigs?.[0].species.speciesId; - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); }); @@ -133,11 +133,12 @@ describe("Berries Abound - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty); const numBerries = game.scene.currentBattle.mysteryEncounter.misc.numBerries; + scene.modifiers = []; - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const berriesAfter = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; const berriesAfterCount = berriesAfter.reduce((a, b) => a + b.stackCount, 0); @@ -147,10 +148,10 @@ describe("Berries Abound - Mystery Encounter", () => { it("should spawn a shop with 5 berries", async () => { await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty); - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); @@ -178,14 +179,14 @@ describe("Berries Abound - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty); const config = game.scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]; - const speciesToSpawn = config.pokemonConfigs[0].species.speciesId; + const speciesToSpawn = config.pokemonConfigs?.[0].species.speciesId; // Setting enemy's level arbitrarily high to outspeed - config.pokemonConfigs[0].dataSource.level = 1000; + config.pokemonConfigs![0].dataSource!.level = 1000; - await runMysteryEncounterToEnd(game, 2, null, true); + await runMysteryEncounterToEnd(game, 2, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); @@ -207,7 +208,7 @@ describe("Berries Abound - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); diff --git a/src/test/mystery-encounter/encounters/clowning-around-encounter.test.ts b/src/test/mystery-encounter/encounters/clowning-around-encounter.test.ts index 80f022a3e3e..136ba5b3d32 100644 --- a/src/test/mystery-encounter/encounters/clowning-around-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/clowning-around-encounter.test.ts @@ -50,7 +50,7 @@ describe("Clowning Around - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -78,9 +78,9 @@ describe("Clowning Around - Mystery Encounter", () => { text: `${namespace}.intro_dialogue`, }, ]); - expect(ClowningAroundEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(ClowningAroundEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(ClowningAroundEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(ClowningAroundEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(ClowningAroundEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(ClowningAroundEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(ClowningAroundEncounter.options.length).toBe(3); }); @@ -111,23 +111,23 @@ describe("Clowning Around - Mystery Encounter", () => { expect(ClowningAroundEncounter.onInit).toBeDefined(); ClowningAroundEncounter.populateDialogueTokensFromRequirements(scene); - const onInitResult = onInit(scene); + const onInitResult = onInit!(scene); const config = ClowningAroundEncounter.enemyPartyConfigs[0]; expect(config.doubleBattle).toBe(true); - expect(config.trainerConfig.trainerType).toBe(TrainerType.HARLEQUIN); - expect(config.pokemonConfigs[0]).toEqual({ + expect(config.trainerConfig?.trainerType).toBe(TrainerType.HARLEQUIN); + expect(config.pokemonConfigs?.[0]).toEqual({ species: getPokemonSpecies(Species.MR_MIME), isBoss: true, moveSet: [Moves.TEETER_DANCE, Moves.ALLY_SWITCH, Moves.DAZZLING_GLEAM, Moves.PSYCHIC] }); - expect(config.pokemonConfigs[1]).toEqual({ + expect(config.pokemonConfigs?.[1]).toEqual({ species: getPokemonSpecies(Species.BLACEPHALON), mysteryEncounterData: expect.anything(), isBoss: true, moveSet: [Moves.TRICK, Moves.HYPNOSIS, Moves.SHADOW_BALL, Moves.MIND_BLOWN] }); - expect(config.pokemonConfigs[1].mysteryEncounterData.types.length).toBe(2); + expect(config.pokemonConfigs?.[1].mysteryEncounterData?.types.length).toBe(2); expect([ Abilities.STURDY, Abilities.PICKUP, @@ -144,8 +144,8 @@ describe("Clowning Around - Mystery Encounter", () => { Abilities.MAGICIAN, Abilities.SHEER_FORCE, Abilities.PRANKSTER - ]).toContain(config.pokemonConfigs[1].mysteryEncounterData.ability); - expect(ClowningAroundEncounter.misc.ability).toBe(config.pokemonConfigs[1].mysteryEncounterData.ability); + ]).toContain(config.pokemonConfigs?.[1].mysteryEncounterData?.ability); + expect(ClowningAroundEncounter.misc.ability).toBe(config.pokemonConfigs?.[1].mysteryEncounterData?.ability); await vi.waitFor(() => expect(moveInitSpy).toHaveBeenCalled()); await vi.waitFor(() => expect(moveLoadSpy).toHaveBeenCalled()); expect(onInitResult).toBe(true); @@ -172,10 +172,10 @@ describe("Clowning Around - Mystery Encounter", () => { const phaseSpy = vi.spyOn(scene, "pushPhase"); await game.runToMysteryEncounter(MysteryEncounterType.CLOWNING_AROUND, defaultParty); - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(2); expect(enemyField[0].species.speciesId).toBe(Species.MR_MIME); expect(enemyField[0].moveset).toEqual([new PokemonMove(Moves.TEETER_DANCE), new PokemonMove(Moves.ALLY_SWITCH), new PokemonMove(Moves.DAZZLING_GLEAM), new PokemonMove(Moves.PSYCHIC)]); @@ -191,10 +191,10 @@ describe("Clowning Around - Mystery Encounter", () => { it("should let the player gain the ability after battle completion", async () => { await game.runToMysteryEncounter(MysteryEncounterType.CLOWNING_AROUND, defaultParty); - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); const abilityToTrain = scene.currentBattle.mysteryEncounter.misc.ability; @@ -209,7 +209,7 @@ describe("Clowning Around - Mystery Encounter", () => { vi.spyOn(partyUiHandler, "show"); game.endPhase(); await game.phaseInterceptor.to(PostMysteryEncounterPhase); - expect(scene.getCurrentPhase().constructor.name).toBe(PostMysteryEncounterPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(PostMysteryEncounterPhase.name); // Wait for Yes/No confirmation to appear await vi.waitFor(() => expect(optionSelectUiHandler.show).toHaveBeenCalled()); @@ -224,7 +224,7 @@ describe("Clowning Around - Mystery Encounter", () => { await game.phaseInterceptor.to(NewBattlePhase, false); const leadPokemon = scene.getParty()[0]; - expect(leadPokemon.mysteryEncounterData.ability).toBe(abilityToTrain); + expect(leadPokemon.mysteryEncounterData?.ability).toBe(abilityToTrain); }); }); @@ -255,6 +255,9 @@ describe("Clowning Around - Mystery Encounter", () => { it("should randomize held items of the Pokemon with the most items, and not the held items of other pokemon", async () => { await game.runToMysteryEncounter(MysteryEncounterType.CLOWNING_AROUND, defaultParty); + // Set some moves on party for attack type booster generation + scene.getParty()[0].moveset = [new PokemonMove(Moves.TACKLE), new PokemonMove(Moves.THIEF)]; + // 2 Sitrus Berries on lead scene.modifiers = []; let itemType = generateModifierType(scene, modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType; @@ -294,8 +297,8 @@ describe("Clowning Around - Mystery Encounter", () => { const secondItemsAfter = scene.getParty()[1].getHeldItems(); expect(secondItemsAfter.length).toBe(1); expect(secondItemsAfter[0].type.id).toBe("SOUL_DEW"); - expect(secondItemsAfter[0].stackCount).toBe(5); - }); + expect(secondItemsAfter[0]?.stackCount).toBe(5); + }, 2000000); it("should leave encounter without battle", async () => { const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); @@ -342,9 +345,9 @@ describe("Clowning Around - Mystery Encounter", () => { scene.getParty()[2].moveset = []; await runMysteryEncounterToEnd(game, 3); - const leadTypesAfter = scene.getParty()[0].mysteryEncounterData.types; - const secondaryTypesAfter = scene.getParty()[1].mysteryEncounterData.types; - const thirdTypesAfter = scene.getParty()[2].mysteryEncounterData.types; + const leadTypesAfter = scene.getParty()[0].mysteryEncounterData?.types; + const secondaryTypesAfter = scene.getParty()[1].mysteryEncounterData?.types; + const thirdTypesAfter = scene.getParty()[2].mysteryEncounterData?.types; expect(leadTypesAfter.length).toBe(2); expect(leadTypesAfter).not.toBe([Type.ICE, Type.WATER]); diff --git a/src/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts b/src/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts index 89afd9d43aa..1f4571c5d61 100644 --- a/src/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts @@ -37,7 +37,7 @@ describe("Dancing Lessons - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -60,9 +60,9 @@ describe("Dancing Lessons - Mystery Encounter", () => { expect(DancingLessonsEncounter.encounterTier).toBe(MysteryEncounterTier.GREAT); expect(DancingLessonsEncounter.dialogue).toBeDefined(); expect(DancingLessonsEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]); - expect(DancingLessonsEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(DancingLessonsEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(DancingLessonsEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(DancingLessonsEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(DancingLessonsEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(DancingLessonsEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(DancingLessonsEncounter.options.length).toBe(3); }); @@ -110,14 +110,14 @@ describe("Dancing Lessons - Mystery Encounter", () => { const phaseSpy = vi.spyOn(scene, "pushPhase"); await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty); - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(Species.ORICORIO); expect(enemyField[0].summonData.battleStats).toEqual([1, 1, 1, 1, 1, 0, 0]); - const moveset = enemyField[0].moveset.map(m => m.moveId); + const moveset = enemyField[0].moveset.map(m => m?.moveId); expect(moveset.some(m => m === Moves.REVELATION_DANCE)).toBeTruthy(); const movePhases = phaseSpy.mock.calls.filter(p => p[0] instanceof MovePhase).map(p => p[0]); @@ -127,10 +127,10 @@ describe("Dancing Lessons - Mystery Encounter", () => { it("should have a Baton in the rewards after battle", async () => { await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty); - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); @@ -205,7 +205,7 @@ describe("Dancing Lessons - Mystery Encounter", () => { expect(partyCountBefore + 1).toBe(partyCountAfter); const oricorio = scene.getParty()[scene.getParty().length - 1]; expect(oricorio.species.speciesId).toBe(Species.ORICORIO); - const moveset = oricorio.moveset.map(m => m.moveId); + const moveset = oricorio.moveset.map(m => m?.moveId); expect(moveset?.some(m => m === Moves.REVELATION_DANCE)).toBeTruthy(); expect(moveset?.some(m => m === Moves.DRAGON_DANCE)).toBeTruthy(); }); @@ -217,7 +217,7 @@ describe("Dancing Lessons - Mystery Encounter", () => { await game.phaseInterceptor.to(MysteryEncounterPhase, false); const encounterPhase = scene.getCurrentPhase(); - expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name); + expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); vi.spyOn(mysteryEncounterPhase, "handleOptionSelect"); @@ -226,7 +226,7 @@ describe("Dancing Lessons - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 3); const partyCountAfter = scene.getParty().length; - expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); diff --git a/src/test/mystery-encounter/encounters/delibirdy-encounter.test.ts b/src/test/mystery-encounter/encounters/delibirdy-encounter.test.ts index 43f9c5612db..136df416ea7 100644 --- a/src/test/mystery-encounter/encounters/delibirdy-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/delibirdy-encounter.test.ts @@ -37,7 +37,7 @@ describe("Delibird-y - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -60,9 +60,9 @@ describe("Delibird-y - Mystery Encounter", () => { expect(DelibirdyEncounter.dialogue).toBeDefined(); expect(DelibirdyEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]); expect(DelibirdyEncounter.dialogue.outro).toStrictEqual([{ text: `${namespace}.outro` }]); - expect(DelibirdyEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(DelibirdyEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(DelibirdyEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(DelibirdyEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(DelibirdyEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(DelibirdyEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(DelibirdyEncounter.options.length).toBe(3); }); @@ -128,7 +128,7 @@ describe("Delibird-y - Mystery Encounter", () => { const itemModifier = scene.findModifier(m => m instanceof HiddenAbilityRateBoosterModifier) as HiddenAbilityRateBoosterModifier; expect(itemModifier).toBeDefined(); - expect(itemModifier.stackCount).toBe(1); + expect(itemModifier?.stackCount).toBe(1); }); it("Should give the player a Shell Bell if they have max stacks of Berry Pouches", async () => { @@ -148,9 +148,9 @@ describe("Delibird-y - Mystery Encounter", () => { const shellBellAfter = scene.findModifier(m => m instanceof HitHealModifier); expect(abilityCharmAfter).toBeDefined(); - expect(abilityCharmAfter.stackCount).toBe(4); + expect(abilityCharmAfter?.stackCount).toBe(4); expect(shellBellAfter).toBeDefined(); - expect(shellBellAfter.stackCount).toBe(1); + expect(shellBellAfter?.stackCount).toBe(1); }); it("should be disabled if player does not have enough money", async () => { @@ -159,7 +159,7 @@ describe("Delibird-y - Mystery Encounter", () => { await game.phaseInterceptor.to(MysteryEncounterPhase, false); const encounterPhase = scene.getCurrentPhase(); - expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name); + expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); vi.spyOn(mysteryEncounterPhase, "handleOptionSelect"); @@ -167,7 +167,7 @@ describe("Delibird-y - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 1); - expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -217,9 +217,9 @@ describe("Delibird-y - Mystery Encounter", () => { const sitrusAfter = scene.findModifier(m => m instanceof BerryModifier); const candyJarAfter = scene.findModifier(m => m instanceof LevelIncrementBoosterModifier); - expect(sitrusAfter.stackCount).toBe(1); + expect(sitrusAfter?.stackCount).toBe(1); expect(candyJarAfter).toBeDefined(); - expect(candyJarAfter.stackCount).toBe(1); + expect(candyJarAfter?.stackCount).toBe(1); }); it("Should remove Reviver Seed and give the player a Healing Charm", async () => { @@ -240,7 +240,7 @@ describe("Delibird-y - Mystery Encounter", () => { expect(reviverSeedAfter).toBeUndefined(); expect(healingCharmAfter).toBeDefined(); - expect(healingCharmAfter.stackCount).toBe(1); + expect(healingCharmAfter?.stackCount).toBe(1); }); it("Should give the player a Shell Bell if they have max stacks of Candy Jars", async () => { @@ -265,11 +265,11 @@ describe("Delibird-y - Mystery Encounter", () => { const candyJarAfter = scene.findModifier(m => m instanceof LevelIncrementBoosterModifier); const shellBellAfter = scene.findModifier(m => m instanceof HitHealModifier); - expect(sitrusAfter.stackCount).toBe(1); + expect(sitrusAfter?.stackCount).toBe(1); expect(candyJarAfter).toBeDefined(); - expect(candyJarAfter.stackCount).toBe(99); + expect(candyJarAfter?.stackCount).toBe(99); expect(shellBellAfter).toBeDefined(); - expect(shellBellAfter.stackCount).toBe(1); + expect(shellBellAfter?.stackCount).toBe(1); }); it("Should give the player a Shell Bell if they have max stacks of Healing Charms", async () => { @@ -296,9 +296,9 @@ describe("Delibird-y - Mystery Encounter", () => { expect(reviverSeedAfter).toBeUndefined(); expect(healingCharmAfter).toBeDefined(); - expect(healingCharmAfter.stackCount).toBe(5); + expect(healingCharmAfter?.stackCount).toBe(5); expect(shellBellAfter).toBeDefined(); - expect(shellBellAfter.stackCount).toBe(1); + expect(shellBellAfter?.stackCount).toBe(1); }); it("should be disabled if player does not have any proper items", async () => { @@ -314,7 +314,7 @@ describe("Delibird-y - Mystery Encounter", () => { await game.phaseInterceptor.to(MysteryEncounterPhase, false); const encounterPhase = scene.getCurrentPhase(); - expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name); + expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); vi.spyOn(mysteryEncounterPhase, "handleOptionSelect"); @@ -322,7 +322,7 @@ describe("Delibird-y - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 2); - expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -379,9 +379,9 @@ describe("Delibird-y - Mystery Encounter", () => { const soulDewAfter = scene.findModifier(m => m instanceof PokemonNatureWeightModifier); const berryPouchAfter = scene.findModifier(m => m instanceof PreserveBerryModifier); - expect(soulDewAfter.stackCount).toBe(1); + expect(soulDewAfter?.stackCount).toBe(1); expect(berryPouchAfter).toBeDefined(); - expect(berryPouchAfter.stackCount).toBe(1); + expect(berryPouchAfter?.stackCount).toBe(1); }); it("Should remove held item and give the player a Berry Pouch", async () => { @@ -402,7 +402,7 @@ describe("Delibird-y - Mystery Encounter", () => { expect(soulDewAfter).toBeUndefined(); expect(berryPouchAfter).toBeDefined(); - expect(berryPouchAfter.stackCount).toBe(1); + expect(berryPouchAfter?.stackCount).toBe(1); }); it("Should give the player a Shell Bell if they have max stacks of Berry Pouches", async () => { @@ -429,9 +429,9 @@ describe("Delibird-y - Mystery Encounter", () => { expect(soulDewAfter).toBeUndefined(); expect(berryPouchAfter).toBeDefined(); - expect(berryPouchAfter.stackCount).toBe(3); + expect(berryPouchAfter?.stackCount).toBe(3); expect(shellBellAfter).toBeDefined(); - expect(shellBellAfter.stackCount).toBe(1); + expect(shellBellAfter?.stackCount).toBe(1); }); it("should be disabled if player does not have any proper items", async () => { @@ -447,7 +447,7 @@ describe("Delibird-y - Mystery Encounter", () => { await game.phaseInterceptor.to(MysteryEncounterPhase, false); const encounterPhase = scene.getCurrentPhase(); - expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name); + expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); vi.spyOn(mysteryEncounterPhase, "handleOptionSelect"); @@ -455,7 +455,7 @@ describe("Delibird-y - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 3); - expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); diff --git a/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts b/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts index 1ffd1708523..89387556cc5 100644 --- a/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts @@ -35,7 +35,7 @@ describe("Department Store Sale - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); const biomeMap = new Map([ [Biome.VOLCANO, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]], @@ -65,9 +65,9 @@ describe("Department Store Sale - Mystery Encounter", () => { text: `${namespace}.intro_dialogue`, } ]); - expect(DepartmentStoreSaleEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(DepartmentStoreSaleEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(DepartmentStoreSaleEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(DepartmentStoreSaleEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(DepartmentStoreSaleEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(DepartmentStoreSaleEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(DepartmentStoreSaleEncounter.options.length).toBe(4); }); @@ -109,7 +109,7 @@ describe("Department Store Sale - Mystery Encounter", () => { it("should have shop with only TMs", async () => { await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); await runMysteryEncounterToEnd(game, 1); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); @@ -144,7 +144,7 @@ describe("Department Store Sale - Mystery Encounter", () => { it("should have shop with only Vitamins", async () => { await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); await runMysteryEncounterToEnd(game, 2); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); @@ -180,7 +180,7 @@ describe("Department Store Sale - Mystery Encounter", () => { it("should have shop with only X Items", async () => { await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); await runMysteryEncounterToEnd(game, 3); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); @@ -216,7 +216,7 @@ describe("Department Store Sale - Mystery Encounter", () => { it("should have shop with only Pokeballs", async () => { await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); await runMysteryEncounterToEnd(game, 4); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); diff --git a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts index 31f41a32701..e9c5e303ec1 100644 --- a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts @@ -42,7 +42,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -65,9 +65,9 @@ describe("Fiery Fallout - Mystery Encounter", () => { expect(FieryFalloutEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON); expect(FieryFalloutEncounter.dialogue).toBeDefined(); expect(FieryFalloutEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]); - expect(FieryFalloutEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(FieryFalloutEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(FieryFalloutEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(FieryFalloutEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(FieryFalloutEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(FieryFalloutEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(FieryFalloutEncounter.options.length).toBe(3); }); @@ -106,7 +106,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { expect(FieryFalloutEncounter.onInit).toBeDefined(); FieryFalloutEncounter.populateDialogueTokensFromRequirements(scene); - const onInitResult = onInit(scene); + const onInitResult = onInit!(scene); expect(FieryFalloutEncounter.enemyPartyConfigs).toEqual([ { @@ -152,10 +152,10 @@ describe("Fiery Fallout - Mystery Encounter", () => { const phaseSpy = vi.spyOn(scene, "pushPhase"); await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty); - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(2); expect(enemyField[0].species.speciesId).toBe(Species.VOLCARONA); expect(enemyField[1].species.speciesId).toBe(Species.VOLCARONA); @@ -169,10 +169,10 @@ describe("Fiery Fallout - Mystery Encounter", () => { it("should give charcoal to lead pokemon", async () => { await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty); - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const leadPokemonId = scene.getParty()?.[0].id; const leadPokemonItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier @@ -202,9 +202,9 @@ describe("Fiery Fallout - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty); const party = scene.getParty(); - const lapras = party.find((pkm) => pkm.species.speciesId === Species.LAPRAS); + const lapras = party.find((pkm) => pkm.species.speciesId === Species.LAPRAS)!; lapras.status = new Status(StatusEffect.POISON); - const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA); + const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA)!; vi.spyOn(abra, "isAllowedInBattle").mockReturnValue(false); await runMysteryEncounterToEnd(game, 2); @@ -251,7 +251,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 3); // await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const leadPokemonId = scene.getParty()?.[0].id; const leadPokemonItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier @@ -274,12 +274,12 @@ describe("Fiery Fallout - Mystery Encounter", () => { await game.phaseInterceptor.to(MysteryEncounterPhase, false); const encounterPhase = scene.getCurrentPhase(); - expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name); + expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const continueEncounterSpy = vi.spyOn((encounterPhase as MysteryEncounterPhase), "continueEncounter"); await runSelectMysteryEncounterOption(game, 3); - expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(continueEncounterSpy).not.toHaveBeenCalled(); }); }); diff --git a/src/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts b/src/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts index 1c8cee8f8c3..a75fe05be0b 100644 --- a/src/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts @@ -38,7 +38,7 @@ describe("Fight or Flight - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -60,9 +60,9 @@ describe("Fight or Flight - Mystery Encounter", () => { expect(FightOrFlightEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON); expect(FightOrFlightEncounter.dialogue).toBeDefined(); expect(FightOrFlightEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]); - expect(FightOrFlightEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(FightOrFlightEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(FightOrFlightEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(FightOrFlightEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(FightOrFlightEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(FightOrFlightEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(FightOrFlightEncounter.options.length).toBe(3); }); @@ -91,12 +91,12 @@ describe("Fight or Flight - Mystery Encounter", () => { expect(FightOrFlightEncounter.onInit).toBeDefined(); FightOrFlightEncounter.populateDialogueTokensFromRequirements(scene); - const onInitResult = onInit(scene); + const onInitResult = onInit!(scene); const config = FightOrFlightEncounter.enemyPartyConfigs[0]; expect(config).toBeDefined(); expect(config.levelAdditiveMultiplier).toBe(1); - expect(config.pokemonConfigs[0].isBoss).toBe(true); + expect(config.pokemonConfigs?.[0].isBoss).toBe(true); expect(onInitResult).toBe(true); }); @@ -120,12 +120,12 @@ describe("Fight or Flight - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.FIGHT_OR_FLIGHT, defaultParty); const config = game.scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]; - const speciesToSpawn = config.pokemonConfigs[0].species.speciesId; + const speciesToSpawn = config.pokemonConfigs?.[0].species.speciesId; - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); }); @@ -135,10 +135,10 @@ describe("Fight or Flight - Mystery Encounter", () => { const item = game.scene.currentBattle.mysteryEncounter.misc; - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); @@ -171,7 +171,7 @@ describe("Fight or Flight - Mystery Encounter", () => { await game.phaseInterceptor.to(MysteryEncounterPhase, false); const encounterPhase = scene.getCurrentPhase(); - expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name); + expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); vi.spyOn(mysteryEncounterPhase, "handleOptionSelect"); @@ -179,7 +179,7 @@ describe("Fight or Flight - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 2); - expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -196,7 +196,7 @@ describe("Fight or Flight - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); diff --git a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts index c9d2d276ecb..f77d9962f01 100644 --- a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -36,7 +36,7 @@ describe("Lost at Sea - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -59,9 +59,9 @@ describe("Lost at Sea - Mystery Encounter", () => { expect(LostAtSeaEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON); expect(LostAtSeaEncounter.dialogue).toBeDefined(); expect(LostAtSeaEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]); - expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(LostAtSeaEncounter.options.length).toBe(3); }); @@ -98,7 +98,7 @@ describe("Lost at Sea - Mystery Encounter", () => { expect(LostAtSeaEncounter.onInit).toBeDefined(); LostAtSeaEncounter.populateDialogueTokensFromRequirements(scene); - const onInitResult = onInit(scene); + const onInitResult = onInit!(scene); expect(LostAtSeaEncounter.dialogueTokens?.damagePercentage).toBe("25"); expect(LostAtSeaEncounter.dialogueTokens?.option1RequiredMove).toBe(Moves[Moves.SURF]); @@ -129,12 +129,12 @@ describe("Lost at Sea - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty); const party = game.scene.getParty(); - const blastoise = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT); - const expBefore = blastoise.exp; + const blastoise = party.find((pkm) => pkm.species.speciesId === Species.BLASTOISE); + const expBefore = blastoise!.exp; - await runMysteryEncounterToEnd(game, 2); + await runMysteryEncounterToEnd(game, 1); - expect(blastoise.exp).toBe(expBefore + Math.floor(laprasSpecies.baseExp * defaultWave / 5 + 1)); + expect(blastoise?.exp).toBe(expBefore + Math.floor(laprasSpecies.baseExp * defaultWave / 5 + 1)); }); it("should leave encounter without battle", async () => { @@ -152,7 +152,7 @@ describe("Lost at Sea - Mystery Encounter", () => { await game.phaseInterceptor.to(MysteryEncounterPhase, false); const encounterPhase = scene.getCurrentPhase(); - expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name); + expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); vi.spyOn(mysteryEncounterPhase, "handleOptionSelect"); @@ -160,7 +160,7 @@ describe("Lost at Sea - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 1); - expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -194,11 +194,11 @@ describe("Lost at Sea - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty); const party = game.scene.getParty(); const pidgeot = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT); - const expBefore = pidgeot.exp; + const expBefore = pidgeot!.exp; await runMysteryEncounterToEnd(game, 2); - expect(pidgeot.exp).toBe(expBefore + Math.floor(laprasBaseExp * defaultWave / 5 + 1)); + expect(pidgeot!.exp).toBe(expBefore + Math.floor(laprasBaseExp * defaultWave / 5 + 1)); }); it("should leave encounter without battle", async () => { @@ -216,7 +216,7 @@ describe("Lost at Sea - Mystery Encounter", () => { await game.phaseInterceptor.to(MysteryEncounterPhase, false); const encounterPhase = scene.getCurrentPhase(); - expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name); + expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); vi.spyOn(mysteryEncounterPhase, "handleOptionSelect"); @@ -224,7 +224,7 @@ describe("Lost at Sea - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 2); - expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -254,7 +254,7 @@ describe("Lost at Sea - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty); const party = game.scene.getParty(); - const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA); + const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA)!; vi.spyOn(abra, "isAllowedInBattle").mockReturnValue(false); await runMysteryEncounterToEnd(game, 3); diff --git a/src/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts b/src/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts index 31fb03cb7d3..67cabad2af4 100644 --- a/src/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts @@ -40,7 +40,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); const biomeMap = new Map([ [Biome.VOLCANO, [MysteryEncounterType.FIGHT_OR_FLIGHT]], @@ -64,9 +64,9 @@ describe("Mysterious Challengers - Mystery Encounter", () => { expect(MysteriousChallengersEncounter.encounterTier).toBe(MysteryEncounterTier.GREAT); expect(MysteriousChallengersEncounter.dialogue).toBeDefined(); expect(MysteriousChallengersEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]); - expect(MysteriousChallengersEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(MysteriousChallengersEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(MysteriousChallengersEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(MysteriousChallengersEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(MysteriousChallengersEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(MysteriousChallengersEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(MysteriousChallengersEncounter.options.length).toBe(3); }); @@ -105,7 +105,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => { expect(encounter.onInit).toBeDefined(); encounter.populateDialogueTokensFromRequirements(scene); - const onInitResult = onInit(scene); + const onInitResult = onInit!(scene); expect(encounter.enemyPartyConfigs).toBeDefined(); expect(encounter.enemyPartyConfigs.length).toBe(3); @@ -125,11 +125,11 @@ describe("Mysterious Challengers - Mystery Encounter", () => { female: expect.any(Boolean), } ]); - expect(encounter.enemyPartyConfigs[1].trainerConfig.partyTemplates[0]).toEqual(new TrainerPartyCompoundTemplate( + expect(encounter.enemyPartyConfigs[1].trainerConfig?.partyTemplates[0]).toEqual(new TrainerPartyCompoundTemplate( new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, false, true), new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE, false, true) )); - expect(encounter.enemyPartyConfigs[2].trainerConfig.partyTemplates[0]).toEqual(new TrainerPartyCompoundTemplate( + expect(encounter.enemyPartyConfigs[2].trainerConfig?.partyTemplates[0]).toEqual(new TrainerPartyCompoundTemplate( new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(3, PartyMemberStrength.STRONG), new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER)) @@ -157,19 +157,19 @@ describe("Mysterious Challengers - Mystery Encounter", () => { it("should start battle against the trainer", async () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); - expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.mysteryEncounter.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); }); it("should have normal trainer rewards after battle", async () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); @@ -199,19 +199,19 @@ describe("Mysterious Challengers - Mystery Encounter", () => { it("should start battle against the trainer", async () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); - await runMysteryEncounterToEnd(game, 2, null, true); + await runMysteryEncounterToEnd(game, 2, undefined, true); - expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.mysteryEncounter.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); }); it("should have hard trainer rewards after battle", async () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); - await runMysteryEncounterToEnd(game, 2, null, true); + await runMysteryEncounterToEnd(game, 2, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); @@ -242,19 +242,19 @@ describe("Mysterious Challengers - Mystery Encounter", () => { it("should start battle against the trainer", async () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); - await runMysteryEncounterToEnd(game, 3, null, true); + await runMysteryEncounterToEnd(game, 3, undefined, true); - expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.mysteryEncounter.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); }); it("should have brutal trainer rewards after battle", async () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); - await runMysteryEncounterToEnd(game, 3, null, true); + await runMysteryEncounterToEnd(game, 3, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); diff --git a/src/test/mystery-encounter/encounters/part-timer-encounter.test.ts b/src/test/mystery-encounter/encounters/part-timer-encounter.test.ts index 95e358bb6ae..df94e4fbe3e 100644 --- a/src/test/mystery-encounter/encounters/part-timer-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/part-timer-encounter.test.ts @@ -36,7 +36,7 @@ describe("Part-Timer - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); const biomeMap = new Map([ [Biome.VOLCANO, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]], @@ -66,9 +66,9 @@ describe("Part-Timer - Mystery Encounter", () => { text: `${namespace}.intro_dialogue`, } ]); - expect(PartTimerEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(PartTimerEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(PartTimerEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(PartTimerEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(PartTimerEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(PartTimerEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(PartTimerEncounter.options.length).toBe(3); }); @@ -127,7 +127,7 @@ describe("Part-Timer - Mystery Encounter", () => { // Expect PP of mon's moves to have been reduced to 2 const moves = scene.getParty()[0].moveset; for (const move of moves) { - expect(move.getMovePp() - move.ppUsed).toBe(2); + expect((move?.getMovePp() ?? 0) - (move?.ppUsed ?? 0)).toBe(2); } }); @@ -147,7 +147,7 @@ describe("Part-Timer - Mystery Encounter", () => { // Expect PP of mon's moves to have been reduced to 2 const moves = scene.getParty()[1].moveset; for (const move of moves) { - expect(move.getMovePp() - move.ppUsed).toBe(2); + expect((move?.getMovePp() ?? 0) - (move?.ppUsed ?? 0)).toBe(2); } }); @@ -192,7 +192,7 @@ describe("Part-Timer - Mystery Encounter", () => { // Expect PP of mon's moves to have been reduced to 2 const moves = scene.getParty()[2].moveset; for (const move of moves) { - expect(move.getMovePp() - move.ppUsed).toBe(2); + expect((move?.getMovePp() ?? 0) - (move?.ppUsed ?? 0)).toBe(2); } }); @@ -212,7 +212,7 @@ describe("Part-Timer - Mystery Encounter", () => { // Expect PP of mon's moves to have been reduced to 2 const moves = scene.getParty()[3].moveset; for (const move of moves) { - expect(move.getMovePp() - move.ppUsed).toBe(2); + expect((move?.getMovePp() ?? 0) - (move?.ppUsed ?? 0)).toBe(2); } }); @@ -252,7 +252,7 @@ describe("Part-Timer - Mystery Encounter", () => { await game.phaseInterceptor.to(MysteryEncounterPhase, false); const encounterPhase = scene.getCurrentPhase(); - expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name); + expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); vi.spyOn(mysteryEncounterPhase, "handleOptionSelect"); @@ -260,7 +260,7 @@ describe("Part-Timer - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 3); - expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -279,7 +279,7 @@ describe("Part-Timer - Mystery Encounter", () => { // Expect PP of mon's moves to have been reduced to 2 const moves = scene.getParty()[0].moveset; for (const move of moves) { - expect(move.getMovePp() - move.ppUsed).toBe(2); + expect((move?.getMovePp() ?? 0) - (move?.ppUsed ?? 0)).toBe(2); } }); diff --git a/src/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts b/src/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts index 833503e2636..7dcf10669d6 100644 --- a/src/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts @@ -35,7 +35,7 @@ describe("The Pokemon Salesman - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); const biomeMap = new Map([ [Biome.VOLCANO, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]], @@ -62,9 +62,9 @@ describe("The Pokemon Salesman - Mystery Encounter", () => { { text: `${namespace}.intro` }, { speaker: `${namespace}.speaker`, text: `${namespace}.intro_dialogue` } ]); - expect(ThePokemonSalesmanEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(ThePokemonSalesmanEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(ThePokemonSalesmanEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(ThePokemonSalesmanEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(ThePokemonSalesmanEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(ThePokemonSalesmanEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(ThePokemonSalesmanEncounter.options.length).toBe(2); }); @@ -101,7 +101,7 @@ describe("The Pokemon Salesman - Mystery Encounter", () => { expect(ThePokemonSalesmanEncounter.onInit).toBeDefined(); ThePokemonSalesmanEncounter.populateDialogueTokensFromRequirements(scene); - const onInitResult = onInit(scene); + const onInitResult = onInit!(scene); expect(ThePokemonSalesmanEncounter.dialogueTokens?.purchasePokemon).toBeDefined(); expect(ThePokemonSalesmanEncounter.dialogueTokens?.price).toBeDefined(); @@ -167,7 +167,7 @@ describe("The Pokemon Salesman - Mystery Encounter", () => { await game.phaseInterceptor.to(MysteryEncounterPhase, false); const encounterPhase = scene.getCurrentPhase(); - expect(encounterPhase.constructor.name).toBe(MysteryEncounterPhase.name); + expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); vi.spyOn(mysteryEncounterPhase, "handleOptionSelect"); @@ -175,7 +175,7 @@ describe("The Pokemon Salesman - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 1); - expect(scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); diff --git a/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts b/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts index 5c526ddeb57..e68ee9753df 100644 --- a/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts @@ -11,7 +11,6 @@ import { runMysteryEncounterToEnd, skipBattleRunMysteryEncounterRewardsPhase } f import { CommandPhase, MovePhase, SelectModifierPhase } from "#app/phases"; import { Moves } from "#enums/moves"; import BattleScene from "#app/battle-scene"; -import * as Modifiers from "#app/modifier/modifier"; import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters/the-strong-stuff-encounter"; import { Nature } from "#app/data/nature"; import { BerryType } from "#enums/berry-type"; @@ -19,7 +18,7 @@ import { BattlerTagType } from "#enums/battler-tag-type"; import { PokemonMove } from "#app/field/pokemon"; import { Mode } from "#app/ui/ui"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { PokemonBaseStatTotalModifier } from "#app/modifier/modifier"; +import { BerryModifier, PokemonBaseStatTotalModifier } from "#app/modifier/modifier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils"; @@ -45,7 +44,7 @@ describe("The Strong Stuff - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -68,9 +67,9 @@ describe("The Strong Stuff - Mystery Encounter", () => { expect(TheStrongStuffEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON); expect(TheStrongStuffEncounter.dialogue).toBeDefined(); expect(TheStrongStuffEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]); - expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(TheStrongStuffEncounter.options.length).toBe(2); }); @@ -109,7 +108,7 @@ describe("The Strong Stuff - Mystery Encounter", () => { expect(TheStrongStuffEncounter.onInit).toBeDefined(); TheStrongStuffEncounter.populateDialogueTokensFromRequirements(scene); - const onInitResult = onInit(scene); + const onInitResult = onInit!(scene); expect(TheStrongStuffEncounter.enemyPartyConfigs).toEqual([ { @@ -198,19 +197,19 @@ describe("The Strong Stuff - Mystery Encounter", () => { const phaseSpy = vi.spyOn(scene, "pushPhase"); await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty); - await runMysteryEncounterToEnd(game, 2, null, true); + await runMysteryEncounterToEnd(game, 2, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(Species.SHUCKLE); expect(enemyField[0].summonData.battleStats).toEqual([0, 2, 0, 2, 0, 0, 0]); - const shuckleItems = scene.getModifiers(Modifiers.BerryModifier, false); + const shuckleItems = enemyField[0].getHeldItems(); expect(shuckleItems.length).toBe(4); - expect(shuckleItems.find(m => m.berryType === BerryType.SITRUS)?.stackCount).toBe(1); - expect(shuckleItems.find(m => m.berryType === BerryType.GANLON)?.stackCount).toBe(1); - expect(shuckleItems.find(m => m.berryType === BerryType.APICOT)?.stackCount).toBe(1); - expect(shuckleItems.find(m => m.berryType === BerryType.LUM)?.stackCount).toBe(2); + expect(shuckleItems.find(m => m instanceof BerryModifier && m.berryType === BerryType.SITRUS)?.stackCount).toBe(1); + expect(shuckleItems.find(m => m instanceof BerryModifier && m.berryType === BerryType.GANLON)?.stackCount).toBe(1); + expect(shuckleItems.find(m => m instanceof BerryModifier && m.berryType === BerryType.APICOT)?.stackCount).toBe(1); + expect(shuckleItems.find(m => m instanceof BerryModifier && m.berryType === BerryType.LUM)?.stackCount).toBe(2); expect(enemyField[0].moveset).toEqual([new PokemonMove(Moves.INFESTATION), new PokemonMove(Moves.SALT_CURE), new PokemonMove(Moves.GASTRO_ACID), new PokemonMove(Moves.HEAL_ORDER)]); // Should have used moves pre-battle @@ -222,10 +221,10 @@ describe("The Strong Stuff - Mystery Encounter", () => { it("should have Soul Dew in rewards", async () => { await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty); - await runMysteryEncounterToEnd(game, 2, null, true); + await runMysteryEncounterToEnd(game, 2, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); diff --git a/src/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts b/src/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts index 667c2d2ec3d..f30acd3b0a4 100644 --- a/src/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts @@ -43,7 +43,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); const biomeMap = new Map([ [Biome.VOLCANO, [MysteryEncounterType.FIGHT_OR_FLIGHT]], @@ -73,9 +73,9 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { text: `${namespace}.intro_dialogue`, } ]); - expect(TheWinstrateChallengeEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(TheWinstrateChallengeEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(TheWinstrateChallengeEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(TheWinstrateChallengeEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(TheWinstrateChallengeEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(TheWinstrateChallengeEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(TheWinstrateChallengeEncounter.options.length).toBe(2); }); @@ -114,7 +114,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { expect(encounter.onInit).toBeDefined(); encounter.populateDialogueTokensFromRequirements(scene); - const onInitResult = onInit(scene); + const onInitResult = onInit!(scene); expect(encounter.enemyPartyConfigs).toBeDefined(); expect(encounter.enemyPartyConfigs.length).toBe(5); @@ -273,42 +273,42 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { it("should battle all 5 trainers for a Macho Brace reward", async () => { await game.runToMysteryEncounter(MysteryEncounterType.THE_WINSTRATE_CHALLENGE, defaultParty); - await runMysteryEncounterToEnd(game, 1, null, true); + await runMysteryEncounterToEnd(game, 1, undefined, true); - expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(scene.currentBattle.trainer).toBeDefined(); - expect(scene.currentBattle.trainer.config.trainerType).toBe(TrainerType.VICTOR); + expect(scene.currentBattle.trainer!.config.trainerType).toBe(TrainerType.VICTOR); expect(scene.currentBattle.mysteryEncounter.enemyPartyConfigs.length).toBe(4); expect(scene.currentBattle.mysteryEncounter.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); await skipBattleToNextBattle(game); expect(scene.currentBattle.trainer).toBeDefined(); - expect(scene.currentBattle.trainer.config.trainerType).toBe(TrainerType.VICTORIA); + expect(scene.currentBattle.trainer!.config.trainerType).toBe(TrainerType.VICTORIA); expect(scene.currentBattle.mysteryEncounter.enemyPartyConfigs.length).toBe(3); expect(scene.currentBattle.mysteryEncounter.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); await skipBattleToNextBattle(game); expect(scene.currentBattle.trainer).toBeDefined(); - expect(scene.currentBattle.trainer.config.trainerType).toBe(TrainerType.VIVI); + expect(scene.currentBattle.trainer!.config.trainerType).toBe(TrainerType.VIVI); expect(scene.currentBattle.mysteryEncounter.enemyPartyConfigs.length).toBe(2); expect(scene.currentBattle.mysteryEncounter.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); await skipBattleToNextBattle(game); expect(scene.currentBattle.trainer).toBeDefined(); - expect(scene.currentBattle.trainer.config.trainerType).toBe(TrainerType.VICKY); + expect(scene.currentBattle.trainer!.config.trainerType).toBe(TrainerType.VICKY); expect(scene.currentBattle.mysteryEncounter.enemyPartyConfigs.length).toBe(1); expect(scene.currentBattle.mysteryEncounter.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); await skipBattleToNextBattle(game); expect(scene.currentBattle.trainer).toBeDefined(); - expect(scene.currentBattle.trainer.config.trainerType).toBe(TrainerType.VITO); + expect(scene.currentBattle.trainer!.config.trainerType).toBe(TrainerType.VITO); expect(scene.currentBattle.mysteryEncounter.enemyPartyConfigs.length).toBe(0); expect(scene.currentBattle.mysteryEncounter.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); // Should have Macho Brace in the rewards await skipBattleToNextBattle(game, true); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); @@ -348,7 +348,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { it("should have a Rarer Candy in the rewards", async () => { await game.runToMysteryEncounter(MysteryEncounterType.THE_WINSTRATE_CHALLENGE, defaultParty); await runMysteryEncounterToEnd(game, 2); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); diff --git a/src/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts b/src/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts index 3d4cd8855cd..37e7ffd1e40 100644 --- a/src/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts @@ -41,7 +41,7 @@ describe("Trash to Treasure - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -63,9 +63,9 @@ describe("Trash to Treasure - Mystery Encounter", () => { expect(TrashToTreasureEncounter.encounterTier).toBe(MysteryEncounterTier.ULTRA); expect(TrashToTreasureEncounter.dialogue).toBeDefined(); expect(TrashToTreasureEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]); - expect(TrashToTreasureEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(TrashToTreasureEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(TrashToTreasureEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(TrashToTreasureEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(TrashToTreasureEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(TrashToTreasureEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(TrashToTreasureEncounter.options.length).toBe(2); }); @@ -96,7 +96,7 @@ describe("Trash to Treasure - Mystery Encounter", () => { expect(TrashToTreasureEncounter.onInit).toBeDefined(); TrashToTreasureEncounter.populateDialogueTokensFromRequirements(scene); - const onInitResult = onInit(scene); + const onInitResult = onInit!(scene); expect(TrashToTreasureEncounter.enemyPartyConfigs).toEqual([ { @@ -138,19 +138,19 @@ describe("Trash to Treasure - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty); await runMysteryEncounterToEnd(game, 1); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const leftovers = scene.findModifier(m => m instanceof TurnHealModifier) as TurnHealModifier; expect(leftovers).toBeDefined(); - expect(leftovers.stackCount).toBe(2); + expect(leftovers?.stackCount).toBe(2); const shellBell = scene.findModifier(m => m instanceof HitHealModifier) as HitHealModifier; expect(shellBell).toBeDefined(); - expect(shellBell.stackCount).toBe(2); + expect(shellBell?.stackCount).toBe(2); const blackSludge = scene.findModifier(m => m instanceof RemoveHealShopModifier) as RemoveHealShopModifier; expect(blackSludge).toBeDefined(); - expect(blackSludge.stackCount).toBe(1); + expect(blackSludge?.stackCount).toBe(1); }); it("should leave encounter without battle", async () => { @@ -183,10 +183,10 @@ describe("Trash to Treasure - Mystery Encounter", () => { const phaseSpy = vi.spyOn(scene, "pushPhase"); await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty); - await runMysteryEncounterToEnd(game, 2, null, true); + await runMysteryEncounterToEnd(game, 2, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(Species.GARBODOR); expect(enemyField[0].moveset).toEqual([new PokemonMove(Moves.PAYBACK), new PokemonMove(Moves.GUNK_SHOT), new PokemonMove(Moves.STOMPING_TANTRUM), new PokemonMove(Moves.DRAIN_PUNCH)]); @@ -200,10 +200,10 @@ describe("Trash to Treasure - Mystery Encounter", () => { it("should have 2 Rogue, 1 Ultra, 1 Great in rewards", async () => { await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty); - await runMysteryEncounterToEnd(game, 2, null, true); + await runMysteryEncounterToEnd(game, 2, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); diff --git a/src/test/mystery-encounter/encounters/weird-dream-encounter.test.ts b/src/test/mystery-encounter/encounters/weird-dream-encounter.test.ts index 0650b3b0ad4..fc5b868f1a1 100644 --- a/src/test/mystery-encounter/encounters/weird-dream-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/weird-dream-encounter.test.ts @@ -36,7 +36,7 @@ describe("Weird Dream - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(true); + game.override.disableTrainerWaves(); vi.spyOn(EncounterTransformationSequence, "doPokemonTransformationSequence").mockImplementation(() => new Promise(resolve => resolve())); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( @@ -67,9 +67,9 @@ describe("Weird Dream - Mystery Encounter", () => { text: `${namespace}.intro_dialogue`, }, ]); - expect(WeirdDreamEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}.title`); - expect(WeirdDreamEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}.description`); - expect(WeirdDreamEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}.query`); + expect(WeirdDreamEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); + expect(WeirdDreamEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`); + expect(WeirdDreamEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`); expect(WeirdDreamEncounter.options.length).toBe(2); }); @@ -99,7 +99,7 @@ describe("Weird Dream - Mystery Encounter", () => { expect(WeirdDreamEncounter.onInit).toBeDefined(); WeirdDreamEncounter.populateDialogueTokensFromRequirements(scene); - const onInitResult = onInit(scene); + const onInitResult = onInit!(scene); expect(loadBgmSpy).toHaveBeenCalled(); expect(onInitResult).toBe(true); @@ -129,7 +129,7 @@ describe("Weird Dream - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const pokemonAfter = scene.getParty(); const bstsAfter = pokemonAfter.map(pokemon => pokemon.getSpeciesForm().getBaseStatTotal()); @@ -138,7 +138,7 @@ describe("Weird Dream - Mystery Encounter", () => { for (let i = 0; i < pokemonAfter.length; i++) { const newPokemon = pokemonAfter[i]; expect(newPokemon.getSpeciesForm().speciesId).not.toBe(pokemonPrior[i].getSpeciesForm().speciesId); - expect(newPokemon.mysteryEncounterData.types.length).toBe(2); + expect(newPokemon.mysteryEncounterData?.types.length).toBe(2); } const plus90To110 = bstDiff.filter(bst => bst > 80); @@ -152,7 +152,7 @@ describe("Weird Dream - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty); await runMysteryEncounterToEnd(game, 1); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); diff --git a/src/test/mystery-encounter/mystery-encounter-utils.test.ts b/src/test/mystery-encounter/mystery-encounter-utils.test.ts index a0e055e3bcf..26442f9eaf4 100644 --- a/src/test/mystery-encounter/mystery-encounter-utils.test.ts +++ b/src/test/mystery-encounter/mystery-encounter-utils.test.ts @@ -230,7 +230,7 @@ describe("Mystery Encounter Utils", () => { it("gets species of specified types", () => { // Only 9 tiers are: Koraidon, Miraidon, Arceus, Rayquaza, Kyogre, Groudon, Zacian - const result = getRandomSpeciesByStarterTier(9, null, [Type.GROUND]); + const result = getRandomSpeciesByStarterTier(9, undefined, [Type.GROUND]); const pokeSpecies = getPokemonSpecies(result); expect(pokeSpecies.speciesId).toBe(Species.GROUDON); }); diff --git a/src/test/mystery-encounter/mystery-encounter.test.ts b/src/test/mystery-encounter/mystery-encounter.test.ts index 0ba28bb77ec..d2a2e7f9d92 100644 --- a/src/test/mystery-encounter/mystery-encounter.test.ts +++ b/src/test/mystery-encounter/mystery-encounter.test.ts @@ -29,14 +29,14 @@ describe("Mystery Encounters", () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(MysteryEncounterPhase.name); }); it("", async () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(MysteryEncounterPhase.name); }); it("spawns mysterious challengers encounter", async () => { diff --git a/src/test/phases/mystery-encounter-phase.test.ts b/src/test/phases/mystery-encounter-phase.test.ts index 74ed7ff190d..5e02f6c94ab 100644 --- a/src/test/phases/mystery-encounter-phase.test.ts +++ b/src/test/phases/mystery-encounter-phase.test.ts @@ -37,7 +37,7 @@ describe("Mystery Encounter Phases", () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + expect(game.scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); }); it("Runs MysteryEncounterPhase", async() => { @@ -73,7 +73,7 @@ describe("Mystery Encounter Phases", () => { handler.processInput(Button.ACTION); // Waitfor required so that option select messages and preOptionPhase logic are handled - await vi.waitFor(() => expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterOptionSelectedPhase.name)); + await vi.waitFor(() => expect(game.scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterOptionSelectedPhase.name)); expect(game.scene.ui.getMode()).toBe(Mode.MESSAGE); expect(dialogueSpy).toHaveBeenCalledTimes(1); expect(messageSpy).toHaveBeenCalledTimes(2); diff --git a/src/test/phases/select-modifier-phase.test.ts b/src/test/phases/select-modifier-phase.test.ts index 7efcf47ac03..13c907c6414 100644 --- a/src/test/phases/select-modifier-phase.test.ts +++ b/src/test/phases/select-modifier-phase.test.ts @@ -28,12 +28,6 @@ describe("SelectModifierPhase", () => { game = new GameManager(phaserGame); scene = game.scene; - vi.spyOn(scene, "resetSeed").mockImplementation(() => { - scene.waveSeed = "test"; - Phaser.Math.RND.sow([scene.waveSeed]); - scene.rngCounter = 0; - }); - initSceneWithoutEncounterPhase(scene, [Species.ABRA, Species.VOLCARONA]); }); @@ -49,7 +43,7 @@ describe("SelectModifierPhase", () => { await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); - }, 20000); + }); it("should generate random modifiers", async () => { const selectModifierPhase = new SelectModifierPhase(scene); @@ -60,10 +54,7 @@ describe("SelectModifierPhase", () => { expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; expect(modifierSelectHandler.options.length).toEqual(3); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("TEMP_STAT_BOOSTER"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.id).toEqual("POKEBALL"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.id).toEqual("BERRY"); - }, 5000); + }); it("should modify reroll cost", async () => { const options = [ @@ -73,12 +64,12 @@ describe("SelectModifierPhase", () => { ]; const selectModifierPhase1 = new SelectModifierPhase(scene); - const selectModifierPhase2 = new SelectModifierPhase(scene, 0, null, { rerollMultiplier: 2 }); + const selectModifierPhase2 = new SelectModifierPhase(scene, 0, undefined, { rerollMultiplier: 2 }); const cost1 = selectModifierPhase1.getRerollCost(options, false); const cost2 = selectModifierPhase2.getRerollCost(options, false); expect(cost2).toEqual(cost1 * 2); - }, 5000); + }); it("should generate random modifiers from reroll", async () => { let selectModifierPhase = new SelectModifierPhase(scene); @@ -88,9 +79,6 @@ describe("SelectModifierPhase", () => { expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; expect(modifierSelectHandler.options.length).toEqual(3); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("TEMP_STAT_BOOSTER"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.id).toEqual("POKEBALL"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.id).toEqual("BERRY"); // Simulate selecting reroll selectModifierPhase = new SelectModifierPhase(scene, 1, [ModifierTier.COMMON, ModifierTier.COMMON, ModifierTier.COMMON]); @@ -100,10 +88,7 @@ describe("SelectModifierPhase", () => { expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); expect(modifierSelectHandler.options.length).toEqual(3); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("TM_COMMON"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.id).toEqual("LURE"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.id).toEqual("PP_UP"); - }, 5000); + }); it("should generate random modifiers of same tier for reroll with reroll lock", async () => { // Just use fully random seed for this test @@ -137,13 +122,13 @@ describe("SelectModifierPhase", () => { expect(modifierSelectHandler.options[0].modifierTypeOption.type.tier - modifierSelectHandler.options[0].modifierTypeOption.upgradeCount).toEqual(firstRollTiers[0]); expect(modifierSelectHandler.options[1].modifierTypeOption.type.tier - modifierSelectHandler.options[1].modifierTypeOption.upgradeCount).toEqual(firstRollTiers[1]); expect(modifierSelectHandler.options[2].modifierTypeOption.type.tier - modifierSelectHandler.options[2].modifierTypeOption.upgradeCount).toEqual(firstRollTiers[2]); - }, 5000); + }); it("should generate custom modifiers", async () => { const customModifiers: CustomModifierSettings = { guaranteedModifierTypeFuncs: [modifierTypes.MEMORY_MUSHROOM, modifierTypes.TM_ULTRA, modifierTypes.LEFTOVERS, modifierTypes.AMULET_COIN, modifierTypes.GOLDEN_PUNCH] }; - const selectModifierPhase = new SelectModifierPhase(scene, 0, null, customModifiers); + const selectModifierPhase = new SelectModifierPhase(scene, 0, undefined, customModifiers); scene.pushPhase(selectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase); @@ -156,7 +141,7 @@ describe("SelectModifierPhase", () => { expect(modifierSelectHandler.options[2].modifierTypeOption.type.id).toEqual("LEFTOVERS"); expect(modifierSelectHandler.options[3].modifierTypeOption.type.id).toEqual("AMULET_COIN"); expect(modifierSelectHandler.options[4].modifierTypeOption.type.id).toEqual("GOLDEN_PUNCH"); - }, 5000); + }); it("should generate custom modifier tiers that can upgrade from luck", async () => { const customModifiers: CustomModifierSettings = { @@ -170,7 +155,7 @@ describe("SelectModifierPhase", () => { } scene.getParty().push(pokemon, pokemon, pokemon, pokemon, pokemon, pokemon); - const selectModifierPhase = new SelectModifierPhase(scene, 0, null, customModifiers); + const selectModifierPhase = new SelectModifierPhase(scene, 0, undefined, customModifiers); scene.pushPhase(selectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase); @@ -183,14 +168,14 @@ describe("SelectModifierPhase", () => { expect(modifierSelectHandler.options[2].modifierTypeOption.type.tier - modifierSelectHandler.options[2].modifierTypeOption.upgradeCount).toEqual(ModifierTier.ULTRA); expect(modifierSelectHandler.options[3].modifierTypeOption.type.tier - modifierSelectHandler.options[3].modifierTypeOption.upgradeCount).toEqual(ModifierTier.ROGUE); expect(modifierSelectHandler.options[4].modifierTypeOption.type.tier - modifierSelectHandler.options[4].modifierTypeOption.upgradeCount).toEqual(ModifierTier.MASTER); - }, 5000); + }); it("should generate custom modifiers and modifier tiers together", async () => { const customModifiers: CustomModifierSettings = { guaranteedModifierTypeFuncs: [modifierTypes.MEMORY_MUSHROOM, modifierTypes.TM_COMMON], guaranteedModifierTiers: [ModifierTier.MASTER, ModifierTier.MASTER] }; - const selectModifierPhase = new SelectModifierPhase(scene, 0, null, customModifiers); + const selectModifierPhase = new SelectModifierPhase(scene, 0, undefined, customModifiers); scene.pushPhase(selectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase); @@ -202,7 +187,7 @@ describe("SelectModifierPhase", () => { expect(modifierSelectHandler.options[1].modifierTypeOption.type.id).toEqual("TM_COMMON"); expect(modifierSelectHandler.options[2].modifierTypeOption.type.tier).toEqual(ModifierTier.MASTER); expect(modifierSelectHandler.options[3].modifierTypeOption.type.tier).toEqual(ModifierTier.MASTER); - }, 5000); + }); it("should fill remaining modifiers if fillRemaining is true with custom modifiers", async () => { const customModifiers: CustomModifierSettings = { @@ -210,7 +195,7 @@ describe("SelectModifierPhase", () => { guaranteedModifierTiers: [ModifierTier.MASTER], fillRemaining: true }; - const selectModifierPhase = new SelectModifierPhase(scene, 0, null, customModifiers); + const selectModifierPhase = new SelectModifierPhase(scene, 0, undefined, customModifiers); scene.pushPhase(selectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase); @@ -220,5 +205,5 @@ describe("SelectModifierPhase", () => { expect(modifierSelectHandler.options.length).toEqual(3); expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("MEMORY_MUSHROOM"); expect(modifierSelectHandler.options[1].modifierTypeOption.type.tier).toEqual(ModifierTier.MASTER); - }, 5000); + }); }); diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 260df8e79c5..a3e0b972f63 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -171,8 +171,8 @@ export default class GameManager { */ async runToMysteryEncounter(encounterType?: MysteryEncounterType, species?: Species[]) { if (!isNullOrUndefined(encounterType)) { - this.override.disableTrainerWaves(true); - this.override.mysteryEncounter(encounterType); + this.override.disableTrainerWaves(); + this.override.mysteryEncounter(encounterType!); } await this.runToTitle(); diff --git a/src/test/utils/gameManagerUtils.ts b/src/test/utils/gameManagerUtils.ts index f4aecbbb096..ff0f735c82d 100644 --- a/src/test/utils/gameManagerUtils.ts +++ b/src/test/utils/gameManagerUtils.ts @@ -108,5 +108,5 @@ export function initSceneWithoutEncounterPhase(scene, species?: Species[]) { scene.getParty().push(starterPokemon); }); - scene.currentBattle = new Battle(getGameMode(GameModes.CLASSIC), 5, BattleType.WILD, null, false); + scene.currentBattle = new Battle(getGameMode(GameModes.CLASSIC), 5, BattleType.WILD, undefined, false); } diff --git a/src/test/utils/helpers/overridesHelper.ts b/src/test/utils/helpers/overridesHelper.ts index d5eaee003db..40f0111ae78 100644 --- a/src/test/utils/helpers/overridesHelper.ts +++ b/src/test/utils/helpers/overridesHelper.ts @@ -8,8 +8,10 @@ import * as GameMode from "#app/game-mode"; import { GameModes, getGameMode } from "#app/game-mode"; import { ModifierOverride } from "#app/modifier/modifier-type.js"; import Overrides from "#app/overrides"; -import { vi } from "vitest"; +import { MockInstance, vi } from "vitest"; import { GameManagerHelper } from "./gameManagerHelper"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; /** * Helper to handle overrides in tests @@ -281,6 +283,41 @@ export class OverridesHelper extends GameManagerHelper { return this; } + /** + * Override the encounter chance for a mystery encounter. + * @param percentage the encounter chance in % + * @returns spy instance + */ + mysteryEncounterChance(percentage: number) { + const maxRate: number = 256; // 100% + const rate = maxRate * (percentage / 100); + const spy = vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(rate); + this.log(`Mystery encounter chance set to ${percentage}% (=${rate})!`); + return spy; + } + + /** + * Override the encounter chance for a mystery encounter. + * @returns spy instance + * @param tier + */ + mysteryEncounterTier(tier: MysteryEncounterTier): MockInstance { + const spy = vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_TIER_OVERRIDE", "get").mockReturnValue(tier); + this.log(`Mystery encounter tier set to ${tier}!`); + return spy; + } + + /** + * Override the encounter that spawns for the scene + * @param encounterType + * @returns spy instance + */ + mysteryEncounter(encounterType: MysteryEncounterType) { + const spy = vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_OVERRIDE", "get").mockReturnValue(encounterType); + this.log(`Mystery encounter override set to ${encounterType}!`); + return spy; + } + private log(...params: any[]) { console.log("Overrides:", ...params); } diff --git a/src/test/utils/mocks/mockGameObject.ts b/src/test/utils/mocks/mockGameObject.ts index 9138e0f687a..4c243ec9ca1 100644 --- a/src/test/utils/mocks/mockGameObject.ts +++ b/src/test/utils/mocks/mockGameObject.ts @@ -1,3 +1,3 @@ export interface MockGameObject { - + name: string; } diff --git a/src/test/utils/mocks/mocksContainer/mockContainer.ts b/src/test/utils/mocks/mocksContainer/mockContainer.ts index 319d702bfd9..78d84f7845b 100644 --- a/src/test/utils/mocks/mocksContainer/mockContainer.ts +++ b/src/test/utils/mocks/mocksContainer/mockContainer.ts @@ -14,7 +14,7 @@ export default class MockContainer implements MockGameObject { public frame; protected textureManager; public list: MockGameObject[] = []; - private name?: string; + name: string; constructor(textureManager: MockTextureManager, x, y) { this.x = x; diff --git a/src/test/utils/mocks/mocksContainer/mockGraphics.ts b/src/test/utils/mocks/mocksContainer/mockGraphics.ts index e026b212e16..70a38c80aa0 100644 --- a/src/test/utils/mocks/mocksContainer/mockGraphics.ts +++ b/src/test/utils/mocks/mocksContainer/mockGraphics.ts @@ -3,6 +3,7 @@ import { MockGameObject } from "../mockGameObject"; export default class MockGraphics implements MockGameObject { private scene; public list: MockGameObject[] = []; + name: string; constructor(textureManager, config) { this.scene = textureManager.scene; } diff --git a/src/test/utils/mocks/mocksContainer/mockRectangle.ts b/src/test/utils/mocks/mocksContainer/mockRectangle.ts index 26c2f74ea42..696427d10a3 100644 --- a/src/test/utils/mocks/mocksContainer/mockRectangle.ts +++ b/src/test/utils/mocks/mocksContainer/mockRectangle.ts @@ -4,6 +4,7 @@ export default class MockRectangle implements MockGameObject { private fillColor; private scene; public list: MockGameObject[] = []; + name: string; constructor(textureManager, x, y, width, height, fillColor) { this.fillColor = fillColor; diff --git a/src/test/utils/mocks/mocksContainer/mockSprite.ts b/src/test/utils/mocks/mocksContainer/mockSprite.ts index 9c566fc4bcb..6020b6aadc0 100644 --- a/src/test/utils/mocks/mocksContainer/mockSprite.ts +++ b/src/test/utils/mocks/mocksContainer/mockSprite.ts @@ -14,6 +14,7 @@ export default class MockSprite implements MockGameObject { public scene; public anims; public list: MockGameObject[] = []; + name: string; constructor(textureManager, x, y, texture) { this.textureManager = textureManager; this.scene = textureManager.scene; diff --git a/src/test/utils/mocks/mocksContainer/mockText.ts b/src/test/utils/mocks/mocksContainer/mockText.ts index 825f2474a6f..f307da99361 100644 --- a/src/test/utils/mocks/mocksContainer/mockText.ts +++ b/src/test/utils/mocks/mocksContainer/mockText.ts @@ -11,7 +11,7 @@ export default class MockText implements MockGameObject { public list: MockGameObject[] = []; public style; public text = ""; - private name?: string; + name: string; public color?: string; constructor(textureManager, x, y, content, styleOptions) { diff --git a/src/test/utils/mocks/mocksContainer/mockTexture.ts b/src/test/utils/mocks/mocksContainer/mockTexture.ts index 03bedb4751b..988e9be150f 100644 --- a/src/test/utils/mocks/mocksContainer/mockTexture.ts +++ b/src/test/utils/mocks/mocksContainer/mockTexture.ts @@ -12,6 +12,7 @@ export default class MockTexture implements MockGameObject { public source; public frames: object; public firstFrame: string; + name: string; constructor(manager, key: string, source) { this.manager = manager; diff --git a/src/test/utils/phaseInterceptor.ts b/src/test/utils/phaseInterceptor.ts index 40644683a99..d30adf11f7d 100644 --- a/src/test/utils/phaseInterceptor.ts +++ b/src/test/utils/phaseInterceptor.ts @@ -336,7 +336,7 @@ export default class PhaseInterceptor { const actionForNextPrompt = this.prompts[0]; const expireFn = actionForNextPrompt.expireFn && actionForNextPrompt.expireFn(); const currentMode = this.scene.ui.getMode(); - const currentPhase = this.scene.getCurrentPhase().constructor.name; + const currentPhase = this.scene.getCurrentPhase()?.constructor.name; const currentHandler = this.scene.ui.getHandler(); if (expireFn) { this.prompts.shift(); diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index d5bae208629..094b8264bb1 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -18,24 +18,24 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; export default class MysteryEncounterUiHandler extends UiHandler { private cursorContainer: Phaser.GameObjects.Container; - private cursorObj: Phaser.GameObjects.Image; + private cursorObj?: Phaser.GameObjects.Image; private optionsContainer: Phaser.GameObjects.Container; private tooltipWindow: Phaser.GameObjects.NineSlice; private tooltipContainer: Phaser.GameObjects.Container; - private tooltipScrollTween: Phaser.Tweens.Tween; + private tooltipScrollTween?: Phaser.Tweens.Tween; private descriptionWindow: Phaser.GameObjects.NineSlice; private descriptionContainer: Phaser.GameObjects.Container; - private descriptionScrollTween: Phaser.Tweens.Tween; + private descriptionScrollTween?: Phaser.Tweens.Tween; private rarityBall: Phaser.GameObjects.Sprite; private dexProgressWindow: Phaser.GameObjects.NineSlice; private dexProgressContainer: Phaser.GameObjects.Container; private showDexProgress: boolean = false; - private overrideSettings: OptionSelectSettings; + private overrideSettings?: OptionSelectSettings; private encounterOptions: MysteryEncounterOption[] = []; private optionsMeetsReqs: boolean[]; @@ -330,9 +330,9 @@ export default class MysteryEncounterUiHandler extends UiHandler { this.encounterOptions = this.overrideSettings?.overrideOptions ?? mysteryEncounter.options; this.optionsMeetsReqs = []; - const titleText: string = getEncounterText(this.scene, mysteryEncounter.dialogue.encounterOptionsDialogue.title, TextStyle.TOOLTIP_TITLE); - const descriptionText: string = getEncounterText(this.scene, mysteryEncounter.dialogue.encounterOptionsDialogue.description, TextStyle.TOOLTIP_CONTENT); - const queryText: string = getEncounterText(this.scene, mysteryEncounter.dialogue.encounterOptionsDialogue.query, TextStyle.TOOLTIP_CONTENT); + const titleText: string | null = getEncounterText(this.scene, mysteryEncounter.dialogue.encounterOptionsDialogue?.title, TextStyle.TOOLTIP_TITLE); + const descriptionText: string | null = getEncounterText(this.scene, mysteryEncounter.dialogue.encounterOptionsDialogue?.description, TextStyle.TOOLTIP_CONTENT); + const queryText: string | null = getEncounterText(this.scene, mysteryEncounter.dialogue.encounterOptionsDialogue?.query, TextStyle.TOOLTIP_CONTENT); // Clear options container (except cursor) this.optionsContainer.removeAll(true); @@ -355,9 +355,9 @@ export default class MysteryEncounterUiHandler extends UiHandler { } this.optionsMeetsReqs.push(option.meetsRequirements(this.scene)); - const optionDialogue = option.dialogue; + const optionDialogue = option.dialogue!; const label = !this.optionsMeetsReqs[i] && optionDialogue.disabledButtonLabel ? optionDialogue.disabledButtonLabel : optionDialogue.buttonLabel; - let text: string; + let text: string | null; if (option.hasRequirements() && this.optionsMeetsReqs[i] && (option.optionMode === MysteryEncounterOptionMode.DEFAULT_OR_SPECIAL || option.optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)) { // Options with special requirements that are met are automatically colored green text = getEncounterText(this.scene, label, TextStyle.SUMMARY_GREEN); @@ -383,7 +383,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { this.optionsContainer.add(viewPartyText); // Description Window - const titleTextObject = addBBCodeTextObject(this.scene, 0, 0, titleText, TextStyle.TOOLTIP_TITLE, { wordWrap: { width: 750 }, align: "center", lineSpacing: -8 }); + const titleTextObject = addBBCodeTextObject(this.scene, 0, 0, titleText ?? "", TextStyle.TOOLTIP_TITLE, { wordWrap: { width: 750 }, align: "center", lineSpacing: -8 }); this.descriptionContainer.add(titleTextObject); titleTextObject.setPosition(72 - titleTextObject.displayWidth / 2, 5.5); @@ -395,7 +395,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { const ballType = getPokeballAtlasKey(index); this.rarityBall.setTexture("pb", ballType); - const descriptionTextObject = addBBCodeTextObject(this.scene, 6, 25, descriptionText, TextStyle.TOOLTIP_CONTENT, { wordWrap: { width: 830 } }); + const descriptionTextObject = addBBCodeTextObject(this.scene, 6, 25, descriptionText ?? "", TextStyle.TOOLTIP_CONTENT, { wordWrap: { width: 830 } }); // Sets up the mask that hides the description text to give an illusion of scrolling const descriptionTextMaskRect = this.scene.make.graphics({}); @@ -412,7 +412,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { if (this.descriptionScrollTween) { this.descriptionScrollTween.remove(); - this.descriptionScrollTween = null; + this.descriptionScrollTween = undefined; } // Animates the description text moving upwards @@ -429,7 +429,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { this.descriptionContainer.add(descriptionTextObject); - const queryTextObject = addBBCodeTextObject(this.scene, 0, 0, queryText, TextStyle.TOOLTIP_CONTENT, { wordWrap: { width: 830 } }); + const queryTextObject = addBBCodeTextObject(this.scene, 0, 0, queryText ?? "", TextStyle.TOOLTIP_CONTENT, { wordWrap: { width: 830 } }); this.descriptionContainer.add(queryTextObject); queryTextObject.setPosition(75 - queryTextObject.displayWidth / 2, 90); @@ -460,9 +460,9 @@ export default class MysteryEncounterUiHandler extends UiHandler { return; } - let text: string; + let text: string | null; const cursorOption = this.encounterOptions[cursor]; - const optionDialogue = cursorOption.dialogue; + const optionDialogue = cursorOption.dialogue!; if (!this.optionsMeetsReqs[cursor] && (cursorOption.optionMode === MysteryEncounterOptionMode.DISABLED_OR_DEFAULT || cursorOption.optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL) && optionDialogue.disabledButtonTooltip) { text = getEncounterText(this.scene, optionDialogue.disabledButtonTooltip, TextStyle.TOOLTIP_CONTENT); } else { @@ -470,9 +470,11 @@ export default class MysteryEncounterUiHandler extends UiHandler { } // Auto-color options green/blue for good/bad by looking for (+)/(-) - const primaryStyleString = [...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))][0]; - text = text.replace(/(\(\+\)[^\(\[]*)/gi, substring => "[/color][/shadow]" + getBBCodeFrag(substring, TextStyle.SUMMARY_GREEN) + "[/color][/shadow]" + primaryStyleString); - text = text.replace(/(\(\-\)[^\(\[]*)/gi, substring => "[/color][/shadow]" + getBBCodeFrag(substring, TextStyle.SUMMARY_BLUE) + "[/color][/shadow]" + primaryStyleString); + if (text) { + const primaryStyleString = [...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))!][0]; + text = text.replace(/(\(\+\)[^\(\[]*)/gi, substring => "[/color][/shadow]" + getBBCodeFrag(substring, TextStyle.SUMMARY_GREEN) + "[/color][/shadow]" + primaryStyleString); + text = text.replace(/(\(\-\)[^\(\[]*)/gi, substring => "[/color][/shadow]" + getBBCodeFrag(substring, TextStyle.SUMMARY_BLUE) + "[/color][/shadow]" + primaryStyleString); + } if (text) { const tooltipTextObject = addBBCodeTextObject(this.scene, 6, 7, text, TextStyle.TOOLTIP_CONTENT, { wordWrap: { width: 600 }, fontSize: "72px" }); @@ -492,7 +494,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { if (this.tooltipScrollTween) { this.tooltipScrollTween.remove(); - this.tooltipScrollTween = null; + this.tooltipScrollTween = undefined; } // Animates the tooltip text moving upwards @@ -518,7 +520,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { clear(): void { super.clear(); - this.overrideSettings = null; + this.overrideSettings = undefined; this.optionsContainer.setVisible(false); this.optionsContainer.removeAll(true); this.dexProgressContainer.setVisible(false); @@ -534,7 +536,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { if (this.cursorObj) { this.cursorObj.destroy(); } - this.cursorObj = null; + this.cursorObj = undefined; } /** diff --git a/src/ui/text.ts b/src/ui/text.ts index 262e1280c38..62534f8ee45 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -243,7 +243,7 @@ export function getBBCodeFrag(content: string, textStyle: TextStyle, uiTheme: Ui export function getTextWithColors(content: string, primaryStyle: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string { // Apply primary styling before anything else let text = getBBCodeFrag(content, primaryStyle, uiTheme) + "[/color][/shadow]"; - const primaryStyleString = [...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))][0]; + const primaryStyleString = [...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))!][0]; // Set custom colors text = text.replace(/@\[([^{]*)\]{([^}]*)}/gi, (substring, textStyle: string, textToColor: string) => {