From 5997744aa2853909170c9b04298f06fee7c09884 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer <110984302+ben-lear@users.noreply.github.com> Date: Thu, 26 Sep 2024 18:43:28 -0400 Subject: [PATCH] [Beta][Bug] Major/minor ME bug fixes (#4451) * major/minor ME bug fixes * potential fix for failed save with rental pokemon * Update src/system/game-data.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> * Update src/system/game-data.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> * more bug fix cleanup and PR feedback * fix Uncommon Breed ME crash * real fix to Fun and Games force switch issues * add isBattleMysteryEncounter() helper function * add isBattleMysteryEncounter() helper function * fix unintentional replace all errors * fix catches not updating dex --------- Co-authored-by: ImperialSympathizer Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> --- public/images/trainer/bug_type_superfan.json | 1450 +---------------- public/images/trainer/bug_type_superfan.png | Bin 4111 -> 1159 bytes src/battle-scene.ts | 7 +- src/battle.ts | 9 +- src/data/move.ts | 5 + .../encounters/absolute-avarice-encounter.ts | 6 +- .../an-offer-you-cant-refuse-encounter.ts | 2 +- .../encounters/berries-abound-encounter.ts | 1 + .../encounters/bug-type-superfan-encounter.ts | 89 +- .../encounters/clowning-around-encounter.ts | 10 +- .../encounters/dancing-lessons-encounter.ts | 1 + .../encounters/fiery-fallout-encounter.ts | 1 + .../encounters/fight-or-flight-encounter.ts | 1 + .../encounters/fun-and-games-encounter.ts | 1 + .../slumbering-snorlax-encounter.ts | 1 + .../teleporting-hijinks-encounter.ts | 1 + .../the-expert-pokemon-breeder-encounter.ts | 5 +- .../encounters/the-strong-stuff-encounter.ts | 1 + .../encounters/training-session-encounter.ts | 33 +- .../encounters/trash-to-treasure-encounter.ts | 1 + .../encounters/uncommon-breed-encounter.ts | 8 +- .../encounters/weird-dream-encounter.ts | 22 +- .../utils/encounter-phase-utils.ts | 4 +- .../utils/encounter-pokemon-utils.ts | 12 +- src/field/pokemon.ts | 2 +- .../safari-zone-dialogue.json | 2 +- src/modifier/modifier.ts | 33 +- src/phases/command-phase.ts | 4 +- src/phases/encounter-phase.ts | 22 +- src/phases/post-summon-phase.ts | 3 +- src/phases/summon-phase.ts | 2 +- src/phases/victory-phase.ts | 2 +- src/system/game-data.ts | 40 +- 33 files changed, 217 insertions(+), 1564 deletions(-) diff --git a/public/images/trainer/bug_type_superfan.json b/public/images/trainer/bug_type_superfan.json index 74dca3583d5..6a77fe69912 100644 --- a/public/images/trainer/bug_type_superfan.json +++ b/public/images/trainer/bug_type_superfan.json @@ -4,1458 +4,30 @@ "image": "bug_type_superfan.png", "format": "RGBA8888", "size": { - "w": 224, - "h": 224 + "w": 71, + "h": 71 }, "scale": 1, "frames": [ - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 5, - "y": 1, - "w": 52, - "h": 85 - }, - "frame": { - "x": 1, - "y": 1, - "w": 52, - "h": 85 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 5, - "y": 1, - "w": 52, - "h": 85 - }, - "frame": { - "x": 1, - "y": 1, - "w": 52, - "h": 85 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 9, - "y": 11, - "w": 60, - "h": 75 - }, - "frame": { - "x": 55, - "y": 1, - "w": 60, - "h": 75 - } - }, { "filename": "0001.png", "rotated": false, "trimmed": true, "sourceSize": { "w": 80, - "h": 86 + "h": 80 }, "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 + "x": 4, + "y": 9, + "w": 71, + "h": 71 }, "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 65, - "h": 68 - }, - "frame": { - "x": 117, - "y": 1, - "w": 65, - "h": 68 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 10, + "x": 0, "y": 0, - "w": 46, - "h": 86 - }, - "frame": { - "x": 1, - "y": 88, - "w": 46, - "h": 86 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 46, - "h": 86 - }, - "frame": { - "x": 1, - "y": 88, - "w": 46, - "h": 86 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 46, - "h": 86 - }, - "frame": { - "x": 1, - "y": 88, - "w": 46, - "h": 86 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 46, - "h": 86 - }, - "frame": { - "x": 1, - "y": 88, - "w": 46, - "h": 86 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 46, - "h": 86 - }, - "frame": { - "x": 1, - "y": 88, - "w": 46, - "h": 86 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 46, - "h": 86 - }, - "frame": { - "x": 1, - "y": 88, - "w": 46, - "h": 86 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 10, - "y": 0, - "w": 46, - "h": 86 - }, - "frame": { - "x": 1, - "y": 88, - "w": 46, - "h": 86 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 19, - "w": 66, - "h": 67 - }, - "frame": { - "x": 49, - "y": 88, - "w": 66, - "h": 67 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 19, - "w": 66, - "h": 67 - }, - "frame": { - "x": 49, - "y": 88, - "w": 66, - "h": 67 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 49, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 49, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 8, - "y": 20, - "w": 65, - "h": 66 - }, - "frame": { - "x": 116, - "y": 157, - "w": 65, - "h": 66 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 7, - "y": 19, - "w": 65, - "h": 67 - }, - "frame": { - "x": 117, - "y": 71, - "w": 65, - "h": 67 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 86 - }, - "spriteSourceSize": { - "x": 7, - "y": 19, - "w": 65, - "h": 67 - }, - "frame": { - "x": 117, - "y": 71, - "w": 65, - "h": 67 + "w": 71, + "h": 71 } } ] @@ -1464,6 +36,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:442c13442d70348845d7f5fcdfc121b3:3b8402aa64ee8990e64c7f03ffffbc55:568199339797fd79d11ae8d741953c1c$" + "smartupdate": "$TexturePacker:SmartUpdate:d051332055512f245ae68e912a28cd81:4926ba3fd9fe432bb535d13f4df1df4e:621aca0914900a3b9e235ebf0431ce2b$" } } diff --git a/public/images/trainer/bug_type_superfan.png b/public/images/trainer/bug_type_superfan.png index 59316fe6ed898706b85c654253306dc183d9888a..0d2fd7a68aeeb55a419e8aebd4ce290f85540754 100644 GIT binary patch delta 1153 zcmV-{1b+LEAcqNmiBL{Q4GJ0x0000DNk~Le0000-0000-2m=5B0Ag;cEdT%jW>8F2 zMNDaNnX9x@EgH;kPJw)dHI!DRV@Uu20A{~M@UnhpJ2n6Q|LEf4oK7H1YB#&PyS#d3 z@trX}t2#6v5_(EmP+oo2m1G|!Gc`UtU@Q$ZLSZ5@Eps#!C|RS*_xJa+EhMwD!ls_4 zaBBcnkzE&m4#WTe4#WYKD-Ig~00X*7L_t(o!@byxmYOgS24F4;gIr?8Wd(J&?fbuE z|Cvbuttenm(~F#4`T9>X32vk4KMcd;A0Xu(zuu%6VaFT}hc_Uz_hAkPqV15Dv=?Px zZgxzmX~5u$4)k^!o~5mh&P~M(&-KJTXD^rFzJU3NkwmD(JE8K3NfO*4_ z6b4Ggxi(j2ydza@ag?c06&#MUjiLSEFtc@%DJ)eb4qhjdLl~>##@ru;t!$?7=^ZBHbJ;8hq$)>DyM^%LFsb9@K1c?V<*r$!8yGj3$*@t&} zhyfX=ljT9r>hf_2%zkX++)FyFec?nD6#AGjydSJmP1AVgd0P{)f{UqhJSGXbU*uE* zfpL7b^m6;hj~^qG@SyTsJLl*ZjO2*1XqQcY9ASA9?7Cd(N(#Q&_ONsc58 z24`EQM13!DD47lqB@$;Dlcs&oW&H&uKhyNOnBkeIdw>3g!V+h$b^Y^7#x0Li_ccuy ziy0Rix}ra0i)C9hIsL$lVO(S5*wpDYj?t2hH4EY}a}!4nW*ZwM!e89lczwC%L^T0_ z^KF?V#^|O%ASlA{B`s({Owj=OTW=iV%hwD^!zqM2y~B_?cA*oE5Hq;dS=Xo|Wr;%z zHkLx2GBGuJXxORqJkW>Vp8|DVtWG%LA3K+}ZRhqm@n-5;fv70dao%Yha)#qOx?IYy zOZzyELgE5ue8wX_ z{p-7D^ust|dM1uM1?N98&czv=fSUBEH}+%t&)_?wm|swVxhJb8(_#QkHjiVTg3ge5{V6(!G<; z#0i^S9n23EQwPv(E6?=tEo?mw3|B>7WdYc*Q_^;y+0YFdp Te~$TR00000NkvXXu0mjf$+;9I literal 4111 zcmZWscQo8h7yel#2(c`}>Kkp9=p{|`9%Yq8?CBZ6DLI|-T zh)(pfLa@=I#wYLj&iBtZ=iGVb&U2r8=G>Y4$HW*JXwlO`X#oJB*U?r-T;Tj)qb9$Y z#douYFF?{r-&ErQFNPFCRTu5#ROIi0hzr%V3=Ku2$5K-KTU$e&T3cIZi$b?&ozBNv z&(F`-e{5gB5Rg4a7?=Rcs}rsl4vmktg+BmXhWs@$AS;Ir02qRF)KyG_rZ#OZXBP@H z$&N;d;P=nZr}f$|@p?1iR=2)qVmFW8*U&Rb(+D^=Z=H|-v;XOaP(15-Zh8y1T8~1D zyU`1GnJBjRi7mM*!b4tR-8k+7TA2ZG#P1tbSKG-+2YUTbx4NTt zq8enq1K_65>w0#DNdC&-&(Q~@uG21#Y=^32hg$79DccVApzDWM*P0V@{e zXZf!VK>$P8E%nORp&Jzd(XSmQA+G(mP7S854BLrU~nI;n&vkOnXf&sIb!CP`;b6k1a;lgj) z7?#d1$9>trS5>pfqAcTIR4&~{`3Qi&F$Ilv)S8g@9Kp@s<>Zou(ZYD!S*mHOT&bC6 z^`jH*Lq4mWJ(Tx1o)5b zZD`L;mR0-E0KO=2zOv$ig!OY?EnzoSXMv>Wr`O2mTTO^S3J%O@@cOG{S7(N`w|zfI z1Xb?gCRG$#vf6uC{P1OM&O5V@(?h+|?`_QLnDUk86RNn*4l7Mc1i=fHkXOA~{IZqi z5s}rPd?g0kn`ioePAg4>79b>_nR_rVH3pM&?f?{Pn;)usM)CcV+wDVTCAB6cWL-K< zTQQ+dSLU{VPcYaktU8IRNvVu7g%s_2=zm;ZAAui&p&vVUBJwc>dB+YxOWP1WPjwpf zB#`l``V-VIV0{hCCoLr(yA~R~wY^=zrJW%wzh3lglTcs@K?HZ+UT znO8F{Pu?%rCOKJXJ<aJgq?b_?%v_T@9Yl(%0t%sUd#G*pihodZkQpw zjSRt<7_XTYY2*ynB-^S48~FT_rbDuPY}3`5bQW9dk@!@suKBAdnw|YN@B-TxPm|QL z&vGZ!(v0>* zUvr`agt&M;%p9!uAWZM;g?BA^zEtflHhl%%n9h4agZyUL%1?zk$aAxo0A=D-e*& zt^=W9go4EK^FlHuN_XkVkg^usGswv5xM%AwZTVkid z>PT?La*_Bg`wFRc&*O(_M=duQdXkN}YH;#KpuRVG<%A8OmcaW!Lf1u?*bQ7f7PA$P zjoYPh_0|!Yd&3nw`vKg*6q0&(t;A+!V!|>ZK5<{bMUXJ1NS5w)vDGfp+L|rXA5PZ{U^4Pvmi?4V9d)Z7559s{}kh?+CE# zIr7C(|49JHZdVk6V?&r=jDwU62)g7ZB*U@OybU&rJc zC%YZxadTa&ak#JFU+-HkHUPVwqS&N8*{@_(Tl=--WARn^Es?O$CxZVFY^Fz*3r_`h zzEPS^6v9>{Y^zKbmtia3{!$1@ zb+5;_o?U~vQ2L1ame$0Z=XicsGv|T8^A1;4QGEUNi4{t4gp#W}&0GYwIAo$RszON= zv6`-9)&WI-5hkW;n)6(R$FliTIvqT$zvH~`n_QFDF0J`v$n)g$`V!2=36rJ|Qs6KY z#Pw?sto;{69JdVQa}CU`VpERH=?6QzO?r_|$$oOmS2+fRaGUCcmO+f@=WeWXKrF}; zh8P1_$Jo+^vSKTapF25t2_fWx)hO_t&9B{wO39k$D}3wbFqdbFWh@KRz2z%)uj!v6 zlGCF~tzuFbP_?cj8OI_={5UAbcAZwY|Lffhf;~s_&Psb|3uT|=-dOK?*(7)V?pw3F z?HJe!WPZyz*J-UX*(}kO4nFtO=>{?Onl$DP2$KP=LUv0SH}>we!B?64O>u(XtLeY|7L=O6!PT$VjZP^=wJk zM>Y9T|IwY*E%Xd3?xt&1b2baKOul*hs@?vzf4F?W;pW5#i zp)AH93Ieb4;p zJPOyno9nr=S;MkN?kRvsv!P)n8t zj$`qmw&}z;aSQsq@|^aYdyta`TX~B3i9uo(9(;34?-iKDv`7yj3M;x#eBw+31KC1K z!p4Yoh$~62@}eB>gKn*q3evYOB_NsXlj4ZCZ315xkqKMF4a06EAiwEiP;m;(a2(xr zA7{y7<9imprZ0UXOcsCHmWIJ=xkjC%s{JjSaN}$@l&!U2P4xesOhCdS0}Mp5ODT#+ zQbB_E&UHCQQ&RQghY4pXif2+mH^TDue@SzVzDJg@Ty*8S6Nyl*mbJKYvpitbK=h*F z;+|m$jbCV+5|B3d;TLMHn$PI|I5)S`h8c-s%{fcDdJ{XcZzfsujOdoI7#b9?E2sT< zZn;tS@}-F`Dl-9jkz@cQ=3|NrunbI z>UI>{nY8fGl2i$x?}GES4Cb_Ndt+HNOsEC)I7g$g=2K5%n6%`OC8@ z3>?vw{~OmL-mkECa1&X?@t5ke%Pa^i!lVnUf0S<;8Ei&$>M_G8?F$M5D>3PU>ahFb zl6wYYq+{Zo3b>;fOd_;xBpC^JeHBJ04%Y>dI?EZbpQP}`YAxC*eI zOy;KU9{sgsVSjcujc3scID^_sCa7F8ogiZ;HT=z(wO~q2C1KQ(vi(?IZ{%DEeF)+U z@n2AQsYQnI3<~;cSH%^I46rK4wV<2fG|<^EJ_LG6?ufAzob;P3^b+pXUW*C(Rp7#6 z>wxU0w+p5(9Fk?aBZTTQhE;vZ~Q%{$n+<7uBZ4de%A@+ZS#J`HW|F1~=mpI4JO%SNt)75Tgk9eaG cdB_fVZkrZFe?2{H`TMWe(J)Z2Qniox54TLqv;Y7A diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 6b53ffd6a7f..ff0ca47bcac 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1201,7 +1201,7 @@ export default class BattleScene extends SceneBase { // Check for mystery encounter // Can only occur in place of a standard (non-boss) wild battle, waves 10-180 - if (this.isWaveMysteryEncounter(newBattleType, newWaveIndex, mysteryEncounterType) || newBattleType === BattleType.MYSTERY_ENCOUNTER || !isNullOrUndefined(mysteryEncounterType)) { + if (this.isWaveMysteryEncounter(newBattleType, newWaveIndex, mysteryEncounterType) || newBattleType === BattleType.MYSTERY_ENCOUNTER) { newBattleType = BattleType.MYSTERY_ENCOUNTER; // Reset base spawn weight this.mysteryEncounterSaveData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT; @@ -3002,7 +3002,7 @@ export default class BattleScene extends SceneBase { if (participantIds.size > 0) { if (this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) { expValue = Math.floor(expValue * 1.5); - } else if (this.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && this.currentBattle.mysteryEncounter) { + } else if (this.currentBattle.isBattleMysteryEncounter() && this.currentBattle.mysteryEncounter) { expValue = Math.floor(expValue * this.currentBattle.mysteryEncounter.expMultiplier); } for (const partyMember of nonFaintedPartyMembers) { @@ -3102,9 +3102,10 @@ export default class BattleScene extends SceneBase { // If total number of encounters is lower than expected for the run, slightly favor a new encounter spawn (reverse as well) // Reduces occurrence of runs with total encounters significantly different from AVERAGE_ENCOUNTERS_PER_RUN_TARGET + // Favored rate changes can never exceed 50%. So if base rate is 15/256 and favored rate would add 200/256, result will be (15 + 128)/256 const expectedEncountersByFloor = AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave) * (waveIndex - lowestMysteryEncounterWave); const currentRunDiffFromAvg = expectedEncountersByFloor - encounteredEvents.length; - const favoredEncounterRate = sessionEncounterRate + currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER; + const favoredEncounterRate = sessionEncounterRate + Math.min(currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT / 2); const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE) ? favoredEncounterRate : Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE!; diff --git a/src/battle.ts b/src/battle.ts index f9c16d0189d..778d2467803 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -213,7 +213,7 @@ export default class Battle { getBgmOverride(scene: BattleScene): string | null { const battlers = this.enemyParty.slice(0, this.getBattlerCount()); - if (this.battleType === BattleType.MYSTERY_ENCOUNTER && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) { + if (this.isBattleMysteryEncounter() && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) { // Music is overridden for MEs during ME onInit() // Should not use any BGM overrides before swapping from DEFAULT mode return null; @@ -409,6 +409,13 @@ export default class Battle { scene.rngSeedOverride = tempSeedOverride; return ret; } + + /** + * Returns if the battle is of type {@linkcode BattleType.MYSTERY_ENCOUNTER} + */ + isBattleMysteryEncounter(): boolean { + return this.battleType === BattleType.MYSTERY_ENCOUNTER; + } } export class FixedBattle extends Battle { diff --git a/src/data/move.ts b/src/data/move.ts index 43f0b3be14d..74ecead73fa 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -5290,6 +5290,11 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { return false; } + if (!player && user.scene.currentBattle.isBattleMysteryEncounter() && !user.scene.currentBattle.mysteryEncounter?.fleeAllowed) { + // Don't allow wild opponents to be force switched during MEs with flee disabled + return false; + } + const blockedByAbility = new Utils.BooleanHolder(false); applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility); return !blockedByAbility.value; diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index efcd41054ea..432a0b778da 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -24,6 +24,7 @@ import { BerryType } from "#enums/berry-type"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { Stat } from "#enums/stat"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import i18next from "i18next"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounter:absoluteAvarice"; @@ -38,6 +39,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = .withEncounterTier(MysteryEncounterTier.GREAT) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withSceneRequirement(new PersistentModifierRequirement("BerryModifier", 4)) // Must have at least 4 berries to spawn + .withFleeAllowed(false) .withIntroSpriteConfigs([ { // This sprite has the shadow @@ -262,15 +264,13 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = // Provides 1x Reviver Seed to each party member at end of battle const revSeed = generateModifierType(scene, modifierTypes.REVIVER_SEED); + encounter.setDialogueToken("foodReward", revSeed?.name ?? i18next.t("modifierType:ModifierType.REVIVER_SEED.name")); const givePartyPokemonReviverSeeds = () => { const party = scene.getParty(); party.forEach(p => { const heldItems = p.getHeldItems(); if (revSeed && !heldItems.some(item => item instanceof PokemonInstantReviveModifier)) { const seedModifier = revSeed.newModifier(p); - if (seedModifier) { - encounter.setDialogueToken("foodReward", seedModifier.type.name); - } scene.addModifier(seedModifier, false, false, false, true); } }); 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 919cd1df5ca..8746c387545 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 @@ -70,7 +70,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter!; const pokemon = getHighestStatTotalPlayerPokemon(scene, true, true); - const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId(true); + const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId(); const starterValue: number = speciesStarters[baseSpecies] ?? 1; const multiplier = Math.max(MONEY_MAXIMUM_MULTIPLIER / 10 * starterValue, MONEY_MINIMUM_MULTIPLIER); const price = scene.getWaveMoneyAmount(multiplier); diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index a112355b033..a7f8d62c233 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -46,6 +46,7 @@ export const BerriesAboundEncounter: MysteryEncounter = .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withCatchAllowed(true) .withHideWildIntroMessage(true) + .withFleeAllowed(false) .withIntroSpriteConfigs([]) // Set in onInit() .withIntroDialogue([ { diff --git a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts index 35858b2e6db..3a80e92918d 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -49,6 +49,7 @@ import MoveInfoOverlay from "#app/ui/move-info-overlay"; import { allMoves } from "#app/data/move"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:bugTypeSuperfan"; @@ -177,9 +178,14 @@ const MISC_TUTOR_MOVES = [ Moves.U_TURN ]; +/** + * Wave breakpoints that determine how strong to make the Bug-Type Superfan's team + */ +const WAVE_LEVEL_BREAKPOINTS = [30, 50, 70, 100, 120, 140, 160]; + /** * Bug Type Superfan encounter. - * @see {@link https://github.com/pagefaultgames/pokerogue/issues/3810 | GitHub Issue #3810} + * @see {@link https://github.com/pagefaultgames/pokerogue/issues/3820 | GitHub Issue #3820} * @see For biome requirements check {@linkcode mysteryEncountersByBiome} */ export const BugTypeSuperfanEncounter: MysteryEncounter = @@ -216,11 +222,46 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = female: true, }); + let beedrillKeys: { spriteKey: string, fileRoot: string }, butterfreeKeys: { spriteKey: string, fileRoot: string }; + if (scene.currentBattle.waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) { + beedrillKeys = getSpriteKeysFromSpecies(Species.BEEDRILL, false); + butterfreeKeys = getSpriteKeysFromSpecies(Species.BUTTERFREE, false); + } else { + // Mega Beedrill/Gmax Butterfree + beedrillKeys = getSpriteKeysFromSpecies(Species.BEEDRILL, false, 1); + butterfreeKeys = getSpriteKeysFromSpecies(Species.BUTTERFREE, false, 1); + } + encounter.spriteConfigs = [ + { + spriteKey: beedrillKeys.spriteKey, + fileRoot: beedrillKeys.fileRoot, + hasShadow: true, + repeat: true, + isPokemon: true, + x: -30, + tint: 0.15, + y: -4, + yShadow: -4 + }, + { + spriteKey: butterfreeKeys.spriteKey, + fileRoot: butterfreeKeys.fileRoot, + hasShadow: true, + repeat: true, + isPokemon: true, + x: 30, + tint: 0.15, + y: -4, + yShadow: -4 + }, { spriteKey: spriteKey, fileRoot: "trainer", hasShadow: true, + x: 4, + y: 7, + yShadow: 7 }, ]; @@ -330,6 +371,10 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = if (formChangeModifier) { specialOptions.push(formChangeModifier); } + const rareFormChangeModifier = generateModifierTypeOption(scene, modifierTypes.RARE_FORM_CHANGE_ITEM); + if (rareFormChangeModifier) { + specialOptions.push(rareFormChangeModifier); + } if (specialOptions.length > 0) { modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]); } @@ -445,29 +490,29 @@ function getTrainerConfigForWave(waveIndex: number) { const config = trainerConfigs[TrainerType.BUG_TYPE_SUPERFAN].clone(); config.name = i18next.t("trainerNames:bug_type_superfan"); - const pool3Copy = POOL_3_POKEMON.slice(0); - randSeedShuffle(pool3Copy); + let pool3Copy = POOL_3_POKEMON.slice(0); + pool3Copy = randSeedShuffle(pool3Copy); const pool3Mon = pool3Copy.pop()!; - if (waveIndex < 30) { + if (waveIndex < WAVE_LEVEL_BREAKPOINTS[0]) { // Use default template (2 AVG) config .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true)); - } else if (waveIndex < 50) { + } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[1]) { config .setPartyTemplates(new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE)) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true)); - } else if (waveIndex < 70) { + } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[2]) { config .setPartyTemplates(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE)) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true)) .setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true)); - } else if (waveIndex < 100) { + } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) { config .setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE)) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true)) @@ -475,7 +520,7 @@ function getTrainerConfigForWave(waveIndex: number) { .setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true)) .setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true)) .setPartyMemberFunc(4, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true)); - } else if (waveIndex < 120) { + } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[4]) { config .setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE)) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => { @@ -497,8 +542,8 @@ function getTrainerConfigForWave(waveIndex: number) { p.generateName(); } })); - } else if (waveIndex < 140) { - randSeedShuffle(pool3Copy); + } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[5]) { + pool3Copy = randSeedShuffle(pool3Copy); const pool3Mon2 = pool3Copy.pop()!; config .setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE)) @@ -527,7 +572,7 @@ function getTrainerConfigForWave(waveIndex: number) { p.generateName(); } })); - } else if (waveIndex < 160) { + } else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[6]) { config .setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(1, PartyMemberStrength.STRONG))) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => { @@ -629,27 +674,7 @@ function doBugTypeMoveTutor(scene: BattleScene): Promise { moveInfoOverlay.setVisible(false); } - // TODO: add menu to confirm player doesn't want to teach a move - // while (!result && !forceExit) { - // // Didn't teach a move, ask the player to confirm they don't want to teach a move - // await showEncounterDialogue(scene, `${namespace}.confirm_no_teach`, `${namespace}.speaker`); - // const confirm = await new Promise(confirmResolve => { - // scene.ui.setMode(Mode.CONFIRM, () => confirmResolve(true), () => confirmResolve(false)); - // }); - // scene.ui.clearText(); - // await scene.ui.setMode(Mode.MESSAGE); - // if (confirm) { - // // No teach, break out of loop - // forceExit = true; - // } else { - // // Re-show learn menu - // result = await selectOptionThenPokemon(scene, optionSelectItems, `${namespace}.teach_move_prompt`, undefined, onHoverOverCancel); - // if (!result) { - // moveInfoOverlay.active = false; - // moveInfoOverlay.setVisible(false); - // } - // } - // } + // TODO: add menu to confirm player doesn't want to teach a move? // Option select complete, handle if they are learning a move if (result && result.selectedOptionIndex < moveOptions.length) { diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index e1e681e95dd..7e846b42ea4 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -336,8 +336,8 @@ export const ClowningAroundEncounter: MysteryEncounter = .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); + priorityTypes = [...new Set(priorityTypes)].sort(); + priorityTypes = randSeedShuffle(priorityTypes); } const newTypes = [originalTypes[0]]; @@ -494,9 +494,13 @@ function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItem } for (let i = 0; i < numItems; i++) { + if (pool.length === 0) { + // Stop generating new items if somehow runs out of items to spawn + return; + } const randIndex = randSeedInt(pool.length); const newItemType = pool[randIndex]; - let newMod; + let newMod: PokemonHeldItemModifierType; if (tier === "Berries") { newMod = generateModifierType(scene, modifierTypes.BERRY, [newItemType[0]]) as PokemonHeldItemModifierType; } else { diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index 1ceb14a7372..a99395726c8 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -90,6 +90,7 @@ export const DancingLessonsEncounter: MysteryEncounter = .withHideWildIntroMessage(true) .withAutoHideIntroVisuals(false) .withCatchAllowed(true) + .withFleeAllowed(false) .withOnVisualsStart((scene: BattleScene) => { const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getParty()[0]); danceAnim.play(scene); diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 13f9d926345..6775aee6b30 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -46,6 +46,7 @@ export const FieryFalloutEncounter: MysteryEncounter = .withIntroSpriteConfigs([]) // Set in onInit() .withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT) .withAutoHideIntroVisuals(false) + .withFleeAllowed(false) .withIntroDialogue([ { text: `${namespace}.intro`, 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 349984f1958..b3afdaf5f17 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -44,6 +44,7 @@ export const FightOrFlightEncounter: MysteryEncounter = .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withCatchAllowed(true) .withHideWildIntroMessage(true) + .withFleeAllowed(false) .withIntroSpriteConfigs([]) // Set in onInit() .withIntroDialogue([ { diff --git a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts index a144aa88299..1d8bf42528b 100644 --- a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts +++ b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts @@ -44,6 +44,7 @@ export const FunAndGamesEncounter: MysteryEncounter = .withSkipEnemyBattleTurns(true) // Will skip COMMAND selection menu and go straight to FIGHT (move select) menu .withSkipToFightInput(true) + .withFleeAllowed(false) .withIntroSpriteConfigs([ { spriteKey: "fun_and_games_game", diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index bfccc46ee0f..fe61766a888 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -34,6 +34,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withCatchAllowed(true) .withHideWildIntroMessage(true) + .withFleeAllowed(false) .withIntroSpriteConfigs([ { spriteKey: Species.SNORLAX.toString(), diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts index abea725f113..386f4170156 100644 --- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts +++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts @@ -42,6 +42,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter = .withSceneRequirement(new MoneyRequirement(0, MONEY_COST_MULTIPLIER)) // Must be able to pay teleport cost .withAutoHideIntroVisuals(false) .withCatchAllowed(true) + .withFleeAllowed(false) .withIntroSpriteConfigs([ { spriteKey: "teleporting_hijinks_teleporter", diff --git a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts index 91aeea79111..e41b3ab03ef 100644 --- a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts @@ -437,8 +437,7 @@ function getPartyConfig(scene: BattleScene): EnemyPartyConfig { } function getSpeciesFromPool(speciesPool: (Species | BreederSpeciesEvolution)[][], waveIndex: number): Species { - const poolCopy = speciesPool.slice(0); - randSeedShuffle(poolCopy); + const poolCopy = randSeedShuffle(speciesPool.slice(0)); const speciesEvolutions = poolCopy.pop()!.slice(0); let speciesObject = speciesEvolutions.pop()!; while (speciesObject instanceof BreederSpeciesEvolution && speciesObject.evolution > waveIndex) { @@ -452,7 +451,7 @@ function calculateEggRewardsForPokemon(pokemon: PlayerPokemon): [number, number] // 1 point for every 20 points below 680 BST the pokemon is, (max 18, min 1) const pointsFromBst = Math.min(Math.max(Math.floor((680 - bst) / 20), 1), 18); - const rootSpecies = pokemon.species.getRootSpeciesId(true); + const rootSpecies = pokemon.species.getRootSpeciesId(); let pointsFromStarterTier = 0; // 2 points for every 1 below 7 that the pokemon's starter tier is (max 12, min 0) if (speciesStarters.hasOwnProperty(rootSpecies)) { 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 56328695128..10537cbe200 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -39,6 +39,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = .withMaxAllowedEncounters(1) .withHideWildIntroMessage(true) .withAutoHideIntroVisuals(false) + .withFleeAllowed(false) .withIntroSpriteConfigs([ { spriteKey: "berry_juice", diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index 33864c00143..ea59b8003f5 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -101,7 +101,7 @@ export const TrainingSessionEncounter: MysteryEncounter = encounter.setDialogueToken("stat1", "-"); encounter.setDialogueToken("stat2", "-"); // Add the pokemon back to party with IV boost - const ivIndexes: any[] = []; + let ivIndexes: any[] = []; playerPokemon.ivs.forEach((iv, index) => { if (iv < 31) { ivIndexes.push({ iv: iv, index: index }); @@ -117,7 +117,7 @@ export const TrainingSessionEncounter: MysteryEncounter = // 25-27 starting IV caps in 2 encounters let improvedCount = 0; while (ivIndexes.length > 0 && improvedCount < 2) { - randSeedShuffle(ivIndexes); + ivIndexes = randSeedShuffle(ivIndexes); const ivToChange = ivIndexes.pop(); let newVal = ivToChange.iv; if (improvedCount === 0) { @@ -145,10 +145,7 @@ export const TrainingSessionEncounter: MysteryEncounter = if (improvedCount > 0) { playerPokemon.calculateStats(); - scene.gameData.updateSpeciesDexIvs( - playerPokemon.species.getRootSpeciesId(true), - playerPokemon.ivs - ); + scene.gameData.updateSpeciesDexIvs(playerPokemon.species.getRootSpeciesId(true), playerPokemon.ivs); scene.gameData.setPokemonCaught(playerPokemon, false); } @@ -322,27 +319,23 @@ export const TrainingSessionEncounter: MysteryEncounter = queueEncounterMessage(scene, `${namespace}.option.3.finished`); // Add the pokemon back to party with ability change const abilityIndex = encounter.misc.abilityIndex; + if (!!playerPokemon.getFusionSpeciesForm()) { playerPokemon.fusionAbilityIndex = abilityIndex; - 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; + + // Only update the fusion's dex data if the Pokemon is already caught in dex (ignore rentals) + const rootFusionSpecies = playerPokemon.fusionSpecies?.getRootSpeciesId(); + if (!isNullOrUndefined(rootFusionSpecies) + && speciesStarters.hasOwnProperty(rootFusionSpecies) + && !!scene.gameData.dexData[rootFusionSpecies].caughtAttr) { + scene.gameData.starterData[rootFusionSpecies].abilityAttr |= playerPokemon.fusionAbilityIndex !== 1 || playerPokemon.fusionSpecies?.ability2 + ? 1 << playerPokemon.fusionAbilityIndex + : AbilityAttr.ABILITY_HIDDEN; } } else { playerPokemon.abilityIndex = abilityIndex; - if (speciesStarters.hasOwnProperty(playerPokemon.species.speciesId)) { - scene.gameData.starterData[playerPokemon.species.speciesId] - .abilityAttr |= - abilityIndex !== 1 || playerPokemon.species.ability2 - ? Math.pow(2, playerPokemon.abilityIndex) - : AbilityAttr.ABILITY_HIDDEN; - } } - playerPokemon.getAbility(); playerPokemon.calculateStats(); scene.gameData.setPokemonCaught(playerPokemon, false); 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 98aa90c0818..4b2333297c0 100644 --- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts +++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts @@ -36,6 +36,7 @@ export const TrashToTreasureEncounter: MysteryEncounter = .withEncounterTier(MysteryEncounterTier.ULTRA) .withSceneWaveRangeRequirement(60, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1]) .withMaxAllowedEncounters(1) + .withFleeAllowed(false) .withIntroSpriteConfigs([ { spriteKey: Species.GARBODOR.toString() + "-gigantamax", diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index 24298a633df..14ba7895434 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -38,6 +38,7 @@ export const UncommonBreedEncounter: MysteryEncounter = .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withCatchAllowed(true) .withHideWildIntroMessage(true) + .withFleeAllowed(false) .withIntroSpriteConfigs([]) // Set in onInit() .withIntroDialogue([ { @@ -59,17 +60,18 @@ export const UncommonBreedEncounter: MysteryEncounter = const eggMoveIndex = randSeedInt(4); const randomEggMove: Moves = eggMoves[eggMoveIndex]; encounter.misc = { - eggMove: randomEggMove + eggMove: randomEggMove, + pokemon: pokemon }; if (pokemon.moveset.length < 4) { pokemon.moveset.push(new PokemonMove(randomEggMove)); } else { pokemon.moveset[0] = new PokemonMove(randomEggMove); } + } else { + encounter.misc.pokemon = pokemon; } - encounter.misc.pokemon = pokemon; - // Defense/Spd buffs below wave 50, +1 to all stats otherwise const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ? [Stat.DEF, Stat.SPDEF, Stat.SPD] : diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index 8fa60774a72..71e8491df69 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -368,7 +368,7 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon const newEggMoveIndex = await addEggMoveToNewPokemonMoveset(scene, newPokemon, speciesRootForm); // Try to add a favored STAB move (might fail if Pokemon already knows a bunch of moves from newPokemonGeneratedMoveset) - addFavoredMoveToNewPokemonMoveset(scene, newPokemon, newPokemonGeneratedMoveset, newEggMoveIndex); + addFavoredMoveToNewPokemonMoveset(newPokemon, newPokemonGeneratedMoveset, newEggMoveIndex); // Randomize the second type of the pokemon // If the pokemon does not normally have a second type, it will gain 1 @@ -553,8 +553,7 @@ async function addEggMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Pla let eggMoveIndex: null | number = null; const eggMoves = newPokemon.getEggMoves()?.slice(0); if (eggMoves) { - const eggMoveIndices = [0, 1, 2, 3]; - randSeedShuffle(eggMoveIndices); + const eggMoveIndices = randSeedShuffle([0, 1, 2, 3]); let randomEggMoveIndex = eggMoveIndices.pop(); let randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex] : null; let retries = 0; @@ -587,12 +586,11 @@ async function addEggMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Pla /** * Returns index of the new egg move within the Pokemon's moveset (not the index of the move in `speciesEggMoves`) - * @param scene * @param newPokemon * @param newPokemonGeneratedMoveset * @param newEggMoveIndex */ -function addFavoredMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: PlayerPokemon, newPokemonGeneratedMoveset: (PokemonMove | null)[], newEggMoveIndex: number | null) { +function addFavoredMoveToNewPokemonMoveset(newPokemon: PlayerPokemon, newPokemonGeneratedMoveset: (PokemonMove | null)[], newEggMoveIndex: number | null) { let favoredMove: PokemonMove | null = null; for (const move of newPokemonGeneratedMoveset) { // Needs to match first type, second type will be replaced @@ -614,11 +612,15 @@ function addFavoredMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Playe } // Finally, assign favored move to random index that isn't the new egg move index if (favoredMove) { - let favoredMoveIndex = randSeedInt(4); - while (newEggMoveIndex !== null && favoredMoveIndex === newEggMoveIndex) { - favoredMoveIndex = randSeedInt(4); - } + if (newPokemon.moveset.length < 4) { + newPokemon.moveset.push(favoredMove); + } else { + let favoredMoveIndex = randSeedInt(4); + while (newEggMoveIndex !== null && favoredMoveIndex === newEggMoveIndex) { + favoredMoveIndex = randSeedInt(4); + } - newPokemon.moveset[favoredMoveIndex] = favoredMove; + newPokemon.moveset[favoredMoveIndex] = favoredMove; + } } } diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index ea04241e663..ca02c8bdfd6 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -832,7 +832,7 @@ export function transitionMysteryEncounterIntroVisuals(scene: BattleScene, hide: */ export function handleMysteryEncounterBattleStartEffects(scene: BattleScene) { const encounter = scene.currentBattle.mysteryEncounter; - if (scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && encounter && encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE && !encounter.startOfBattleEffectsComplete) { + if (scene.currentBattle.isBattleMysteryEncounter() && encounter && encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE && !encounter.startOfBattleEffectsComplete) { const effects = encounter.startOfBattleEffects; effects.forEach(effect => { let source; @@ -871,7 +871,7 @@ export function handleMysteryEncounterBattleStartEffects(scene: BattleScene) { */ export function handleMysteryEncounterTurnStartEffects(scene: BattleScene): boolean { const encounter = scene.currentBattle.mysteryEncounter; - if (scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && encounter && encounter.onTurnStart) { + if (scene.currentBattle.isBattleMysteryEncounter() && encounter && encounter.onTurnStart) { return encounter.onTurnStart(scene); } diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index d7e596af879..c9b30415299 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -210,10 +210,10 @@ export function getRandomSpeciesByStarterTier(starterTiers: number | [number, nu .map(s => [parseInt(s) as Species, speciesStarters[s] as number]) .filter(s => { const pokemonSpecies = getPokemonSpecies(s[0]); - return pokemonSpecies && (!excludedSpecies || !excludedSpecies.includes(s[0]) + return pokemonSpecies && (!excludedSpecies || !excludedSpecies.includes(s[0])) && (allowSubLegendary || !pokemonSpecies.subLegendary) && (allowLegendary || !pokemonSpecies.legendary) - && (allowMythical || !pokemonSpecies.mythical)); + && (allowMythical || !pokemonSpecies.mythical); }) .map(s => [getPokemonSpecies(s[0]), s[1]]); @@ -757,9 +757,10 @@ const GOLDEN_BUG_NET_SPECIES_POOL: [Species, number][] = [ ]; /** - * Will randomly return one of the species from GOLDEN_BUG_NET_SPECIES_POOL, based on their weights + * Will randomly return one of the species from GOLDEN_BUG_NET_SPECIES_POOL, based on their weights. + * Will also check for and evolve pokemon based on level. */ -export function getGoldenBugNetSpecies(): PokemonSpecies { +export function getGoldenBugNetSpecies(level: number): PokemonSpecies { const totalWeight = GOLDEN_BUG_NET_SPECIES_POOL.reduce((a, b) => a + b[1], 0); const roll = randSeedInt(totalWeight); @@ -767,7 +768,8 @@ export function getGoldenBugNetSpecies(): PokemonSpecies { for (const speciesWeightPair of GOLDEN_BUG_NET_SPECIES_POOL) { w += speciesWeightPair[1]; if (roll < w) { - return getPokemonSpecies(speciesWeightPair[0]); + const initialSpecies = getPokemonSpecies(speciesWeightPair[0]); + return getPokemonSpecies(initialSpecies.getSpeciesForLevel(level, true)); } } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index fda4d2226c0..0fda3d075be 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1801,7 +1801,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns list of egg moves */ getEggMoves() : Moves[] | undefined { - return speciesEggMoves[this.getSpeciesForm().getRootSpeciesId(true)]; + return speciesEggMoves[this.getSpeciesForm().getRootSpeciesId()]; } setMove(moveIndex: integer, moveId: Moves): void { diff --git a/src/locales/en/mystery-encounters/safari-zone-dialogue.json b/src/locales/en/mystery-encounters/safari-zone-dialogue.json index b96e3b5beb8..c1e4dd30852 100644 --- a/src/locales/en/mystery-encounters/safari-zone-dialogue.json +++ b/src/locales/en/mystery-encounters/safari-zone-dialogue.json @@ -1,7 +1,7 @@ { "intro": "It's a safari zone!", "title": "The Safari Zone", - "description": "There are all kinds of rare and special Pokémon that can be found here!\nIf you choose to enter, you'll have a time limit of @[TOOLTIP_TITLE]{{{numEncounters}} wild encounters} where you can try to catch these special Pokémon.\n\nBeware, though. These Pokémon may flee before you're able to catch them!", + "description": "There are all kinds of rare and special Pokémon that can be found here!\nIf you choose to enter, you'll have a time limit of@[TOOLTIP_TITLE]{ {{numEncounters}} wild encounters} where you can try to catch these special Pokémon.\n\nBeware, though. These Pokémon may flee before you're able to catch them!", "query": "Would you like to enter?", "option": { "1": { diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 6c998105c1d..f53a07e8e21 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1,4 +1,5 @@ import * as ModifierTypes from "./modifier-type"; +import { ModifierType, modifierTypes } from "./modifier-type"; import BattleScene from "../battle-scene"; import { getLevelTotalExp } from "../data/exp"; import { MAX_PER_TYPE_POKEBALLS, PokeballType } from "../data/pokeball"; @@ -12,16 +13,15 @@ import * as Utils from "../utils"; import { getBerryEffectFunc, getBerryPredicate } from "../data/berry"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; -import { StatusEffect, getStatusEffectHealText } from "../data/status-effect"; +import { getStatusEffectHealText, StatusEffect } from "#app/data/status-effect"; import { achvs } from "../system/achv"; import { VoucherType } from "../system/voucher"; import { FormChangeItem, SpeciesFormChangeItemTrigger, SpeciesFormChangeLapseTeraTrigger, SpeciesFormChangeTeraTrigger } from "../data/pokemon-forms"; import { Nature } from "#app/data/nature"; import Overrides from "#app/overrides"; -import { ModifierType, modifierTypes } from "./modifier-type"; import { Command } from "#app/ui/command-ui-handler"; import { Species } from "#enums/species"; -import { Stat, type PermanentStat, type TempBattleStat, BATTLE_STATS, TEMP_BATTLE_STATS } from "#app/enums/stat"; +import { BATTLE_STATS, type PermanentStat, Stat, TEMP_BATTLE_STATS, type TempBattleStat } from "#app/enums/stat"; import i18next from "i18next"; import { allMoves } from "#app/data/move"; @@ -882,7 +882,7 @@ export class PokemonBaseStatTotalModifier extends PokemonHeldItemModifier { private statModifier: integer; public isTransferable: boolean = false; - constructor(type: ModifierTypes.PokemonBaseStatTotalModifierType, pokemonId: integer, statModifier: integer, stackCount?: integer) { + constructor(type: ModifierTypes.PokemonBaseStatTotalModifierType, pokemonId: number, statModifier: number, stackCount?: integer) { super(type, pokemonId, stackCount); this.statModifier = statModifier; } @@ -927,11 +927,11 @@ export class PokemonBaseStatTotalModifier extends PokemonHeldItemModifier { * Currently used by Old Gateau item */ export class PokemonBaseStatFlatModifier extends PokemonHeldItemModifier { - private statModifier: integer; + private statModifier: number; private stats: Stat[]; public isTransferable: boolean = false; - constructor (type: ModifierType, pokemonId: integer, statModifier: integer, stats: Stat[], stackCount?: integer) { + constructor (type: ModifierType, pokemonId: number, statModifier: number, stats: Stat[], stackCount?: number) { super(type, pokemonId, stackCount); this.statModifier = statModifier; @@ -947,7 +947,7 @@ export class PokemonBaseStatFlatModifier extends PokemonHeldItemModifier { } override getArgs(): any[] { - return super.getArgs().concat(this.statModifier, this.stats); + return [ ...super.getArgs(), this.statModifier, this.stats ]; } override shouldApply(args: any[]): boolean { @@ -989,8 +989,8 @@ export class PokemonIncrementingStatModifier extends PokemonHeldItemModifier { return modifier instanceof PokemonIncrementingStatModifier; } - clone(): PersistentModifier { - return new PokemonIncrementingStatModifier(this.type, this.pokemonId); + clone(): PokemonIncrementingStatModifier { + return new PokemonIncrementingStatModifier(this.type, this.pokemonId, this.stackCount); } getArgs(): any[] { @@ -1004,14 +1004,19 @@ export class PokemonIncrementingStatModifier extends PokemonHeldItemModifier { apply(args: any[]): boolean { // Modifies the passed in stats[] array by +1 per stack for HP, +2 per stack for other stats // If the Macho Brace is at max stacks (50), adds additional 5% to total HP and 10% to other stats + const targetToApply = args[0] as Pokemon; + args[1].forEach((v, i) => { const isHp = i === 0; - let mult = 1; - if (this.stackCount === this.getMaxHeldItemCount()) { - mult = isHp ? 1.05 : 1.1; + // Doesn't modify HP if holder has Wonder Guard + if (!isHp || !targetToApply.hasAbility(Abilities.WONDER_GUARD)) { + let mult = 1; + if (this.stackCount === this.getMaxHeldItemCount()) { + mult = isHp ? 1.05 : 1.1; + } + const newVal = Math.floor((v + this.stackCount * (isHp ? 1 : 2)) * mult); + args[1][i] = Math.min(Math.max(newVal, 1), 999999); } - const newVal = Math.floor((v + this.stackCount * (isHp ? 1 : 2)) * mult); - args[1][i] = Math.min(Math.max(newVal, 1), 999999); }); return true; diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 66e39cf98a5..a374f885d3f 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -70,7 +70,7 @@ export class CommandPhase extends FieldPhase { } } } else { - if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && this.scene.currentBattle.mysteryEncounter?.skipToFightInput) { + if (this.scene.currentBattle.isBattleMysteryEncounter() && this.scene.currentBattle.mysteryEncounter?.skipToFightInput) { this.scene.ui.clearText(); this.scene.ui.setMode(Mode.FIGHT, this.fieldIndex); } else { @@ -141,7 +141,7 @@ export class CommandPhase extends FieldPhase { this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); - } else if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && !this.scene.currentBattle.mysteryEncounter!.catchAllowed) { + } else if (this.scene.currentBattle.isBattleMysteryEncounter() && !this.scene.currentBattle.mysteryEncounter!.catchAllowed) { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noPokeballMysteryEncounter"), null, () => { diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index 25b4398025f..798ee3dca3c 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -34,6 +34,7 @@ import { doTrainerExclamation } from "#app/data/mystery-encounters/utils/encount import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { getGoldenBugNetSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; +import { Biome } from "#enums/biome"; export class EncounterPhase extends BattlePhase { private loaded: boolean; @@ -63,7 +64,7 @@ export class EncounterPhase extends BattlePhase { const battle = this.scene.currentBattle; // Generate and Init Mystery Encounter - if (battle.battleType === BattleType.MYSTERY_ENCOUNTER && !battle.mysteryEncounter) { + if (battle.isBattleMysteryEncounter() && !battle.mysteryEncounter) { this.scene.executeWithSeedOffset(() => { const currentSessionEncounterType = battle.mysteryEncounterType; battle.mysteryEncounter = this.scene.getMysteryEncounter(currentSessionEncounterType); @@ -79,7 +80,7 @@ export class EncounterPhase extends BattlePhase { mysteryEncounter.onInit(this.scene); } mysteryEncounter.populateDialogueTokensFromRequirements(this.scene); - }, this.scene.currentBattle.waveIndex); + }, battle.waveIndex); // Add any special encounter animations to load if (mysteryEncounter.encounterAnimations && mysteryEncounter.encounterAnimations.length > 0) { @@ -94,7 +95,7 @@ export class EncounterPhase extends BattlePhase { let totalBst = 0; battle.enemyLevels?.every((level, e) => { - if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) { + if (battle.isBattleMysteryEncounter()) { // Skip enemy loading for MEs, those are loaded elsewhere return false; } @@ -103,9 +104,12 @@ export class EncounterPhase extends BattlePhase { battle.enemyParty[e] = battle.trainer?.genPartyMember(e)!; // TODO:: is the bang correct here? } else { let enemySpecies = this.scene.randomSpecies(battle.waveIndex, level, true); - // If player has golden bug net, rolls 10% chance to replace with species from the golden bug net bug pool - if (this.scene.findModifier(m => m instanceof BoostBugSpawnModifier) && randSeedInt(10) === 0) { - enemySpecies = getGoldenBugNetSpecies(); + // If player has golden bug net, rolls 10% chance to replace non-boss wave wild species from the golden bug net bug pool + if (this.scene.findModifier(m => m instanceof BoostBugSpawnModifier) + && !this.scene.gameMode.isBoss(battle.waveIndex) + && this.scene.arena.biomeType !== Biome.END + && randSeedInt(10) === 0) { + enemySpecies = getGoldenBugNetSpecies(level); } battle.enemyParty[e] = this.scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, !!this.scene.getEncounterBossSegments(battle.waveIndex, level, enemySpecies)); if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { @@ -157,7 +161,7 @@ export class EncounterPhase extends BattlePhase { if (battle.battleType === BattleType.TRAINER) { loadEnemyAssets.push(battle.trainer?.loadAssets().then(() => battle.trainer?.initSprite())!); // TODO: is this bang correct? - } else if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) { + } else if (battle.isBattleMysteryEncounter()) { if (battle.mysteryEncounter?.introVisuals) { loadEnemyAssets.push(battle.mysteryEncounter.introVisuals.loadAssets().then(() => battle.mysteryEncounter!.introVisuals!.initSprite())); } @@ -189,7 +193,7 @@ export class EncounterPhase extends BattlePhase { Promise.all(loadEnemyAssets).then(() => { battle.enemyParty.every((enemyPokemon, e) => { - if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) { + if (battle.isBattleMysteryEncounter()) { return false; } if (e < (battle.double ? 2 : 1)) { @@ -363,7 +367,7 @@ export class EncounterPhase extends BattlePhase { showDialogueAndSummon(); } } - } else if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && this.scene.currentBattle.mysteryEncounter) { + } else if (this.scene.currentBattle.isBattleMysteryEncounter() && this.scene.currentBattle.mysteryEncounter) { const encounter = this.scene.currentBattle.mysteryEncounter; const introVisuals = encounter.introVisuals; introVisuals?.playAnim(); diff --git a/src/phases/post-summon-phase.ts b/src/phases/post-summon-phase.ts index e7f6c6ea3db..b99c0b90fd8 100644 --- a/src/phases/post-summon-phase.ts +++ b/src/phases/post-summon-phase.ts @@ -6,7 +6,6 @@ import { StatusEffect } from "#app/enums/status-effect"; import { PokemonPhase } from "./pokemon-phase"; import { MysteryEncounterPostSummonTag } from "#app/data/battler-tags"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { BattleType } from "#app/battle"; export class PostSummonPhase extends PokemonPhase { constructor(scene: BattleScene, battlerIndex: BattlerIndex) { @@ -24,7 +23,7 @@ export class PostSummonPhase extends PokemonPhase { this.scene.arena.applyTags(ArenaTrapTag, pokemon); // If this is mystery encounter and has post summon phase tag, apply post summon effects - if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && pokemon.findTags(t => t instanceof MysteryEncounterPostSummonTag).length > 0) { + if (this.scene.currentBattle.isBattleMysteryEncounter() && pokemon.findTags(t => t instanceof MysteryEncounterPostSummonTag).length > 0) { pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON); } diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index d909c5c3501..dfa374307d5 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -87,7 +87,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { this.scene.pbTrayEnemy.hide(); this.scene.ui.showText(message, null, () => this.summon()); - } else if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER) { + } else if (this.scene.currentBattle.isBattleMysteryEncounter()) { this.scene.pbTrayEnemy.hide(); this.summonWild(); } diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index c10adc5683d..e900ff97fc6 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -30,7 +30,7 @@ export class VictoryPhase extends PokemonPhase { const expValue = this.getPokemon().getExpValue(); this.scene.applyPartyExp(expValue, true); - if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER) { + if (this.scene.currentBattle.isBattleMysteryEncounter()) { handleMysteryEncounterVictory(this.scene, false, this.isExpOnly); return this.end(); } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 34c4f67328d..63c7cb1b13a 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -1,5 +1,5 @@ import i18next from "i18next"; -import BattleScene, { PokeballCounts, bypassLogin } from "../battle-scene"; +import BattleScene, { bypassLogin, PokeballCounts } from "../battle-scene"; import Pokemon, { EnemyPokemon, PlayerPokemon } from "../field/pokemon"; import { pokemonPrevolutions } from "../data/pokemon-evolutions"; import PokemonSpecies, { allSpecies, getPokemonSpecies, noStarterFormKeys, speciesStarters } from "../data/pokemon-species"; @@ -13,11 +13,11 @@ import { GameModes, getGameMode } from "../game-mode"; import { BattleType } from "../battle"; import TrainerData from "./trainer-data"; import { trainerConfigs } from "../data/trainer-config"; -import { SettingKeys, resetSettings, setSetting } from "./settings/settings"; +import { resetSettings, setSetting, SettingKeys } from "./settings/settings"; import { achvs } from "./achv"; import EggData from "./egg-data"; import { Egg } from "../data/egg"; -import { VoucherType, vouchers } from "./voucher"; +import { vouchers, VoucherType } from "./voucher"; import { AES, enc } from "crypto-js"; import { Mode } from "../ui/ui"; import { clientSessionId, loggedInUser, updateUserInfo } from "../account"; @@ -28,8 +28,8 @@ import { speciesEggMoves } from "../data/egg-moves"; import { allMoves } from "../data/move"; import { TrainerVariant } from "../field/trainer"; import { Variant } from "#app/data/variant"; -import {setSettingGamepad, SettingGamepad, settingGamepadDefaults} from "./settings/settings-gamepad"; -import {setSettingKeyboard, SettingKeyboard} from "#app/system/settings/settings-keyboard"; +import { setSettingGamepad, SettingGamepad, settingGamepadDefaults } from "./settings/settings-gamepad"; +import { setSettingKeyboard, SettingKeyboard } from "#app/system/settings/settings-keyboard"; import { TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; import * as Modifier from "../modifier/modifier"; import { StatusEffect } from "#app/data/status-effect"; @@ -1583,12 +1583,20 @@ export class GameData { * @returns `true` if Pokemon catch unlocked a new starter, `false` if Pokemon catch did not unlock a starter */ setPokemonCaught(pokemon: Pokemon, incrementCount: boolean = true, fromEgg: boolean = false, showMessage: boolean = true): Promise { - return this.setPokemonSpeciesCaught(pokemon, pokemon.species, incrementCount, fromEgg, showMessage); + // If incrementCount === false (not a catch scenario), only update the pokemon's dex data if the Pokemon has already been marked as caught in dex + // Prevents form changes, nature changes, etc. from unintentionally updating the dex data of a "rental" pokemon + const speciesRootForm = pokemon.species.getRootSpeciesId(); + if (!incrementCount && !this.scene.gameData.dexData[speciesRootForm].caughtAttr) { + return Promise.resolve(false); + } else { + return this.setPokemonSpeciesCaught(pokemon, pokemon.species, incrementCount, fromEgg, showMessage); + } } /** * * @param pokemon + * @param species * @param incrementCount * @param fromEgg * @param showMessage @@ -1604,12 +1612,18 @@ export class GameData { } const dexAttr = pokemon.getDexAttr(); pokemon.formIndex = formIndex; + + // Mark as caught dexEntry.caughtAttr |= dexAttr; + + // Unlock ability if (speciesStarters.hasOwnProperty(species.speciesId)) { this.starterData[species.speciesId].abilityAttr |= pokemon.abilityIndex !== 1 || pokemon.species.ability2 ? 1 << pokemon.abilityIndex : AbilityAttr.ABILITY_HIDDEN; } + + // Unlock nature dexEntry.natureAttr |= 1 << (pokemon.nature + 1); const hasPrevolution = pokemonPrevolutions.hasOwnProperty(species.speciesId); @@ -1704,9 +1718,19 @@ export class GameData { return ++this.starterData[speciesIdToIncrement].classicWinCount; } + /** + * Adds a candy to the player's game data for a given {@linkcode PokemonSpecies}. + * Will do nothing if the player does not have the Pokemon owned in their system save data. + * @param species + * @param count + */ addStarterCandy(species: PokemonSpecies, count: integer): void { - this.scene.candyBar.showStarterSpeciesCandy(species.speciesId, count); - this.starterData[species.speciesId].candyCount += count; + // Only gain candies if the Pokemon has already been marked as caught in dex (ignore "rental" pokemon) + const speciesRootForm = species.getRootSpeciesId(); + if (this.scene.gameData.dexData[speciesRootForm].caughtAttr) { + this.scene.candyBar.showStarterSpeciesCandy(species.speciesId, count); + this.starterData[species.speciesId].candyCount += count; + } } /**