From a8be171305bbcb6f071c86607ac44cbec76d5e97 Mon Sep 17 00:00:00 2001 From: Felix Staud Date: Tue, 9 Jul 2024 22:34:53 -0700 Subject: [PATCH] add `MysterEncounterBuilder.withOptionPhase()` --- .../encounters/dark-deal.ts | 15 +- .../encounters/department-store-sale.ts | 151 ++++++++---------- .../encounters/fight-or-flight.ts | 101 ++++++------ .../encounters/mysterious-challengers.ts | 83 +++++----- .../encounters/mysterious-chest.ts | 15 +- .../encounters/shady-vitamin-dealer.ts | 16 +- .../encounters/sleeping-snorlax.ts | 79 +++++---- .../encounters/training-session.ts | 9 +- .../mystery-encounter-option.ts | 21 +-- .../mystery-encounters/mystery-encounter.ts | 13 +- 10 files changed, 246 insertions(+), 257 deletions(-) diff --git a/src/data/mystery-encounters/encounters/dark-deal.ts b/src/data/mystery-encounters/encounters/dark-deal.ts index 3a6d3ec273a..1985ffcf8dd 100644 --- a/src/data/mystery-encounters/encounters/dark-deal.ts +++ b/src/data/mystery-encounters/encounters/dark-deal.ts @@ -123,12 +123,11 @@ export const DarkDealEncounter: MysteryEncounter = MysteryEncounterBuilder }; return initBattleWithEnemyConfig(scene, config); }) - .build()) - .withOption(new MysteryEncounterOptionBuilder() - .withOptionPhase(async (scene: BattleScene) => { - // Leave encounter with no rewards or exp - leaveEncounterWithoutBattle(scene, true); - return true; - }) - .build()) + .build() + ) + .withOptionPhase(async (scene: BattleScene) => { + // Leave encounter with no rewards or exp + leaveEncounterWithoutBattle(scene, true); + return true; + }) .build(); diff --git a/src/data/mystery-encounters/encounters/department-store-sale.ts b/src/data/mystery-encounters/encounters/department-store-sale.ts index 38b599c5390..1d44c2b3af8 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale.ts @@ -8,7 +8,6 @@ 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"; export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBuilder .withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE) @@ -30,91 +29,83 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu ]) // .withHideIntroVisuals(false) .withSceneWaveRangeRequirement(10, 100) - .withOption(new MysteryEncounterOptionBuilder() - .withOptionPhase(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++; + .withOptionPhase(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++; + } - setEncounterExp(scene, scene.getParty().map(p => p.id), 300); - setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); - leaveEncounterWithoutBattle(scene); - }) - .build()) - .withOption(new MysteryEncounterOptionBuilder() - .withOptionPhase(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++; + setEncounterExp(scene, scene.getParty().map(p => p.id), 300); + setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); + leaveEncounterWithoutBattle(scene); + }) + .withOptionPhase(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); - }) - .build()) - .withOption(new MysteryEncounterOptionBuilder() - .withOptionPhase(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); + }) + .withOptionPhase(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); - }) - .build()) - .withOption(new MysteryEncounterOptionBuilder() - .withOptionPhase(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); + }) + .withOptionPhase(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); - }) - .build()) + setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); + leaveEncounterWithoutBattle(scene); + }) .build(); diff --git a/src/data/mystery-encounters/encounters/fight-or-flight.ts b/src/data/mystery-encounters/encounters/fight-or-flight.ts index e5f58cb7cda..506b450aa98 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight.ts @@ -23,7 +23,6 @@ import { Moves } from "#enums/moves"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; -import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { MoveRequirement } from "../mystery-encounter-requirements"; const validMovesForSteal = [ @@ -99,59 +98,53 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder return true; }) - .withOption(new MysteryEncounterOptionBuilder() - .withOptionPhase(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]); - }) - .build()) - .withOption(new MysteryEncounterOptionBuilder() - .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 }); + .withOptionPhase(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]); + }) + .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 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; + } - 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()) - .withOption(new MysteryEncounterOptionBuilder() - .withOptionPhase(async (scene: BattleScene) => { - // Leave encounter with no rewards or exp - leaveEncounterWithoutBattle(scene, true); - return true; - }) - .build()) + 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); + } + }) + .withOptionPhase(async (scene: BattleScene) => { + // Leave encounter with no rewards or exp + leaveEncounterWithoutBattle(scene, true); + return true; + }) .build(); diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers.ts b/src/data/mystery-encounters/encounters/mysterious-challengers.ts index df363e89c2d..880c7be4ca2 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers.ts @@ -12,7 +12,6 @@ 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 { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounterBuilder .withEncounterType(MysteryEncounterType.MYSTERIOUS_CHALLENGERS) @@ -96,55 +95,49 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter return true; }) - .withOption(new MysteryEncounterOptionBuilder() - .withOptionPhase(async (scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - // Spawn standard trainer battle with memory mushroom reward - const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; + .withOptionPhase(async (scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + // 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; - scene.executeWithSeedOffset(() => { - ret = initBattleWithEnemyConfig(scene, config); - }, scene.currentBattle.waveIndex * 10); - return ret; - }) - .build()) - .withOption(new MysteryEncounterOptionBuilder() - .withOptionPhase(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]; + // 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; + }) + .withOptionPhase(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.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; - scene.executeWithSeedOffset(() => { - ret = initBattleWithEnemyConfig(scene, config); - }, scene.currentBattle.waveIndex * 100); - return ret; - }) - .build()) - .withOption(new MysteryEncounterOptionBuilder() - .withOptionPhase(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]; + // 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; + }) + .withOptionPhase(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; + // 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; - 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 * 1000); + return ret; + }) .build(); diff --git a/src/data/mystery-encounters/encounters/mysterious-chest.ts b/src/data/mystery-encounters/encounters/mysterious-chest.ts index 9068c8521a5..5e572131562 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest.ts @@ -83,12 +83,11 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde }); } }) - .build()) - .withOption(new MysteryEncounterOptionBuilder() - .withOptionPhase(async (scene: BattleScene) => { - // Leave encounter with no rewards or exp - leaveEncounterWithoutBattle(scene, true); - return true; - }) - .build()) + .build() + ) + .withOptionPhase(async (scene: BattleScene) => { + // Leave encounter with no rewards or exp + leaveEncounterWithoutBattle(scene, true); + return true; + }) .build(); diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts index 1cdfdfa00e0..4c99bfb3d96 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer.ts @@ -116,6 +116,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui chosenPokemon.updateInfo(); }) .build()) + .withOption(new MysteryEncounterOptionBuilder() .withSceneRequirement(new MoneyRequirement(0, 5)) // Wave scaling multiplier of 2 for cost .withOptionPhase(async (scene: BattleScene) => { @@ -136,12 +137,11 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); leaveEncounterWithoutBattle(scene); }) - .build()) - .withOption(new MysteryEncounterOptionBuilder() - .withOptionPhase(async (scene: BattleScene) => { - // Leave encounter with no rewards or exp - leaveEncounterWithoutBattle(scene, true); - return true; - }) - .build()) + .build() + ) + .withOptionPhase(async (scene: BattleScene) => { + // Leave encounter with no rewards or exp + leaveEncounterWithoutBattle(scene, true); + return true; + }) .build(); diff --git a/src/data/mystery-encounters/encounters/sleeping-snorlax.ts b/src/data/mystery-encounters/encounters/sleeping-snorlax.ts index 9931ff1c16d..1a011bb05a0 100644 --- a/src/data/mystery-encounters/encounters/sleeping-snorlax.ts +++ b/src/data/mystery-encounters/encounters/sleeping-snorlax.ts @@ -54,49 +54,45 @@ export const SleepingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuilde encounter.enemyPartyConfigs = [config]; return true; }) - .withOption(new MysteryEncounterOptionBuilder() - .withOptionPhase(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]); - }) - .build()) - .withOption(new MysteryEncounterOptionBuilder() - .withOptionPhase(async (scene: BattleScene) => { - const instance = scene.currentBattle.mysteryEncounter; - let roll: integer; - scene.executeWithSeedOffset(() => { - roll = Utils.randSeedInt(16, 0); - }, scene.currentBattle.waveIndex); - console.log(roll); - 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]); + .withOptionPhase(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) => { + const instance = scene.currentBattle.mysteryEncounter; + let roll: integer; + scene.executeWithSeedOffset(() => { + roll = Utils.randSeedInt(16, 0); + }, scene.currentBattle.waveIndex); + console.log(roll); + 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]); - setEncounterRewards(scene, { guaranteedModifierTypeOptions: [new ModifierTypeOption(sitrus, 0)], 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); + setEncounterRewards(scene, { guaranteedModifierTypeOptions: [new ModifierTypeOption(sitrus, 0)], 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; } - - queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_2_good_result"); - leaveEncounterWithoutBattle(scene); + pokemon.updateInfo(true); } - }) - .build()) + + queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_2_good_result"); + leaveEncounterWithoutBattle(scene); + } + }) .withOption(new MysteryEncounterOptionBuilder() .withPrimaryPokemonRequirement(new MoveRequirement([Moves.PLUCK, Moves.COVET, Moves.KNOCK_OFF, Moves.THIEF, Moves.TRICK, Moves.SWITCHEROO])) .withOptionPhase(async (scene: BattleScene) => { @@ -105,5 +101,6 @@ export const SleepingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuilde queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_3_good_result"); leaveEncounterWithoutBattle(scene); }) - .build()) + .build() + ) .build(); diff --git a/src/data/mystery-encounters/encounters/training-session.ts b/src/data/mystery-encounters/encounters/training-session.ts index e6167171036..8030cbaf850 100644 --- a/src/data/mystery-encounters/encounters/training-session.ts +++ b/src/data/mystery-encounters/encounters/training-session.ts @@ -134,7 +134,8 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde return initBattleWithEnemyConfig(scene, config); }) - .build()) + .build() + ) .withOption(new MysteryEncounterOptionBuilder() .withPreOptionPhase(async (scene: BattleScene): Promise => { // Open menu for selecting pokemon and Nature @@ -190,7 +191,8 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde return initBattleWithEnemyConfig(scene, config); }) - .build()) + .build() + ) .withOption(new MysteryEncounterOptionBuilder() .withPreOptionPhase(async (scene: BattleScene): Promise => { // Open menu for selecting pokemon and ability to learn @@ -271,7 +273,8 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde return initBattleWithEnemyConfig(scene, config); }) - .build()) + .build() + ) .build(); function getEnemyConfig(scene: BattleScene, playerPokemon: PlayerPokemon, segments: number, modifiers: ModifiersHolder): EnemyPartyConfig { diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index 995b10f2714..5c42bfdd4ce 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -4,6 +4,9 @@ import BattleScene from "../../battle-scene"; import { EncounterPokemonRequirement, EncounterSceneRequirement } from "./mystery-encounter-requirements"; import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue"; + +export type OptionPhaseCallback = (scene: BattleScene) => Promise; + export default interface MysteryEncounterOption { requirements?: EncounterSceneRequirement[]; primaryPokemonRequirements?: EncounterPokemonRequirement[]; @@ -19,11 +22,11 @@ export default interface MysteryEncounterOption { dialogue?: OptionTextDisplay; // Executes before any following dialogue or business logic from option. Usually this will be for calculating dialogueTokens or performing scene/data updates - onPreOptionPhase?: (scene: BattleScene) => Promise; + onPreOptionPhase?: OptionPhaseCallback; // Business logic for option - onOptionPhase?: (scene: BattleScene) => Promise; + onOptionPhase?: OptionPhaseCallback; // Executes after the encounter is over. Usually this will be for calculating dialogueTokens or performing data updates - onPostOptionPhase?: (scene: BattleScene) => Promise; + onPostOptionPhase?: OptionPhaseCallback; } export default class MysteryEncounterOption implements MysteryEncounterOption { @@ -121,24 +124,24 @@ export class MysteryEncounterOptionBuilder implements Partial Promise; - onOptionPhase?: (scene: BattleScene) => Promise; - onPostOptionPhase?: (scene: BattleScene) => Promise; + onPreOptionPhase?: OptionPhaseCallback; + onOptionPhase?: OptionPhaseCallback; + onPostOptionPhase?: OptionPhaseCallback; withSceneRequirement(requirement: EncounterSceneRequirement): this & Required> { this.requirements.push(requirement); return Object.assign(this, { requirements: this.requirements }); } - withPreOptionPhase(onPreOptionPhase: (scene: BattleScene) => Promise): this & Required> { + withPreOptionPhase(onPreOptionPhase: OptionPhaseCallback): this & Required> { return Object.assign(this, { onPreOptionPhase: onPreOptionPhase }); } - withOptionPhase(onOptionPhase: (scene: BattleScene) => Promise): this & Required> { + withOptionPhase(onOptionPhase: OptionPhaseCallback): this & Required> { return Object.assign(this, { onOptionPhase: onOptionPhase }); } - withPostOptionPhase(onPostOptionPhase: (scene: BattleScene) => Promise): this & Required> { + withPostOptionPhase(onPostOptionPhase: OptionPhaseCallback): this & Required> { return Object.assign(this, { onPostOptionPhase: onPostOptionPhase }); } diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 0f2cd05870e..99e58b7e3a1 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -4,7 +4,7 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import MysteryEncounterDialogue, { allMysteryEncounterDialogue } from "./mystery-encounter-dialogue"; -import MysteryEncounterOption from "./mystery-encounter-option"; +import MysteryEncounterOption, { MysteryEncounterOptionBuilder, OptionPhaseCallback } from "./mystery-encounter-option"; import { EncounterPokemonRequirement, EncounterSceneRequirement, @@ -387,6 +387,17 @@ export class MysteryEncounterBuilder implements Partial { } } + /** + * Adds a streamlined option phase. + * Only use if no pre-/post-options or condtions necessary. + * + * @param callback - OptionPhaseCallback + * @returns + */ + withOptionPhase(callback: OptionPhaseCallback) { + return this.withOption(new MysteryEncounterOptionBuilder().withOptionPhase(callback).build()); + } + /** * Defines the sprites that will be shown on the enemy field when the encounter spawns * Can be one or more sprites, recommended not to exceed 4