From 810c50cda27b5359add24eabc752beff37df0ffb Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 11 Jul 2024 11:40:07 -0400 Subject: [PATCH 01/29] new Field Trip encounter and add EXP gains to other encounters --- public/images/mystery-encounters/teacher.json | 41 ++++ public/images/mystery-encounters/teacher.png | Bin 0 -> 727 bytes .../dialogue/field-trip-dialogue.ts | 50 ++++ .../dialogue/shady-vitamin-dealer.ts | 4 +- .../encounters/department-store-sale.ts | 3 +- .../encounters/field-trip-encounter.ts | 220 ++++++++++++++++++ .../encounters/shady-vitamin-dealer.ts | 97 ++++++-- .../encounters/sleeping-snorlax.ts | 23 +- .../mystery-encounter-dialogue.ts | 2 + .../mystery-encounter-option.ts | 24 +- .../mystery-encounter-utils.ts | 7 +- .../mystery-encounters/mystery-encounter.ts | 7 +- .../mystery-encounters/mystery-encounters.ts | 2 + src/enums/mystery-encounter-type.ts | 3 +- src/locales/en/mystery-encounter.ts | 35 ++- src/overrides.ts | 7 +- src/phases.ts | 1 + src/ui/mystery-encounter-ui-handler.ts | 33 ++- 18 files changed, 495 insertions(+), 64 deletions(-) create mode 100644 public/images/mystery-encounters/teacher.json create mode 100644 public/images/mystery-encounters/teacher.png create mode 100644 src/data/mystery-encounters/dialogue/field-trip-dialogue.ts create mode 100644 src/data/mystery-encounters/encounters/field-trip-encounter.ts diff --git a/public/images/mystery-encounters/teacher.json b/public/images/mystery-encounters/teacher.json new file mode 100644 index 00000000000..881386d7228 --- /dev/null +++ b/public/images/mystery-encounters/teacher.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "teacher.png", + "format": "RGBA8888", + "size": { + "w": 43, + "h": 74 + }, + "scale": 1, + "frames": [ + { + "filename": "Spr_HGSS_Teacher.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 19, + "y": 8, + "w": 41, + "h": 72 + }, + "frame": { + "x": 1, + "y": 1, + "w": 41, + "h": 72 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:506e5a4ce79c134a7b4af90a90aef244:1b81d3d84bf12cedc419805eaff82548:59bc5dd000b5e72588320b473e31c312$" + } +} diff --git a/public/images/mystery-encounters/teacher.png b/public/images/mystery-encounters/teacher.png new file mode 100644 index 0000000000000000000000000000000000000000..b4332bc0032ccd93ec4319f4a1172e5bb676bcf7 GIT binary patch literal 727 zcmV;|0x127P)z5oCJY(`#A00001 zbW%=J06^y0W&i*H32;bRa{vGf6951U69E94oEQKA0zgSbK~zY`z1EGAqaY9jU>gJk zg1rCRZqI<4Tq2sfy`yS38~wH!ej=Hs|JY`2_vErH{B^14O-h?Fq|Gc_!)50uSbD2h`;Pajl)9EFxrdi0=*R8iKopcYvc zS-o>xN|GXCO>-*qD0c#T*Miq|Nk#Y2`P#*euBG4;jWIq2%h=?nQQ8o~LF?mzBe+!z zbV-}@hT}upjzL`_6+~L+XhXG)u8$a>?37tj%Kbh2cQqWAvmf$qYlvC7_G`t~PHEE` z<4#Sep+;S2uj6vL)@+Z_a6Ek`jQ2G+T8#yBpJKPsl-Q_Yifo`%s{w-M+Lz@*&NZkd ztsa&%vkzF8bAGW?U`O8cSg}5n8V|<)K?MhIB#8A^I9TwF;15>r@RrwCwO+3v18=*h zV`#XAwR*vWf4x~WYlw|Gf?@N!)nLP2K*`awXT_YLlVD!dR7;s9nem^Bn2Zj>i)9I# zM002ov JPDHLkV1m$-S=|5t literal 0 HcmV?d00001 diff --git a/src/data/mystery-encounters/dialogue/field-trip-dialogue.ts b/src/data/mystery-encounters/dialogue/field-trip-dialogue.ts new file mode 100644 index 00000000000..dca3f48f5bf --- /dev/null +++ b/src/data/mystery-encounters/dialogue/field-trip-dialogue.ts @@ -0,0 +1,50 @@ +import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; + +export const FieldTripDialogue: MysteryEncounterDialogue = { + intro: [ + { + text: "mysteryEncounter:field_trip_intro_message" + }, + { + text: "mysteryEncounter:field_trip_intro_dialogue", + speaker: "mysteryEncounter:field_trip_speaker" + } + ], + encounterOptionsDialogue: { + title: "mysteryEncounter:field_trip_title", + description: "mysteryEncounter:field_trip_description", + query: "mysteryEncounter:field_trip_query", + options: [ + { + buttonLabel: "mysteryEncounter:field_trip_option_1_label", + buttonTooltip: "mysteryEncounter:field_trip_option_1_tooltip", + secondOptionPrompt: "mysteryEncounter:field_trip_second_option_prompt", + selected: [ + { + text: "mysteryEncounter:field_trip_option_selected" + } + ] + }, + { + buttonLabel: "mysteryEncounter:field_trip_option_2_label", + buttonTooltip: "mysteryEncounter:field_trip_option_2_tooltip", + secondOptionPrompt: "mysteryEncounter:field_trip_second_option_prompt", + selected: [ + { + text: "mysteryEncounter:field_trip_option_selected" + } + ] + }, + { + buttonLabel: "mysteryEncounter:field_trip_option_3_label", + buttonTooltip: "mysteryEncounter:field_trip_option_3_tooltip", + secondOptionPrompt: "mysteryEncounter:field_trip_second_option_prompt", + selected: [ + { + text: "mysteryEncounter:field_trip_option_selected" + } + ] + } + ] + } +}; diff --git a/src/data/mystery-encounters/dialogue/shady-vitamin-dealer.ts b/src/data/mystery-encounters/dialogue/shady-vitamin-dealer.ts index cb0c5ac130e..52b9741caf8 100644 --- a/src/data/mystery-encounters/dialogue/shady-vitamin-dealer.ts +++ b/src/data/mystery-encounters/dialogue/shady-vitamin-dealer.ts @@ -21,7 +21,7 @@ export const ShadyVitaminDealerDialogue: MysteryEncounterDialogue = { selected: [ { text: "mysteryEncounter:shady_vitamin_dealer_option_selected" - }, + } ] }, { @@ -30,7 +30,7 @@ export const ShadyVitaminDealerDialogue: MysteryEncounterDialogue = { selected: [ { text: "mysteryEncounter:shady_vitamin_dealer_option_selected" - }, + } ] }, { diff --git a/src/data/mystery-encounters/encounters/department-store-sale.ts b/src/data/mystery-encounters/encounters/department-store-sale.ts index 1d44c2b3af8..19269a774f3 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale.ts @@ -1,5 +1,5 @@ import { - leaveEncounterWithoutBattle, setEncounterExp, + leaveEncounterWithoutBattle, setEncounterRewards, } from "#app/data/mystery-encounters/mystery-encounter-utils"; import { modifierTypes } from "#app/modifier/modifier-type"; @@ -46,7 +46,6 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu i++; } - setEncounterExp(scene, scene.getParty().map(p => p.id), 300); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); leaveEncounterWithoutBattle(scene); }) diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts new file mode 100644 index 00000000000..410df127b6e --- /dev/null +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -0,0 +1,220 @@ +import { generateModifierTypeOption, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards, } from "#app/data/mystery-encounters/mystery-encounter-utils"; +import { modifierTypes } from "#app/modifier/modifier-type"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import BattleScene from "../../../battle-scene"; +import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; +import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; +import { MoveCategory } from "#app/data/move"; +import { TempBattleStat } from "#app/data/temp-battle-stat"; + +export const FieldTripEncounter: MysteryEncounter = MysteryEncounterBuilder + .withEncounterType(MysteryEncounterType.FIELD_TRIP) + .withEncounterTier(MysteryEncounterTier.COMMON) + .withIntroSpriteConfigs([ + { + spriteKey: "preschooler_m", + fileRoot: "trainer", + hasShadow: true + }, + { + spriteKey: "teacher", + fileRoot: "mystery-encounters", + hasShadow: true + }, + { + spriteKey: "preschooler_f", + fileRoot: "trainer", + hasShadow: true + }, + ]) + .withHideIntroVisuals(false) + .withSceneWaveRangeRequirement(10, 180) + .withOption(new MysteryEncounterOptionBuilder() + .withPreOptionPhase(async (scene: BattleScene): Promise => { + const encounter = scene.currentBattle.mysteryEncounter; + const onPokemonSelected = (pokemon: PlayerPokemon) => { + // Return the options for Pokemon move valid for this option + return pokemon.moveset.map((move: PokemonMove) => { + const option: OptionSelectItem = { + label: move.getName(), + handler: () => { + // Pokemon and move selected + const correctMove = move.getMove().category === MoveCategory.PHYSICAL; + encounter.setDialogueToken("moveCategory", "Physical"); + if (!correctMove) { + encounter.dialogue.encounterOptionsDialogue.options[0].selected = [ + { + text: "mysteryEncounter:field_trip_option_incorrect", + speaker: "mysteryEncounter:field_trip_speaker" + }, + { + text: "mysteryEncounter:field_trip_lesson_learned", + } + ]; + setEncounterExp(scene, scene.getParty().map(p => p.id), 50); + } else { + encounter.setDialogueToken("pokeName", pokemon.name); + encounter.setDialogueToken("move", move.getName()); + encounter.dialogue.encounterOptionsDialogue.options[0].selected = [ + { + text: "mysteryEncounter:field_trip_option_selected" + } + ]; + setEncounterExp(scene, [pokemon.id], 100); + } + encounter.misc = { + correctMove: correctMove + }; + return true; + } + }; + return option; + }); + }; + + return selectPokemonForOption(scene, onPokemonSelected); + }) + .withOptionPhase(async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + if (encounter.misc.correctMove) { + const modifiers = [ + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.ATK]), + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.DEF]), + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]), + generateModifierTypeOption(scene, modifierTypes.DIRE_HIT) + ]; + + setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); + } + + leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); + }) + .build() + ) + .withOption(new MysteryEncounterOptionBuilder() + .withPreOptionPhase(async (scene: BattleScene): Promise => { + const encounter = scene.currentBattle.mysteryEncounter; + const onPokemonSelected = (pokemon: PlayerPokemon) => { + // Return the options for Pokemon move valid for this option + return pokemon.moveset.map((move: PokemonMove) => { + const option: OptionSelectItem = { + label: move.getName(), + handler: () => { + // Pokemon and move selected + const correctMove = move.getMove().category === MoveCategory.SPECIAL; + encounter.setDialogueToken("moveCategory", "Special"); + if (!correctMove) { + encounter.dialogue.encounterOptionsDialogue.options[1].selected = [ + { + text: "mysteryEncounter:field_trip_option_incorrect", + speaker: "mysteryEncounter:field_trip_speaker" + }, + { + text: "mysteryEncounter:field_trip_lesson_learned", + } + ]; + setEncounterExp(scene, scene.getParty().map(p => p.id), 50); + } else { + encounter.setDialogueToken("pokeName", pokemon.name); + encounter.setDialogueToken("move", move.getName()); + encounter.dialogue.encounterOptionsDialogue.options[1].selected = [ + { + text: "mysteryEncounter:field_trip_option_selected" + } + ]; + setEncounterExp(scene, [pokemon.id], 100); + } + encounter.misc = { + correctMove: correctMove + }; + return true; + } + }; + return option; + }); + }; + + return selectPokemonForOption(scene, onPokemonSelected); + }) + .withOptionPhase(async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + if (encounter.misc.correctMove) { + const modifiers = [ + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPATK]), + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPDEF]), + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]), + generateModifierTypeOption(scene, modifierTypes.DIRE_HIT) + ]; + + setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); + } + + leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); + }) + .build() + ) + .withOption(new MysteryEncounterOptionBuilder() + .withPreOptionPhase(async (scene: BattleScene): Promise => { + const encounter = scene.currentBattle.mysteryEncounter; + const onPokemonSelected = (pokemon: PlayerPokemon) => { + // Return the options for Pokemon move valid for this option + return pokemon.moveset.map((move: PokemonMove) => { + const option: OptionSelectItem = { + label: move.getName(), + handler: () => { + // Pokemon and move selected + const correctMove = move.getMove().category === MoveCategory.STATUS; + encounter.setDialogueToken("moveCategory", "Status"); + if (!correctMove) { + encounter.dialogue.encounterOptionsDialogue.options[2].selected = [ + { + text: "mysteryEncounter:field_trip_option_incorrect", + speaker: "mysteryEncounter:field_trip_speaker" + }, + { + text: "mysteryEncounter:field_trip_lesson_learned", + } + ]; + setEncounterExp(scene, scene.getParty().map(p => p.id), 50); + } else { + encounter.setDialogueToken("pokeName", pokemon.name); + encounter.setDialogueToken("move", move.getName()); + encounter.dialogue.encounterOptionsDialogue.options[2].selected = [ + { + text: "mysteryEncounter:field_trip_option_selected" + } + ]; + setEncounterExp(scene, [pokemon.id], 100); + } + encounter.misc = { + correctMove: correctMove + }; + return true; + } + }; + return option; + }); + }; + + return selectPokemonForOption(scene, onPokemonSelected); + }) + .withOptionPhase(async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + if (encounter.misc.correctMove) { + const modifiers = [ + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.ACC]), + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]), + generateModifierTypeOption(scene, modifierTypes.GREAT_BALL), + generateModifierTypeOption(scene, modifierTypes.IV_SCANNER) + ]; + + setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); + } + + leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); + }) + .build() + ) + .build(); diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts index d58bc44daf7..21e1b199e04 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts @@ -1,9 +1,8 @@ import { - generateModifierType, + generateModifierTypeOption, leaveEncounterWithoutBattle, queueEncounterMessage, - selectPokemonForOption, - setEncounterRewards, + selectPokemonForOption, setEncounterExp, updatePlayerMoney, } from "#app/data/mystery-encounters/mystery-encounter-utils"; import { StatusEffect } from "#app/data/status-effect"; @@ -18,6 +17,7 @@ import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { MoneyRequirement } from "../mystery-encounter-requirements"; +import i18next from "i18next"; export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBuilder .withEncounterType(MysteryEncounterType.SHADY_VITAMIN_DEALER) @@ -43,7 +43,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui .withPrimaryPokemonStatusEffectRequirement([StatusEffect.NONE]) // Pokemon must not have status .withPrimaryPokemonHealthRatioRequirement([0.34, 1]) // Pokemon must have above 1/3rd HP .withOption(new MysteryEncounterOptionBuilder() - .withSceneMoneyRequirement(0, 2) // Wave scaling multiplier of 2 for cost + .withSceneMoneyRequirement(0, 2) // Wave scaling money multiplier of 2 .withPreOptionPhase(async (scene: BattleScene): Promise => { const encounter = scene.currentBattle.mysteryEncounter; const onPokemonSelected = (pokemon: PlayerPokemon) => { @@ -51,8 +51,8 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney); // Calculate modifiers and dialogue tokens const modifiers = [ - generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER), - generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER) + generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type, + generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type ]; encounter.setDialogueToken("boost1", modifiers[0].name); encounter.setDialogueToken("boost2", modifiers[1].name); @@ -62,12 +62,12 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui }; }; - // Only Pokemon that can gain benefits are unfainted with no status + // Only Pokemon that can gain benefits are above 1/3rd HP with no status const selectableFilter = (pokemon: Pokemon) => { // If pokemon meets primary pokemon reqs, it can be selected const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon); if (!meetsReqs) { - return "Pokémon must be healthy enough."; + return i18next.t("mysteryEncounter:shady_vitamin_dealer_invalid_selection"); } return null; @@ -99,7 +99,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui chosenPokemon.hp = Math.max(chosenPokemon.hp - damage, 0); // Roll for poison (80%) - if (randSeedInt(10) < 10) { + if (randSeedInt(10) < 8) { if (chosenPokemon.trySetStatus(StatusEffect.TOXIC)) { // Toxic applied queueEncounterMessage(scene, "mysteryEncounter:shady_vitamin_dealer_bad_poison"); @@ -111,32 +111,81 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui queueEncounterMessage(scene, "mysteryEncounter:shady_vitamin_dealer_damage_only"); } + setEncounterExp(scene, [chosenPokemon.id], 100); + chosenPokemon.updateInfo(); }) .build()) - .withOption(new MysteryEncounterOptionBuilder() - .withSceneMoneyRequirement(0, 5) // Wave scaling multiplier of 2 for cost + .withSceneMoneyRequirement(0, 5) // Wave scaling money multiplier of 5 + .withPreOptionPhase(async (scene: BattleScene): Promise => { + const encounter = scene.currentBattle.mysteryEncounter; + const onPokemonSelected = (pokemon: PlayerPokemon) => { + // Update money + updatePlayerMoney(scene, -(encounter.options[1].requirements[0] as MoneyRequirement).requiredMoney); + // Calculate modifiers and dialogue tokens + const modifiers = [ + generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type, + generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type + ]; + encounter.setDialogueToken("boost1", modifiers[0].name); + encounter.setDialogueToken("boost2", modifiers[1].name); + encounter.misc = { + chosenPokemon: pokemon, + modifiers: modifiers + }; + }; + + // Only Pokemon that can gain benefits are above 1/3rd HP with no status + const selectableFilter = (pokemon: Pokemon) => { + // If pokemon meets primary pokemon reqs, it can be selected + const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon); + if (!meetsReqs) { + return i18next.t("mysteryEncounter:shady_vitamin_dealer_invalid_selection"); + } + + return null; + }; + + return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); + }) .withOptionPhase(async (scene: BattleScene) => { // Choose Expensive Option - const modifiers = []; - let i = 0; - while (i < 3) { - // 2/1 weight on base stat booster vs PP Up - const roll = randSeedInt(3); - if (roll === 0) { - modifiers.push(modifierTypes.PP_UP); - } else { + const encounter = scene.currentBattle.mysteryEncounter; + const chosenPokemon = encounter.misc.chosenPokemon; + const modifiers = encounter.misc.modifiers; - } - i++; + for (const modType of modifiers) { + const modifier = modType.newModifier(chosenPokemon); + await scene.addModifier(modifier, true, false, false, true); } + scene.updateModifiers(true); - setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); leaveEncounterWithoutBattle(scene); }) - .build() - ) + .withPostOptionPhase(async (scene: BattleScene) => { + // Status applied after dealer leaves (to make thematic sense) + const encounter = scene.currentBattle.mysteryEncounter; + const chosenPokemon = encounter.misc.chosenPokemon; + + // Roll for poison (20%) + if (randSeedInt(10) < 2) { + if (chosenPokemon.trySetStatus(StatusEffect.POISON)) { + // Poison applied + queueEncounterMessage(scene, "mysteryEncounter:shady_vitamin_dealer_poison"); + } else { + // Pokemon immune or something else prevents status + queueEncounterMessage(scene, "mysteryEncounter:shady_vitamin_dealer_no_bad_effects"); + } + } else { + queueEncounterMessage(scene, "mysteryEncounter:shady_vitamin_dealer_no_bad_effects"); + } + + setEncounterExp(scene, [chosenPokemon.id], 100); + + chosenPokemon.updateInfo(); + }) + .build()) .withOptionPhase(async (scene: BattleScene) => { // Leave encounter with no rewards or exp leaveEncounterWithoutBattle(scene, true); diff --git a/src/data/mystery-encounters/encounters/sleeping-snorlax.ts b/src/data/mystery-encounters/encounters/sleeping-snorlax.ts index 1a011bb05a0..cb2029272ef 100644 --- a/src/data/mystery-encounters/encounters/sleeping-snorlax.ts +++ b/src/data/mystery-encounters/encounters/sleeping-snorlax.ts @@ -1,5 +1,4 @@ import { - ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type"; import { BerryType } from "#enums/berry-type"; @@ -15,9 +14,9 @@ import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { MoveRequirement } from "../mystery-encounter-requirements"; import { EnemyPartyConfig, - EnemyPokemonConfig, generateModifierType, + EnemyPokemonConfig, generateModifierTypeOption, initBattleWithEnemyConfig, - leaveEncounterWithoutBattle, queueEncounterMessage, + leaveEncounterWithoutBattle, queueEncounterMessage, setEncounterExp, setEncounterRewards } from "../mystery-encounter-utils"; @@ -30,7 +29,9 @@ export const SleepingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuilde fileRoot: "pokemon", hasShadow: true, tint: 0.25, - repeat: true + scale: 1.5, + repeat: true, + y: 5 } ]) .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 @@ -66,16 +67,19 @@ export const SleepingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuilde scene.executeWithSeedOffset(() => { roll = Utils.randSeedInt(16, 0); }, scene.currentBattle.waveIndex); - console.log(roll); + + // Half Snorlax exp to entire party + setEncounterExp(scene, scene.getParty().map(p => p.id), 98); + if (roll > 4) { // Fall asleep and get a sitrus berry (75%) const p = instance.primaryPokemon; p.status = new Status(StatusEffect.SLEEP, 0, 3); p.updateInfo(true); // const sitrus = (modifierTypes.BERRY?.() as ModifierTypeGenerator).generateType(scene.getParty(), [BerryType.SITRUS]); - const sitrus = generateModifierType(scene, modifierTypes.BERRY, [BerryType.SITRUS]); + const sitrus = generateModifierTypeOption(scene, modifierTypes.BERRY, [BerryType.SITRUS]); - setEncounterRewards(scene, { guaranteedModifierTypeOptions: [new ModifierTypeOption(sitrus, 0)], fillRemaining: false }); + setEncounterRewards(scene, { guaranteedModifierTypeOptions: [sitrus], fillRemaining: false }); queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_2_bad_result"); leaveEncounterWithoutBattle(scene); } else { @@ -96,9 +100,12 @@ export const SleepingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuilde .withOption(new MysteryEncounterOptionBuilder() .withPrimaryPokemonRequirement(new MoveRequirement([Moves.PLUCK, Moves.COVET, Moves.KNOCK_OFF, Moves.THIEF, Moves.TRICK, Moves.SWITCHEROO])) .withOptionPhase(async (scene: BattleScene) => { - // Leave encounter with no rewards or exp + // Steal the Snorlax's Leftovers + const instance = scene.currentBattle.mysteryEncounter; setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: false }); queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_3_good_result"); + // Snorlax exp to Pokemon that did the stealing + setEncounterExp(scene, [instance.primaryPokemon.id], 189); leaveEncounterWithoutBattle(scene); }) .build() diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index 1e2dfe85045..5129f3bf23e 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 { FieldTripDialogue } from "#app/data/mystery-encounters/dialogue/field-trip-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.FIELD_TRIP] = FieldTripDialogue; } diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index f1316ff64f1..7731468a977 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -3,6 +3,7 @@ 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 { isNullOrUndefined } from "../../utils"; export type OptionPhaseCallback = (scene: BattleScene) => Promise; @@ -14,6 +15,12 @@ export default interface MysteryEncounterOption { primaryPokemon?: PlayerPokemon; secondaryPokemon?: PlayerPokemon[]; excludePrimaryFromSecondaryRequirements?: boolean; + /** + * There are two modes of option requirements: + * 1 (DEFAULT): Option is completely disabled if requirements are not met (unselectable and greyed out) + * 2: Option is *NOT* disabled if requirements are not met + */ + isDisabledOnRequirementsNotMet?: boolean; /** * Dialogue object containing all the dialogue, messages, tooltips, etc. for this option @@ -33,12 +40,19 @@ export default class MysteryEncounterOption implements MysteryEncounterOption { constructor(option: MysteryEncounterOption) { Object.assign(this, option); this.requirements = this.requirements ? this.requirements : []; + this.primaryPokemonRequirements = this.primaryPokemonRequirements ? this.primaryPokemonRequirements : []; + this.secondaryPokemonRequirements = this.secondaryPokemonRequirements ? this.secondaryPokemonRequirements : []; + this.isDisabledOnRequirementsNotMet = isNullOrUndefined(this.isDisabledOnRequirementsNotMet) ? true : this.isDisabledOnRequirementsNotMet; + } + + hasRequirements?() { + return this.requirements.length > 0 || this.primaryPokemonRequirements.length > 0 || this.secondaryPokemonRequirements.length > 0; } meetsRequirements?(scene: BattleScene) { return !this.requirements.some(requirement => !requirement.meetsRequirement(scene)) && - this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene) && - this.meetsSupportingRequirementAndSupportingPokemonSelected(scene); + this.meetsSupportingRequirementAndSupportingPokemonSelected(scene) && + this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene); } meetsPrimaryRequirementAndPrimaryPokemonSelected?(scene: BattleScene) { @@ -124,6 +138,7 @@ export class MysteryEncounterOptionBuilder implements Partial> { + this.isDisabledOnRequirementsNotMet = disabled; + return Object.assign(this, { isDisabledOnRequirementsNotMet: this.isDisabledOnRequirementsNotMet }); + } } diff --git a/src/data/mystery-encounters/mystery-encounter-utils.ts b/src/data/mystery-encounters/mystery-encounter-utils.ts index 2d8d7bfbf66..d2e1a5eba2f 100644 --- a/src/data/mystery-encounters/mystery-encounter-utils.ts +++ b/src/data/mystery-encounters/mystery-encounter-utils.ts @@ -8,7 +8,7 @@ import { TrainerConfig, trainerConfigs, TrainerSlot } from "../trainer-config"; 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, modifierTypes, PokemonHeldItemModifierType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; +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 { MysteryEncounterBattlePhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phase"; import * as Utils from "../../utils"; @@ -443,7 +443,7 @@ export function updatePlayerMoney(scene: BattleScene, changeValue: number, playS * @param modifier * @param pregenArgs - can specify BerryType for berries, TM for TMs, AttackBoostType for item, etc. */ -export function generateModifierType(scene: BattleScene, modifier: () => ModifierType, pregenArgs?: any[]): ModifierType { +export function generateModifierTypeOption(scene: BattleScene, modifier: () => ModifierType, pregenArgs?: any[]): ModifierTypeOption { const modifierId = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifier); let result: ModifierType = modifierTypes[modifierId]?.(); @@ -463,7 +463,7 @@ export function generateModifierType(scene: BattleScene, modifier: () => Modifie }); result = result instanceof ModifierTypeGenerator ? result.generateType(scene.getParty(), pregenArgs) : result; - return result; + return new ModifierTypeOption(result, 0); } /** @@ -593,6 +593,7 @@ export function setEncounterRewards(scene: BattleScene, customShopRewards?: Cust * 290 - trio legendaries * 340 - box legendaries * 608 - Blissey (highest in game) + * 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) { diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 87b80aba321..0d656988009 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -120,6 +120,7 @@ export default interface MysteryEncounter { /** * Generic property to set any custom data required for the encounter + * Extremely useful for carrying state/data between onPreOptionPhase/onOptionPhase/onPostOptionPhase */ misc?: any; } @@ -276,6 +277,7 @@ export default class MysteryEncounter implements MysteryEncounter { * For multiple support pokemon in the dialogue token, it will have to be overridden. */ populateDialogueTokensFromRequirements?(scene: BattleScene) { + this.meetsRequirements(scene); if (this.requirements?.length > 0) { for (const req of this.requirements) { const dialogueToken = req.getDialogueToken(scene); @@ -304,6 +306,7 @@ export default class MysteryEncounter implements MysteryEncounter { // Dialogue tokens for options for (let i = 0; i < this.options.length; i++) { const opt = this.options[i]; + opt.meetsRequirements(scene); const j = i + 1; if (opt.requirements?.length > 0) { for (const req of opt.requirements) { @@ -316,7 +319,7 @@ export default class MysteryEncounter implements MysteryEncounter { for (const req of opt.primaryPokemonRequirements) { if (!req.invertQuery) { const value = req.getDialogueToken(scene, opt.primaryPokemon); - this.setDialogueToken("option" + j + "Primary", value[1]); + this.setDialogueToken("option" + j + "Primary" + this.capitalizeFirstLetter(value[0]), value[1]); } } } @@ -325,7 +328,7 @@ export default class MysteryEncounter implements MysteryEncounter { for (const req of opt.secondaryPokemonRequirements) { if (!req.invertQuery) { const value = req.getDialogueToken(scene, opt.secondaryPokemon[0]); - this.setDialogueToken("option" + j + "Secondary", value[1]); + this.setDialogueToken("option" + j + "Secondary" + this.capitalizeFirstLetter(value[0]), value[1]); } } } diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index e4a748d3a23..a1f8a73192e 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 { FieldTripEncounter } from "#app/data/mystery-encounters/encounters/field-trip-encounter"; // Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * ) / 256 export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1; @@ -95,6 +96,7 @@ export function initMysteryEncounters() { allMysteryEncounters[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxEncounter; allMysteryEncounters[MysteryEncounterType.DEPARTMENT_STORE_SALE] = DepartmentStoreSaleEncounter; allMysteryEncounters[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerEncounter; + allMysteryEncounters[MysteryEncounterType.FIELD_TRIP] = FieldTripEncounter; // 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..b5b0144c3be 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, + FIELD_TRIP } diff --git a/src/locales/en/mystery-encounter.ts b/src/locales/en/mystery-encounter.ts index ac04eed2688..59fcf6940c2 100644 --- a/src/locales/en/mystery-encounter.ts +++ b/src/locales/en/mystery-encounter.ts @@ -17,6 +17,7 @@ export const mysteryEncounter: SimpleTranslationEntries = { "unit_test_dialogue": "@ec{test}@ec{test} @ec{test@ec{test}} @ec{test1} @ec{test\} @ec{test\\} @ec{test\\\} {test}", // Mystery Encounters -- Common Tier + "mysterious_chest_intro_message": "You found...@d{32} a chest?", "mysterious_chest_title": "The Mysterious Chest", "mysterious_chest_description": "A beautifully ornamented chest stands on the ground. There must be something good inside... right?", @@ -82,6 +83,7 @@ export const mysteryEncounter: SimpleTranslationEntries = { "shady_vitamin_dealer_title": "The Vitamin Dealer", "shady_vitamin_dealer_description": "The man opens his jacket to reveal some Pokémon vitamins. The numbers he quotes seem like a really good deal. Almost too good...\nHe offers two package deals to choose from.", "shady_vitamin_dealer_query": "Which deal will choose?", + "shady_vitamin_dealer_invalid_selection": "Pokémon must be healthy enough.", "shady_vitamin_dealer_option_1_label": "The Cheap Deal", "shady_vitamin_dealer_option_1_tooltip": "(-) Pay @ec{option1Money}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins", "shady_vitamin_dealer_option_2_label": "The Pricey Deal", @@ -94,9 +96,34 @@ export const mysteryEncounter: SimpleTranslationEntries = { $Your @ec{selectedPokemon} takes some damage\nand becomes badly poisoned...`, "shady_vitamin_dealer_poison": `But the medicine had some side effects! $Your @ec{selectedPokemon} becomes poisoned...`, + "shady_vitamin_dealer_no_bad_effects": "Looks like there were no side-effects this time.", "shady_vitamin_dealer_option_3_label": "Leave", "shady_vitamin_dealer_option_3_tooltip": "(-) No Rewards", - "shady_vitamin_dealer_outro_good": "Looks like there were no side-effects this time.", + + "field_trip_intro_message": "It's a teacher and some school children!", + "field_trip_speaker": "Teacher", + "field_trip_intro_dialogue": `Hello, there! Would you be able to\nspare a minute for my students? + $I'm teaching them about Pokémon moves\nand would love to show them a demonstration. + $Would you mind showing us one of\nthe moves your Pokémon can use?`, + "field_trip_title": "Field Trip", + "field_trip_description": "A teacher is requesting a move demonstration from a Pokémon. Depending on the move you choose, she might have something useful for you in exchange.", + "field_trip_query": "Which move category will you show off?", + // "field_trip_invalid_selection": "Pokémon doesn't know that type of move.", + "field_trip_option_1_label": "A Physical Move", + "field_trip_option_1_tooltip": "(+) Physical Item Rewards", + "field_trip_option_2_label": "A Special Move", + "field_trip_option_2_tooltip": "(+) Special Item Rewards", + "field_trip_option_3_label": "A Status Move", + "field_trip_option_3_tooltip": "(+) Status Item Rewards", + "field_trip_second_option_prompt": "Choose a move for your Pokémon to use.", + "field_trip_option_selected": "@ec{pokeName} shows off an awesome display of @ec{move}!", + "field_trip_option_incorrect": `... + $That isn't a @ec{moveCategory} move! + $I'm sorry, but I can't give you anything.`, + "field_trip_lesson_learned": `Looks like you learned a valuable lesson? + $Your Pokémon also gained some knowledge.`, + "field_trip_outro_good": "Thank you so much for your kindness!\nI hope the items I had were helpful!", + "field_trip_outro_bad": "Come along children, we'll\nfind a better demonstration elsewhere.", // Mystery Encounters -- Uncommon Tier @@ -162,14 +189,14 @@ export const mysteryEncounter: SimpleTranslationEntries = { "sleeping_snorlax_intro_message": `As you walk down a narrow pathway, you see a towering silhouette blocking your path. $You get closer to see a Snorlax sleeping peacefully.\nIt seems like there's no way around it.`, "sleeping_snorlax_title": "Sleeping Snorlax", - "sleeping_snorlax_description": "You could attack it to try and get it to move, or simply wait for it to wake up.", + "sleeping_snorlax_description": "You could attack it to try and get it to move, or simply wait for it to wake up. Who knows how long that could take, though...", "sleeping_snorlax_query": "What will you do?", "sleeping_snorlax_option_1_label": "Fight it", "sleeping_snorlax_option_1_tooltip": "(-) Fight Sleeping Snorlax", "sleeping_snorlax_option_2_label": "Wait for it to move", "sleeping_snorlax_option_2_tooltip": "@[SUMMARY_BLUE]{(75%) Wait a short time}\n@[SUMMARY_BLUE]{(25%) Wait a long time}", - "sleeping_snorlax_option_3_label": "Steal", - "sleeping_snorlax_option_3_tooltip": "(+) Leftovers", + "sleeping_snorlax_option_3_label": "Steal its item", + "sleeping_snorlax_option_3_tooltip": "(+) @ec{option3PrimaryName} uses @ec{option3PrimaryMove}\n(+) Leftovers", "sleeping_snorlax_option_3_disabled_tooltip": "Your Pokémon need to know certain moves to choose this", "sleeping_snorlax_option_1_selected_message": "You approach the\nPokémon without fear.", "sleeping_snorlax_option_2_selected_message": `.@d{32}.@d{32}.@d{32} diff --git a/src/overrides.ts b/src/overrides.ts index ce706173691..e19a5bf20dd 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -8,18 +8,17 @@ import { PokeballCounts } from "./battle-scene"; import { PokeballType } from "./data/pokeball"; import { Gender } from "./data/gender"; import { StatusEffect } from "./data/status-effect"; -import { SpeciesStatBoosterItem, modifierTypes } from "./modifier/modifier-type"; +import { modifierTypes, SpeciesStatBoosterItem } from "./modifier/modifier-type"; import { VariantTier } from "./enums/variant-tiers"; import { EggTier } from "#enums/egg-type"; -import { allSpecies } from "./data/pokemon-species"; // eslint-disable-line @typescript-eslint/no-unused-vars import { Abilities } from "#enums/abilities"; import { BerryType } from "#enums/berry-type"; import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { TimeOfDay } from "#enums/time-of-day"; -import {MysteryEncounterType} from "#enums/mystery-encounter-type"; // eslint-disable-line @typescript-eslint/no-unused-vars -import {MysteryEncounterTier} from "#app/data/mystery-encounters/mystery-encounter"; // eslint-disable-line @typescript-eslint/no-unused-vars +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; // eslint-disable-line @typescript-eslint/no-unused-vars +import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter"; // eslint-disable-line @typescript-eslint/no-unused-vars /** * Overrides for testing different in game situations diff --git a/src/phases.ts b/src/phases.ts index 091629b31d5..6c64cc9454f 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -5363,6 +5363,7 @@ export class SelectModifierPhase extends BattlePhase { this.scene.ui.revertMode(); this.scene.ui.setMode(Mode.MESSAGE); super.end(); + break; } modifierType = typeOptions[cursor].type; break; diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 163d79784f8..8be862b965d 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -1,16 +1,16 @@ import BattleScene from "../battle-scene"; -import {addBBCodeTextObject, getBBCodeFrag, TextStyle} from "./text"; -import {Mode} from "./ui"; +import { addBBCodeTextObject, getBBCodeFrag, TextStyle } from "./text"; +import { Mode } from "./ui"; import UiHandler from "./ui-handler"; -import {Button} from "#enums/buttons"; -import {addWindow, WindowVariant} from "./ui-theme"; -import {MysteryEncounterPhase} from "../phases/mystery-encounter-phase"; -import {PartyUiMode} from "./party-ui-handler"; +import { Button } from "#enums/buttons"; +import { addWindow, WindowVariant } from "./ui-theme"; +import { MysteryEncounterPhase } from "../phases/mystery-encounter-phase"; +import { PartyUiMode } from "./party-ui-handler"; import MysteryEncounterOption from "../data/mystery-encounters/mystery-encounter-option"; import * as Utils from "../utils"; -import {isNullOrUndefined} from "../utils"; -import {getPokeballAtlasKey} from "../data/pokeball"; -import {getEncounterText} from "#app/data/mystery-encounters/mystery-encounter-utils"; +import { isNullOrUndefined } from "../utils"; +import { getPokeballAtlasKey } from "../data/pokeball"; +import { getEncounterText } from "#app/data/mystery-encounters/mystery-encounter-utils"; export default class MysteryEncounterUiHandler extends UiHandler { private cursorContainer: Phaser.GameObjects.Container; @@ -319,13 +319,22 @@ export default class MysteryEncounterUiHandler extends UiHandler { optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, i < 2 ? 0 : 16, "-", TextStyle.WINDOW, { wordWrap: { width: 558 }, fontSize: "80px", lineSpacing: -8 }); break; } - const option = mysteryEncounter.dialogue.encounterOptionsDialogue.options[i]; - const text = getEncounterText(this.scene, option.buttonLabel, option.style ? option.style : TextStyle.WINDOW); + this.optionsMeetsReqs.push(this.filteredEncounterOptions[i].meetsRequirements(this.scene)); + + const optionDialogue = mysteryEncounter.dialogue.encounterOptionsDialogue.options[i]; + let text; + if (this.filteredEncounterOptions[i].hasRequirements() && this.optionsMeetsReqs[i]) { + // Options with special requirements that are met are automatically colored green + text = getEncounterText(this.scene, optionDialogue.buttonLabel, TextStyle.SUMMARY_GREEN); + } else { + text = getEncounterText(this.scene, optionDialogue.buttonLabel, optionDialogue.style ? optionDialogue.style : TextStyle.WINDOW); + } + if (text) { optionText.setText(text); } - this.optionsMeetsReqs.push(this.filteredEncounterOptions[i].meetsRequirements(this.scene)); + if (!this.optionsMeetsReqs[i]) { optionText.setAlpha(0.5); From f128b47db5dcd279ce76143c6c2d008f5f9fa04e Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 11 Jul 2024 14:37:29 -0400 Subject: [PATCH 02/29] update encounter biome mapping and option coloring/disablement with requirements --- .../mystery-encounters/mad_scientist_m.json | 10 +- .../mystery-encounters/mad_scientist_m.png | Bin 13992 -> 920 bytes public/images/mystery-encounters/teacher.json | 2 +- src/battle-scene.ts | 2 +- .../encounters/fight-or-flight.ts | 96 ++++---- .../encounters/sleeping-snorlax.ts | 4 +- .../mystery-encounters/mystery-encounters.ts | 218 ++++++++++++++---- .../requirements/requirement-groups.ts | 42 ++++ src/locales/en/mystery-encounter.ts | 4 +- src/ui/mystery-encounter-ui-handler.ts | 10 +- 10 files changed, 276 insertions(+), 112 deletions(-) create mode 100644 src/data/mystery-encounters/requirements/requirement-groups.ts diff --git a/public/images/mystery-encounters/mad_scientist_m.json b/public/images/mystery-encounters/mad_scientist_m.json index 0cb3f904cb0..10aa3d6f42a 100644 --- a/public/images/mystery-encounters/mad_scientist_m.json +++ b/public/images/mystery-encounters/mad_scientist_m.json @@ -4,8 +4,8 @@ "image": "mad_scientist_m.png", "format": "RGBA8888", "size": { - "w": 44, - "h": 74 + "w": 46, + "h": 76 }, "scale": 1, "frames": [ @@ -24,8 +24,8 @@ "h": 74 }, "frame": { - "x": 0, - "y": 0, + "x": 1, + "y": 1, "w": 44, "h": 74 } @@ -36,6 +36,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:895f0a79b89fa0fb44167f4584fd9a22:357b46953b7e17c6b2f43a62d52855d8:cc1ed0e4f90aaa9dcf1b39a0af1283b0$" + "smartupdate": "$TexturePacker:SmartUpdate:a7f8ff2bbb362868f51125c254eb6681:cf76e61ddd31a8f46af67ced168c44a2:4fc09abe16c0608828269e5da81d0744$" } } diff --git a/public/images/mystery-encounters/mad_scientist_m.png b/public/images/mystery-encounters/mad_scientist_m.png index 84349b46c8a9ab6a3dcf29de3b67f42631c38547..453cb767ec14c4032d52ba665cb23381b8ebf280 100644 GIT binary patch literal 920 zcmV;J184k+P))01&J}F)=w>QfhNCIdgM+drDe^bDW%{th2Mfy?bKC&DGU0G2Wzm?(YBp)xEj^ zRtEq800MMUPE!DUFCg$Q00009a7bBm000XU000XU0RWnu7ytkQ`bk7VR7l5_*Wr4r zAPfdz8-r;%qT&8;_IwG{ss-!#vF&j$F@%WiFf@h;VGMW2UpVZ;Mz}SjSm@V_A3wbe z|5O9U3p|7$o=m^Wuo*A<9s9*xXI$q~Vl$H95}WzTOF$w#=bRad`wZ%H@p7!$IIp-t zV5<{#YiK;z!B(FOZrc++eDl=aOjUYtu*}z=99UA~hc9%_^Nint*DjBe1pEQGW=vM1 zu&&(Z_*GTY-{M`LJ6_et6X3ifsza@F1SB?_dZvz%lypw ziPc1sFOAlaapa+TD~!&0eQH#SkUJk{#7H113Ut<_@zQMJoOjUu9RVDGpdM+p&4tjonCi+5X>TN!=N#gdx9FSSpI z_E=a!3-`)gy%LZFO@&AANKIF796WVwTF}PoRlkfLDFhE*d#t-n zaB^>EX>4U6ba`-PAZ2)IW&i+q+O3>vk{mgXh5z#ubp-ANkHgh$ZZOB6?=eCujjFo4 z#%xO6QYsbU;Z6c?0l;+UzyIsHKmPb5_>iK@rCIH@dj88j_c-{%{Gb1w@1LLV1)tyV z@4x=zbzI8*`VT+9iu{@QGkw0<^7miU_xnFyUw{6|h5qIrLiz0t>F3A9Uq85w@AspR z|LcxIj?Mr2pZ)p&d$-+tK2JAtWf|({p+0YQKQ6-yC##g++bX{r|1W&q-mlIt^TV%@ z{r1aGeyz|$49VY+!**eW8&25#xx(Teb3C!}XN)VR_dQE3_PCNyA!YpvH@4K%PL^U? z@uZZpOY!Hug!j4qeclSjC-1;ZW8h+J@W21%{?iNo$*+5tXN^MO>BBcJxUU$s4?)YA z^gAzOL&E*;TX_ro*Muw>ba zRcmI}ZS>d^DOq~$ty}MX3_f#YH_G7MgIA0(<4iNpGV5&9W}jmbJ}a-X>T1hYUt`Ce zcHU*z-L~z%#|ei}awng1>S@PLKjRW>H{E>8t+!pf{f?iz7QSiw`>+4Qu7&Te#kW&> zUHiFfe77}S=v^WRXNvZW9g8`z<4rq2Ku7JFw~%wx&S}qlk7PxO++@+-?BItBl`YIC z#B#&Wz58?L{_(y!*7`r~Tl^ibg0fTgJ)i`?~YcQv<1TbG!cqh5b`!l@)J zEbE1)w)qKu3uIT{6#y)keZ4#Kam;pZ%OI>b$k+vuC zLfRoo_C#79rrj}sC5{7FfF`17OS>e z=bhPz{<>|kkGytOC?;)h#A@-4J1aH0{K98+A#LV={$KxD6YE4gkUb~ZPAAPhx1C0K zHP^{RpV1(Fb)RpGiEz%lu0nj3(FrzVOW$LV5KDjFk^C<3=UQha?l;foh=thURvVP$Rb%iUAiF${e@w4(-(_t#y_troPM_5=wD0ZztFn3)6ugP{Y#Q zeQ|%s?*)&u7Wdr7GV22EfP22oYi;??ExB0%!!_2PW%%96Z66L-+l?I_nJs9E#L3*l zjYXL^*F3W~;xEt9D~Wf*EY2^o0X(y`{Ly_Bu1I_;Pk6#`lV5-oI|5|g69sZS3u#Aw z7Gk5IK-giI&W!x~V1ax95ifzh$(@mNfFJk1H438qz8kQvVFE>MSEXkJ-N~)=h-NRm&*`i(w0jOu5VL+Ct0SSd zpJ^xxDk=^2)oNN7Ccj`1I-sl_+RC8N+ODx`wez9uO>e}>#~ZaG35z2{l3SNm;j=v2 zw(i;oF_IumV7#%*iU2;GrD|4K9S54T-9SFk$`zrPaR?|df@{KcWvmqd(3p0F?}%RR z4?d_6UPp(FT8f>5a`7tX>|l2l=^3pfZwUXELiWtHsQAu6#)={x(l# zJwbBzMeI~wls4^sB^*fhum#t~y zbQ|Pm>)>}`S0IgpfVn3X2L_JO*DjF3&6T_YCSyUhpq@?y;gBMms}+Pp!lE@cix;%S z{~Fsd=H?s2O*tGWlXUS?0WI>J2#v_|S0qp3B5VMgS0?N3A&v?lttt6*H|YzXOawbw zu?ztTNG@o$;O7FD5yu$}3f6824I)C9`9j|IslR|gBnQ`w(H=JeY={!>swOy=oKx1_ z9p_?|5rp7EkMfFK{dD;0W`cwm+Y{PG(hS)*9EF>(5YNL}*GNcro{sns9+&kgl@=!;Fh#5q=uLVy@rpNzF4zgv*z6@F*hwkl;9Z=Ov1q* zhKvco7vwk55vPzs?nZK^pifTeJ|WB`T)1m^*79aw6}1YIF5NkWIfzY{*a z0_mL-NC{IkG6__l8IXdZ(IX409}G+s{z`O9<=V!#-P(v4v~fh!B1InV z#O4w^Tk#2i|3ahsmzd zB%klEN3n&$(y?t&6G%u_9W)sF1^9~CArwQ0v`^r+0TpBo77&}vzF{_aBOqI6Vk;Mf z0%?$fEwK|~4oq!%MhCkC5rBn~%2}3!M>7_L?*nqE|0VACc-0ZW8?;<)0M)>CuNeb& zN=98UIq5BM8K7p-7sF;IC2(JO)vR}Bi`Z`cO*yrW)# zK-3axd73#y=@iDH`&a<>kYyYl5M%=;Q65oU_)mE85|nsd1k!-j&;+Q8k&qG{F>{@k zt;j1cZ5129m#DK_l~T}5=F_bO90jrsM@?d2lF?QR!_PW)5hf4k8vDhnB61s0NRUAq zr_7uYlEf;>kkG~#U>KLxFn}mrFCn%g z+7^%KA=WK3h0Xx<_jIeE`{6@)J)38-1~F3)G>7)Xtwg9B1yFHANGA;aRpEkd$CR*2 zB9 z*#CYYqymIS87zSpLU!)Rw)wn*X5)ck| zVB-lWUb~=^f`LGIqO{qE07B=?u3?^%sv#);zDdNQLdcMk`(2~Ajygp`z!ZQX%M705 zP@$G}oF_OjR8tiQ$dpWP+DFi&khcjZ&c}}n(&4U1CehJiOS;HefsS`Mq&?>NfIJ>8 zOCX^FYLP5};z&nKk_il}mbwzz7-eFx6O;)84BV2uc|-SbN65^AnZ=Y74hj$eVBx=c z0D?)i9)LU%2d#nmJK-Hp#uuu9l3&$)G&BLbRw8M8(B7;70*YM7XrS4197xEPD@tOh zPH+%c7X_GrStz{BU~E!8GW*vp{aZvK3z*@lDeZ9^pl3FZ zOExT!0#}3|&938px)dDnK%}=^Zc&Mgt@Tntsn-OBObmM;Y-$!Z5?$me(Q3)q>Iwfz=MZW zu?e4GSA_yTk$WN*K{;RnWw@|s1HTN+J*9&hl}wEum(9{+2|CEelf-*={*=Emj(#Bs z0R$d%B4LoGn=c}tPh25+2KfV;$b1$j4+sU-{0fB#{-e7>iARM*2H+7SEF{ZbK_V6g zg)AY?rcDngiMWX zN1A#51%!dBU@Wv1kf+LWluhHGKv++p!dlU@J8GMi=}{35Re&6ZjZiY&I6;l21C?Pr zVD#Xb4-d*0cvrOQBGG_cP>=1{Bvlk>pzv{!NM8tkU)6N*J=}zsb?mCDt|1>p$Ql42 z@R~44M@*oQ@IdUJAx*0%r5zp=nGzIbRi;obj7v(u=H+X!sG5T(x?1n7ZZZH}ep?6+ z>2%43SXDoYW>MEIvpq6l<+ggJF!p2`AfO z$qB!vGA^vD$^n$CZS^7qhh~ z0*hBkJEOXe3q4+C6O~ZrHtWB4L8g34J6VNtYfz(Vkn9jhPLd$~@p{(K;`=4n5TzLQ z;bN^*fsQhQW;9i|3t%R2gP9MkJS5QdOvo;SDh!k+UgPpUW>ViIv{}u&(B;?dfhs)E zGZIC01z;noy^26Nk`?&-dBT7YA#eh=PXL@a!;(lqv8g~ie#(JjF9BKDiDCgLN%L{! zT2*KvXt^*p&IA7u|JhXP<%ZT&q9i{MZ1s9zr1Pm`S>YOMdR5X`j3(~`K*6I}2Spw% zjHR4uaEk{aeb_pbw!)DGsMAG=)Oa3Ms*l)m5ebknaZ^y-FgYZ~YO;Zd_~C^x@D)(J zUe)`c;YsD?@Y|p^F>~Y-yq3<=E21F$h`lAQ=ZlU5LSlyqiX)phaSJ+65+YIBMZ z*ag;WDnPL3*s*V|+`ct&q#fCTt3+7qLWrjgcj!6FVIQ%{)v0t`@{vQtzRtDK2V2Ta z4+Op~b#0J5uq?ETNUlx2C-ptqKFp)%edL@=@_5jFk?&Y>Yj@RfK7c%F!>t0eHJk@(HKDORt1Gu!q_iuiHEJ8 zAj>6&;4@4`fhhpLkP9~jH2|w%@Kj(DAlxB3J9VCx@J2BO`PCZ-sGcH_5=&<3a^o+= z7va!RIzb(Nh@&etgNmWRZj z2#_6^2|TlRXkPI!Fssa%QqT7Se4kc|3VuRlIb>2m5H{2`#j{5R=Ni(LIY38J9RxDO z5Y%a$i7iWNLn1Rbq)GZAcUb$IcSoI}#RfNW5&Qu3G%^Ag=aXYK2n?bfWwW@SV>dSh z%tlxf%RKVkd<^V2fOs<;=j6?iToOL1nBGSn(?Ms z^e%G)$eyJwJ}PGz>yy;e;vtDK%)Q58cTu9@^oDxbJ(@5J<H`qfF4W z;(AC2F2`0%>MjrnFAJyym57l=Fr%wxJ!->M9!VGdQlUEZM1&{C5&Y@r>}@x`}}zT;t)XtPY>1(NWkXaPfs{*mjA_>3Y7d_$3U8R z5cGB0@vTT%9+=f|1$Mt5sz&{fYZo4!3%|D|rMWSPfV~&_mwnY%M@vPC9Q@2EqX8;2 z47b(TCL)kPCSMj{R?HM+=7G`&Raj;D1_=pZR~2=BmqNkUL7JfhaLJ*8KozhT(~bhp zxNt1bs%ngRHW#QN=wRagJaR57b(8Pq`^X8G2}T}A0OF0J6sK(A_7aP5)57~IxL~4) z=EuMK07{d{Llp$lh=*k5H8`?n0NB6>6Yr3*UjoNb*bf6_@JPV%$CGMbV)s%2eg zJib?T8L+6B6U3Fe<+F8tAVzCrBj87PXUQs%y=YP}8Pp?oK?$xK>UOuX16&!eBKjXM zxfcR~&xFrs}pj0_m#6A;+}34B5psBL`%{)DumrRKWeHtup`;kY3^3 zi%bn(AWChPf`yb(V4(wykIW$l0dI0F$@Ix;Cog=vRwVI*5tk?)pK@_%BydFaNjO0j z01#iF%$rHIEoBT0V!V%MYw@QVd{8m9YOPz**hvLZ-65oL=nHm3Joq1VT?xXyREuhPQ?URsFXSei8_;FhtLp|tp#7u?P#-pdKY|?91n3ec z=Qw`Q>UnON_LcI>50si(_bOcwV-T|u*dB$n5HA7R56L&#_VP;g+E)U0Y1K$r zugQ$^h7=C5jBbEL3;t6a2Ui!oa>!4x_H=QY;8W-h&rCRNK>${pG}C;NfzSlX%6e&1 zy0NZ(Y03g!Mab8=K~tG*}5&f}QH$A;!dD=zalc1P|C{5C?=|wR&U2y}EfV zsC~<_mAovzmVuMo(3Ip~MsjjM5@b+EK+`ZBY9v6rsy8&;BE$p$p;6I{MFS`*z`edX z7H7(70L^Uw1$eQ7vJMRb03QM48`ucoP6pV-sfq&iE_QU>I1-{-1bSFzBvDSPRbiKk z+LriaweZtxhI&5brtHy_NDP4MobyDY?@sXPVGOUjIY_vkZ%PAyq57vH<_1(ISWqxO zEM2RBm->C03QEy#G~VxU#3|>d4q*VqR!^^mk8W!X_i!F}wEN7w!CJaag$6!|+X=cK zNU*C>*VO}8XFMFVyp+b1-9bUC0YP*p9*lJdU(tF*KW+k}BV>&TO4T^$bY?0&$1(=F zEFC6a)K9`YC%g~&sIEZ==2AXWo%#iVNI&I#NQ#bB^$@7TRHsUIY9uN0J~l)h6C_Sl zbzi&!D9Fwc3H-D2<0%8R*Fmk4C35$52nlesyi(DtZHKEl4)j`^vgXZrMQQ4f;2Fre z*LGx$wvSz1>>9)(02_KyTKUZ=gy_*0N#5@Qr_x(P>V>nMS2l~ zMs?f3QFWcJm8gXPdxbe<6Pg{|6Zwaw@l_4t$iU(YRI3GBiB~AF z4`f7$>crhCP-;ALeRGEZ3gLiEz3RabvMjNjs3y7zZOH66pcOyEwTtE}2Km3$`Bo89 zc&&*T=o@3$Ng7EcQ%wN+OnN>PN8hS_f=92x<`=L5{Ht6B{;PggSRjy@gn5D;pV#hD ze+6B#8`#Hw@QwI{GY)cTI)CdmniuC{7YF^F&#M1wK1-94P;gT3KEB%Cb|eOBB+JKu z^w{I8QGt_Q!5tVxevXld4a=ZxW0Qh9{|gw9xo?JyAdHHGd^D3QC#a5=94(d#T3by8 zUx9qufQtIu>BweWjci}=dCsAR9PR+Db}Dcq{2*B3Mwf$INZWN@3t_7kw2d4f4RM4= zqt4Lm699V`_>oVBS++y9M>SHj!FDYAmUIPkMv0gaB-SfF7BZ!%W3Y!BDF(yj!K zU>Rsfj7~&Gy}MSZL>3#40Mg|Miq^Ft{{>MI=3JVKkVV71%>68d3iBBQ>_{K7JG7Kc zsLnr>rr|CK9xJfOWHQ&HwDeZ(UTss79WO#SlicF$jy7?)=TtrV9<0{z@%7n+mxOcI zeEh{#_gzh6c;pZe5?N(*pvEVs?tKEW?3zx7s0WK-7Z4FzO=bhZ_BqB-yg~fB!(lipqgnU9bSOB`}84NS-lh@DQZANbdE?D*JfZVX#U33+lf{tvPwmiEDfb8 z7Lf0@?&^jVa7x5<-8#La>MT-R^T7grVjLY#(pbwo|74s1pUDsPZNs(5KwRZ%_EM6!d16z3A}Nwst})>r_Ia;*U?L8fWl%AKJ> z3Yxp~&=_7~vc#^!vnus`9~J?X2I!{42M`BhhUWlPmPP~cPhgESI|3Q;I5nT4{$?aj z*rl0xo~YJ1@)%)(;SK3cbCT-tw)#&z_Oxzsa0|qx#(?(=k@2;B$Xhjt@ps^I@D4F# zo^bY3R{LlaF}~=Q3ri!bYLSE2_!e>nx6skSI)*W@l?kf={cCo^Az=!(3d3s$>`Wr6I(X$M+14^M5^5LvoUgP-``)y@;POViM4hpx!DSXe3)%lhwp{Z3)LC ztBp04CbH;TEuX1Ik4M5kQr58SQeg;3fWPQ40_!^oe!WQ|*Z1HGcpL}|wW@eNfpB=NErL576^<7Nm^jy9*o3V zDsE_oqrA?Z+_gr&_5Fa2^Sly=(c%C`P}swK#OsN3Fc+*5;kz_hD7QE~l0@#1{aBDC zm#HdjE=_5dSO2+-ZPr*op1TwO;q^h9v#9ob(WtUCe#hbAFnAkUU9(;~9THGWjnv1x zwI@O)-_SGo6HhJ_RkGb!sa~8YhOJee8H_Vjx~XmK<)z>nz_uwpz^oBzs z@~+^PA$l+s|JijkDDEOpjkLppuy+JIXb9Fg)n`*u5vgi^=X1hMos5_ZWaoY7~>zm})+$mw2`*l)8{t2H7N z*R~MV1JBjMo~k{7U0}Z^qtK3MeN7H)yl{JslNDa#HMlTTCymLez5*aYgH=tO5s$rn z(*#kUuR(QY(7ZNrd{x#wUiEdAH_&aWn(I_@appB%Z{wXyI;v+j=tBLM6m)E$sIZ0F zgupg>hOLiiLbhkgNEHGD*3h0PA)2fl7N-_KA$fAV7U2a&{_!@^sawM5uFP zI>Le&Q6y_xf#f32YIz(pezMO`(n*F@0VuB0=q6v^sEVTX4e)Ku0_~Iv-yxSc74==9NF~lc5tZN8`#YyN-}xt-~ACLinKY zI!+=fI#B*-y&a!@RBkMckw*$u?LChf+mj+o$0h>SrIUgAYzbc5Qt z+auC+9#Dr!ND)~U&AGipw=g#O7Hn{%C?ly<{8a zuP(v1H~5PzO#7X(g#+JFAf2;8E}l?l+C^wBd6c@t+o=EnhLN}8n4UH-8fL=KRKKG% zl4Y8%_ryCona2v%bUY^7*1-32`f(p!L%(WWtt-BeB=myO6V!g`$mki=yya>rQ-e-# zSKnJdH2_dnp)5FL+4-EHfg$0-s~hA?=vcEl@<^{JmF63o)!Yb368f9LV42jG)od`P z1}uDVHq@2re(1<&JV{4z5T7k^PXOCt?VyDnBTKB~^g&)$iJqcrDh}GynJzTto_DJH zlGD|w!67%fCOA=WKg7LGHxZUw^-Vm9(va&j(v;lJ!{ihmo zDotyk@+0G=9H7!XbwZ-tSM87ZHYliXYbG^f(23K5KN`L)|(gzf>kb z{bUVKy-7*Xc>Yo>2ezDSbY^AM#6??v#zA%Pq?TIVSqzQi!UEv>n+COn5xCEU5cTXd zlAajn(OAl%1KXOndaC>L6GJ#p;gIEl)F=m2YcXUwvWymm5jx>{CO1Ei|cp(#zm z_F!-{XQk6(7umpd2*6Rk_6jwjSvs=$`3%+|y@_$46X?16IVXs{t^NVIQ20<)XZ7O~ zu119k4$b8PS3HlpIjpg6!FN@{rV~r(4741Ie@7j`0sR;p2|o>M>xT{CJuS0OWP}dC zsCt}6YZAGKpx0=uQ?n4fQH1J8F6dxKxvVifA*R0e+^%YLnrjCawz^u# zHiyBK+Fh(`RsEGcgCZKMzcp5BC3X#Js>BOA5ZZYZ9TtWQ(~ZQds%(6UvcD|p5)G>B zKqLc(M;K)3Yre=xhd9=^^6|QQ7IYf&sFut9A#q--5`s|? z64G!mROuaLV>P(ac&(+XmW;tU)8xg*8}<5(?HL>o--Dyyp*>I;NnCl0C zNH{>nV_-pWCn`#i3M!h#gdfyN!cPIn{rwMwY0~RNTwd&c&`|+It=EaO)Zam&qoZA_ z{jNMADMPStG?5NXYrb74irx?J(2=IM1}vw&ALuica&KG?bwMn59T}945v(qJ|JC%$ zl`PP3m1b5j6h2%6VQAR&4VdbHyQ(BH>m(}sTI8V-BpNxor0ZU;zf-Ff$Ch9*WC#qB zWiXfM08JuEf@cDo4Gh+itKN!E3hG>m=}4k!K+WmE=t(+}BUjEnNlR|HHKWNgB zq_{W=t_25w7OM^}&bm6d3WDGdh_i!}qKlMxUs7lhb4)iZ;tk@N zO-tvzPaI)oNg+Nb9yjQM#E)E;U4G+Sa#-M*5hI(PCyo${r7o7cn3WBcc$zq>s2b%9 zIhPgATb$Kuowe`DUl=ZED;ciS8bJa}NFoIhGU}+H3JWpXHBwBZ={)Y?A9nmHa>?YX zfRSSXHK>prKlmT~o~>D&nsk%GaUk$w+aIGqa2IGbZ2SAzwp%BF{~5T_I{sP{nEfQZ z(b1wuz`!YT&7<$qrLvkcPEumNh-p}Zp^1$FN(6{E!t$mKu2OvwcTD}1e z4uP=}Wv_d@JJdb5e|y^V`vJ|)a%FV9@XP=J00d`2O+f$vv5yP7Rg0xv5K`tc91KSm^I8|sZlaW85Y4XZ(-Ohux32^ zIkGKhJLl-wmL>V>&VBd2H!`r{;dI^!r}IuUb-fmzVGwfD0Ceorh3Af<0T`P+AH#W8 zB~W5grPq#qTFR_E7QjlNx0(R^@rb~7-l~0j$1D?Q#N1QhCHo(Ov*!=GQ8d7%Yr~{= z<#(@x2y>^n1y2|lo2)#$Yr8}l*xO-G`xJpEx0=?e1iU#Pc3n>Z5Ymox08fqEVXxr9 zPUjuu^%@22x}I=dPk3Gr)2UED!V?Cb+~VUA$sC`sXyaM4wFs=|_279u_)O6@&dN= z7Jr{d@VvQo8Er$!;L^1lP16~6r&A%OQz5QzVj-nLsU^yenNEe&xG38%;<2FA68A$- z+z;pR(1;|Y6nwu6-|x<*IEvtTy{B#xA|B)8i4EWHqTM#oZW|EsI3tVh=-;)-Eb~Hf zeG|_l5rzN&&+Fmv@MR%0xvnRIz@MFz0$ViRez-(9`hx457^5&m5cozTK{pCR9PIBd z1PcJ$Ji-0Y!~M`JTq6p&}iPlqwfmIq0NDqbP#wdKg&WF^FuK z6({>wp#eDJ)G-l-Utv3Ml`~Lx00gHQyCdcf2&J=Ea|a`BR+q|r@=kxb_@l5cR$tNnRidzrxr(M1OdZof*H2&e3c2Sx-u}HT-*IJo$y_GyGtZJc9!W@D5V*yYKcvNz5c2qCK#KWX+65@fB;Lo$8zT7f>n zBhIm1$tp2-=8mBiRgEvC5ISo#&yL^V?D$R5t{g|Pe}cyu$h6|qS=9(yzr2(zm)*Gr z(f}aJbX>k%wNomg>zf#y^O>78$*@*m7@GAMd6ofy2f_S3V<42OCO}F;XO|FCTNY2{ zZ&7hM2^E@TqU2{w++HC`_>tQ+-YI{mvd9cLXsZ~?d1}v+!AN?U74)@9ewU{(2f=`!A z-ZdKQ@vd)Tk#-}he~T$q?`4^o@9E^Gt!-pV%l!5f!&}R^gsxTjUcTzlpSLc_sFcAn zsd$~C7?2D}Hv7X1#jf0!XH;kYSr`UjwA+S|$)l477EkUnbwI~))GWl57Si*gM3bJ^ zs7t7%(QX?cl!9}v8rC52aj?IOm{RE0S7mSdw&~SNBV}61jzcC3ms&ttLsS+?CF!~@ z=yjhW8M>`eYN>(;z)TLdt?C3R9aRTwAnRJ&y6eNDKfJ)%@f*2>3M7-#>W(v}K$S5o z6Gs{+M&p2GGn%evWLTB8aKI+O@f%QT6`I*Rv(F_7d=3KjFa`h~%$YMhm=u&+bLMMg zKw|Ewm7$FRE*lT&4UpVxosm{>qG`Z_P?}+PwkpqZ*2Nkpqt%oYh)F?AngK39>%sTG z0H)NcoMs((hV7ay&{H~YtpHZk(WC@j27VnKZ*2mswm74n$uTwop|n=Ry2->*Q76ov zcIH#Va$KXzTObO~UqNp`D%t};4~gCOt=S+6mM!_k*UfNng~H4Rp;Gs@hewXRl| z0V@|&)^Us05)q$yT)T-Zrga_7NP@9RAq7)v;j^|hwd~Ltmtf`d!HsEn)&D>EezzbK zO0A5YrJc6eXtlQN2m&ANwvnl;5B7Ieuefd#x}FPYjLL#2AG8uUrB(rSYg5zD8LKRK z2&L*F&i~@WlvH0fvRX&Y@r5>AXWY z`cj)=BaPC=Q*~8CAVKE$8Tk2zm}|NCH{PwE5U`R$S(ETHGF)&a9q)f 0) { - // Use first valid pokemon to execute the theivery - const pokemon = validPokemon[0]; - encounter.setDialogueToken("thiefPokemon", pokemon.name); - encounter.setDialogueToken(...moveRequirement.getDialogueToken(scene, pokemon)); + encounter.options[1].meetsRequirements(scene); + const primaryPokemon = encounter.options[1].primaryPokemon; + if (primaryPokemon) { + // Use primaryPokemon to execute the thievery encounter.dialogue.encounterOptionsDialogue.options[1].buttonTooltip = "mysteryEncounter:fight_or_flight_option_2_steal_tooltip"; encounter.dialogue.encounterOptionsDialogue.options[1].style = TextStyle.SUMMARY_GREEN; } else { @@ -104,44 +92,44 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false }); await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]); }) - .withOptionPhase(async (scene: BattleScene) => { - // Pick steal - const encounter = scene.currentBattle.mysteryEncounter; - const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption; - setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false }); + .withOption(new MysteryEncounterOptionBuilder() + .withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically + .withDisabledOnRequirementsNotMet(false) + .withOptionPhase(async (scene: BattleScene) => { + // Pick steal + const encounter = scene.currentBattle.mysteryEncounter; + const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption; + setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false }); - // If player has a stealing move, they succeed automatically - const moveRequirement = new MoveRequirement(validMovesForSteal); - const validPokemon = moveRequirement.queryParty(scene.getParty()); - if (validPokemon?.length > 0) { - // Use first valid pokemon to execute the theivery - const pokemon = validPokemon[0]; - encounter.setDialogueToken("thiefPokemon", pokemon.name); - encounter.setDialogueToken(...moveRequirement.getDialogueToken(scene, pokemon)); - await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_steal_result"); - leaveEncounterWithoutBattle(scene); - return; - } + // If player has a stealing move, they succeed automatically + const primaryPokemon = encounter.options[1].primaryPokemon; + if (primaryPokemon) { + // Use primaryPokemon to execute the thievery + await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_steal_result"); + leaveEncounterWithoutBattle(scene); + return; + } - const roll = randSeedInt(16); - if (roll > 6) { - // Noticed and attacked by boss, gets +1 to all stats at start of fight (62.5%) - const config = scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]; - config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON]; - config.pokemonConfigs[0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { - pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken("enemyPokemon", pokemon.name); - queueEncounterMessage(pokemon.scene, "mysteryEncounter:fight_or_flight_boss_enraged"); - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1)); - }; - await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_bad_result"); - await initBattleWithEnemyConfig(scene, config); - } else { - // Steal item (37.5%) - // Display result message then proceed to rewards - await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_good_result"); - leaveEncounterWithoutBattle(scene); - } - }) + const roll = randSeedInt(16); + if (roll > 6) { + // Noticed and attacked by boss, gets +1 to all stats at start of fight (62.5%) + const config = scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]; + config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON]; + config.pokemonConfigs[0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { + pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken("enemyPokemon", pokemon.name); + queueEncounterMessage(pokemon.scene, "mysteryEncounter:fight_or_flight_boss_enraged"); + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1)); + }; + await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_bad_result"); + await initBattleWithEnemyConfig(scene, config); + } else { + // Steal item (37.5%) + // Display result message then proceed to rewards + await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_good_result"); + leaveEncounterWithoutBattle(scene); + } + }) + .build()) .withOptionPhase(async (scene: BattleScene) => { // Leave encounter with no rewards or exp leaveEncounterWithoutBattle(scene, true); diff --git a/src/data/mystery-encounters/encounters/sleeping-snorlax.ts b/src/data/mystery-encounters/encounters/sleeping-snorlax.ts index cb2029272ef..444ce5a6581 100644 --- a/src/data/mystery-encounters/encounters/sleeping-snorlax.ts +++ b/src/data/mystery-encounters/encounters/sleeping-snorlax.ts @@ -2,7 +2,6 @@ import { modifierTypes } from "#app/modifier/modifier-type"; import { BerryType } from "#enums/berry-type"; -import { Moves } from "#enums/moves"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import BattleScene from "../../../battle-scene"; @@ -19,6 +18,7 @@ import { leaveEncounterWithoutBattle, queueEncounterMessage, setEncounterExp, setEncounterRewards } from "../mystery-encounter-utils"; +import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; export const SleepingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuilder .withEncounterType(MysteryEncounterType.SLEEPING_SNORLAX) @@ -98,7 +98,7 @@ export const SleepingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuilde } }) .withOption(new MysteryEncounterOptionBuilder() - .withPrimaryPokemonRequirement(new MoveRequirement([Moves.PLUCK, Moves.COVET, Moves.KNOCK_OFF, Moves.THIEF, Moves.TRICK, Moves.SWITCHEROO])) + .withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) .withOptionPhase(async (scene: BattleScene) => { // Steal the Snorlax's Leftovers const instance = scene.currentBattle.mysteryEncounter; diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index a1f8a73192e..0244281be26 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -16,36 +16,148 @@ export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1; export const WIGHT_INCREMENT_ON_SPAWN_MISS = 5; export const AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 15; +export const EXTREME_ENCOUNTER_BIOMES = [ + Biome.SEA, + Biome.SEABED, + Biome.BADLANDS, + Biome.DESERT, + Biome.ICE_CAVE, + Biome.VOLCANO, + Biome.WASTELAND, + Biome.ABYSS, + Biome.SPACE, + Biome.END +]; + +export const NON_EXTREME_ENCOUNTER_BIOMES = [ + Biome.TOWN, + Biome.PLAINS, + Biome.GRASS, + Biome.TALL_GRASS, + Biome.METROPOLIS, + Biome.FOREST, + Biome.SWAMP, + Biome.BEACH, + Biome.LAKE, + Biome.MOUNTAIN, + Biome.CAVE, + Biome.MEADOW, + Biome.POWER_PLANT, + Biome.GRAVEYARD, + Biome.DOJO, + Biome.FACTORY, + Biome.RUINS, + Biome.CONSTRUCTION_SITE, + Biome.JUNGLE, + Biome.FAIRY_CAVE, + Biome.TEMPLE, + Biome.SLUM, + Biome.SNOWY_FOREST, + Biome.ISLAND, + Biome.LABORATORY +]; + +/** + * Places where you could very reasonably expect to encounter a single human + * + * Diff from NON_EXTREME_ENCOUNTER_BIOMES: + * + BADLANDS + * + DESERT + * + ICE_CAVE + */ +export const HUMAN_TRANSITABLE_BIOMES = [ + Biome.TOWN, + Biome.PLAINS, + Biome.GRASS, + Biome.TALL_GRASS, + Biome.METROPOLIS, + Biome.FOREST, + Biome.SWAMP, + Biome.BEACH, + Biome.LAKE, + Biome.MOUNTAIN, + Biome.BADLANDS, + Biome.CAVE, + Biome.DESERT, + Biome.ICE_CAVE, + Biome.MEADOW, + Biome.POWER_PLANT, + Biome.GRAVEYARD, + Biome.DOJO, + Biome.FACTORY, + Biome.RUINS, + Biome.CONSTRUCTION_SITE, + Biome.JUNGLE, + Biome.FAIRY_CAVE, + Biome.TEMPLE, + Biome.SLUM, + Biome.SNOWY_FOREST, + Biome.ISLAND, + Biome.LABORATORY +]; + +/** + * Places where you could expect a town or city, some form of large civilization + */ +export const CIVILIZATION_ENCOUNTER_BIOMES = [ + Biome.TOWN, + Biome.PLAINS, + Biome.GRASS, + Biome.TALL_GRASS, + Biome.METROPOLIS, + Biome.BEACH, + Biome.LAKE, + Biome.MEADOW, + Biome.POWER_PLANT, + Biome.GRAVEYARD, + Biome.DOJO, + Biome.FACTORY, + Biome.CONSTRUCTION_SITE, + Biome.SLUM, + Biome.ISLAND +]; + export const allMysteryEncounters: { [encounterType: number]: MysteryEncounter } = {}; +// TO ENABLE AN ENCOUNTER IN ALL BIOMES, DO NOT SPECIFY IN ANY OF THESE LISTS/MAPS + +const extremeBiomeEncounters: MysteryEncounterType[] = []; + +const nonExtremeBiomeEncounters: MysteryEncounterType[] = [ + MysteryEncounterType.FIELD_TRIP +]; + +const humanTransitableBiomeEncounters: MysteryEncounterType[] = [ + MysteryEncounterType.MYSTERIOUS_CHALLENGERS, + MysteryEncounterType.SHADY_VITAMIN_DEALER +]; + +const civilizationBiomeEncounters: MysteryEncounterType[] = [ + MysteryEncounterType.DEPARTMENT_STORE_SALE +]; + +const anyBiomeEncounters: MysteryEncounterType[] = [ + MysteryEncounterType.FIGHT_OR_FLIGHT, + MysteryEncounterType.DARK_DEAL, + MysteryEncounterType.MYSTERIOUS_CHEST, + MysteryEncounterType.TRAINING_SESSION +]; + // Add MysteryEncounterType to biomes to enable it exclusively for those biomes -// To enable an encounter in all biomes, do not add to this map export const mysteryEncountersByBiome = new Map([ - [Biome.TOWN, [ - MysteryEncounterType.DEPARTMENT_STORE_SALE - ]], - [Biome.PLAINS, [ - MysteryEncounterType.DEPARTMENT_STORE_SALE - ]], + [Biome.TOWN, []], + [Biome.PLAINS, []], [Biome.GRASS, [ MysteryEncounterType.SLEEPING_SNORLAX, - MysteryEncounterType.DEPARTMENT_STORE_SALE - ]], - [Biome.TALL_GRASS, [ - MysteryEncounterType.DEPARTMENT_STORE_SALE - ]], - [Biome.METROPOLIS, [ - MysteryEncounterType.DEPARTMENT_STORE_SALE ]], + [Biome.TALL_GRASS, []], + [Biome.METROPOLIS, []], [Biome.FOREST, [ MysteryEncounterType.SLEEPING_SNORLAX ]], - [Biome.SEA, []], [Biome.SWAMP, []], - [Biome.BEACH, [ - MysteryEncounterType.DEPARTMENT_STORE_SALE - ]], + [Biome.BEACH, []], [Biome.LAKE, []], [Biome.SEABED, []], [Biome.MOUNTAIN, [ @@ -57,31 +169,21 @@ export const mysteryEncountersByBiome = new Map([ ]], [Biome.DESERT, []], [Biome.ICE_CAVE, []], - [Biome.MEADOW, [ - MysteryEncounterType.DEPARTMENT_STORE_SALE - ]], - [Biome.POWER_PLANT, [ - MysteryEncounterType.DEPARTMENT_STORE_SALE - ]], + [Biome.MEADOW, []], + [Biome.POWER_PLANT, []], [Biome.VOLCANO, []], [Biome.GRAVEYARD, []], [Biome.DOJO, []], - [Biome.FACTORY, [ - MysteryEncounterType.DEPARTMENT_STORE_SALE - ]], + [Biome.FACTORY, []], [Biome.RUINS, []], [Biome.WASTELAND, []], [Biome.ABYSS, []], [Biome.SPACE, []], - [Biome.CONSTRUCTION_SITE, [ - MysteryEncounterType.DEPARTMENT_STORE_SALE - ]], + [Biome.CONSTRUCTION_SITE, []], [Biome.JUNGLE, []], [Biome.FAIRY_CAVE, []], [Biome.TEMPLE, []], - [Biome.SLUM, [ - MysteryEncounterType.DEPARTMENT_STORE_SALE - ]], + [Biome.SLUM, []], [Biome.SNOWY_FOREST, []], [Biome.ISLAND, []], [Biome.LABORATORY, []] @@ -90,7 +192,7 @@ export const mysteryEncountersByBiome = new Map([ export function initMysteryEncounters() { allMysteryEncounters[MysteryEncounterType.MYSTERIOUS_CHALLENGERS] = MysteriousChallengersEncounter; allMysteryEncounters[MysteryEncounterType.MYSTERIOUS_CHEST] = MysteriousChestEncounter; - allMysteryEncounters[MysteryEncounterType.DARK_DEAL] = DarkDealEncounter; + allMysteryEncounters[MysteryEncounterType.DARK_DEAL] = DarkDealEncounter; // TODO: move to HUMAN if we add an ANY biome ROGUE tier encounter allMysteryEncounters[MysteryEncounterType.FIGHT_OR_FLIGHT] = FightOrFlightEncounter; allMysteryEncounters[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionEncounter; allMysteryEncounters[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxEncounter; @@ -98,15 +200,49 @@ export function initMysteryEncounters() { allMysteryEncounters[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerEncounter; allMysteryEncounters[MysteryEncounterType.FIELD_TRIP] = FieldTripEncounter; - // 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); - mysteryEncountersByBiome.forEach(biomeEncounters => { - biomeEncounters.forEach(e => { - if (anyBiomeEncounters.includes(e)) { - anyBiomeEncounters.splice(anyBiomeEncounters.indexOf(e), 1); + // Add extreme encounters to biome map + extremeBiomeEncounters.forEach(encounter => { + EXTREME_ENCOUNTER_BIOMES.forEach(biome => { + const encountersForBiome = mysteryEncountersByBiome.get(biome); + if (!encountersForBiome.includes(encounter)) { + encountersForBiome.push(encounter); + } + }); + }); + // Add non-extreme encounters to biome map + nonExtremeBiomeEncounters.forEach(encounter => { + NON_EXTREME_ENCOUNTER_BIOMES.forEach(biome => { + const encountersForBiome = mysteryEncountersByBiome.get(biome); + if (!encountersForBiome.includes(encounter)) { + encountersForBiome.push(encounter); + } + }); + }); + // Add human encounters to biome map + humanTransitableBiomeEncounters.forEach(encounter => { + HUMAN_TRANSITABLE_BIOMES.forEach(biome => { + const encountersForBiome = mysteryEncountersByBiome.get(biome); + if (!encountersForBiome.includes(encounter)) { + encountersForBiome.push(encounter); + } + }); + }); + // Add civilization encounters to biome map + civilizationBiomeEncounters.forEach(encounter => { + CIVILIZATION_ENCOUNTER_BIOMES.forEach(biome => { + const encountersForBiome = mysteryEncountersByBiome.get(biome); + if (!encountersForBiome.includes(encounter)) { + encountersForBiome.push(encounter); } }); }); - mysteryEncountersByBiome.forEach(biomeEncounters => biomeEncounters.push(...anyBiomeEncounters)); + // Add ANY biome encounters to biome map + mysteryEncountersByBiome.forEach(biomeEncounters => { + anyBiomeEncounters.forEach(encounter => { + if (!biomeEncounters.includes(encounter)) { + biomeEncounters.push(encounter); + } + }); + }); } diff --git a/src/data/mystery-encounters/requirements/requirement-groups.ts b/src/data/mystery-encounters/requirements/requirement-groups.ts new file mode 100644 index 00000000000..3281129e63e --- /dev/null +++ b/src/data/mystery-encounters/requirements/requirement-groups.ts @@ -0,0 +1,42 @@ +import { Moves } from "#enums/moves"; + +export const STEALING_MOVES = [ + Moves.PLUCK, + Moves.COVET, + Moves.KNOCK_OFF, + Moves.FAKE_OUT, + Moves.THIEF, + Moves.TRICK, + Moves.SWITCHEROO +]; + +export const DISTRACTION_MOVES = [ + Moves.FAKE_OUT, + Moves.FOLLOW_ME, + Moves.TAUNT, + Moves.ROAR, + Moves.TELEPORT, + Moves.CHARM, + Moves.FAKE_TEARS, + Moves.TICKLE, + Moves.CAPTIVATE, + Moves.RAGE_POWDER, + Moves.SUBSTITUTE, + Moves.SHED_TAIL +]; + +export const PROTECTING_MOVES = [ + Moves.PROTECT, + Moves.WIDE_GUARD, + Moves.MAX_GUARD, + Moves.SAFEGUARD, + Moves.REFLECT, + Moves.BARRIER, + Moves.QUICK_GUARD, + Moves.FLOWER_SHIELD, + Moves.KINGS_SHIELD, + Moves.CRAFTY_SHIELD, + Moves.SPIKY_SHIELD, + Moves.OBSTRUCT, + Moves.DETECT +]; diff --git a/src/locales/en/mystery-encounter.ts b/src/locales/en/mystery-encounter.ts index 59fcf6940c2..b07e22f7a9a 100644 --- a/src/locales/en/mystery-encounter.ts +++ b/src/locales/en/mystery-encounter.ts @@ -43,14 +43,14 @@ export const mysteryEncounter: SimpleTranslationEntries = { "fight_or_flight_option_1_tooltip": "(-) Hard Battle\n(+) New Item", "fight_or_flight_option_2_label": "Steal the item", "fight_or_flight_option_2_tooltip": "@[SUMMARY_GREEN]{(35%) Steal Item}\n@[SUMMARY_BLUE]{(65%) Harder Battle}", - "fight_or_flight_option_2_steal_tooltip": "@[SUMMARY_GREEN]{(?) Use a Pokémon Move}", + "fight_or_flight_option_2_steal_tooltip": "(+) @ec{option2PrimaryName} uses @ec{option2PrimaryMove}", "fight_or_flight_option_3_label": "Leave", "fight_or_flight_option_3_tooltip": "(-) No Rewards", "fight_or_flight_option_1_selected_message": "You approach the\nPokémon without fear.", "fight_or_flight_option_2_good_result": `.@d{32}.@d{32}.@d{32} $You manage to sneak your way\npast and grab the item!`, "fight_or_flight_option_2_steal_result": `.@d{32}.@d{32}.@d{32} - $Your @ec{thiefPokemon} helps you out and uses @ec{move}! + $Your @ec{option2PrimaryName} helps you out and uses @ec{option2PrimaryMove}! $ You nabbed the item!`, "fight_or_flight_option_2_bad_result": `.@d{32}.@d{32}.@d{32} $The Pokémon catches you\nas you try to sneak around!`, diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 8be862b965d..306fb01c575 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -110,7 +110,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { this.unblockInput(); }, 300); }); - } else if (this.blockInput || !this.optionsMeetsReqs[cursor]) { + } else if (this.blockInput || (!this.optionsMeetsReqs[cursor] && this.filteredEncounterOptions[cursor].isDisabledOnRequirementsNotMet)) { success = false; } else { const selected = this.filteredEncounterOptions[cursor]; @@ -253,7 +253,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { if (this.blockInput) { this.blockInput = false; for (let i = 0; i < this.optionsContainer.length - 1; i++) { - if (!this.optionsMeetsReqs[i]) { + if (!this.optionsMeetsReqs[i] && this.filteredEncounterOptions[i].isDisabledOnRequirementsNotMet) { continue; } (this.optionsContainer.getAt(i) as Phaser.GameObjects.Text).setAlpha(1); @@ -334,9 +334,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { optionText.setText(text); } - - - if (!this.optionsMeetsReqs[i]) { + if (!this.optionsMeetsReqs[i] && this.filteredEncounterOptions[i].isDisabledOnRequirementsNotMet) { optionText.setAlpha(0.5); } if (this.blockInput) { @@ -424,7 +422,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { const mysteryEncounter = this.scene.currentBattle.mysteryEncounter; let text; const option = mysteryEncounter.dialogue.encounterOptionsDialogue.options[cursor]; - if (!this.optionsMeetsReqs[cursor] && option.disabledTooltip) { + if (!this.optionsMeetsReqs[cursor] && this.filteredEncounterOptions[cursor].isDisabledOnRequirementsNotMet && option.disabledTooltip) { text = getEncounterText(this.scene, option.disabledTooltip, TextStyle.TOOLTIP_CONTENT); } else { text = getEncounterText(this.scene, option.buttonTooltip, TextStyle.TOOLTIP_CONTENT); From b6ef21058490430e9646bfaf94cbf41bc479b441 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 11 Jul 2024 14:43:57 -0400 Subject: [PATCH 03/29] remove unnecessary styling override on fight or flight option --- src/data/mystery-encounters/encounters/fight-or-flight.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/data/mystery-encounters/encounters/fight-or-flight.ts b/src/data/mystery-encounters/encounters/fight-or-flight.ts index 4392468c7bc..8a82439772c 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight.ts @@ -16,7 +16,6 @@ import { regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; import { StatChangePhase } from "#app/phases"; -import { TextStyle } from "#app/ui/text"; import { randSeedInt } from "#app/utils"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; @@ -78,10 +77,8 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder if (primaryPokemon) { // Use primaryPokemon to execute the thievery encounter.dialogue.encounterOptionsDialogue.options[1].buttonTooltip = "mysteryEncounter:fight_or_flight_option_2_steal_tooltip"; - encounter.dialogue.encounterOptionsDialogue.options[1].style = TextStyle.SUMMARY_GREEN; } else { encounter.dialogue.encounterOptionsDialogue.options[1].buttonTooltip = "mysteryEncounter:fight_or_flight_option_2_tooltip"; - encounter.dialogue.encounterOptionsDialogue.options[1].style = null; } return true; From c72bed0ee76b7f50b9be7400703da9fb3b9e2b53 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 11 Jul 2024 14:58:44 -0400 Subject: [PATCH 04/29] small cleanup of changes --- src/data/mystery-encounters/mystery-encounters.ts | 15 ++++++++++++--- src/locales/en/mystery-encounter.ts | 7 +++---- src/ui/mystery-encounter-ui-handler.ts | 1 + 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index 0244281be26..1651430e59d 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -119,7 +119,6 @@ export const CIVILIZATION_ENCOUNTER_BIOMES = [ export const allMysteryEncounters: { [encounterType: number]: MysteryEncounter } = {}; -// TO ENABLE AN ENCOUNTER IN ALL BIOMES, DO NOT SPECIFY IN ANY OF THESE LISTS/MAPS const extremeBiomeEncounters: MysteryEncounterType[] = []; @@ -136,6 +135,10 @@ const civilizationBiomeEncounters: MysteryEncounterType[] = [ MysteryEncounterType.DEPARTMENT_STORE_SALE ]; + +/** + * To add an encounter to every biome possible, use this array + */ const anyBiomeEncounters: MysteryEncounterType[] = [ MysteryEncounterType.FIGHT_OR_FLIGHT, MysteryEncounterType.DARK_DEAL, @@ -143,7 +146,13 @@ const anyBiomeEncounters: MysteryEncounterType[] = [ MysteryEncounterType.TRAINING_SESSION ]; -// Add MysteryEncounterType to biomes to enable it exclusively for those biomes +/** + * ENCOUNTER BIOME MAPPING + * To add an Encounter to a biome group, instead of cluttering the map, use the biome group arrays above + * + * Adding specific Encounters to the mysteryEncountersByBiome map is for specific cases and special circumstances + * that biome groups do not cover + */ export const mysteryEncountersByBiome = new Map([ [Biome.TOWN, []], [Biome.PLAINS, []], @@ -192,7 +201,7 @@ export const mysteryEncountersByBiome = new Map([ export function initMysteryEncounters() { allMysteryEncounters[MysteryEncounterType.MYSTERIOUS_CHALLENGERS] = MysteriousChallengersEncounter; allMysteryEncounters[MysteryEncounterType.MYSTERIOUS_CHEST] = MysteriousChestEncounter; - allMysteryEncounters[MysteryEncounterType.DARK_DEAL] = DarkDealEncounter; // TODO: move to HUMAN if we add an ANY biome ROGUE tier encounter + allMysteryEncounters[MysteryEncounterType.DARK_DEAL] = DarkDealEncounter; allMysteryEncounters[MysteryEncounterType.FIGHT_OR_FLIGHT] = FightOrFlightEncounter; allMysteryEncounters[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionEncounter; allMysteryEncounters[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxEncounter; diff --git a/src/locales/en/mystery-encounter.ts b/src/locales/en/mystery-encounter.ts index b07e22f7a9a..f944615452a 100644 --- a/src/locales/en/mystery-encounter.ts +++ b/src/locales/en/mystery-encounter.ts @@ -108,7 +108,6 @@ export const mysteryEncounter: SimpleTranslationEntries = { "field_trip_title": "Field Trip", "field_trip_description": "A teacher is requesting a move demonstration from a Pokémon. Depending on the move you choose, she might have something useful for you in exchange.", "field_trip_query": "Which move category will you show off?", - // "field_trip_invalid_selection": "Pokémon doesn't know that type of move.", "field_trip_option_1_label": "A Physical Move", "field_trip_option_1_tooltip": "(+) Physical Item Rewards", "field_trip_option_2_label": "A Special Move", @@ -141,6 +140,7 @@ export const mysteryEncounter: SimpleTranslationEntries = { "mysterious_challengers_outro_win": "The mysterious challenger was defeated!", // Mystery Encounters -- Rare Tier + "training_session_intro_message": "You've come across some\ntraining tools and supplies.", "training_session_title": "Training Session", "training_session_description": "These supplies look like they could be used to train a member of your party! There are a few ways you could train your Pokémon, by battling against it with the rest of your team.", @@ -173,8 +173,8 @@ export const mysteryEncounter: SimpleTranslationEntries = { "dark_deal_title": "Dark Deal", "dark_deal_description": "The disturbing fellow holds up some Pokéballs.\n\"I'll make it worth your while! You can have these strong Pokéballs as payment, All I need is a Pokémon from your team! Hehe...\"", "dark_deal_query": "What will you do?", - "dark_deal_option_1_label": "Accept", // Give player 10 rogue balls. Remove a random Pokémon from player's party. Fight a legendary Pokémon as a boss - "dark_deal_option_1_tooltip": "(+) 5 Rogue Balls\n(?) Enhance a Random Pokémon", // Give player 10 rogue balls. Remove a random Pokémon from player's party. Fight a legendary Pokémon as a boss + "dark_deal_option_1_label": "Accept", + "dark_deal_option_1_tooltip": "(+) 5 Rogue Balls\n(?) Enhance a Random Pokémon", "dark_deal_option_2_label": "Refuse", "dark_deal_option_2_tooltip": "(-) No Rewards", "dark_deal_option_1_selected": `Let's see, that @ec{pokeName} will do nicely! @@ -206,6 +206,5 @@ export const mysteryEncounter: SimpleTranslationEntries = { $But on the bright side, the Snorlax left something behind... $@s{item_fanfare}You gained a Berry!`, "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!", } as const; diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 306fb01c575..cad678acf87 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -325,6 +325,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { let text; if (this.filteredEncounterOptions[i].hasRequirements() && this.optionsMeetsReqs[i]) { // Options with special requirements that are met are automatically colored green + // In cases where isDisabledOnRequirementsNotMet = false and requirements are not met, option will not be auto-colored text = getEncounterText(this.scene, optionDialogue.buttonLabel, TextStyle.SUMMARY_GREEN); } else { text = getEncounterText(this.scene, optionDialogue.buttonLabel, optionDialogue.style ? optionDialogue.style : TextStyle.WINDOW); From 390b2e93f92d8aae1a93e6c3c8ff7aa1c60e7dc2 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 11 Jul 2024 15:03:57 -0400 Subject: [PATCH 05/29] remove fake out from stealing move group --- src/data/mystery-encounters/requirements/requirement-groups.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/data/mystery-encounters/requirements/requirement-groups.ts b/src/data/mystery-encounters/requirements/requirement-groups.ts index 3281129e63e..c4d1c592df4 100644 --- a/src/data/mystery-encounters/requirements/requirement-groups.ts +++ b/src/data/mystery-encounters/requirements/requirement-groups.ts @@ -4,7 +4,6 @@ export const STEALING_MOVES = [ Moves.PLUCK, Moves.COVET, Moves.KNOCK_OFF, - Moves.FAKE_OUT, Moves.THIEF, Moves.TRICK, Moves.SWITCHEROO From 74125ef1cfd0db811a2d3e35c0b8cc90f5dffbad Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 12:56:15 -0700 Subject: [PATCH 06/29] WIP: add option to use dialogues in builder --- .../dialogue/dark-deal-dialogue.ts | 68 +++--- .../encounters/dark-deal.ts | 197 ++++++++++++------ .../mystery-encounter-dialogue.ts | 2 +- .../mystery-encounter-option.ts | 6 + .../mystery-encounters/mystery-encounter.ts | 19 +- src/overrides.ts | 6 +- src/phases.ts | 2 +- src/ui/mystery-encounter-ui-handler.ts | 6 +- 8 files changed, 195 insertions(+), 111 deletions(-) diff --git a/src/data/mystery-encounters/dialogue/dark-deal-dialogue.ts b/src/data/mystery-encounters/dialogue/dark-deal-dialogue.ts index 25b31f79a50..9ee7ce5472b 100644 --- a/src/data/mystery-encounters/dialogue/dark-deal-dialogue.ts +++ b/src/data/mystery-encounters/dialogue/dark-deal-dialogue.ts @@ -1,44 +1,44 @@ import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; export const DarkDealDialogue: MysteryEncounterDialogue = { - intro: [ - { - text: "mysteryEncounter:dark_deal_intro_message" - }, - { - speaker: "mysteryEncounter:dark_deal_speaker", - text: "mysteryEncounter:dark_deal_intro_dialogue" - } - ], + // intro: [ + // { + // text: "mysteryEncounter:dark_deal_intro_message" + // }, + // { + // speaker: "mysteryEncounter:dark_deal_speaker", + // text: "mysteryEncounter:dark_deal_intro_dialogue" + // } + // ], encounterOptionsDialogue: { title: "mysteryEncounter:dark_deal_title", description: "mysteryEncounter:dark_deal_description", query: "mysteryEncounter:dark_deal_query", - options: [ - { - buttonLabel: "mysteryEncounter:dark_deal_option_1_label", - buttonTooltip: "mysteryEncounter:dark_deal_option_1_tooltip", - selected: [ - { - speaker: "mysteryEncounter:dark_deal_speaker", - text: "mysteryEncounter:dark_deal_option_1_selected" - }, - { - text: "mysteryEncounter:dark_deal_option_1_selected_message" - } - ] - }, - { - buttonLabel: "mysteryEncounter:dark_deal_option_2_label", - buttonTooltip: "mysteryEncounter:dark_deal_option_2_tooltip", - selected: [ - { - speaker: "mysteryEncounter:dark_deal_speaker", - text: "mysteryEncounter:dark_deal_option_2_selected" - } - ] - } - ] + // options: [ + // { + // buttonLabel: "mysteryEncounter:dark_deal_option_1_label", + // buttonTooltip: "mysteryEncounter:dark_deal_option_1_tooltip", + // selected: [ + // { + // speaker: "mysteryEncounter:dark_deal_speaker", + // text: "mysteryEncounter:dark_deal_option_1_selected" + // }, + // { + // text: "mysteryEncounter:dark_deal_option_1_selected_message" + // } + // ] + // }, + // { + // buttonLabel: "mysteryEncounter:dark_deal_option_2_label", + // buttonTooltip: "mysteryEncounter:dark_deal_option_2_tooltip", + // selected: [ + // { + // speaker: "mysteryEncounter:dark_deal_speaker", + // text: "mysteryEncounter:dark_deal_option_2_selected" + // } + // ] + // } + // ] }, outro: [ { diff --git a/src/data/mystery-encounters/encounters/dark-deal.ts b/src/data/mystery-encounters/encounters/dark-deal.ts index 1985ffcf8dd..c1651aa8dec 100644 --- a/src/data/mystery-encounters/encounters/dark-deal.ts +++ b/src/data/mystery-encounters/encounters/dark-deal.ts @@ -7,14 +7,18 @@ import BattleScene from "../../../battle-scene"; import { AddPokeballModifierType } from "../../../modifier/modifier-type"; import { PokeballType } from "../../pokeball"; import { getPokemonSpecies } from "../../pokemon-species"; -import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; +import MysteryEncounter, { + MysteryEncounterBuilder, + MysteryEncounterTier, +} from "../mystery-encounter"; import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { - EnemyPartyConfig, EnemyPokemonConfig, + EnemyPartyConfig, + EnemyPokemonConfig, getRandomPlayerPokemon, getRandomSpeciesByStarterTier, initBattleWithEnemyConfig, - leaveEncounterWithoutBattle + leaveEncounterWithoutBattle, } from "../mystery-encounter-utils"; // Exclude Ultra Beasts, Paradox, Necrozma, Eternatus, and egg-locked mythicals @@ -63,71 +67,132 @@ const excludedBosses = [ Species.ARCEUS, Species.VICTINI, Species.MELTAN, - Species.PECHARUNT + Species.PECHARUNT, ]; -export const DarkDealEncounter: MysteryEncounter = MysteryEncounterBuilder - .withEncounterType(MysteryEncounterType.DARK_DEAL) - .withEncounterTier(MysteryEncounterTier.ROGUE) - .withIntroSpriteConfigs([ - { - spriteKey: "mad_scientist_m", - fileRoot: "mystery-encounters", - hasShadow: true - }, - { - spriteKey: "dark_deal_porygon", - fileRoot: "mystery-encounters", - hasShadow: true, - repeat: true - } - ]) - .withSceneWaveRangeRequirement(30, 180) // waves 30 to 180 - .withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party - .withCatchAllowed(true) - .withOption(new MysteryEncounterOptionBuilder() - .withPreOptionPhase(async (scene: BattleScene) => { - // Removes random pokemon (including fainted) from party and adds name to dialogue data tokens - // Will never return last battle able mon and instead pick fainted/unable to battle - const removedPokemon = getRandomPlayerPokemon(scene, false, true); - scene.removePokemonFromPlayerParty(removedPokemon); +export const DarkDealEncounter: MysteryEncounter = + MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DARK_DEAL) + .withEncounterTier(MysteryEncounterTier.ROGUE) + .withIntroSpriteConfigs([ + { + spriteKey: "mad_scientist_m", + fileRoot: "mystery-encounters", + hasShadow: true, + }, + { + spriteKey: "dark_deal_porygon", + fileRoot: "mystery-encounters", + hasShadow: true, + repeat: true, + }, + ]) + .withIntroDialogue([ + { + text: "mysteryEncounter:dark_deal_intro_message", + }, + { + speaker: "mysteryEncounter:dark_deal_speaker", + text: "mysteryEncounter:dark_deal_intro_dialogue", + }, + ]) + .withSceneWaveRangeRequirement(30, 180) // waves 30 to 180 + .withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party + .withCatchAllowed(true) + .withOption( + new MysteryEncounterOptionBuilder() + .withDialogue({ + buttonLabel: "mysteryEncounter:dark_deal_option_1_label", + buttonTooltip: "mysteryEncounter:dark_deal_option_1_tooltip", + selected: [ + { + speaker: "mysteryEncounter:dark_deal_speaker", + text: "mysteryEncounter:dark_deal_option_1_selected", + }, + { + text: "mysteryEncounter:dark_deal_option_1_selected_message", + }, + ], + }) + .withPreOptionPhase(async (scene: BattleScene) => { + // Removes random pokemon (including fainted) from party and adds name to dialogue data tokens + // Will never return last battle able mon and instead pick fainted/unable to battle + const removedPokemon = getRandomPlayerPokemon(scene, false, true); + scene.removePokemonFromPlayerParty(removedPokemon); - scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", removedPokemon.name); + scene.currentBattle.mysteryEncounter.setDialogueToken( + "pokeName", + removedPokemon.name + ); - // Store removed pokemon types - scene.currentBattle.mysteryEncounter.misc = [removedPokemon.species.type1]; - if (removedPokemon.species.type2) { - scene.currentBattle.mysteryEncounter.misc.push(removedPokemon.species.type2); - } - }) - .withOptionPhase(async (scene: BattleScene) => { - // Give the player 5 Rogue Balls - scene.unshiftPhase(new ModifierRewardPhase(scene, () => new AddPokeballModifierType("rb", PokeballType.ROGUE_BALL, 5))); + // Store removed pokemon types + scene.currentBattle.mysteryEncounter.misc = [ + removedPokemon.species.type1, + ]; + if (removedPokemon.species.type2) { + scene.currentBattle.mysteryEncounter.misc.push( + removedPokemon.species.type2 + ); + } + }) + .withOptionPhase(async (scene: BattleScene) => { + // Give the player 5 Rogue Balls + scene.unshiftPhase( + new ModifierRewardPhase( + scene, + () => + new AddPokeballModifierType("rb", PokeballType.ROGUE_BALL, 5) + ) + ); - // Start encounter with random legendary (7-10 starter strength) that has level additive - const bossTypes = scene.currentBattle.mysteryEncounter.misc as Type[]; - // Starter egg tier, 35/50/10/5 %odds for tiers 6/7/8/9+ - const roll = randSeedInt(100); - const starterTier: number | [number, number] = roll > 65 ? 6 : roll > 15 ? 7 : roll > 5 ? 8 : [9, 10]; - const bossSpecies = getPokemonSpecies(getRandomSpeciesByStarterTier(starterTier, excludedBosses, bossTypes)); - const pokemonConfig: EnemyPokemonConfig = { - species: bossSpecies, - isBoss: true - }; - if (!isNullOrUndefined(bossSpecies.forms) && bossSpecies.forms.length > 0) { - pokemonConfig.formIndex = 0; - } - const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 0.75, - pokemonConfigs: [pokemonConfig] - }; - return initBattleWithEnemyConfig(scene, config); - }) - .build() - ) - .withOptionPhase(async (scene: BattleScene) => { - // Leave encounter with no rewards or exp - leaveEncounterWithoutBattle(scene, true); - return true; - }) - .build(); + // Start encounter with random legendary (7-10 starter strength) that has level additive + const bossTypes = scene.currentBattle.mysteryEncounter.misc as Type[]; + // Starter egg tier, 35/50/10/5 %odds for tiers 6/7/8/9+ + const roll = randSeedInt(100); + const starterTier: number | [number, number] = + roll > 65 ? 6 : roll > 15 ? 7 : roll > 5 ? 8 : [9, 10]; + const bossSpecies = getPokemonSpecies( + getRandomSpeciesByStarterTier( + starterTier, + excludedBosses, + bossTypes + ) + ); + const pokemonConfig: EnemyPokemonConfig = { + species: bossSpecies, + isBoss: true, + }; + if ( + !isNullOrUndefined(bossSpecies.forms) && + bossSpecies.forms.length > 0 + ) { + pokemonConfig.formIndex = 0; + } + const config: EnemyPartyConfig = { + levelAdditiveMultiplier: 0.75, + pokemonConfigs: [pokemonConfig], + }; + return initBattleWithEnemyConfig(scene, config); + }) + .build() + ) + .withOption( + new MysteryEncounterOptionBuilder() + .withDialogue({ + buttonLabel: "mysteryEncounter:dark_deal_option_2_label", + buttonTooltip: "mysteryEncounter:dark_deal_option_2_tooltip", + selected: [ + { + speaker: "mysteryEncounter:dark_deal_speaker", + text: "mysteryEncounter:dark_deal_option_2_selected", + }, + ], + }) + .withOptionPhase(async (scene: BattleScene) => { + // Leave encounter with no rewards or exp + + leaveEncounterWithoutBattle(scene, true); + return true; + }) + .build() + ) + .build(); diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index 1e2dfe85045..7a9427a5d4a 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -28,7 +28,7 @@ export class EncounterOptionsDialogue { title: TemplateStringsArray | `mysteryEncounter:${string}`; description: TemplateStringsArray | `mysteryEncounter:${string}`; query?: TemplateStringsArray | `mysteryEncounter:${string}`; - options: [OptionTextDisplay, OptionTextDisplay, ...OptionTextDisplay[]]; // Options array with minimum 2 options + options?: [...OptionTextDisplay[]]; // Options array with minimum 2 options } export default class MysteryEncounterDialogue { diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index f1316ff64f1..2d1b4e5cfb9 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -127,6 +127,7 @@ export class MysteryEncounterOptionBuilder implements Partial> { this.requirements.push(requirement); @@ -163,4 +164,9 @@ export class MysteryEncounterOptionBuilder implements Partial o.dialogue = this.dialogue.encounterOptionsDialogue.options[i]); + if (this.dialogue?.encounterOptionsDialogue) { + // this.options.forEach((o, i) => o.dialogue = this.dialogue.encounterOptionsDialogue.options[i]); } // Reset any dirty flags or encounter data @@ -408,9 +410,20 @@ export class MysteryEncounterBuilder implements Partial { * @returns */ withIntroSpriteConfigs(spriteConfigs: MysteryEncounterSpriteConfig[]): this & Pick { + console.debug("with intro sprite configs: ", spriteConfigs); return Object.assign(this, { spriteConfigs: spriteConfigs }); } + withIntroDialogue(dialogue: MysteryEncounterDialogue["intro"] = []): this { + console.debug("with intro dialogue: ", dialogue); + this.dialogue = {...this.dialogue, intro: dialogue }; + return this; + } + + withIntro({spriteConfigs, dialogue} : {spriteConfigs: MysteryEncounterSpriteConfig[], dialogue?: MysteryEncounterDialogue["intro"]}) { + return this.withIntroSpriteConfigs(spriteConfigs).withIntroDialogue(dialogue); + } + /** * OPTIONAL */ diff --git a/src/overrides.ts b/src/overrides.ts index ce706173691..f6a3778c4da 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -35,7 +35,7 @@ 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_WAVE_OVERRIDE: integer = 33; 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 @@ -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 = 10000; export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null; -export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null; +export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.DARK_DEAL; /** * MODIFIER / ITEM OVERRIDES diff --git a/src/phases.ts b/src/phases.ts index 091629b31d5..2233763dc7f 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -828,7 +828,7 @@ export class EncounterPhase extends BattlePhase { if (mysteryEncounter.onInit) { mysteryEncounter.onInit(this.scene); } - mysteryEncounter.populateDialogueTokensFromRequirements(this.scene); + // mysteryEncounter.populateDialogueTokensFromRequirements(this.scene); }, this.scene.currentBattle.waveIndex); // Add intro visuals for mystery encounter diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 163d79784f8..7be7f16e8bc 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -319,13 +319,13 @@ export default class MysteryEncounterUiHandler extends UiHandler { optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, i < 2 ? 0 : 16, "-", TextStyle.WINDOW, { wordWrap: { width: 558 }, fontSize: "80px", lineSpacing: -8 }); break; } - const option = mysteryEncounter.dialogue.encounterOptionsDialogue.options[i]; - const text = getEncounterText(this.scene, option.buttonLabel, option.style ? option.style : TextStyle.WINDOW); + const option = this.filteredEncounterOptions[i]; + const text = getEncounterText(this.scene, option.dialogue?.buttonLabel, option.dialogue?.style ? option.dialogue?.style : TextStyle.WINDOW); if (text) { optionText.setText(text); } - this.optionsMeetsReqs.push(this.filteredEncounterOptions[i].meetsRequirements(this.scene)); + this.optionsMeetsReqs.push(option.meetsRequirements(this.scene)); if (!this.optionsMeetsReqs[i]) { optionText.setAlpha(0.5); From c589689a6c067189f22b918e91a82f1914ac2045 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 13:26:21 -0700 Subject: [PATCH 07/29] migrate dark-deal encounter to new dialogue setup --- .../dialogue/dark-deal-dialogue.ts | 48 ------------- .../encounters/dark-deal.ts | 42 +++++++----- .../encounters/department-store-sale.ts | 8 +-- .../encounters/fight-or-flight.ts | 4 +- .../encounters/mysterious-challengers.ts | 6 +- .../encounters/mysterious-chest.ts | 2 +- .../encounters/shady-vitamin-dealer.ts | 2 +- .../encounters/sleeping-snorlax.ts | 4 +- .../mystery-encounter-dialogue.ts | 8 +-- .../mystery-encounters/mystery-encounter.ts | 68 ++++++++++++++++--- src/ui/mystery-encounter-ui-handler.ts | 11 +-- 11 files changed, 104 insertions(+), 99 deletions(-) delete mode 100644 src/data/mystery-encounters/dialogue/dark-deal-dialogue.ts diff --git a/src/data/mystery-encounters/dialogue/dark-deal-dialogue.ts b/src/data/mystery-encounters/dialogue/dark-deal-dialogue.ts deleted file mode 100644 index 9ee7ce5472b..00000000000 --- a/src/data/mystery-encounters/dialogue/dark-deal-dialogue.ts +++ /dev/null @@ -1,48 +0,0 @@ -import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; - -export const DarkDealDialogue: MysteryEncounterDialogue = { - // intro: [ - // { - // text: "mysteryEncounter:dark_deal_intro_message" - // }, - // { - // speaker: "mysteryEncounter:dark_deal_speaker", - // text: "mysteryEncounter:dark_deal_intro_dialogue" - // } - // ], - encounterOptionsDialogue: { - title: "mysteryEncounter:dark_deal_title", - description: "mysteryEncounter:dark_deal_description", - query: "mysteryEncounter:dark_deal_query", - // options: [ - // { - // buttonLabel: "mysteryEncounter:dark_deal_option_1_label", - // buttonTooltip: "mysteryEncounter:dark_deal_option_1_tooltip", - // selected: [ - // { - // speaker: "mysteryEncounter:dark_deal_speaker", - // text: "mysteryEncounter:dark_deal_option_1_selected" - // }, - // { - // text: "mysteryEncounter:dark_deal_option_1_selected_message" - // } - // ] - // }, - // { - // buttonLabel: "mysteryEncounter:dark_deal_option_2_label", - // buttonTooltip: "mysteryEncounter:dark_deal_option_2_tooltip", - // selected: [ - // { - // speaker: "mysteryEncounter:dark_deal_speaker", - // text: "mysteryEncounter:dark_deal_option_2_selected" - // } - // ] - // } - // ] - }, - outro: [ - { - text: "mysteryEncounter:dark_deal_outro" - } - ] -}; diff --git a/src/data/mystery-encounters/encounters/dark-deal.ts b/src/data/mystery-encounters/encounters/dark-deal.ts index c1651aa8dec..740c4e86c28 100644 --- a/src/data/mystery-encounters/encounters/dark-deal.ts +++ b/src/data/mystery-encounters/encounters/dark-deal.ts @@ -98,6 +98,9 @@ export const DarkDealEncounter: MysteryEncounter = .withSceneWaveRangeRequirement(30, 180) // waves 30 to 180 .withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party .withCatchAllowed(true) + .withTitle("mysteryEncounter:dark_deal_title") + .withDescription("mysteryEncounter:dark_deal_description") + .withQuery("mysteryEncounter:dark_deal_query") .withOption( new MysteryEncounterOptionBuilder() .withDialogue({ @@ -175,24 +178,27 @@ export const DarkDealEncounter: MysteryEncounter = }) .build() ) - .withOption( - new MysteryEncounterOptionBuilder() - .withDialogue({ - buttonLabel: "mysteryEncounter:dark_deal_option_2_label", - buttonTooltip: "mysteryEncounter:dark_deal_option_2_tooltip", - selected: [ - { - speaker: "mysteryEncounter:dark_deal_speaker", - text: "mysteryEncounter:dark_deal_option_2_selected", - }, - ], - }) - .withOptionPhase(async (scene: BattleScene) => { - // Leave encounter with no rewards or exp + .withSimpleOption( + { + buttonLabel: "mysteryEncounter:dark_deal_option_2_label", + buttonTooltip: "mysteryEncounter:dark_deal_option_2_tooltip", + selected: [ + { + speaker: "mysteryEncounter:dark_deal_speaker", + text: "mysteryEncounter:dark_deal_option_2_selected", + }, + ], + }, + async (scene: BattleScene) => { + // Leave encounter with no rewards or exp - leaveEncounterWithoutBattle(scene, true); - return true; - }) - .build() + leaveEncounterWithoutBattle(scene, true); + return true; + } ) + .withOutroDialogue([ + { + text: "mysteryEncounter:dark_deal_outro" + } + ]) .build(); diff --git a/src/data/mystery-encounters/encounters/department-store-sale.ts b/src/data/mystery-encounters/encounters/department-store-sale.ts index 19269a774f3..4323fc15434 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale.ts @@ -29,7 +29,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu ]) // .withHideIntroVisuals(false) .withSceneWaveRangeRequirement(10, 100) - .withOptionPhase(async (scene: BattleScene) => { + .withSimpleOption(async (scene: BattleScene) => { // Choose TMs const modifiers = []; let i = 0; @@ -49,7 +49,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); leaveEncounterWithoutBattle(scene); }) - .withOptionPhase(async (scene: BattleScene) => { + .withSimpleOption(async (scene: BattleScene) => { // Choose Vitamins const modifiers = []; let i = 0; @@ -67,7 +67,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); leaveEncounterWithoutBattle(scene); }) - .withOptionPhase(async (scene: BattleScene) => { + .withSimpleOption(async (scene: BattleScene) => { // Choose X Items const modifiers = []; let i = 0; @@ -85,7 +85,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); leaveEncounterWithoutBattle(scene); }) - .withOptionPhase(async (scene: BattleScene) => { + .withSimpleOption(async (scene: BattleScene) => { // Choose Pokeballs const modifiers = []; let i = 0; diff --git a/src/data/mystery-encounters/encounters/fight-or-flight.ts b/src/data/mystery-encounters/encounters/fight-or-flight.ts index 8a82439772c..2a362eaed37 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight.ts @@ -83,7 +83,7 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder return true; }) - .withOptionPhase(async (scene: BattleScene) => { + .withSimpleOption(async (scene: BattleScene) => { // Pick battle const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption; setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false }); @@ -127,7 +127,7 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder } }) .build()) - .withOptionPhase(async (scene: BattleScene) => { + .withSimpleOption(async (scene: BattleScene) => { // Leave encounter with no rewards or exp leaveEncounterWithoutBattle(scene, true); return true; diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers.ts b/src/data/mystery-encounters/encounters/mysterious-challengers.ts index 880c7be4ca2..db794c58e9f 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers.ts @@ -95,7 +95,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter return true; }) - .withOptionPhase(async (scene: BattleScene) => { + .withSimpleOption(async (scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter; // Spawn standard trainer battle with memory mushroom reward const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; @@ -109,7 +109,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter }, scene.currentBattle.waveIndex * 10); return ret; }) - .withOptionPhase(async (scene: BattleScene) => { + .withSimpleOption(async (scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter; // Spawn hard fight with ULTRA/GREAT reward (can improve with luck) const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1]; @@ -123,7 +123,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter }, scene.currentBattle.waveIndex * 100); return ret; }) - .withOptionPhase(async (scene: BattleScene) => { + .withSimpleOption(async (scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter; // Spawn brutal fight with ROGUE/ULTRA/GREAT reward (can improve with luck) const config: EnemyPartyConfig = encounter.enemyPartyConfigs[2]; diff --git a/src/data/mystery-encounters/encounters/mysterious-chest.ts b/src/data/mystery-encounters/encounters/mysterious-chest.ts index 5e572131562..69ae93f312f 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest.ts @@ -85,7 +85,7 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde }) .build() ) - .withOptionPhase(async (scene: BattleScene) => { + .withSimpleOption(async (scene: BattleScene) => { // Leave encounter with no rewards or exp leaveEncounterWithoutBattle(scene, true); return true; diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts index 21e1b199e04..6ab668d73a6 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts @@ -186,7 +186,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui chosenPokemon.updateInfo(); }) .build()) - .withOptionPhase(async (scene: BattleScene) => { + .withSimpleOption(async (scene: BattleScene) => { // Leave encounter with no rewards or exp leaveEncounterWithoutBattle(scene, true); return true; diff --git a/src/data/mystery-encounters/encounters/sleeping-snorlax.ts b/src/data/mystery-encounters/encounters/sleeping-snorlax.ts index 444ce5a6581..6a820812ae1 100644 --- a/src/data/mystery-encounters/encounters/sleeping-snorlax.ts +++ b/src/data/mystery-encounters/encounters/sleeping-snorlax.ts @@ -55,13 +55,13 @@ export const SleepingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuilde encounter.enemyPartyConfigs = [config]; return true; }) - .withOptionPhase(async (scene: BattleScene) => { + .withSimpleOption(async (scene: BattleScene) => { // Pick battle // TODO: do we want special rewards for this? // setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: true}); await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]); }) - .withOptionPhase(async (scene: BattleScene) => { + .withSimpleOption(async (scene: BattleScene) => { const instance = scene.currentBattle.mysteryEncounter; let roll: integer; scene.executeWithSeedOffset(() => { diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index c0eb3fdfec7..11135a58085 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -1,7 +1,6 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteriousChallengersDialogue } from "#app/data/mystery-encounters/dialogue/mysterious-challengers-dialogue"; import { MysteriousChestDialogue } from "#app/data/mystery-encounters/dialogue/mysterious-chest-dialogue"; -import { DarkDealDialogue } from "#app/data/mystery-encounters/dialogue/dark-deal-dialogue"; import { FightOrFlightDialogue } from "#app/data/mystery-encounters/dialogue/fight-or-flight-dialogue"; import { TrainingSessionDialogue } from "#app/data/mystery-encounters/dialogue/training-session-dialogue"; import { SleepingSnorlaxDialogue } from "./dialogue/sleeping-snorlax-dialogue"; @@ -26,15 +25,15 @@ export class OptionTextDisplay { } export class EncounterOptionsDialogue { - title: TemplateStringsArray | `mysteryEncounter:${string}`; - description: TemplateStringsArray | `mysteryEncounter:${string}`; + title?: TemplateStringsArray | `mysteryEncounter:${string}`; + description?: TemplateStringsArray | `mysteryEncounter:${string}`; query?: TemplateStringsArray | `mysteryEncounter:${string}`; options?: [...OptionTextDisplay[]]; // Options array with minimum 2 options } export default class MysteryEncounterDialogue { intro?: TextDisplay[]; - encounterOptionsDialogue: EncounterOptionsDialogue; + encounterOptionsDialogue?: EncounterOptionsDialogue; outro?: TextDisplay[]; } @@ -87,7 +86,6 @@ export const allMysteryEncounterDialogue: { [encounterType: number]: MysteryEnco export function initMysteryEncounterDialogue() { allMysteryEncounterDialogue[MysteryEncounterType.MYSTERIOUS_CHALLENGERS] = MysteriousChallengersDialogue; allMysteryEncounterDialogue[MysteryEncounterType.MYSTERIOUS_CHEST] = MysteriousChestDialogue; - allMysteryEncounterDialogue[MysteryEncounterType.DARK_DEAL] = DarkDealDialogue; allMysteryEncounterDialogue[MysteryEncounterType.FIGHT_OR_FLIGHT] = FightOrFlightDialogue; allMysteryEncounterDialogue[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionDialogue; allMysteryEncounterDialogue[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxDialogue; diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 6c8c0b4381d..0780aa33ae4 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -7,7 +7,8 @@ import MysteryEncounterIntroVisuals, { MysteryEncounterSpriteConfig } from "../. import * as Utils from "../../utils"; import { StatusEffect } from "../status-effect"; import MysteryEncounterDialogue, { - allMysteryEncounterDialogue + allMysteryEncounterDialogue, + OptionTextDisplay } from "./mystery-encounter-dialogue"; import MysteryEncounterOption, { MysteryEncounterOptionBuilder, OptionPhaseCallback } from "./mystery-encounter-option"; import { @@ -136,9 +137,7 @@ export default class MysteryEncounter implements MysteryEncounter { Object.assign(this, encounter); } this.encounterTier = this.encounterTier ? this.encounterTier : MysteryEncounterTier.COMMON; - this.dialogue = Object.assign((this.dialogue ?? {}), allMysteryEncounterDialogue[this.encounterType]); - // this.dialogue = allMysteryEncounterDialogue[this.encounterType]; - console.log(`${MysteryEncounterType[encounter.encounterType]} Encounter Dialogue:`, this.dialogue); + this.dialogue = Object.assign((this.dialogue ?? {}), allMysteryEncounterDialogue[this.encounterType] ?? {}); this.encounterVariant = MysteryEncounterVariant.DEFAULT; this.requirements = this.requirements ? this.requirements : []; this.hideBattleIntroMessage = !isNullOrUndefined(this.hideBattleIntroMessage) ? this.hideBattleIntroMessage : false; @@ -399,11 +398,12 @@ export class MysteryEncounterBuilder implements Partial { * Adds a streamlined option phase. * Only use if no pre-/post-options or condtions necessary. * - * @param callback - OptionPhaseCallback + * @param dialogue - {@linkcode OptionTextDisplay} + * @param callback - {@linkcode OptionPhaseCallback} * @returns */ - withOptionPhase(callback: OptionPhaseCallback) { - return this.withOption(new MysteryEncounterOptionBuilder().withOptionPhase(callback).build()); + withSimpleOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback) { + return this.withOption(new MysteryEncounterOptionBuilder().withDialogue(dialogue).withOptionPhase(callback).build()); } /** @@ -413,12 +413,10 @@ export class MysteryEncounterBuilder implements Partial { * @returns */ withIntroSpriteConfigs(spriteConfigs: MysteryEncounterSpriteConfig[]): this & Pick { - console.debug("with intro sprite configs: ", spriteConfigs); return Object.assign(this, { spriteConfigs: spriteConfigs }); } - withIntroDialogue(dialogue: MysteryEncounterDialogue["intro"] = []): this { - console.debug("with intro dialogue: ", dialogue); + withIntroDialogue(dialogue: MysteryEncounterDialogue["intro"] = []) { this.dialogue = {...this.dialogue, intro: dialogue }; return this; } @@ -581,6 +579,56 @@ export class MysteryEncounterBuilder implements Partial { return Object.assign(this, { hideIntroVisuals: hideIntroVisuals }); } + withTitle(title: TemplateStringsArray | `mysteryEncounter:${string}`) { + const dialogue = this.dialogue ?? {}; + const encounterOptionsDialogue = this.dialogue?.encounterOptionsDialogue ?? {}; + + this.dialogue = { + ...dialogue, + encounterOptionsDialogue: { + ...encounterOptionsDialogue, + title, + } + }; + + return this; + } + + withDescription(description: TemplateStringsArray | `mysteryEncounter:${string}`) { + const dialogue = this.dialogue ?? {}; + const encounterOptionsDialogue = this.dialogue?.encounterOptionsDialogue ?? {}; + + this.dialogue = { + ...dialogue, + encounterOptionsDialogue: { + ...encounterOptionsDialogue, + description, + } + }; + + return this; + } + + withQuery(query: TemplateStringsArray | `mysteryEncounter:${string}`) { + const dialogue = this.dialogue ?? {}; + const encounterOptionsDialogue = this.dialogue?.encounterOptionsDialogue ?? {}; + + this.dialogue = { + ...dialogue, + encounterOptionsDialogue: { + ...encounterOptionsDialogue, + query, + } + }; + + return this; + } + + withOutroDialogue(dialogue: MysteryEncounterDialogue["outro"] = []) { + this.dialogue = {...this.dialogue, outro: dialogue }; + return this; + } + build(this: MysteryEncounter) { return new MysteryEncounter(this); } diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 5c0ceac4964..c446481b744 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -424,12 +424,13 @@ export default class MysteryEncounterUiHandler extends UiHandler { } const mysteryEncounter = this.scene.currentBattle.mysteryEncounter; - let text; - const option = mysteryEncounter.dialogue.encounterOptionsDialogue.options[cursor]; - if (!this.optionsMeetsReqs[cursor] && this.filteredEncounterOptions[cursor].isDisabledOnRequirementsNotMet && option.disabledTooltip) { - text = getEncounterText(this.scene, option.disabledTooltip, TextStyle.TOOLTIP_CONTENT); + let text: string; + const option = this.filteredEncounterOptions[cursor]; + const optionDialogue = option.dialogue ?? mysteryEncounter.dialogue.encounterOptionsDialogue.options[cursor]; + if (!this.optionsMeetsReqs[cursor] && this.filteredEncounterOptions[cursor].isDisabledOnRequirementsNotMet && optionDialogue.disabledTooltip) { + text = getEncounterText(this.scene, optionDialogue.disabledTooltip, TextStyle.TOOLTIP_CONTENT); } else { - text = getEncounterText(this.scene, option.buttonTooltip, TextStyle.TOOLTIP_CONTENT); + text = getEncounterText(this.scene, optionDialogue.buttonTooltip, TextStyle.TOOLTIP_CONTENT); } // Auto-color options green/blue for good/bad by looking for (+)/(-) From d9e0957d409828895abe2f52a09dd992440b4519 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 13:35:46 -0700 Subject: [PATCH 08/29] migrate department-store-sale encounter --- .../department-store-sale-dialogue.ts | 36 --- .../encounters/dark-deal.ts | 35 +-- .../encounters/department-store-sale.ts | 242 +++++++++++------- .../mystery-encounter-dialogue.ts | 2 - 4 files changed, 168 insertions(+), 147 deletions(-) delete mode 100644 src/data/mystery-encounters/dialogue/department-store-sale-dialogue.ts diff --git a/src/data/mystery-encounters/dialogue/department-store-sale-dialogue.ts b/src/data/mystery-encounters/dialogue/department-store-sale-dialogue.ts deleted file mode 100644 index 87cd8dbda55..00000000000 --- a/src/data/mystery-encounters/dialogue/department-store-sale-dialogue.ts +++ /dev/null @@ -1,36 +0,0 @@ -import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; - -export const DepartmentStoreSaleDialogue: MysteryEncounterDialogue = { - intro: [ - { - text: "mysteryEncounter:department_store_sale_intro_message" - }, - { - text: "mysteryEncounter:department_store_sale_intro_dialogue", - speaker: "mysteryEncounter:department_store_sale_speaker" - } - ], - encounterOptionsDialogue: { - title: "mysteryEncounter:department_store_sale_title", - description: "mysteryEncounter:department_store_sale_description", - query: "mysteryEncounter:department_store_sale_query", - options: [ - { - buttonLabel: "mysteryEncounter:department_store_sale_option_1_label", - buttonTooltip: "mysteryEncounter:department_store_sale_option_1_tooltip" - }, - { - buttonLabel: "mysteryEncounter:department_store_sale_option_2_label", - buttonTooltip: "mysteryEncounter:department_store_sale_option_2_tooltip" - }, - { - buttonLabel: "mysteryEncounter:department_store_sale_option_3_label", - buttonTooltip: "mysteryEncounter:department_store_sale_option_3_tooltip" - }, - { - buttonLabel: "mysteryEncounter:department_store_sale_option_4_label", - buttonTooltip: "mysteryEncounter:department_store_sale_option_4_tooltip" - } - ] - } -}; diff --git a/src/data/mystery-encounters/encounters/dark-deal.ts b/src/data/mystery-encounters/encounters/dark-deal.ts index 740c4e86c28..8f13e373e9a 100644 --- a/src/data/mystery-encounters/encounters/dark-deal.ts +++ b/src/data/mystery-encounters/encounters/dark-deal.ts @@ -21,6 +21,9 @@ import { leaveEncounterWithoutBattle, } from "../mystery-encounter-utils"; +/** i18n namespace for encounter */ +const namespace = "mysteryEncounter:dark_deal"; + // Exclude Ultra Beasts, Paradox, Necrozma, Eternatus, and egg-locked mythicals const excludedBosses = [ Species.NECROZMA, @@ -88,31 +91,31 @@ export const DarkDealEncounter: MysteryEncounter = ]) .withIntroDialogue([ { - text: "mysteryEncounter:dark_deal_intro_message", + text: `${namespace}_intro_message`, }, { - speaker: "mysteryEncounter:dark_deal_speaker", - text: "mysteryEncounter:dark_deal_intro_dialogue", + speaker: `${namespace}_speaker`, + text: `${namespace}_intro_dialogue`, }, ]) .withSceneWaveRangeRequirement(30, 180) // waves 30 to 180 .withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party .withCatchAllowed(true) - .withTitle("mysteryEncounter:dark_deal_title") - .withDescription("mysteryEncounter:dark_deal_description") - .withQuery("mysteryEncounter:dark_deal_query") + .withTitle(`${namespace}_title`) + .withDescription(`${namespace}_description`) + .withQuery(`${namespace}_query`) .withOption( new MysteryEncounterOptionBuilder() .withDialogue({ - buttonLabel: "mysteryEncounter:dark_deal_option_1_label", - buttonTooltip: "mysteryEncounter:dark_deal_option_1_tooltip", + buttonLabel: `${namespace}_option_1_label`, + buttonTooltip: `${namespace}_option_1_tooltip`, selected: [ { - speaker: "mysteryEncounter:dark_deal_speaker", - text: "mysteryEncounter:dark_deal_option_1_selected", + speaker: `${namespace}_speaker`, + text: `${namespace}_option_1_selected`, }, { - text: "mysteryEncounter:dark_deal_option_1_selected_message", + text: `${namespace}_option_1_selected_message`, }, ], }) @@ -180,12 +183,12 @@ export const DarkDealEncounter: MysteryEncounter = ) .withSimpleOption( { - buttonLabel: "mysteryEncounter:dark_deal_option_2_label", - buttonTooltip: "mysteryEncounter:dark_deal_option_2_tooltip", + buttonLabel: `${namespace}_option_2_label`, + buttonTooltip: `${namespace}_option_2_tooltip`, selected: [ { - speaker: "mysteryEncounter:dark_deal_speaker", - text: "mysteryEncounter:dark_deal_option_2_selected", + speaker: `${namespace}_speaker`, + text: `${namespace}_option_2_selected`, }, ], }, @@ -198,7 +201,7 @@ export const DarkDealEncounter: MysteryEncounter = ) .withOutroDialogue([ { - text: "mysteryEncounter:dark_deal_outro" + text: `${namespace}_outro` } ]) .build(); diff --git a/src/data/mystery-encounters/encounters/department-store-sale.ts b/src/data/mystery-encounters/encounters/department-store-sale.ts index 4323fc15434..2dcee4455df 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale.ts @@ -7,104 +7,160 @@ import { randSeedInt } from "#app/utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import BattleScene from "../../../battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; +import MysteryEncounter, { + MysteryEncounterBuilder, + MysteryEncounterTier, +} from "../mystery-encounter"; -export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBuilder - .withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE) - .withEncounterTier(MysteryEncounterTier.COMMON) - .withIntroSpriteConfigs([ - { - spriteKey: "b2w2_lady", - fileRoot: "mystery-encounters", - hasShadow: true, - x: -20 - }, - { - spriteKey: Species.FURFROU.toString(), - fileRoot: "pokemon", - hasShadow: true, - repeat: true, - x: 30 - } - ]) - // .withHideIntroVisuals(false) - .withSceneWaveRangeRequirement(10, 100) - .withSimpleOption(async (scene: BattleScene) => { - // Choose TMs - const modifiers = []; - let i = 0; - while (i < 4) { - // 2/2/1 weight on TM rarity - const roll = randSeedInt(5); - if (roll < 2) { - modifiers.push(modifierTypes.TM_COMMON); - } else if (roll < 4) { - modifiers.push(modifierTypes.TM_GREAT); - } else { - modifiers.push(modifierTypes.TM_ULTRA); +/** i18n namespace for encounter */ +const namespace = "mysteryEncounter:department_store_sale"; + +export const DepartmentStoreSaleEncounter: MysteryEncounter = + MysteryEncounterBuilder.withEncounterType( + MysteryEncounterType.DEPARTMENT_STORE_SALE + ) + .withEncounterTier(MysteryEncounterTier.COMMON) + .withSceneWaveRangeRequirement(10, 100) + .withIntroSpriteConfigs([ + { + spriteKey: "b2w2_lady", + fileRoot: "mystery-encounters", + hasShadow: true, + x: -20, + }, + { + spriteKey: Species.FURFROU.toString(), + fileRoot: "pokemon", + hasShadow: true, + repeat: true, + x: 30, + }, + ]) + .withIntroDialogue([ + { + text: `${namespace}_intro_message`, + }, + { + text: `${namespace}_intro_dialogue`, + speaker: `${namespace}_speaker`, + }, + ]) + // .withHideIntroVisuals(false) + .withTitle(`${namespace}_title`) + .withDescription(`${namespace}_description`) + .withQuery(`${namespace}_query`) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_1_label`, + buttonTooltip: `${namespace}_option_1_tooltip`, + }, + async (scene: BattleScene) => { + // Choose TMs + const modifiers = []; + let i = 0; + while (i < 4) { + // 2/2/1 weight on TM rarity + const roll = randSeedInt(5); + if (roll < 2) { + modifiers.push(modifierTypes.TM_COMMON); + } else if (roll < 4) { + modifiers.push(modifierTypes.TM_GREAT); + } else { + modifiers.push(modifierTypes.TM_ULTRA); + } + i++; + } + + setEncounterRewards(scene, { + guaranteedModifierTypeFuncs: modifiers, + fillRemaining: false, + }); + leaveEncounterWithoutBattle(scene); } - i++; - } + ) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_2_label`, + buttonTooltip: `${namespace}_option_2_tooltip`, + }, + async (scene: BattleScene) => { + // Choose Vitamins + const modifiers = []; + let i = 0; + while (i < 3) { + // 2/1 weight on base stat booster vs PP Up + const roll = randSeedInt(3); + if (roll === 0) { + modifiers.push(modifierTypes.PP_UP); + } else { + modifiers.push(modifierTypes.BASE_STAT_BOOSTER); + } + i++; + } - setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); - leaveEncounterWithoutBattle(scene); - }) - .withSimpleOption(async (scene: BattleScene) => { - // Choose Vitamins - const modifiers = []; - let i = 0; - while (i < 3) { - // 2/1 weight on base stat booster vs PP Up - const roll = randSeedInt(3); - if (roll === 0) { - modifiers.push(modifierTypes.PP_UP); - } else { - modifiers.push(modifierTypes.BASE_STAT_BOOSTER); + setEncounterRewards(scene, { + guaranteedModifierTypeFuncs: modifiers, + fillRemaining: false, + }); + leaveEncounterWithoutBattle(scene); } - i++; - } + ) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_3_label`, + buttonTooltip: `${namespace}_option_3_tooltip`, + }, + async (scene: BattleScene) => { + // Choose X Items + const modifiers = []; + let i = 0; + while (i < 5) { + // 4/1 weight on base stat booster vs Dire Hit + const roll = randSeedInt(5); + if (roll === 0) { + modifiers.push(modifierTypes.DIRE_HIT); + } else { + modifiers.push(modifierTypes.TEMP_STAT_BOOSTER); + } + i++; + } - setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); - leaveEncounterWithoutBattle(scene); - }) - .withSimpleOption(async (scene: BattleScene) => { - // Choose X Items - const modifiers = []; - let i = 0; - while (i < 5) { - // 4/1 weight on base stat booster vs Dire Hit - const roll = randSeedInt(5); - if (roll === 0) { - modifiers.push(modifierTypes.DIRE_HIT); - } else { - modifiers.push(modifierTypes.TEMP_STAT_BOOSTER); + setEncounterRewards(scene, { + guaranteedModifierTypeFuncs: modifiers, + fillRemaining: false, + }); + leaveEncounterWithoutBattle(scene); } - i++; - } + ) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_4_label`, + buttonTooltip: `${namespace}_option_4_tooltip`, + }, + async (scene: BattleScene) => { + // Choose Pokeballs + const modifiers = []; + let i = 0; + while (i < 4) { + // 10/30/20/5 weight on pokeballs + const roll = randSeedInt(65); + if (roll < 10) { + modifiers.push(modifierTypes.POKEBALL); + } else if (roll < 40) { + modifiers.push(modifierTypes.GREAT_BALL); + } else if (roll < 60) { + modifiers.push(modifierTypes.ULTRA_BALL); + } else { + modifiers.push(modifierTypes.ROGUE_BALL); + } + i++; + } - setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); - leaveEncounterWithoutBattle(scene); - }) - .withSimpleOption(async (scene: BattleScene) => { - // Choose Pokeballs - const modifiers = []; - let i = 0; - while (i < 4) { - // 10/30/20/5 weight on pokeballs - const roll = randSeedInt(65); - if (roll < 10) { - modifiers.push(modifierTypes.POKEBALL); - } else if (roll < 40) { - modifiers.push(modifierTypes.GREAT_BALL); - } else if (roll < 60) { - modifiers.push(modifierTypes.ULTRA_BALL); - } else { - modifiers.push(modifierTypes.ROGUE_BALL); + setEncounterRewards(scene, { + guaranteedModifierTypeFuncs: modifiers, + fillRemaining: false, + }); + leaveEncounterWithoutBattle(scene); } - i++; - } - - setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); - leaveEncounterWithoutBattle(scene); - }) - .build(); + ) + .build(); diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index 11135a58085..d6938f49607 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -4,7 +4,6 @@ import { MysteriousChestDialogue } from "#app/data/mystery-encounters/dialogue/m import { FightOrFlightDialogue } from "#app/data/mystery-encounters/dialogue/fight-or-flight-dialogue"; import { TrainingSessionDialogue } from "#app/data/mystery-encounters/dialogue/training-session-dialogue"; 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 { FieldTripDialogue } from "#app/data/mystery-encounters/dialogue/field-trip-dialogue"; @@ -89,7 +88,6 @@ export function initMysteryEncounterDialogue() { allMysteryEncounterDialogue[MysteryEncounterType.FIGHT_OR_FLIGHT] = FightOrFlightDialogue; allMysteryEncounterDialogue[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionDialogue; allMysteryEncounterDialogue[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxDialogue; - allMysteryEncounterDialogue[MysteryEncounterType.DEPARTMENT_STORE_SALE] = DepartmentStoreSaleDialogue; allMysteryEncounterDialogue[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerDialogue; allMysteryEncounterDialogue[MysteryEncounterType.FIELD_TRIP] = FieldTripDialogue; } From ce73c38e330694108e856d05bf9d8c7cb5ad77eb Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 13:47:10 -0700 Subject: [PATCH 09/29] migrate field-trip encounter --- .../dialogue/field-trip-dialogue.ts | 50 -- .../encounters/field-trip-encounter.ts | 509 +++++++++++------- .../mystery-encounter-dialogue.ts | 2 - 3 files changed, 311 insertions(+), 250 deletions(-) delete mode 100644 src/data/mystery-encounters/dialogue/field-trip-dialogue.ts diff --git a/src/data/mystery-encounters/dialogue/field-trip-dialogue.ts b/src/data/mystery-encounters/dialogue/field-trip-dialogue.ts deleted file mode 100644 index dca3f48f5bf..00000000000 --- a/src/data/mystery-encounters/dialogue/field-trip-dialogue.ts +++ /dev/null @@ -1,50 +0,0 @@ -import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; - -export const FieldTripDialogue: MysteryEncounterDialogue = { - intro: [ - { - text: "mysteryEncounter:field_trip_intro_message" - }, - { - text: "mysteryEncounter:field_trip_intro_dialogue", - speaker: "mysteryEncounter:field_trip_speaker" - } - ], - encounterOptionsDialogue: { - title: "mysteryEncounter:field_trip_title", - description: "mysteryEncounter:field_trip_description", - query: "mysteryEncounter:field_trip_query", - options: [ - { - buttonLabel: "mysteryEncounter:field_trip_option_1_label", - buttonTooltip: "mysteryEncounter:field_trip_option_1_tooltip", - secondOptionPrompt: "mysteryEncounter:field_trip_second_option_prompt", - selected: [ - { - text: "mysteryEncounter:field_trip_option_selected" - } - ] - }, - { - buttonLabel: "mysteryEncounter:field_trip_option_2_label", - buttonTooltip: "mysteryEncounter:field_trip_option_2_tooltip", - secondOptionPrompt: "mysteryEncounter:field_trip_second_option_prompt", - selected: [ - { - text: "mysteryEncounter:field_trip_option_selected" - } - ] - }, - { - buttonLabel: "mysteryEncounter:field_trip_option_3_label", - buttonTooltip: "mysteryEncounter:field_trip_option_3_tooltip", - secondOptionPrompt: "mysteryEncounter:field_trip_second_option_prompt", - selected: [ - { - text: "mysteryEncounter:field_trip_option_selected" - } - ] - } - ] - } -}; diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index 410df127b6e..8cb6fe65048 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -1,220 +1,333 @@ -import { generateModifierTypeOption, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards, } from "#app/data/mystery-encounters/mystery-encounter-utils"; +import { MoveCategory } from "#app/data/move"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { + generateModifierTypeOption, + leaveEncounterWithoutBattle, + selectPokemonForOption, + setEncounterExp, + setEncounterRewards, +} from "#app/data/mystery-encounters/mystery-encounter-utils"; +import { TempBattleStat } from "#app/data/temp-battle-stat"; +import { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import { modifierTypes } from "#app/modifier/modifier-type"; +import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; -import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import { MoveCategory } from "#app/data/move"; -import { TempBattleStat } from "#app/data/temp-battle-stat"; +import MysteryEncounter, { + MysteryEncounterBuilder, + MysteryEncounterTier, +} from "../mystery-encounter"; -export const FieldTripEncounter: MysteryEncounter = MysteryEncounterBuilder - .withEncounterType(MysteryEncounterType.FIELD_TRIP) - .withEncounterTier(MysteryEncounterTier.COMMON) - .withIntroSpriteConfigs([ - { - spriteKey: "preschooler_m", - fileRoot: "trainer", - hasShadow: true - }, - { - spriteKey: "teacher", - fileRoot: "mystery-encounters", - hasShadow: true - }, - { - spriteKey: "preschooler_f", - fileRoot: "trainer", - hasShadow: true - }, - ]) - .withHideIntroVisuals(false) - .withSceneWaveRangeRequirement(10, 180) - .withOption(new MysteryEncounterOptionBuilder() - .withPreOptionPhase(async (scene: BattleScene): Promise => { - const encounter = scene.currentBattle.mysteryEncounter; - const onPokemonSelected = (pokemon: PlayerPokemon) => { - // Return the options for Pokemon move valid for this option - return pokemon.moveset.map((move: PokemonMove) => { - const option: OptionSelectItem = { - label: move.getName(), - handler: () => { - // Pokemon and move selected - const correctMove = move.getMove().category === MoveCategory.PHYSICAL; - encounter.setDialogueToken("moveCategory", "Physical"); - if (!correctMove) { - encounter.dialogue.encounterOptionsDialogue.options[0].selected = [ - { - text: "mysteryEncounter:field_trip_option_incorrect", - speaker: "mysteryEncounter:field_trip_speaker" - }, - { - text: "mysteryEncounter:field_trip_lesson_learned", +/** i18n namespace for the encounter */ +const namespace = "mysteryEncounter:field_trip"; + +export const FieldTripEncounter: MysteryEncounter = + MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP) + .withEncounterTier(MysteryEncounterTier.COMMON) + .withSceneWaveRangeRequirement(10, 180) + .withIntroSpriteConfigs([ + { + spriteKey: "preschooler_m", + fileRoot: "trainer", + hasShadow: true, + }, + { + spriteKey: "teacher", + fileRoot: "mystery-encounters", + hasShadow: true, + }, + { + spriteKey: "preschooler_f", + fileRoot: "trainer", + hasShadow: true, + }, + ]) + .withIntroDialogue([ + { + text: `${namespace}_intro_message`, + }, + { + text: `${namespace}_intro_dialogue`, + speaker: `${namespace}_speaker`, + }, + ]) + .withHideIntroVisuals(false) + .withTitle(`${namespace}_title`) + .withDescription(`${namespace}_description`) + .withQuery(`${namespace}_query`) + .withOption( + new MysteryEncounterOptionBuilder() + .withDialogue({ + buttonLabel: `${namespace}_option_1_label`, + buttonTooltip: `${namespace}_option_1_tooltip`, + secondOptionPrompt: `${namespace}_second_option_prompt`, + selected: [ + { + text: `${namespace}_option_selected`, + }, + ], + }) + .withPreOptionPhase(async (scene: BattleScene): Promise => { + const encounter = scene.currentBattle.mysteryEncounter; + const onPokemonSelected = (pokemon: PlayerPokemon) => { + // Return the options for Pokemon move valid for this option + return pokemon.moveset.map((move: PokemonMove) => { + const option: OptionSelectItem = { + label: move.getName(), + handler: () => { + // Pokemon and move selected + const correctMove = + move.getMove().category === MoveCategory.PHYSICAL; + encounter.setDialogueToken("moveCategory", "Physical"); + if (!correctMove) { + encounter.options[0].dialogue.selected = [ + { + text: `${namespace}_option_incorrect`, + speaker: `${namespace}_speaker`, + }, + { + text: `${namespace}_lesson_learned`, + }, + ]; + setEncounterExp( + scene, + scene.getParty().map((p) => p.id), + 50 + ); + } else { + encounter.setDialogueToken("pokeName", pokemon.name); + encounter.setDialogueToken("move", move.getName()); + encounter.options[0].dialogue.selected = [ + { + text: `${namespace}_option_selected`, + }, + ]; + setEncounterExp(scene, [pokemon.id], 100); } - ]; - setEncounterExp(scene, scene.getParty().map(p => p.id), 50); - } else { - encounter.setDialogueToken("pokeName", pokemon.name); - encounter.setDialogueToken("move", move.getName()); - encounter.dialogue.encounterOptionsDialogue.options[0].selected = [ - { - text: "mysteryEncounter:field_trip_option_selected" - } - ]; - setEncounterExp(scene, [pokemon.id], 100); - } - encounter.misc = { - correctMove: correctMove + encounter.misc = { + correctMove: correctMove, + }; + return true; + }, }; - return true; - } + return option; + }); }; - return option; - }); - }; - return selectPokemonForOption(scene, onPokemonSelected); - }) - .withOptionPhase(async (scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - if (encounter.misc.correctMove) { - const modifiers = [ - generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.ATK]), - generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.DEF]), - generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]), - generateModifierTypeOption(scene, modifierTypes.DIRE_HIT) - ]; + return selectPokemonForOption(scene, onPokemonSelected); + }) + .withOptionPhase(async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + if (encounter.misc.correctMove) { + const modifiers = [ + generateModifierTypeOption( + scene, + modifierTypes.TEMP_STAT_BOOSTER, + [TempBattleStat.ATK] + ), + generateModifierTypeOption( + scene, + modifierTypes.TEMP_STAT_BOOSTER, + [TempBattleStat.DEF] + ), + generateModifierTypeOption( + scene, + modifierTypes.TEMP_STAT_BOOSTER, + [TempBattleStat.SPD] + ), + generateModifierTypeOption(scene, modifierTypes.DIRE_HIT), + ]; - setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); - } + setEncounterRewards(scene, { + guaranteedModifierTypeOptions: modifiers, + fillRemaining: false, + }); + } - leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); - }) - .build() - ) - .withOption(new MysteryEncounterOptionBuilder() - .withPreOptionPhase(async (scene: BattleScene): Promise => { - const encounter = scene.currentBattle.mysteryEncounter; - const onPokemonSelected = (pokemon: PlayerPokemon) => { - // Return the options for Pokemon move valid for this option - return pokemon.moveset.map((move: PokemonMove) => { - const option: OptionSelectItem = { - label: move.getName(), - handler: () => { - // Pokemon and move selected - const correctMove = move.getMove().category === MoveCategory.SPECIAL; - encounter.setDialogueToken("moveCategory", "Special"); - if (!correctMove) { - encounter.dialogue.encounterOptionsDialogue.options[1].selected = [ - { - text: "mysteryEncounter:field_trip_option_incorrect", - speaker: "mysteryEncounter:field_trip_speaker" - }, - { - text: "mysteryEncounter:field_trip_lesson_learned", + leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); + }) + .build() + ) + .withOption( + new MysteryEncounterOptionBuilder() + .withDialogue({ + buttonLabel: `${namespace}_option_2_label`, + buttonTooltip: `${namespace}_option_2_tooltip`, + secondOptionPrompt: `${namespace}_second_option_prompt`, + selected: [ + { + text: `${namespace}_option_selected`, + }, + ], + }) + .withPreOptionPhase(async (scene: BattleScene): Promise => { + const encounter = scene.currentBattle.mysteryEncounter; + const onPokemonSelected = (pokemon: PlayerPokemon) => { + // Return the options for Pokemon move valid for this option + return pokemon.moveset.map((move: PokemonMove) => { + const option: OptionSelectItem = { + label: move.getName(), + handler: () => { + // Pokemon and move selected + const correctMove = + move.getMove().category === MoveCategory.SPECIAL; + encounter.setDialogueToken("moveCategory", "Special"); + if (!correctMove) { + encounter.options[1].dialogue.selected = [ + { + text: `${namespace}_option_incorrect`, + speaker: `${namespace}_speaker`, + }, + { + text: `${namespace}_lesson_learned`, + }, + ]; + setEncounterExp( + scene, + scene.getParty().map((p) => p.id), + 50 + ); + } else { + encounter.setDialogueToken("pokeName", pokemon.name); + encounter.setDialogueToken("move", move.getName()); + encounter.options[1].dialogue.selected = [ + { + text: `${namespace}_option_selected`, + }, + ]; + setEncounterExp(scene, [pokemon.id], 100); } - ]; - setEncounterExp(scene, scene.getParty().map(p => p.id), 50); - } else { - encounter.setDialogueToken("pokeName", pokemon.name); - encounter.setDialogueToken("move", move.getName()); - encounter.dialogue.encounterOptionsDialogue.options[1].selected = [ - { - text: "mysteryEncounter:field_trip_option_selected" - } - ]; - setEncounterExp(scene, [pokemon.id], 100); - } - encounter.misc = { - correctMove: correctMove + encounter.misc = { + correctMove: correctMove, + }; + return true; + }, }; - return true; - } + return option; + }); }; - return option; - }); - }; - return selectPokemonForOption(scene, onPokemonSelected); - }) - .withOptionPhase(async (scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - if (encounter.misc.correctMove) { - const modifiers = [ - generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPATK]), - generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPDEF]), - generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]), - generateModifierTypeOption(scene, modifierTypes.DIRE_HIT) - ]; + return selectPokemonForOption(scene, onPokemonSelected); + }) + .withOptionPhase(async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + if (encounter.misc.correctMove) { + const modifiers = [ + generateModifierTypeOption( + scene, + modifierTypes.TEMP_STAT_BOOSTER, + [TempBattleStat.SPATK] + ), + generateModifierTypeOption( + scene, + modifierTypes.TEMP_STAT_BOOSTER, + [TempBattleStat.SPDEF] + ), + generateModifierTypeOption( + scene, + modifierTypes.TEMP_STAT_BOOSTER, + [TempBattleStat.SPD] + ), + generateModifierTypeOption(scene, modifierTypes.DIRE_HIT), + ]; - setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); - } + setEncounterRewards(scene, { + guaranteedModifierTypeOptions: modifiers, + fillRemaining: false, + }); + } - leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); - }) - .build() - ) - .withOption(new MysteryEncounterOptionBuilder() - .withPreOptionPhase(async (scene: BattleScene): Promise => { - const encounter = scene.currentBattle.mysteryEncounter; - const onPokemonSelected = (pokemon: PlayerPokemon) => { - // Return the options for Pokemon move valid for this option - return pokemon.moveset.map((move: PokemonMove) => { - const option: OptionSelectItem = { - label: move.getName(), - handler: () => { - // Pokemon and move selected - const correctMove = move.getMove().category === MoveCategory.STATUS; - encounter.setDialogueToken("moveCategory", "Status"); - if (!correctMove) { - encounter.dialogue.encounterOptionsDialogue.options[2].selected = [ - { - text: "mysteryEncounter:field_trip_option_incorrect", - speaker: "mysteryEncounter:field_trip_speaker" - }, - { - text: "mysteryEncounter:field_trip_lesson_learned", + leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); + }) + .build() + ) + .withOption( + new MysteryEncounterOptionBuilder() + .withDialogue({ + buttonLabel: `${namespace}_option_3_label`, + buttonTooltip: `${namespace}_option_3_tooltip`, + secondOptionPrompt: `${namespace}_second_option_prompt`, + selected: [ + { + text: `${namespace}_option_selected`, + }, + ], + }) + .withPreOptionPhase(async (scene: BattleScene): Promise => { + const encounter = scene.currentBattle.mysteryEncounter; + const onPokemonSelected = (pokemon: PlayerPokemon) => { + // Return the options for Pokemon move valid for this option + return pokemon.moveset.map((move: PokemonMove) => { + const option: OptionSelectItem = { + label: move.getName(), + handler: () => { + // Pokemon and move selected + const correctMove = + move.getMove().category === MoveCategory.STATUS; + encounter.setDialogueToken("moveCategory", "Status"); + if (!correctMove) { + encounter.options[2].dialogue.selected = [ + { + text: `${namespace}_option_incorrect`, + speaker: `${namespace}_speaker`, + }, + { + text: `${namespace}_lesson_learned`, + }, + ]; + setEncounterExp( + scene, + scene.getParty().map((p) => p.id), + 50 + ); + } else { + encounter.setDialogueToken("pokeName", pokemon.name); + encounter.setDialogueToken("move", move.getName()); + encounter.options[2].dialogue.selected = [ + { + text: `${namespace}_option_selected`, + }, + ]; + setEncounterExp(scene, [pokemon.id], 100); } - ]; - setEncounterExp(scene, scene.getParty().map(p => p.id), 50); - } else { - encounter.setDialogueToken("pokeName", pokemon.name); - encounter.setDialogueToken("move", move.getName()); - encounter.dialogue.encounterOptionsDialogue.options[2].selected = [ - { - text: "mysteryEncounter:field_trip_option_selected" - } - ]; - setEncounterExp(scene, [pokemon.id], 100); - } - encounter.misc = { - correctMove: correctMove + encounter.misc = { + correctMove: correctMove, + }; + return true; + }, }; - return true; - } + return option; + }); }; - return option; - }); - }; - return selectPokemonForOption(scene, onPokemonSelected); - }) - .withOptionPhase(async (scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - if (encounter.misc.correctMove) { - const modifiers = [ - generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.ACC]), - generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]), - generateModifierTypeOption(scene, modifierTypes.GREAT_BALL), - generateModifierTypeOption(scene, modifierTypes.IV_SCANNER) - ]; + return selectPokemonForOption(scene, onPokemonSelected); + }) + .withOptionPhase(async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + if (encounter.misc.correctMove) { + const modifiers = [ + generateModifierTypeOption( + scene, + modifierTypes.TEMP_STAT_BOOSTER, + [TempBattleStat.ACC] + ), + generateModifierTypeOption( + scene, + modifierTypes.TEMP_STAT_BOOSTER, + [TempBattleStat.SPD] + ), + generateModifierTypeOption(scene, modifierTypes.GREAT_BALL), + generateModifierTypeOption(scene, modifierTypes.IV_SCANNER), + ]; - setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); - } + setEncounterRewards(scene, { + guaranteedModifierTypeOptions: modifiers, + fillRemaining: false, + }); + } - leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); - }) - .build() - ) - .build(); + leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); + }) + .build() + ) + .build(); diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index d6938f49607..bfeb094c9a5 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -6,7 +6,6 @@ import { TrainingSessionDialogue } from "#app/data/mystery-encounters/dialogue/t import { SleepingSnorlaxDialogue } from "./dialogue/sleeping-snorlax-dialogue"; import { ShadyVitaminDealerDialogue } from "#app/data/mystery-encounters/dialogue/shady-vitamin-dealer"; import { TextStyle } from "#app/ui/text"; -import { FieldTripDialogue } from "#app/data/mystery-encounters/dialogue/field-trip-dialogue"; export class TextDisplay { speaker?: TemplateStringsArray | `mysteryEncounter:${string}`; @@ -89,5 +88,4 @@ export function initMysteryEncounterDialogue() { allMysteryEncounterDialogue[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionDialogue; allMysteryEncounterDialogue[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxDialogue; allMysteryEncounterDialogue[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerDialogue; - allMysteryEncounterDialogue[MysteryEncounterType.FIELD_TRIP] = FieldTripDialogue; } From 171339509170803b1bd6a0036f337620822316d9 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 13:54:44 -0700 Subject: [PATCH 10/29] migrate fight-or-flight encounter --- .../dialogue/fight-or-flight-dialogue.ts | 38 --- .../encounters/fight-or-flight.ts | 304 ++++++++++++------ .../mystery-encounter-dialogue.ts | 2 - 3 files changed, 202 insertions(+), 142 deletions(-) delete mode 100644 src/data/mystery-encounters/dialogue/fight-or-flight-dialogue.ts diff --git a/src/data/mystery-encounters/dialogue/fight-or-flight-dialogue.ts b/src/data/mystery-encounters/dialogue/fight-or-flight-dialogue.ts deleted file mode 100644 index 93d46548d7f..00000000000 --- a/src/data/mystery-encounters/dialogue/fight-or-flight-dialogue.ts +++ /dev/null @@ -1,38 +0,0 @@ -import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; - -export const FightOrFlightDialogue: MysteryEncounterDialogue = { - intro: [ - { - text: "mysteryEncounter:fight_or_flight_intro_message" - } - ], - encounterOptionsDialogue: { - title: "mysteryEncounter:fight_or_flight_title", - description: "mysteryEncounter:fight_or_flight_description", - query: "mysteryEncounter:fight_or_flight_query", - options: [ - { - buttonLabel: "mysteryEncounter:fight_or_flight_option_1_label", - buttonTooltip: "mysteryEncounter:fight_or_flight_option_1_tooltip", - selected: [ - { - text: "mysteryEncounter:fight_or_flight_option_1_selected_message" - } - ] - }, - { - buttonLabel: "mysteryEncounter:fight_or_flight_option_2_label", - buttonTooltip: "mysteryEncounter:fight_or_flight_option_2_tooltip" - }, - { - buttonLabel: "mysteryEncounter:fight_or_flight_option_3_label", - buttonTooltip: "mysteryEncounter:fight_or_flight_option_3_tooltip", - selected: [ - { - text: "mysteryEncounter:fight_or_flight_option_3_selected" - } - ] - } - ] - } -}; diff --git a/src/data/mystery-encounters/encounters/fight-or-flight.ts b/src/data/mystery-encounters/encounters/fight-or-flight.ts index 2a362eaed37..3080ab76810 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight.ts @@ -1,11 +1,14 @@ import { BattleStat } from "#app/data/battle-stat"; +import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { EnemyPartyConfig, initBattleWithEnemyConfig, - leaveEncounterWithoutBattle, queueEncounterMessage, + leaveEncounterWithoutBattle, + queueEncounterMessage, setEncounterRewards, - showEncounterText + showEncounterText, } from "#app/data/mystery-encounters/mystery-encounter-utils"; +import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import Pokemon from "#app/field/pokemon"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { @@ -13,123 +16,220 @@ import { getPlayerModifierTypeOptions, ModifierPoolType, ModifierTypeOption, - regenerateModifierPoolThresholds + regenerateModifierPoolThresholds, } from "#app/modifier/modifier-type"; import { StatChangePhase } from "#app/phases"; import { randSeedInt } from "#app/utils"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; +import MysteryEncounter, { + MysteryEncounterBuilder, + MysteryEncounterTier, +} from "../mystery-encounter"; import { MoveRequirement } from "../mystery-encounter-requirements"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; -export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder - .withEncounterType(MysteryEncounterType.FIGHT_OR_FLIGHT) - .withEncounterTier(MysteryEncounterTier.COMMON) - .withIntroSpriteConfigs([]) // Set in onInit() - .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 - .withCatchAllowed(true) - .withHideWildIntroMessage(true) - .withOnInit((scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; +/** the i18n namespace for the encounter */ +const namespace = "mysteryEncounter:fight_or_flight"; - // Calculate boss mon - const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, scene.currentBattle.waveIndex, 0, getPartyLuckValue(scene.getParty()), true); - const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 1, - pokemonConfigs: [{ species: bossSpecies, isBoss: true }] - }; - encounter.enemyPartyConfigs = [config]; - - // Calculate item - // 10-60 GREAT, 60-110 ULTRA, 110-160 ROGUE, 160-180 MASTER - const tier = scene.currentBattle.waveIndex > 160 ? ModifierTier.MASTER : scene.currentBattle.waveIndex > 110 ? ModifierTier.ROGUE : scene.currentBattle.waveIndex > 60 ? ModifierTier.ULTRA : ModifierTier.GREAT; - regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0); // refresh player item pool - const item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier] })[0]; - encounter.setDialogueToken("itemName", item.type.name); - encounter.misc = item; - - encounter.spriteConfigs = [ +export const FightOrFlightEncounter: MysteryEncounter = + MysteryEncounterBuilder.withEncounterType( + MysteryEncounterType.FIGHT_OR_FLIGHT + ) + .withEncounterTier(MysteryEncounterTier.COMMON) + .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 + .withCatchAllowed(true) + .withHideWildIntroMessage(true) + .withIntroSpriteConfigs([]) // Set in onInit() + .withIntroDialogue([ { - spriteKey: item.type.iconImage, - fileRoot: "items", - hasShadow: false, - x: 35, - y: -5, - scale: 0.75, - isItem: true + text: `${namespace}_intro_message`, }, - { - spriteKey: bossSpecies.speciesId.toString(), - fileRoot: "pokemon", - hasShadow: true, - tint: 0.25, - x: -5, - repeat: true - } - ]; - - // If player has a stealing move, they succeed automatically - encounter.options[1].meetsRequirements(scene); - const primaryPokemon = encounter.options[1].primaryPokemon; - if (primaryPokemon) { - // Use primaryPokemon to execute the thievery - encounter.dialogue.encounterOptionsDialogue.options[1].buttonTooltip = "mysteryEncounter:fight_or_flight_option_2_steal_tooltip"; - } else { - encounter.dialogue.encounterOptionsDialogue.options[1].buttonTooltip = "mysteryEncounter:fight_or_flight_option_2_tooltip"; - } - - return true; - }) - .withSimpleOption(async (scene: BattleScene) => { - // Pick battle - const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption; - setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false }); - await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]); - }) - .withOption(new MysteryEncounterOptionBuilder() - .withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically - .withDisabledOnRequirementsNotMet(false) - .withOptionPhase(async (scene: BattleScene) => { - // Pick steal + ]) + .withOnInit((scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter; - const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption; - setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false }); + + // Calculate boss mon + const bossSpecies = scene.arena.randomSpecies( + scene.currentBattle.waveIndex, + scene.currentBattle.waveIndex, + 0, + getPartyLuckValue(scene.getParty()), + true + ); + const config: EnemyPartyConfig = { + levelAdditiveMultiplier: 1, + pokemonConfigs: [{ species: bossSpecies, isBoss: true }], + }; + encounter.enemyPartyConfigs = [config]; + + // Calculate item + // 10-60 GREAT, 60-110 ULTRA, 110-160 ROGUE, 160-180 MASTER + const tier = + scene.currentBattle.waveIndex > 160 + ? ModifierTier.MASTER + : scene.currentBattle.waveIndex > 110 + ? ModifierTier.ROGUE + : scene.currentBattle.waveIndex > 60 + ? ModifierTier.ULTRA + : ModifierTier.GREAT; + regenerateModifierPoolThresholds( + scene.getParty(), + ModifierPoolType.PLAYER, + 0 + ); // refresh player item pool + const item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { + guaranteedModifierTiers: [tier], + })[0]; + encounter.setDialogueToken("itemName", item.type.name); + encounter.misc = item; + + encounter.spriteConfigs = [ + { + spriteKey: item.type.iconImage, + fileRoot: "items", + hasShadow: false, + x: 35, + y: -5, + scale: 0.75, + isItem: true, + }, + { + spriteKey: bossSpecies.speciesId.toString(), + fileRoot: "pokemon", + hasShadow: true, + tint: 0.25, + x: -5, + repeat: true, + }, + ]; // If player has a stealing move, they succeed automatically + encounter.options[1].meetsRequirements(scene); const primaryPokemon = encounter.options[1].primaryPokemon; if (primaryPokemon) { // Use primaryPokemon to execute the thievery - await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_steal_result"); - leaveEncounterWithoutBattle(scene); - return; + encounter.options[1].dialogue.buttonTooltip = `${namespace}_option_2_steal_tooltip`; + } else { + encounter.options[1].dialogue.buttonTooltip = `${namespace}_option_2_tooltip`; } - const roll = randSeedInt(16); - if (roll > 6) { - // Noticed and attacked by boss, gets +1 to all stats at start of fight (62.5%) - const config = scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]; - config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON]; - config.pokemonConfigs[0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { - pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken("enemyPokemon", pokemon.name); - queueEncounterMessage(pokemon.scene, "mysteryEncounter:fight_or_flight_boss_enraged"); - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1)); - }; - await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_bad_result"); - await initBattleWithEnemyConfig(scene, config); - } else { - // Steal item (37.5%) - // Display result message then proceed to rewards - await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_good_result"); - leaveEncounterWithoutBattle(scene); - } + return true; }) - .build()) - .withSimpleOption(async (scene: BattleScene) => { - // Leave encounter with no rewards or exp - leaveEncounterWithoutBattle(scene, true); - return true; - }) - .build(); + .withTitle(`${namespace}_title`) + .withDescription(`${namespace}_description`) + .withQuery(`${namespace}_query`) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_1_label`, + buttonTooltip: `${namespace}_option_1_tooltip`, + selected: [ + { + text: `${namespace}_option_1_selected_message`, + }, + ], + }, + async (scene: BattleScene) => { + // Pick battle + const item = scene.currentBattle.mysteryEncounter + .misc as ModifierTypeOption; + setEncounterRewards(scene, { + guaranteedModifierTypeOptions: [item], + fillRemaining: false, + }); + await initBattleWithEnemyConfig( + scene, + scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0] + ); + } + ) + .withOption( + new MysteryEncounterOptionBuilder() + .withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically + .withDisabledOnRequirementsNotMet(false) + .withDialogue({ + buttonLabel: `${namespace}_option_2_label`, + buttonTooltip: `${namespace}_option_2_tooltip`, + }) + .withOptionPhase(async (scene: BattleScene) => { + // Pick steal + const encounter = scene.currentBattle.mysteryEncounter; + const item = scene.currentBattle.mysteryEncounter + .misc as ModifierTypeOption; + setEncounterRewards(scene, { + guaranteedModifierTypeOptions: [item], + fillRemaining: false, + }); + + // If player has a stealing move, they succeed automatically + const primaryPokemon = encounter.options[1].primaryPokemon; + if (primaryPokemon) { + // Use primaryPokemon to execute the thievery + await showEncounterText( + scene, + `${namespace}_option_2_steal_result` + ); + leaveEncounterWithoutBattle(scene); + return; + } + + const roll = randSeedInt(16); + if (roll > 6) { + // Noticed and attacked by boss, gets +1 to all stats at start of fight (62.5%) + const config = + scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]; + config.pokemonConfigs[0].tags = [ + BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON, + ]; + config.pokemonConfigs[0].mysteryEncounterBattleEffects = ( + pokemon: Pokemon + ) => { + pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken( + "enemyPokemon", + pokemon.name + ); + queueEncounterMessage(pokemon.scene, `${namespace}_boss_enraged`); + pokemon.scene.unshiftPhase( + new StatChangePhase( + pokemon.scene, + pokemon.getBattlerIndex(), + true, + [ + BattleStat.ATK, + BattleStat.DEF, + BattleStat.SPATK, + BattleStat.SPDEF, + BattleStat.SPD, + ], + 1 + ) + ); + }; + await showEncounterText(scene, `${namespace}_option_2_bad_result`); + await initBattleWithEnemyConfig(scene, config); + } else { + // Steal item (37.5%) + // Display result message then proceed to rewards + await showEncounterText(scene, `${namespace}_option_2_good_result`); + leaveEncounterWithoutBattle(scene); + } + }) + .build() + ) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_3_label`, + buttonTooltip: `${namespace}_option_3_tooltip`, + selected: [ + { + text: `${namespace}_option_3_selected`, + }, + ], + }, + async (scene: BattleScene) => { + // Leave encounter with no rewards or exp + leaveEncounterWithoutBattle(scene, true); + return true; + } + ) + .build(); diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index bfeb094c9a5..d9532ee4984 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -1,7 +1,6 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteriousChallengersDialogue } from "#app/data/mystery-encounters/dialogue/mysterious-challengers-dialogue"; import { MysteriousChestDialogue } from "#app/data/mystery-encounters/dialogue/mysterious-chest-dialogue"; -import { FightOrFlightDialogue } from "#app/data/mystery-encounters/dialogue/fight-or-flight-dialogue"; import { TrainingSessionDialogue } from "#app/data/mystery-encounters/dialogue/training-session-dialogue"; import { SleepingSnorlaxDialogue } from "./dialogue/sleeping-snorlax-dialogue"; import { ShadyVitaminDealerDialogue } from "#app/data/mystery-encounters/dialogue/shady-vitamin-dealer"; @@ -84,7 +83,6 @@ export const allMysteryEncounterDialogue: { [encounterType: number]: MysteryEnco export function initMysteryEncounterDialogue() { allMysteryEncounterDialogue[MysteryEncounterType.MYSTERIOUS_CHALLENGERS] = MysteriousChallengersDialogue; allMysteryEncounterDialogue[MysteryEncounterType.MYSTERIOUS_CHEST] = MysteriousChestDialogue; - allMysteryEncounterDialogue[MysteryEncounterType.FIGHT_OR_FLIGHT] = FightOrFlightDialogue; allMysteryEncounterDialogue[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionDialogue; allMysteryEncounterDialogue[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxDialogue; allMysteryEncounterDialogue[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerDialogue; From e6bf12ab8c1e0918cf938b006f0a3a6942df8474 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 14:02:26 -0700 Subject: [PATCH 11/29] migrate mysterious-challengers --- .../mysterious-challengers-dialogue.ts | 57 --- .../encounters/mysterious-challengers.ts | 339 ++++++++++++------ .../mystery-encounter-dialogue.ts | 2 - 3 files changed, 220 insertions(+), 178 deletions(-) delete mode 100644 src/data/mystery-encounters/dialogue/mysterious-challengers-dialogue.ts diff --git a/src/data/mystery-encounters/dialogue/mysterious-challengers-dialogue.ts b/src/data/mystery-encounters/dialogue/mysterious-challengers-dialogue.ts deleted file mode 100644 index 065cfceb1b7..00000000000 --- a/src/data/mystery-encounters/dialogue/mysterious-challengers-dialogue.ts +++ /dev/null @@ -1,57 +0,0 @@ -import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; - -export const MysteriousChallengersDialogue: MysteryEncounterDialogue = { - intro: [ - { - text: "mysteryEncounter:mysterious_challengers_intro_message" - } - ], - encounterOptionsDialogue: { - title: "mysteryEncounter:mysterious_challengers_title", - description: "mysteryEncounter:mysterious_challengers_description", - query: "mysteryEncounter:mysterious_challengers_query", - options: [ - { - buttonLabel: "mysteryEncounter:mysterious_challengers_option_1_label", - buttonTooltip: "mysteryEncounter:mysterious_challengers_option_1_tooltip", - selected: [ - { - text: "mysteryEncounter:mysterious_challengers_option_selected_message" - } - ] - }, - { - buttonLabel: "mysteryEncounter:mysterious_challengers_option_2_label", - buttonTooltip: "mysteryEncounter:mysterious_challengers_option_2_tooltip", - selected: [ - { - text: "mysteryEncounter:mysterious_challengers_option_selected_message" - } - ] - }, - { - buttonLabel: "mysteryEncounter:mysterious_challengers_option_3_label", - buttonTooltip: "mysteryEncounter:mysterious_challengers_option_3_tooltip", - selected: [ - { - text: "mysteryEncounter:mysterious_challengers_option_selected_message" - } - ] - }, - { - buttonLabel: "mysteryEncounter:mysterious_challengers_option_4_label", - buttonTooltip: "mysteryEncounter:mysterious_challengers_option_4_tooltip", - selected: [ - { - text: "mysteryEncounter:mysterious_challengers_option_4_selected_message" - } - ] - } - ] - }, - outro: [ - { - text: "mysteryEncounter:mysterious_challengers_outro_win" - } - ] -}; diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers.ts b/src/data/mystery-encounters/encounters/mysterious-challengers.ts index db794c58e9f..e8efc3470ed 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers.ts @@ -1,9 +1,13 @@ -import { EnemyPartyConfig, initBattleWithEnemyConfig, setEncounterRewards } from "#app/data/mystery-encounters/mystery-encounter-utils"; +import { + EnemyPartyConfig, + initBattleWithEnemyConfig, + setEncounterRewards, +} from "#app/data/mystery-encounters/mystery-encounter-utils"; import { trainerConfigs, TrainerPartyCompoundTemplate, TrainerPartyTemplate, - trainerPartyTemplates + trainerPartyTemplates, } from "#app/data/trainer-config"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { modifierTypes } from "#app/modifier/modifier-type"; @@ -11,133 +15,230 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PartyMemberStrength } from "#enums/party-member-strength"; import BattleScene from "../../../battle-scene"; import * as Utils from "../../../utils"; -import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; +import MysteryEncounter, { + MysteryEncounterBuilder, + MysteryEncounterTier, +} from "../mystery-encounter"; -export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounterBuilder - .withEncounterType(MysteryEncounterType.MYSTERIOUS_CHALLENGERS) - .withEncounterTier(MysteryEncounterTier.GREAT) - .withIntroSpriteConfigs([]) // These are set in onInit() - .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 - .withOnInit((scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - // Calculates what trainers are available for battle in the encounter +/** the i18n namespace for the encounter */ +const namespace = "mysteryEncounter:mysterious_challengers"; - // Normal difficulty trainer is randomly pulled from biome - const normalTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex); - const normalConfig = trainerConfigs[normalTrainerType].copy(); - let female = false; - if (normalConfig.hasGenders) { - female = !!(Utils.randSeedInt(2)); - } - const normalSpriteKey = normalConfig.getSpriteKey(female, normalConfig.doubleOnly); - encounter.enemyPartyConfigs.push({ - trainerConfig: normalConfig, - female: female - }); - - // Hard difficulty trainer is another random trainer, but with AVERAGE_BALANCED config - // Number of mons is based off wave: 1-20 is 2, 20-40 is 3, etc. capping at 6 after wave 100 - const hardTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex); - const hardTemplate = new TrainerPartyCompoundTemplate( - new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, false, true), - new TrainerPartyTemplate(Math.min(Math.ceil(scene.currentBattle.waveIndex / 20), 5), PartyMemberStrength.AVERAGE, false, true)); - const hardConfig = trainerConfigs[hardTrainerType].copy(); - hardConfig.setPartyTemplates(hardTemplate); - female = false; - if (hardConfig.hasGenders) { - female = !!(Utils.randSeedInt(2)); - } - const hardSpriteKey = hardConfig.getSpriteKey(female, hardConfig.doubleOnly); - encounter.enemyPartyConfigs.push({ - trainerConfig: hardConfig, - levelAdditiveMultiplier: 0.5, - female: female, - }); - - // Brutal trainer is pulled from pool of boss trainers (gym leaders) for the biome - // They are given an E4 template team, so will be stronger than usual boss encounter and always have 6 mons - const brutalTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex, true); - const e4Template = trainerPartyTemplates.ELITE_FOUR; - const brutalConfig = trainerConfigs[brutalTrainerType].copy(); - brutalConfig.setPartyTemplates(e4Template); - brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func - female = false; - if (brutalConfig.hasGenders) { - female = !!(Utils.randSeedInt(2)); - } - const brutalSpriteKey = brutalConfig.getSpriteKey(female, brutalConfig.doubleOnly); - encounter.enemyPartyConfigs.push({ - trainerConfig: brutalConfig, - levelAdditiveMultiplier: 1.1, - female: female - }); - - encounter.spriteConfigs = [ +export const MysteriousChallengersEncounter: MysteryEncounter = + MysteryEncounterBuilder.withEncounterType( + MysteryEncounterType.MYSTERIOUS_CHALLENGERS + ) + .withEncounterTier(MysteryEncounterTier.GREAT) + .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 + .withIntroSpriteConfigs([]) // These are set in onInit() + .withIntroDialogue([ { - spriteKey: normalSpriteKey, - fileRoot: "trainer", - hasShadow: true, - tint: 1 + text: `${namespace}_intro_message`, }, - { - spriteKey: hardSpriteKey, - fileRoot: "trainer", - hasShadow: true, - tint: 1 - }, - { - spriteKey: brutalSpriteKey, - fileRoot: "trainer", - hasShadow: true, - tint: 1 + ]) + .withOnInit((scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + // Calculates what trainers are available for battle in the encounter + + // Normal difficulty trainer is randomly pulled from biome + const normalTrainerType = scene.arena.randomTrainerType( + scene.currentBattle.waveIndex + ); + const normalConfig = trainerConfigs[normalTrainerType].copy(); + let female = false; + if (normalConfig.hasGenders) { + female = !!Utils.randSeedInt(2); } - ]; + const normalSpriteKey = normalConfig.getSpriteKey( + female, + normalConfig.doubleOnly + ); + encounter.enemyPartyConfigs.push({ + trainerConfig: normalConfig, + female: female, + }); - return true; - }) - .withSimpleOption(async (scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - // Spawn standard trainer battle with memory mushroom reward - const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; + // Hard difficulty trainer is another random trainer, but with AVERAGE_BALANCED config + // Number of mons is based off wave: 1-20 is 2, 20-40 is 3, etc. capping at 6 after wave 100 + const hardTrainerType = scene.arena.randomTrainerType( + scene.currentBattle.waveIndex + ); + const hardTemplate = new TrainerPartyCompoundTemplate( + new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, false, true), + new TrainerPartyTemplate( + Math.min(Math.ceil(scene.currentBattle.waveIndex / 20), 5), + PartyMemberStrength.AVERAGE, + false, + true + ) + ); + const hardConfig = trainerConfigs[hardTrainerType].copy(); + hardConfig.setPartyTemplates(hardTemplate); + female = false; + if (hardConfig.hasGenders) { + female = !!Utils.randSeedInt(2); + } + const hardSpriteKey = hardConfig.getSpriteKey( + female, + hardConfig.doubleOnly + ); + encounter.enemyPartyConfigs.push({ + trainerConfig: hardConfig, + levelAdditiveMultiplier: 0.5, + female: female, + }); - setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM], fillRemaining: true }); + // Brutal trainer is pulled from pool of boss trainers (gym leaders) for the biome + // They are given an E4 template team, so will be stronger than usual boss encounter and always have 6 mons + const brutalTrainerType = scene.arena.randomTrainerType( + scene.currentBattle.waveIndex, + true + ); + const e4Template = trainerPartyTemplates.ELITE_FOUR; + const brutalConfig = trainerConfigs[brutalTrainerType].copy(); + brutalConfig.setPartyTemplates(e4Template); + brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func + female = false; + if (brutalConfig.hasGenders) { + female = !!Utils.randSeedInt(2); + } + const brutalSpriteKey = brutalConfig.getSpriteKey( + female, + brutalConfig.doubleOnly + ); + encounter.enemyPartyConfigs.push({ + trainerConfig: brutalConfig, + levelAdditiveMultiplier: 1.1, + female: female, + }); - // Seed offsets to remove possibility of different trainers having exact same teams - let ret; - scene.executeWithSeedOffset(() => { - ret = initBattleWithEnemyConfig(scene, config); - }, scene.currentBattle.waveIndex * 10); - return ret; - }) - .withSimpleOption(async (scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - // Spawn hard fight with ULTRA/GREAT reward (can improve with luck) - const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1]; + encounter.spriteConfigs = [ + { + spriteKey: normalSpriteKey, + fileRoot: "trainer", + hasShadow: true, + tint: 1, + }, + { + spriteKey: hardSpriteKey, + fileRoot: "trainer", + hasShadow: true, + tint: 1, + }, + { + spriteKey: brutalSpriteKey, + fileRoot: "trainer", + hasShadow: true, + tint: 1, + }, + ]; - setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], fillRemaining: true }); + return true; + }) + .withTitle(`${namespace}_title`) + .withDescription(`${namespace}_description`) + .withQuery(`${namespace}_query`) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_1_label`, + buttonTooltip: `${namespace}_option_1_tooltip`, + selected: [ + { + text: `${namespace}_option_selected_message`, + }, + ], + }, + async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + // Spawn standard trainer battle with memory mushroom reward + const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; - // Seed offsets to remove possibility of different trainers having exact same teams - let ret; - scene.executeWithSeedOffset(() => { - ret = initBattleWithEnemyConfig(scene, config); - }, scene.currentBattle.waveIndex * 100); - return ret; - }) - .withSimpleOption(async (scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - // Spawn brutal fight with ROGUE/ULTRA/GREAT reward (can improve with luck) - const config: EnemyPartyConfig = encounter.enemyPartyConfigs[2]; + setEncounterRewards(scene, { + guaranteedModifierTypeFuncs: [ + modifierTypes.TM_COMMON, + modifierTypes.TM_GREAT, + modifierTypes.MEMORY_MUSHROOM, + ], + fillRemaining: true, + }); - // To avoid player level snowballing from picking this option - encounter.expMultiplier = 0.9; + // Seed offsets to remove possibility of different trainers having exact same teams + let ret; + scene.executeWithSeedOffset(() => { + ret = initBattleWithEnemyConfig(scene, config); + }, scene.currentBattle.waveIndex * 10); + return ret; + } + ) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_2_label`, + buttonTooltip: `${namespace}_option_2_tooltip`, + selected: [ + { + text: `${namespace}_option_selected_message`, + }, + ], + }, + async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + // Spawn hard fight with ULTRA/GREAT reward (can improve with luck) + const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1]; - setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT], fillRemaining: true }); + setEncounterRewards(scene, { + guaranteedModifierTiers: [ + ModifierTier.ULTRA, + ModifierTier.GREAT, + ModifierTier.GREAT, + ], + fillRemaining: true, + }); - // Seed offsets to remove possibility of different trainers having exact same teams - let ret; - scene.executeWithSeedOffset(() => { - ret = initBattleWithEnemyConfig(scene, config); - }, scene.currentBattle.waveIndex * 1000); - return ret; - }) - .build(); + // Seed offsets to remove possibility of different trainers having exact same teams + let ret; + scene.executeWithSeedOffset(() => { + ret = initBattleWithEnemyConfig(scene, config); + }, scene.currentBattle.waveIndex * 100); + return ret; + } + ) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_3_label`, + buttonTooltip: `${namespace}_option_3_tooltip`, + selected: [ + { + text: `${namespace}_option_selected_message`, + }, + ], + }, + async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + // Spawn brutal fight with ROGUE/ULTRA/GREAT reward (can improve with luck) + const config: EnemyPartyConfig = encounter.enemyPartyConfigs[2]; + + // To avoid player level snowballing from picking this option + encounter.expMultiplier = 0.9; + + setEncounterRewards(scene, { + guaranteedModifierTiers: [ + ModifierTier.ROGUE, + ModifierTier.ULTRA, + ModifierTier.GREAT, + ], + fillRemaining: true, + }); + + // Seed offsets to remove possibility of different trainers having exact same teams + let ret; + scene.executeWithSeedOffset(() => { + ret = initBattleWithEnemyConfig(scene, config); + }, scene.currentBattle.waveIndex * 1000); + return ret; + } + ) + .withOutroDialogue([ + { + text: `${namespace}_outro_win`, + }, + ]) + .build(); diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index d9532ee4984..f1051c698d5 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -1,5 +1,4 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { MysteriousChallengersDialogue } from "#app/data/mystery-encounters/dialogue/mysterious-challengers-dialogue"; import { MysteriousChestDialogue } from "#app/data/mystery-encounters/dialogue/mysterious-chest-dialogue"; import { TrainingSessionDialogue } from "#app/data/mystery-encounters/dialogue/training-session-dialogue"; import { SleepingSnorlaxDialogue } from "./dialogue/sleeping-snorlax-dialogue"; @@ -81,7 +80,6 @@ export default class MysteryEncounterDialogue { export const allMysteryEncounterDialogue: { [encounterType: number]: MysteryEncounterDialogue } = {}; export function initMysteryEncounterDialogue() { - allMysteryEncounterDialogue[MysteryEncounterType.MYSTERIOUS_CHALLENGERS] = MysteriousChallengersDialogue; allMysteryEncounterDialogue[MysteryEncounterType.MYSTERIOUS_CHEST] = MysteriousChestDialogue; allMysteryEncounterDialogue[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionDialogue; allMysteryEncounterDialogue[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxDialogue; From 71a047a1f66f4e78551bda6e577a9e363f9d7fa9 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 14:10:21 -0700 Subject: [PATCH 12/29] migrate mysterious-chest encounter --- .../dialogue/mysterious-chest-dialogue.ts | 34 --- .../encounters/mysterious-chest.ts | 231 ++++++++++++------ .../mystery-encounter-dialogue.ts | 2 - 3 files changed, 153 insertions(+), 114 deletions(-) delete mode 100644 src/data/mystery-encounters/dialogue/mysterious-chest-dialogue.ts diff --git a/src/data/mystery-encounters/dialogue/mysterious-chest-dialogue.ts b/src/data/mystery-encounters/dialogue/mysterious-chest-dialogue.ts deleted file mode 100644 index ba8dafc6826..00000000000 --- a/src/data/mystery-encounters/dialogue/mysterious-chest-dialogue.ts +++ /dev/null @@ -1,34 +0,0 @@ -import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; - -export const MysteriousChestDialogue: MysteryEncounterDialogue = { - intro: [ - { - text: "mysteryEncounter:mysterious_chest_intro_message" - } - ], - encounterOptionsDialogue: { - title: "mysteryEncounter:mysterious_chest_title", - description: "mysteryEncounter:mysterious_chest_description", - query: "mysteryEncounter:mysterious_chest_query", - options: [ - { - buttonLabel: "mysteryEncounter:mysterious_chest_option_1_label", - buttonTooltip: "mysteryEncounter:mysterious_chest_option_1_tooltip", - selected: [ - { - text: "mysteryEncounter:mysterious_chest_option_1_selected_message" - } - ] - }, - { - buttonLabel: "mysteryEncounter:mysterious_chest_option_2_label", - buttonTooltip: "mysteryEncounter:mysterious_chest_option_2_tooltip", - selected: [ - { - text: "mysteryEncounter:mysterious_chest_option_2_selected_message" - } - ] - } - ] - } -}; diff --git a/src/data/mystery-encounters/encounters/mysterious-chest.ts b/src/data/mystery-encounters/encounters/mysterious-chest.ts index 69ae93f312f..9dd4f56f623 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest.ts @@ -4,90 +4,165 @@ import { leaveEncounterWithoutBattle, queueEncounterMessage, setEncounterRewards, - showEncounterText + showEncounterText, } from "#app/data/mystery-encounters/mystery-encounter-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 MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; +import MysteryEncounter, { + MysteryEncounterBuilder, + MysteryEncounterTier, +} from "../mystery-encounter"; import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilder - .withEncounterType(MysteryEncounterType.MYSTERIOUS_CHEST) - .withEncounterTier(MysteryEncounterTier.COMMON) - .withIntroSpriteConfigs([ - { - spriteKey: "chest_blue", - fileRoot: "mystery-encounters", - hasShadow: true, - x: 4, - y: 8, - disableAnimation: true // Re-enabled after option select - } - ]) - .withHideIntroVisuals(false) - .withSceneWaveRangeRequirement(10, 180) // waves 2 to 180 - .withOption(new MysteryEncounterOptionBuilder() - .withPreOptionPhase(async (scene: BattleScene) => { - // Play animation - const introVisuals = scene.currentBattle.mysteryEncounter.introVisuals; - introVisuals.spriteConfigs[0].disableAnimation = false; - introVisuals.playAnim(); - }) - .withOptionPhase(async (scene: BattleScene) => { - // Open the chest - const roll = randSeedInt(100); - if (roll > 60) { - // Choose between 2 COMMON / 2 GREAT tier items (40%) - setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.COMMON, ModifierTier.COMMON, ModifierTier.GREAT, ModifierTier.GREAT] }); - // Display result message then proceed to rewards - queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_normal_result"); - leaveEncounterWithoutBattle(scene); - } else if (roll > 40) { - // Choose between 3 ULTRA tier items (20%) - setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA] }); - // Display result message then proceed to rewards - queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_good_result"); - leaveEncounterWithoutBattle(scene); - } else if (roll > 36) { - // Choose between 2 ROGUE tier items (4%) - setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE] }); - // Display result message then proceed to rewards - queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_great_result"); - leaveEncounterWithoutBattle(scene); - } else if (roll > 35) { - // Choose 1 MASTER tier item (1%) - setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.MASTER] }); - // Display result message then proceed to rewards - queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_amazing_result"); - leaveEncounterWithoutBattle(scene); - } else { - // Your highest level unfainted Pok�mon gets OHKO. Progress with no rewards (35%) - const highestLevelPokemon = getHighestLevelPlayerPokemon(scene, true); - koPlayerPokemon(highestLevelPokemon); - - 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); - } - }); - } - }) - .build() +export const MysteriousChestEncounter: MysteryEncounter = + MysteryEncounterBuilder.withEncounterType( + MysteryEncounterType.MYSTERIOUS_CHEST ) - .withSimpleOption(async (scene: BattleScene) => { - // Leave encounter with no rewards or exp - leaveEncounterWithoutBattle(scene, true); - return true; - }) - .build(); + .withEncounterTier(MysteryEncounterTier.COMMON) + .withSceneWaveRangeRequirement(10, 180) // waves 2 to 180 + .withHideIntroVisuals(false) + .withIntroSpriteConfigs([ + { + spriteKey: "chest_blue", + fileRoot: "mystery-encounters", + hasShadow: true, + x: 4, + y: 8, + disableAnimation: true, // Re-enabled after option select + }, + ]) + .withIntroDialogue([ + { + text: "mysteryEncounter:mysterious_chest_intro_message", + }, + ]) + .withTitle("mysteryEncounter:mysterious_chest_title") + .withDescription("mysteryEncounter:mysterious_chest_description") + .withQuery("mysteryEncounter:mysterious_chest_query") + .withOption( + new MysteryEncounterOptionBuilder() + .withDialogue({ + buttonLabel: "mysteryEncounter:mysterious_chest_option_1_label", + buttonTooltip: "mysteryEncounter:mysterious_chest_option_1_tooltip", + selected: [ + { + text: "mysteryEncounter:mysterious_chest_option_1_selected_message", + }, + ], + }) + .withPreOptionPhase(async (scene: BattleScene) => { + // Play animation + const introVisuals = + scene.currentBattle.mysteryEncounter.introVisuals; + introVisuals.spriteConfigs[0].disableAnimation = false; + introVisuals.playAnim(); + }) + .withOptionPhase(async (scene: BattleScene) => { + // Open the chest + const roll = randSeedInt(100); + if (roll > 60) { + // Choose between 2 COMMON / 2 GREAT tier items (40%) + setEncounterRewards(scene, { + guaranteedModifierTiers: [ + ModifierTier.COMMON, + ModifierTier.COMMON, + ModifierTier.GREAT, + ModifierTier.GREAT, + ], + }); + // Display result message then proceed to rewards + queueEncounterMessage( + scene, + "mysteryEncounter:mysterious_chest_option_1_normal_result" + ); + leaveEncounterWithoutBattle(scene); + } else if (roll > 40) { + // Choose between 3 ULTRA tier items (20%) + setEncounterRewards(scene, { + guaranteedModifierTiers: [ + ModifierTier.ULTRA, + ModifierTier.ULTRA, + ModifierTier.ULTRA, + ], + }); + // Display result message then proceed to rewards + queueEncounterMessage( + scene, + "mysteryEncounter:mysterious_chest_option_1_good_result" + ); + leaveEncounterWithoutBattle(scene); + } else if (roll > 36) { + // Choose between 2 ROGUE tier items (4%) + setEncounterRewards(scene, { + guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE], + }); + // Display result message then proceed to rewards + queueEncounterMessage( + scene, + "mysteryEncounter:mysterious_chest_option_1_great_result" + ); + leaveEncounterWithoutBattle(scene); + } else if (roll > 35) { + // Choose 1 MASTER tier item (1%) + setEncounterRewards(scene, { + guaranteedModifierTiers: [ModifierTier.MASTER], + }); + // Display result message then proceed to rewards + queueEncounterMessage( + scene, + "mysteryEncounter:mysterious_chest_option_1_amazing_result" + ); + leaveEncounterWithoutBattle(scene); + } else { + // Your highest level unfainted Pok�mon gets OHKO. Progress with no rewards (35%) + const highestLevelPokemon = getHighestLevelPlayerPokemon( + scene, + true + ); + koPlayerPokemon(highestLevelPokemon); + + 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); + } + }); + } + }) + .build() + ) + .withSimpleOption( + { + buttonLabel: "mysteryEncounter:mysterious_chest_option_2_label", + buttonTooltip: "mysteryEncounter:mysterious_chest_option_2_tooltip", + selected: [ + { + text: "mysteryEncounter:mysterious_chest_option_2_selected_message", + }, + ], + }, + async (scene: BattleScene) => { + // Leave encounter with no rewards or exp + leaveEncounterWithoutBattle(scene, true); + return true; + } + ) + .build(); diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index f1051c698d5..c5663eef6ba 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -1,5 +1,4 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { MysteriousChestDialogue } from "#app/data/mystery-encounters/dialogue/mysterious-chest-dialogue"; import { TrainingSessionDialogue } from "#app/data/mystery-encounters/dialogue/training-session-dialogue"; import { SleepingSnorlaxDialogue } from "./dialogue/sleeping-snorlax-dialogue"; import { ShadyVitaminDealerDialogue } from "#app/data/mystery-encounters/dialogue/shady-vitamin-dealer"; @@ -80,7 +79,6 @@ export default class MysteryEncounterDialogue { export const allMysteryEncounterDialogue: { [encounterType: number]: MysteryEncounterDialogue } = {}; export function initMysteryEncounterDialogue() { - allMysteryEncounterDialogue[MysteryEncounterType.MYSTERIOUS_CHEST] = MysteriousChestDialogue; allMysteryEncounterDialogue[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionDialogue; allMysteryEncounterDialogue[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxDialogue; allMysteryEncounterDialogue[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerDialogue; From 19d96ed381b2aea7bbb17f2d52bf2cc843491730 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 14:39:18 -0700 Subject: [PATCH 13/29] migrate shady-vitamin-dealer encounter --- src/battle-scene.ts | 4 +- .../dialogue/shady-vitamin-dealer.ts | 42 -- .../encounters/shady-vitamin-dealer.ts | 446 +++++++++++------- .../mystery-encounter-dialogue.ts | 2 - 4 files changed, 273 insertions(+), 221 deletions(-) delete mode 100644 src/data/mystery-encounters/dialogue/shady-vitamin-dealer.ts diff --git a/src/battle-scene.ts b/src/battle-scene.ts index d3eac7fc973..be0e83b7976 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -2646,7 +2646,7 @@ export default class BattleScene extends SceneBase { if (encounter) { encounter = new MysteryEncounter(encounter); - encounter.meetsRequirements(this); + encounter.populateDialogueTokensFromRequirements(this); return encounter; } @@ -2674,7 +2674,7 @@ export default class BattleScene extends SceneBase { tier = Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE; } - let availableEncounters = []; + let availableEncounters: MysteryEncounter[] = []; // 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); diff --git a/src/data/mystery-encounters/dialogue/shady-vitamin-dealer.ts b/src/data/mystery-encounters/dialogue/shady-vitamin-dealer.ts deleted file mode 100644 index 52b9741caf8..00000000000 --- a/src/data/mystery-encounters/dialogue/shady-vitamin-dealer.ts +++ /dev/null @@ -1,42 +0,0 @@ -import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; - -export const ShadyVitaminDealerDialogue: MysteryEncounterDialogue = { - intro: [ - { - text: "mysteryEncounter:shady_vitamin_dealer_intro_message" - }, - { - text: "mysteryEncounter:shady_vitamin_dealer_intro_dialogue", - speaker: "mysteryEncounter:shady_vitamin_dealer_speaker" - } - ], - encounterOptionsDialogue: { - title: "mysteryEncounter:shady_vitamin_dealer_title", - description: "mysteryEncounter:shady_vitamin_dealer_description", - query: "mysteryEncounter:shady_vitamin_dealer_query", - options: [ - { - buttonLabel: "mysteryEncounter:shady_vitamin_dealer_option_1_label", - buttonTooltip: "mysteryEncounter:shady_vitamin_dealer_option_1_tooltip", - selected: [ - { - text: "mysteryEncounter:shady_vitamin_dealer_option_selected" - } - ] - }, - { - buttonLabel: "mysteryEncounter:shady_vitamin_dealer_option_2_label", - buttonTooltip: "mysteryEncounter:shady_vitamin_dealer_option_2_tooltip", - selected: [ - { - text: "mysteryEncounter:shady_vitamin_dealer_option_selected" - } - ] - }, - { - buttonLabel: "mysteryEncounter:shady_vitamin_dealer_option_3_label", - buttonTooltip: "mysteryEncounter:shady_vitamin_dealer_option_3_tooltip" - } - ] - } -}; diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts index 6ab668d73a6..773c4b97b0e 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts @@ -2,7 +2,8 @@ import { generateModifierTypeOption, leaveEncounterWithoutBattle, queueEncounterMessage, - selectPokemonForOption, setEncounterExp, + selectPokemonForOption, + setEncounterExp, updatePlayerMoney, } from "#app/data/mystery-encounters/mystery-encounter-utils"; import { StatusEffect } from "#app/data/status-effect"; @@ -11,184 +12,279 @@ import { modifierTypes } from "#app/modifier/modifier-type"; import { randSeedInt } from "#app/utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; -import BattleScene from "../../../battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -import { - MoneyRequirement -} from "../mystery-encounter-requirements"; import i18next from "i18next"; +import BattleScene from "../../../battle-scene"; +import MysteryEncounter, { + MysteryEncounterBuilder, + MysteryEncounterTier, +} from "../mystery-encounter"; +import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import { MoneyRequirement } from "../mystery-encounter-requirements"; -export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBuilder - .withEncounterType(MysteryEncounterType.SHADY_VITAMIN_DEALER) - .withEncounterTier(MysteryEncounterTier.COMMON) - .withIntroSpriteConfigs([ - { - spriteKey: Species.KROOKODILE.toString(), - fileRoot: "pokemon", - hasShadow: true, - repeat: true, - x: 10, - y: -1 - }, - { - spriteKey: "b2w2_veteran_m", - fileRoot: "mystery-encounters", - hasShadow: true, - x: -10, - y: 2 - } - ]) - .withSceneWaveRangeRequirement(10, 180) - .withPrimaryPokemonStatusEffectRequirement([StatusEffect.NONE]) // Pokemon must not have status - .withPrimaryPokemonHealthRatioRequirement([0.34, 1]) // Pokemon must have above 1/3rd HP - .withOption(new MysteryEncounterOptionBuilder() - .withSceneMoneyRequirement(0, 2) // Wave scaling money multiplier of 2 - .withPreOptionPhase(async (scene: BattleScene): Promise => { - const encounter = scene.currentBattle.mysteryEncounter; - const onPokemonSelected = (pokemon: PlayerPokemon) => { - // Update money - updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney); - // Calculate modifiers and dialogue tokens - const modifiers = [ - generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type, - generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type - ]; - encounter.setDialogueToken("boost1", modifiers[0].name); - encounter.setDialogueToken("boost2", modifiers[1].name); - encounter.misc = { - chosenPokemon: pokemon, - modifiers: modifiers - }; - }; +export const ShadyVitaminDealerEncounter: MysteryEncounter = + MysteryEncounterBuilder.withEncounterType( + MysteryEncounterType.SHADY_VITAMIN_DEALER + ) + .withEncounterTier(MysteryEncounterTier.COMMON) + .withSceneWaveRangeRequirement(10, 180) + .withPrimaryPokemonStatusEffectRequirement([StatusEffect.NONE]) // Pokemon must not have status + .withPrimaryPokemonHealthRatioRequirement([0.34, 1]) // Pokemon must have above 1/3rd HP + .withIntroSpriteConfigs([ + { + spriteKey: Species.KROOKODILE.toString(), + fileRoot: "pokemon", + hasShadow: true, + repeat: true, + x: 10, + y: -1, + }, + { + spriteKey: "b2w2_veteran_m", + fileRoot: "mystery-encounters", + hasShadow: true, + x: -10, + y: 2, + }, + ]) + .withIntroDialogue([ + { + text: "mysteryEncounter:shady_vitamin_dealer_intro_message", + }, + { + text: "mysteryEncounter:shady_vitamin_dealer_intro_dialogue", + speaker: "mysteryEncounter:shady_vitamin_dealer_speaker", + }, + ]) + .withTitle("mysteryEncounter:shady_vitamin_dealer_title") + .withDescription("mysteryEncounter:shady_vitamin_dealer_description") + .withQuery("mysteryEncounter:shady_vitamin_dealer_query") + .withOption( + new MysteryEncounterOptionBuilder() + .withSceneMoneyRequirement(0, 2) // Wave scaling money multiplier of 2 + .withDialogue({ + buttonLabel: "mysteryEncounter:shady_vitamin_dealer_option_1_label", + buttonTooltip: + "mysteryEncounter:shady_vitamin_dealer_option_1_tooltip", + selected: [ + { + text: "mysteryEncounter:shady_vitamin_dealer_option_selected", + }, + ], + }) + .withPreOptionPhase(async (scene: BattleScene): Promise => { + const encounter = scene.currentBattle.mysteryEncounter; + const onPokemonSelected = (pokemon: PlayerPokemon) => { + // Update money + updatePlayerMoney( + scene, + -(encounter.options[0].requirements[0] as MoneyRequirement) + .requiredMoney + ); + // Calculate modifiers and dialogue tokens + const modifiers = [ + generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER) + .type, + generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER) + .type, + ]; + encounter.setDialogueToken("boost1", modifiers[0].name); + encounter.setDialogueToken("boost2", modifiers[1].name); + encounter.misc = { + chosenPokemon: pokemon, + modifiers: modifiers, + }; + }; - // Only Pokemon that can gain benefits are above 1/3rd HP with no status - const selectableFilter = (pokemon: Pokemon) => { - // If pokemon meets primary pokemon reqs, it can be selected - const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon); - if (!meetsReqs) { - return i18next.t("mysteryEncounter:shady_vitamin_dealer_invalid_selection"); - } + // Only Pokemon that can gain benefits are above 1/3rd HP with no status + const selectableFilter = (pokemon: Pokemon) => { + // If pokemon meets primary pokemon reqs, it can be selected + const meetsReqs = encounter.pokemonMeetsPrimaryRequirements( + scene, + pokemon + ); + if (!meetsReqs) { + return i18next.t( + "mysteryEncounter:shady_vitamin_dealer_invalid_selection" + ); + } - return null; - }; + return null; + }; - return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); - }) - .withOptionPhase(async (scene: BattleScene) => { - // Choose Cheap Option - const encounter = scene.currentBattle.mysteryEncounter; - const chosenPokemon = encounter.misc.chosenPokemon; - const modifiers = encounter.misc.modifiers; + return selectPokemonForOption( + scene, + onPokemonSelected, + null, + selectableFilter + ); + }) + .withOptionPhase(async (scene: BattleScene) => { + // Choose Cheap Option + const encounter = scene.currentBattle.mysteryEncounter; + const chosenPokemon = encounter.misc.chosenPokemon; + const modifiers = encounter.misc.modifiers; - for (const modType of modifiers) { - const modifier = modType.newModifier(chosenPokemon); - await scene.addModifier(modifier, true, false, false, true); + for (const modType of modifiers) { + const modifier = modType.newModifier(chosenPokemon); + await scene.addModifier(modifier, true, false, false, true); + } + scene.updateModifiers(true); + + leaveEncounterWithoutBattle(scene); + }) + .withPostOptionPhase(async (scene: BattleScene) => { + // Damage and status applied after dealer leaves (to make thematic sense) + const encounter = scene.currentBattle.mysteryEncounter; + const chosenPokemon = encounter.misc.chosenPokemon; + + // Pokemon takes 1/3 max HP damage + const damage = Math.round(chosenPokemon.getMaxHp() / 3); + chosenPokemon.hp = Math.max(chosenPokemon.hp - damage, 0); + + // Roll for poison (80%) + if (randSeedInt(10) < 8) { + if (chosenPokemon.trySetStatus(StatusEffect.TOXIC)) { + // Toxic applied + queueEncounterMessage( + scene, + "mysteryEncounter:shady_vitamin_dealer_bad_poison" + ); + } else { + // Pokemon immune or something else prevents status + queueEncounterMessage( + scene, + "mysteryEncounter:shady_vitamin_dealer_damage_only" + ); + } + } else { + queueEncounterMessage( + scene, + "mysteryEncounter:shady_vitamin_dealer_damage_only" + ); + } + + setEncounterExp(scene, [chosenPokemon.id], 100); + + chosenPokemon.updateInfo(); + }) + .build() + ) + .withOption( + new MysteryEncounterOptionBuilder() + .withDialogue({ + buttonLabel: "mysteryEncounter:shady_vitamin_dealer_option_2_label", + buttonTooltip: + "mysteryEncounter:shady_vitamin_dealer_option_2_tooltip", + selected: [ + { + text: "mysteryEncounter:shady_vitamin_dealer_option_selected", + }, + ], + }) + .withSceneMoneyRequirement(0, 5) // Wave scaling money multiplier of 5 + .withPreOptionPhase(async (scene: BattleScene): Promise => { + const encounter = scene.currentBattle.mysteryEncounter; + const onPokemonSelected = (pokemon: PlayerPokemon) => { + // Update money + updatePlayerMoney( + scene, + -(encounter.options[1].requirements[0] as MoneyRequirement) + .requiredMoney + ); + // Calculate modifiers and dialogue tokens + const modifiers = [ + generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER) + .type, + generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER) + .type, + ]; + encounter.setDialogueToken("boost1", modifiers[0].name); + encounter.setDialogueToken("boost2", modifiers[1].name); + encounter.misc = { + chosenPokemon: pokemon, + modifiers: modifiers, + }; + }; + + // Only Pokemon that can gain benefits are above 1/3rd HP with no status + const selectableFilter = (pokemon: Pokemon) => { + // If pokemon meets primary pokemon reqs, it can be selected + const meetsReqs = encounter.pokemonMeetsPrimaryRequirements( + scene, + pokemon + ); + if (!meetsReqs) { + return i18next.t( + "mysteryEncounter:shady_vitamin_dealer_invalid_selection" + ); + } + + return null; + }; + + return selectPokemonForOption( + scene, + onPokemonSelected, + null, + selectableFilter + ); + }) + .withOptionPhase(async (scene: BattleScene) => { + // Choose Expensive Option + const encounter = scene.currentBattle.mysteryEncounter; + const chosenPokemon = encounter.misc.chosenPokemon; + const modifiers = encounter.misc.modifiers; + + for (const modType of modifiers) { + const modifier = modType.newModifier(chosenPokemon); + await scene.addModifier(modifier, true, false, false, true); + } + scene.updateModifiers(true); + + leaveEncounterWithoutBattle(scene); + }) + .withPostOptionPhase(async (scene: BattleScene) => { + // Status applied after dealer leaves (to make thematic sense) + const encounter = scene.currentBattle.mysteryEncounter; + const chosenPokemon = encounter.misc.chosenPokemon; + + // Roll for poison (20%) + if (randSeedInt(10) < 2) { + if (chosenPokemon.trySetStatus(StatusEffect.POISON)) { + // Poison applied + queueEncounterMessage( + scene, + "mysteryEncounter:shady_vitamin_dealer_poison" + ); + } else { + // Pokemon immune or something else prevents status + queueEncounterMessage( + scene, + "mysteryEncounter:shady_vitamin_dealer_no_bad_effects" + ); + } + } else { + queueEncounterMessage( + scene, + "mysteryEncounter:shady_vitamin_dealer_no_bad_effects" + ); + } + + setEncounterExp(scene, [chosenPokemon.id], 100); + + chosenPokemon.updateInfo(); + }) + .build() + ) + .withSimpleOption( + { + buttonLabel: "mysteryEncounter:shady_vitamin_dealer_option_3_label", + buttonTooltip: "mysteryEncounter:shady_vitamin_dealer_option_3_tooltip", + }, + async (scene: BattleScene) => { + // Leave encounter with no rewards or exp + leaveEncounterWithoutBattle(scene, true); + return true; } - scene.updateModifiers(true); - - leaveEncounterWithoutBattle(scene); - }) - .withPostOptionPhase(async (scene: BattleScene) => { - // Damage and status applied after dealer leaves (to make thematic sense) - const encounter = scene.currentBattle.mysteryEncounter; - const chosenPokemon = encounter.misc.chosenPokemon; - - // Pokemon takes 1/3 max HP damage - const damage = Math.round(chosenPokemon.getMaxHp() / 3); - chosenPokemon.hp = Math.max(chosenPokemon.hp - damage, 0); - - // Roll for poison (80%) - if (randSeedInt(10) < 8) { - if (chosenPokemon.trySetStatus(StatusEffect.TOXIC)) { - // Toxic applied - queueEncounterMessage(scene, "mysteryEncounter:shady_vitamin_dealer_bad_poison"); - } else { - // Pokemon immune or something else prevents status - queueEncounterMessage(scene, "mysteryEncounter:shady_vitamin_dealer_damage_only"); - } - } else { - queueEncounterMessage(scene, "mysteryEncounter:shady_vitamin_dealer_damage_only"); - } - - setEncounterExp(scene, [chosenPokemon.id], 100); - - chosenPokemon.updateInfo(); - }) - .build()) - .withOption(new MysteryEncounterOptionBuilder() - .withSceneMoneyRequirement(0, 5) // Wave scaling money multiplier of 5 - .withPreOptionPhase(async (scene: BattleScene): Promise => { - const encounter = scene.currentBattle.mysteryEncounter; - const onPokemonSelected = (pokemon: PlayerPokemon) => { - // Update money - updatePlayerMoney(scene, -(encounter.options[1].requirements[0] as MoneyRequirement).requiredMoney); - // Calculate modifiers and dialogue tokens - const modifiers = [ - generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type, - generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type - ]; - encounter.setDialogueToken("boost1", modifiers[0].name); - encounter.setDialogueToken("boost2", modifiers[1].name); - encounter.misc = { - chosenPokemon: pokemon, - modifiers: modifiers - }; - }; - - // Only Pokemon that can gain benefits are above 1/3rd HP with no status - const selectableFilter = (pokemon: Pokemon) => { - // If pokemon meets primary pokemon reqs, it can be selected - const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon); - if (!meetsReqs) { - return i18next.t("mysteryEncounter:shady_vitamin_dealer_invalid_selection"); - } - - return null; - }; - - return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); - }) - .withOptionPhase(async (scene: BattleScene) => { - // Choose Expensive Option - const encounter = scene.currentBattle.mysteryEncounter; - const chosenPokemon = encounter.misc.chosenPokemon; - const modifiers = encounter.misc.modifiers; - - for (const modType of modifiers) { - const modifier = modType.newModifier(chosenPokemon); - await scene.addModifier(modifier, true, false, false, true); - } - scene.updateModifiers(true); - - leaveEncounterWithoutBattle(scene); - }) - .withPostOptionPhase(async (scene: BattleScene) => { - // Status applied after dealer leaves (to make thematic sense) - const encounter = scene.currentBattle.mysteryEncounter; - const chosenPokemon = encounter.misc.chosenPokemon; - - // Roll for poison (20%) - if (randSeedInt(10) < 2) { - if (chosenPokemon.trySetStatus(StatusEffect.POISON)) { - // Poison applied - queueEncounterMessage(scene, "mysteryEncounter:shady_vitamin_dealer_poison"); - } else { - // Pokemon immune or something else prevents status - queueEncounterMessage(scene, "mysteryEncounter:shady_vitamin_dealer_no_bad_effects"); - } - } else { - queueEncounterMessage(scene, "mysteryEncounter:shady_vitamin_dealer_no_bad_effects"); - } - - setEncounterExp(scene, [chosenPokemon.id], 100); - - chosenPokemon.updateInfo(); - }) - .build()) - .withSimpleOption(async (scene: BattleScene) => { - // Leave encounter with no rewards or exp - leaveEncounterWithoutBattle(scene, true); - return true; - }) - .build(); + ) + .build(); diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index c5663eef6ba..f44921ba677 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -1,7 +1,6 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { TrainingSessionDialogue } from "#app/data/mystery-encounters/dialogue/training-session-dialogue"; import { SleepingSnorlaxDialogue } from "./dialogue/sleeping-snorlax-dialogue"; -import { ShadyVitaminDealerDialogue } from "#app/data/mystery-encounters/dialogue/shady-vitamin-dealer"; import { TextStyle } from "#app/ui/text"; export class TextDisplay { @@ -81,5 +80,4 @@ export const allMysteryEncounterDialogue: { [encounterType: number]: MysteryEnco export function initMysteryEncounterDialogue() { allMysteryEncounterDialogue[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionDialogue; allMysteryEncounterDialogue[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxDialogue; - allMysteryEncounterDialogue[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerDialogue; } From 2d4269732103eac8c6dadc93211c06ed1808188a Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 14:44:31 -0700 Subject: [PATCH 14/29] migrate sleeping snorlax encounter --- .../dialogue/sleeping-snorlax-dialogue.ts | 39 --- .../encounters/sleeping-snorlax.ts | 256 +++++++++++------- .../mystery-encounter-dialogue.ts | 2 - 3 files changed, 159 insertions(+), 138 deletions(-) delete mode 100644 src/data/mystery-encounters/dialogue/sleeping-snorlax-dialogue.ts diff --git a/src/data/mystery-encounters/dialogue/sleeping-snorlax-dialogue.ts b/src/data/mystery-encounters/dialogue/sleeping-snorlax-dialogue.ts deleted file mode 100644 index c10835f728e..00000000000 --- a/src/data/mystery-encounters/dialogue/sleeping-snorlax-dialogue.ts +++ /dev/null @@ -1,39 +0,0 @@ -import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; - -export const SleepingSnorlaxDialogue: MysteryEncounterDialogue = { - intro: [ - { - text: "mysteryEncounter:sleeping_snorlax_intro_message" - } - ], - encounterOptionsDialogue: { - title: "mysteryEncounter:sleeping_snorlax_title", - description: "mysteryEncounter:sleeping_snorlax_description", - query: "mysteryEncounter:sleeping_snorlax_query", - options: [ - { - buttonLabel: "mysteryEncounter:sleeping_snorlax_option_1_label", - buttonTooltip: "mysteryEncounter:sleeping_snorlax_option_1_tooltip", - selected: [ - { - text: "mysteryEncounter:sleeping_snorlax_option_1_selected_message" - } - ] - }, - { - buttonLabel: "mysteryEncounter:sleeping_snorlax_option_2_label", - buttonTooltip: "mysteryEncounter:sleeping_snorlax_option_2_tooltip", - selected: [ - { - text: "mysteryEncounter:sleeping_snorlax_option_2_selected_message" - } - ] - }, - { - buttonLabel: "mysteryEncounter:sleeping_snorlax_option_3_label", - buttonTooltip: "mysteryEncounter:sleeping_snorlax_option_3_tooltip", - disabledTooltip: "mysteryEncounter:sleeping_snorlax_option_3_disabled_tooltip" - } - ] - } -}; diff --git a/src/data/mystery-encounters/encounters/sleeping-snorlax.ts b/src/data/mystery-encounters/encounters/sleeping-snorlax.ts index 6a820812ae1..324beb35e99 100644 --- a/src/data/mystery-encounters/encounters/sleeping-snorlax.ts +++ b/src/data/mystery-encounters/encounters/sleeping-snorlax.ts @@ -1,6 +1,5 @@ -import { - modifierTypes -} from "#app/modifier/modifier-type"; +import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; +import { modifierTypes } from "#app/modifier/modifier-type"; import { BerryType } from "#enums/berry-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; @@ -8,106 +7,169 @@ import BattleScene from "../../../battle-scene"; import * as Utils from "../../../utils"; import { getPokemonSpecies } from "../../pokemon-species"; import { Status, StatusEffect } from "../../status-effect"; -import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; +import MysteryEncounter, { + MysteryEncounterBuilder, + MysteryEncounterTier, +} from "../mystery-encounter"; import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { MoveRequirement } from "../mystery-encounter-requirements"; import { EnemyPartyConfig, - EnemyPokemonConfig, generateModifierTypeOption, + EnemyPokemonConfig, + generateModifierTypeOption, initBattleWithEnemyConfig, - leaveEncounterWithoutBattle, queueEncounterMessage, setEncounterExp, - setEncounterRewards + leaveEncounterWithoutBattle, + queueEncounterMessage, + setEncounterExp, + setEncounterRewards, } from "../mystery-encounter-utils"; -import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; -export const SleepingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuilder - .withEncounterType(MysteryEncounterType.SLEEPING_SNORLAX) - .withEncounterTier(MysteryEncounterTier.ULTRA) - .withIntroSpriteConfigs([ - { - spriteKey: Species.SNORLAX.toString(), - fileRoot: "pokemon", - hasShadow: true, - tint: 0.25, - scale: 1.5, - repeat: true, - y: 5 - } - ]) - .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 - .withCatchAllowed(true) - .withHideWildIntroMessage(true) - .withOnInit((scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - console.log(encounter); +/** i18n namespace for the encounter */ +const namespace = "mysteryEncounter:sleeping_snorlax"; - // Calculate boss mon - const bossSpecies = getPokemonSpecies(Species.SNORLAX); - const pokemonConfig: EnemyPokemonConfig = { - species: bossSpecies, - isBoss: true, - status: StatusEffect.SLEEP - }; - const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 2, - pokemonConfigs: [pokemonConfig] - }; - encounter.enemyPartyConfigs = [config]; - return true; - }) - .withSimpleOption(async (scene: BattleScene) => { - // Pick battle - // TODO: do we want special rewards for this? - // setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: true}); - await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]); - }) - .withSimpleOption(async (scene: BattleScene) => { - const instance = scene.currentBattle.mysteryEncounter; - let roll: integer; - scene.executeWithSeedOffset(() => { - roll = Utils.randSeedInt(16, 0); - }, scene.currentBattle.waveIndex); - - // Half Snorlax exp to entire party - setEncounterExp(scene, scene.getParty().map(p => p.id), 98); - - if (roll > 4) { - // Fall asleep and get a sitrus berry (75%) - const p = instance.primaryPokemon; - p.status = new Status(StatusEffect.SLEEP, 0, 3); - p.updateInfo(true); - // const sitrus = (modifierTypes.BERRY?.() as ModifierTypeGenerator).generateType(scene.getParty(), [BerryType.SITRUS]); - const sitrus = generateModifierTypeOption(scene, modifierTypes.BERRY, [BerryType.SITRUS]); - - setEncounterRewards(scene, { guaranteedModifierTypeOptions: [sitrus], fillRemaining: false }); - queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_2_bad_result"); - leaveEncounterWithoutBattle(scene); - } else { - // Heal to full (25%) - for (const pokemon of scene.getParty()) { - pokemon.hp = pokemon.getMaxHp(); - pokemon.resetStatus(); - for (const move of pokemon.moveset) { - move.ppUsed = 0; - } - pokemon.updateInfo(true); - } - - queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_2_good_result"); - leaveEncounterWithoutBattle(scene); - } - }) - .withOption(new MysteryEncounterOptionBuilder() - .withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) - .withOptionPhase(async (scene: BattleScene) => { - // Steal the Snorlax's Leftovers - const instance = scene.currentBattle.mysteryEncounter; - setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: false }); - queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_3_good_result"); - // Snorlax exp to Pokemon that did the stealing - setEncounterExp(scene, [instance.primaryPokemon.id], 189); - leaveEncounterWithoutBattle(scene); - }) - .build() +export const SleepingSnorlaxEncounter: MysteryEncounter = + MysteryEncounterBuilder.withEncounterType( + MysteryEncounterType.SLEEPING_SNORLAX ) - .build(); + .withEncounterTier(MysteryEncounterTier.ULTRA) + .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 + .withCatchAllowed(true) + .withHideWildIntroMessage(true) + .withIntroSpriteConfigs([ + { + spriteKey: Species.SNORLAX.toString(), + fileRoot: "pokemon", + hasShadow: true, + tint: 0.25, + scale: 1.5, + repeat: true, + y: 5, + }, + ]) + .withIntroDialogue([ + { + text: `${namespace}_intro_message`, + }, + ]) + .withOnInit((scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + console.log(encounter); + + // Calculate boss mon + const bossSpecies = getPokemonSpecies(Species.SNORLAX); + const pokemonConfig: EnemyPokemonConfig = { + species: bossSpecies, + isBoss: true, + status: StatusEffect.SLEEP, + }; + const config: EnemyPartyConfig = { + levelAdditiveMultiplier: 2, + pokemonConfigs: [pokemonConfig], + }; + encounter.enemyPartyConfigs = [config]; + return true; + }) + .withTitle(`${namespace}_title`) + .withDescription(`${namespace}_description`) + .withQuery(`${namespace}_query`) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_1_label`, + buttonTooltip: `${namespace}_option_1_tooltip`, + selected: [ + { + text: `${namespace}_option_1_selected_message`, + }, + ], + }, + async (scene: BattleScene) => { + // Pick battle + // TODO: do we want special rewards for this? + // setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: true}); + await initBattleWithEnemyConfig( + scene, + scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0] + ); + } + ) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_2_label`, + buttonTooltip: `${namespace}_option_2_tooltip`, + selected: [ + { + text: `${namespace}_option_2_selected_message`, + }, + ], + }, + async (scene: BattleScene) => { + const instance = scene.currentBattle.mysteryEncounter; + let roll: integer; + scene.executeWithSeedOffset(() => { + roll = Utils.randSeedInt(16, 0); + }, scene.currentBattle.waveIndex); + + // Half Snorlax exp to entire party + setEncounterExp( + scene, + scene.getParty().map((p) => p.id), + 98 + ); + + if (roll > 4) { + // Fall asleep and get a sitrus berry (75%) + const p = instance.primaryPokemon; + p.status = new Status(StatusEffect.SLEEP, 0, 3); + p.updateInfo(true); + // const sitrus = (modifierTypes.BERRY?.() as ModifierTypeGenerator).generateType(scene.getParty(), [BerryType.SITRUS]); + const sitrus = generateModifierTypeOption( + scene, + modifierTypes.BERRY, + [BerryType.SITRUS] + ); + + setEncounterRewards(scene, { + guaranteedModifierTypeOptions: [sitrus], + fillRemaining: false, + }); + queueEncounterMessage(scene, `${namespace}_option_2_bad_result`); + leaveEncounterWithoutBattle(scene); + } else { + // Heal to full (25%) + for (const pokemon of scene.getParty()) { + pokemon.hp = pokemon.getMaxHp(); + pokemon.resetStatus(); + for (const move of pokemon.moveset) { + move.ppUsed = 0; + } + pokemon.updateInfo(true); + } + + queueEncounterMessage(scene, `${namespace}_option_2_good_result`); + leaveEncounterWithoutBattle(scene); + } + } + ) + .withOption( + new MysteryEncounterOptionBuilder() + .withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) + .withDialogue({ + buttonLabel: `${namespace}_option_3_label`, + buttonTooltip: `${namespace}_option_3_tooltip`, + disabledTooltip: `${namespace}_option_3_disabled_tooltip`, + }) + .withOptionPhase(async (scene: BattleScene) => { + // Steal the Snorlax's Leftovers + const instance = scene.currentBattle.mysteryEncounter; + setEncounterRewards(scene, { + guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], + fillRemaining: false, + }); + queueEncounterMessage(scene, `${namespace}_option_3_good_result`); + // Snorlax exp to Pokemon that did the stealing + setEncounterExp(scene, [instance.primaryPokemon.id], 189); + leaveEncounterWithoutBattle(scene); + }) + .build() + ) + .build(); diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index f44921ba677..fcd2db8cc52 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -1,6 +1,5 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { TrainingSessionDialogue } from "#app/data/mystery-encounters/dialogue/training-session-dialogue"; -import { SleepingSnorlaxDialogue } from "./dialogue/sleeping-snorlax-dialogue"; import { TextStyle } from "#app/ui/text"; export class TextDisplay { @@ -79,5 +78,4 @@ export const allMysteryEncounterDialogue: { [encounterType: number]: MysteryEnco export function initMysteryEncounterDialogue() { allMysteryEncounterDialogue[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionDialogue; - allMysteryEncounterDialogue[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxDialogue; } From 39f98222a6a0a8a82b362f67b1f7b97ff9bcc327 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 14:50:19 -0700 Subject: [PATCH 15/29] migrate training session encounter --- .../dialogue/training-session-dialogue.ts | 50 -- .../encounters/training-session.ts | 649 +++++++++++------- .../mystery-encounter-dialogue.ts | 3 - 3 files changed, 396 insertions(+), 306 deletions(-) delete mode 100644 src/data/mystery-encounters/dialogue/training-session-dialogue.ts diff --git a/src/data/mystery-encounters/dialogue/training-session-dialogue.ts b/src/data/mystery-encounters/dialogue/training-session-dialogue.ts deleted file mode 100644 index 87f30ca14c2..00000000000 --- a/src/data/mystery-encounters/dialogue/training-session-dialogue.ts +++ /dev/null @@ -1,50 +0,0 @@ -import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; - -export const TrainingSessionDialogue: MysteryEncounterDialogue = { - intro: [ - { - text: "mysteryEncounter:training_session_intro_message" - } - ], - encounterOptionsDialogue: { - title: "mysteryEncounter:training_session_title", - description: "mysteryEncounter:training_session_description", - query: "mysteryEncounter:training_session_query", - options: [ - { - buttonLabel: "mysteryEncounter:training_session_option_1_label", - buttonTooltip: "mysteryEncounter:training_session_option_1_tooltip", - selected: [ - { - text: "mysteryEncounter:training_session_option_selected_message" - } - ] - }, - { - buttonLabel: "mysteryEncounter:training_session_option_2_label", - buttonTooltip: "mysteryEncounter:training_session_option_2_tooltip", - secondOptionPrompt: "mysteryEncounter:training_session_option_2_select_prompt", - selected: [ - { - text: "mysteryEncounter:training_session_option_selected_message" - } - ] - }, - { - buttonLabel: "mysteryEncounter:training_session_option_3_label", - buttonTooltip: "mysteryEncounter:training_session_option_3_tooltip", - secondOptionPrompt: "mysteryEncounter:training_session_option_3_select_prompt", - selected: [ - { - text: "mysteryEncounter:training_session_option_selected_message" - } - ] - } - ] - }, - outro: [ - { - text: "mysteryEncounter:training_session_outro_win" - } - ] -}; diff --git a/src/data/mystery-encounters/encounters/training-session.ts b/src/data/mystery-encounters/encounters/training-session.ts index 8030cbaf850..e9b0461857d 100644 --- a/src/data/mystery-encounters/encounters/training-session.ts +++ b/src/data/mystery-encounters/encounters/training-session.ts @@ -4,7 +4,7 @@ import { getEncounterText, initBattleWithEnemyConfig, selectPokemonForOption, - setEncounterRewards + setEncounterRewards, } from "#app/data/mystery-encounters/mystery-encounter-utils"; import { getNatureName, Nature } from "#app/data/nature"; import { speciesStarters } from "#app/data/pokemon-species"; @@ -20,270 +20,414 @@ import { randSeedShuffle } from "#app/utils"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; -import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; +import MysteryEncounter, { + MysteryEncounterBuilder, + MysteryEncounterTier, +} from "../mystery-encounter"; import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilder - .withEncounterType(MysteryEncounterType.TRAINING_SESSION) - .withEncounterTier(MysteryEncounterTier.ULTRA) - .withIntroSpriteConfigs([ - { - spriteKey: "training_gear", - fileRoot: "mystery-encounters", - hasShadow: true, - y: 3 - } - ]) - .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 - .withHideWildIntroMessage(true) - .withOption(new MysteryEncounterOptionBuilder() - .withPreOptionPhase(async (scene: BattleScene): Promise => { - const encounter = scene.currentBattle.mysteryEncounter; - const onPokemonSelected = (pokemon: PlayerPokemon) => { - encounter.misc = { - playerPokemon: pokemon - }; - }; - - return selectPokemonForOption(scene, onPokemonSelected); - }) - .withOptionPhase(async (scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon; - - // Spawn light training session with chosen pokemon - // Every 50 waves, add +1 boss segment, capping at 5 - const segments = Math.min(2 + Math.floor(scene.currentBattle.waveIndex / 50), 5); - const modifiers = new ModifiersHolder(); - const config = getEnemyConfig(scene, playerPokemon, segments, modifiers); - scene.removePokemonFromPlayerParty(playerPokemon, false); - - const getIvName = (index: number) => { - switch (index) { - case Stat.HP: - return pokemonInfo.Stat["HPshortened"]; - case Stat.ATK: - return pokemonInfo.Stat["ATKshortened"]; - case Stat.DEF: - return pokemonInfo.Stat["DEFshortened"]; - case Stat.SPATK: - return pokemonInfo.Stat["SPATKshortened"]; - case Stat.SPDEF: - return pokemonInfo.Stat["SPDEFshortened"]; - case Stat.SPD: - return pokemonInfo.Stat["SPDshortened"]; - } - }; - - const onBeforeRewardsPhase = () => { - encounter.setDialogueToken("stat1", "-"); - encounter.setDialogueToken("stat2", "-"); - // Add the pokemon back to party with IV boost - const ivIndexes = []; - playerPokemon.ivs.forEach((iv, index) => { - if (iv < 31) { - ivIndexes.push({ iv: iv, index: index }); - } - }); - - // Improves 2 random non-maxed IVs - // +10 if IV is < 10, +5 if between 10-20, and +3 if > 20 - // A 0-4 starting IV will cap in 6 encounters (assuming you always rolled that IV) - // 5-14 starting IV caps in 5 encounters - // 15-19 starting IV caps in 4 encounters - // 20-24 starting IV caps in 3 encounters - // 25-27 starting IV caps in 2 encounters - let improvedCount = 0; - while (ivIndexes.length > 0 && improvedCount < 2) { - randSeedShuffle(ivIndexes); - const ivToChange = ivIndexes.pop(); - let newVal = ivToChange.iv; - if (improvedCount === 0) { - encounter.setDialogueToken("stat1", getIvName(ivToChange.index)); - } else { - encounter.setDialogueToken("stat2", getIvName(ivToChange.index)); - } - - // Corrects required encounter breakpoints to be continuous for all IV values - if (ivToChange.iv <= 21 && ivToChange.iv - 1 % 5 === 0) { - newVal += 1; - } - - newVal += ivToChange.iv <= 10 ? 10 : ivToChange.iv <= 20 ? 5 : 3; - newVal = Math.min(newVal, 31); - playerPokemon.ivs[ivToChange.index] = newVal; - improvedCount++; - } - - if (improvedCount > 0) { - playerPokemon.calculateStats(); - scene.gameData.updateSpeciesDexIvs(playerPokemon.species.getRootSpeciesId(true), playerPokemon.ivs); - scene.gameData.setPokemonCaught(playerPokemon, false); - } - - // Add pokemon and mods back - scene.getParty().push(playerPokemon); - for (const mod of modifiers.value) { - scene.addModifier(mod, true, false, false, true); - } - scene.updateModifiers(true); - scene.queueMessage(getEncounterText(scene, "mysteryEncounter:training_session_battle_finished_1"), null, true); - }; - - setEncounterRewards(scene, { fillRemaining: true }, null, onBeforeRewardsPhase); - - return initBattleWithEnemyConfig(scene, config); - }) - .build() +export const TrainingSessionEncounter: MysteryEncounter = + MysteryEncounterBuilder.withEncounterType( + MysteryEncounterType.TRAINING_SESSION ) - .withOption(new MysteryEncounterOptionBuilder() - .withPreOptionPhase(async (scene: BattleScene): Promise => { - // Open menu for selecting pokemon and Nature - const encounter = scene.currentBattle.mysteryEncounter; - const natures = new Array(25).fill(null).map((val, i) => i as Nature); - const onPokemonSelected = (pokemon: PlayerPokemon) => { - // Return the options for nature selection - return natures.map((nature: Nature) => { - const option: OptionSelectItem = { - label: getNatureName(nature, true, true, true, scene.uiTheme), - handler: () => { - // Pokemon and second option selected - encounter.setDialogueToken("nature", getNatureName(nature)); - encounter.misc = { - playerPokemon: pokemon, - chosenNature: nature - }; - return true; - } - }; - return option; - }); - }; - - return selectPokemonForOption(scene, onPokemonSelected); - }) - .withOptionPhase(async (scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon; - - // Spawn medium training session with chosen pokemon - // Every 40 waves, add +1 boss segment, capping at 6 - const segments = Math.min(2 + Math.floor(scene.currentBattle.waveIndex / 40), 6); - const modifiers = new ModifiersHolder(); - const config = getEnemyConfig(scene, playerPokemon, segments, modifiers); - scene.removePokemonFromPlayerParty(playerPokemon, false); - - const onBeforeRewardsPhase = () => { - scene.queueMessage(getEncounterText(scene, "mysteryEncounter:training_session_battle_finished_2"), null, true); - // Add the pokemon back to party with Nature change - playerPokemon.setNature(encounter.misc.chosenNature); - scene.gameData.setPokemonCaught(playerPokemon, false); - - // Add pokemon and mods back - scene.getParty().push(playerPokemon); - for (const mod of modifiers.value) { - scene.addModifier(mod, true, false, false, true); - } - scene.updateModifiers(true); - }; - - setEncounterRewards(scene, { fillRemaining: true }, null, onBeforeRewardsPhase); - - return initBattleWithEnemyConfig(scene, config); - }) - .build() - ) - .withOption(new MysteryEncounterOptionBuilder() - .withPreOptionPhase(async (scene: BattleScene): Promise => { - // Open menu for selecting pokemon and ability to learn - const encounter = scene.currentBattle.mysteryEncounter; - const onPokemonSelected = (pokemon: PlayerPokemon) => { - // Return the options for ability selection - const speciesForm = !!pokemon.getFusionSpeciesForm() ? pokemon.getFusionSpeciesForm() : pokemon.getSpeciesForm(); - const abilityCount = speciesForm.getAbilityCount(); - const abilities = new Array(abilityCount).fill(null).map((val, i) => allAbilities[speciesForm.getAbility(i)]); - return abilities.map((ability: Ability, index) => { - const option: OptionSelectItem = { - label: ability.name, - handler: () => { - // Pokemon and ability selected - encounter.setDialogueToken("ability", ability.name); - encounter.misc = { - playerPokemon: pokemon, - abilityIndex: index - }; - return true; + .withEncounterTier(MysteryEncounterTier.ULTRA) + .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 + .withHideWildIntroMessage(true) + .withIntroSpriteConfigs([ + { + spriteKey: "training_gear", + fileRoot: "mystery-encounters", + hasShadow: true, + y: 3, + }, + ]) + .withIntroDialogue([ + { + text: "mysteryEncounter:training_session_intro_message", + }, + ]) + .withTitle("mysteryEncounter:training_session_title") + .withDescription("mysteryEncounter:training_session_description") + .withQuery("mysteryEncounter:training_session_query") + .withOption( + new MysteryEncounterOptionBuilder() + .withDialogue({ + buttonLabel: "mysteryEncounter:training_session_option_1_label", + buttonTooltip: "mysteryEncounter:training_session_option_1_tooltip", + selected: [ + { + text: "mysteryEncounter:training_session_option_selected_message", }, - onHover: () => { - scene.ui.showText(ability.description); + ], + }) + .withPreOptionPhase(async (scene: BattleScene): Promise => { + const encounter = scene.currentBattle.mysteryEncounter; + const onPokemonSelected = (pokemon: PlayerPokemon) => { + encounter.misc = { + playerPokemon: pokemon, + }; + }; + + return selectPokemonForOption(scene, onPokemonSelected); + }) + .withOptionPhase(async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon; + + // Spawn light training session with chosen pokemon + // Every 50 waves, add +1 boss segment, capping at 5 + const segments = Math.min( + 2 + Math.floor(scene.currentBattle.waveIndex / 50), + 5 + ); + const modifiers = new ModifiersHolder(); + const config = getEnemyConfig( + scene, + playerPokemon, + segments, + modifiers + ); + scene.removePokemonFromPlayerParty(playerPokemon, false); + + const getIvName = (index: number) => { + switch (index) { + case Stat.HP: + return pokemonInfo.Stat["HPshortened"]; + case Stat.ATK: + return pokemonInfo.Stat["ATKshortened"]; + case Stat.DEF: + return pokemonInfo.Stat["DEFshortened"]; + case Stat.SPATK: + return pokemonInfo.Stat["SPATKshortened"]; + case Stat.SPDEF: + return pokemonInfo.Stat["SPDEFshortened"]; + case Stat.SPD: + return pokemonInfo.Stat["SPDshortened"]; } }; - return option; - }); - }; - return selectPokemonForOption(scene, onPokemonSelected); - }) - .withOptionPhase(async (scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon; + const onBeforeRewardsPhase = () => { + encounter.setDialogueToken("stat1", "-"); + encounter.setDialogueToken("stat2", "-"); + // Add the pokemon back to party with IV boost + const ivIndexes = []; + playerPokemon.ivs.forEach((iv, index) => { + if (iv < 31) { + ivIndexes.push({ iv: iv, index: index }); + } + }); - // Spawn hard training session with chosen pokemon - // Every 30 waves, add +1 boss segment, capping at 6 - // Also starts with +1 to all stats - const segments = Math.min(2 + Math.floor(scene.currentBattle.waveIndex / 30), 6); - const modifiers = new ModifiersHolder(); - const config = getEnemyConfig(scene, playerPokemon, segments, modifiers); - config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON]; - scene.removePokemonFromPlayerParty(playerPokemon, false); + // Improves 2 random non-maxed IVs + // +10 if IV is < 10, +5 if between 10-20, and +3 if > 20 + // A 0-4 starting IV will cap in 6 encounters (assuming you always rolled that IV) + // 5-14 starting IV caps in 5 encounters + // 15-19 starting IV caps in 4 encounters + // 20-24 starting IV caps in 3 encounters + // 25-27 starting IV caps in 2 encounters + let improvedCount = 0; + while (ivIndexes.length > 0 && improvedCount < 2) { + randSeedShuffle(ivIndexes); + const ivToChange = ivIndexes.pop(); + let newVal = ivToChange.iv; + if (improvedCount === 0) { + encounter.setDialogueToken( + "stat1", + getIvName(ivToChange.index) + ); + } else { + encounter.setDialogueToken( + "stat2", + getIvName(ivToChange.index) + ); + } - const onBeforeRewardsPhase = () => { - scene.queueMessage(getEncounterText(scene, "mysteryEncounter:training_session_battle_finished_3"), null, true); - // Add the pokemon back to party with ability change - const abilityIndex = encounter.misc.abilityIndex; - if (!!playerPokemon.getFusionSpeciesForm()) { - playerPokemon.fusionAbilityIndex = abilityIndex; - if (speciesStarters.hasOwnProperty(playerPokemon.fusionSpecies.speciesId)) { - scene.gameData.starterData[playerPokemon.fusionSpecies.speciesId].abilityAttr |= abilityIndex !== 1 || playerPokemon.fusionSpecies.ability2 - ? Math.pow(2, playerPokemon.fusionAbilityIndex) - : AbilityAttr.ABILITY_HIDDEN; - } - } else { - playerPokemon.abilityIndex = abilityIndex; - if (speciesStarters.hasOwnProperty(playerPokemon.species.speciesId)) { - scene.gameData.starterData[playerPokemon.species.speciesId].abilityAttr |= abilityIndex !== 1 || playerPokemon.species.ability2 - ? Math.pow(2, playerPokemon.abilityIndex) - : AbilityAttr.ABILITY_HIDDEN; - } - } + // Corrects required encounter breakpoints to be continuous for all IV values + if (ivToChange.iv <= 21 && ivToChange.iv - (1 % 5) === 0) { + newVal += 1; + } - playerPokemon.getAbility(); - playerPokemon.calculateStats(); - scene.gameData.setPokemonCaught(playerPokemon, false); + newVal += ivToChange.iv <= 10 ? 10 : ivToChange.iv <= 20 ? 5 : 3; + newVal = Math.min(newVal, 31); + playerPokemon.ivs[ivToChange.index] = newVal; + improvedCount++; + } - // Add pokemon and mods back - scene.getParty().push(playerPokemon); - for (const mod of modifiers.value) { - scene.addModifier(mod, true, false, false, true); - } - scene.updateModifiers(true); - }; + if (improvedCount > 0) { + playerPokemon.calculateStats(); + scene.gameData.updateSpeciesDexIvs( + playerPokemon.species.getRootSpeciesId(true), + playerPokemon.ivs + ); + scene.gameData.setPokemonCaught(playerPokemon, false); + } - setEncounterRewards(scene, { fillRemaining: true }, null, onBeforeRewardsPhase); + // Add pokemon and mods back + scene.getParty().push(playerPokemon); + for (const mod of modifiers.value) { + scene.addModifier(mod, true, false, false, true); + } + scene.updateModifiers(true); + scene.queueMessage( + getEncounterText( + scene, + "mysteryEncounter:training_session_battle_finished_1" + ), + null, + true + ); + }; - return initBattleWithEnemyConfig(scene, config); - }) - .build() - ) - .build(); + setEncounterRewards( + scene, + { fillRemaining: true }, + null, + onBeforeRewardsPhase + ); -function getEnemyConfig(scene: BattleScene, playerPokemon: PlayerPokemon, segments: number, modifiers: ModifiersHolder): EnemyPartyConfig { + return initBattleWithEnemyConfig(scene, config); + }) + .build() + ) + .withOption( + new MysteryEncounterOptionBuilder() + .withDialogue({ + buttonLabel: "mysteryEncounter:training_session_option_2_label", + buttonTooltip: "mysteryEncounter:training_session_option_2_tooltip", + secondOptionPrompt: + "mysteryEncounter:training_session_option_2_select_prompt", + selected: [ + { + text: "mysteryEncounter:training_session_option_selected_message", + }, + ], + }) + .withPreOptionPhase(async (scene: BattleScene): Promise => { + // Open menu for selecting pokemon and Nature + const encounter = scene.currentBattle.mysteryEncounter; + const natures = new Array(25).fill(null).map((val, i) => i as Nature); + const onPokemonSelected = (pokemon: PlayerPokemon) => { + // Return the options for nature selection + return natures.map((nature: Nature) => { + const option: OptionSelectItem = { + label: getNatureName(nature, true, true, true, scene.uiTheme), + handler: () => { + // Pokemon and second option selected + encounter.setDialogueToken("nature", getNatureName(nature)); + encounter.misc = { + playerPokemon: pokemon, + chosenNature: nature, + }; + return true; + }, + }; + return option; + }); + }; + + return selectPokemonForOption(scene, onPokemonSelected); + }) + .withOptionPhase(async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon; + + // Spawn medium training session with chosen pokemon + // Every 40 waves, add +1 boss segment, capping at 6 + const segments = Math.min( + 2 + Math.floor(scene.currentBattle.waveIndex / 40), + 6 + ); + const modifiers = new ModifiersHolder(); + const config = getEnemyConfig( + scene, + playerPokemon, + segments, + modifiers + ); + scene.removePokemonFromPlayerParty(playerPokemon, false); + + const onBeforeRewardsPhase = () => { + scene.queueMessage( + getEncounterText( + scene, + "mysteryEncounter:training_session_battle_finished_2" + ), + null, + true + ); + // Add the pokemon back to party with Nature change + playerPokemon.setNature(encounter.misc.chosenNature); + scene.gameData.setPokemonCaught(playerPokemon, false); + + // Add pokemon and mods back + scene.getParty().push(playerPokemon); + for (const mod of modifiers.value) { + scene.addModifier(mod, true, false, false, true); + } + scene.updateModifiers(true); + }; + + setEncounterRewards( + scene, + { fillRemaining: true }, + null, + onBeforeRewardsPhase + ); + + return initBattleWithEnemyConfig(scene, config); + }) + .build() + ) + .withOption( + new MysteryEncounterOptionBuilder() + .withDialogue({ + buttonLabel: "mysteryEncounter:training_session_option_3_label", + buttonTooltip: "mysteryEncounter:training_session_option_3_tooltip", + secondOptionPrompt: + "mysteryEncounter:training_session_option_3_select_prompt", + selected: [ + { + text: "mysteryEncounter:training_session_option_selected_message", + }, + ], + }) + .withPreOptionPhase(async (scene: BattleScene): Promise => { + // Open menu for selecting pokemon and ability to learn + const encounter = scene.currentBattle.mysteryEncounter; + const onPokemonSelected = (pokemon: PlayerPokemon) => { + // Return the options for ability selection + const speciesForm = !!pokemon.getFusionSpeciesForm() + ? pokemon.getFusionSpeciesForm() + : pokemon.getSpeciesForm(); + const abilityCount = speciesForm.getAbilityCount(); + const abilities = new Array(abilityCount) + .fill(null) + .map((val, i) => allAbilities[speciesForm.getAbility(i)]); + return abilities.map((ability: Ability, index) => { + const option: OptionSelectItem = { + label: ability.name, + handler: () => { + // Pokemon and ability selected + encounter.setDialogueToken("ability", ability.name); + encounter.misc = { + playerPokemon: pokemon, + abilityIndex: index, + }; + return true; + }, + onHover: () => { + scene.ui.showText(ability.description); + }, + }; + return option; + }); + }; + + return selectPokemonForOption(scene, onPokemonSelected); + }) + .withOptionPhase(async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon; + + // Spawn hard training session with chosen pokemon + // Every 30 waves, add +1 boss segment, capping at 6 + // Also starts with +1 to all stats + const segments = Math.min( + 2 + Math.floor(scene.currentBattle.waveIndex / 30), + 6 + ); + const modifiers = new ModifiersHolder(); + const config = getEnemyConfig( + scene, + playerPokemon, + segments, + modifiers + ); + config.pokemonConfigs[0].tags = [ + BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON, + ]; + scene.removePokemonFromPlayerParty(playerPokemon, false); + + const onBeforeRewardsPhase = () => { + scene.queueMessage( + getEncounterText( + scene, + "mysteryEncounter:training_session_battle_finished_3" + ), + null, + true + ); + // Add the pokemon back to party with ability change + const abilityIndex = encounter.misc.abilityIndex; + if (!!playerPokemon.getFusionSpeciesForm()) { + playerPokemon.fusionAbilityIndex = abilityIndex; + if ( + speciesStarters.hasOwnProperty( + playerPokemon.fusionSpecies.speciesId + ) + ) { + scene.gameData.starterData[ + playerPokemon.fusionSpecies.speciesId + ].abilityAttr |= + abilityIndex !== 1 || playerPokemon.fusionSpecies.ability2 + ? Math.pow(2, playerPokemon.fusionAbilityIndex) + : AbilityAttr.ABILITY_HIDDEN; + } + } else { + playerPokemon.abilityIndex = abilityIndex; + if ( + speciesStarters.hasOwnProperty(playerPokemon.species.speciesId) + ) { + scene.gameData.starterData[ + playerPokemon.species.speciesId + ].abilityAttr |= + abilityIndex !== 1 || playerPokemon.species.ability2 + ? Math.pow(2, playerPokemon.abilityIndex) + : AbilityAttr.ABILITY_HIDDEN; + } + } + + playerPokemon.getAbility(); + playerPokemon.calculateStats(); + scene.gameData.setPokemonCaught(playerPokemon, false); + + // Add pokemon and mods back + scene.getParty().push(playerPokemon); + for (const mod of modifiers.value) { + scene.addModifier(mod, true, false, false, true); + } + scene.updateModifiers(true); + }; + + setEncounterRewards( + scene, + { fillRemaining: true }, + null, + onBeforeRewardsPhase + ); + + return initBattleWithEnemyConfig(scene, config); + }) + .build() + ) + .build(); + +function getEnemyConfig( + scene: BattleScene, + playerPokemon: PlayerPokemon, + segments: number, + modifiers: ModifiersHolder +): EnemyPartyConfig { playerPokemon.resetSummonData(); // Passes modifiers by reference - modifiers.value = scene.findModifiers(m => m instanceof PokemonHeldItemModifier - && (m as PokemonHeldItemModifier).pokemonId === playerPokemon.id) as PokemonHeldItemModifier[]; - const modifierTypes = modifiers.value.map(mod => mod.type) as PokemonHeldItemModifierType[]; + modifiers.value = scene.findModifiers( + (m) => + m instanceof PokemonHeldItemModifier && + (m as PokemonHeldItemModifier).pokemonId === playerPokemon.id + ) as PokemonHeldItemModifier[]; + const modifierTypes = modifiers.value.map( + (mod) => mod.type + ) as PokemonHeldItemModifierType[]; const data = new PokemonData(playerPokemon); return { @@ -295,15 +439,14 @@ function getEnemyConfig(scene: BattleScene, playerPokemon: PlayerPokemon, segmen formIndex: playerPokemon.formIndex, level: playerPokemon.level, dataSource: data, - modifierTypes: modifierTypes - } - ] + modifierTypes: modifierTypes, + }, + ], }; } class ModifiersHolder { public value: PokemonHeldItemModifier[] = []; - constructor() { - } + constructor() {} } diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index fcd2db8cc52..9e4943f37a2 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -1,5 +1,3 @@ -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { TrainingSessionDialogue } from "#app/data/mystery-encounters/dialogue/training-session-dialogue"; import { TextStyle } from "#app/ui/text"; export class TextDisplay { @@ -77,5 +75,4 @@ export default class MysteryEncounterDialogue { export const allMysteryEncounterDialogue: { [encounterType: number]: MysteryEncounterDialogue } = {}; export function initMysteryEncounterDialogue() { - allMysteryEncounterDialogue[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionDialogue; } From 941a8e71d66848bc02ca06c1377146be1240380d Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 14:58:52 -0700 Subject: [PATCH 16/29] finishing touches --- .../mystery-encounter-dialogue.ts | 5 -- .../mystery-encounters/mystery-encounter.ts | 75 +++++++++++++++---- src/loading-scene.ts | 2 - src/overrides.ts | 4 +- src/test/vitest.setup.ts | 2 - 5 files changed, 61 insertions(+), 27 deletions(-) diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index 9e4943f37a2..d96aeb87ba6 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -71,8 +71,3 @@ export default class MysteryEncounterDialogue { } * */ - -export const allMysteryEncounterDialogue: { [encounterType: number]: MysteryEncounterDialogue } = {}; - -export function initMysteryEncounterDialogue() { -} diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 0780aa33ae4..c367f2377b2 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -7,7 +7,6 @@ import MysteryEncounterIntroVisuals, { MysteryEncounterSpriteConfig } from "../. import * as Utils from "../../utils"; import { StatusEffect } from "../status-effect"; import MysteryEncounterDialogue, { - allMysteryEncounterDialogue, OptionTextDisplay } from "./mystery-encounter-dialogue"; import MysteryEncounterOption, { MysteryEncounterOptionBuilder, OptionPhaseCallback } from "./mystery-encounter-option"; @@ -137,17 +136,12 @@ export default class MysteryEncounter implements MysteryEncounter { Object.assign(this, encounter); } this.encounterTier = this.encounterTier ? this.encounterTier : MysteryEncounterTier.COMMON; - this.dialogue = Object.assign((this.dialogue ?? {}), allMysteryEncounterDialogue[this.encounterType] ?? {}); + this.dialogue = {}; this.encounterVariant = MysteryEncounterVariant.DEFAULT; this.requirements = this.requirements ? this.requirements : []; this.hideBattleIntroMessage = !isNullOrUndefined(this.hideBattleIntroMessage) ? this.hideBattleIntroMessage : false; this.hideIntroVisuals = !isNullOrUndefined(this.hideIntroVisuals) ? this.hideIntroVisuals : true; - // Populate options with respective dialogue - if (this.dialogue?.encounterOptionsDialogue) { - // this.options.forEach((o, i) => o.dialogue = this.dialogue.encounterOptionsDialogue.options[i]); - } - // Reset any dirty flags or encounter data this.lockEncounterRewardTiers = true; this.dialogueTokens = new Map; @@ -482,15 +476,37 @@ export class MysteryEncounterBuilder implements Partial { return this.withSceneRequirement(new PartySizeRequirement([min, max ?? min])); } + /** + * Add a primary pokemon requirement + * + * @param requirement {@linkcode EncounterPokemonRequirement} + * @returns + */ withPrimaryPokemonRequirement(requirement: EncounterPokemonRequirement): this & Required> { this.primaryPokemonRequirements.push(requirement); return Object.assign(this, { primaryPokemonRequirements: this.primaryPokemonRequirements }); } + /** + * Add a primary pokemon status effect requirement + * + * @param statusEffect the status effect/s to check + * @param minNumberOfPokemon minimum number of pokemon to have the effect + * @param invertQuery if true will invert the query + * @returns + */ withPrimaryPokemonStatusEffectRequirement(statusEffect: StatusEffect | StatusEffect[], minNumberOfPokemon: number = 1, invertQuery: boolean = false): this & Required> { return this.withPrimaryPokemonRequirement(new StatusEffectRequirement(statusEffect, minNumberOfPokemon, invertQuery)); } + /** + * Add a primary pokemon health ratio requirement + * + * @param requiredHealthRange the health range to check + * @param minNumberOfPokemon minimum number of pokemon to have the health range + * @param invertQuery if true will invert the query + * @returns + */ withPrimaryPokemonHealthRatioRequirement(requiredHealthRange: [number, number], minNumberOfPokemon: number = 1, invertQuery: boolean = false): this & Required> { return this.withPrimaryPokemonRequirement(new HealthRatioRequirement(requiredHealthRange, minNumberOfPokemon, invertQuery)); } @@ -579,12 +595,17 @@ export class MysteryEncounterBuilder implements Partial { return Object.assign(this, { hideIntroVisuals: hideIntroVisuals }); } + /** + * Add a title for the encounter + * + * @param title - title of the encounter + * @returns + */ withTitle(title: TemplateStringsArray | `mysteryEncounter:${string}`) { - const dialogue = this.dialogue ?? {}; - const encounterOptionsDialogue = this.dialogue?.encounterOptionsDialogue ?? {}; + const encounterOptionsDialogue = this.dialogue.encounterOptionsDialogue ?? {}; this.dialogue = { - ...dialogue, + ...this.dialogue, encounterOptionsDialogue: { ...encounterOptionsDialogue, title, @@ -594,12 +615,17 @@ export class MysteryEncounterBuilder implements Partial { return this; } + /** + * Add a description of the encounter + * + * @param description - description of the encounter + * @returns + */ withDescription(description: TemplateStringsArray | `mysteryEncounter:${string}`) { - const dialogue = this.dialogue ?? {}; - const encounterOptionsDialogue = this.dialogue?.encounterOptionsDialogue ?? {}; + const encounterOptionsDialogue = this.dialogue.encounterOptionsDialogue ?? {}; this.dialogue = { - ...dialogue, + ...this.dialogue, encounterOptionsDialogue: { ...encounterOptionsDialogue, description, @@ -609,12 +635,17 @@ export class MysteryEncounterBuilder implements Partial { return this; } + /** + * Add a query for the encounter + * + * @param query - query to use for the encounter + * @returns + */ withQuery(query: TemplateStringsArray | `mysteryEncounter:${string}`) { - const dialogue = this.dialogue ?? {}; - const encounterOptionsDialogue = this.dialogue?.encounterOptionsDialogue ?? {}; + const encounterOptionsDialogue = this.dialogue.encounterOptionsDialogue ?? {}; this.dialogue = { - ...dialogue, + ...this.dialogue, encounterOptionsDialogue: { ...encounterOptionsDialogue, query, @@ -624,11 +655,23 @@ export class MysteryEncounterBuilder implements Partial { return this; } + /** + * Add outro dialogue/s for the encounter + * + * @param dialogue - outro dialogue/s + * @returns + */ withOutroDialogue(dialogue: MysteryEncounterDialogue["outro"] = []) { this.dialogue = {...this.dialogue, outro: dialogue }; return this; } + /** + * Builds the mystery encounter + * + * @param this - MysteryEncounter + * @returns + */ build(this: MysteryEncounter) { return new MysteryEncounter(this); } diff --git a/src/loading-scene.ts b/src/loading-scene.ts index 096986ebcc8..1ce8ed87156 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -22,7 +22,6 @@ import { initStatsKeys } from "./ui/game-stats-ui-handler"; import { initVouchers } from "./system/voucher"; import { Biome } from "#enums/biome"; import { TrainerType } from "#enums/trainer-type"; -import {initMysteryEncounterDialogue} from "#app/data/mystery-encounters/mystery-encounter-dialogue"; import {initMysteryEncounters} from "#app/data/mystery-encounters/mystery-encounters"; export class LoadingScene extends SceneBase { @@ -346,7 +345,6 @@ export class LoadingScene extends SceneBase { initMoves(); initAbilities(); initChallenges(); - initMysteryEncounterDialogue(); initMysteryEncounters(); } diff --git a/src/overrides.ts b/src/overrides.ts index c2a6edf9868..1e428c6dd2d 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -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 = 10000; +export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = null; export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null; -export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.DARK_DEAL; +export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null; /** * MODIFIER / ITEM OVERRIDES diff --git a/src/test/vitest.setup.ts b/src/test/vitest.setup.ts index b8b655e3e96..37c533a33db 100644 --- a/src/test/vitest.setup.ts +++ b/src/test/vitest.setup.ts @@ -13,7 +13,6 @@ import { initVouchers } from "#app/system/voucher"; import { initAchievements } from "#app/system/achv"; import { initStatsKeys } from "#app/ui/game-stats-ui-handler"; import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters"; -import { initMysteryEncounterDialogue } from "#app/data/mystery-encounters/mystery-encounter-dialogue"; import { beforeAll, beforeEach, vi } from "vitest"; import * as overrides from "#app/overrides"; @@ -28,7 +27,6 @@ initSpecies(); initMoves(); initAbilities(); initLoggedInUser(); -initMysteryEncounterDialogue(); initMysteryEncounters(); global.testFailed = false; From e8670c4f00d2dfe9ee020a2fb70bedcdf796ada1 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 15:01:30 -0700 Subject: [PATCH 17/29] rename MysteryEncounter interface to IMysteryEncounter --- src/battle-scene.ts | 16 +++---- src/battle.ts | 4 +- .../encounters/dark-deal.ts | 4 +- .../encounters/department-store-sale.ts | 4 +- .../encounters/field-trip-encounter.ts | 4 +- .../encounters/fight-or-flight.ts | 4 +- .../encounters/mysterious-challengers.ts | 4 +- .../encounters/mysterious-chest.ts | 4 +- .../encounters/shady-vitamin-dealer.ts | 4 +- .../encounters/sleeping-snorlax.ts | 4 +- .../encounters/training-session.ts | 4 +- .../mystery-encounters/mystery-encounter.ts | 44 +++++++++---------- .../mystery-encounters/mystery-encounters.ts | 4 +- src/field/mystery-encounter-intro.ts | 6 +-- src/system/game-data.ts | 6 +-- .../mystery-encounter-utils.test.ts | 12 ++--- 16 files changed, 64 insertions(+), 64 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index be0e83b7976..729cc826886 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -67,7 +67,7 @@ import { Species } from "#enums/species"; import { UiTheme } from "#enums/ui-theme"; import { TimedEventManager } from "#app/timed-event-manager.js"; import i18next from "i18next"; -import MysteryEncounter, { MysteryEncounterTier, MysteryEncounterVariant } from "./data/mystery-encounters/mystery-encounter"; +import IMysteryEncounter, { MysteryEncounterTier, MysteryEncounterVariant } from "./data/mystery-encounters/mystery-encounter"; import { mysteryEncountersByBiome, allMysteryEncounters, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters"; import { MysteryEncounterData } from "#app/data/mystery-encounters/mystery-encounter-data"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; @@ -216,7 +216,7 @@ export default class BattleScene extends SceneBase { public pokemonInfoContainer: PokemonInfoContainer; private party: PlayerPokemon[]; public mysteryEncounterData: MysteryEncounterData = new MysteryEncounterData(null); - public lastMysteryEncounter: MysteryEncounter; + public lastMysteryEncounter: IMysteryEncounter; /** Combined Biome and Wave count text */ private biomeWaveText: Phaser.GameObjects.Text; private moneyText: Phaser.GameObjects.Text; @@ -1026,7 +1026,7 @@ export default class BattleScene extends SceneBase { } } - newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean, mysteryEncounter?: MysteryEncounter): Battle { + newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean, mysteryEncounter?: IMysteryEncounter): Battle { const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave; const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1); let newDouble: boolean; @@ -2621,9 +2621,9 @@ export default class BattleScene extends SceneBase { * @param override - used to load session encounter when restarting game, etc. * @returns */ - getMysteryEncounter(override: MysteryEncounter): MysteryEncounter { + getMysteryEncounter(override: IMysteryEncounter): IMysteryEncounter { // Loading override or session encounter - let encounter: MysteryEncounter; + let encounter: IMysteryEncounter; if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)) { encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE]; } else { @@ -2645,7 +2645,7 @@ export default class BattleScene extends SceneBase { } if (encounter) { - encounter = new MysteryEncounter(encounter); + encounter = new IMysteryEncounter(encounter); encounter.populateDialogueTokensFromRequirements(this); return encounter; } @@ -2674,7 +2674,7 @@ export default class BattleScene extends SceneBase { tier = Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE; } - let availableEncounters: MysteryEncounter[] = []; + 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); @@ -2695,7 +2695,7 @@ export default class BattleScene extends SceneBase { } encounter = availableEncounters[Utils.randSeedInt(availableEncounters.length)]; // New encounter object to not dirty flags - encounter = new MysteryEncounter(encounter); + encounter = new IMysteryEncounter(encounter); encounter.populateDialogueTokensFromRequirements(this); return encounter; } diff --git a/src/battle.ts b/src/battle.ts index 30543933f49..ab47eac7993 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -14,7 +14,7 @@ import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; import i18next from "#app/plugins/i18n"; -import MysteryEncounter, { MysteryEncounterVariant } from "./data/mystery-encounters/mystery-encounter"; +import IMysteryEncounter, { MysteryEncounterVariant } from "./data/mystery-encounters/mystery-encounter"; export enum BattleType { WILD, @@ -69,7 +69,7 @@ export default class Battle { public lastUsedPokeball: PokeballType; public playerFaints: number; // The amount of times pokemon on the players side have fainted public enemyFaints: number; // The amount of times pokemon on the enemies side have fainted - public mysteryEncounter: MysteryEncounter; + public mysteryEncounter: IMysteryEncounter; private rngCounter: integer = 0; diff --git a/src/data/mystery-encounters/encounters/dark-deal.ts b/src/data/mystery-encounters/encounters/dark-deal.ts index 8f13e373e9a..9d755ad9493 100644 --- a/src/data/mystery-encounters/encounters/dark-deal.ts +++ b/src/data/mystery-encounters/encounters/dark-deal.ts @@ -7,7 +7,7 @@ import BattleScene from "../../../battle-scene"; import { AddPokeballModifierType } from "../../../modifier/modifier-type"; import { PokeballType } from "../../pokeball"; import { getPokemonSpecies } from "../../pokemon-species"; -import MysteryEncounter, { +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; @@ -73,7 +73,7 @@ const excludedBosses = [ Species.PECHARUNT, ]; -export const DarkDealEncounter: MysteryEncounter = +export const DarkDealEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DARK_DEAL) .withEncounterTier(MysteryEncounterTier.ROGUE) .withIntroSpriteConfigs([ diff --git a/src/data/mystery-encounters/encounters/department-store-sale.ts b/src/data/mystery-encounters/encounters/department-store-sale.ts index 2dcee4455df..d821a952864 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale.ts @@ -7,7 +7,7 @@ import { randSeedInt } from "#app/utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import BattleScene from "../../../battle-scene"; -import MysteryEncounter, { +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; @@ -15,7 +15,7 @@ import MysteryEncounter, { /** i18n namespace for encounter */ const namespace = "mysteryEncounter:department_store_sale"; -export const DepartmentStoreSaleEncounter: MysteryEncounter = +export const DepartmentStoreSaleEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.DEPARTMENT_STORE_SALE ) diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index 8cb6fe65048..9235504e332 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -13,7 +13,7 @@ import { modifierTypes } from "#app/modifier/modifier-type"; import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; -import MysteryEncounter, { +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; @@ -21,7 +21,7 @@ import MysteryEncounter, { /** i18n namespace for the encounter */ const namespace = "mysteryEncounter:field_trip"; -export const FieldTripEncounter: MysteryEncounter = +export const FieldTripEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP) .withEncounterTier(MysteryEncounterTier.COMMON) .withSceneWaveRangeRequirement(10, 180) diff --git a/src/data/mystery-encounters/encounters/fight-or-flight.ts b/src/data/mystery-encounters/encounters/fight-or-flight.ts index 3080ab76810..c98d7cd485f 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight.ts @@ -23,7 +23,7 @@ import { randSeedInt } from "#app/utils"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; -import MysteryEncounter, { +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; @@ -32,7 +32,7 @@ import { MoveRequirement } from "../mystery-encounter-requirements"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:fight_or_flight"; -export const FightOrFlightEncounter: MysteryEncounter = +export const FightOrFlightEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.FIGHT_OR_FLIGHT ) diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers.ts b/src/data/mystery-encounters/encounters/mysterious-challengers.ts index e8efc3470ed..1276d6e2226 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers.ts @@ -15,7 +15,7 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PartyMemberStrength } from "#enums/party-member-strength"; import BattleScene from "../../../battle-scene"; import * as Utils from "../../../utils"; -import MysteryEncounter, { +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; @@ -23,7 +23,7 @@ import MysteryEncounter, { /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:mysterious_challengers"; -export const MysteriousChallengersEncounter: MysteryEncounter = +export const MysteriousChallengersEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.MYSTERIOUS_CHALLENGERS ) diff --git a/src/data/mystery-encounters/encounters/mysterious-chest.ts b/src/data/mystery-encounters/encounters/mysterious-chest.ts index 9dd4f56f623..8d1f1c2eeb1 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest.ts @@ -11,13 +11,13 @@ import { GameOverPhase } from "#app/phases"; import { randSeedInt } from "#app/utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; -import MysteryEncounter, { +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -export const MysteriousChestEncounter: MysteryEncounter = +export const MysteriousChestEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.MYSTERIOUS_CHEST ) diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts index 773c4b97b0e..f26890cefbc 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts @@ -14,14 +14,14 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import i18next from "i18next"; import BattleScene from "../../../battle-scene"; -import MysteryEncounter, { +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { MoneyRequirement } from "../mystery-encounter-requirements"; -export const ShadyVitaminDealerEncounter: MysteryEncounter = +export const ShadyVitaminDealerEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.SHADY_VITAMIN_DEALER ) diff --git a/src/data/mystery-encounters/encounters/sleeping-snorlax.ts b/src/data/mystery-encounters/encounters/sleeping-snorlax.ts index 324beb35e99..b0130c34419 100644 --- a/src/data/mystery-encounters/encounters/sleeping-snorlax.ts +++ b/src/data/mystery-encounters/encounters/sleeping-snorlax.ts @@ -7,7 +7,7 @@ import BattleScene from "../../../battle-scene"; import * as Utils from "../../../utils"; import { getPokemonSpecies } from "../../pokemon-species"; import { Status, StatusEffect } from "../../status-effect"; -import MysteryEncounter, { +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; @@ -27,7 +27,7 @@ import { /** i18n namespace for the encounter */ const namespace = "mysteryEncounter:sleeping_snorlax"; -export const SleepingSnorlaxEncounter: MysteryEncounter = +export const SleepingSnorlaxEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.SLEEPING_SNORLAX ) diff --git a/src/data/mystery-encounters/encounters/training-session.ts b/src/data/mystery-encounters/encounters/training-session.ts index e9b0461857d..4338f587583 100644 --- a/src/data/mystery-encounters/encounters/training-session.ts +++ b/src/data/mystery-encounters/encounters/training-session.ts @@ -20,13 +20,13 @@ import { randSeedShuffle } from "#app/utils"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; -import MysteryEncounter, { +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -export const TrainingSessionEncounter: MysteryEncounter = +export const TrainingSessionEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.TRAINING_SESSION ) diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index c367f2377b2..30e2e6682a6 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -35,7 +35,7 @@ export enum MysteryEncounterTier { MASTER // Not currently used } -export default interface MysteryEncounter { +export default interface IMysteryEncounter { /** * Required params */ @@ -130,8 +130,8 @@ export default interface MysteryEncounter { * These objects will be saved as part of session data any time the player is on a floor with an encounter * Unless you know what you're doing, you should use MysteryEncounterBuilder to create an instance for this class */ -export default class MysteryEncounter implements MysteryEncounter { - constructor(encounter: MysteryEncounter) { +export default class IMysteryEncounter implements IMysteryEncounter { + constructor(encounter: IMysteryEncounter) { if (!isNullOrUndefined(encounter)) { Object.assign(this, encounter); } @@ -339,7 +339,7 @@ export default class MysteryEncounter implements MysteryEncounter { } } -export class MysteryEncounterBuilder implements Partial { +export class MysteryEncounterBuilder implements Partial { encounterType?: MysteryEncounterType; options?: [MysteryEncounterOption, MysteryEncounterOption, ...MysteryEncounterOption[]] = [null, null]; spriteConfigs?: MysteryEncounterSpriteConfig[]; @@ -367,7 +367,7 @@ export class MysteryEncounterBuilder implements Partial { * @param encounterType * @returns this */ - static withEncounterType(encounterType: MysteryEncounterType): MysteryEncounterBuilder & Pick { + static withEncounterType(encounterType: MysteryEncounterType): MysteryEncounterBuilder & Pick { return Object.assign(new MysteryEncounterBuilder(), { encounterType: encounterType }); } @@ -377,7 +377,7 @@ export class MysteryEncounterBuilder implements Partial { * @param option - MysteryEncounterOption to add, can use MysteryEncounterOptionBuilder to create instance * @returns */ - withOption(option: MysteryEncounterOption): this & Pick { + withOption(option: MysteryEncounterOption): this & Pick { if (this.options[0] === null) { return Object.assign(this, { options: [option, this.options[0]] }); } else if (this.options[1] === null) { @@ -406,7 +406,7 @@ export class MysteryEncounterBuilder implements Partial { * @param spriteConfigs * @returns */ - withIntroSpriteConfigs(spriteConfigs: MysteryEncounterSpriteConfig[]): this & Pick { + withIntroSpriteConfigs(spriteConfigs: MysteryEncounterSpriteConfig[]): this & Pick { return Object.assign(this, { spriteConfigs: spriteConfigs }); } @@ -435,7 +435,7 @@ export class MysteryEncounterBuilder implements Partial { * @param encounterTier * @returns */ - withEncounterTier(encounterTier: MysteryEncounterTier): this & Required> { + withEncounterTier(encounterTier: MysteryEncounterTier): this & Required> { return Object.assign(this, { encounterTier: encounterTier }); } @@ -446,7 +446,7 @@ export class MysteryEncounterBuilder implements Partial { * @param requirement * @returns */ - withSceneRequirement(requirement: EncounterSceneRequirement): this & Required> { + withSceneRequirement(requirement: EncounterSceneRequirement): this & Required> { if (requirement instanceof EncounterPokemonRequirement) { Error("Incorrectly added pokemon requirement as scene requirement."); } @@ -482,7 +482,7 @@ export class MysteryEncounterBuilder implements Partial { * @param requirement {@linkcode EncounterPokemonRequirement} * @returns */ - withPrimaryPokemonRequirement(requirement: EncounterPokemonRequirement): this & Required> { + withPrimaryPokemonRequirement(requirement: EncounterPokemonRequirement): this & Required> { this.primaryPokemonRequirements.push(requirement); return Object.assign(this, { primaryPokemonRequirements: this.primaryPokemonRequirements }); } @@ -495,7 +495,7 @@ export class MysteryEncounterBuilder implements Partial { * @param invertQuery if true will invert the query * @returns */ - withPrimaryPokemonStatusEffectRequirement(statusEffect: StatusEffect | StatusEffect[], minNumberOfPokemon: number = 1, invertQuery: boolean = false): this & Required> { + withPrimaryPokemonStatusEffectRequirement(statusEffect: StatusEffect | StatusEffect[], minNumberOfPokemon: number = 1, invertQuery: boolean = false): this & Required> { return this.withPrimaryPokemonRequirement(new StatusEffectRequirement(statusEffect, minNumberOfPokemon, invertQuery)); } @@ -507,14 +507,14 @@ export class MysteryEncounterBuilder implements Partial { * @param invertQuery if true will invert the query * @returns */ - withPrimaryPokemonHealthRatioRequirement(requiredHealthRange: [number, number], minNumberOfPokemon: number = 1, invertQuery: boolean = false): this & Required> { + withPrimaryPokemonHealthRatioRequirement(requiredHealthRange: [number, number], minNumberOfPokemon: number = 1, invertQuery: boolean = false): this & Required> { return this.withPrimaryPokemonRequirement(new HealthRatioRequirement(requiredHealthRange, minNumberOfPokemon, invertQuery)); } // TODO: Maybe add an optional parameter for excluding primary pokemon from the support cast? // ex. if your only grass type pokemon, a snivy, is chosen as primary, if the support pokemon requires a grass type, the event won't trigger because // it's already been - withSecondaryPokemonRequirement(requirement: EncounterPokemonRequirement, excludePrimaryFromSecondaryRequirements: boolean = false): this & Required> { + withSecondaryPokemonRequirement(requirement: EncounterPokemonRequirement, excludePrimaryFromSecondaryRequirements: boolean = false): this & Required> { this.secondaryPokemonRequirements.push(requirement); this.excludePrimaryFromSupportRequirements = excludePrimaryFromSecondaryRequirements; return Object.assign(this, { excludePrimaryFromSecondaryRequirements: this.excludePrimaryFromSupportRequirements, secondaryPokemonRequirements: this.secondaryPokemonRequirements }); @@ -530,7 +530,7 @@ export class MysteryEncounterBuilder implements Partial { * @param doEncounterRewards - synchronous callback function to perform during rewards phase of the encounter * @returns */ - withRewards(doEncounterRewards: (scene: BattleScene) => boolean): this & Required> { + withRewards(doEncounterRewards: (scene: BattleScene) => boolean): this & Required> { return Object.assign(this, { doEncounterRewards: doEncounterRewards }); } @@ -544,7 +544,7 @@ export class MysteryEncounterBuilder implements Partial { * @param doEncounterExp - synchronous callback function to perform during rewards phase of the encounter * @returns */ - withExp(doEncounterExp: (scene: BattleScene) => boolean): this & Required> { + withExp(doEncounterExp: (scene: BattleScene) => boolean): this & Required> { return Object.assign(this, { doEncounterExp: doEncounterExp }); } @@ -555,7 +555,7 @@ export class MysteryEncounterBuilder implements Partial { * @param onInit - synchronous callback function to perform as soon as the encounter is selected for the next phase * @returns */ - withOnInit(onInit: (scene: BattleScene) => boolean): this & Required> { + withOnInit(onInit: (scene: BattleScene) => boolean): this & Required> { return Object.assign(this, { onInit: onInit }); } @@ -564,7 +564,7 @@ export class MysteryEncounterBuilder implements Partial { * @param enemyPartyConfig * @returns */ - withEnemyPartyConfig(enemyPartyConfig: EnemyPartyConfig): this & Required> { + withEnemyPartyConfig(enemyPartyConfig: EnemyPartyConfig): this & Required> { this.enemyPartyConfigs.push(enemyPartyConfig); return Object.assign(this, { enemyPartyConfigs: this.enemyPartyConfigs }); } @@ -575,7 +575,7 @@ export class MysteryEncounterBuilder implements Partial { * @param catchAllowed - if true, allows enemy pokemon to be caught during the encounter * @returns */ - withCatchAllowed(catchAllowed: boolean): this & Required> { + withCatchAllowed(catchAllowed: boolean): this & Required> { return Object.assign(this, { catchAllowed: catchAllowed }); } @@ -583,7 +583,7 @@ export class MysteryEncounterBuilder implements Partial { * @param hideBattleIntroMessage - if true, will not show the trainerAppeared/wildAppeared/bossAppeared message for an encounter * @returns */ - withHideWildIntroMessage(hideBattleIntroMessage: boolean): this & Required> { + withHideWildIntroMessage(hideBattleIntroMessage: boolean): this & Required> { return Object.assign(this, { hideBattleIntroMessage: hideBattleIntroMessage }); } @@ -591,7 +591,7 @@ export class MysteryEncounterBuilder implements Partial { * @param hideIntroVisuals - if false, will not hide the intro visuals that are displayed at the beginning of encounter * @returns */ - withHideIntroVisuals(hideIntroVisuals: boolean): this & Required> { + withHideIntroVisuals(hideIntroVisuals: boolean): this & Required> { return Object.assign(this, { hideIntroVisuals: hideIntroVisuals }); } @@ -672,7 +672,7 @@ export class MysteryEncounterBuilder implements Partial { * @param this - MysteryEncounter * @returns */ - build(this: MysteryEncounter) { - return new MysteryEncounter(this); + build(this: IMysteryEncounter) { + return new IMysteryEncounter(this); } } diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index 1651430e59d..602a01627c6 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -1,4 +1,4 @@ -import MysteryEncounter from "./mystery-encounter"; +import IMysteryEncounter from "./mystery-encounter"; import { DarkDealEncounter } from "./encounters/dark-deal"; import { MysteriousChallengersEncounter } from "./encounters/mysterious-challengers"; import { MysteriousChestEncounter } from "./encounters/mysterious-chest"; @@ -117,7 +117,7 @@ export const CIVILIZATION_ENCOUNTER_BIOMES = [ Biome.ISLAND ]; -export const allMysteryEncounters: { [encounterType: number]: MysteryEncounter } = {}; +export const allMysteryEncounters: { [encounterType: number]: IMysteryEncounter } = {}; const extremeBiomeEncounters: MysteryEncounterType[] = []; diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index adb700b4854..1b0fb3bca01 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -1,6 +1,6 @@ import { GameObjects } from "phaser"; import BattleScene from "../battle-scene"; -import MysteryEncounter from "../data/mystery-encounters/mystery-encounter"; +import IMysteryEncounter from "../data/mystery-encounters/mystery-encounter"; export class MysteryEncounterSpriteConfig { spriteKey: string; // e.g. "ace_trainer_f" @@ -21,10 +21,10 @@ export class MysteryEncounterSpriteConfig { * Note: intro visuals are not "Trainers" or any other specific game object, though they may contain trainer sprites */ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Container { - public encounter: MysteryEncounter; + public encounter: IMysteryEncounter; public spriteConfigs: MysteryEncounterSpriteConfig[]; - constructor(scene: BattleScene, encounter: MysteryEncounter) { + constructor(scene: BattleScene, encounter: IMysteryEncounter) { super(scene, -72, 76); this.encounter = encounter; // Shallow copy configs to allow visual config updates at runtime without dirtying master copy of Encounter diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 871e2458bbd..606d63d70fe 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -41,7 +41,7 @@ import { Moves } from "#enums/moves"; import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; import { MysteryEncounterData } from "../data/mystery-encounters/mystery-encounter-data"; -import MysteryEncounter from "../data/mystery-encounters/mystery-encounter"; +import IMysteryEncounter from "../data/mystery-encounters/mystery-encounter"; export const defaultStarterSpecies: Species[] = [ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, @@ -124,7 +124,7 @@ export interface SessionSaveData { gameVersion: string; timestamp: integer; challenges: ChallengeData[]; - mysteryEncounter: MysteryEncounter; + mysteryEncounter: IMysteryEncounter; mysteryEncounterData: MysteryEncounterData; } @@ -1155,7 +1155,7 @@ export class GameData { } if (k === "mysteryEncounter") { - return new MysteryEncounter(v); + return new IMysteryEncounter(v); } if (k === "mysteryEncounterData") { diff --git a/src/test/mystery-encounter/mystery-encounter-utils.test.ts b/src/test/mystery-encounter/mystery-encounter-utils.test.ts index e558fe7a95c..50853c05a2e 100644 --- a/src/test/mystery-encounter/mystery-encounter-utils.test.ts +++ b/src/test/mystery-encounter/mystery-encounter-utils.test.ts @@ -10,7 +10,7 @@ import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils"; import { Species } from "#enums/species"; import BattleScene from "#app/battle-scene"; import { StatusEffect } from "#app/data/status-effect"; -import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; +import IMysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import { MessagePhase } from "#app/phases"; import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species"; import { Type } from "#app/data/type"; @@ -273,7 +273,7 @@ describe("Mystery Encounter Utils", () => { describe("getTextWithEncounterDialogueTokens", () => { it("injects dialogue tokens and color styling", () => { - scene.currentBattle.mysteryEncounter = new MysteryEncounter(null); + scene.currentBattle.mysteryEncounter = new IMysteryEncounter(null); scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value"); const result = getEncounterText(scene, "mysteryEncounter:unit_test_dialogue"); @@ -281,7 +281,7 @@ describe("Mystery Encounter Utils", () => { }); it("can perform nested dialogue token injection", () => { - scene.currentBattle.mysteryEncounter = new MysteryEncounter(null); + scene.currentBattle.mysteryEncounter = new IMysteryEncounter(null); scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value"); scene.currentBattle.mysteryEncounter.setDialogueToken("testvalue", "new"); @@ -292,7 +292,7 @@ describe("Mystery Encounter Utils", () => { describe("queueEncounterMessage", () => { it("queues a message with encounter dialogue tokens", async () => { - scene.currentBattle.mysteryEncounter = new MysteryEncounter(null); + scene.currentBattle.mysteryEncounter = new IMysteryEncounter(null); scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value"); const spy = vi.spyOn(game.scene, "queueMessage"); const phaseSpy = vi.spyOn(game.scene, "unshiftPhase"); @@ -305,7 +305,7 @@ describe("Mystery Encounter Utils", () => { describe("showEncounterText", () => { it("showText with dialogue tokens", async () => { - scene.currentBattle.mysteryEncounter = new MysteryEncounter(null); + scene.currentBattle.mysteryEncounter = new IMysteryEncounter(null); scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value"); const spy = vi.spyOn(game.scene.ui, "showText"); @@ -316,7 +316,7 @@ describe("Mystery Encounter Utils", () => { describe("showEncounterDialogue", () => { it("showText with dialogue tokens", async () => { - scene.currentBattle.mysteryEncounter = new MysteryEncounter(null); + scene.currentBattle.mysteryEncounter = new IMysteryEncounter(null); scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value"); const spy = vi.spyOn(game.scene.ui, "showDialogue"); From 55805d180470f757118c5f6097665f466caef6ca Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 15:13:01 -0700 Subject: [PATCH 18/29] apply `namespace` for shady vitamin dealer encounter --- .../encounters/shady-vitamin-dealer.ts | 71 +++++++------------ 1 file changed, 25 insertions(+), 46 deletions(-) diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts index f26890cefbc..14dddf91189 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts @@ -21,6 +21,9 @@ import IMysteryEncounter, { import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { MoneyRequirement } from "../mystery-encounter-requirements"; +/** the i18n namespace for this encounter */ +const namespace = "mysteryEncounter:shady_vitamin_dealer"; + export const ShadyVitaminDealerEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.SHADY_VITAMIN_DEALER @@ -48,26 +51,25 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = ]) .withIntroDialogue([ { - text: "mysteryEncounter:shady_vitamin_dealer_intro_message", + text: `${namespace}_intro_message`, }, { - text: "mysteryEncounter:shady_vitamin_dealer_intro_dialogue", - speaker: "mysteryEncounter:shady_vitamin_dealer_speaker", + text: `${namespace}_intro_dialogue`, + speaker: `${namespace}_speaker`, }, ]) - .withTitle("mysteryEncounter:shady_vitamin_dealer_title") - .withDescription("mysteryEncounter:shady_vitamin_dealer_description") - .withQuery("mysteryEncounter:shady_vitamin_dealer_query") + .withTitle(`${namespace}_title`) + .withDescription(`${namespace}_description`) + .withQuery(`${namespace}_query`) .withOption( new MysteryEncounterOptionBuilder() .withSceneMoneyRequirement(0, 2) // Wave scaling money multiplier of 2 .withDialogue({ - buttonLabel: "mysteryEncounter:shady_vitamin_dealer_option_1_label", - buttonTooltip: - "mysteryEncounter:shady_vitamin_dealer_option_1_tooltip", + buttonLabel: `${namespace}_option_1_label`, + buttonTooltip: `${namespace}_option_1_tooltip`, selected: [ { - text: "mysteryEncounter:shady_vitamin_dealer_option_selected", + text: `${namespace}_option_selected`, }, ], }) @@ -103,9 +105,7 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = pokemon ); if (!meetsReqs) { - return i18next.t( - "mysteryEncounter:shady_vitamin_dealer_invalid_selection" - ); + return i18next.t(`${namespace}_invalid_selection`); } return null; @@ -145,22 +145,13 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = if (randSeedInt(10) < 8) { if (chosenPokemon.trySetStatus(StatusEffect.TOXIC)) { // Toxic applied - queueEncounterMessage( - scene, - "mysteryEncounter:shady_vitamin_dealer_bad_poison" - ); + queueEncounterMessage(scene, `${namespace}_bad_poison`); } else { // Pokemon immune or something else prevents status - queueEncounterMessage( - scene, - "mysteryEncounter:shady_vitamin_dealer_damage_only" - ); + queueEncounterMessage(scene, `${namespace}_damage_only`); } } else { - queueEncounterMessage( - scene, - "mysteryEncounter:shady_vitamin_dealer_damage_only" - ); + queueEncounterMessage(scene, `${namespace}_damage_only`); } setEncounterExp(scene, [chosenPokemon.id], 100); @@ -172,12 +163,11 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = .withOption( new MysteryEncounterOptionBuilder() .withDialogue({ - buttonLabel: "mysteryEncounter:shady_vitamin_dealer_option_2_label", - buttonTooltip: - "mysteryEncounter:shady_vitamin_dealer_option_2_tooltip", + buttonLabel: `${namespace}_option_2_label`, + buttonTooltip: `${namespace}_option_2_tooltip`, selected: [ { - text: "mysteryEncounter:shady_vitamin_dealer_option_selected", + text: `${namespace}_option_selected`, }, ], }) @@ -214,9 +204,7 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = pokemon ); if (!meetsReqs) { - return i18next.t( - "mysteryEncounter:shady_vitamin_dealer_invalid_selection" - ); + return i18next.t(`${namespace}_invalid_selection`); } return null; @@ -252,22 +240,13 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = if (randSeedInt(10) < 2) { if (chosenPokemon.trySetStatus(StatusEffect.POISON)) { // Poison applied - queueEncounterMessage( - scene, - "mysteryEncounter:shady_vitamin_dealer_poison" - ); + queueEncounterMessage(scene, `${namespace}_poison`); } else { // Pokemon immune or something else prevents status - queueEncounterMessage( - scene, - "mysteryEncounter:shady_vitamin_dealer_no_bad_effects" - ); + queueEncounterMessage(scene, `${namespace}_no_bad_effects`); } } else { - queueEncounterMessage( - scene, - "mysteryEncounter:shady_vitamin_dealer_no_bad_effects" - ); + queueEncounterMessage(scene, `${namespace}_no_bad_effects`); } setEncounterExp(scene, [chosenPokemon.id], 100); @@ -278,8 +257,8 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = ) .withSimpleOption( { - buttonLabel: "mysteryEncounter:shady_vitamin_dealer_option_3_label", - buttonTooltip: "mysteryEncounter:shady_vitamin_dealer_option_3_tooltip", + buttonLabel: `${namespace}_option_3_label`, + buttonTooltip: `${namespace}_option_3_tooltip`, }, async (scene: BattleScene) => { // Leave encounter with no rewards or exp From 4da5e0fb107011affe5772bb3b8ecbdba70d55d3 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 15:14:03 -0700 Subject: [PATCH 19/29] apply `namespace` for training session encounter --- .../encounters/training-session.ts | 52 ++++++++----------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/src/data/mystery-encounters/encounters/training-session.ts b/src/data/mystery-encounters/encounters/training-session.ts index 4338f587583..c5f0ee2e46f 100644 --- a/src/data/mystery-encounters/encounters/training-session.ts +++ b/src/data/mystery-encounters/encounters/training-session.ts @@ -6,7 +6,7 @@ import { selectPokemonForOption, setEncounterRewards, } from "#app/data/mystery-encounters/mystery-encounter-utils"; -import { getNatureName, Nature } from "#app/data/nature"; +import { Nature, getNatureName } from "#app/data/nature"; import { speciesStarters } from "#app/data/pokemon-species"; import { Stat } from "#app/data/pokemon-stat"; import { PlayerPokemon } from "#app/field/pokemon"; @@ -26,6 +26,9 @@ import IMysteryEncounter, { } from "../mystery-encounter"; import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +/** The i18n namespace for the encounter */ +const namespace = "mysteryEncounter:training_session"; + export const TrainingSessionEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.TRAINING_SESSION @@ -43,20 +46,20 @@ export const TrainingSessionEncounter: IMysteryEncounter = ]) .withIntroDialogue([ { - text: "mysteryEncounter:training_session_intro_message", + text: `${namespace}_intro_message`, }, ]) - .withTitle("mysteryEncounter:training_session_title") - .withDescription("mysteryEncounter:training_session_description") - .withQuery("mysteryEncounter:training_session_query") + .withTitle(`${namespace}_title`) + .withDescription(`${namespace}_description`) + .withQuery(`${namespace}_query`) .withOption( new MysteryEncounterOptionBuilder() .withDialogue({ - buttonLabel: "mysteryEncounter:training_session_option_1_label", - buttonTooltip: "mysteryEncounter:training_session_option_1_tooltip", + buttonLabel: `${namespace}_option_1_label`, + buttonTooltip: `${namespace}_option_1_tooltip`, selected: [ { - text: "mysteryEncounter:training_session_option_selected_message", + text: `${namespace}_option_selected_message`, }, ], }) @@ -168,10 +171,7 @@ export const TrainingSessionEncounter: IMysteryEncounter = } scene.updateModifiers(true); scene.queueMessage( - getEncounterText( - scene, - "mysteryEncounter:training_session_battle_finished_1" - ), + getEncounterText(scene, `${namespace}_battle_finished_1`), null, true ); @@ -191,13 +191,12 @@ export const TrainingSessionEncounter: IMysteryEncounter = .withOption( new MysteryEncounterOptionBuilder() .withDialogue({ - buttonLabel: "mysteryEncounter:training_session_option_2_label", - buttonTooltip: "mysteryEncounter:training_session_option_2_tooltip", - secondOptionPrompt: - "mysteryEncounter:training_session_option_2_select_prompt", + buttonLabel: `${namespace}_option_2_label`, + buttonTooltip: `${namespace}_option_2_tooltip`, + secondOptionPrompt: `${namespace}_option_2_select_prompt`, selected: [ { - text: "mysteryEncounter:training_session_option_selected_message", + text: `${namespace}_option_selected_message`, }, ], }) @@ -247,10 +246,7 @@ export const TrainingSessionEncounter: IMysteryEncounter = const onBeforeRewardsPhase = () => { scene.queueMessage( - getEncounterText( - scene, - "mysteryEncounter:training_session_battle_finished_2" - ), + getEncounterText(scene, `${namespace}_battle_finished_2`), null, true ); @@ -280,13 +276,12 @@ export const TrainingSessionEncounter: IMysteryEncounter = .withOption( new MysteryEncounterOptionBuilder() .withDialogue({ - buttonLabel: "mysteryEncounter:training_session_option_3_label", - buttonTooltip: "mysteryEncounter:training_session_option_3_tooltip", - secondOptionPrompt: - "mysteryEncounter:training_session_option_3_select_prompt", + buttonLabel: `${namespace}_option_3_label`, + buttonTooltip: `${namespace}_option_3_tooltip`, + secondOptionPrompt: `${namespace}_option_3_select_prompt`, selected: [ { - text: "mysteryEncounter:training_session_option_selected_message", + text: `${namespace}_option_selected_message`, }, ], }) @@ -349,10 +344,7 @@ export const TrainingSessionEncounter: IMysteryEncounter = const onBeforeRewardsPhase = () => { scene.queueMessage( - getEncounterText( - scene, - "mysteryEncounter:training_session_battle_finished_3" - ), + getEncounterText(scene, `${namespace}_battle_finished_3`), null, true ); From 14289d27362bd75356af346fdfad628500117c69 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 15:17:37 -0700 Subject: [PATCH 20/29] add `-encounter` as suffix for encounter files --- .../{dark-deal.ts => dark-deal-encounter.ts} | 0 ...e.ts => department-store-sale-encounter.ts} | 0 ...-flight.ts => fight-or-flight-encounter.ts} | 0 ....ts => mysterious-challengers-encounter.ts} | 0 ...-chest.ts => mysterious-chest-encounter.ts} | 0 ...er.ts => shady-vitamin-dealer-encounter.ts} | 0 ...norlax.ts => sleeping-snorlax-encounter.ts} | 0 ...ession.ts => training-session-encounter.ts} | 0 .../mystery-encounters/mystery-encounters.ts | 18 +++++++++--------- 9 files changed, 9 insertions(+), 9 deletions(-) rename src/data/mystery-encounters/encounters/{dark-deal.ts => dark-deal-encounter.ts} (100%) rename src/data/mystery-encounters/encounters/{department-store-sale.ts => department-store-sale-encounter.ts} (100%) rename src/data/mystery-encounters/encounters/{fight-or-flight.ts => fight-or-flight-encounter.ts} (100%) rename src/data/mystery-encounters/encounters/{mysterious-challengers.ts => mysterious-challengers-encounter.ts} (100%) rename src/data/mystery-encounters/encounters/{mysterious-chest.ts => mysterious-chest-encounter.ts} (100%) rename src/data/mystery-encounters/encounters/{shady-vitamin-dealer.ts => shady-vitamin-dealer-encounter.ts} (100%) rename src/data/mystery-encounters/encounters/{sleeping-snorlax.ts => sleeping-snorlax-encounter.ts} (100%) rename src/data/mystery-encounters/encounters/{training-session.ts => training-session-encounter.ts} (100%) diff --git a/src/data/mystery-encounters/encounters/dark-deal.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts similarity index 100% rename from src/data/mystery-encounters/encounters/dark-deal.ts rename to src/data/mystery-encounters/encounters/dark-deal-encounter.ts diff --git a/src/data/mystery-encounters/encounters/department-store-sale.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts similarity index 100% rename from src/data/mystery-encounters/encounters/department-store-sale.ts rename to src/data/mystery-encounters/encounters/department-store-sale-encounter.ts diff --git a/src/data/mystery-encounters/encounters/fight-or-flight.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts similarity index 100% rename from src/data/mystery-encounters/encounters/fight-or-flight.ts rename to src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts similarity index 100% rename from src/data/mystery-encounters/encounters/mysterious-challengers.ts rename to src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts diff --git a/src/data/mystery-encounters/encounters/mysterious-chest.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts similarity index 100% rename from src/data/mystery-encounters/encounters/mysterious-chest.ts rename to src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts similarity index 100% rename from src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts rename to src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts diff --git a/src/data/mystery-encounters/encounters/sleeping-snorlax.ts b/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts similarity index 100% rename from src/data/mystery-encounters/encounters/sleeping-snorlax.ts rename to src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts diff --git a/src/data/mystery-encounters/encounters/training-session.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts similarity index 100% rename from src/data/mystery-encounters/encounters/training-session.ts rename to src/data/mystery-encounters/encounters/training-session-encounter.ts diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index 602a01627c6..7a78c6edb4c 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -1,15 +1,15 @@ import IMysteryEncounter from "./mystery-encounter"; -import { DarkDealEncounter } from "./encounters/dark-deal"; -import { MysteriousChallengersEncounter } from "./encounters/mysterious-challengers"; -import { MysteriousChestEncounter } from "./encounters/mysterious-chest"; -import { FightOrFlightEncounter } from "#app/data/mystery-encounters/encounters/fight-or-flight"; -import { TrainingSessionEncounter } from "#app/data/mystery-encounters/encounters/training-session"; +import { DarkDealEncounter } from "./encounters/dark-deal-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"; +import { SleepingSnorlaxEncounter } from "./encounters/sleeping-snorlax-encounter"; 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 { FieldTripEncounter } from "#app/data/mystery-encounters/encounters/field-trip-encounter"; +import { DepartmentStoreSaleEncounter } from "./encounters/department-store-sale-encounter"; +import { ShadyVitaminDealerEncounter } from "./encounters/shady-vitamin-dealer-encounter"; +import { FieldTripEncounter } from "./encounters/field-trip-encounter"; // Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * ) / 256 export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1; From 92565691810f29354489c5efa988396431faa632 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 15:18:27 -0700 Subject: [PATCH 21/29] minor fix in phases.ts for myst.-enc. --- src/phases.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phases.ts b/src/phases.ts index eeee9640427..6c64cc9454f 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -828,7 +828,7 @@ export class EncounterPhase extends BattlePhase { if (mysteryEncounter.onInit) { mysteryEncounter.onInit(this.scene); } - // mysteryEncounter.populateDialogueTokensFromRequirements(this.scene); + mysteryEncounter.populateDialogueTokensFromRequirements(this.scene); }, this.scene.currentBattle.waveIndex); // Add intro visuals for mystery encounter From 0326cb25383e0f4dc0fab834cbc6b4fad0b41b60 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 15:18:59 -0700 Subject: [PATCH 22/29] reset overrides.ts --- src/overrides.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overrides.ts b/src/overrides.ts index 1e428c6dd2d..e19a5bf20dd 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -34,7 +34,7 @@ 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 = 33; +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 From 4d4d0e55cc9fdc6f7a4ff38df8759a8450b6c07a Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 15:24:40 -0700 Subject: [PATCH 23/29] fix myster-encounter-ui-handler duplicate req. check --- src/ui/mystery-encounter-ui-handler.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index c446481b744..7eb93f9b4ab 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -307,6 +307,8 @@ export default class MysteryEncounterUiHandler extends UiHandler { // Options Window for (let i = 0; i < this.filteredEncounterOptions.length; i++) { + const option = this.filteredEncounterOptions[i]; + let optionText; switch (this.filteredEncounterOptions.length) { case 2: @@ -319,10 +321,9 @@ export default class MysteryEncounterUiHandler extends UiHandler { optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, i < 2 ? 0 : 16, "-", TextStyle.WINDOW, { wordWrap: { width: 558 }, fontSize: "80px", lineSpacing: -8 }); break; } - const option = this.filteredEncounterOptions[i]; - this.optionsMeetsReqs.push(this.filteredEncounterOptions[i].meetsRequirements(this.scene)); - const optionDialogue = option.dialogue ?? mysteryEncounter.dialogue.encounterOptionsDialogue.options[i]; + this.optionsMeetsReqs.push(option.meetsRequirements(this.scene)); + const optionDialogue = option.dialogue; let text: string; if (option.hasRequirements() && this.optionsMeetsReqs[i]) { // Options with special requirements that are met are automatically colored green @@ -336,8 +337,6 @@ export default class MysteryEncounterUiHandler extends UiHandler { optionText.setText(text); } - this.optionsMeetsReqs.push(option.meetsRequirements(this.scene)); - if (!this.optionsMeetsReqs[i]) { optionText.setAlpha(0.5); } From 96554427b586ed3acdac3c3e825d16958c037fd2 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Thu, 11 Jul 2024 15:27:33 -0700 Subject: [PATCH 24/29] fix myster-encounter-ui-handler duplicate --- src/ui/mystery-encounter-ui-handler.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 7eb93f9b4ab..9eccfd5d0e8 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -337,7 +337,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { optionText.setText(text); } - if (!this.optionsMeetsReqs[i]) { + if (!this.optionsMeetsReqs[i] && option.isDisabledOnRequirementsNotMet) { optionText.setAlpha(0.5); } if (this.blockInput) { @@ -422,11 +422,10 @@ export default class MysteryEncounterUiHandler extends UiHandler { return; } - const mysteryEncounter = this.scene.currentBattle.mysteryEncounter; let text: string; - const option = this.filteredEncounterOptions[cursor]; - const optionDialogue = option.dialogue ?? mysteryEncounter.dialogue.encounterOptionsDialogue.options[cursor]; - if (!this.optionsMeetsReqs[cursor] && this.filteredEncounterOptions[cursor].isDisabledOnRequirementsNotMet && optionDialogue.disabledTooltip) { + const cursorOption = this.filteredEncounterOptions[cursor]; + const optionDialogue = cursorOption.dialogue; + if (!this.optionsMeetsReqs[cursor] && cursorOption.isDisabledOnRequirementsNotMet && optionDialogue.disabledTooltip) { text = getEncounterText(this.scene, optionDialogue.disabledTooltip, TextStyle.TOOLTIP_CONTENT); } else { text = getEncounterText(this.scene, optionDialogue.buttonTooltip, TextStyle.TOOLTIP_CONTENT); From 4412769cd99e1faacdfcf5b8e2a00418d1b14f30 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 11 Jul 2024 18:31:48 -0400 Subject: [PATCH 25/29] small bug fixes --- src/battle-scene.ts | 2 +- src/overrides.ts | 4 ++-- src/phases.ts | 2 +- src/phases/mystery-encounter-phase.ts | 19 +++---------------- 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index d3eac7fc973..2edec910f7b 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -2364,7 +2364,7 @@ export default class BattleScene extends SceneBase { } party.every((enemyPokemon: EnemyPokemon, i: integer) => { - if (customHeldModifiers && i < customHeldModifiers.length && customHeldModifiers[i].length > 0) { + if (customHeldModifiers && i < customHeldModifiers.length && customHeldModifiers[i] && customHeldModifiers[i].length > 0) { customHeldModifiers[i].forEach(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false, this)); return true; } diff --git a/src/overrides.ts b/src/overrides.ts index e19a5bf20dd..485f9c14864 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -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 = null; +export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = 256; export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null; -export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null; +export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.SHADY_VITAMIN_DEALER; /** * MODIFIER / ITEM OVERRIDES diff --git a/src/phases.ts b/src/phases.ts index 6c64cc9454f..4be1305c8b7 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -5363,7 +5363,7 @@ export class SelectModifierPhase extends BattlePhase { this.scene.ui.revertMode(); this.scene.ui.setMode(Mode.MESSAGE); super.end(); - break; + return; } modifierType = typeOptions[cursor].type; break; diff --git a/src/phases/mystery-encounter-phase.ts b/src/phases/mystery-encounter-phase.ts index d84a5eb6b2f..3db7816387a 100644 --- a/src/phases/mystery-encounter-phase.ts +++ b/src/phases/mystery-encounter-phase.ts @@ -5,7 +5,7 @@ import { Mode } from "../ui/ui"; import { getEncounterText } from "../data/mystery-encounters/mystery-encounter-utils"; -import { CheckSwitchPhase, NewBattlePhase, PostSummonPhase, ReturnPhase, ScanIvsPhase, SelectModifierPhase, SummonPhase, ToggleDoublePositionPhase } from "../phases"; +import { CheckSwitchPhase, NewBattlePhase, ReturnPhase, ScanIvsPhase, SelectModifierPhase, SummonPhase, ToggleDoublePositionPhase } from "../phases"; import MysteryEncounterOption from "../data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterVariant } from "../data/mystery-encounters/mystery-encounter"; import { getCharVariantFromDialogue } from "../data/dialogue"; @@ -285,22 +285,9 @@ export class MysteryEncounterBattlePhase extends Phase { const enemyField = scene.getEnemyField(); const encounterVariant = scene.currentBattle.mysteryEncounter.encounterVariant; + // PostSummon and ShinySparkle phases are handled by SummonPhase + if (encounterVariant !== MysteryEncounterVariant.TRAINER_BATTLE) { - enemyField.map(p => this.scene.pushConditionalPhase(new PostSummonPhase(this.scene, p.getBattlerIndex()), () => { - // if there is not a player party, we can't continue - if (!this.scene.getParty()?.length) { - return false; - } - // how many player pokemon are on the field ? - const pokemonsOnFieldCount = this.scene.getParty().filter(p => p.isOnField()).length; - // if it's a 2vs1, there will never be a 2nd pokemon on our field even - const requiredPokemonsOnField = Math.min(this.scene.getParty().filter((p) => !p.isFainted()).length, 2); - // if it's a double, there should be 2, otherwise 1 - if (this.scene.currentBattle.double) { - return pokemonsOnFieldCount === requiredPokemonsOnField; - } - return pokemonsOnFieldCount === 1; - })); const ivScannerModifier = this.scene.findModifier(m => m instanceof IvScannerModifier); if (ivScannerModifier) { enemyField.map(p => this.scene.pushPhase(new ScanIvsPhase(this.scene, p.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6)))); From 61330c6967fb0ce794d148b8589752bd01c4b5bf Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 12 Jul 2024 11:28:22 -0400 Subject: [PATCH 26/29] remove overrides changes --- src/overrides.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overrides.ts b/src/overrides.ts index 485f9c14864..e19a5bf20dd 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -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 = 256; +export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = null; export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null; -export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.SHADY_VITAMIN_DEALER; +export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null; /** * MODIFIER / ITEM OVERRIDES From c99a6382ca02939700e629ec27a58820024aa0a2 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 12 Jul 2024 13:20:14 -0400 Subject: [PATCH 27/29] add option mode for UI styling consistency --- .../encounters/dark-deal-encounter.ts | 47 +++------- .../department-store-sale-encounter.ts | 26 ++---- .../encounters/field-trip-encounter.ts | 89 +++++-------------- .../encounters/fight-or-flight-encounter.ts | 82 ++++------------- .../mysterious-challengers-encounter.ts | 50 ++--------- .../encounters/mysterious-chest-encounter.ts | 3 +- .../shady-vitamin-dealer-encounter.ts | 68 ++++---------- .../encounters/sleeping-snorlax-encounter.ts | 19 +--- .../encounters/training-session-encounter.ts | 20 ++--- .../mystery-encounter-option.ts | 30 ++++--- .../mystery-encounters/mystery-encounter.ts | 6 +- src/ui/mystery-encounter-ui-handler.ts | 16 ++-- 12 files changed, 121 insertions(+), 335 deletions(-) diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index 9d755ad9493..cb66208a6ff 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -7,19 +7,9 @@ import BattleScene from "../../../battle-scene"; import { AddPokeballModifierType } from "../../../modifier/modifier-type"; import { PokeballType } from "../../pokeball"; import { getPokemonSpecies } from "../../pokemon-species"; -import IMysteryEncounter, { - MysteryEncounterBuilder, - MysteryEncounterTier, -} from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -import { - EnemyPartyConfig, - EnemyPokemonConfig, - getRandomPlayerPokemon, - getRandomSpeciesByStarterTier, - initBattleWithEnemyConfig, - leaveEncounterWithoutBattle, -} from "../mystery-encounter-utils"; +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; +import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import { EnemyPartyConfig, EnemyPokemonConfig, getRandomPlayerPokemon, getRandomSpeciesByStarterTier, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, } from "../mystery-encounter-utils"; /** i18n namespace for encounter */ const namespace = "mysteryEncounter:dark_deal"; @@ -27,6 +17,10 @@ const namespace = "mysteryEncounter:dark_deal"; // Exclude Ultra Beasts, Paradox, Necrozma, Eternatus, and egg-locked mythicals const excludedBosses = [ Species.NECROZMA, + Species.COSMOG, + Species.COSMOEM, + Species.SOLGALEO, + Species.LUNALA, Species.ETERNATUS, Species.NIHILEGO, Species.BUZZWOLE, @@ -106,6 +100,7 @@ export const DarkDealEncounter: IMysteryEncounter = .withQuery(`${namespace}_query`) .withOption( new MysteryEncounterOptionBuilder() + .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ buttonLabel: `${namespace}_option_1_label`, buttonTooltip: `${namespace}_option_1_tooltip`, @@ -135,20 +130,12 @@ export const DarkDealEncounter: IMysteryEncounter = removedPokemon.species.type1, ]; if (removedPokemon.species.type2) { - scene.currentBattle.mysteryEncounter.misc.push( - removedPokemon.species.type2 - ); + scene.currentBattle.mysteryEncounter.misc.push(removedPokemon.species.type2); } }) .withOptionPhase(async (scene: BattleScene) => { // Give the player 5 Rogue Balls - scene.unshiftPhase( - new ModifierRewardPhase( - scene, - () => - new AddPokeballModifierType("rb", PokeballType.ROGUE_BALL, 5) - ) - ); + scene.unshiftPhase(new ModifierRewardPhase(scene, () => new AddPokeballModifierType("rb", PokeballType.ROGUE_BALL, 5))); // Start encounter with random legendary (7-10 starter strength) that has level additive const bossTypes = scene.currentBattle.mysteryEncounter.misc as Type[]; @@ -156,21 +143,12 @@ export const DarkDealEncounter: IMysteryEncounter = const roll = randSeedInt(100); const starterTier: number | [number, number] = roll > 65 ? 6 : roll > 15 ? 7 : roll > 5 ? 8 : [9, 10]; - const bossSpecies = getPokemonSpecies( - getRandomSpeciesByStarterTier( - starterTier, - excludedBosses, - bossTypes - ) - ); + const bossSpecies = getPokemonSpecies(getRandomSpeciesByStarterTier(starterTier, excludedBosses, bossTypes)); const pokemonConfig: EnemyPokemonConfig = { species: bossSpecies, isBoss: true, }; - if ( - !isNullOrUndefined(bossSpecies.forms) && - bossSpecies.forms.length > 0 - ) { + if (!isNullOrUndefined(bossSpecies.forms) && bossSpecies.forms.length > 0) { pokemonConfig.formIndex = 0; } const config: EnemyPartyConfig = { @@ -194,7 +172,6 @@ export const DarkDealEncounter: IMysteryEncounter = }, async (scene: BattleScene) => { // Leave encounter with no rewards or exp - leaveEncounterWithoutBattle(scene, true); return true; } diff --git a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts index d821a952864..9ad90a92cfb 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -16,9 +16,7 @@ import IMysteryEncounter, { const namespace = "mysteryEncounter:department_store_sale"; export const DepartmentStoreSaleEncounter: IMysteryEncounter = - MysteryEncounterBuilder.withEncounterType( - MysteryEncounterType.DEPARTMENT_STORE_SALE - ) + MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE) .withEncounterTier(MysteryEncounterTier.COMMON) .withSceneWaveRangeRequirement(10, 100) .withIntroSpriteConfigs([ @@ -45,7 +43,7 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter = speaker: `${namespace}_speaker`, }, ]) - // .withHideIntroVisuals(false) + .withHideIntroVisuals(false) .withTitle(`${namespace}_title`) .withDescription(`${namespace}_description`) .withQuery(`${namespace}_query`) @@ -71,10 +69,7 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter = i++; } - setEncounterRewards(scene, { - guaranteedModifierTypeFuncs: modifiers, - fillRemaining: false, - }); + setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, }); leaveEncounterWithoutBattle(scene); } ) @@ -98,10 +93,7 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter = i++; } - setEncounterRewards(scene, { - guaranteedModifierTypeFuncs: modifiers, - fillRemaining: false, - }); + setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, }); leaveEncounterWithoutBattle(scene); } ) @@ -125,10 +117,7 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter = i++; } - setEncounterRewards(scene, { - guaranteedModifierTypeFuncs: modifiers, - fillRemaining: false, - }); + setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, }); leaveEncounterWithoutBattle(scene); } ) @@ -156,10 +145,7 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter = i++; } - setEncounterRewards(scene, { - guaranteedModifierTypeFuncs: modifiers, - fillRemaining: false, - }); + setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, }); leaveEncounterWithoutBattle(scene); } ) diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index 9235504e332..b0131fa54c5 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -1,5 +1,5 @@ import { MoveCategory } from "#app/data/move"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { generateModifierTypeOption, leaveEncounterWithoutBattle, @@ -57,6 +57,7 @@ export const FieldTripEncounter: IMysteryEncounter = .withQuery(`${namespace}_query`) .withOption( new MysteryEncounterOptionBuilder() + .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ buttonLabel: `${namespace}_option_1_label`, buttonTooltip: `${namespace}_option_1_tooltip`, @@ -76,8 +77,7 @@ export const FieldTripEncounter: IMysteryEncounter = label: move.getName(), handler: () => { // Pokemon and move selected - const correctMove = - move.getMove().category === MoveCategory.PHYSICAL; + const correctMove = move.getMove().category === MoveCategory.PHYSICAL; encounter.setDialogueToken("moveCategory", "Physical"); if (!correctMove) { encounter.options[0].dialogue.selected = [ @@ -89,11 +89,7 @@ export const FieldTripEncounter: IMysteryEncounter = text: `${namespace}_lesson_learned`, }, ]; - setEncounterExp( - scene, - scene.getParty().map((p) => p.id), - 50 - ); + setEncounterExp(scene, scene.getParty().map((p) => p.id), 50); } else { encounter.setDialogueToken("pokeName", pokemon.name); encounter.setDialogueToken("move", move.getName()); @@ -120,28 +116,13 @@ export const FieldTripEncounter: IMysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter; if (encounter.misc.correctMove) { const modifiers = [ - generateModifierTypeOption( - scene, - modifierTypes.TEMP_STAT_BOOSTER, - [TempBattleStat.ATK] - ), - generateModifierTypeOption( - scene, - modifierTypes.TEMP_STAT_BOOSTER, - [TempBattleStat.DEF] - ), - generateModifierTypeOption( - scene, - modifierTypes.TEMP_STAT_BOOSTER, - [TempBattleStat.SPD] - ), + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.ATK]), + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.DEF]), + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]), generateModifierTypeOption(scene, modifierTypes.DIRE_HIT), ]; - setEncounterRewards(scene, { - guaranteedModifierTypeOptions: modifiers, - fillRemaining: false, - }); + setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); } leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); @@ -150,6 +131,7 @@ export const FieldTripEncounter: IMysteryEncounter = ) .withOption( new MysteryEncounterOptionBuilder() + .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ buttonLabel: `${namespace}_option_2_label`, buttonTooltip: `${namespace}_option_2_tooltip`, @@ -169,8 +151,7 @@ export const FieldTripEncounter: IMysteryEncounter = label: move.getName(), handler: () => { // Pokemon and move selected - const correctMove = - move.getMove().category === MoveCategory.SPECIAL; + const correctMove = move.getMove().category === MoveCategory.SPECIAL; encounter.setDialogueToken("moveCategory", "Special"); if (!correctMove) { encounter.options[1].dialogue.selected = [ @@ -182,11 +163,7 @@ export const FieldTripEncounter: IMysteryEncounter = text: `${namespace}_lesson_learned`, }, ]; - setEncounterExp( - scene, - scene.getParty().map((p) => p.id), - 50 - ); + setEncounterExp(scene, scene.getParty().map((p) => p.id), 50); } else { encounter.setDialogueToken("pokeName", pokemon.name); encounter.setDialogueToken("move", move.getName()); @@ -213,28 +190,13 @@ export const FieldTripEncounter: IMysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter; if (encounter.misc.correctMove) { const modifiers = [ - generateModifierTypeOption( - scene, - modifierTypes.TEMP_STAT_BOOSTER, - [TempBattleStat.SPATK] - ), - generateModifierTypeOption( - scene, - modifierTypes.TEMP_STAT_BOOSTER, - [TempBattleStat.SPDEF] - ), - generateModifierTypeOption( - scene, - modifierTypes.TEMP_STAT_BOOSTER, - [TempBattleStat.SPD] - ), + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPATK]), + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPDEF]), + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]), generateModifierTypeOption(scene, modifierTypes.DIRE_HIT), ]; - setEncounterRewards(scene, { - guaranteedModifierTypeOptions: modifiers, - fillRemaining: false, - }); + setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); } leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); @@ -243,6 +205,7 @@ export const FieldTripEncounter: IMysteryEncounter = ) .withOption( new MysteryEncounterOptionBuilder() + .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ buttonLabel: `${namespace}_option_3_label`, buttonTooltip: `${namespace}_option_3_tooltip`, @@ -262,8 +225,7 @@ export const FieldTripEncounter: IMysteryEncounter = label: move.getName(), handler: () => { // Pokemon and move selected - const correctMove = - move.getMove().category === MoveCategory.STATUS; + const correctMove = move.getMove().category === MoveCategory.STATUS; encounter.setDialogueToken("moveCategory", "Status"); if (!correctMove) { encounter.options[2].dialogue.selected = [ @@ -306,24 +268,13 @@ export const FieldTripEncounter: IMysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter; if (encounter.misc.correctMove) { const modifiers = [ - generateModifierTypeOption( - scene, - modifierTypes.TEMP_STAT_BOOSTER, - [TempBattleStat.ACC] - ), - generateModifierTypeOption( - scene, - modifierTypes.TEMP_STAT_BOOSTER, - [TempBattleStat.SPD] - ), + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.ACC]), + generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]), generateModifierTypeOption(scene, modifierTypes.GREAT_BALL), generateModifierTypeOption(scene, modifierTypes.IV_SCANNER), ]; - setEncounterRewards(scene, { - guaranteedModifierTypeOptions: modifiers, - fillRemaining: false, - }); + setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); } leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index c98d7cd485f..a742282a73f 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -1,5 +1,5 @@ import { BattleStat } from "#app/data/battle-stat"; -import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { EnemyPartyConfig, initBattleWithEnemyConfig, @@ -8,7 +8,7 @@ import { setEncounterRewards, showEncounterText, } from "#app/data/mystery-encounters/mystery-encounter-utils"; -import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; +import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import Pokemon from "#app/field/pokemon"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { @@ -50,13 +50,7 @@ export const FightOrFlightEncounter: IMysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter; // Calculate boss mon - const bossSpecies = scene.arena.randomSpecies( - scene.currentBattle.waveIndex, - scene.currentBattle.waveIndex, - 0, - getPartyLuckValue(scene.getParty()), - true - ); + const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, scene.currentBattle.waveIndex, 0, getPartyLuckValue(scene.getParty()), true); const config: EnemyPartyConfig = { levelAdditiveMultiplier: 1, pokemonConfigs: [{ species: bossSpecies, isBoss: true }], @@ -73,17 +67,12 @@ export const FightOrFlightEncounter: IMysteryEncounter = : scene.currentBattle.waveIndex > 60 ? ModifierTier.ULTRA : ModifierTier.GREAT; - regenerateModifierPoolThresholds( - scene.getParty(), - ModifierPoolType.PLAYER, - 0 - ); // refresh player item pool - const item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { - guaranteedModifierTiers: [tier], - })[0]; + regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0); + const item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier] })[0]; encounter.setDialogueToken("itemName", item.type.name); encounter.misc = item; + const bossSpriteKey = bossSpecies.getSpriteId(false, bossSpecies.forms ? 0 : null, false, bossSpecies.hasVariants() ? 0 : null); encounter.spriteConfigs = [ { spriteKey: item.type.iconImage, @@ -95,7 +84,7 @@ export const FightOrFlightEncounter: IMysteryEncounter = isItem: true, }, { - spriteKey: bossSpecies.speciesId.toString(), + spriteKey: bossSpriteKey, fileRoot: "pokemon", hasShadow: true, tint: 0.25, @@ -133,20 +122,14 @@ export const FightOrFlightEncounter: IMysteryEncounter = // Pick battle const item = scene.currentBattle.mysteryEncounter .misc as ModifierTypeOption; - setEncounterRewards(scene, { - guaranteedModifierTypeOptions: [item], - fillRemaining: false, - }); - await initBattleWithEnemyConfig( - scene, - scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0] - ); + setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false }); + await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]); } ) .withOption( new MysteryEncounterOptionBuilder() + .withOptionMode(EncounterOptionMode.DEFAULT_OR_SPECIAL) .withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically - .withDisabledOnRequirementsNotMet(false) .withDialogue({ buttonLabel: `${namespace}_option_2_label`, buttonTooltip: `${namespace}_option_2_tooltip`, @@ -154,21 +137,14 @@ export const FightOrFlightEncounter: IMysteryEncounter = .withOptionPhase(async (scene: BattleScene) => { // Pick steal const encounter = scene.currentBattle.mysteryEncounter; - const item = scene.currentBattle.mysteryEncounter - .misc as ModifierTypeOption; - setEncounterRewards(scene, { - guaranteedModifierTypeOptions: [item], - fillRemaining: false, - }); + const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption; + setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false }); // If player has a stealing move, they succeed automatically const primaryPokemon = encounter.options[1].primaryPokemon; if (primaryPokemon) { // Use primaryPokemon to execute the thievery - await showEncounterText( - scene, - `${namespace}_option_2_steal_result` - ); + await showEncounterText(scene, `${namespace}_option_2_steal_result`); leaveEncounterWithoutBattle(scene); return; } @@ -176,34 +152,12 @@ export const FightOrFlightEncounter: IMysteryEncounter = const roll = randSeedInt(16); if (roll > 6) { // Noticed and attacked by boss, gets +1 to all stats at start of fight (62.5%) - const config = - scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]; - config.pokemonConfigs[0].tags = [ - BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON, - ]; - config.pokemonConfigs[0].mysteryEncounterBattleEffects = ( - pokemon: Pokemon - ) => { - pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken( - "enemyPokemon", - pokemon.name - ); + const config = scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]; + config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON]; + config.pokemonConfigs[0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { + pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken("enemyPokemon", pokemon.name); queueEncounterMessage(pokemon.scene, `${namespace}_boss_enraged`); - pokemon.scene.unshiftPhase( - new StatChangePhase( - pokemon.scene, - pokemon.getBattlerIndex(), - true, - [ - BattleStat.ATK, - BattleStat.DEF, - BattleStat.SPATK, - BattleStat.SPDEF, - BattleStat.SPD, - ], - 1 - ) - ); + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1)); }; await showEncounterText(scene, `${namespace}_option_2_bad_result`); await initBattleWithEnemyConfig(scene, config); diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index 1276d6e2226..7c7d03b7895 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -40,18 +40,13 @@ export const MysteriousChallengersEncounter: IMysteryEncounter = // Calculates what trainers are available for battle in the encounter // Normal difficulty trainer is randomly pulled from biome - const normalTrainerType = scene.arena.randomTrainerType( - scene.currentBattle.waveIndex - ); + const normalTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex); const normalConfig = trainerConfigs[normalTrainerType].copy(); let female = false; if (normalConfig.hasGenders) { female = !!Utils.randSeedInt(2); } - const normalSpriteKey = normalConfig.getSpriteKey( - female, - normalConfig.doubleOnly - ); + const normalSpriteKey = normalConfig.getSpriteKey(female, normalConfig.doubleOnly); encounter.enemyPartyConfigs.push({ trainerConfig: normalConfig, female: female, @@ -59,9 +54,7 @@ export const MysteriousChallengersEncounter: IMysteryEncounter = // Hard difficulty trainer is another random trainer, but with AVERAGE_BALANCED config // Number of mons is based off wave: 1-20 is 2, 20-40 is 3, etc. capping at 6 after wave 100 - const hardTrainerType = scene.arena.randomTrainerType( - scene.currentBattle.waveIndex - ); + const hardTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex); const hardTemplate = new TrainerPartyCompoundTemplate( new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, false, true), new TrainerPartyTemplate( @@ -77,10 +70,7 @@ export const MysteriousChallengersEncounter: IMysteryEncounter = if (hardConfig.hasGenders) { female = !!Utils.randSeedInt(2); } - const hardSpriteKey = hardConfig.getSpriteKey( - female, - hardConfig.doubleOnly - ); + const hardSpriteKey = hardConfig.getSpriteKey(female, hardConfig.doubleOnly); encounter.enemyPartyConfigs.push({ trainerConfig: hardConfig, levelAdditiveMultiplier: 0.5, @@ -101,10 +91,7 @@ export const MysteriousChallengersEncounter: IMysteryEncounter = if (brutalConfig.hasGenders) { female = !!Utils.randSeedInt(2); } - const brutalSpriteKey = brutalConfig.getSpriteKey( - female, - brutalConfig.doubleOnly - ); + const brutalSpriteKey = brutalConfig.getSpriteKey(female, brutalConfig.doubleOnly); encounter.enemyPartyConfigs.push({ trainerConfig: brutalConfig, levelAdditiveMultiplier: 1.1, @@ -152,14 +139,7 @@ export const MysteriousChallengersEncounter: IMysteryEncounter = // Spawn standard trainer battle with memory mushroom reward const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; - setEncounterRewards(scene, { - guaranteedModifierTypeFuncs: [ - modifierTypes.TM_COMMON, - modifierTypes.TM_GREAT, - modifierTypes.MEMORY_MUSHROOM, - ], - fillRemaining: true, - }); + setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM], fillRemaining: true }); // Seed offsets to remove possibility of different trainers having exact same teams let ret; @@ -184,14 +164,7 @@ export const MysteriousChallengersEncounter: IMysteryEncounter = // Spawn hard fight with ULTRA/GREAT reward (can improve with luck) const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1]; - setEncounterRewards(scene, { - guaranteedModifierTiers: [ - ModifierTier.ULTRA, - ModifierTier.GREAT, - ModifierTier.GREAT, - ], - fillRemaining: true, - }); + setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], fillRemaining: true }); // Seed offsets to remove possibility of different trainers having exact same teams let ret; @@ -219,14 +192,7 @@ export const MysteriousChallengersEncounter: IMysteryEncounter = // To avoid player level snowballing from picking this option encounter.expMultiplier = 0.9; - setEncounterRewards(scene, { - guaranteedModifierTiers: [ - ModifierTier.ROGUE, - ModifierTier.ULTRA, - ModifierTier.GREAT, - ], - fillRemaining: true, - }); + setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT], fillRemaining: true }); // Seed offsets to remove possibility of different trainers having exact same teams let ret; diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 8d1f1c2eeb1..49672ee636c 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -15,7 +15,7 @@ import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; export const MysteriousChestEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( @@ -44,6 +44,7 @@ export const MysteriousChestEncounter: IMysteryEncounter = .withQuery("mysteryEncounter:mysterious_chest_query") .withOption( new MysteryEncounterOptionBuilder() + .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ buttonLabel: "mysteryEncounter:mysterious_chest_option_1_label", buttonTooltip: "mysteryEncounter:mysterious_chest_option_1_tooltip", diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts index 14dddf91189..6018b651639 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -1,11 +1,4 @@ -import { - generateModifierTypeOption, - leaveEncounterWithoutBattle, - queueEncounterMessage, - selectPokemonForOption, - setEncounterExp, - updatePlayerMoney, -} from "#app/data/mystery-encounters/mystery-encounter-utils"; +import { generateModifierTypeOption, leaveEncounterWithoutBattle, queueEncounterMessage, selectPokemonForOption, setEncounterExp, updatePlayerMoney, } from "#app/data/mystery-encounters/mystery-encounter-utils"; import { StatusEffect } from "#app/data/status-effect"; import Pokemon, { PlayerPokemon } from "#app/field/pokemon"; import { modifierTypes } from "#app/modifier/modifier-type"; @@ -14,11 +7,8 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import i18next from "i18next"; import BattleScene from "../../../battle-scene"; -import IMysteryEncounter, { - MysteryEncounterBuilder, - MysteryEncounterTier, -} from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; +import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { MoneyRequirement } from "../mystery-encounter-requirements"; /** the i18n namespace for this encounter */ @@ -63,6 +53,7 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = .withQuery(`${namespace}_query`) .withOption( new MysteryEncounterOptionBuilder() + .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) .withSceneMoneyRequirement(0, 2) // Wave scaling money multiplier of 2 .withDialogue({ buttonLabel: `${namespace}_option_1_label`, @@ -77,17 +68,11 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter; const onPokemonSelected = (pokemon: PlayerPokemon) => { // Update money - updatePlayerMoney( - scene, - -(encounter.options[0].requirements[0] as MoneyRequirement) - .requiredMoney - ); + updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney); // Calculate modifiers and dialogue tokens const modifiers = [ - generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER) - .type, - generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER) - .type, + generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type, + generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type, ]; encounter.setDialogueToken("boost1", modifiers[0].name); encounter.setDialogueToken("boost2", modifiers[1].name); @@ -100,10 +85,7 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = // Only Pokemon that can gain benefits are above 1/3rd HP with no status const selectableFilter = (pokemon: Pokemon) => { // If pokemon meets primary pokemon reqs, it can be selected - const meetsReqs = encounter.pokemonMeetsPrimaryRequirements( - scene, - pokemon - ); + const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon); if (!meetsReqs) { return i18next.t(`${namespace}_invalid_selection`); } @@ -111,12 +93,7 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = return null; }; - return selectPokemonForOption( - scene, - onPokemonSelected, - null, - selectableFilter - ); + return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); }) .withOptionPhase(async (scene: BattleScene) => { // Choose Cheap Option @@ -162,6 +139,8 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = ) .withOption( new MysteryEncounterOptionBuilder() + .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) + .withSceneMoneyRequirement(0, 5) // Wave scaling money multiplier of 5 .withDialogue({ buttonLabel: `${namespace}_option_2_label`, buttonTooltip: `${namespace}_option_2_tooltip`, @@ -171,22 +150,15 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = }, ], }) - .withSceneMoneyRequirement(0, 5) // Wave scaling money multiplier of 5 .withPreOptionPhase(async (scene: BattleScene): Promise => { const encounter = scene.currentBattle.mysteryEncounter; const onPokemonSelected = (pokemon: PlayerPokemon) => { // Update money - updatePlayerMoney( - scene, - -(encounter.options[1].requirements[0] as MoneyRequirement) - .requiredMoney - ); + updatePlayerMoney(scene, -(encounter.options[1].requirements[0] as MoneyRequirement).requiredMoney); // Calculate modifiers and dialogue tokens const modifiers = [ - generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER) - .type, - generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER) - .type, + generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type, + generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type, ]; encounter.setDialogueToken("boost1", modifiers[0].name); encounter.setDialogueToken("boost2", modifiers[1].name); @@ -199,10 +171,7 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = // Only Pokemon that can gain benefits are above 1/3rd HP with no status const selectableFilter = (pokemon: Pokemon) => { // If pokemon meets primary pokemon reqs, it can be selected - const meetsReqs = encounter.pokemonMeetsPrimaryRequirements( - scene, - pokemon - ); + const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon); if (!meetsReqs) { return i18next.t(`${namespace}_invalid_selection`); } @@ -210,12 +179,7 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = return null; }; - return selectPokemonForOption( - scene, - onPokemonSelected, - null, - selectableFilter - ); + return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter); }) .withOptionPhase(async (scene: BattleScene) => { // Choose Expensive Option diff --git a/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts index b0130c34419..d26bacf7c5c 100644 --- a/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts @@ -7,22 +7,10 @@ import BattleScene from "../../../battle-scene"; import * as Utils from "../../../utils"; import { getPokemonSpecies } from "../../pokemon-species"; import { Status, StatusEffect } from "../../status-effect"; -import IMysteryEncounter, { - MysteryEncounterBuilder, - MysteryEncounterTier, -} from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; +import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { MoveRequirement } from "../mystery-encounter-requirements"; -import { - EnemyPartyConfig, - EnemyPokemonConfig, - generateModifierTypeOption, - initBattleWithEnemyConfig, - leaveEncounterWithoutBattle, - queueEncounterMessage, - setEncounterExp, - setEncounterRewards, -} from "../mystery-encounter-utils"; +import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierTypeOption, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, queueEncounterMessage, setEncounterExp, setEncounterRewards, } from "../mystery-encounter-utils"; /** i18n namespace for the encounter */ const namespace = "mysteryEncounter:sleeping_snorlax"; @@ -152,6 +140,7 @@ export const SleepingSnorlaxEncounter: IMysteryEncounter = ) .withOption( new MysteryEncounterOptionBuilder() + .withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL) .withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) .withDialogue({ buttonLabel: `${namespace}_option_3_label`, diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index c5f0ee2e46f..da76d8a2d64 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -1,12 +1,6 @@ import { Ability, allAbilities } from "#app/data/ability"; -import { - EnemyPartyConfig, - getEncounterText, - initBattleWithEnemyConfig, - selectPokemonForOption, - setEncounterRewards, -} from "#app/data/mystery-encounters/mystery-encounter-utils"; -import { Nature, getNatureName } from "#app/data/nature"; +import { EnemyPartyConfig, getEncounterText, initBattleWithEnemyConfig, selectPokemonForOption, setEncounterRewards, } from "#app/data/mystery-encounters/mystery-encounter-utils"; +import { getNatureName, Nature } from "#app/data/nature"; import { speciesStarters } from "#app/data/pokemon-species"; import { Stat } from "#app/data/pokemon-stat"; import { PlayerPokemon } from "#app/field/pokemon"; @@ -20,11 +14,8 @@ import { randSeedShuffle } from "#app/utils"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; -import IMysteryEncounter, { - MysteryEncounterBuilder, - MysteryEncounterTier, -} from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; +import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; /** The i18n namespace for the encounter */ const namespace = "mysteryEncounter:training_session"; @@ -54,6 +45,7 @@ export const TrainingSessionEncounter: IMysteryEncounter = .withQuery(`${namespace}_query`) .withOption( new MysteryEncounterOptionBuilder() + .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ buttonLabel: `${namespace}_option_1_label`, buttonTooltip: `${namespace}_option_1_tooltip`, @@ -190,6 +182,7 @@ export const TrainingSessionEncounter: IMysteryEncounter = ) .withOption( new MysteryEncounterOptionBuilder() + .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ buttonLabel: `${namespace}_option_2_label`, buttonTooltip: `${namespace}_option_2_tooltip`, @@ -275,6 +268,7 @@ export const TrainingSessionEncounter: IMysteryEncounter = ) .withOption( new MysteryEncounterOptionBuilder() + .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ buttonLabel: `${namespace}_option_3_label`, buttonTooltip: `${namespace}_option_3_tooltip`, diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index 1ab6a4d59ef..05a717c3e85 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -3,24 +3,29 @@ 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 { isNullOrUndefined } from "../../utils"; + +export enum EncounterOptionMode { + /** Default style */ + DEFAULT, + /** Disabled on requirements not met, default style on requirements met */ + DISABLED_OR_DEFAULT, + /** Default style on requirements not met, special style on requirements met */ + DEFAULT_OR_SPECIAL, + /** Disabled on requirements not met, special style on requirements met */ + DISABLED_OR_SPECIAL +} export type OptionPhaseCallback = (scene: BattleScene) => Promise; export default interface MysteryEncounterOption { + optionMode: EncounterOptionMode; requirements?: EncounterSceneRequirement[]; primaryPokemonRequirements?: EncounterPokemonRequirement[]; secondaryPokemonRequirements?: EncounterPokemonRequirement[]; primaryPokemon?: PlayerPokemon; secondaryPokemon?: PlayerPokemon[]; excludePrimaryFromSecondaryRequirements?: boolean; - /** - * There are two modes of option requirements: - * 1 (DEFAULT): Option is completely disabled if requirements are not met (unselectable and greyed out) - * 2: Option is *NOT* disabled if requirements are not met - */ - isDisabledOnRequirementsNotMet?: boolean; /** * Dialogue object containing all the dialogue, messages, tooltips, etc. for this option @@ -42,7 +47,6 @@ export default class MysteryEncounterOption implements MysteryEncounterOption { this.requirements = this.requirements ? this.requirements : []; this.primaryPokemonRequirements = this.primaryPokemonRequirements ? this.primaryPokemonRequirements : []; this.secondaryPokemonRequirements = this.secondaryPokemonRequirements ? this.secondaryPokemonRequirements : []; - this.isDisabledOnRequirementsNotMet = isNullOrUndefined(this.isDisabledOnRequirementsNotMet) ? true : this.isDisabledOnRequirementsNotMet; } hasRequirements?() { @@ -134,6 +138,7 @@ export default class MysteryEncounterOption implements MysteryEncounterOption { export class MysteryEncounterOptionBuilder implements Partial { + optionMode?: EncounterOptionMode; requirements?: EncounterSceneRequirement[] = []; primaryPokemonRequirements?: EncounterPokemonRequirement[] = []; secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = []; @@ -144,6 +149,10 @@ export class MysteryEncounterOptionBuilder implements Partial { + return Object.assign(this, { optionMode: optionMode }); + } + withSceneRequirement(requirement: EncounterSceneRequirement): this & Required> { this.requirements.push(requirement); return Object.assign(this, { requirements: this.requirements }); @@ -180,11 +189,6 @@ export class MysteryEncounterOptionBuilder implements Partial> { - this.isDisabledOnRequirementsNotMet = disabled; - return Object.assign(this, { isDisabledOnRequirementsNotMet: this.isDisabledOnRequirementsNotMet }); - } - withDialogue(dialogue: OptionTextDisplay) { this.dialogue = dialogue; return this; diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 30e2e6682a6..6fee6e493cf 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -9,7 +9,7 @@ import { StatusEffect } from "../status-effect"; import MysteryEncounterDialogue, { OptionTextDisplay } from "./mystery-encounter-dialogue"; -import MysteryEncounterOption, { MysteryEncounterOptionBuilder, OptionPhaseCallback } from "./mystery-encounter-option"; +import MysteryEncounterOption, { EncounterOptionMode, MysteryEncounterOptionBuilder, OptionPhaseCallback } from "./mystery-encounter-option"; import { EncounterPokemonRequirement, EncounterSceneRequirement, @@ -136,7 +136,7 @@ export default class IMysteryEncounter implements IMysteryEncounter { Object.assign(this, encounter); } this.encounterTier = this.encounterTier ? this.encounterTier : MysteryEncounterTier.COMMON; - this.dialogue = {}; + this.dialogue = this.dialogue ?? {}; this.encounterVariant = MysteryEncounterVariant.DEFAULT; this.requirements = this.requirements ? this.requirements : []; this.hideBattleIntroMessage = !isNullOrUndefined(this.hideBattleIntroMessage) ? this.hideBattleIntroMessage : false; @@ -397,7 +397,7 @@ export class MysteryEncounterBuilder implements Partial { * @returns */ withSimpleOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback) { - return this.withOption(new MysteryEncounterOptionBuilder().withDialogue(dialogue).withOptionPhase(callback).build()); + return this.withOption(new MysteryEncounterOptionBuilder().withOptionMode(EncounterOptionMode.DEFAULT).withDialogue(dialogue).withOptionPhase(callback).build()); } /** diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 9eccfd5d0e8..2d35e98c772 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -6,7 +6,7 @@ import { Button } from "#enums/buttons"; import { addWindow, WindowVariant } from "./ui-theme"; import { MysteryEncounterPhase } from "../phases/mystery-encounter-phase"; import { PartyUiMode } from "./party-ui-handler"; -import MysteryEncounterOption from "../data/mystery-encounters/mystery-encounter-option"; +import MysteryEncounterOption, { EncounterOptionMode } from "../data/mystery-encounters/mystery-encounter-option"; import * as Utils from "../utils"; import { isNullOrUndefined } from "../utils"; import { getPokeballAtlasKey } from "../data/pokeball"; @@ -100,6 +100,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { if (button === Button.CANCEL || button === Button.ACTION) { if (button === Button.ACTION) { + const selected = this.filteredEncounterOptions[cursor]; if (cursor === this.viewPartyIndex) { // Handle view party success = true; @@ -110,10 +111,9 @@ export default class MysteryEncounterUiHandler extends UiHandler { this.unblockInput(); }, 300); }); - } else if (this.blockInput || (!this.optionsMeetsReqs[cursor] && this.filteredEncounterOptions[cursor].isDisabledOnRequirementsNotMet)) { + } else if (this.blockInput || (!this.optionsMeetsReqs[cursor] && (selected.optionMode === EncounterOptionMode.DISABLED_OR_DEFAULT || selected.optionMode === EncounterOptionMode.DISABLED_OR_SPECIAL))) { success = false; } else { - const selected = this.filteredEncounterOptions[cursor]; if ((this.scene.getCurrentPhase() as MysteryEncounterPhase).handleOptionSelect(selected, cursor)) { success = true; } else { @@ -253,7 +253,8 @@ export default class MysteryEncounterUiHandler extends UiHandler { if (this.blockInput) { this.blockInput = false; for (let i = 0; i < this.optionsContainer.length - 1; i++) { - if (!this.optionsMeetsReqs[i] && this.filteredEncounterOptions[i].isDisabledOnRequirementsNotMet) { + const optionMode = this.filteredEncounterOptions[i].optionMode; + if (!this.optionsMeetsReqs[i] && (optionMode === EncounterOptionMode.DISABLED_OR_DEFAULT || optionMode === EncounterOptionMode.DISABLED_OR_SPECIAL)) { continue; } (this.optionsContainer.getAt(i) as Phaser.GameObjects.Text).setAlpha(1); @@ -325,9 +326,8 @@ export default class MysteryEncounterUiHandler extends UiHandler { this.optionsMeetsReqs.push(option.meetsRequirements(this.scene)); const optionDialogue = option.dialogue; let text: string; - if (option.hasRequirements() && this.optionsMeetsReqs[i]) { + if (option.hasRequirements() && this.optionsMeetsReqs[i] && (option.optionMode === EncounterOptionMode.DEFAULT_OR_SPECIAL || option.optionMode === EncounterOptionMode.DISABLED_OR_SPECIAL)) { // Options with special requirements that are met are automatically colored green - // In cases where isDisabledOnRequirementsNotMet = false and requirements are not met, option will not be auto-colored text = getEncounterText(this.scene, optionDialogue.buttonLabel, TextStyle.SUMMARY_GREEN); } else { text = getEncounterText(this.scene, optionDialogue.buttonLabel, optionDialogue.style ? optionDialogue.style : TextStyle.WINDOW); @@ -337,7 +337,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { optionText.setText(text); } - if (!this.optionsMeetsReqs[i] && option.isDisabledOnRequirementsNotMet) { + if (!this.optionsMeetsReqs[i] && (option.optionMode === EncounterOptionMode.DISABLED_OR_DEFAULT || option.optionMode === EncounterOptionMode.DISABLED_OR_SPECIAL)) { optionText.setAlpha(0.5); } if (this.blockInput) { @@ -425,7 +425,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { let text: string; const cursorOption = this.filteredEncounterOptions[cursor]; const optionDialogue = cursorOption.dialogue; - if (!this.optionsMeetsReqs[cursor] && cursorOption.isDisabledOnRequirementsNotMet && optionDialogue.disabledTooltip) { + if (!this.optionsMeetsReqs[cursor] && (cursorOption.optionMode === EncounterOptionMode.DISABLED_OR_DEFAULT || cursorOption.optionMode === EncounterOptionMode.DISABLED_OR_SPECIAL) && optionDialogue.disabledTooltip) { text = getEncounterText(this.scene, optionDialogue.disabledTooltip, TextStyle.TOOLTIP_CONTENT); } else { text = getEncounterText(this.scene, optionDialogue.buttonTooltip, TextStyle.TOOLTIP_CONTENT); From 14ec528fea3c85dc9171ce1a76f54600b516952c Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 12 Jul 2024 13:29:47 -0400 Subject: [PATCH 28/29] remove extra space --- .../mystery-encounters/encounters/fight-or-flight-encounter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index a742282a73f..7a96e3f89aa 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -8,7 +8,7 @@ import { setEncounterRewards, showEncounterText, } from "#app/data/mystery-encounters/mystery-encounter-utils"; -import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; +import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import Pokemon from "#app/field/pokemon"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { From bf72985c1987fb7b22da3e25c9afa907d528ff55 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer <110984302+ben-lear@users.noreply.github.com> Date: Fri, 12 Jul 2024 13:37:59 -0400 Subject: [PATCH 29/29] Update src/data/mystery-encounters/mystery-encounter-option.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> --- src/data/mystery-encounters/mystery-encounter-option.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index 05a717c3e85..7a65d77488e 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -150,7 +150,7 @@ export class MysteryEncounterOptionBuilder implements Partial { - return Object.assign(this, { optionMode: optionMode }); + return Object.assign(this, { optionMode }); } withSceneRequirement(requirement: EncounterSceneRequirement): this & Required> {