From ec7cabc8c87679b5653d2c539f09e1daeca08e35 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 10 Jul 2024 18:39:55 -0700 Subject: [PATCH 01/69] getting lost at the sea MVP (event) --- .../getting-lost-at-the-sea-dialogue.ts | 50 +++++++++++++++++++ .../getting-lost-at-the-sea-encounter.ts | 45 +++++++++++++++++ .../mystery-encounter-dialogue.ts | 2 + .../mystery-encounters/mystery-encounters.ts | 6 ++- src/enums/mystery-encounter-type.ts | 3 +- src/locales/en/mystery-encounter.ts | 5 +- .../getting-lost-at-the-sea.ts | 25 ++++++++++ src/overrides.ts | 8 +-- 8 files changed, 136 insertions(+), 8 deletions(-) create mode 100644 src/data/mystery-encounters/dialogue/getting-lost-at-the-sea-dialogue.ts create mode 100644 src/data/mystery-encounters/encounters/getting-lost-at-the-sea-encounter.ts create mode 100644 src/locales/en/mystery-encounters/getting-lost-at-the-sea.ts diff --git a/src/data/mystery-encounters/dialogue/getting-lost-at-the-sea-dialogue.ts b/src/data/mystery-encounters/dialogue/getting-lost-at-the-sea-dialogue.ts new file mode 100644 index 00000000000..9e008c93c57 --- /dev/null +++ b/src/data/mystery-encounters/dialogue/getting-lost-at-the-sea-dialogue.ts @@ -0,0 +1,50 @@ +import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; + +const namepsace = "mysteryEncounter:gettingLostAtTheSea"; + +export const GettingLostAtTheSeaDialogue: MysteryEncounterDialogue = { + intro: [ + { + text: `${namepsace}:intro` + } + ], + encounterOptionsDialogue: { + title: `${namepsace}:title`, + description: `${namepsace}:description`, + query: `${namepsace}:query`, + options: [ + { + buttonLabel: `${namepsace}:option:1:label`, + buttonTooltip: `${namepsace}:option:1:tooltip`, + selected: [ + { + text: `${namepsace}:option:1:selected`, + }, + ], + }, + { + buttonLabel: `${namepsace}:option:2:label`, + buttonTooltip: `${namepsace}:option:2:tooltip`, + selected: [ + { + text: `${namepsace}:option:2:selected`, + }, + ], + }, + { + buttonLabel: `${namepsace}:option:3:label`, + buttonTooltip: `${namepsace}:option:3:tooltip`, + selected: [ + { + text: `${namepsace}:option:3:selected`, + }, + ], + }, + ], + }, + outro: [ + { + text: `${namepsace}:outro`, + }, + ], +}; diff --git a/src/data/mystery-encounters/encounters/getting-lost-at-the-sea-encounter.ts b/src/data/mystery-encounters/encounters/getting-lost-at-the-sea-encounter.ts new file mode 100644 index 00000000000..3b9b2366158 --- /dev/null +++ b/src/data/mystery-encounters/encounters/getting-lost-at-the-sea-encounter.ts @@ -0,0 +1,45 @@ +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import BattleScene from "../../../battle-scene"; +import MysteryEncounter, { + MysteryEncounterBuilder, + MysteryEncounterTier, +} from "../mystery-encounter"; + +/** + * Getting lost at the sea encounter. + * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9|GitHub Issue #9} + * @see {@linkcode MysteryEncounter|Dialogues} + * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + */ +export const GettingLostAtTheSeaEncounter: MysteryEncounter = + MysteryEncounterBuilder.withEncounterType( + MysteryEncounterType.GETTING_LOST_AT_THE_SEA + ) + .withEncounterTier(MysteryEncounterTier.COMMON) + .withIntroSpriteConfigs([ + { + fileRoot: "pokemon", + spriteKey: "130", // gyarados for now + hasShadow: false, + scale: 4, + y: 100, + x: 130, + tint: .25 + }, + ]) + .withSceneWaveRangeRequirement(11, 179) + .withOnInit((_scene: BattleScene) => { + console.log("GettingLostAtTheSeaEncounter OnInit"); + return true; + }) + .withOptionPhase(async (scene: BattleScene) => { + // OPTION 1 + }) + .withOptionPhase(async (scene: BattleScene) => { + // OPTION 2 + }) + .withOptionPhase(async (scene: BattleScene) => { + // OPTION 3 + return true; + }) + .build(); diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index 1e2dfe85045..6f8f6a844ed 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -8,6 +8,7 @@ import { SleepingSnorlaxDialogue } from "./dialogue/sleeping-snorlax-dialogue"; import { DepartmentStoreSaleDialogue } from "#app/data/mystery-encounters/dialogue/department-store-sale-dialogue"; import { ShadyVitaminDealerDialogue } from "#app/data/mystery-encounters/dialogue/shady-vitamin-dealer"; import { TextStyle } from "#app/ui/text"; +import { GettingLostAtTheSeaDialogue } from "./dialogue/getting-lost-at-the-sea-dialogue"; export class TextDisplay { speaker?: TemplateStringsArray | `mysteryEncounter:${string}`; @@ -92,4 +93,5 @@ export function initMysteryEncounterDialogue() { allMysteryEncounterDialogue[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxDialogue; allMysteryEncounterDialogue[MysteryEncounterType.DEPARTMENT_STORE_SALE] = DepartmentStoreSaleDialogue; allMysteryEncounterDialogue[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerDialogue; + allMysteryEncounterDialogue[MysteryEncounterType.GETTING_LOST_AT_THE_SEA] = GettingLostAtTheSeaDialogue; } diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index e4a748d3a23..bb27426d798 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -9,6 +9,7 @@ import { SleepingSnorlaxEncounter } from "./encounters/sleeping-snorlax"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { DepartmentStoreSaleEncounter } from "#app/data/mystery-encounters/encounters/department-store-sale"; import { ShadyVitaminDealerEncounter } from "#app/data/mystery-encounters/encounters/shady-vitamin-dealer"; +import { GettingLostAtTheSeaEncounter } from "./encounters/getting-lost-at-the-sea-encounter"; // Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * ) / 256 export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1; @@ -40,7 +41,9 @@ export const mysteryEncountersByBiome = new Map([ MysteryEncounterType.SLEEPING_SNORLAX ]], - [Biome.SEA, []], + [Biome.SEA, [ + MysteryEncounterType.GETTING_LOST_AT_THE_SEA + ]], [Biome.SWAMP, []], [Biome.BEACH, [ MysteryEncounterType.DEPARTMENT_STORE_SALE @@ -95,6 +98,7 @@ export function initMysteryEncounters() { allMysteryEncounters[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxEncounter; allMysteryEncounters[MysteryEncounterType.DEPARTMENT_STORE_SALE] = DepartmentStoreSaleEncounter; allMysteryEncounters[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerEncounter; + allMysteryEncounters[MysteryEncounterType.GETTING_LOST_AT_THE_SEA] = GettingLostAtTheSeaEncounter; // Append encounters that can occur in any biome to biome map const anyBiomeEncounters: MysteryEncounterType[] = Object.keys(MysteryEncounterType).filter(e => !isNaN(Number(e))).map(k => Number(k) as MysteryEncounterType); diff --git a/src/enums/mystery-encounter-type.ts b/src/enums/mystery-encounter-type.ts index 6e2815babca..9a78bf1b3ea 100644 --- a/src/enums/mystery-encounter-type.ts +++ b/src/enums/mystery-encounter-type.ts @@ -6,5 +6,6 @@ export enum MysteryEncounterType { SLEEPING_SNORLAX, TRAINING_SESSION, DEPARTMENT_STORE_SALE, - SHADY_VITAMIN_DEALER + SHADY_VITAMIN_DEALER, + GETTING_LOST_AT_THE_SEA //might be generalized later on } diff --git a/src/locales/en/mystery-encounter.ts b/src/locales/en/mystery-encounter.ts index ac04eed2688..3ff720dae0b 100644 --- a/src/locales/en/mystery-encounter.ts +++ b/src/locales/en/mystery-encounter.ts @@ -1,4 +1,4 @@ -import { SimpleTranslationEntries } from "#app/interfaces/locales"; +import { gettingLostAtTheSea } from "./mystery-encounters/getting-lost-at-the-sea"; /** * Patterns that can be used: @@ -12,7 +12,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; * Any '(+)' or '(-)' type of tooltip will auto-color to green/blue respectively. THIS ONLY OCCURS FOR OPTION TOOLTIPS, NOWHERE ELSE * Other types of '(...)' tooltips will have to specify the text color manually by using '@[SUMMARY_GREEN]{}' pattern */ -export const mysteryEncounter: SimpleTranslationEntries = { +export const mysteryEncounter = { // DO NOT REMOVE "unit_test_dialogue": "@ec{test}@ec{test} @ec{test@ec{test}} @ec{test1} @ec{test\} @ec{test\\} @ec{test\\\} {test}", @@ -181,4 +181,5 @@ export const mysteryEncounter: SimpleTranslationEntries = { "sleeping_snorlax_option_3_good_result": "Your @ec{option3PrimaryName} uses @ec{option3PrimaryMove}! @s{item_fanfare}It steals Leftovers off the sleeping Snorlax and you make out like bandits!", // "sleeping_snorlax_outro_win": "The mysterious challengers were defeated!", + gettingLostAtTheSea, } as const; diff --git a/src/locales/en/mystery-encounters/getting-lost-at-the-sea.ts b/src/locales/en/mystery-encounters/getting-lost-at-the-sea.ts new file mode 100644 index 00000000000..a9640a20490 --- /dev/null +++ b/src/locales/en/mystery-encounters/getting-lost-at-the-sea.ts @@ -0,0 +1,25 @@ + +export const gettingLostAtTheSea = { + intro: "TBA: INTRO MESSAGE", + "title": "Getting lost at the sea", + "description": "You get lost. Certain Pokémons can help you get back on track unharmed.", + "query": "What will you do?", + option: { + 1: { + label: "Let (Water type) guide you back", //TODO: replace (Water type) with pokemon in team + tooltip: "Needs a Water type in the party. That PKMN earns EXP as if having defeated a Lapras.", + selected: "TBA: OPTION 1 SELECTED TEXT " + }, + 2: { + label: " Let (Flying type) guide you back", //TODO: replace (Flying type) with pokemon in team + tooltip: "Needs a Flying type in the party. That PKMN earns EXP as if having defeated a Lapras.", + selected: "TBA: OPTION 2 SELECTED TEXT " + }, + 3: { + label: "Wander aimlessly until you're back", + tooltip: "All your Pokémon lose 30% of their HP. Any below that are KO'd.", + selected: "TBA: OPTION 3 SELECTED TEXT " + } + }, + "outro": "TBA GETTING LOST AT SEA OUTRO MESSAGE", +}; diff --git a/src/overrides.ts b/src/overrides.ts index ce706173691..596ee8f20f1 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -35,8 +35,8 @@ export const SEED_OVERRIDE: string = ""; export const WEATHER_OVERRIDE: WeatherType = WeatherType.NONE; export const DOUBLE_BATTLE_OVERRIDE: boolean = false; export const SINGLE_BATTLE_OVERRIDE: boolean = false; -export const STARTING_WAVE_OVERRIDE: integer = 0; -export const STARTING_BIOME_OVERRIDE: Biome = Biome.TOWN; +export const STARTING_WAVE_OVERRIDE: integer = 15; +export const STARTING_BIOME_OVERRIDE: Biome = Biome.SEA; export const ARENA_TINT_OVERRIDE: TimeOfDay = null; // Multiplies XP gained by this value including 0. Set to null to ignore the override export const XP_MULTIPLIER_OVERRIDE: number = null; @@ -118,9 +118,9 @@ export const EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0; */ // 1 to 256, set to null to ignore -export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = null; +export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = 100000; export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null; -export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null; +export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.GETTING_LOST_AT_THE_SEA; /** * MODIFIER / ITEM OVERRIDES From d9ea676a92f58394a7a5dbad59216b11125320b9 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 10 Jul 2024 20:34:28 -0700 Subject: [PATCH 02/69] add `KnownFileRoot` template literal --- src/field/mystery-encounter-intro.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index adb700b4854..ef3baa17cdf 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -2,9 +2,11 @@ import { GameObjects } from "phaser"; import BattleScene from "../battle-scene"; import MysteryEncounter from "../data/mystery-encounters/mystery-encounter"; +type KnownFileRoot = "trainer" | "pokemon" | "arenas" | "battle_anims" | "cg" | "character" | "effect" | "egg" | "events" | "inputs" | "items" | "mystery-encounters" | "pokeball" | "pokemon" | "statuses" | "trainer" | "ui"; + export class MysteryEncounterSpriteConfig { spriteKey: string; // e.g. "ace_trainer_f" - fileRoot: string; // "trainer" for trainer sprites, "pokemon" for pokemon, etc. Refer to /public/images directory for the folder name + fileRoot: KnownFileRoot & string; // Refer to /public/images directory for the folder name hasShadow?: boolean = false; // Spawns shadow underneath sprite disableAnimation?: boolean = false; // Animates frames or not repeat?: boolean = false; // Cycles animation From 81112eeabe67833d758bf3b5568cc895051e3c0f Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 10 Jul 2024 20:43:31 -0700 Subject: [PATCH 03/69] myster-encounter-intro.ts improve types and documentation --- src/field/mystery-encounter-intro.ts | 47 ++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index ef3baa17cdf..ab284d83fb4 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -2,19 +2,48 @@ import { GameObjects } from "phaser"; import BattleScene from "../battle-scene"; import MysteryEncounter from "../data/mystery-encounters/mystery-encounter"; -type KnownFileRoot = "trainer" | "pokemon" | "arenas" | "battle_anims" | "cg" | "character" | "effect" | "egg" | "events" | "inputs" | "items" | "mystery-encounters" | "pokeball" | "pokemon" | "statuses" | "trainer" | "ui"; +type KnownFileRoot = + | "trainer" + | "pokemon" + | "arenas" + | "battle_anims" + | "cg" + | "character" + | "effect" + | "egg" + | "events" + | "inputs" + | "items" + | "mystery-encounters" + | "pokeball" + | "pokemon" + | "statuses" + | "trainer" + | "ui"; export class MysteryEncounterSpriteConfig { - spriteKey: string; // e.g. "ace_trainer_f" - fileRoot: KnownFileRoot & string; // Refer to /public/images directory for the folder name - hasShadow?: boolean = false; // Spawns shadow underneath sprite - disableAnimation?: boolean = false; // Animates frames or not - repeat?: boolean = false; // Cycles animation + /** The sprite key (which is the image file name). e.g. "ace_trainer_f" */ + spriteKey: string; + /** Refer to [/public/images](../../public/images) directorty for all folder names */ + fileRoot: KnownFileRoot & string; + /** Enable shadow. Defaults to `false` */ + hasShadow?: boolean = false; + /** Disable animation. Defaults to `false` */ + disableAnimation?: boolean = false; + /** Depeat the animation. Defaults to `false` */ + repeat?: boolean = false; + /** Tint color. `0` - `1`. Higher means darker tint. */ tint?: number; - x?: number; // X offset - y?: number; // Y offset + /** X offset */ + x?: number; + /** Y offset */ + y?: number; + /** Sprite scale. `0` - `n` */ scale?: number; - isItem?: boolean; // For item sprites, set to true + /** If you are using an item sprite, set to `true` */ + isItem?: boolean; + /** The sprites alpha. `0` - `1` The lower the number, the more transparent */ + alpha?: number; } /** From 5ff1ae617dc7fb6ec144b9c2b2e225779f448414 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 10 Jul 2024 20:54:55 -0700 Subject: [PATCH 04/69] add alpha config for myst-ec sprites --- src/field/mystery-encounter-intro.ts | 37 +++++++++++++++++----------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index ab284d83fb4..3cdb6fad681 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -90,32 +90,34 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con const spacingValue = Math.round((maxX - minX) / Math.max(this.spriteConfigs.filter(s => !s.x && !s.y).length, 1)); this.spriteConfigs?.forEach((config) => { + const { spriteKey, isItem, hasShadow, scale, x, y, alpha } = config; + let sprite: GameObjects.Sprite; let tintSprite: GameObjects.Sprite; - if (!config.isItem) { - sprite = getSprite(config.spriteKey, config.hasShadow); - tintSprite = getSprite(config.spriteKey); + if (!isItem) { + sprite = getSprite(spriteKey, hasShadow); + tintSprite = getSprite(spriteKey); } else { - sprite = getItemSprite(config.spriteKey); - tintSprite = getItemSprite(config.spriteKey); + sprite = getItemSprite(spriteKey); + tintSprite = getItemSprite(spriteKey); } tintSprite.setVisible(false); - if (config.scale) { - sprite.setScale(config.scale); - tintSprite.setScale(config.scale); + if (scale) { + sprite.setScale(scale); + tintSprite.setScale(scale); } // Sprite offset from origin - if (config.x || config.y) { - if (config.x) { - sprite.setPosition(origin + config.x, sprite.y); - tintSprite.setPosition(origin + config.x, tintSprite.y); + if (x || y) { + if (x) { + sprite.setPosition(origin + x, sprite.y); + tintSprite.setPosition(origin + x, tintSprite.y); } - if (config.y) { - sprite.setPosition(sprite.x, config.y); - tintSprite.setPosition(tintSprite.x, config.y); + if (y) { + sprite.setPosition(sprite.x, y); + tintSprite.setPosition(tintSprite.x, y); } } else { // Single sprite @@ -130,6 +132,11 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con } } + if (alpha) { + sprite.setAlpha(alpha); + tintSprite.setAlpha(alpha); + } + this.add(sprite); this.add(tintSprite); }); From 0f9c0c5d24234efb85e8b8f7727c6ba86aa4e890 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 07:50:58 -0700 Subject: [PATCH 05/69] improve jsdocs in myster-encounter.ts --- src/data/mystery-encounters/mystery-encounter.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 87b80aba321..8fcf2033b61 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -356,6 +356,7 @@ export class MysteryEncounterBuilder implements Partial { doEncounterExp?: (scene: BattleScene) => boolean; doEncounterRewards?: (scene: BattleScene) => boolean; onInit?: (scene: BattleScene) => boolean; + onDone?: (scene: BattleScene) => boolean; hideBattleIntroMessage?: boolean; hideIntroVisuals?: boolean; enemyPartyConfigs?: EnemyPartyConfig[] = []; @@ -374,8 +375,11 @@ export class MysteryEncounterBuilder implements Partial { } /** - * Defines an option for the encounter - * There should be at least 2 options defined and no more than 4 + * Defines an option for the encounter. + * Use for complex options. + * There should be at least 2 options defined and no more than 4. + * If easy/streamlined use {@linkcode MysteryEncounterBuilder.withOptionPhase} + * * @param option - MysteryEncounterOption to add, can use MysteryEncounterOptionBuilder to create instance * @returns */ @@ -391,8 +395,10 @@ export class MysteryEncounterBuilder implements Partial { } /** - * Adds a streamlined option phase. - * Only use if no pre-/post-options or condtions necessary. + * Defines an option + phasefor the encounter. + * Use for easy/streamlined options. + * There should be at least 2 options defined and no more than 4. + * If complex use {@linkcode MysteryEncounterBuilder.withOption} * * @param callback - OptionPhaseCallback * @returns From ef4222cbfaf869f2aedd01efc7cd6b1ee5cf3281 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 07:57:41 -0700 Subject: [PATCH 06/69] Lost at sea myster encounter MVP.... - filled some dialogue - add placeholders for the pokemons that can help - rename it to `Lost at Sea` (from Getting lost at the sea - add damage calculation for 3rd option --- ...ea-dialogue.ts => lost-at-sea-dialogue.ts} | 4 +- .../getting-lost-at-the-sea-encounter.ts | 45 -------- .../encounters/lost-at-sea-encounter.ts | 102 ++++++++++++++++++ .../mystery-encounter-dialogue.ts | 4 +- .../mystery-encounter-option.ts | 17 ++- .../mystery-encounter-requirements.ts | 12 ++- .../mystery-encounters/mystery-encounters.ts | 6 +- src/enums/mystery-encounter-type.ts | 2 +- src/locales/en/mystery-encounter.ts | 4 +- .../getting-lost-at-the-sea.ts | 25 ----- .../en/mystery-encounters/lost-at-sea.ts | 29 +++++ src/overrides.ts | 2 +- 12 files changed, 168 insertions(+), 84 deletions(-) rename src/data/mystery-encounters/dialogue/{getting-lost-at-the-sea-dialogue.ts => lost-at-sea-dialogue.ts} (89%) delete mode 100644 src/data/mystery-encounters/encounters/getting-lost-at-the-sea-encounter.ts create mode 100644 src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts delete mode 100644 src/locales/en/mystery-encounters/getting-lost-at-the-sea.ts create mode 100644 src/locales/en/mystery-encounters/lost-at-sea.ts diff --git a/src/data/mystery-encounters/dialogue/getting-lost-at-the-sea-dialogue.ts b/src/data/mystery-encounters/dialogue/lost-at-sea-dialogue.ts similarity index 89% rename from src/data/mystery-encounters/dialogue/getting-lost-at-the-sea-dialogue.ts rename to src/data/mystery-encounters/dialogue/lost-at-sea-dialogue.ts index 9e008c93c57..1bfea19d83e 100644 --- a/src/data/mystery-encounters/dialogue/getting-lost-at-the-sea-dialogue.ts +++ b/src/data/mystery-encounters/dialogue/lost-at-sea-dialogue.ts @@ -1,8 +1,8 @@ import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; -const namepsace = "mysteryEncounter:gettingLostAtTheSea"; +const namepsace = "mysteryEncounter:lostAtSea"; -export const GettingLostAtTheSeaDialogue: MysteryEncounterDialogue = { +export const LostAtSeaDialogue: MysteryEncounterDialogue = { intro: [ { text: `${namepsace}:intro` diff --git a/src/data/mystery-encounters/encounters/getting-lost-at-the-sea-encounter.ts b/src/data/mystery-encounters/encounters/getting-lost-at-the-sea-encounter.ts deleted file mode 100644 index 3b9b2366158..00000000000 --- a/src/data/mystery-encounters/encounters/getting-lost-at-the-sea-encounter.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import BattleScene from "../../../battle-scene"; -import MysteryEncounter, { - MysteryEncounterBuilder, - MysteryEncounterTier, -} from "../mystery-encounter"; - -/** - * Getting lost at the sea encounter. - * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9|GitHub Issue #9} - * @see {@linkcode MysteryEncounter|Dialogues} - * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) - */ -export const GettingLostAtTheSeaEncounter: MysteryEncounter = - MysteryEncounterBuilder.withEncounterType( - MysteryEncounterType.GETTING_LOST_AT_THE_SEA - ) - .withEncounterTier(MysteryEncounterTier.COMMON) - .withIntroSpriteConfigs([ - { - fileRoot: "pokemon", - spriteKey: "130", // gyarados for now - hasShadow: false, - scale: 4, - y: 100, - x: 130, - tint: .25 - }, - ]) - .withSceneWaveRangeRequirement(11, 179) - .withOnInit((_scene: BattleScene) => { - console.log("GettingLostAtTheSeaEncounter OnInit"); - return true; - }) - .withOptionPhase(async (scene: BattleScene) => { - // OPTION 1 - }) - .withOptionPhase(async (scene: BattleScene) => { - // OPTION 2 - }) - .withOptionPhase(async (scene: BattleScene) => { - // OPTION 3 - return true; - }) - .build(); diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts new file mode 100644 index 00000000000..d2c60762b2d --- /dev/null +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -0,0 +1,102 @@ +import { Type } from "#app/data/type.js"; +import { Species } from "#app/enums/species.js"; +import { PlayerPokemon } from "#app/field/pokemon.js"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import BattleScene from "../../../battle-scene"; +import MysteryEncounter, { + MysteryEncounterBuilder, + MysteryEncounterTier, +} from "../mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import { leaveEncounterWithoutBattle } from "../mystery-encounter-utils"; + +const DAMAGE_PERCENTAGE: number = 30; // 0 - 100 + +/** + * Lost at sea encounter. + * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9|GitHub Issue #9} + * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + */ +export const LostAtSeaEncounter: MysteryEncounter = + MysteryEncounterBuilder.withEncounterType( + MysteryEncounterType.LOST_AT_SEA + ) + .withEncounterTier(MysteryEncounterTier.COMMON) + .withIntroSpriteConfigs([ + { + fileRoot: "pokemon", + spriteKey: `${Species.GYARADOS}`, + hasShadow: false, + scale: 4, + y: 100, + x: 130, + tint: 0.75, + alpha: 0.25, + }, + ]) + .withSceneWaveRangeRequirement(11, 179) + .withOnInit((scene: BattleScene) => { + const party = scene.getParty(); + const { mysteryEncounter } = scene.currentBattle; + + mysteryEncounter.setDialogueToken( + "damagePercentage", + String(DAMAGE_PERCENTAGE) + ); + + // check for water pokemon + const waterPkm = findPokemonByType(party, Type.WATER); + mysteryEncounter.setDialogueToken("waterPkm", waterPkm?.name ?? ""); + + // check for flying pokemon + const flyingPkm = findPokemonByType(party, Type.FLYING); + mysteryEncounter.setDialogueToken( + "flyingPkm", + flyingPkm?.name ?? "" + ); + + return true; + }) + /** + * Option 1: Use a (non fainted) water pokemon to guide you back. + * Receives EXP similar to defeating a Lapras + */ + .withOption( + new MysteryEncounterOptionBuilder() + .withPokemonTypeRequirement(Type.WATER, true, 1) + .withOptionPhase(async (scene: BattleScene) => { + console.debug("Lost at sea: Option 1 - Water Pokemon"); + leaveEncounterWithoutBattle(scene); + }) + .build() + ) + /** + * Option 2: Use a (non fainted) flying pokemon to guide you back. + * Receives EXP similar to defeating a Lapras + */ + .withOption( + new MysteryEncounterOptionBuilder() + .withPokemonTypeRequirement(Type.FLYING, true, 1) + .withOptionPhase(async (scene: BattleScene) => { + console.debug("Lost at sea: Option 2 - Flying Pokemon"); + leaveEncounterWithoutBattle(scene); + }) + .build() + ) + /** + * Option 3: Wander aimlessly. All pokemons lose 30% of their HP (or KO on 0 HP). + */ + .withOptionPhase(async (scene: BattleScene) => { + const party = scene.getParty().filter((p) => !p.isFainted()); + party.forEach((pkm) => { + const damage = Math.round(pkm.getMaxHp() / 3); + pkm.hp = Math.min(pkm.hp, damage); + }); + leaveEncounterWithoutBattle(scene); + return true; + }) + .build(); + +const findPokemonByType = (party: PlayerPokemon[], type: Type) => { + return party.find((p) => p.getTypes(true).includes(type)); +}; diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index 6f8f6a844ed..aa04ea89e73 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -8,7 +8,7 @@ import { SleepingSnorlaxDialogue } from "./dialogue/sleeping-snorlax-dialogue"; import { DepartmentStoreSaleDialogue } from "#app/data/mystery-encounters/dialogue/department-store-sale-dialogue"; import { ShadyVitaminDealerDialogue } from "#app/data/mystery-encounters/dialogue/shady-vitamin-dealer"; import { TextStyle } from "#app/ui/text"; -import { GettingLostAtTheSeaDialogue } from "./dialogue/getting-lost-at-the-sea-dialogue"; +import { LostAtSeaDialogue } from "./dialogue/lost-at-sea-dialogue"; export class TextDisplay { speaker?: TemplateStringsArray | `mysteryEncounter:${string}`; @@ -93,5 +93,5 @@ export function initMysteryEncounterDialogue() { allMysteryEncounterDialogue[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxDialogue; allMysteryEncounterDialogue[MysteryEncounterType.DEPARTMENT_STORE_SALE] = DepartmentStoreSaleDialogue; allMysteryEncounterDialogue[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerDialogue; - allMysteryEncounterDialogue[MysteryEncounterType.GETTING_LOST_AT_THE_SEA] = GettingLostAtTheSeaDialogue; + allMysteryEncounterDialogue[MysteryEncounterType.LOST_AT_SEA] = LostAtSeaDialogue; } diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index f1316ff64f1..3fc04213d44 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -2,7 +2,8 @@ import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounte import { PlayerPokemon } from "#app/field/pokemon"; import BattleScene from "../../battle-scene"; import * as Utils from "../../utils"; -import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement } from "./mystery-encounter-requirements"; +import { Type } from "../type"; +import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "./mystery-encounter-requirements"; export type OptionPhaseCallback = (scene: BattleScene) => Promise; @@ -158,6 +159,20 @@ export class MysteryEncounterOptionBuilder implements Partial> { this.secondaryPokemonRequirements.push(requirement); this.excludePrimaryFromSecondaryRequirements = excludePrimaryFromSecondaryRequirements; diff --git a/src/data/mystery-encounters/mystery-encounter-requirements.ts b/src/data/mystery-encounters/mystery-encounter-requirements.ts index 8781e5a4cdc..f31b37118ee 100644 --- a/src/data/mystery-encounters/mystery-encounter-requirements.ts +++ b/src/data/mystery-encounters/mystery-encounter-requirements.ts @@ -331,11 +331,13 @@ export class NatureRequirement extends EncounterPokemonRequirement { export class TypeRequirement extends EncounterPokemonRequirement { requiredType: Type[]; + excludeFainted: boolean; minNumberOfPokemon: number; invertQuery: boolean; - constructor(type: Type | Type[], minNumberOfPokemon: number = 1, invertQuery: boolean = false) { + constructor(type: Type | Type[], excludeFainted: boolean = true, minNumberOfPokemon: number = 1, invertQuery: boolean = false) { super(); + this.excludeFainted = excludeFainted; this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; if (type instanceof Array) { @@ -347,10 +349,16 @@ export class TypeRequirement extends EncounterPokemonRequirement { } meetsRequirement(scene: BattleScene): boolean { - const partyPokemon = scene.getParty(); + let partyPokemon = scene.getParty(); + if (isNullOrUndefined(partyPokemon) || this?.requiredType?.length < 0) { return false; } + + if (!this.excludeFainted) { + partyPokemon = partyPokemon.filter((pokemon) => !pokemon.isFainted()); + } + return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon; } diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index bb27426d798..e74101763fe 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -9,7 +9,7 @@ import { SleepingSnorlaxEncounter } from "./encounters/sleeping-snorlax"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { DepartmentStoreSaleEncounter } from "#app/data/mystery-encounters/encounters/department-store-sale"; import { ShadyVitaminDealerEncounter } from "#app/data/mystery-encounters/encounters/shady-vitamin-dealer"; -import { GettingLostAtTheSeaEncounter } from "./encounters/getting-lost-at-the-sea-encounter"; +import { LostAtSeaEncounter } from "./encounters/lost-at-sea-encounter"; // Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * ) / 256 export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1; @@ -42,7 +42,7 @@ export const mysteryEncountersByBiome = new Map([ ]], [Biome.SEA, [ - MysteryEncounterType.GETTING_LOST_AT_THE_SEA + MysteryEncounterType.LOST_AT_SEA ]], [Biome.SWAMP, []], [Biome.BEACH, [ @@ -98,7 +98,7 @@ export function initMysteryEncounters() { allMysteryEncounters[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxEncounter; allMysteryEncounters[MysteryEncounterType.DEPARTMENT_STORE_SALE] = DepartmentStoreSaleEncounter; allMysteryEncounters[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerEncounter; - allMysteryEncounters[MysteryEncounterType.GETTING_LOST_AT_THE_SEA] = GettingLostAtTheSeaEncounter; + allMysteryEncounters[MysteryEncounterType.LOST_AT_SEA] = LostAtSeaEncounter; // Append encounters that can occur in any biome to biome map const anyBiomeEncounters: MysteryEncounterType[] = Object.keys(MysteryEncounterType).filter(e => !isNaN(Number(e))).map(k => Number(k) as MysteryEncounterType); diff --git a/src/enums/mystery-encounter-type.ts b/src/enums/mystery-encounter-type.ts index 9a78bf1b3ea..2c467dc0873 100644 --- a/src/enums/mystery-encounter-type.ts +++ b/src/enums/mystery-encounter-type.ts @@ -7,5 +7,5 @@ export enum MysteryEncounterType { TRAINING_SESSION, DEPARTMENT_STORE_SALE, SHADY_VITAMIN_DEALER, - GETTING_LOST_AT_THE_SEA //might be generalized later on + LOST_AT_SEA //might be generalized later on } diff --git a/src/locales/en/mystery-encounter.ts b/src/locales/en/mystery-encounter.ts index 3ff720dae0b..180aa6dd2d8 100644 --- a/src/locales/en/mystery-encounter.ts +++ b/src/locales/en/mystery-encounter.ts @@ -1,4 +1,4 @@ -import { gettingLostAtTheSea } from "./mystery-encounters/getting-lost-at-the-sea"; +import { lostAtSea } from "./mystery-encounters/lost-at-sea"; /** * Patterns that can be used: @@ -181,5 +181,5 @@ export const mysteryEncounter = { "sleeping_snorlax_option_3_good_result": "Your @ec{option3PrimaryName} uses @ec{option3PrimaryMove}! @s{item_fanfare}It steals Leftovers off the sleeping Snorlax and you make out like bandits!", // "sleeping_snorlax_outro_win": "The mysterious challengers were defeated!", - gettingLostAtTheSea, + lostAtSea, } as const; diff --git a/src/locales/en/mystery-encounters/getting-lost-at-the-sea.ts b/src/locales/en/mystery-encounters/getting-lost-at-the-sea.ts deleted file mode 100644 index a9640a20490..00000000000 --- a/src/locales/en/mystery-encounters/getting-lost-at-the-sea.ts +++ /dev/null @@ -1,25 +0,0 @@ - -export const gettingLostAtTheSea = { - intro: "TBA: INTRO MESSAGE", - "title": "Getting lost at the sea", - "description": "You get lost. Certain Pokémons can help you get back on track unharmed.", - "query": "What will you do?", - option: { - 1: { - label: "Let (Water type) guide you back", //TODO: replace (Water type) with pokemon in team - tooltip: "Needs a Water type in the party. That PKMN earns EXP as if having defeated a Lapras.", - selected: "TBA: OPTION 1 SELECTED TEXT " - }, - 2: { - label: " Let (Flying type) guide you back", //TODO: replace (Flying type) with pokemon in team - tooltip: "Needs a Flying type in the party. That PKMN earns EXP as if having defeated a Lapras.", - selected: "TBA: OPTION 2 SELECTED TEXT " - }, - 3: { - label: "Wander aimlessly until you're back", - tooltip: "All your Pokémon lose 30% of their HP. Any below that are KO'd.", - selected: "TBA: OPTION 3 SELECTED TEXT " - } - }, - "outro": "TBA GETTING LOST AT SEA OUTRO MESSAGE", -}; diff --git a/src/locales/en/mystery-encounters/lost-at-sea.ts b/src/locales/en/mystery-encounters/lost-at-sea.ts new file mode 100644 index 00000000000..a5846acf058 --- /dev/null +++ b/src/locales/en/mystery-encounters/lost-at-sea.ts @@ -0,0 +1,29 @@ +export const lostAtSea = { + intro: "You are halucinating and starting to loose your bearings.", + title: "Lost at sea", + description: + "You get lost at sea. All you \"sea\" is water everywhere and the sun is burning bright. Certain Pokémons can help you get back on track unharmed.", + query: "What will you do?", + option: { + 1: { + label: "Use @ec{waterPkm}", // pkm has to be of type water + tooltip: + "Use @ec{waterPkm} to guide you back. @ec{waterPkm} earns EXP as if having defeated a Lapras.", + selected: "@ec{waterPkm} guides you back and earns EXP.", + }, + 2: { + label: "Use @ec{flyingPkm}", // pkm has to be of type water + tooltip: + "Use @ec{flyingPkm} to guide you back. @ec{flyingPkm} earns EXP as if having defeated a Lapras.", + selected: "@ec{flyingPkm} guides you back and earns EXP.", + }, + 3: { + label: "Wander aimlessly", + tooltip: + "Wander aimlessly until you're back. All your Pokémon lose @ec{damagePercentage}% of their HP. Any below that are KO'd.", + selected: + "You wander aimlessly around. After hours of wandering, you find your way back. You and your team take the toll.", + }, + }, + // outro: "TBA: OUTRO MESSAGE", +}; diff --git a/src/overrides.ts b/src/overrides.ts index 596ee8f20f1..0f2ff365ae1 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -120,7 +120,7 @@ export const EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0; // 1 to 256, set to null to ignore export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = 100000; export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null; -export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.GETTING_LOST_AT_THE_SEA; +export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.LOST_AT_SEA; /** * MODIFIER / ITEM OVERRIDES From b7bf22e3e595741bbdbabd5932dee7561a447195 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 08:02:01 -0700 Subject: [PATCH 07/69] fix typo in myster-encounter-intro --- src/field/mystery-encounter-intro.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index 3cdb6fad681..683170df4ac 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -30,7 +30,7 @@ export class MysteryEncounterSpriteConfig { hasShadow?: boolean = false; /** Disable animation. Defaults to `false` */ disableAnimation?: boolean = false; - /** Depeat the animation. Defaults to `false` */ + /** Repeat the animation. Defaults to `false` */ repeat?: boolean = false; /** Tint color. `0` - `1`. Higher means darker tint. */ tint?: number; From 524d4a4ca7571ebc3d55c254136d81c09dfa31c5 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 08:03:12 -0700 Subject: [PATCH 08/69] lost-at-sea ME: fix using DAMAGE_PERCENTAGE for damage calculation --- src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index d2c60762b2d..0e906bb60c4 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -89,7 +89,7 @@ export const LostAtSeaEncounter: MysteryEncounter = .withOptionPhase(async (scene: BattleScene) => { const party = scene.getParty().filter((p) => !p.isFainted()); party.forEach((pkm) => { - const damage = Math.round(pkm.getMaxHp() / 3); + const damage = Math.round(pkm.getMaxHp() / (DAMAGE_PERCENTAGE / 100)); pkm.hp = Math.min(pkm.hp, damage); }); leaveEncounterWithoutBattle(scene); From 93fbb0e401a7a354ca31133d6b28e56d2230d172 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 08:04:33 -0700 Subject: [PATCH 09/69] add jsdoc to lost-at-sea-encounter --- .../encounters/lost-at-sea-encounter.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 0e906bb60c4..28a8c572872 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -10,6 +10,11 @@ import MysteryEncounter, { import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { leaveEncounterWithoutBattle } from "../mystery-encounter-utils"; +/** + * Damage percentage taken when wandering aimlessly. + * Can be a number between `0` - `100`. + * The higher the more damage taken (100% = instant KO). + */ const DAMAGE_PERCENTAGE: number = 30; // 0 - 100 /** @@ -18,9 +23,7 @@ const DAMAGE_PERCENTAGE: number = 30; // 0 - 100 * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) */ export const LostAtSeaEncounter: MysteryEncounter = - MysteryEncounterBuilder.withEncounterType( - MysteryEncounterType.LOST_AT_SEA - ) + MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.LOST_AT_SEA) .withEncounterTier(MysteryEncounterTier.COMMON) .withIntroSpriteConfigs([ { From 47f12b665b07413850192e8de46a0a46689f4db4 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 09:23:43 -0700 Subject: [PATCH 10/69] ME: add damage/heal application utils --- .../mystery-encounter-utils.ts | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/data/mystery-encounters/mystery-encounter-utils.ts b/src/data/mystery-encounters/mystery-encounter-utils.ts index 2d8d7bfbf66..00e6eb90040 100644 --- a/src/data/mystery-encounters/mystery-encounter-utils.ts +++ b/src/data/mystery-encounters/mystery-encounter-utils.ts @@ -1,4 +1,4 @@ -import i18next from "i18next"; +import i18next, { ParseKeys } from "i18next"; import { BattleType } from "#app/battle"; import BattleScene from "../../battle-scene"; import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "../pokemon-species"; @@ -179,7 +179,7 @@ function getTextWithDialogueTokens(scene: BattleScene, textKey: TemplateStringsA return null; } - let textString: string = i18next.t(textKey); + let textString: string = i18next.t(textKey as ParseKeys); // Apply dialogue tokens const dialogueTokens = scene.currentBattle?.mysteryEncounter?.dialogueTokens; @@ -821,3 +821,52 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n console.log(`Starting weight: ${baseSpawnWeight}\nAverage MEs per run: ${totalMean}\nStandard Deviation: ${totalStd}\nAvg Commons: ${commonMean}\nAvg Uncommons: ${uncommonMean}\nAvg Rares: ${rareMean}\nAvg Super Rares: ${superRareMean}`); } + +/** + * Handles applying hp changes to a player pokemon. + * Takes care of not going below `0`, above max-hp, adding `FNT` status correctly and updating the pokemon info. + * TODO: handle special cases like wonder-guard/ninjask + * + * @param pokemon the player pokemon to apply the hp change to + * @param damage the hp change amount. Positive for heal. Negative for damage + * + */ +function applyHpChangeToPokemon(pokemon: PlayerPokemon, value: number) { + const hpChange = Math.round(pokemon.hp + value); + const nextHp = Math.max(Math.min(hpChange, pokemon.getMaxHp()), 0); + if (nextHp === 0) { + koPlayerPokemon(pokemon); + } else { + pokemon.hp = nextHp; + } +} + +/** + * Handles applying damage to a player pokemon + * + * @param pokemon the player pokemon to apply damage to + * @param damage the amount of damage to apply + * @see {@linkcode applyHpChangeToPokemon} + */ +export function applyDamageToPokemon(pokemon: PlayerPokemon, damage: number) { + if (damage <= 0) { + console.warn("Healing pokemon with `applyDamageToPokemon` is not recommended! Please use `applyHealToPokemon` instead."); + } + + applyHpChangeToPokemon(pokemon, -damage); +} + +/** + * Handles applying heal to a player pokemon + * + * @param pokemon the player pokemon to apply heal to + * @param heal the amount of heal to apply + * @see {@linkcode applyHpChangeToPokemon} + */ +export function applyHealToPokemon(pokemon: PlayerPokemon, heal: number) { + if (heal <= 0) { + console.warn("Damaging pokemong with `applyHealToPokemon` is not recommended! Please use `applyDamageToPokemon` instead."); + } + + applyHpChangeToPokemon(pokemon, heal); +} From 85df834811e4c0dad0a8394de9960a5bd02ff580 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 09:35:15 -0700 Subject: [PATCH 11/69] optimize mystery-encounter-utils.setEncounterExp() --- src/data/mystery-encounters/mystery-encounter-utils.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/data/mystery-encounters/mystery-encounter-utils.ts b/src/data/mystery-encounters/mystery-encounter-utils.ts index 00e6eb90040..2f9f3eb8a45 100644 --- a/src/data/mystery-encounters/mystery-encounter-utils.ts +++ b/src/data/mystery-encounters/mystery-encounter-utils.ts @@ -582,7 +582,7 @@ export function setEncounterRewards(scene: BattleScene, customShopRewards?: Cust * Will initialize exp phases into the phase queue (these are in addition to any combat or other exp earned) * Exp Share and Exp Balance will still function as normal * @param scene - Battle Scene - * @param participantIds - ids of party pokemon that get full exp value. Other party members will receive Exp Share amounts + * @param participantId - ids of party pokemon that get full exp value. Other party members will receive Exp Share amounts * @param baseExpValue - gives exp equivalent to a pokemon of the wave index's level. * Guidelines: * 36 - Sunkern (lowest in game) @@ -595,7 +595,8 @@ export function setEncounterRewards(scene: BattleScene, customShopRewards?: Cust * 608 - Blissey (highest in game) * @param useWaveIndex - set to false when directly passing the the full exp value instead of baseExpValue */ -export function setEncounterExp(scene: BattleScene, participantIds: integer[], baseExpValue: number, useWaveIndex: boolean = true) { +export function setEncounterExp(scene: BattleScene, participantId: integer | integer[], baseExpValue: number, useWaveIndex: boolean = true) { + const participantIds = Array.isArray(participantId) ? participantId : [participantId]; scene.currentBattle.mysteryEncounter.doEncounterExp = (scene: BattleScene) => { const party = scene.getParty(); const expShareModifier = scene.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier; From 5a79b50f1dca55cad11605381c7d52698bf5cb0d Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 10:30:26 -0700 Subject: [PATCH 12/69] lost at sea encounter finishing touches --- .../dialogue/lost-at-sea-dialogue.ts | 9 +-- .../encounters/lost-at-sea-encounter.ts | 77 ++++++++++++++----- .../en/mystery-encounters/lost-at-sea.ts | 3 +- 3 files changed, 61 insertions(+), 28 deletions(-) diff --git a/src/data/mystery-encounters/dialogue/lost-at-sea-dialogue.ts b/src/data/mystery-encounters/dialogue/lost-at-sea-dialogue.ts index 1bfea19d83e..3e0e69c1a97 100644 --- a/src/data/mystery-encounters/dialogue/lost-at-sea-dialogue.ts +++ b/src/data/mystery-encounters/dialogue/lost-at-sea-dialogue.ts @@ -5,8 +5,8 @@ const namepsace = "mysteryEncounter:lostAtSea"; export const LostAtSeaDialogue: MysteryEncounterDialogue = { intro: [ { - text: `${namepsace}:intro` - } + text: `${namepsace}:intro`, + }, ], encounterOptionsDialogue: { title: `${namepsace}:title`, @@ -42,9 +42,4 @@ export const LostAtSeaDialogue: MysteryEncounterDialogue = { }, ], }, - outro: [ - { - text: `${namepsace}:outro`, - }, - ], }; diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 28a8c572872..fb902bb9958 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -8,7 +8,11 @@ import MysteryEncounter, { MysteryEncounterTier, } from "../mystery-encounter"; import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -import { leaveEncounterWithoutBattle } from "../mystery-encounter-utils"; +import { + applyDamageToPokemon, + leaveEncounterWithoutBattle, + setEncounterExp, +} from "../mystery-encounter-utils"; /** * Damage percentage taken when wandering aimlessly. @@ -17,9 +21,12 @@ import { leaveEncounterWithoutBattle } from "../mystery-encounter-utils"; */ const DAMAGE_PERCENTAGE: number = 30; // 0 - 100 +let waterPkm: PlayerPokemon; +let flyingPkm: PlayerPokemon; + /** * Lost at sea encounter. - * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9|GitHub Issue #9} + * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9 | GitHub Issue #9} * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) */ export const LostAtSeaEncounter: MysteryEncounter = @@ -39,7 +46,9 @@ export const LostAtSeaEncounter: MysteryEncounter = ]) .withSceneWaveRangeRequirement(11, 179) .withOnInit((scene: BattleScene) => { - const party = scene.getParty(); + const allowedPokemon = scene + .getParty() + .filter((p) => p.isAllowedInBattle()); const { mysteryEncounter } = scene.currentBattle; mysteryEncounter.setDialogueToken( @@ -48,11 +57,11 @@ export const LostAtSeaEncounter: MysteryEncounter = ); // check for water pokemon - const waterPkm = findPokemonByType(party, Type.WATER); + waterPkm = findPokemonByType(allowedPokemon, Type.WATER); mysteryEncounter.setDialogueToken("waterPkm", waterPkm?.name ?? ""); // check for flying pokemon - const flyingPkm = findPokemonByType(party, Type.FLYING); + flyingPkm = findPokemonByType(allowedPokemon, Type.FLYING); mysteryEncounter.setDialogueToken( "flyingPkm", flyingPkm?.name ?? "" @@ -67,10 +76,9 @@ export const LostAtSeaEncounter: MysteryEncounter = .withOption( new MysteryEncounterOptionBuilder() .withPokemonTypeRequirement(Type.WATER, true, 1) - .withOptionPhase(async (scene: BattleScene) => { - console.debug("Lost at sea: Option 1 - Water Pokemon"); - leaveEncounterWithoutBattle(scene); - }) + .withOptionPhase(async (scene: BattleScene) => + handleGuidingOption(scene, waterPkm) + ) .build() ) /** @@ -80,26 +88,57 @@ export const LostAtSeaEncounter: MysteryEncounter = .withOption( new MysteryEncounterOptionBuilder() .withPokemonTypeRequirement(Type.FLYING, true, 1) - .withOptionPhase(async (scene: BattleScene) => { - console.debug("Lost at sea: Option 2 - Flying Pokemon"); - leaveEncounterWithoutBattle(scene); - }) + .withOptionPhase(async (scene: BattleScene) => + handleGuidingOption(scene, flyingPkm) + ) .build() ) /** * Option 3: Wander aimlessly. All pokemons lose 30% of their HP (or KO on 0 HP). */ .withOptionPhase(async (scene: BattleScene) => { - const party = scene.getParty().filter((p) => !p.isFainted()); - party.forEach((pkm) => { - const damage = Math.round(pkm.getMaxHp() / (DAMAGE_PERCENTAGE / 100)); - pkm.hp = Math.min(pkm.hp, damage); + const allowedPokemon = scene + .getParty() + .filter((p) => p.isAllowedInBattle()); + + allowedPokemon.forEach((pkm) => { + const percentage = DAMAGE_PERCENTAGE / 100; + const damage = Math.floor(pkm.getMaxHp() * percentage); + return applyDamageToPokemon(pkm, damage); }); leaveEncounterWithoutBattle(scene); return true; }) .build(); -const findPokemonByType = (party: PlayerPokemon[], type: Type) => { +/** + * Find a pokemon inside the given party by a given type + * + * @param party player pokemon party + * @param type type to search for + * @returns + */ +function findPokemonByType(party: PlayerPokemon[], type: Type) { return party.find((p) => p.getTypes(true).includes(type)); -}; +} + +/** + * Generic handler for using a guiding pokemon to guide you back. + * + * @param scene Battle scene + * @param guidePokemon pokemon choosen as a guide + */ +function handleGuidingOption(scene: BattleScene, guidePokemon: PlayerPokemon) { + /** Base EXP value for guiding pokemon. Currently Lapras base-value */ + const baseExpValue: number = 187; + + if (guidePokemon) { + setEncounterExp(scene, guidePokemon.id, baseExpValue, true); + } else { + console.warn( + "Lost at sea: No guide pokemon found but pokemon guides player. huh!?" + ); + } + + leaveEncounterWithoutBattle(scene); +} diff --git a/src/locales/en/mystery-encounters/lost-at-sea.ts b/src/locales/en/mystery-encounters/lost-at-sea.ts index a5846acf058..e4ea07c77ad 100644 --- a/src/locales/en/mystery-encounters/lost-at-sea.ts +++ b/src/locales/en/mystery-encounters/lost-at-sea.ts @@ -12,7 +12,7 @@ export const lostAtSea = { selected: "@ec{waterPkm} guides you back and earns EXP.", }, 2: { - label: "Use @ec{flyingPkm}", // pkm has to be of type water + label: "Use @ec{flyingPkm}", // pkm has to be of type flying tooltip: "Use @ec{flyingPkm} to guide you back. @ec{flyingPkm} earns EXP as if having defeated a Lapras.", selected: "@ec{flyingPkm} guides you back and earns EXP.", @@ -25,5 +25,4 @@ export const lostAtSea = { "You wander aimlessly around. After hours of wandering, you find your way back. You and your team take the toll.", }, }, - // outro: "TBA: OUTRO MESSAGE", }; From 58d533fb348170fb83c2ff1850c0dd67ac682f69 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Fri, 12 Jul 2024 11:17:01 -0700 Subject: [PATCH 13/69] migrate lost at sea encounter to new dialogue pattern --- .../dialogue/lost-at-sea-dialogue.ts | 45 -------- .../encounters/lost-at-sea-encounter.ts | 103 +++++++++++++----- 2 files changed, 75 insertions(+), 73 deletions(-) delete mode 100644 src/data/mystery-encounters/dialogue/lost-at-sea-dialogue.ts diff --git a/src/data/mystery-encounters/dialogue/lost-at-sea-dialogue.ts b/src/data/mystery-encounters/dialogue/lost-at-sea-dialogue.ts deleted file mode 100644 index 3e0e69c1a97..00000000000 --- a/src/data/mystery-encounters/dialogue/lost-at-sea-dialogue.ts +++ /dev/null @@ -1,45 +0,0 @@ -import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; - -const namepsace = "mysteryEncounter:lostAtSea"; - -export const LostAtSeaDialogue: MysteryEncounterDialogue = { - intro: [ - { - text: `${namepsace}:intro`, - }, - ], - encounterOptionsDialogue: { - title: `${namepsace}:title`, - description: `${namepsace}:description`, - query: `${namepsace}:query`, - options: [ - { - buttonLabel: `${namepsace}:option:1:label`, - buttonTooltip: `${namepsace}:option:1:tooltip`, - selected: [ - { - text: `${namepsace}:option:1:selected`, - }, - ], - }, - { - buttonLabel: `${namepsace}:option:2:label`, - buttonTooltip: `${namepsace}:option:2:tooltip`, - selected: [ - { - text: `${namepsace}:option:2:selected`, - }, - ], - }, - { - buttonLabel: `${namepsace}:option:3:label`, - buttonTooltip: `${namepsace}:option:3:tooltip`, - selected: [ - { - text: `${namepsace}:option:3:selected`, - }, - ], - }, - ], - }, -}; diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index fb902bb9958..b4525176cf1 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -7,7 +7,10 @@ import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import { + EncounterOptionMode, + MysteryEncounterOptionBuilder, +} from "../mystery-encounter-option"; import { applyDamageToPokemon, leaveEncounterWithoutBattle, @@ -20,6 +23,8 @@ import { * The higher the more damage taken (100% = instant KO). */ const DAMAGE_PERCENTAGE: number = 30; // 0 - 100 +/** The i18n namespace for the encounter */ +const namepsace = "mysteryEncounter:lostAtSea"; let waterPkm: PlayerPokemon; let flyingPkm: PlayerPokemon; @@ -32,6 +37,7 @@ let flyingPkm: PlayerPokemon; export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.LOST_AT_SEA) .withEncounterTier(MysteryEncounterTier.COMMON) + .withSceneWaveRangeRequirement(11, 179) .withIntroSpriteConfigs([ { fileRoot: "pokemon", @@ -44,7 +50,11 @@ export const LostAtSeaEncounter: MysteryEncounter = alpha: 0.25, }, ]) - .withSceneWaveRangeRequirement(11, 179) + .withIntroDialogue([ + { + text: `${namepsace}:intro`, + }, + ]) .withOnInit((scene: BattleScene) => { const allowedPokemon = scene .getParty() @@ -69,46 +79,80 @@ export const LostAtSeaEncounter: MysteryEncounter = return true; }) - /** - * Option 1: Use a (non fainted) water pokemon to guide you back. - * Receives EXP similar to defeating a Lapras - */ + .withTitle(`${namepsace}:title`) + .withDescription(`${namepsace}:description`) + .withQuery(`${namepsace}:query`) .withOption( + /** + * Option 1: Use a (non fainted) water pokemon to guide you back. + * Receives EXP similar to defeating a Lapras + */ new MysteryEncounterOptionBuilder() .withPokemonTypeRequirement(Type.WATER, true, 1) + .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) + .withDialogue({ + buttonLabel: `${namepsace}:option:1:label`, + buttonTooltip: `${namepsace}:option:1:tooltip`, + selected: [ + { + text: `${namepsace}:option:1:selected`, + }, + ], + }) .withOptionPhase(async (scene: BattleScene) => - handleGuidingOption(scene, waterPkm) + handleGuidingOptionPhase(scene, waterPkm) ) .build() ) - /** - * Option 2: Use a (non fainted) flying pokemon to guide you back. - * Receives EXP similar to defeating a Lapras - */ .withOption( + /** + * Option 2: Use a (non fainted) flying pokemon to guide you back. + * Receives EXP similar to defeating a Lapras + */ new MysteryEncounterOptionBuilder() .withPokemonTypeRequirement(Type.FLYING, true, 1) + .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) + .withDialogue({ + buttonLabel: `${namepsace}:option:2:label`, + buttonTooltip: `${namepsace}:option:2:tooltip`, + selected: [ + { + text: `${namepsace}:option:2:selected`, + }, + ], + }) .withOptionPhase(async (scene: BattleScene) => - handleGuidingOption(scene, flyingPkm) + handleGuidingOptionPhase(scene, flyingPkm) ) .build() ) - /** - * Option 3: Wander aimlessly. All pokemons lose 30% of their HP (or KO on 0 HP). - */ - .withOptionPhase(async (scene: BattleScene) => { - const allowedPokemon = scene - .getParty() - .filter((p) => p.isAllowedInBattle()); + .withSimpleOption( + /** + * Option 3: Wander aimlessly. All pokemons lose {@linkcode DAMAGE_PERCENTAGE}}% of their HP (or KO on 0 HP). + */ + { + buttonLabel: `${namepsace}:option:3:label`, + buttonTooltip: `${namepsace}:option:3:tooltip`, + selected: [ + { + text: `${namepsace}:option:3:selected`, + }, + ], + }, + async (scene: BattleScene) => { + const allowedPokemon = scene + .getParty() + .filter((p) => p.isAllowedInBattle()); - allowedPokemon.forEach((pkm) => { - const percentage = DAMAGE_PERCENTAGE / 100; - const damage = Math.floor(pkm.getMaxHp() * percentage); - return applyDamageToPokemon(pkm, damage); - }); - leaveEncounterWithoutBattle(scene); - return true; - }) + allowedPokemon.forEach((pkm) => { + const percentage = DAMAGE_PERCENTAGE / 100; + const damage = Math.floor(pkm.getMaxHp() * percentage); + return applyDamageToPokemon(pkm, damage); + }); + leaveEncounterWithoutBattle(scene); + return true; + } + ) .build(); /** @@ -128,7 +172,10 @@ function findPokemonByType(party: PlayerPokemon[], type: Type) { * @param scene Battle scene * @param guidePokemon pokemon choosen as a guide */ -function handleGuidingOption(scene: BattleScene, guidePokemon: PlayerPokemon) { +function handleGuidingOptionPhase( + scene: BattleScene, + guidePokemon: PlayerPokemon +) { /** Base EXP value for guiding pokemon. Currently Lapras base-value */ const baseExpValue: number = 187; From 28d14ae45acd8aa2245be1e159760d9227553d2a Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Fri, 12 Jul 2024 11:37:58 -0700 Subject: [PATCH 14/69] minor refactor of abstract `EncounterPokemonRequirement` --- .../mystery-encounter-requirements.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/data/mystery-encounters/mystery-encounter-requirements.ts b/src/data/mystery-encounters/mystery-encounter-requirements.ts index f31b37118ee..c00a31fa872 100644 --- a/src/data/mystery-encounters/mystery-encounter-requirements.ts +++ b/src/data/mystery-encounters/mystery-encounter-requirements.ts @@ -31,12 +31,10 @@ export abstract class EncounterSceneRequirement implements EncounterRequirement } export abstract class EncounterPokemonRequirement implements EncounterRequirement { - minNumberOfPokemon: number; - invertQuery: boolean; + protected minNumberOfPokemon: number; + protected invertQuery: boolean; - meetsRequirement(scene: BattleScene): boolean { - throw new Error("Method not implemented."); - } + abstract meetsRequirement(scene: BattleScene): boolean; // Returns all party members that are compatible with this requirement. For non pokemon related requirements, the entire party is returned.. queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] { @@ -954,3 +952,5 @@ export class WeightRequirement extends EncounterPokemonRequirement { return ["weight", pokemon.getWeight().toString()]; } } + + From d8fce8b245a2bd51830157d5c72677fae619c4de Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Fri, 12 Jul 2024 12:56:51 -0700 Subject: [PATCH 15/69] add `Pokemon.getEggMoves()` --- src/field/pokemon.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 08119931084..e32b5303dc7 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1319,6 +1319,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return ret; } + /** + * Get a list of all egg moves + * + * @returns list of egg moves + */ + getEggMoves() : Moves[] { + return speciesEggMoves[this.species.speciesId]; + } + setMove(moveIndex: integer, moveId: Moves): void { const move = moveId ? new PokemonMove(moveId) : null; this.moveset[moveIndex] = move; From 72c76c4d39732ce5d90b1e8d3685d028fa4cfaf6 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Fri, 12 Jul 2024 13:10:53 -0700 Subject: [PATCH 16/69] add `Pokemon.isAllowed()` to check if pokemon is allowed even if it's fainted --- src/field/pokemon.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index e32b5303dc7..279ffeccc42 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -276,7 +276,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { isAllowedInBattle(): boolean { const challengeAllowed = new Utils.BooleanHolder(true); applyChallenges(this.scene.gameMode, ChallengeType.POKEMON_IN_BATTLE, this, challengeAllowed); - return !this.isFainted() && challengeAllowed.value; + return !this.isFainted() && this.isAllowed(); + } + + /** + * Check if this pokemon is allowed (no challenge exclusion) + * This is frequently a better alternative to {@link isFainted} + * @returns {boolean} True if pokemon is allowed in battle + */ + isAllowed(): boolean { + const challengeAllowed = new Utils.BooleanHolder(true); + applyChallenges(this.scene.gameMode, ChallengeType.POKEMON_IN_BATTLE, this, challengeAllowed); + return challengeAllowed.value; } isActive(onField?: boolean): boolean { From 7164b0afe4985ffdddf364cc265ad813f5016c1f Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Fri, 12 Jul 2024 13:23:48 -0700 Subject: [PATCH 17/69] add` CanLearnMoveRequirement` --- .../mystery-encounter-requirements.ts | 4 +- .../can-learn-move-requirement.ts | 113 ++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 src/data/mystery-encounters/requirements/can-learn-move-requirement.ts diff --git a/src/data/mystery-encounters/mystery-encounter-requirements.ts b/src/data/mystery-encounters/mystery-encounter-requirements.ts index c00a31fa872..3ce57b5a558 100644 --- a/src/data/mystery-encounters/mystery-encounter-requirements.ts +++ b/src/data/mystery-encounters/mystery-encounter-requirements.ts @@ -31,8 +31,8 @@ export abstract class EncounterSceneRequirement implements EncounterRequirement } export abstract class EncounterPokemonRequirement implements EncounterRequirement { - protected minNumberOfPokemon: number; - protected invertQuery: boolean; + public minNumberOfPokemon: number; + public invertQuery: boolean; abstract meetsRequirement(scene: BattleScene): boolean; diff --git a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts new file mode 100644 index 00000000000..c881602b4f8 --- /dev/null +++ b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts @@ -0,0 +1,113 @@ +import BattleScene from "#app/battle-scene.js"; +import { Moves } from "#app/enums/moves.js"; +import { PlayerPokemon } from "#app/field/pokemon.js"; +import { isNullOrUndefined } from "#app/utils.js"; +import { EncounterPokemonRequirement } from "../mystery-encounter-requirements"; + +/** + * {@linkcode CanLearnMoveRequirement} options + */ +interface Options { + excludeLevelMoves?: boolean; + excludeTmMoves?: boolean; + excludeEggMoves?: boolean; + includeFainted?: boolean; + minNumberOfPokemon?: number; + invertQuery?: boolean; +} + +/** + * Requires that a pokemon can learn a specific move/moveset. + */ +export class CanLearnMoveRequirement extends EncounterPokemonRequirement { + private readonly requiredMoves: Moves[]; + private readonly excludeLevelMoves?: boolean; + private readonly excludeTmMoves?: boolean; + private readonly excludeEggMoves?: boolean; + private readonly includeFainted?: boolean; + + constructor(requiredMoves: Moves | Moves[], options: Options = {}) { + super(); + this.requiredMoves = Array.isArray(requiredMoves) + ? requiredMoves + : [requiredMoves]; + + const { + excludeLevelMoves, + excludeTmMoves, + excludeEggMoves, + includeFainted, + minNumberOfPokemon, + invertQuery, + } = options; + + this.excludeLevelMoves = excludeLevelMoves ?? false; + this.excludeTmMoves = excludeTmMoves ?? false; + this.excludeEggMoves = excludeEggMoves ?? false; + this.includeFainted = includeFainted ?? false; + this.minNumberOfPokemon = minNumberOfPokemon ?? 1; + this.invertQuery = invertQuery; + } + + override meetsRequirement(scene: BattleScene): boolean { + const partyPokemon = scene + .getParty() + .filter((pkm) => + this.includeFainted ? pkm.isAllowed() : pkm.isAllowedInBattle() + ); + + if (isNullOrUndefined(partyPokemon) || this?.requiredMoves?.length < 0) { + return false; + } + + return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon; + } + + override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] { + if (!this.invertQuery) { + return partyPokemon.filter((pokemon) => + // every required move should be included + this.requiredMoves.every((requiredMove) => + this.getAllPokemonMoves(pokemon).includes(requiredMove) + ) + ); + } else { + return partyPokemon.filter( + (pokemon) => + // none of the "required" moves should be included + !this.requiredMoves.some((requiredMove) => + this.getAllPokemonMoves(pokemon).includes(requiredMove) + ) + ); + } + } + + override getDialogueToken( + _scene: BattleScene, + _pokemon?: PlayerPokemon + ): [string, string] { + return ["requiredMoves", this.requiredMoves.join(", ")]; + } + + private getPokemonLevelMoves(pkm: PlayerPokemon): Moves[] { + return pkm.getLevelMoves().map(([_level, move]) => move); + } + + private getAllPokemonMoves(pkm: PlayerPokemon): Moves[] { + const allPokemonMoves: Moves[] = []; + + if (!this.excludeLevelMoves) { + allPokemonMoves.push(...this.getPokemonLevelMoves(pkm)); + } + + if (this.excludeTmMoves) { + allPokemonMoves.push(...pkm.compatibleTms); + } + + if (this.excludeEggMoves) { + allPokemonMoves.push(...pkm.getEggMoves()); + } + + return allPokemonMoves; + } +} From c9cbdd7d4467c4245a9d2fc8ecd625ec741c3d21 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Fri, 12 Jul 2024 14:07:33 -0700 Subject: [PATCH 18/69] lost-at-sea encounter - further progress --- .../encounters/lost-at-sea-encounter.ts | 241 +++++++----------- .../mystery-encounter-option.ts | 16 +- .../mystery-encounters/mystery-encounters.ts | 17 +- .../can-learn-move-requirement.ts | 38 +-- .../en/mystery-encounters/lost-at-sea.ts | 6 +- 5 files changed, 134 insertions(+), 184 deletions(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index b4525176cf1..f3af2e79c0e 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -1,21 +1,12 @@ import { Type } from "#app/data/type.js"; +import { Moves } from "#app/enums/moves.js"; import { Species } from "#app/enums/species.js"; import { PlayerPokemon } from "#app/field/pokemon.js"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; -import MysteryEncounter, { - MysteryEncounterBuilder, - MysteryEncounterTier, -} from "../mystery-encounter"; -import { - EncounterOptionMode, - MysteryEncounterOptionBuilder, -} from "../mystery-encounter-option"; -import { - applyDamageToPokemon, - leaveEncounterWithoutBattle, - setEncounterExp, -} from "../mystery-encounter-utils"; +import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; +import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import { applyDamageToPokemon, leaveEncounterWithoutBattle, setEncounterExp } from "../mystery-encounter-utils"; /** * Damage percentage taken when wandering aimlessly. @@ -26,7 +17,7 @@ const DAMAGE_PERCENTAGE: number = 30; // 0 - 100 /** The i18n namespace for the encounter */ const namepsace = "mysteryEncounter:lostAtSea"; -let waterPkm: PlayerPokemon; +let surfablePkm: PlayerPokemon; let flyingPkm: PlayerPokemon; /** @@ -34,137 +25,108 @@ let flyingPkm: PlayerPokemon; * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9 | GitHub Issue #9} * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) */ -export const LostAtSeaEncounter: MysteryEncounter = - MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.LOST_AT_SEA) - .withEncounterTier(MysteryEncounterTier.COMMON) - .withSceneWaveRangeRequirement(11, 179) - .withIntroSpriteConfigs([ - { - fileRoot: "pokemon", - spriteKey: `${Species.GYARADOS}`, - hasShadow: false, - scale: 4, - y: 100, - x: 130, - tint: 0.75, - alpha: 0.25, - }, - ]) - .withIntroDialogue([ - { - text: `${namepsace}:intro`, - }, - ]) - .withOnInit((scene: BattleScene) => { - const allowedPokemon = scene - .getParty() - .filter((p) => p.isAllowedInBattle()); - const { mysteryEncounter } = scene.currentBattle; +export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.LOST_AT_SEA) + .withEncounterTier(MysteryEncounterTier.COMMON) + .withSceneWaveRangeRequirement(11, 179) + .withIntroSpriteConfigs([ + { + fileRoot: "pokemon", + spriteKey: `${Species.GYARADOS}`, + hasShadow: false, + scale: 4, + y: 100, + x: 130, + tint: 0.75, + alpha: 0.25, + }, + ]) + .withIntroDialogue([{ text: `${namepsace}:intro` }]) + .withOnInit((scene: BattleScene) => { + // const allowedPokemon = scene.getParty().filter((p) => p.isAllowedInBattle()); + const { mysteryEncounter } = scene.currentBattle; - mysteryEncounter.setDialogueToken( - "damagePercentage", - String(DAMAGE_PERCENTAGE) - ); + mysteryEncounter.setDialogueToken("damagePercentage", String(DAMAGE_PERCENTAGE)); - // check for water pokemon - waterPkm = findPokemonByType(allowedPokemon, Type.WATER); - mysteryEncounter.setDialogueToken("waterPkm", waterPkm?.name ?? ""); + // check for water pokemon + // surfablePkm = findPokemonThatCanLearnMove(allowedPokemon, Type.WATER); + // mysteryEncounter.setDialogueToken("waterPkm", surfablePkm?.name ?? ""); - // check for flying pokemon - flyingPkm = findPokemonByType(allowedPokemon, Type.FLYING); - mysteryEncounter.setDialogueToken( - "flyingPkm", - flyingPkm?.name ?? "" - ); + // check for flying pokemon + // flyingPkm = findPokemonThatCanLearnMove(allowedPokemon, Type.FLYING); + // mysteryEncounter.setDialogueToken("flyingPkm", flyingPkm?.name ?? ""); - return true; - }) - .withTitle(`${namepsace}:title`) - .withDescription(`${namepsace}:description`) - .withQuery(`${namepsace}:query`) - .withOption( - /** - * Option 1: Use a (non fainted) water pokemon to guide you back. - * Receives EXP similar to defeating a Lapras - */ - new MysteryEncounterOptionBuilder() - .withPokemonTypeRequirement(Type.WATER, true, 1) - .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) - .withDialogue({ - buttonLabel: `${namepsace}:option:1:label`, - buttonTooltip: `${namepsace}:option:1:tooltip`, - selected: [ - { - text: `${namepsace}:option:1:selected`, - }, - ], - }) - .withOptionPhase(async (scene: BattleScene) => - handleGuidingOptionPhase(scene, waterPkm) - ) - .build() - ) - .withOption( - /** - * Option 2: Use a (non fainted) flying pokemon to guide you back. - * Receives EXP similar to defeating a Lapras - */ - new MysteryEncounterOptionBuilder() - .withPokemonTypeRequirement(Type.FLYING, true, 1) - .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) - .withDialogue({ - buttonLabel: `${namepsace}:option:2:label`, - buttonTooltip: `${namepsace}:option:2:tooltip`, - selected: [ - { - text: `${namepsace}:option:2:selected`, - }, - ], - }) - .withOptionPhase(async (scene: BattleScene) => - handleGuidingOptionPhase(scene, flyingPkm) - ) - .build() - ) - .withSimpleOption( - /** - * Option 3: Wander aimlessly. All pokemons lose {@linkcode DAMAGE_PERCENTAGE}}% of their HP (or KO on 0 HP). - */ - { - buttonLabel: `${namepsace}:option:3:label`, - buttonTooltip: `${namepsace}:option:3:tooltip`, + return true; + }) + .withTitle(`${namepsace}:title`) + .withDescription(`${namepsace}:description`) + .withQuery(`${namepsace}:query`) + .withOption( + /** + * Option 1: Use a (non fainted) water pokemon to guide you back. + * Receives EXP similar to defeating a Lapras + */ + new MysteryEncounterOptionBuilder() + .withPokemonCanLearnMoveRequirement(Moves.SURF) + .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) + .withDialogue({ + buttonLabel: `${namepsace}:option:1:label`, + buttonTooltip: `${namepsace}:option:1:tooltip`, selected: [ { - text: `${namepsace}:option:3:selected`, + text: `${namepsace}:option:1:selected`, }, ], - }, - async (scene: BattleScene) => { - const allowedPokemon = scene - .getParty() - .filter((p) => p.isAllowedInBattle()); + }) + .withOptionPhase(async (scene: BattleScene) => handleGuidingOptionPhase(scene, surfablePkm)) + .build() + ) + .withOption( + /** + * Option 2: Use a (non fainted) flying pokemon to guide you back. + * Receives EXP similar to defeating a Lapras + */ + new MysteryEncounterOptionBuilder() + .withPokemonTypeRequirement(Type.FLYING, true, 1) + .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) + .withDialogue({ + buttonLabel: `${namepsace}:option:2:label`, + buttonTooltip: `${namepsace}:option:2:tooltip`, + selected: [ + { + text: `${namepsace}:option:2:selected`, + }, + ], + }) + .withOptionPhase(async (scene: BattleScene) => handleGuidingOptionPhase(scene, flyingPkm)) + .build() + ) + .withSimpleOption( + /** + * Option 3: Wander aimlessly. All pokemons lose {@linkcode DAMAGE_PERCENTAGE}}% of their HP (or KO on 0 HP). + */ + { + buttonLabel: `${namepsace}:option:3:label`, + buttonTooltip: `${namepsace}:option:3:tooltip`, + selected: [ + { + text: `${namepsace}:option:3:selected`, + }, + ], + }, + async (scene: BattleScene) => { + const allowedPokemon = scene.getParty().filter((p) => p.isAllowedInBattle()); - allowedPokemon.forEach((pkm) => { - const percentage = DAMAGE_PERCENTAGE / 100; - const damage = Math.floor(pkm.getMaxHp() * percentage); - return applyDamageToPokemon(pkm, damage); - }); - leaveEncounterWithoutBattle(scene); - return true; - } - ) - .build(); + allowedPokemon.forEach((pkm) => { + const percentage = DAMAGE_PERCENTAGE / 100; + const damage = Math.floor(pkm.getMaxHp() * percentage); + return applyDamageToPokemon(pkm, damage); + }); + leaveEncounterWithoutBattle(scene); -/** - * Find a pokemon inside the given party by a given type - * - * @param party player pokemon party - * @param type type to search for - * @returns - */ -function findPokemonByType(party: PlayerPokemon[], type: Type) { - return party.find((p) => p.getTypes(true).includes(type)); -} + return true; + } + ) + .build(); /** * Generic handler for using a guiding pokemon to guide you back. @@ -172,19 +134,14 @@ function findPokemonByType(party: PlayerPokemon[], type: Type) { * @param scene Battle scene * @param guidePokemon pokemon choosen as a guide */ -function handleGuidingOptionPhase( - scene: BattleScene, - guidePokemon: PlayerPokemon -) { +function handleGuidingOptionPhase(scene: BattleScene, guidePokemon: PlayerPokemon) { /** Base EXP value for guiding pokemon. Currently Lapras base-value */ const baseExpValue: number = 187; if (guidePokemon) { setEncounterExp(scene, guidePokemon.id, baseExpValue, true); } else { - console.warn( - "Lost at sea: No guide pokemon found but pokemon guides player. huh!?" - ); + console.warn("Lost at sea: No guide pokemon found but pokemon guides player. huh!?"); } leaveEncounterWithoutBattle(scene); diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index 1dc62a5d2e5..7bbde3f3830 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -1,9 +1,11 @@ import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue"; +import { Moves } from "#app/enums/moves.js"; import { PlayerPokemon } from "#app/field/pokemon"; import BattleScene from "../../battle-scene"; import * as Utils from "../../utils"; import { Type } from "../type"; import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "./mystery-encounter-requirements"; +import { CanLearnMoveRequirement, CanlearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement"; export enum EncounterOptionMode { /** Default style */ @@ -194,8 +196,18 @@ export class MysteryEncounterOptionBuilder implements Partial> { diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index 67c95917a33..d489c51e178 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -1,15 +1,16 @@ -import IMysteryEncounter from "./mystery-encounter"; +import { Biome } from "#enums/biome"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { DarkDealEncounter } from "./encounters/dark-deal-encounter"; +import { DepartmentStoreSaleEncounter } from "./encounters/department-store-sale-encounter"; +import { FieldTripEncounter } from "./encounters/field-trip-encounter"; +import { FightOrFlightEncounter } from "./encounters/fight-or-flight-encounter"; +import { LostAtSeaEncounter } from "./encounters/lost-at-sea-encounter"; import { MysteriousChallengersEncounter } from "./encounters/mysterious-challengers-encounter"; import { MysteriousChestEncounter } from "./encounters/mysterious-chest-encounter"; -import { FightOrFlightEncounter } from "./encounters/fight-or-flight-encounter"; -import { TrainingSessionEncounter } from "./encounters/training-session-encounter"; -import { Biome } from "#enums/biome"; -import { SleepingSnorlaxEncounter } from "./encounters/sleeping-snorlax-encounter"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { DepartmentStoreSaleEncounter } from "./encounters/department-store-sale-encounter"; import { ShadyVitaminDealerEncounter } from "./encounters/shady-vitamin-dealer-encounter"; -import { LostAtSeaEncounter } from "./encounters/lost-at-sea-encounter"; +import { SleepingSnorlaxEncounter } from "./encounters/sleeping-snorlax-encounter"; +import { TrainingSessionEncounter } from "./encounters/training-session-encounter"; +import IMysteryEncounter from "./mystery-encounter"; // Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * ) / 256 export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1; 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 c881602b4f8..ffdd1324177 100644 --- a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts +++ b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts @@ -7,7 +7,7 @@ import { EncounterPokemonRequirement } from "../mystery-encounter-requirements"; /** * {@linkcode CanLearnMoveRequirement} options */ -interface Options { +export interface CanlearnMoveRequirementOptions { excludeLevelMoves?: boolean; excludeTmMoves?: boolean; excludeEggMoves?: boolean; @@ -26,20 +26,11 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement { private readonly excludeEggMoves?: boolean; private readonly includeFainted?: boolean; - constructor(requiredMoves: Moves | Moves[], options: Options = {}) { + constructor(requiredMoves: Moves | Moves[], options: CanlearnMoveRequirementOptions = {}) { super(); - this.requiredMoves = Array.isArray(requiredMoves) - ? requiredMoves - : [requiredMoves]; + this.requiredMoves = Array.isArray(requiredMoves) ? requiredMoves : [requiredMoves]; - const { - excludeLevelMoves, - excludeTmMoves, - excludeEggMoves, - includeFainted, - minNumberOfPokemon, - invertQuery, - } = options; + const { excludeLevelMoves, excludeTmMoves, excludeEggMoves, includeFainted, minNumberOfPokemon, invertQuery } = options; this.excludeLevelMoves = excludeLevelMoves ?? false; this.excludeTmMoves = excludeTmMoves ?? false; @@ -50,11 +41,7 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement { } override meetsRequirement(scene: BattleScene): boolean { - const partyPokemon = scene - .getParty() - .filter((pkm) => - this.includeFainted ? pkm.isAllowed() : pkm.isAllowedInBattle() - ); + const partyPokemon = scene.getParty().filter((pkm) => (this.includeFainted ? pkm.isAllowed() : pkm.isAllowedInBattle())); if (isNullOrUndefined(partyPokemon) || this?.requiredMoves?.length < 0) { return false; @@ -67,26 +54,19 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement { if (!this.invertQuery) { return partyPokemon.filter((pokemon) => // every required move should be included - this.requiredMoves.every((requiredMove) => - this.getAllPokemonMoves(pokemon).includes(requiredMove) - ) + this.requiredMoves.every((requiredMove) => this.getAllPokemonMoves(pokemon).includes(requiredMove)) ); } else { return partyPokemon.filter( (pokemon) => // none of the "required" moves should be included - !this.requiredMoves.some((requiredMove) => - this.getAllPokemonMoves(pokemon).includes(requiredMove) - ) + !this.requiredMoves.some((requiredMove) => this.getAllPokemonMoves(pokemon).includes(requiredMove)) ); } } - override getDialogueToken( - _scene: BattleScene, - _pokemon?: PlayerPokemon - ): [string, string] { - return ["requiredMoves", this.requiredMoves.join(", ")]; + override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { + return pokemon ? ["pokemonCanLearnMove", pokemon.name] : null; } private getPokemonLevelMoves(pkm: PlayerPokemon): Moves[] { diff --git a/src/locales/en/mystery-encounters/lost-at-sea.ts b/src/locales/en/mystery-encounters/lost-at-sea.ts index e4ea07c77ad..6c4664b8a4a 100644 --- a/src/locales/en/mystery-encounters/lost-at-sea.ts +++ b/src/locales/en/mystery-encounters/lost-at-sea.ts @@ -6,10 +6,10 @@ export const lostAtSea = { query: "What will you do?", option: { 1: { - label: "Use @ec{waterPkm}", // pkm has to be of type water + label: "Use @ec{pokemonCanLearnMove}", // pkm has to be of type water tooltip: - "Use @ec{waterPkm} to guide you back. @ec{waterPkm} earns EXP as if having defeated a Lapras.", - selected: "@ec{waterPkm} guides you back and earns EXP.", + "Use @ec{pokemonCanLearnMove} to guide you back. @ec{pokemonCanLearnMove} earns EXP as if having defeated a Lapras.", + selected: "@ec{pokemonCanLearnMove} guides you back and earns EXP.", }, 2: { label: "Use @ec{flyingPkm}", // pkm has to be of type flying From 53f178fde2f37dd9cfada64e507b8a936824f311 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Fri, 12 Jul 2024 14:10:19 -0700 Subject: [PATCH 19/69] replace @ec with i18n variables --- src/locales/en/mystery-encounters/lost-at-sea.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/locales/en/mystery-encounters/lost-at-sea.ts b/src/locales/en/mystery-encounters/lost-at-sea.ts index 6c4664b8a4a..f3485c723af 100644 --- a/src/locales/en/mystery-encounters/lost-at-sea.ts +++ b/src/locales/en/mystery-encounters/lost-at-sea.ts @@ -6,21 +6,21 @@ export const lostAtSea = { query: "What will you do?", option: { 1: { - label: "Use @ec{pokemonCanLearnMove}", // pkm has to be of type water + label: "Use {{pokemonCanLearnMove}}", // pkm has to be of type water tooltip: - "Use @ec{pokemonCanLearnMove} to guide you back. @ec{pokemonCanLearnMove} earns EXP as if having defeated a Lapras.", - selected: "@ec{pokemonCanLearnMove} guides you back and earns EXP.", + "Use {{pokemonCanLearnMove}} to guide you back. {{pokemonCanLearnMove}} earns EXP as if having defeated a Lapras.", + selected: "{{pokemonCanLearnMove}} guides you back and earns EXP.", }, 2: { - label: "Use @ec{flyingPkm}", // pkm has to be of type flying + label: "Use {{flyingPkm}}", // pkm has to be of type flying tooltip: - "Use @ec{flyingPkm} to guide you back. @ec{flyingPkm} earns EXP as if having defeated a Lapras.", - selected: "@ec{flyingPkm} guides you back and earns EXP.", + "Use {{flyingPkm}} to guide you back. {{flyingPkm}} earns EXP as if having defeated a Lapras.", + selected: "{{flyingPkm}} guides you back and earns EXP.", }, 3: { label: "Wander aimlessly", tooltip: - "Wander aimlessly until you're back. All your Pokémon lose @ec{damagePercentage}% of their HP. Any below that are KO'd.", + "Wander aimlessly until you're back. All your Pokémon lose {{damagePercentage}}% of their HP. Any below that are KO'd.", selected: "You wander aimlessly around. After hours of wandering, you find your way back. You and your team take the toll.", }, From 0a212b3f81529678d50f79b747fe5771d24937b1 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Fri, 12 Jul 2024 14:24:37 -0700 Subject: [PATCH 20/69] minor changes to CanLearnMoveRequirement --- .../can-learn-move-requirement.ts | 43 ++++++++++++++----- 1 file changed, 33 insertions(+), 10 deletions(-) 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 ffdd1324177..3d13019dc1c 100644 --- a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts +++ b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts @@ -26,11 +26,23 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement { private readonly excludeEggMoves?: boolean; private readonly includeFainted?: boolean; - constructor(requiredMoves: Moves | Moves[], options: CanlearnMoveRequirementOptions = {}) { + constructor( + requiredMoves: Moves | Moves[], + options: CanlearnMoveRequirementOptions = {} + ) { super(); - this.requiredMoves = Array.isArray(requiredMoves) ? requiredMoves : [requiredMoves]; + this.requiredMoves = Array.isArray(requiredMoves) + ? requiredMoves + : [requiredMoves]; - const { excludeLevelMoves, excludeTmMoves, excludeEggMoves, includeFainted, minNumberOfPokemon, invertQuery } = options; + const { + excludeLevelMoves, + excludeTmMoves, + excludeEggMoves, + includeFainted, + minNumberOfPokemon, + invertQuery, + } = options; this.excludeLevelMoves = excludeLevelMoves ?? false; this.excludeTmMoves = excludeTmMoves ?? false; @@ -41,7 +53,11 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement { } override meetsRequirement(scene: BattleScene): boolean { - const partyPokemon = scene.getParty().filter((pkm) => (this.includeFainted ? pkm.isAllowed() : pkm.isAllowedInBattle())); + const partyPokemon = scene + .getParty() + .filter((pkm) => + this.includeFainted ? pkm.isAllowed() : pkm.isAllowedInBattle() + ); if (isNullOrUndefined(partyPokemon) || this?.requiredMoves?.length < 0) { return false; @@ -54,19 +70,26 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement { if (!this.invertQuery) { return partyPokemon.filter((pokemon) => // every required move should be included - this.requiredMoves.every((requiredMove) => this.getAllPokemonMoves(pokemon).includes(requiredMove)) + this.requiredMoves.every((requiredMove) => + this.getAllPokemonMoves(pokemon).includes(requiredMove) + ) ); } else { return partyPokemon.filter( (pokemon) => // none of the "required" moves should be included - !this.requiredMoves.some((requiredMove) => this.getAllPokemonMoves(pokemon).includes(requiredMove)) + !this.requiredMoves.some((requiredMove) => + this.getAllPokemonMoves(pokemon).includes(requiredMove) + ) ); } } - override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { - return pokemon ? ["pokemonCanLearnMove", pokemon.name] : null; + override getDialogueToken( + _scene: BattleScene, + _pokemon?: PlayerPokemon + ): [string, string] { + return ["requiredMoves", this.requiredMoves.join(", ")]; } private getPokemonLevelMoves(pkm: PlayerPokemon): Moves[] { @@ -80,11 +103,11 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement { allPokemonMoves.push(...this.getPokemonLevelMoves(pkm)); } - if (this.excludeTmMoves) { + if (!this.excludeTmMoves) { allPokemonMoves.push(...pkm.compatibleTms); } - if (this.excludeEggMoves) { + if (!this.excludeEggMoves) { allPokemonMoves.push(...pkm.getEggMoves()); } From b23664cb9e221ac7ed41b74914aff5ebd128ecde Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Fri, 12 Jul 2024 14:40:47 -0700 Subject: [PATCH 21/69] extend MysteryEncounterOptionBuilder --- .../mystery-encounter-option.ts | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index 7bbde3f3830..43ccd258ce3 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -1,4 +1,4 @@ -import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue"; +import { OptionTextDisplay, TextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue"; import { Moves } from "#app/enums/moves.js"; import { PlayerPokemon } from "#app/field/pokemon"; import BattleScene from "../../battle-scene"; @@ -216,8 +216,48 @@ export class MysteryEncounterOptionBuilder implements Partial Date: Fri, 12 Jul 2024 14:50:32 -0700 Subject: [PATCH 22/69] undo some MysterEncounterOptionBuilder changes --- .../mystery-encounter-option.ts | 36 +------------------ 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index 43ccd258ce3..f6542a19d1a 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -1,4 +1,4 @@ -import { OptionTextDisplay, TextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue"; +import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue"; import { Moves } from "#app/enums/moves.js"; import { PlayerPokemon } from "#app/field/pokemon"; import BattleScene from "../../battle-scene"; @@ -226,38 +226,4 @@ export class MysteryEncounterOptionBuilder implements Partial Date: Fri, 12 Jul 2024 14:55:52 -0700 Subject: [PATCH 23/69] apply surf & fly condition (not water & flying) --- .../encounters/lost-at-sea-encounter.ts | 17 +++++++++---- .../en/mystery-encounters/lost-at-sea.ts | 24 +++++++++---------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index f3af2e79c0e..e3e3ff5bc16 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -1,4 +1,3 @@ -import { Type } from "#app/data/type.js"; import { Moves } from "#app/enums/moves.js"; import { Species } from "#app/enums/species.js"; import { PlayerPokemon } from "#app/field/pokemon.js"; @@ -8,6 +7,8 @@ import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { applyDamageToPokemon, leaveEncounterWithoutBattle, setEncounterExp } from "../mystery-encounter-utils"; +const OPTION_1_REQUIRED_MOVE = Moves.SURF; +const OPTION_2_REQUIRED_MOVE = Moves.FLY; /** * Damage percentage taken when wandering aimlessly. * Can be a number between `0` - `100`. @@ -46,6 +47,8 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with const { mysteryEncounter } = scene.currentBattle; mysteryEncounter.setDialogueToken("damagePercentage", String(DAMAGE_PERCENTAGE)); + mysteryEncounter.setDialogueToken("option1RequiredMove", Moves[OPTION_1_REQUIRED_MOVE]); + mysteryEncounter.setDialogueToken("option2RequiredMove", Moves[OPTION_2_REQUIRED_MOVE]); // check for water pokemon // surfablePkm = findPokemonThatCanLearnMove(allowedPokemon, Type.WATER); @@ -62,15 +65,17 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with .withQuery(`${namepsace}:query`) .withOption( /** - * Option 1: Use a (non fainted) water pokemon to guide you back. + * Option 1: Use a (non fainted) pokemon that can learn Surf to guide you back. * Receives EXP similar to defeating a Lapras */ new MysteryEncounterOptionBuilder() - .withPokemonCanLearnMoveRequirement(Moves.SURF) + .withPokemonCanLearnMoveRequirement(OPTION_1_REQUIRED_MOVE) .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) .withDialogue({ buttonLabel: `${namepsace}:option:1:label`, + disabledButtonLabel: `${namepsace}:option:1:label_disabled`, buttonTooltip: `${namepsace}:option:1:tooltip`, + disabledButtonTooltip: `${namepsace}:option:1:tooltip_disabled`, selected: [ { text: `${namepsace}:option:1:selected`, @@ -82,15 +87,17 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with ) .withOption( /** - * Option 2: Use a (non fainted) flying pokemon to guide you back. + * Option 2: Use a (non fainted) pokemon that can learn fly to guide you back. * Receives EXP similar to defeating a Lapras */ new MysteryEncounterOptionBuilder() - .withPokemonTypeRequirement(Type.FLYING, true, 1) + .withPokemonCanLearnMoveRequirement(OPTION_2_REQUIRED_MOVE) .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) .withDialogue({ buttonLabel: `${namepsace}:option:2:label`, + disabledButtonLabel: `${namepsace}:option:2:label_disabled`, buttonTooltip: `${namepsace}:option:2:tooltip`, + disabledButtonTooltip: `${namepsace}:option:2:tooltip_disabled`, selected: [ { text: `${namepsace}:option:2:selected`, diff --git a/src/locales/en/mystery-encounters/lost-at-sea.ts b/src/locales/en/mystery-encounters/lost-at-sea.ts index f3485c723af..d5413a48ffc 100644 --- a/src/locales/en/mystery-encounters/lost-at-sea.ts +++ b/src/locales/en/mystery-encounters/lost-at-sea.ts @@ -6,23 +6,23 @@ export const lostAtSea = { query: "What will you do?", option: { 1: { - label: "Use {{pokemonCanLearnMove}}", // pkm has to be of type water - tooltip: - "Use {{pokemonCanLearnMove}} to guide you back. {{pokemonCanLearnMove}} earns EXP as if having defeated a Lapras.", - selected: "{{pokemonCanLearnMove}} guides you back and earns EXP.", + label: "Use {{option1PrimaryName}}", + label_disabled: "Can't {{option1RequiredMove}}", + tooltip: "Use {{option1PrimaryName}} to guide you back. {{option1PrimaryName}} earns EXP as if having defeated a Lapras.", + tooltip_disabled: "You have no Pokémon that could learn {{option1RequiredMove}}", + selected: "{{option1PrimaryName}} guides you back and earns EXP.", }, 2: { - label: "Use {{flyingPkm}}", // pkm has to be of type flying - tooltip: - "Use {{flyingPkm}} to guide you back. {{flyingPkm}} earns EXP as if having defeated a Lapras.", - selected: "{{flyingPkm}} guides you back and earns EXP.", + label: "Use {{option2PrimaryName}}", + label_disabled: "Can't {{option2RequiredMove}}", + tooltip: "Use {{option2PrimaryName}} to guide you back. {{option2PrimaryName}} earns EXP as if having defeated a Lapras.", + tooltip_disabled: "You have no Pokémon that could learn {{option2RequiredMove}}", + selected: "{{option2PrimaryName}} guides you back and earns EXP.", }, 3: { label: "Wander aimlessly", - tooltip: - "Wander aimlessly until you're back. All your Pokémon lose {{damagePercentage}}% of their HP. Any below that are KO'd.", - selected: - "You wander aimlessly around. After hours of wandering, you find your way back. You and your team take the toll.", + tooltip: "Wander aimlessly until you're back. All your Pokémon lose {{damagePercentage}}% of their HP. Any below that are KO'd.", + selected: "You wander aimlessly around. After hours of wandering, you find your way back. You and your team take the toll.", }, }, }; From 9208fd226b53742a3873a1edc742d4909f759cc6 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Sun, 14 Jul 2024 21:31:43 -0700 Subject: [PATCH 24/69] RESET OVERRIDES.TS!! --- .../encounters/lost-at-sea-encounter.ts | 15 +++------------ src/overrides.ts | 8 ++++---- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index e3e3ff5bc16..340bfdd4281 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -43,21 +43,12 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with ]) .withIntroDialogue([{ text: `${namepsace}:intro` }]) .withOnInit((scene: BattleScene) => { - // const allowedPokemon = scene.getParty().filter((p) => p.isAllowedInBattle()); const { mysteryEncounter } = scene.currentBattle; mysteryEncounter.setDialogueToken("damagePercentage", String(DAMAGE_PERCENTAGE)); mysteryEncounter.setDialogueToken("option1RequiredMove", Moves[OPTION_1_REQUIRED_MOVE]); mysteryEncounter.setDialogueToken("option2RequiredMove", Moves[OPTION_2_REQUIRED_MOVE]); - // check for water pokemon - // surfablePkm = findPokemonThatCanLearnMove(allowedPokemon, Type.WATER); - // mysteryEncounter.setDialogueToken("waterPkm", surfablePkm?.name ?? ""); - - // check for flying pokemon - // flyingPkm = findPokemonThatCanLearnMove(allowedPokemon, Type.FLYING); - // mysteryEncounter.setDialogueToken("flyingPkm", flyingPkm?.name ?? ""); - return true; }) .withTitle(`${namepsace}:title`) @@ -82,7 +73,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with }, ], }) - .withOptionPhase(async (scene: BattleScene) => handleGuidingOptionPhase(scene, surfablePkm)) + .withOptionPhase(async (scene: BattleScene) => handlePokemongGuidingYouPhase(scene, surfablePkm)) .build() ) .withOption( @@ -104,7 +95,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with }, ], }) - .withOptionPhase(async (scene: BattleScene) => handleGuidingOptionPhase(scene, flyingPkm)) + .withOptionPhase(async (scene: BattleScene) => handlePokemongGuidingYouPhase(scene, flyingPkm)) .build() ) .withSimpleOption( @@ -141,7 +132,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with * @param scene Battle scene * @param guidePokemon pokemon choosen as a guide */ -function handleGuidingOptionPhase(scene: BattleScene, guidePokemon: PlayerPokemon) { +function handlePokemongGuidingYouPhase(scene: BattleScene, guidePokemon: PlayerPokemon) { /** Base EXP value for guiding pokemon. Currently Lapras base-value */ const baseExpValue: number = 187; diff --git a/src/overrides.ts b/src/overrides.ts index 7a9262ab73a..e19a5bf20dd 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -34,8 +34,8 @@ export const SEED_OVERRIDE: string = ""; export const WEATHER_OVERRIDE: WeatherType = WeatherType.NONE; export const DOUBLE_BATTLE_OVERRIDE: boolean = false; export const SINGLE_BATTLE_OVERRIDE: boolean = false; -export const STARTING_WAVE_OVERRIDE: integer = 15; -export const STARTING_BIOME_OVERRIDE: Biome = Biome.SEA; +export const STARTING_WAVE_OVERRIDE: integer = 0; +export const STARTING_BIOME_OVERRIDE: Biome = Biome.TOWN; export const ARENA_TINT_OVERRIDE: TimeOfDay = null; // Multiplies XP gained by this value including 0. Set to null to ignore the override export const XP_MULTIPLIER_OVERRIDE: number = null; @@ -117,9 +117,9 @@ export const EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0; */ // 1 to 256, set to null to ignore -export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = 100000; +export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = null; export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null; -export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.LOST_AT_SEA; +export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null; /** * MODIFIER / ITEM OVERRIDES From e2c431b913acc7f32790fee3ab55b23c868219c8 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Sun, 14 Jul 2024 21:32:22 -0700 Subject: [PATCH 25/69] polish lost at sea encounter - fix guide pokemon not receiving exp --- .../encounters/lost-at-sea-encounter.ts | 16 +++++++--------- .../mystery-encounter-option.ts | 1 - src/data/mystery-encounters/mystery-encounter.ts | 1 - src/locales/en/mystery-encounters/lost-at-sea.ts | 4 ++-- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 340bfdd4281..5e95c9d2b1f 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -1,6 +1,5 @@ import { Moves } from "#app/enums/moves.js"; import { Species } from "#app/enums/species.js"; -import { PlayerPokemon } from "#app/field/pokemon.js"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; @@ -18,9 +17,6 @@ const DAMAGE_PERCENTAGE: number = 30; // 0 - 100 /** The i18n namespace for the encounter */ const namepsace = "mysteryEncounter:lostAtSea"; -let surfablePkm: PlayerPokemon; -let flyingPkm: PlayerPokemon; - /** * Lost at sea encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9 | GitHub Issue #9} @@ -73,7 +69,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with }, ], }) - .withOptionPhase(async (scene: BattleScene) => handlePokemongGuidingYouPhase(scene, surfablePkm)) + .withOptionPhase(async (scene: BattleScene) => handlePokemongGuidingYouPhase(scene)) .build() ) .withOption( @@ -95,7 +91,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with }, ], }) - .withOptionPhase(async (scene: BattleScene) => handlePokemongGuidingYouPhase(scene, flyingPkm)) + .withOptionPhase(async (scene: BattleScene) => handlePokemongGuidingYouPhase(scene)) .build() ) .withSimpleOption( @@ -132,15 +128,17 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with * @param scene Battle scene * @param guidePokemon pokemon choosen as a guide */ -function handlePokemongGuidingYouPhase(scene: BattleScene, guidePokemon: PlayerPokemon) { +function handlePokemongGuidingYouPhase(scene: BattleScene) { /** Base EXP value for guiding pokemon. Currently Lapras base-value */ const baseExpValue: number = 187; + const { mysteryEncounter } = scene.currentBattle; - if (guidePokemon) { - setEncounterExp(scene, guidePokemon.id, baseExpValue, true); + if (mysteryEncounter.selectedOption) { + setEncounterExp(scene, mysteryEncounter.selectedOption.primaryPokemon.id, baseExpValue, true); } else { console.warn("Lost at sea: No guide pokemon found but pokemon guides player. huh!?"); } leaveEncounterWithoutBattle(scene); + return true; } diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index f6542a19d1a..edf08b26da7 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -68,7 +68,6 @@ export default class MysteryEncounterOption implements MysteryEncounterOption { } let qualified: PlayerPokemon[] = scene.getParty(); for (const req of this.primaryPokemonRequirements) { - console.log(req); if (req.meetsRequirement(scene)) { if (req instanceof EncounterPokemonRequirement) { qualified = qualified.filter(pkmn => req.queryParty(scene.getParty()).includes(pkmn)); diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index dfe824cc831..a072c0f80a8 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -188,7 +188,6 @@ export default class IMysteryEncounter implements IMysteryEncounter { } let qualified: PlayerPokemon[] = scene.getParty(); for (const req of this.primaryPokemonRequirements) { - console.log(req); if (req.meetsRequirement(scene)) { if (req instanceof EncounterPokemonRequirement) { qualified = qualified.filter(pkmn => req.queryParty(scene.getParty()).includes(pkmn)); diff --git a/src/locales/en/mystery-encounters/lost-at-sea.ts b/src/locales/en/mystery-encounters/lost-at-sea.ts index d5413a48ffc..35cceaa17a8 100644 --- a/src/locales/en/mystery-encounters/lost-at-sea.ts +++ b/src/locales/en/mystery-encounters/lost-at-sea.ts @@ -9,14 +9,14 @@ export const lostAtSea = { label: "Use {{option1PrimaryName}}", label_disabled: "Can't {{option1RequiredMove}}", tooltip: "Use {{option1PrimaryName}} to guide you back. {{option1PrimaryName}} earns EXP as if having defeated a Lapras.", - tooltip_disabled: "You have no Pokémon that could learn {{option1RequiredMove}}", + tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", selected: "{{option1PrimaryName}} guides you back and earns EXP.", }, 2: { label: "Use {{option2PrimaryName}}", label_disabled: "Can't {{option2RequiredMove}}", tooltip: "Use {{option2PrimaryName}} to guide you back. {{option2PrimaryName}} earns EXP as if having defeated a Lapras.", - tooltip_disabled: "You have no Pokémon that could learn {{option2RequiredMove}}", + tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", selected: "{{option2PrimaryName}} guides you back and earns EXP.", }, 3: { From 6ac54def30a49f10fc31a9deb9ec51e9e9852d96 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Mon, 15 Jul 2024 13:45:25 -0700 Subject: [PATCH 26/69] re-add lost utiltiy methods: - applyHpChangeToPokemon - applyDamageToPokemon - applyHealToPokemon - koPlayerPokemon --- .../encounters/lost-at-sea-encounter.ts | 2 +- .../utils/encounter-phase-utils.ts | 66 ++++++++++++++++++- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 5e95c9d2b1f..8f1d9f3eb63 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -4,7 +4,7 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -import { applyDamageToPokemon, leaveEncounterWithoutBattle, setEncounterExp } from "../mystery-encounter-utils"; +import { applyDamageToPokemon, leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter-phase-utils"; const OPTION_1_REQUIRED_MOVE = Moves.SURF; const OPTION_2_REQUIRED_MOVE = Moves.FLY; diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 100c25aa45d..6fa15ec373d 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -385,7 +385,7 @@ export function setEncounterRewards(scene: BattleScene, customShopRewards?: Cust * Will initialize exp phases into the phase queue (these are in addition to any combat or other exp earned) * Exp Share and Exp Balance will still function as normal * @param scene - Battle Scene - * @param participantIds - ids of party pokemon that get full exp value. Other party members will receive Exp Share amounts + * @param participantId - id/s of party pokemon that get full exp value. Other party members will receive Exp Share amounts * @param baseExpValue - gives exp equivalent to a pokemon of the wave index's level. * Guidelines: * 36 - Sunkern (lowest in game) @@ -399,7 +399,9 @@ export function setEncounterRewards(scene: BattleScene, customShopRewards?: Cust * https://bulbapedia.bulbagarden.net/wiki/List_of_Pok%C3%A9mon_by_effort_value_yield_(Generation_IX) * @param useWaveIndex - set to false when directly passing the the full exp value instead of baseExpValue */ -export function setEncounterExp(scene: BattleScene, participantIds: integer[], baseExpValue: number, useWaveIndex: boolean = true) { +export function setEncounterExp(scene: BattleScene, participantId: integer | integer[], baseExpValue: number, useWaveIndex: boolean = true) { + const participantIds = Array.isArray(participantId) ? participantId : [participantId]; + scene.currentBattle.mysteryEncounter.doEncounterExp = (scene: BattleScene) => { const party = scene.getParty(); const expShareModifier = scene.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier; @@ -673,3 +675,63 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n console.log(`Starting weight: ${baseSpawnWeight}\nAverage MEs per run: ${totalMean}\nStandard Deviation: ${totalStd}\nAvg Commons: ${commonMean}\nAvg Uncommons: ${uncommonMean}\nAvg Rares: ${rareMean}\nAvg Super Rares: ${superRareMean}`); } + +/** + * Takes care of handling player pokemon KO (with all its side effects) + * + * @param pokemon the player pokemon to KO + */ +export function koPlayerPokemon(pokemon: PlayerPokemon) { + pokemon.hp = 0; + pokemon.trySetStatus(StatusEffect.FAINT); + pokemon.updateInfo(); +} + +/** + * Handles applying hp changes to a player pokemon. + * Takes care of not going below `0`, above max-hp, adding `FNT` status correctly and updating the pokemon info. + * TODO: handle special cases like wonder-guard/ninjask + * + * @param pokemon the player pokemon to apply the hp change to + * @param damage the hp change amount. Positive for heal. Negative for damage + * + */ +function applyHpChangeToPokemon(pokemon: PlayerPokemon, value: number) { + const hpChange = Math.round(pokemon.hp + value); + const nextHp = Math.max(Math.min(hpChange, pokemon.getMaxHp()), 0); + if (nextHp === 0) { + koPlayerPokemon(pokemon); + } else { + pokemon.hp = nextHp; + } +} + +/** + * Handles applying damage to a player pokemon + * + * @param pokemon the player pokemon to apply damage to + * @param damage the amount of damage to apply + * @see {@linkcode applyHpChangeToPokemon} + */ +export function applyDamageToPokemon(pokemon: PlayerPokemon, damage: number) { + if (damage <= 0) { + console.warn("Healing pokemon with `applyDamageToPokemon` is not recommended! Please use `applyHealToPokemon` instead."); + } + + applyHpChangeToPokemon(pokemon, -damage); +} + +/** + * Handles applying heal to a player pokemon + * + * @param pokemon the player pokemon to apply heal to + * @param heal the amount of heal to apply + * @see {@linkcode applyHpChangeToPokemon} + */ +export function applyHealToPokemon(pokemon: PlayerPokemon, heal: number) { + if (heal <= 0) { + console.warn("Damaging pokemong with `applyHealToPokemon` is not recommended! Please use `applyDamageToPokemon` instead."); + } + + applyHpChangeToPokemon(pokemon, heal); +} From 8cd5f34b0cf6463f746409df14f4213a3df85387 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Mon, 15 Jul 2024 13:59:04 -0700 Subject: [PATCH 27/69] lost at sea enc.: reduce dmg to 25% --- src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 8f1d9f3eb63..093829ca0a9 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -13,7 +13,7 @@ const OPTION_2_REQUIRED_MOVE = Moves.FLY; * Can be a number between `0` - `100`. * The higher the more damage taken (100% = instant KO). */ -const DAMAGE_PERCENTAGE: number = 30; // 0 - 100 +const DAMAGE_PERCENTAGE: number = 25; // 0 - 100 /** The i18n namespace for the encounter */ const namepsace = "mysteryEncounter:lostAtSea"; From d73bf1e81d78219705cfcecdd58f3fd978bd0512 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Mon, 15 Jul 2024 15:16:23 -0700 Subject: [PATCH 28/69] remove ".js" in imports --- .../mystery-encounters/encounters/lost-at-sea-encounter.ts | 4 ++-- src/data/mystery-encounters/mystery-encounter-option.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 093829ca0a9..cbbdbdccf92 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -1,5 +1,5 @@ -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index edf08b26da7..b02cfd351a4 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -1,5 +1,5 @@ import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue"; -import { Moves } from "#app/enums/moves.js"; +import { Moves } from "#app/enums/moves"; import { PlayerPokemon } from "#app/field/pokemon"; import BattleScene from "../../battle-scene"; import * as Utils from "../../utils"; From a7eb972d9ea5d49796370f803d777a4193346cec Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 10:46:07 -0700 Subject: [PATCH 29/69] ui.getHandler() add generic type with default https://www.typescriptlang.org/docs/handbook/2/generics.html#generic-parameter-defaults --- src/ui/mystery-encounter-ui-handler.ts | 2 +- src/ui/ui.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index d1ce9fb5d04..e6699afbfe8 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -135,7 +135,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { // TODO: If we need to handle cancel option? Maybe default logic to leave/run from encounter idk } } else { - switch (this.optionsContainer.length) { + switch (this.optionsContainer.list.length) { case 3: success = this.handleTwoOptionMoveInput(button); break; diff --git a/src/ui/ui.ts b/src/ui/ui.ts index fa7dc9fad9f..aac2779e111 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -234,8 +234,8 @@ export default class UI extends Phaser.GameObjects.Container { (this.scene as BattleScene).uiContainer.add(this.tooltipContainer); } - getHandler(): UiHandler { - return this.handlers[this.mode]; + getHandler(): H { + return this.handlers[this.mode] as H; } getMessageHandler(): BattleMessageUiHandler { From d967ac52b6278070fcf779719c3a834303dbfba0 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 10:46:19 -0700 Subject: [PATCH 30/69] add buoy sprite --- public/images/mystery-encounters/buoy.json | 19 +++++++++++++++++++ public/images/mystery-encounters/buoy.png | Bin 0 -> 1721 bytes 2 files changed, 19 insertions(+) create mode 100644 public/images/mystery-encounters/buoy.json create mode 100644 public/images/mystery-encounters/buoy.png diff --git a/public/images/mystery-encounters/buoy.json b/public/images/mystery-encounters/buoy.json new file mode 100644 index 00000000000..ba5d9567fe5 --- /dev/null +++ b/public/images/mystery-encounters/buoy.json @@ -0,0 +1,19 @@ +{ "frames": [ + { + "filename": "0001.png", + "frame": { "x": 0, "y": 0, "w": 46, "h": 60 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 46, "h": 60 }, + "sourceSize": { "w": 46, "h": 60 } + } + ], + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.7-x64", + "image": "buoy-sheet.png", + "format": "RGBA8888", + "size": { "w": 46, "h": 60 }, + "scale": "1" + } +} diff --git a/public/images/mystery-encounters/buoy.png b/public/images/mystery-encounters/buoy.png new file mode 100644 index 0000000000000000000000000000000000000000..dee7194365054238ce1e082ab53edaaaa5b192fc GIT binary patch literal 1721 zcmV;q21fabP)Px*SxH1eRA_HTyfr1&z9$aA$ zu6@xFp%8u2&QP|s4};Q&p>4%(VJ(=Kt?1TBkq&00q(zZ3RFqPk#;sTwoINDx^xT`9 z#?{B_%>M&m~$}b&z=q|4pebtpzl?v9j z9RR{izTk-Kqyef*1$|V4iTt4h;+GMUbh^(_V8o@jt#I?jQ^V(iA`aHxUE6lhU0!cE zS8&9wFtWOy5?Kn4cruC@KAU9%1r}A3UKLkMD_Glh5R32PU3vtN62}V?A!myKB3RaR86Ud(ag24v)ucOCATlIPDTG7U2bhb9qTvx;FvlRfs;|ZNFZfvS^03l}!fXoWh zBIla&^#p{w!%Jstqv5*z66T`WjGRRkJ(|b}qDPibD;iSfv>!Xp*^`F>D4m|p$S&xUr#_tc?1T&QRn5RBW3bn`E|+x*<}Jb^!V7!Jk51A+`cwo(6+uP!Aj$TSbR6@ z{c}bx3vlF*M~j9mdDLowH0KjlWhkeWsw?VbiFvs z{fJ6r1d z?6Vbu{(gFghtvTu_i$2O(~Xh_*vSI-Mf}e7LC5)XUCL{`>qYyq<4o>ssfyfOg_lfC89GxE?hdaqLPC05Ry&%<72ZmA zsQ;s3WOd!pPLKwaOidZ)SQ#Oasoe&s$|xK8jKnSesl&+Xy842Kk|P}Y>3hJ~bxE;n2xSY_2W=@P5&P;rt());g4)m82y`BT;p4S+04P9vQ|YlPr0SVz=LL z@3LyA<#!fIYjgwT;Gb9D46KdMlMdM`o^&;)4xMy5vbyIjb(HBTSm9N8IHMjz&K6 Date: Tue, 16 Jul 2024 10:46:39 -0700 Subject: [PATCH 31/69] chore: format can-learn-move-requirement.ts --- .../can-learn-move-requirement.ts | 45 +++++-------------- 1 file changed, 11 insertions(+), 34 deletions(-) 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 3d13019dc1c..c9d54d54d49 100644 --- a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts +++ b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts @@ -1,7 +1,7 @@ -import BattleScene from "#app/battle-scene.js"; -import { Moves } from "#app/enums/moves.js"; -import { PlayerPokemon } from "#app/field/pokemon.js"; -import { isNullOrUndefined } from "#app/utils.js"; +import BattleScene from "#app/battle-scene"; +import { Moves } from "#app/enums/moves"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { isNullOrUndefined } from "#app/utils"; import { EncounterPokemonRequirement } from "../mystery-encounter-requirements"; /** @@ -26,23 +26,11 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement { private readonly excludeEggMoves?: boolean; private readonly includeFainted?: boolean; - constructor( - requiredMoves: Moves | Moves[], - options: CanlearnMoveRequirementOptions = {} - ) { + constructor(requiredMoves: Moves | Moves[], options: CanlearnMoveRequirementOptions = {}) { super(); - this.requiredMoves = Array.isArray(requiredMoves) - ? requiredMoves - : [requiredMoves]; + this.requiredMoves = Array.isArray(requiredMoves) ? requiredMoves : [requiredMoves]; - const { - excludeLevelMoves, - excludeTmMoves, - excludeEggMoves, - includeFainted, - minNumberOfPokemon, - invertQuery, - } = options; + const { excludeLevelMoves, excludeTmMoves, excludeEggMoves, includeFainted, minNumberOfPokemon, invertQuery } = options; this.excludeLevelMoves = excludeLevelMoves ?? false; this.excludeTmMoves = excludeTmMoves ?? false; @@ -53,11 +41,7 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement { } override meetsRequirement(scene: BattleScene): boolean { - const partyPokemon = scene - .getParty() - .filter((pkm) => - this.includeFainted ? pkm.isAllowed() : pkm.isAllowedInBattle() - ); + const partyPokemon = scene.getParty().filter((pkm) => (this.includeFainted ? pkm.isAllowed() : pkm.isAllowedInBattle())); if (isNullOrUndefined(partyPokemon) || this?.requiredMoves?.length < 0) { return false; @@ -70,25 +54,18 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement { if (!this.invertQuery) { return partyPokemon.filter((pokemon) => // every required move should be included - this.requiredMoves.every((requiredMove) => - this.getAllPokemonMoves(pokemon).includes(requiredMove) - ) + this.requiredMoves.every((requiredMove) => this.getAllPokemonMoves(pokemon).includes(requiredMove)) ); } else { return partyPokemon.filter( (pokemon) => // none of the "required" moves should be included - !this.requiredMoves.some((requiredMove) => - this.getAllPokemonMoves(pokemon).includes(requiredMove) - ) + !this.requiredMoves.some((requiredMove) => this.getAllPokemonMoves(pokemon).includes(requiredMove)) ); } } - override getDialogueToken( - _scene: BattleScene, - _pokemon?: PlayerPokemon - ): [string, string] { + override getDialogueToken(_scene: BattleScene, _pokemon?: PlayerPokemon): [string, string] { return ["requiredMoves", this.requiredMoves.join(", ")]; } From 75bf80cbad63a70af45df8b530dfc5a2d726c224 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 11:36:45 -0700 Subject: [PATCH 32/69] WIP: add lost-at-sea-encounter.test.ts added a couple of easy tests and 1 int/e2e for option3 --- .../encounters/lost-at-sea-encounter.test.ts | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts 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 new file mode 100644 index 00000000000..6f4a8c3c9e6 --- /dev/null +++ b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -0,0 +1,184 @@ +import { LostAtSeaEncounter } from "#app/data/mystery-encounters/encounters/lost-at-sea-encounter"; +import { EncounterOptionMode } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter.js"; +import { Button } from "#app/enums/buttons.js"; +import { Moves } from "#app/enums/moves"; +import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { Species } from "#app/enums/species.js"; +import * as Overrides from "#app/overrides"; +import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase.js"; +import GameManager from "#app/test/utils/gameManager.js"; +import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler.js"; +import { Mode } from "#app/ui/ui.js"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +const namepsace = "mysteryEncounter:lostAtSea"; + +describe("Lost at Sea - Mystery Encounter", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS }); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(100000); + vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_OVERRIDE", "get").mockReturnValue(MysteryEncounterType.LOST_AT_SEA); + // vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(33); + // vi.spyOn(game.scene.currentBattle, "mysteryEncounter", "get").mockReturnValue(LostAtSeaEncounter); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + it("should have the correct properties", () => { + game.runToMysteryEncounter([Species.MAGIKARP, Species.GYARADOS]); + expect(LostAtSeaEncounter.encounterType).toBe(MysteryEncounterType.LOST_AT_SEA); + expect(LostAtSeaEncounter.dialogue).toBeDefined(); + expect(LostAtSeaEncounter.dialogue.intro).toStrictEqual([{ text: `${namepsace}:intro` }]); + expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namepsace}:title`); + expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namepsace}:description`); + expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namepsace}:query`); + expect(LostAtSeaEncounter.options.length).toBe(3); + }); + + it("should not run below wave 11", async () => { + vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(10); + + await game.runToMysteryEncounter(); + + const { currentBattle } = game.scene; + expect(currentBattle).toBeDefined(); + expect(currentBattle.mysteryEncounter).toBeUndefined(); + }); + + it("should not run above wave 179", async () => { + vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(180); + + await game.runToMysteryEncounter(); + + const { currentBattle } = game.scene; + expect(currentBattle).toBeDefined(); + expect(currentBattle.mysteryEncounter).toBeUndefined(); + }); + + it("should set the correct dialog tokens during initialization", () => { + const { onInit } = LostAtSeaEncounter; + + expect(LostAtSeaEncounter.onInit).toBeDefined(); + + const onInitResult = onInit(game.scene); + + expect(LostAtSeaEncounter.dialogueTokens?.damagePercentage).toBe("25"); + expect(LostAtSeaEncounter.dialogueTokens?.option1RequiredMove).toBe(Moves[Moves.SURF]); + expect(LostAtSeaEncounter.dialogueTokens?.option2RequiredMove).toBe(Moves[Moves.FLY]); + expect(onInitResult).toBe(true); + }); + + describe("Option 1 - Surf", () => { + it("should have the correct properties", () => { + const option1 = LostAtSeaEncounter.options[0]; + expect(option1.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT); + expect(option1.dialogue).toBeDefined(); + expect(option1.dialogue).toStrictEqual({ + buttonLabel: `${namepsace}:option:1:label`, + disabledButtonLabel: `${namepsace}:option:1:label_disabled`, + buttonTooltip: `${namepsace}:option:1:tooltip`, + disabledButtonTooltip: `${namepsace}:option:1:tooltip_disabled`, + selected: [ + { + text: `${namepsace}:option:1:selected`, + }, + ], + }); + }); + }); + + describe("Option 2 - Fly", () => { + it("should have the correct properties", () => { + const option2 = LostAtSeaEncounter.options[1]; + + expect(option2.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT); + expect(option2.dialogue).toBeDefined(); + expect(option2.dialogue).toStrictEqual({ + buttonLabel: `${namepsace}:option:2:label`, + disabledButtonLabel: `${namepsace}:option:2:label_disabled`, + buttonTooltip: `${namepsace}:option:2:tooltip`, + disabledButtonTooltip: `${namepsace}:option:2:tooltip_disabled`, + selected: [ + { + text: `${namepsace}:option:2:selected`, + }, + ], + }); + }); + }); + + describe("Option 3 - Wander aimlessy", () => { + it("should have the correct properties", () => { + const option3 = LostAtSeaEncounter.options[2]; + + expect(option3.optionMode).toBe(EncounterOptionMode.DEFAULT); + expect(option3.dialogue).toBeDefined(); + expect(option3.dialogue).toStrictEqual({ + buttonLabel: `${namepsace}:option:3:label`, + buttonTooltip: `${namepsace}:option:3:tooltip`, + selected: [ + { + text: `${namepsace}:option:3:selected`, + }, + ], + }); + }); + + it("should apply 25% damage to all Pokemon", async () => { + vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(33); + + await game.runToMysteryEncounter([Species.ABRA]); + + game.onNextPrompt("MysteryEncounterPhase", Mode.MESSAGE, () => { + const uiHandler = game.scene.ui.getHandler(); + uiHandler.processInput(Button.ACTION); + }); + game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => { + const uiHandler = game.scene.ui.getHandler(); + uiHandler.unblockInput(); + uiHandler.processInput(Button.DOWN); + uiHandler.processInput(Button.ACTION); + }); + + await game.phaseInterceptor.run(MysteryEncounterPhase); + + const { encounteredEvents } = game.scene.mysteryEncounterData; + expect(encounteredEvents.some(([type, tier]) => type === MysteryEncounterType.LOST_AT_SEA && tier === MysteryEncounterTier.COMMON)).toBe(true); + }); + }); +}); + +/* +// Import necessary dependencies for testing +import { describe, it, expect } from "vitest"; +import { LostAtSeaEncounter } from "../lost-at-sea-encounter"; +import BattleScene from "../../../battle-scene"; + +describe("Lost At Sea Encounter Tests", () => { + it("should set the correct encounter properties", () => { + // Test the properties of the LostAtSeaEncounter object + // For example, test the encounter type, tier, scene wave range requirement, etc. + // You can use expect statements to check if the properties are set correctly + }); + + it("should handle the guiding pokemon phase correctly", () => { + // Create a mock BattleScene object for testing + const scene = new BattleScene(); // You may need to customize this based on your actual implementation + + // Call the handlePokemongGuidingYouPhase function with the scene + handlePokemongGuidingYouPhase(scene); + + // Use expect statements to verify the behavior of the function + // For example, check if the function sets the correct EXP value or handles cases where no guide pokemon is found + }); +});*/ From cb4162252cd5a5b33a7d58176e0717272ae19fae Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 15:17:04 -0700 Subject: [PATCH 33/69] add overrides helper to gameManager --- src/test/utils/gameManager.ts | 3 +++ src/test/utils/overridesHelper.ts | 45 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/test/utils/overridesHelper.ts diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 4550891dcf9..6c750c60a92 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -35,6 +35,7 @@ import { BattlerIndex } from "#app/battle.js"; import TargetSelectUiHandler from "#app/ui/target-select-ui-handler.js"; import BattleMessageUiHandler from "#app/ui/battle-message-ui-handler"; import {MysteryEncounterPhase} from "#app/phases/mystery-encounter-phase"; +import { OverridesHelper } from "./overridesHelper"; /** * Class to manage the game state and transitions between phases. @@ -45,6 +46,7 @@ export default class GameManager { public phaseInterceptor: PhaseInterceptor; public textInterceptor: TextInterceptor; public inputsHandler: InputsHandler; + public readonly override: OverridesHelper; /** * Creates an instance of GameManager. @@ -60,6 +62,7 @@ export default class GameManager { this.phaseInterceptor = new PhaseInterceptor(this.scene); this.textInterceptor = new TextInterceptor(this.scene); this.gameWrapper.setScene(this.scene); + this.override = new OverridesHelper(); } /** diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts new file mode 100644 index 00000000000..5500533470e --- /dev/null +++ b/src/test/utils/overridesHelper.ts @@ -0,0 +1,45 @@ +import { Biome } from "#app/enums/biome"; +import * as Overrides from "#app/overrides"; +import { vi } from "vitest"; + +/** + * Helper to handle overrides in tests + */ +export class OverridesHelper { + constructor() {} + + /** + * Set the encounter chance for a mystery encounter. + * @param percentage the encounter chance in % + */ + mysteryEncounterChance(percentage: number) { + const maxRate: number = 256; // 100% + const rate = maxRate * (percentage / 100); + vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(rate); + this.log(`Mystery encounter chance set to ${percentage}% (=${rate})!`); + } + + /** + * Set the starting biome + * @warning The biome will not be overridden unless you call `workaround_reInitSceneWithOverrides()` (testUtils) + * @param biome the biome to set + */ + startingBiome(biome: Biome) { + vi.spyOn(Overrides, "STARTING_BIOME_OVERRIDE", "get").mockReturnValue(biome); + this.log(`Starting biome set to ${Biome[biome]} (=${biome})!`); + } + + /** + * Set the starting wave (index) + * + * @param wave the wave (index) to set. Classic: `1`-`200` + */ + startingWave(wave: number) { + vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(wave); + this.log(`Starting wave set to ${wave}!`); + } + + private log(...params: any[]) { + console.log("Overrides:", ...params); + } +} From af989fdaa6989c577511eb562c88fe7fb11c5ab6 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 15:17:26 -0700 Subject: [PATCH 34/69] add `testUtils.workaround_reInitSceneWithOverrides()` --- src/test/utils/testUtils.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/test/utils/testUtils.ts b/src/test/utils/testUtils.ts index b922fc9c61c..a8461b3a5db 100644 --- a/src/test/utils/testUtils.ts +++ b/src/test/utils/testUtils.ts @@ -1,5 +1,6 @@ import i18next, { type ParseKeys } from "i18next"; import { vi } from "vitest"; +import GameManager from "./gameManager"; /** * Sets up the i18next mock. @@ -21,3 +22,15 @@ export function mockI18next() { export function arrayOfRange(start: integer, end: integer) { return Array.from({ length: end - start }, (_v, k) => k + start); } + +/** + * Woraround to reinitialize the game scene with overrides being set properly. + * By default the scene is initialized without all overrides even having a chance to be applied. + * @warning USE AT YOUR OWN RISK! Might be deleted in the future + * @param game The game manager + * @deprecated + */ +export async function workaround_reInitSceneWithOverrides(game: GameManager) { + await game.runToTitle(); + game.gameWrapper.setScene(game.scene); +} From 7a15a0f81ed955a59b83be7771c20fdbfcab02ea Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 15:17:48 -0700 Subject: [PATCH 35/69] add lost-at-sea option 3 phase check full test --- .../encounters/lost-at-sea-encounter.test.ts | 53 ++++++++++++++----- 1 file changed, 41 insertions(+), 12 deletions(-) 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 6f4a8c3c9e6..ac0c8d6a430 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 @@ -1,13 +1,17 @@ +import Battle from "#app/battle.js"; import { LostAtSeaEncounter } from "#app/data/mystery-encounters/encounters/lost-at-sea-encounter"; import { EncounterOptionMode } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter.js"; +import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import { Biome } from "#app/enums/biome.js"; import { Button } from "#app/enums/buttons.js"; import { Moves } from "#app/enums/moves"; import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; import { Species } from "#app/enums/species.js"; -import * as Overrides from "#app/overrides"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase.js"; +import { MessagePhase } from "#app/phases.js"; +import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase.js"; import GameManager from "#app/test/utils/gameManager.js"; +import { workaround_reInitSceneWithOverrides } from "#app/test/utils/testUtils.js"; import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler.js"; import { Mode } from "#app/ui/ui.js"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -24,18 +28,18 @@ describe("Lost at Sea - Mystery Encounter", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(100000); - vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_OVERRIDE", "get").mockReturnValue(MysteryEncounterType.LOST_AT_SEA); - // vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(33); - // vi.spyOn(game.scene.currentBattle, "mysteryEncounter", "get").mockReturnValue(LostAtSeaEncounter); + game.override.mysteryEncounterChance(100); + game.override.startingBiome(Biome.SEA); + vi.spyOn(MysteryEncounters, "allMysteryEncounters", "get").mockReturnValue({ [MysteryEncounterType.LOST_AT_SEA]: LostAtSeaEncounter }); }); afterEach(() => { game.phaseInterceptor.restoreOg(); }); - it("should have the correct properties", () => { - game.runToMysteryEncounter([Species.MAGIKARP, Species.GYARADOS]); + it("should have the correct properties", async () => { + await game.runToMysteryEncounter([Species.ABRA]); + expect(LostAtSeaEncounter.encounterType).toBe(MysteryEncounterType.LOST_AT_SEA); expect(LostAtSeaEncounter.dialogue).toBeDefined(); expect(LostAtSeaEncounter.dialogue.intro).toStrictEqual([{ text: `${namepsace}:intro` }]); @@ -46,7 +50,7 @@ describe("Lost at Sea - Mystery Encounter", () => { }); it("should not run below wave 11", async () => { - vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(10); + game.override.startingWave(10); await game.runToMysteryEncounter(); @@ -56,7 +60,7 @@ describe("Lost at Sea - Mystery Encounter", () => { }); it("should not run above wave 179", async () => { - vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(180); + game.override.startingWave(180); await game.runToMysteryEncounter(); @@ -66,6 +70,8 @@ describe("Lost at Sea - Mystery Encounter", () => { }); it("should set the correct dialog tokens during initialization", () => { + vi.spyOn(game.scene, "currentBattle", "get").mockReturnValue({ mysteryEncounter: LostAtSeaEncounter } as Battle); + const { onInit } = LostAtSeaEncounter; expect(LostAtSeaEncounter.onInit).toBeDefined(); @@ -135,12 +141,13 @@ describe("Lost at Sea - Mystery Encounter", () => { }); it("should apply 25% damage to all Pokemon", async () => { - vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(33); + game.override.startingWave(33); + workaround_reInitSceneWithOverrides(game); await game.runToMysteryEncounter([Species.ABRA]); game.onNextPrompt("MysteryEncounterPhase", Mode.MESSAGE, () => { - const uiHandler = game.scene.ui.getHandler(); + const uiHandler = game.scene.ui.getHandler(); uiHandler.processInput(Button.ACTION); }); game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => { @@ -150,10 +157,32 @@ describe("Lost at Sea - Mystery Encounter", () => { uiHandler.processInput(Button.ACTION); }); + /** There is some inconsistency in the phase order here. Probably because of the workaround */ + if (game.isCurrentPhase(MessagePhase)) { + game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { + const uiHandler = game.scene.ui.getHandler(); + uiHandler.processInput(Button.ACTION); + }); + await game.phaseInterceptor.run(MessagePhase); + } + await game.phaseInterceptor.run(MysteryEncounterPhase); const { encounteredEvents } = game.scene.mysteryEncounterData; expect(encounteredEvents.some(([type, tier]) => type === MysteryEncounterType.LOST_AT_SEA && tier === MysteryEncounterTier.COMMON)).toBe(true); + + game.onNextPrompt("MysteryEncounterOptionSelectedPhase", Mode.MESSAGE, () => { + const uiHandler = game.scene.ui.getHandler(); + uiHandler.processInput(Button.ACTION); + }); + await game.phaseInterceptor.run(MysteryEncounterOptionSelectedPhase); + + const party = game.scene.getParty(); + party.forEach((pkm) => { + const maxHp = pkm.getMaxHp(); + const msg = `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${maxHp} HP`; + expect(pkm.hp, msg).toBe(maxHp - Math.floor(maxHp * 0.25)); + }); }); }); }); From 944197330c6ac14a20c803006cc73555fbd1fbc6 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 15:31:42 -0700 Subject: [PATCH 36/69] fix can-learn-move-requirement getAllPokemonMoves --- .../requirements/can-learn-move-requirement.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 c9d54d54d49..a194933a404 100644 --- a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts +++ b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts @@ -77,15 +77,15 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement { const allPokemonMoves: Moves[] = []; if (!this.excludeLevelMoves) { - allPokemonMoves.push(...this.getPokemonLevelMoves(pkm)); + allPokemonMoves.push(...(this.getPokemonLevelMoves(pkm) ?? [])); } if (!this.excludeTmMoves) { - allPokemonMoves.push(...pkm.compatibleTms); + allPokemonMoves.push(...(pkm.compatibleTms ?? [])); } if (!this.excludeEggMoves) { - allPokemonMoves.push(...pkm.getEggMoves()); + allPokemonMoves.push(...(pkm.getEggMoves() ?? [])); } return allPokemonMoves; From 391054c8e0fcb740a9fdbae66a7fbc05c9f10ae8 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 16:41:30 -0700 Subject: [PATCH 37/69] extend overridesHelper --- src/test/utils/overridesHelper.ts | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts index 5500533470e..7bd14b213b5 100644 --- a/src/test/utils/overridesHelper.ts +++ b/src/test/utils/overridesHelper.ts @@ -1,3 +1,4 @@ +import { Weather, WeatherType } from "#app/data/weather.js"; import { Biome } from "#app/enums/biome"; import * as Overrides from "#app/overrides"; import { vi } from "vitest"; @@ -9,7 +10,7 @@ export class OverridesHelper { constructor() {} /** - * Set the encounter chance for a mystery encounter. + * Override the encounter chance for a mystery encounter. * @param percentage the encounter chance in % */ mysteryEncounterChance(percentage: number) { @@ -20,7 +21,7 @@ export class OverridesHelper { } /** - * Set the starting biome + * Override the starting biome * @warning The biome will not be overridden unless you call `workaround_reInitSceneWithOverrides()` (testUtils) * @param biome the biome to set */ @@ -30,8 +31,7 @@ export class OverridesHelper { } /** - * Set the starting wave (index) - * + * Override the starting wave (index) * @param wave the wave (index) to set. Classic: `1`-`200` */ startingWave(wave: number) { @@ -39,6 +39,25 @@ export class OverridesHelper { this.log(`Starting wave set to ${wave}!`); } + /** + * Override the weather (type) + * @param type weather type to set + */ + weather(type: WeatherType) { + vi.spyOn(Overrides, "WEATHER_OVERRIDE", "get").mockReturnValue(type); + this.log(`Weather set to ${Weather[type]} (=${type})!`); + } + + /** + * Override the seed + * @warning The seed will not be overridden unless you call `workaround_reInitSceneWithOverrides()` (testUtils) + * @param seed the seed to set + */ + seed(seed: string) { + vi.spyOn(Overrides, "SEED_OVERRIDE", "get").mockReturnValue(seed); + this.log(`Seed set to "${seed}"!`); + } + private log(...params: any[]) { console.log("Overrides:", ...params); } From 94e43e633c93434c060feec3ba52bbc5a0b6917a Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 16:41:47 -0700 Subject: [PATCH 38/69] add encounterTestUtils.ts --- .../mystery-encounter/encounterTestUtils.ts | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/test/mystery-encounter/encounterTestUtils.ts diff --git a/src/test/mystery-encounter/encounterTestUtils.ts b/src/test/mystery-encounter/encounterTestUtils.ts new file mode 100644 index 00000000000..1fb0d0bbc17 --- /dev/null +++ b/src/test/mystery-encounter/encounterTestUtils.ts @@ -0,0 +1,54 @@ +import { Button } from "#app/enums/buttons.js"; +import { MessagePhase } from "#app/phases.js"; +import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase.js"; +import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler.js"; +import { Mode } from "#app/ui/ui.js"; +import GameManager from "../utils/gameManager"; + +export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number) { + // Handle eventual weather messages (e.g. a downpour started!) + if (game.isCurrentPhase(MessagePhase)) { + game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { + const uiHandler = game.scene.ui.getHandler(); + uiHandler.processInput(Button.ACTION); + }); + await game.phaseInterceptor.run(MessagePhase); + } + + // dispose of intro messages + game.onNextPrompt("MysteryEncounterPhase", Mode.MESSAGE, () => { + const uiHandler = game.scene.ui.getHandler(); + uiHandler.processInput(Button.ACTION); + }); + // select the desired option + game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => { + const uiHandler = game.scene.ui.getHandler(); + uiHandler.unblockInput(); + + switch (optionNo) { + case 1: + // no movement needed. Default cursor position + break; + case 2: + uiHandler.processInput(Button.RIGHT); + break; + case 3: + uiHandler.processInput(Button.DOWN); + break; + case 4: + uiHandler.processInput(Button.RIGHT); + uiHandler.processInput(Button.DOWN); + break; + } + + uiHandler.processInput(Button.ACTION); + }); + await game.phaseInterceptor.run(MysteryEncounterPhase); + + // run the selected options phase + game.onNextPrompt("MysteryEncounterOptionSelectedPhase", Mode.MESSAGE, () => { + const uiHandler = game.scene.ui.getHandler(); + uiHandler.processInput(Button.ACTION); + }); + await game.phaseInterceptor.run(MysteryEncounterOptionSelectedPhase); +} From fa02f84448be46a5fa56fa8610212daab9af2565 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 16:41:53 -0700 Subject: [PATCH 39/69] use encounterTestUtils.ts --- .../encounters/lost-at-sea-encounter.test.ts | 90 +++++-------------- 1 file changed, 20 insertions(+), 70 deletions(-) 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 ac0c8d6a430..8d08abd66c6 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 @@ -1,20 +1,18 @@ -import Battle from "#app/battle.js"; +import Battle from "#app/battle"; import { LostAtSeaEncounter } from "#app/data/mystery-encounters/encounters/lost-at-sea-encounter"; +import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter"; import { EncounterOptionMode } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter.js"; import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import { Biome } from "#app/enums/biome.js"; -import { Button } from "#app/enums/buttons.js"; +import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { WeatherType } from "#app/data/weather"; +import { Biome } from "#app/enums/biome"; import { Moves } from "#app/enums/moves"; import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; -import { Species } from "#app/enums/species.js"; -import { MessagePhase } from "#app/phases.js"; -import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase.js"; -import GameManager from "#app/test/utils/gameManager.js"; -import { workaround_reInitSceneWithOverrides } from "#app/test/utils/testUtils.js"; -import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import { Species } from "#app/enums/species"; +import GameManager from "#app/test/utils/gameManager"; +import { workaround_reInitSceneWithOverrides } from "#app/test/utils/testUtils"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { runSelectMysteryEncounterOption } from "../encounterTestUtils"; const namepsace = "mysteryEncounter:lostAtSea"; @@ -140,74 +138,26 @@ describe("Lost at Sea - Mystery Encounter", () => { }); }); - it("should apply 25% damage to all Pokemon", async () => { + it("should handle the option phase properly", async () => { game.override.startingWave(33); + game.override.weather(WeatherType.RAIN); + const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); workaround_reInitSceneWithOverrides(game); - await game.runToMysteryEncounter([Species.ABRA]); - - game.onNextPrompt("MysteryEncounterPhase", Mode.MESSAGE, () => { - const uiHandler = game.scene.ui.getHandler(); - uiHandler.processInput(Button.ACTION); - }); - game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => { - const uiHandler = game.scene.ui.getHandler(); - uiHandler.unblockInput(); - uiHandler.processInput(Button.DOWN); - uiHandler.processInput(Button.ACTION); - }); - - /** There is some inconsistency in the phase order here. Probably because of the workaround */ - if (game.isCurrentPhase(MessagePhase)) { - game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { - const uiHandler = game.scene.ui.getHandler(); - uiHandler.processInput(Button.ACTION); - }); - await game.phaseInterceptor.run(MessagePhase); - } - - await game.phaseInterceptor.run(MysteryEncounterPhase); + await game.runToMysteryEncounter([Species.ABRA, Species.ZEBSTRIKA, Species.BULBASAUR, Species.GROUDON]); + await runSelectMysteryEncounterOption(game, 3); const { encounteredEvents } = game.scene.mysteryEncounterData; - expect(encounteredEvents.some(([type, tier]) => type === MysteryEncounterType.LOST_AT_SEA && tier === MysteryEncounterTier.COMMON)).toBe(true); - - game.onNextPrompt("MysteryEncounterOptionSelectedPhase", Mode.MESSAGE, () => { - const uiHandler = game.scene.ui.getHandler(); - uiHandler.processInput(Button.ACTION); - }); - await game.phaseInterceptor.run(MysteryEncounterOptionSelectedPhase); - const party = game.scene.getParty(); + + expect(encounteredEvents.some(([type, tier]) => type === MysteryEncounterType.LOST_AT_SEA && tier === MysteryEncounterTier.COMMON)).toBe(true); party.forEach((pkm) => { const maxHp = pkm.getMaxHp(); - const msg = `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${maxHp} HP`; - expect(pkm.hp, msg).toBe(maxHp - Math.floor(maxHp * 0.25)); + const expectMsg = `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${maxHp} HP`; + + expect(pkm.hp, expectMsg).toBe(maxHp - Math.floor(maxHp * 0.25)); }); + expect(leaveEncounterWithoutBattleSpy).toBeCalled(); }); }); }); - -/* -// Import necessary dependencies for testing -import { describe, it, expect } from "vitest"; -import { LostAtSeaEncounter } from "../lost-at-sea-encounter"; -import BattleScene from "../../../battle-scene"; - -describe("Lost At Sea Encounter Tests", () => { - it("should set the correct encounter properties", () => { - // Test the properties of the LostAtSeaEncounter object - // For example, test the encounter type, tier, scene wave range requirement, etc. - // You can use expect statements to check if the properties are set correctly - }); - - it("should handle the guiding pokemon phase correctly", () => { - // Create a mock BattleScene object for testing - const scene = new BattleScene(); // You may need to customize this based on your actual implementation - - // Call the handlePokemongGuidingYouPhase function with the scene - handlePokemongGuidingYouPhase(scene); - - // Use expect statements to verify the behavior of the function - // For example, check if the function sets the correct EXP value or handles cases where no guide pokemon is found - }); -});*/ From 233f6072d679a49d4c3af8ae180fd4555b72f1a9 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 16:42:53 -0700 Subject: [PATCH 40/69] chore: remove .js from imports --- src/test/mystery-encounter/encounterTestUtils.ts | 10 +++++----- src/test/utils/overridesHelper.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/mystery-encounter/encounterTestUtils.ts b/src/test/mystery-encounter/encounterTestUtils.ts index 1fb0d0bbc17..246c763690a 100644 --- a/src/test/mystery-encounter/encounterTestUtils.ts +++ b/src/test/mystery-encounter/encounterTestUtils.ts @@ -1,8 +1,8 @@ -import { Button } from "#app/enums/buttons.js"; -import { MessagePhase } from "#app/phases.js"; -import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase.js"; -import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import { Button } from "#app/enums/buttons"; +import { MessagePhase } from "#app/phases"; +import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase"; +import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; +import { Mode } from "#app/ui/ui"; import GameManager from "../utils/gameManager"; export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number) { diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts index 7bd14b213b5..dd45d972b50 100644 --- a/src/test/utils/overridesHelper.ts +++ b/src/test/utils/overridesHelper.ts @@ -1,4 +1,4 @@ -import { Weather, WeatherType } from "#app/data/weather.js"; +import { Weather, WeatherType } from "#app/data/weather"; import { Biome } from "#app/enums/biome"; import * as Overrides from "#app/overrides"; import { vi } from "vitest"; From 5cded35e364d4b08751c4bd07fb71d3efe34bb66 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 17:04:29 -0700 Subject: [PATCH 41/69] minor changes to encounterTestUtils.ts --- src/test/mystery-encounter/encounterTestUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/mystery-encounter/encounterTestUtils.ts b/src/test/mystery-encounter/encounterTestUtils.ts index 246c763690a..10fa9e7c648 100644 --- a/src/test/mystery-encounter/encounterTestUtils.ts +++ b/src/test/mystery-encounter/encounterTestUtils.ts @@ -23,7 +23,7 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN // select the desired option game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => { const uiHandler = game.scene.ui.getHandler(); - uiHandler.unblockInput(); + uiHandler.unblockInput(); // input are blocked by 1s to prevent accidental input. Tests need to handle that switch (optionNo) { case 1: From b26cffc49e8a87522fec379932aa613f075691fe Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 19:45:24 -0700 Subject: [PATCH 42/69] WIP: working on lost-at-sea-encounter tests --- .../mystery-encounter/encounterTestUtils.ts | 6 ++- .../encounters/lost-at-sea-encounter.test.ts | 53 +++++++++++++------ 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/test/mystery-encounter/encounterTestUtils.ts b/src/test/mystery-encounter/encounterTestUtils.ts index 10fa9e7c648..cfd42f5afad 100644 --- a/src/test/mystery-encounter/encounterTestUtils.ts +++ b/src/test/mystery-encounter/encounterTestUtils.ts @@ -1,13 +1,13 @@ import { Button } from "#app/enums/buttons"; import { MessagePhase } from "#app/phases"; -import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase"; +import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phase"; import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; import { Mode } from "#app/ui/ui"; import GameManager from "../utils/gameManager"; export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number) { - // Handle eventual weather messages (e.g. a downpour started!) if (game.isCurrentPhase(MessagePhase)) { + // Handle eventual weather messages (e.g. a downpour started!) game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { const uiHandler = game.scene.ui.getHandler(); uiHandler.processInput(Button.ACTION); @@ -51,4 +51,6 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN uiHandler.processInput(Button.ACTION); }); await game.phaseInterceptor.run(MysteryEncounterOptionSelectedPhase); + + await game.phaseInterceptor.to(MysteryEncounterRewardsPhase); } 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 8d08abd66c6..30a85ff6d44 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 @@ -1,10 +1,8 @@ import Battle from "#app/battle"; import { LostAtSeaEncounter } from "#app/data/mystery-encounters/encounters/lost-at-sea-encounter"; -import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter"; import { EncounterOptionMode } from "#app/data/mystery-encounters/mystery-encounter-option"; import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { WeatherType } from "#app/data/weather"; import { Biome } from "#app/enums/biome"; import { Moves } from "#app/enums/moves"; import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; @@ -15,6 +13,8 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite import { runSelectMysteryEncounterOption } from "../encounterTestUtils"; const namepsace = "mysteryEncounter:lostAtSea"; +/** Blastoise for surf. Pidgeot for fly. Abra for none. */ +const defaultParty = [Species.BLASTOISE, Species.PIDGEOT, Species.ABRA]; describe("Lost at Sea - Mystery Encounter", () => { let phaserGame: Phaser.Game; @@ -119,6 +119,24 @@ describe("Lost at Sea - Mystery Encounter", () => { ], }); }); + + it("should award exp to surfable pokemone (Blastoise)", async () => { + game.override.startingWave(33); + const setEncounterExpSpy = vi.spyOn(EncounterPhaseUtils, "setEncounterExp"); + + workaround_reInitSceneWithOverrides(game); + await game.runToMysteryEncounter(defaultParty); + let blastoise = game.scene.getParty().find((pkm) => pkm.species.speciesId === Species.BLASTOISE); + console.log("BLASTOISE EXP BEFORE: ", blastoise.exp); + + await runSelectMysteryEncounterOption(game, 2); + + blastoise = game.scene.getParty().find((pkm) => pkm.species.speciesId === Species.BLASTOISE); + console.log("BLASTOISE EXP AFTER: ", blastoise.exp); + + expect(blastoise.exp).toBe(128); + expect(setEncounterExpSpy).toHaveBeenCalledWith(expect.anything(), 128, expect.anything(), true); + }); }); describe("Option 3 - Wander aimlessy", () => { @@ -138,25 +156,30 @@ describe("Lost at Sea - Mystery Encounter", () => { }); }); - it("should handle the option phase properly", async () => { + it("should damage all party pokemon by 25%", async () => { + game.override.startingWave(33); + + workaround_reInitSceneWithOverrides(game); + await game.runToMysteryEncounter(defaultParty); + await runSelectMysteryEncounterOption(game, 3); + + game.scene + .getParty() + .forEach((pkm) => + expect(pkm.hp, `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe( + pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.25) + ) + ); + }); + + it("should leave encounter without battle", async () => { game.override.startingWave(33); - game.override.weather(WeatherType.RAIN); const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); workaround_reInitSceneWithOverrides(game); - await game.runToMysteryEncounter([Species.ABRA, Species.ZEBSTRIKA, Species.BULBASAUR, Species.GROUDON]); + await game.runToMysteryEncounter(defaultParty); await runSelectMysteryEncounterOption(game, 3); - const { encounteredEvents } = game.scene.mysteryEncounterData; - const party = game.scene.getParty(); - - expect(encounteredEvents.some(([type, tier]) => type === MysteryEncounterType.LOST_AT_SEA && tier === MysteryEncounterTier.COMMON)).toBe(true); - party.forEach((pkm) => { - const maxHp = pkm.getMaxHp(); - const expectMsg = `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${maxHp} HP`; - - expect(pkm.hp, expectMsg).toBe(maxHp - Math.floor(maxHp * 0.25)); - }); expect(leaveEncounterWithoutBattleSpy).toBeCalled(); }); }); From 04da4516b44102588a7f4935ac0e7c006549190b Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 21:27:35 -0700 Subject: [PATCH 43/69] finish lost-at-sea-encounter.test.ts (for now) --- .../mystery-encounter/encounterTestUtils.ts | 4 +- .../encounters/lost-at-sea-encounter.test.ts | 103 +++++++++++++----- src/test/utils/phaseInterceptor.ts | 8 +- 3 files changed, 84 insertions(+), 31 deletions(-) diff --git a/src/test/mystery-encounter/encounterTestUtils.ts b/src/test/mystery-encounter/encounterTestUtils.ts index cfd42f5afad..65f6ae0edc9 100644 --- a/src/test/mystery-encounter/encounterTestUtils.ts +++ b/src/test/mystery-encounter/encounterTestUtils.ts @@ -1,6 +1,6 @@ import { Button } from "#app/enums/buttons"; import { MessagePhase } from "#app/phases"; -import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phase"; +import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phase"; import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; import { Mode } from "#app/ui/ui"; import GameManager from "../utils/gameManager"; @@ -50,7 +50,5 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN const uiHandler = game.scene.ui.getHandler(); uiHandler.processInput(Button.ACTION); }); - await game.phaseInterceptor.run(MysteryEncounterOptionSelectedPhase); - await game.phaseInterceptor.to(MysteryEncounterRewardsPhase); } 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 30a85ff6d44..928da9760d4 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 @@ -28,7 +28,9 @@ describe("Lost at Sea - Mystery Encounter", () => { game = new GameManager(phaserGame); game.override.mysteryEncounterChance(100); game.override.startingBiome(Biome.SEA); - vi.spyOn(MysteryEncounters, "allMysteryEncounters", "get").mockReturnValue({ [MysteryEncounterType.LOST_AT_SEA]: LostAtSeaEncounter }); + vi.spyOn(MysteryEncounters, "allMysteryEncounters", "get").mockReturnValue({ + [MysteryEncounterType.LOST_AT_SEA]: LostAtSeaEncounter, + }); }); afterEach(() => { @@ -47,14 +49,16 @@ describe("Lost at Sea - Mystery Encounter", () => { expect(LostAtSeaEncounter.options.length).toBe(3); }); + it("should not run outside of sea biome", async () => { + // TODO: a little tricky + }); + it("should not run below wave 11", async () => { game.override.startingWave(10); await game.runToMysteryEncounter(); - const { currentBattle } = game.scene; - expect(currentBattle).toBeDefined(); - expect(currentBattle.mysteryEncounter).toBeUndefined(); + expect(game.scene.currentBattle.mysteryEncounter).toBeUndefined(); }); it("should not run above wave 179", async () => { @@ -62,9 +66,7 @@ describe("Lost at Sea - Mystery Encounter", () => { await game.runToMysteryEncounter(); - const { currentBattle } = game.scene; - expect(currentBattle).toBeDefined(); - expect(currentBattle.mysteryEncounter).toBeUndefined(); + expect(game.scene.currentBattle.mysteryEncounter).toBeUndefined(); }); it("should set the correct dialog tokens during initialization", () => { @@ -99,6 +101,37 @@ describe("Lost at Sea - Mystery Encounter", () => { ], }); }); + + it("should award exp to surfable PKM (Blastoise)", async () => { + const laprasBaseExp = 187; + const wave = 33; + game.override.startingWave(wave); + + await workaround_reInitSceneWithOverrides(game); + await game.runToMysteryEncounter(defaultParty); + const party = game.scene.getParty(); + const blastoise = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT); + const expBefore = blastoise.exp; + + await runSelectMysteryEncounterOption(game, 2); + + expect(blastoise.exp).toBe(expBefore + laprasBaseExp * wave); + }); + + it("should leave encounter without battle", async () => { + game.override.startingWave(33); + const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); + + await workaround_reInitSceneWithOverrides(game); + await game.runToMysteryEncounter(defaultParty); + await runSelectMysteryEncounterOption(game, 1); + + expect(leaveEncounterWithoutBattleSpy).toBeCalled(); + }); + + it("should be disabled if no surfable PKM is in party", async () => { + // TODO + }); }); describe("Option 2 - Fly", () => { @@ -120,22 +153,35 @@ describe("Lost at Sea - Mystery Encounter", () => { }); }); - it("should award exp to surfable pokemone (Blastoise)", async () => { - game.override.startingWave(33); - const setEncounterExpSpy = vi.spyOn(EncounterPhaseUtils, "setEncounterExp"); + it("should award exp to flyable PKM (Pidgeot)", async () => { + const laprasBaseExp = 187; + const wave = 33; + game.override.startingWave(wave); - workaround_reInitSceneWithOverrides(game); + await workaround_reInitSceneWithOverrides(game); await game.runToMysteryEncounter(defaultParty); - let blastoise = game.scene.getParty().find((pkm) => pkm.species.speciesId === Species.BLASTOISE); - console.log("BLASTOISE EXP BEFORE: ", blastoise.exp); + const party = game.scene.getParty(); + const pidgeot = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT); + const expBefore = pidgeot.exp; await runSelectMysteryEncounterOption(game, 2); - blastoise = game.scene.getParty().find((pkm) => pkm.species.speciesId === Species.BLASTOISE); - console.log("BLASTOISE EXP AFTER: ", blastoise.exp); + expect(pidgeot.exp).toBe(expBefore + laprasBaseExp * wave); + }); - expect(blastoise.exp).toBe(128); - expect(setEncounterExpSpy).toHaveBeenCalledWith(expect.anything(), 128, expect.anything(), true); + it("should leave encounter without battle", async () => { + game.override.startingWave(33); + const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); + + await workaround_reInitSceneWithOverrides(game); + await game.runToMysteryEncounter(defaultParty); + await runSelectMysteryEncounterOption(game, 2); + + expect(leaveEncounterWithoutBattleSpy).toBeCalled(); + }); + + it("should be disabled if no flyable PKM is in party", async () => { + // TODO }); }); @@ -156,20 +202,25 @@ describe("Lost at Sea - Mystery Encounter", () => { }); }); - it("should damage all party pokemon by 25%", async () => { + it("should damage all (allowed in battle) party PKM by 25%", async () => { game.override.startingWave(33); - workaround_reInitSceneWithOverrides(game); + await workaround_reInitSceneWithOverrides(game); await game.runToMysteryEncounter(defaultParty); + + const party = game.scene.getParty(); + const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA); + vi.spyOn(abra, "isAllowedInBattle").mockReturnValue(false); + await runSelectMysteryEncounterOption(game, 3); - game.scene - .getParty() - .forEach((pkm) => - expect(pkm.hp, `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe( - pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.25) - ) - ); + const allowedPkm = party.filter((pkm) => pkm.isAllowedInBattle()); + const notAllowedPkm = party.filter((pkm) => !pkm.isAllowedInBattle()); + allowedPkm.forEach((pkm) => + expect(pkm.hp, `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.25)) + ); + + notAllowedPkm.forEach((pkm) => expect(pkm.hp, `${pkm.name} should be full hp: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp())); }); it("should leave encounter without battle", async () => { diff --git a/src/test/utils/phaseInterceptor.ts b/src/test/utils/phaseInterceptor.ts index cc593030b09..71cf6b495ed 100644 --- a/src/test/utils/phaseInterceptor.ts +++ b/src/test/utils/phaseInterceptor.ts @@ -8,6 +8,7 @@ import { EncounterPhase, EnemyCommandPhase, FaintPhase, + LearnMovePhase, LoginPhase, MessagePhase, MoveEffectPhase, @@ -41,6 +42,7 @@ import { MysteryEncounterBattlePhase, MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase, + MysteryEncounterRewardsPhase, PostMysteryEncounterPhase } from "#app/phases/mystery-encounter-phase"; @@ -100,11 +102,13 @@ export default class PhaseInterceptor { [MysteryEncounterPhase, this.startPhase], [MysteryEncounterOptionSelectedPhase, this.startPhase], [MysteryEncounterBattlePhase, this.startPhase], - [PostMysteryEncounterPhase, this.startPhase] + [MysteryEncounterRewardsPhase, this.startPhase], + [PostMysteryEncounterPhase, this.startPhase], + [LearnMovePhase, this.startPhase] ]; private endBySetMode = [ - TitlePhase, SelectGenderPhase, CommandPhase, SelectModifierPhase + TitlePhase, SelectGenderPhase, CommandPhase, SelectModifierPhase, PostMysteryEncounterPhase ]; /** From 613c13c41938e8eadc48e0d0691667dd8a98454e Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 21:34:05 -0700 Subject: [PATCH 44/69] use buoy sprite in lost-at-sea encounter --- .../encounters/lost-at-sea-encounter.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index cbbdbdccf92..dabbf07469e 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -1,5 +1,4 @@ import { Moves } from "#app/enums/moves"; -import { Species } from "#app/enums/species"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; @@ -27,14 +26,11 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with .withSceneWaveRangeRequirement(11, 179) .withIntroSpriteConfigs([ { - fileRoot: "pokemon", - spriteKey: `${Species.GYARADOS}`, + fileRoot: "mystery-encounters", + spriteKey: "buoy", hasShadow: false, - scale: 4, - y: 100, - x: 130, - tint: 0.75, - alpha: 0.25, + y: 5, + x: 10, }, ]) .withIntroDialogue([{ text: `${namepsace}:intro` }]) From 4452356555dd162c6791959d80ec506a0512560f Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 16 Jul 2024 21:36:56 -0700 Subject: [PATCH 45/69] fix buoy sprite placement --- .../mystery-encounters/encounters/lost-at-sea-encounter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index dabbf07469e..9ee35bb6e31 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -29,8 +29,8 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with fileRoot: "mystery-encounters", spriteKey: "buoy", hasShadow: false, - y: 5, - x: 10, + x: 20, + y: 3, }, ]) .withIntroDialogue([{ text: `${namepsace}:intro` }]) From c978e348b792cb4d286e7d3ba665697473fba0d5 Mon Sep 17 00:00:00 2001 From: flx-sta <50131232+flx-sta@users.noreply.github.com> Date: Wed, 17 Jul 2024 08:06:36 -0700 Subject: [PATCH 46/69] Apply suggestions from code review Co-authored-by: Asdar --- .../en/mystery-encounters/lost-at-sea.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/locales/en/mystery-encounters/lost-at-sea.ts b/src/locales/en/mystery-encounters/lost-at-sea.ts index 35cceaa17a8..9b156f261ec 100644 --- a/src/locales/en/mystery-encounters/lost-at-sea.ts +++ b/src/locales/en/mystery-encounters/lost-at-sea.ts @@ -1,28 +1,28 @@ export const lostAtSea = { - intro: "You are halucinating and starting to loose your bearings.", + intro: "Wandering aimlessly, you effectively get nowhere.", title: "Lost at sea", description: - "You get lost at sea. All you \"sea\" is water everywhere and the sun is burning bright. Certain Pokémons can help you get back on track unharmed.", + "The sea is turbulent in this area and you seem to be running out of fuel as well.\nThis is bad. Is there a way out of the situation?", query: "What will you do?", option: { 1: { - label: "Use {{option1PrimaryName}}", + label: "{{option1PrimaryName}} can help", label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "Use {{option1PrimaryName}} to guide you back. {{option1PrimaryName}} earns EXP as if having defeated a Lapras.", + tooltip: "{{option1PrimaryName}} saves you.\n{{option1PrimaryName}} gains some EXP.", tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: "{{option1PrimaryName}} guides you back and earns EXP.", + selected: "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", }, 2: { - label: "Use {{option2PrimaryName}}", + label: "{{option2PrimaryName}} can help", label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "Use {{option2PrimaryName}} to guide you back. {{option2PrimaryName}} earns EXP as if having defeated a Lapras.", + tooltip: "{{option2PrimaryName}} saves you.\n{{option2PrimaryName}} gains some EXP.", tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: "{{option2PrimaryName}} guides you back and earns EXP.", + selected: "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", }, 3: { label: "Wander aimlessly", - tooltip: "Wander aimlessly until you're back. All your Pokémon lose {{damagePercentage}}% of their HP. Any below that are KO'd.", - selected: "You wander aimlessly around. After hours of wandering, you find your way back. You and your team take the toll.", + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", + selected: "You float about in the boat, steering it aimlessly until you finally get back on track. You and your Pokémon get very fatigued during the whole ordeal", }, }, }; From beb13c8361fe9ceaaffe3c9a6d17169a527ad925 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 08:43:55 -0700 Subject: [PATCH 47/69] fix types in myster-encounter-phase.ts --- src/phases/mystery-encounter-phase.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/phases/mystery-encounter-phase.ts b/src/phases/mystery-encounter-phase.ts index 13856471e4d..cebba02f585 100644 --- a/src/phases/mystery-encounter-phase.ts +++ b/src/phases/mystery-encounter-phase.ts @@ -4,7 +4,7 @@ import { Phase } from "../phase"; import { Mode } from "../ui/ui"; import { hideMysteryEncounterIntroVisuals, OptionSelectSettings } from "../data/mystery-encounters/utils/encounter-phase-utils"; import { CheckSwitchPhase, NewBattlePhase, ReturnPhase, ScanIvsPhase, SelectModifierPhase, SummonPhase, ToggleDoublePositionPhase } from "../phases"; -import MysteryEncounterOption from "../data/mystery-encounters/mystery-encounter-option"; +import MysteryEncounterOption, { OptionPhaseCallback } from "../data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterVariant } from "../data/mystery-encounters/mystery-encounter"; import { getCharVariantFromDialogue } from "../data/dialogue"; import { TrainerSlot } from "../data/trainer-config"; @@ -138,7 +138,7 @@ export class MysteryEncounterPhase extends Phase { * Any phase that is meant to follow this one MUST be queued via the onOptionSelect() logic of the selected option */ export class MysteryEncounterOptionSelectedPhase extends Phase { - onOptionSelect: (scene: BattleScene) => Promise; + onOptionSelect: OptionPhaseCallback; constructor(scene: BattleScene) { super(scene); @@ -394,7 +394,7 @@ export class MysteryEncounterRewardsPhase extends Phase { * - Queuing of the next wave */ export class PostMysteryEncounterPhase extends Phase { - onPostOptionSelect: (scene: BattleScene) => Promise; + onPostOptionSelect: OptionPhaseCallback; constructor(scene: BattleScene) { super(scene); From cd1bed3ab1b4a496cb75a815297131371d272e3f Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 08:52:43 -0700 Subject: [PATCH 48/69] lost at sea enc.: add outro. add fainted msgs --- .../encounters/lost-at-sea-encounter.ts | 28 ++++++++++--------- .../en/mystery-encounters/lost-at-sea.ts | 13 +++++---- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 9ee35bb6e31..d32d9025346 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -12,7 +12,7 @@ const OPTION_2_REQUIRED_MOVE = Moves.FLY; * Can be a number between `0` - `100`. * The higher the more damage taken (100% = instant KO). */ -const DAMAGE_PERCENTAGE: number = 25; // 0 - 100 +const DAMAGE_PERCENTAGE: number = 25; /** The i18n namespace for the encounter */ const namepsace = "mysteryEncounter:lostAtSea"; @@ -47,10 +47,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with .withDescription(`${namepsace}:description`) .withQuery(`${namepsace}:query`) .withOption( - /** - * Option 1: Use a (non fainted) pokemon that can learn Surf to guide you back. - * Receives EXP similar to defeating a Lapras - */ + // Option 1: Use a (non fainted) pokemon that can learn Surf to guide you back/ new MysteryEncounterOptionBuilder() .withPokemonCanLearnMoveRequirement(OPTION_1_REQUIRED_MOVE) .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) @@ -69,10 +66,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with .build() ) .withOption( - /** - * Option 2: Use a (non fainted) pokemon that can learn fly to guide you back. - * Receives EXP similar to defeating a Lapras - */ + //Option 2: Use a (non fainted) pokemon that can learn fly to guide you back. new MysteryEncounterOptionBuilder() .withPokemonCanLearnMoveRequirement(OPTION_2_REQUIRED_MOVE) .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) @@ -91,9 +85,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with .build() ) .withSimpleOption( - /** - * Option 3: Wander aimlessly. All pokemons lose {@linkcode DAMAGE_PERCENTAGE}}% of their HP (or KO on 0 HP). - */ + // Option 3: Wander aimlessly { buttonLabel: `${namepsace}:option:3:label`, buttonTooltip: `${namepsace}:option:3:tooltip`, @@ -109,13 +101,23 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with allowedPokemon.forEach((pkm) => { const percentage = DAMAGE_PERCENTAGE / 100; const damage = Math.floor(pkm.getMaxHp() * percentage); - return applyDamageToPokemon(pkm, damage); + applyDamageToPokemon(pkm, damage); + if (pkm.isFainted()) { + scene.currentBattle.mysteryEncounter.dialogue.outro.push({ + text: `${pkm.name} fainted!`, + }); + } }); leaveEncounterWithoutBattle(scene); return true; } ) + .withOutroDialogue([ + { + text: `${namepsace}:outro`, + }, + ]) .build(); /** diff --git a/src/locales/en/mystery-encounters/lost-at-sea.ts b/src/locales/en/mystery-encounters/lost-at-sea.ts index 9b156f261ec..35d86e44d04 100644 --- a/src/locales/en/mystery-encounters/lost-at-sea.ts +++ b/src/locales/en/mystery-encounters/lost-at-sea.ts @@ -1,8 +1,7 @@ export const lostAtSea = { intro: "Wandering aimlessly, you effectively get nowhere.", title: "Lost at sea", - description: - "The sea is turbulent in this area and you seem to be running out of fuel as well.\nThis is bad. Is there a way out of the situation?", + description: "The sea is turbulent in this area and you seem to be running out of fuel as well.\nThis is bad. Is there a way out of the situation?", query: "What will you do?", option: { 1: { @@ -10,19 +9,23 @@ export const lostAtSea = { label_disabled: "Can't {{option1RequiredMove}}", tooltip: "{{option1PrimaryName}} saves you.\n{{option1PrimaryName}} gains some EXP.", tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: + "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", }, 2: { label: "{{option2PrimaryName}} can help", label_disabled: "Can't {{option2RequiredMove}}", tooltip: "{{option2PrimaryName}} saves you.\n{{option2PrimaryName}} gains some EXP.", tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: + "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", }, 3: { label: "Wander aimlessly", tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", - selected: "You float about in the boat, steering it aimlessly until you finally get back on track. You and your Pokémon get very fatigued during the whole ordeal", + selected: `You float about in the boat, steering it aimlessly until you finally get back on track. + $You and your Pokémon get very fatigued during the whole ordeal`, }, }, + outro: "You are back on track." }; From 274ff219430b33502fa4fd004328d202bac2056c Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 09:53:27 -0700 Subject: [PATCH 49/69] add game-over handling to encounter-phase.-utils it now checks initially if all allowed pokemon are fainted --- .../mystery-encounters/utils/encounter-phase-utils.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 88004fae989..76f530cf0db 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -9,7 +9,7 @@ import Pokemon, { FieldPosition, PlayerPokemon } from "#app/field/pokemon"; import Trainer, { TrainerVariant } from "../../../field/trainer"; import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier"; import { CustomModifierSettings, getModifierPoolForType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, PokemonHeldItemModifierType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; -import { BattleEndPhase, EggLapsePhase, ExpPhase, ModifierRewardPhase, SelectModifierPhase, ShowPartyExpBarPhase, TrainerVictoryPhase } from "#app/phases"; +import { BattleEndPhase, EggLapsePhase, ExpPhase, GameOverPhase, ModifierRewardPhase, SelectModifierPhase, ShowPartyExpBarPhase, TrainerVictoryPhase } from "#app/phases"; import { MysteryEncounterBattlePhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phase"; import * as Utils from "../../../utils"; import { isNullOrUndefined } from "#app/utils"; @@ -521,7 +521,11 @@ export function leaveEncounterWithoutBattle(scene: BattleScene, addHealPhase: bo } export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase: boolean = false) { - if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.SAFARI_BATTLE) { + const allowedPkm = scene.getParty().filter((pkm) => pkm.isAllowedInBattle()); + if (allowedPkm.length === 0) { + scene.clearPhaseQueue(); + scene.unshiftPhase(new GameOverPhase(scene)); + } else if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.SAFARI_BATTLE) { scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase)); } else if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.NO_BATTLE) { scene.pushPhase(new EggLapsePhase(scene)); From 6451eb8049b521f8d0a8bc1eb142fc18d336f3cc Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 09:54:02 -0700 Subject: [PATCH 50/69] lost-at-sea enc.: add `fainted` msg display --- .../encounters/lost-at-sea-encounter.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index d32d9025346..d06aaff5be8 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -1,8 +1,10 @@ import { Moves } from "#app/enums/moves"; +import { getPokemonNameWithAffix } from "#app/messages"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import { showEncounterText } from "../utils/encounter-dialogue-utils"; import { applyDamageToPokemon, leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter-phase-utils"; const OPTION_1_REQUIRED_MOVE = Moves.SURF; @@ -96,18 +98,20 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with ], }, async (scene: BattleScene) => { + const { mysteryEncounter } = scene.currentBattle; const allowedPokemon = scene.getParty().filter((p) => p.isAllowedInBattle()); - allowedPokemon.forEach((pkm) => { + for (const pkm of allowedPokemon) { const percentage = DAMAGE_PERCENTAGE / 100; const damage = Math.floor(pkm.getMaxHp() * percentage); applyDamageToPokemon(pkm, damage); + if (pkm.isFainted()) { - scene.currentBattle.mysteryEncounter.dialogue.outro.push({ - text: `${pkm.name} fainted!`, - }); + mysteryEncounter.setDialogueToken("pokemonNameWithAffix", getPokemonNameWithAffix(pkm)); + await showEncounterText(scene, "battle:fainted"); } - }); + } + leaveEncounterWithoutBattle(scene); return true; From a862dabcd1f38dbda0f9e7cd132b0a84baf2d283 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 10:10:40 -0700 Subject: [PATCH 51/69] optimize handleMysteryEncounterVictory --- src/data/mystery-encounters/utils/encounter-phase-utils.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 76f530cf0db..3f600a0f389 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -522,10 +522,14 @@ export function leaveEncounterWithoutBattle(scene: BattleScene, addHealPhase: bo export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase: boolean = false) { const allowedPkm = scene.getParty().filter((pkm) => pkm.isAllowedInBattle()); + if (allowedPkm.length === 0) { scene.clearPhaseQueue(); scene.unshiftPhase(new GameOverPhase(scene)); - } else if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.SAFARI_BATTLE) { + return; + } + + if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.SAFARI_BATTLE) { scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase)); } else if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.NO_BATTLE) { scene.pushPhase(new EggLapsePhase(scene)); From 78cbc226930efd411db4942a6d79a4b0216e2ec8 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 10:11:34 -0700 Subject: [PATCH 52/69] myst. chest enc.: remove manual game-over check --- .../encounters/mysterious-chest-encounter.ts | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 7773e03501b..419179b4ecc 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -1,10 +1,10 @@ +import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { leaveEncounterWithoutBattle, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { ModifierTier } from "#app/modifier/modifier-tier"; -import { GameOverPhase } from "#app/phases"; -import { randSeedInt } from "#app/utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; import IMysteryEncounter, { @@ -12,8 +12,6 @@ import IMysteryEncounter, { MysteryEncounterTier, } from "../mystery-encounter"; import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; export const MysteriousChestEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( @@ -62,7 +60,8 @@ export const MysteriousChestEncounter: IMysteryEncounter = }) .withOptionPhase(async (scene: BattleScene) => { // Open the chest - const roll = randSeedInt(100); + // const roll = randSeedInt(100); + const roll = 0; if (roll > 60) { // Choose between 2 COMMON / 2 GREAT tier items (40%) setEncounterRewards(scene, { @@ -117,13 +116,13 @@ export const MysteriousChestEncounter: IMysteryEncounter = // Does this synchronously so that game over doesn't happen over result message await showEncounterText(scene, "mysteryEncounter:mysterious_chest_option_1_bad_result") .then(() => { - if (scene.getParty().filter((p) => p.isAllowedInBattle()).length === 0) { - // All pokemon fainted, game over - scene.clearPhaseQueue(); - scene.unshiftPhase(new GameOverPhase(scene)); - } else { - leaveEncounterWithoutBattle(scene); - } + // if (scene.getParty().filter((p) => p.isAllowedInBattle()).length === 0) { + // // All pokemon fainted, game over + // scene.clearPhaseQueue(); + // scene.unshiftPhase(new GameOverPhase(scene)); + // } else { + leaveEncounterWithoutBattle(scene); + // } }); } }) From ad259c9e8f7aae9416bb50781d3abbea7e10cae1 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 10:36:09 -0700 Subject: [PATCH 53/69] MysteriousChestEncounter: revert debugging change (whops) --- .../encounters/mysterious-chest-encounter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 419179b4ecc..5842ab407a3 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -5,6 +5,7 @@ import { } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { ModifierTier } from "#app/modifier/modifier-tier"; +import { randSeedInt } from "#app/utils.js"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; import IMysteryEncounter, { @@ -60,8 +61,7 @@ export const MysteriousChestEncounter: IMysteryEncounter = }) .withOptionPhase(async (scene: BattleScene) => { // Open the chest - // const roll = randSeedInt(100); - const roll = 0; + const roll = randSeedInt(100); if (roll > 60) { // Choose between 2 COMMON / 2 GREAT tier items (40%) setEncounterRewards(scene, { From ebd8eedd1e4d8318fda201379f3e7b98b1953cb0 Mon Sep 17 00:00:00 2001 From: flx-sta <50131232+flx-sta@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:55:14 -0700 Subject: [PATCH 54/69] Update src/locales/en/mystery-encounters/lost-at-sea.ts Co-authored-by: Asdar --- src/locales/en/mystery-encounters/lost-at-sea.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/en/mystery-encounters/lost-at-sea.ts b/src/locales/en/mystery-encounters/lost-at-sea.ts index 35d86e44d04..7e44c02e6be 100644 --- a/src/locales/en/mystery-encounters/lost-at-sea.ts +++ b/src/locales/en/mystery-encounters/lost-at-sea.ts @@ -24,7 +24,7 @@ export const lostAtSea = { label: "Wander aimlessly", tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", selected: `You float about in the boat, steering it aimlessly until you finally get back on track. - $You and your Pokémon get very fatigued during the whole ordeal`, + $You and your Pokémon get very fatigued during the whole ordeal.`, }, }, outro: "You are back on track." From 36dd3798e67ae18a2a3e467fee44acd60ca98ac1 Mon Sep 17 00:00:00 2001 From: flx-sta <50131232+flx-sta@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:55:29 -0700 Subject: [PATCH 55/69] Update src/locales/en/mystery-encounters/lost-at-sea.ts Co-authored-by: Asdar --- src/locales/en/mystery-encounters/lost-at-sea.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/en/mystery-encounters/lost-at-sea.ts b/src/locales/en/mystery-encounters/lost-at-sea.ts index 7e44c02e6be..71743afb2db 100644 --- a/src/locales/en/mystery-encounters/lost-at-sea.ts +++ b/src/locales/en/mystery-encounters/lost-at-sea.ts @@ -7,7 +7,7 @@ export const lostAtSea = { 1: { label: "{{option1PrimaryName}} can help", label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "{{option1PrimaryName}} saves you.\n{{option1PrimaryName}} gains some EXP.", + tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", selected: "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", From 2b47cd2a56f879c950302200c03f674e1c329e25 Mon Sep 17 00:00:00 2001 From: flx-sta <50131232+flx-sta@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:55:43 -0700 Subject: [PATCH 56/69] Update src/locales/en/mystery-encounters/lost-at-sea.ts Co-authored-by: Asdar --- src/locales/en/mystery-encounters/lost-at-sea.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/en/mystery-encounters/lost-at-sea.ts b/src/locales/en/mystery-encounters/lost-at-sea.ts index 71743afb2db..ec016c3e169 100644 --- a/src/locales/en/mystery-encounters/lost-at-sea.ts +++ b/src/locales/en/mystery-encounters/lost-at-sea.ts @@ -1,7 +1,7 @@ export const lostAtSea = { intro: "Wandering aimlessly, you effectively get nowhere.", title: "Lost at sea", - description: "The sea is turbulent in this area and you seem to be running out of fuel as well.\nThis is bad. Is there a way out of the situation?", + description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", query: "What will you do?", option: { 1: { From de4d0f7d7955c627ac4e4d66e1ad355fc51f50e1 Mon Sep 17 00:00:00 2001 From: flx-sta <50131232+flx-sta@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:57:26 -0700 Subject: [PATCH 57/69] Update src/locales/en/mystery-encounters/lost-at-sea.ts Co-authored-by: Asdar --- src/locales/en/mystery-encounters/lost-at-sea.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/en/mystery-encounters/lost-at-sea.ts b/src/locales/en/mystery-encounters/lost-at-sea.ts index ec016c3e169..6e37168a255 100644 --- a/src/locales/en/mystery-encounters/lost-at-sea.ts +++ b/src/locales/en/mystery-encounters/lost-at-sea.ts @@ -15,7 +15,7 @@ export const lostAtSea = { 2: { label: "{{option2PrimaryName}} can help", label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "{{option2PrimaryName}} saves you.\n{{option2PrimaryName}} gains some EXP.", + tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", selected: "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", From 35ede48153a721e385fa00b11859b15eb6dba1f6 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 14:45:12 -0700 Subject: [PATCH 58/69] encounter-phase-utils: update applyHpChangeToPokemon method family - now automatically queues a battle:fainted msg on KO --- .../utils/encounter-phase-utils.ts | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 3f600a0f389..17090467da9 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -1,30 +1,31 @@ -import i18next from "i18next"; import { BattleType } from "#app/battle"; -import BattleScene from "../../../battle-scene"; -import PokemonSpecies from "../../pokemon-species"; -import { MysteryEncounterVariant } from "../mystery-encounter"; -import { Status, StatusEffect } from "../../status-effect"; -import { TrainerConfig, trainerConfigs, TrainerSlot } from "../../trainer-config"; +import { biomeLinks } from "#app/data/biomes"; +import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; +import { WIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters"; +import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import Pokemon, { FieldPosition, PlayerPokemon } from "#app/field/pokemon"; -import Trainer, { TrainerVariant } from "../../../field/trainer"; +import { getPokemonNameWithAffix } from "#app/messages"; import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier"; import { CustomModifierSettings, getModifierPoolForType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, PokemonHeldItemModifierType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; +import * as Overrides from "#app/overrides"; import { BattleEndPhase, EggLapsePhase, ExpPhase, GameOverPhase, ModifierRewardPhase, SelectModifierPhase, ShowPartyExpBarPhase, TrainerVictoryPhase } from "#app/phases"; import { MysteryEncounterBattlePhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phase"; -import * as Utils from "../../../utils"; -import { isNullOrUndefined } from "#app/utils"; -import { TrainerType } from "#enums/trainer-type"; -import { BattlerTagType } from "#enums/battler-tag-type"; import PokemonData from "#app/system/pokemon-data"; -import { Biome } from "#enums/biome"; -import { biomeLinks } from "#app/data/biomes"; -import { Mode } from "#app/ui/ui"; -import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler"; import { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import { WIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters"; -import * as Overrides from "#app/overrides"; -import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; -import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler"; +import { Mode } from "#app/ui/ui"; +import { isNullOrUndefined } from "#app/utils"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { Biome } from "#enums/biome"; +import { TrainerType } from "#enums/trainer-type"; +import i18next from "i18next"; +import BattleScene from "../../../battle-scene"; +import Trainer, { TrainerVariant } from "../../../field/trainer"; +import * as Utils from "../../../utils"; +import PokemonSpecies from "../../pokemon-species"; +import { Status, StatusEffect } from "../../status-effect"; +import { TrainerConfig, trainerConfigs, TrainerSlot } from "../../trainer-config"; +import { MysteryEncounterVariant } from "../mystery-encounter"; export class EnemyPokemonConfig { species: PokemonSpecies; @@ -699,16 +700,17 @@ export function koPlayerPokemon(pokemon: PlayerPokemon) { * Handles applying hp changes to a player pokemon. * Takes care of not going below `0`, above max-hp, adding `FNT` status correctly and updating the pokemon info. * TODO: handle special cases like wonder-guard/ninjask - * + * @param scene the battle scene * @param pokemon the player pokemon to apply the hp change to - * @param damage the hp change amount. Positive for heal. Negative for damage + * @param value the hp change amount. Positive for heal. Negative for damage * */ -function applyHpChangeToPokemon(pokemon: PlayerPokemon, value: number) { +function applyHpChangeToPokemon(scene: BattleScene, pokemon: PlayerPokemon, value: number) { const hpChange = Math.round(pokemon.hp + value); const nextHp = Math.max(Math.min(hpChange, pokemon.getMaxHp()), 0); if (nextHp === 0) { koPlayerPokemon(pokemon); + queueEncounterMessage(scene, i18next.t("battle:fainted", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); } else { pokemon.hp = nextHp; } @@ -716,30 +718,30 @@ function applyHpChangeToPokemon(pokemon: PlayerPokemon, value: number) { /** * Handles applying damage to a player pokemon - * + * @param scene the battle scene * @param pokemon the player pokemon to apply damage to * @param damage the amount of damage to apply * @see {@linkcode applyHpChangeToPokemon} */ -export function applyDamageToPokemon(pokemon: PlayerPokemon, damage: number) { +export function applyDamageToPokemon(scene: BattleScene, pokemon: PlayerPokemon, damage: number) { if (damage <= 0) { console.warn("Healing pokemon with `applyDamageToPokemon` is not recommended! Please use `applyHealToPokemon` instead."); } - applyHpChangeToPokemon(pokemon, -damage); + applyHpChangeToPokemon(scene, pokemon, -damage); } /** * Handles applying heal to a player pokemon - * + * @param scene the battle scene * @param pokemon the player pokemon to apply heal to * @param heal the amount of heal to apply * @see {@linkcode applyHpChangeToPokemon} */ -export function applyHealToPokemon(pokemon: PlayerPokemon, heal: number) { +export function applyHealToPokemon(scene: BattleScene, pokemon: PlayerPokemon, heal: number) { if (heal <= 0) { console.warn("Damaging pokemong with `applyHealToPokemon` is not recommended! Please use `applyDamageToPokemon` instead."); } - applyHpChangeToPokemon(pokemon, heal); + applyHpChangeToPokemon(scene, pokemon, heal); } From ada6eee88463cef76be750c9ed4b8a9945d2407b Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 14:45:49 -0700 Subject: [PATCH 59/69] lost-at-sea-encounter: remove fainted msg code this is automatically handled in the encounter-phase-utils now --- .../encounters/lost-at-sea-encounter.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index d06aaff5be8..49d670a29d5 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -1,10 +1,8 @@ import { Moves } from "#app/enums/moves"; -import { getPokemonNameWithAffix } from "#app/messages"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -import { showEncounterText } from "../utils/encounter-dialogue-utils"; import { applyDamageToPokemon, leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter-phase-utils"; const OPTION_1_REQUIRED_MOVE = Moves.SURF; @@ -98,18 +96,12 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with ], }, async (scene: BattleScene) => { - const { mysteryEncounter } = scene.currentBattle; const allowedPokemon = scene.getParty().filter((p) => p.isAllowedInBattle()); for (const pkm of allowedPokemon) { const percentage = DAMAGE_PERCENTAGE / 100; const damage = Math.floor(pkm.getMaxHp() * percentage); - applyDamageToPokemon(pkm, damage); - - if (pkm.isFainted()) { - mysteryEncounter.setDialogueToken("pokemonNameWithAffix", getPokemonNameWithAffix(pkm)); - await showEncounterText(scene, "battle:fainted"); - } + applyDamageToPokemon(scene, pkm, damage); } leaveEncounterWithoutBattle(scene); From fe2888be4b28e873c182f9aef55c33918428f88c Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 15:08:08 -0700 Subject: [PATCH 60/69] mystery encounters: resolve review comments: Lost at Sea: -fix typo in handlePokemonGuidingYouPhase function Mysterious Chest: - remove obsolete commented code mystery-encounter.ts - remove unused `onDone` field from MysteryEncounterBuilder --- .../encounters/lost-at-sea-encounter.ts | 13 +++++++------ .../encounters/mysterious-chest-encounter.ts | 13 +++---------- src/data/mystery-encounters/mystery-encounter.ts | 1 - 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 49d670a29d5..9e25e003e1b 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -1,4 +1,6 @@ +import { getPokemonSpecies } from "#app/data/pokemon-species.js"; import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species.js"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; @@ -62,7 +64,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with }, ], }) - .withOptionPhase(async (scene: BattleScene) => handlePokemongGuidingYouPhase(scene)) + .withOptionPhase(async (scene: BattleScene) => handlePokemonGuidingYouPhase(scene)) .build() ) .withOption( @@ -81,7 +83,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with }, ], }) - .withOptionPhase(async (scene: BattleScene) => handlePokemongGuidingYouPhase(scene)) + .withOptionPhase(async (scene: BattleScene) => handlePokemonGuidingYouPhase(scene)) .build() ) .withSimpleOption( @@ -122,13 +124,12 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with * @param scene Battle scene * @param guidePokemon pokemon choosen as a guide */ -function handlePokemongGuidingYouPhase(scene: BattleScene) { - /** Base EXP value for guiding pokemon. Currently Lapras base-value */ - const baseExpValue: number = 187; +function handlePokemonGuidingYouPhase(scene: BattleScene) { + const laprasSpecies = getPokemonSpecies(Species.LAPRAS); const { mysteryEncounter } = scene.currentBattle; if (mysteryEncounter.selectedOption) { - setEncounterExp(scene, mysteryEncounter.selectedOption.primaryPokemon.id, baseExpValue, true); + setEncounterExp(scene, mysteryEncounter.selectedOption.primaryPokemon.id, laprasSpecies.baseExp, true); } else { console.warn("Lost at sea: No guide pokemon found but pokemon guides player. huh!?"); } diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 5842ab407a3..9a0a42af2c8 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -114,16 +114,9 @@ export const MysteriousChestEncounter: IMysteryEncounter = scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", highestLevelPokemon.name); // Show which Pokemon was KOed, then leave encounter with no rewards // Does this synchronously so that game over doesn't happen over result message - await showEncounterText(scene, "mysteryEncounter:mysterious_chest_option_1_bad_result") - .then(() => { - // if (scene.getParty().filter((p) => p.isAllowedInBattle()).length === 0) { - // // All pokemon fainted, game over - // scene.clearPhaseQueue(); - // scene.unshiftPhase(new GameOverPhase(scene)); - // } else { - leaveEncounterWithoutBattle(scene); - // } - }); + await showEncounterText(scene, "mysteryEncounter:mysterious_chest_option_1_bad_result").then(() => { + leaveEncounterWithoutBattle(scene); + }); } }) .build() diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 983be1fdb2a..c6fc8c1ca66 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -363,7 +363,6 @@ export class MysteryEncounterBuilder implements Partial { doEncounterExp?: (scene: BattleScene) => boolean; doEncounterRewards?: (scene: BattleScene) => boolean; onInit?: (scene: BattleScene) => boolean; - onDone?: (scene: BattleScene) => boolean; hideBattleIntroMessage?: boolean; hideIntroVisuals?: boolean; enemyPartyConfigs?: EnemyPartyConfig[] = []; From 918320079aa719eeaa7f7cb060702613e56dad6e Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 15:09:17 -0700 Subject: [PATCH 61/69] fix typo in CanLearnMoveRequirementOptions --- src/data/mystery-encounters/mystery-encounter-option.ts | 6 +++--- .../requirements/can-learn-move-requirement.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index b02cfd351a4..c7feb2688c3 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -5,7 +5,7 @@ import BattleScene from "../../battle-scene"; import * as Utils from "../../utils"; import { Type } from "../type"; import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "./mystery-encounter-requirements"; -import { CanLearnMoveRequirement, CanlearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement"; +import { CanLearnMoveRequirement, CanLearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement"; export enum EncounterOptionMode { /** Default style */ @@ -202,10 +202,10 @@ export class MysteryEncounterOptionBuilder implements Partial Date: Wed, 17 Jul 2024 15:10:50 -0700 Subject: [PATCH 62/69] remove redundance from Pokemon.isAllowedInBattle() --- src/field/pokemon.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 279ffeccc42..3221aac18aa 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -274,8 +274,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns {boolean} True if pokemon is allowed in battle */ isAllowedInBattle(): boolean { - const challengeAllowed = new Utils.BooleanHolder(true); - applyChallenges(this.scene.gameMode, ChallengeType.POKEMON_IN_BATTLE, this, challengeAllowed); return !this.isFainted() && this.isAllowed(); } From 96d0df42fc3530ad2dba7e3db95f2987e8446f5f Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 15:11:17 -0700 Subject: [PATCH 63/69] chore: jsdoc formatting --- src/field/pokemon.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 3221aac18aa..ebae5fe8122 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -278,10 +278,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Check if this pokemon is allowed (no challenge exclusion) - * This is frequently a better alternative to {@link isFainted} - * @returns {boolean} True if pokemon is allowed in battle - */ + * Check if this pokemon is allowed (no challenge exclusion) + * This is frequently a better alternative to {@link isFainted} + * @returns {boolean} True if pokemon is allowed in battle + */ isAllowed(): boolean { const challengeAllowed = new Utils.BooleanHolder(true); applyChallenges(this.scene.gameMode, ChallengeType.POKEMON_IN_BATTLE, this, challengeAllowed); From 6e97bca0ad3abc18ac46a2a261e081153a182ccf Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 15:31:12 -0700 Subject: [PATCH 64/69] fix lost-at-sea tests --- .../encounters/lost-at-sea-encounter.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 928da9760d4..1b2bc157772 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 @@ -28,9 +28,9 @@ describe("Lost at Sea - Mystery Encounter", () => { game = new GameManager(phaserGame); game.override.mysteryEncounterChance(100); game.override.startingBiome(Biome.SEA); - vi.spyOn(MysteryEncounters, "allMysteryEncounters", "get").mockReturnValue({ - [MysteryEncounterType.LOST_AT_SEA]: LostAtSeaEncounter, - }); + vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( + new Map([[Biome.SEA, [MysteryEncounterType.LOST_AT_SEA]]]) + ); }); afterEach(() => { From 3fd79984cd3c6d85a0585db08babc348a0739dc6 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 16:06:08 -0700 Subject: [PATCH 65/69] add fallback for biomeMysteryEncounters if empty --- src/battle-scene.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index c0a3f0b090a..6cde5929308 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -2678,7 +2678,7 @@ export default class BattleScene extends SceneBase { let availableEncounters: IMysteryEncounter[] = []; // New encounter will never be the same as the most recent encounter const previousEncounter = this.mysteryEncounterData.encounteredEvents?.length > 0 ? this.mysteryEncounterData.encounteredEvents[this.mysteryEncounterData.encounteredEvents.length - 1][0] : null; - const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType); + const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType) ?? []; // If no valid encounters exist at tier, checks next tier down, continuing until there are some encounters available while (availableEncounters.length === 0 && tier >= 0) { availableEncounters = biomeMysteryEncounters From 41cacde623f5f25c8f7bb9df86d4206f44c5ccb8 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 16:23:31 -0700 Subject: [PATCH 66/69] lost-at-sea-encounter: fix and extend tests --- .../encounters/lost-at-sea-encounter.test.ts | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) 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 1b2bc157772..03d7eb90820 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 @@ -3,6 +3,7 @@ import { LostAtSeaEncounter } from "#app/data/mystery-encounters/encounters/lost import { EncounterOptionMode } from "#app/data/mystery-encounters/mystery-encounter-option"; import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { getPokemonSpecies } from "#app/data/pokemon-species.js"; import { Biome } from "#app/enums/biome"; import { Moves } from "#app/enums/moves"; import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; @@ -15,6 +16,8 @@ import { runSelectMysteryEncounterOption } from "../encounterTestUtils"; const namepsace = "mysteryEncounter:lostAtSea"; /** Blastoise for surf. Pidgeot for fly. Abra for none. */ const defaultParty = [Species.BLASTOISE, Species.PIDGEOT, Species.ABRA]; +const defaultBiome = Biome.SEA; +const defaultWave = 33; describe("Lost at Sea - Mystery Encounter", () => { let phaserGame: Phaser.Game; @@ -24,10 +27,14 @@ describe("Lost at Sea - Mystery Encounter", () => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS }); }); - beforeEach(() => { + beforeEach(async () => { game = new GameManager(phaserGame); game.override.mysteryEncounterChance(100); - game.override.startingBiome(Biome.SEA); + game.override.startingBiome(defaultBiome); + game.override.startingWave(defaultWave); + vi.spyOn(MysteryEncounters, "allMysteryEncounters", "get").mockReturnValue({ + [MysteryEncounterType.LOST_AT_SEA]: LostAtSeaEncounter, + }); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([[Biome.SEA, [MysteryEncounterType.LOST_AT_SEA]]]) ); @@ -38,7 +45,8 @@ describe("Lost at Sea - Mystery Encounter", () => { }); it("should have the correct properties", async () => { - await game.runToMysteryEncounter([Species.ABRA]); + await workaround_reInitSceneWithOverrides(game); + await game.runToMysteryEncounter(defaultParty); expect(LostAtSeaEncounter.encounterType).toBe(MysteryEncounterType.LOST_AT_SEA); expect(LostAtSeaEncounter.dialogue).toBeDefined(); @@ -50,7 +58,12 @@ describe("Lost at Sea - Mystery Encounter", () => { }); it("should not run outside of sea biome", async () => { - // TODO: a little tricky + game.override.startingBiome(Biome.MOUNTAIN); + + await workaround_reInitSceneWithOverrides(game); + + //expect the `TypeError: Cannot read properties of undefined (reading 'introVisuals')` error + await expect(() => game.runToMysteryEncounter()).rejects.toThrowError(/introVisuals/); }); it("should not run below wave 11", async () => { @@ -103,9 +116,7 @@ describe("Lost at Sea - Mystery Encounter", () => { }); it("should award exp to surfable PKM (Blastoise)", async () => { - const laprasBaseExp = 187; - const wave = 33; - game.override.startingWave(wave); + const laprasSpecies = getPokemonSpecies(Species.LAPRAS); await workaround_reInitSceneWithOverrides(game); await game.runToMysteryEncounter(defaultParty); @@ -115,7 +126,7 @@ describe("Lost at Sea - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 2); - expect(blastoise.exp).toBe(expBefore + laprasBaseExp * wave); + expect(blastoise.exp).toBe(expBefore + laprasSpecies.baseExp * defaultWave); }); it("should leave encounter without battle", async () => { From 3e6183cd5066caa0872c59ead15068d7d710ea85 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 16:24:34 -0700 Subject: [PATCH 67/69] move "battle:fainted" into `koPlayerPokemon` --- src/data/mystery-encounters/utils/encounter-phase-utils.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 17090467da9..ffaf11b8219 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -688,12 +688,14 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n /** * Takes care of handling player pokemon KO (with all its side effects) * + * @param scene the battle scene * @param pokemon the player pokemon to KO */ -export function koPlayerPokemon(pokemon: PlayerPokemon) { +export function koPlayerPokemon(scene: BattleScene, pokemon: PlayerPokemon) { pokemon.hp = 0; pokemon.trySetStatus(StatusEffect.FAINT); pokemon.updateInfo(); + queueEncounterMessage(scene, i18next.t("battle:fainted", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); } /** @@ -709,8 +711,7 @@ function applyHpChangeToPokemon(scene: BattleScene, pokemon: PlayerPokemon, valu const hpChange = Math.round(pokemon.hp + value); const nextHp = Math.max(Math.min(hpChange, pokemon.getMaxHp()), 0); if (nextHp === 0) { - koPlayerPokemon(pokemon); - queueEncounterMessage(scene, i18next.t("battle:fainted", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + koPlayerPokemon(scene, pokemon); } else { pokemon.hp = nextHp; } From 6cbf73281c38f15b7cba547a4be7dff7371db91d Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 16:38:25 -0700 Subject: [PATCH 68/69] add retries to quick-draw tests --- src/test/abilities/quick_draw.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/abilities/quick_draw.test.ts b/src/test/abilities/quick_draw.test.ts index 884b108381d..aa66d7e052f 100644 --- a/src/test/abilities/quick_draw.test.ts +++ b/src/test/abilities/quick_draw.test.ts @@ -52,7 +52,7 @@ describe("Abilities - Quick Draw", () => { expect(pokemon.battleData.abilitiesApplied).contain(Abilities.QUICK_DRAW); }, 20000); - test("does not triggered by non damage moves", async () => { + test("does not triggered by non damage moves", { timeout: 20000, retry: 5 }, async () => { await game.startBattle([Species.SLOWBRO]); const pokemon = game.scene.getPlayerPokemon(); @@ -67,7 +67,7 @@ describe("Abilities - Quick Draw", () => { expect(pokemon.isFainted()).toBe(true); expect(enemy.isFainted()).toBe(false); expect(pokemon.battleData.abilitiesApplied).not.contain(Abilities.QUICK_DRAW); - }, 20000); + }); test("does not increase priority", async () => { vi.spyOn(Overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue(Array(4).fill(Moves.EXTREME_SPEED)); From a98dbfdc93a7f5564eeb961fa9a88b021aadeb12 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Wed, 17 Jul 2024 17:05:08 -0700 Subject: [PATCH 69/69] fix lost-at-sea-encounter tests --- .../encounters/lost-at-sea-encounter.test.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) 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 03d7eb90820..3b8017fa4c6 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 @@ -32,11 +32,11 @@ describe("Lost at Sea - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingBiome(defaultBiome); game.override.startingWave(defaultWave); - vi.spyOn(MysteryEncounters, "allMysteryEncounters", "get").mockReturnValue({ - [MysteryEncounterType.LOST_AT_SEA]: LostAtSeaEncounter, - }); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( - new Map([[Biome.SEA, [MysteryEncounterType.LOST_AT_SEA]]]) + new Map([ + [Biome.SEA, [MysteryEncounterType.LOST_AT_SEA]], + [Biome.MOUNTAIN, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]], + ]) ); }); @@ -57,13 +57,12 @@ describe("Lost at Sea - Mystery Encounter", () => { expect(LostAtSeaEncounter.options.length).toBe(3); }); - it("should not run outside of sea biome", async () => { + it("should not spawn outside of sea biome", async () => { game.override.startingBiome(Biome.MOUNTAIN); - await workaround_reInitSceneWithOverrides(game); + await game.runToMysteryEncounter(); - //expect the `TypeError: Cannot read properties of undefined (reading 'introVisuals')` error - await expect(() => game.runToMysteryEncounter()).rejects.toThrowError(/introVisuals/); + expect(game.scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.LOST_AT_SEA); }); it("should not run below wave 11", async () => {