add `MysterEncounterBuilder.withOptionPhase()`

This commit is contained in:
Felix Staud 2024-07-09 22:34:53 -07:00 committed by ImperialSympathizer
parent adf52bf604
commit a8be171305
10 changed files with 246 additions and 257 deletions

View File

@ -123,12 +123,11 @@ export const DarkDealEncounter: MysteryEncounter = MysteryEncounterBuilder
}; };
return initBattleWithEnemyConfig(scene, config); return initBattleWithEnemyConfig(scene, config);
}) })
.build()) .build()
.withOption(new MysteryEncounterOptionBuilder() )
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(scene, true);
return true; return true;
}) })
.build())
.build(); .build();

View File

@ -8,7 +8,6 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import BattleScene from "../../../battle-scene"; 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 DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBuilder export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBuilder
.withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE) .withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE)
@ -30,91 +29,83 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu
]) ])
// .withHideIntroVisuals(false) // .withHideIntroVisuals(false)
.withSceneWaveRangeRequirement(10, 100) .withSceneWaveRangeRequirement(10, 100)
.withOption(new MysteryEncounterOptionBuilder() .withOptionPhase(async (scene: BattleScene) => {
.withOptionPhase(async (scene: BattleScene) => { // Choose TMs
// Choose TMs const modifiers = [];
const modifiers = []; let i = 0;
let i = 0; while (i < 4) {
while (i < 4) { // 2/2/1 weight on TM rarity
// 2/2/1 weight on TM rarity const roll = randSeedInt(5);
const roll = randSeedInt(5); if (roll < 2) {
if (roll < 2) { modifiers.push(modifierTypes.TM_COMMON);
modifiers.push(modifierTypes.TM_COMMON); } else if (roll < 4) {
} else if (roll < 4) { modifiers.push(modifierTypes.TM_GREAT);
modifiers.push(modifierTypes.TM_GREAT); } else {
} else { modifiers.push(modifierTypes.TM_ULTRA);
modifiers.push(modifierTypes.TM_ULTRA);
}
i++;
} }
i++;
}
setEncounterExp(scene, scene.getParty().map(p => p.id), 300); setEncounterExp(scene, scene.getParty().map(p => p.id), 300);
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
}) })
.build()) .withOptionPhase(async (scene: BattleScene) => {
.withOption(new MysteryEncounterOptionBuilder() // Choose Vitamins
.withOptionPhase(async (scene: BattleScene) => { const modifiers = [];
// Choose Vitamins let i = 0;
const modifiers = []; while (i < 3) {
let i = 0; // 2/1 weight on base stat booster vs PP Up
while (i < 3) { const roll = randSeedInt(3);
// 2/1 weight on base stat booster vs PP Up if (roll === 0) {
const roll = randSeedInt(3); modifiers.push(modifierTypes.PP_UP);
if (roll === 0) { } else {
modifiers.push(modifierTypes.PP_UP); modifiers.push(modifierTypes.BASE_STAT_BOOSTER);
} else {
modifiers.push(modifierTypes.BASE_STAT_BOOSTER);
}
i++;
} }
i++;
}
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
}) })
.build()) .withOptionPhase(async (scene: BattleScene) => {
.withOption(new MysteryEncounterOptionBuilder() // Choose X Items
.withOptionPhase(async (scene: BattleScene) => { const modifiers = [];
// Choose X Items let i = 0;
const modifiers = []; while (i < 5) {
let i = 0; // 4/1 weight on base stat booster vs Dire Hit
while (i < 5) { const roll = randSeedInt(5);
// 4/1 weight on base stat booster vs Dire Hit if (roll === 0) {
const roll = randSeedInt(5); modifiers.push(modifierTypes.DIRE_HIT);
if (roll === 0) { } else {
modifiers.push(modifierTypes.DIRE_HIT); modifiers.push(modifierTypes.TEMP_STAT_BOOSTER);
} else {
modifiers.push(modifierTypes.TEMP_STAT_BOOSTER);
}
i++;
} }
i++;
}
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
}) })
.build()) .withOptionPhase(async (scene: BattleScene) => {
.withOption(new MysteryEncounterOptionBuilder() // Choose Pokeballs
.withOptionPhase(async (scene: BattleScene) => { const modifiers = [];
// Choose Pokeballs let i = 0;
const modifiers = []; while (i < 4) {
let i = 0; // 10/30/20/5 weight on pokeballs
while (i < 4) { const roll = randSeedInt(65);
// 10/30/20/5 weight on pokeballs if (roll < 10) {
const roll = randSeedInt(65); modifiers.push(modifierTypes.POKEBALL);
if (roll < 10) { } else if (roll < 40) {
modifiers.push(modifierTypes.POKEBALL); modifiers.push(modifierTypes.GREAT_BALL);
} else if (roll < 40) { } else if (roll < 60) {
modifiers.push(modifierTypes.GREAT_BALL); modifiers.push(modifierTypes.ULTRA_BALL);
} else if (roll < 60) { } else {
modifiers.push(modifierTypes.ULTRA_BALL); modifiers.push(modifierTypes.ROGUE_BALL);
} else {
modifiers.push(modifierTypes.ROGUE_BALL);
}
i++;
} }
i++;
}
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
}) })
.build())
.build(); .build();

View File

@ -23,7 +23,6 @@ import { Moves } from "#enums/moves";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "../../../battle-scene"; 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";
import { MoveRequirement } from "../mystery-encounter-requirements"; import { MoveRequirement } from "../mystery-encounter-requirements";
const validMovesForSteal = [ const validMovesForSteal = [
@ -99,59 +98,53 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder
return true; return true;
}) })
.withOption(new MysteryEncounterOptionBuilder() .withOptionPhase(async (scene: BattleScene) => {
.withOptionPhase(async (scene: BattleScene) => { // Pick battle
// Pick battle const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption;
const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption; setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false });
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false }); await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]);
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]); })
}) .withOptionPhase(async (scene: BattleScene) => {
.build()) // Pick steal
.withOption(new MysteryEncounterOptionBuilder() const encounter = scene.currentBattle.mysteryEncounter;
.withOptionPhase(async (scene: BattleScene) => { const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption;
// Pick steal setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false });
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 // If player has a stealing move, they succeed automatically
const moveRequirement = new MoveRequirement(validMovesForSteal); const moveRequirement = new MoveRequirement(validMovesForSteal);
const validPokemon = moveRequirement.queryParty(scene.getParty()); const validPokemon = moveRequirement.queryParty(scene.getParty());
if (validPokemon?.length > 0) { if (validPokemon?.length > 0) {
// Use first valid pokemon to execute the theivery // Use first valid pokemon to execute the theivery
const pokemon = validPokemon[0]; const pokemon = validPokemon[0];
encounter.setDialogueToken("thiefPokemon", pokemon.name); encounter.setDialogueToken("thiefPokemon", pokemon.name);
encounter.setDialogueToken(...moveRequirement.getDialogueToken(scene, pokemon)); encounter.setDialogueToken(...moveRequirement.getDialogueToken(scene, pokemon));
await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_steal_result"); await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_steal_result");
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
return; return;
} }
const roll = randSeedInt(16); const roll = randSeedInt(16);
if (roll > 6) { if (roll > 6) {
// Noticed and attacked by boss, gets +1 to all stats at start of fight (62.5%) // Noticed and attacked by boss, gets +1 to all stats at start of fight (62.5%)
const config = scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]; const config = scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0];
config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON]; config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON];
config.pokemonConfigs[0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { config.pokemonConfigs[0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken("enemyPokemon", pokemon.name); pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken("enemyPokemon", pokemon.name);
queueEncounterMessage(pokemon.scene, "mysteryEncounter:fight_or_flight_boss_enraged"); 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)); 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 showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_bad_result");
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(scene, config);
} else { } else {
// Steal item (37.5%) // Steal item (37.5%)
// Display result message then proceed to rewards // Display result message then proceed to rewards
await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_good_result"); await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_good_result");
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
} }
}) })
.build()) .withOptionPhase(async (scene: BattleScene) => {
.withOption(new MysteryEncounterOptionBuilder() // Leave encounter with no rewards or exp
.withOptionPhase(async (scene: BattleScene) => { leaveEncounterWithoutBattle(scene, true);
// Leave encounter with no rewards or exp return true;
leaveEncounterWithoutBattle(scene, true); })
return true;
})
.build())
.build(); .build();

View File

@ -12,7 +12,6 @@ import { PartyMemberStrength } from "#enums/party-member-strength";
import BattleScene from "../../../battle-scene"; import BattleScene from "../../../battle-scene";
import * as Utils from "../../../utils"; import * as Utils from "../../../utils";
import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter";
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounterBuilder export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounterBuilder
.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHALLENGERS) .withEncounterType(MysteryEncounterType.MYSTERIOUS_CHALLENGERS)
@ -96,55 +95,49 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
return true; return true;
}) })
.withOption(new MysteryEncounterOptionBuilder() .withOptionPhase(async (scene: BattleScene) => {
.withOptionPhase(async (scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter;
const encounter = scene.currentBattle.mysteryEncounter; // Spawn standard trainer battle with memory mushroom reward
// Spawn standard trainer battle with memory mushroom reward const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
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 // Seed offsets to remove possibility of different trainers having exact same teams
let ret; let ret;
scene.executeWithSeedOffset(() => { scene.executeWithSeedOffset(() => {
ret = initBattleWithEnemyConfig(scene, config); ret = initBattleWithEnemyConfig(scene, config);
}, scene.currentBattle.waveIndex * 10); }, scene.currentBattle.waveIndex * 10);
return ret; return ret;
}) })
.build()) .withOptionPhase(async (scene: BattleScene) => {
.withOption(new MysteryEncounterOptionBuilder() const encounter = scene.currentBattle.mysteryEncounter;
.withOptionPhase(async (scene: BattleScene) => { // Spawn hard fight with ULTRA/GREAT reward (can improve with luck)
const encounter = scene.currentBattle.mysteryEncounter; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1];
// 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 // Seed offsets to remove possibility of different trainers having exact same teams
let ret; let ret;
scene.executeWithSeedOffset(() => { scene.executeWithSeedOffset(() => {
ret = initBattleWithEnemyConfig(scene, config); ret = initBattleWithEnemyConfig(scene, config);
}, scene.currentBattle.waveIndex * 100); }, scene.currentBattle.waveIndex * 100);
return ret; return ret;
}) })
.build()) .withOptionPhase(async (scene: BattleScene) => {
.withOption(new MysteryEncounterOptionBuilder() const encounter = scene.currentBattle.mysteryEncounter;
.withOptionPhase(async (scene: BattleScene) => { // Spawn brutal fight with ROGUE/ULTRA/GREAT reward (can improve with luck)
const encounter = scene.currentBattle.mysteryEncounter; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[2];
// 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 // To avoid player level snowballing from picking this option
encounter.expMultiplier = 0.9; 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 // Seed offsets to remove possibility of different trainers having exact same teams
let ret; let ret;
scene.executeWithSeedOffset(() => { scene.executeWithSeedOffset(() => {
ret = initBattleWithEnemyConfig(scene, config); ret = initBattleWithEnemyConfig(scene, config);
}, scene.currentBattle.waveIndex * 1000); }, scene.currentBattle.waveIndex * 1000);
return ret; return ret;
}) })
.build())
.build(); .build();

View File

@ -83,12 +83,11 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde
}); });
} }
}) })
.build()) .build()
.withOption(new MysteryEncounterOptionBuilder() )
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(scene, true);
return true; return true;
}) })
.build())
.build(); .build();

View File

@ -116,6 +116,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui
chosenPokemon.updateInfo(); chosenPokemon.updateInfo();
}) })
.build()) .build())
.withOption(new MysteryEncounterOptionBuilder() .withOption(new MysteryEncounterOptionBuilder()
.withSceneRequirement(new MoneyRequirement(0, 5)) // Wave scaling multiplier of 2 for cost .withSceneRequirement(new MoneyRequirement(0, 5)) // Wave scaling multiplier of 2 for cost
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
@ -136,12 +137,11 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false }); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
}) })
.build()) .build()
.withOption(new MysteryEncounterOptionBuilder() )
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(scene, true);
return true; return true;
}) })
.build())
.build(); .build();

View File

@ -54,49 +54,45 @@ export const SleepingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuilde
encounter.enemyPartyConfigs = [config]; encounter.enemyPartyConfigs = [config];
return true; return true;
}) })
.withOption(new MysteryEncounterOptionBuilder() .withOptionPhase(async (scene: BattleScene) => {
.withOptionPhase(async (scene: BattleScene) => { // Pick battle
// Pick battle // TODO: do we want special rewards for this?
// TODO: do we want special rewards for this? // setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: true});
// setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: true}); await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]);
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]); })
}) .withOptionPhase(async (scene: BattleScene) => {
.build()) const instance = scene.currentBattle.mysteryEncounter;
.withOption(new MysteryEncounterOptionBuilder() let roll: integer;
.withOptionPhase(async (scene: BattleScene) => { scene.executeWithSeedOffset(() => {
const instance = scene.currentBattle.mysteryEncounter; roll = Utils.randSeedInt(16, 0);
let roll: integer; }, scene.currentBattle.waveIndex);
scene.executeWithSeedOffset(() => { console.log(roll);
roll = Utils.randSeedInt(16, 0); if (roll > 4) {
}, scene.currentBattle.waveIndex); // Fall asleep and get a sitrus berry (75%)
console.log(roll); const p = instance.primaryPokemon;
if (roll > 4) { p.status = new Status(StatusEffect.SLEEP, 0, 3);
// Fall asleep and get a sitrus berry (75%) p.updateInfo(true);
const p = instance.primaryPokemon; // const sitrus = (modifierTypes.BERRY?.() as ModifierTypeGenerator).generateType(scene.getParty(), [BerryType.SITRUS]);
p.status = new Status(StatusEffect.SLEEP, 0, 3); const sitrus = generateModifierType(scene, modifierTypes.BERRY, [BerryType.SITRUS]);
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 }); setEncounterRewards(scene, { guaranteedModifierTypeOptions: [new ModifierTypeOption(sitrus, 0)], fillRemaining: false });
queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_2_bad_result"); queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_2_bad_result");
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
} else { } else {
// Heal to full (25%) // Heal to full (25%)
for (const pokemon of scene.getParty()) { for (const pokemon of scene.getParty()) {
pokemon.hp = pokemon.getMaxHp(); pokemon.hp = pokemon.getMaxHp();
pokemon.resetStatus(); pokemon.resetStatus();
for (const move of pokemon.moveset) { for (const move of pokemon.moveset) {
move.ppUsed = 0; move.ppUsed = 0;
}
pokemon.updateInfo(true);
} }
pokemon.updateInfo(true);
queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_2_good_result");
leaveEncounterWithoutBattle(scene);
} }
})
.build()) queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_2_good_result");
leaveEncounterWithoutBattle(scene);
}
})
.withOption(new MysteryEncounterOptionBuilder() .withOption(new MysteryEncounterOptionBuilder()
.withPrimaryPokemonRequirement(new MoveRequirement([Moves.PLUCK, Moves.COVET, Moves.KNOCK_OFF, Moves.THIEF, Moves.TRICK, Moves.SWITCHEROO])) .withPrimaryPokemonRequirement(new MoveRequirement([Moves.PLUCK, Moves.COVET, Moves.KNOCK_OFF, Moves.THIEF, Moves.TRICK, Moves.SWITCHEROO]))
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
@ -105,5 +101,6 @@ export const SleepingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuilde
queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_3_good_result"); queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_3_good_result");
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
}) })
.build()) .build()
)
.build(); .build();

View File

@ -134,7 +134,8 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde
return initBattleWithEnemyConfig(scene, config); return initBattleWithEnemyConfig(scene, config);
}) })
.build()) .build()
)
.withOption(new MysteryEncounterOptionBuilder() .withOption(new MysteryEncounterOptionBuilder()
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
// Open menu for selecting pokemon and Nature // Open menu for selecting pokemon and Nature
@ -190,7 +191,8 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde
return initBattleWithEnemyConfig(scene, config); return initBattleWithEnemyConfig(scene, config);
}) })
.build()) .build()
)
.withOption(new MysteryEncounterOptionBuilder() .withOption(new MysteryEncounterOptionBuilder()
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
// Open menu for selecting pokemon and ability to learn // Open menu for selecting pokemon and ability to learn
@ -271,7 +273,8 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde
return initBattleWithEnemyConfig(scene, config); return initBattleWithEnemyConfig(scene, config);
}) })
.build()) .build()
)
.build(); .build();
function getEnemyConfig(scene: BattleScene, playerPokemon: PlayerPokemon, segments: number, modifiers: ModifiersHolder): EnemyPartyConfig { function getEnemyConfig(scene: BattleScene, playerPokemon: PlayerPokemon, segments: number, modifiers: ModifiersHolder): EnemyPartyConfig {

View File

@ -4,6 +4,9 @@ import BattleScene from "../../battle-scene";
import { EncounterPokemonRequirement, EncounterSceneRequirement } from "./mystery-encounter-requirements"; import { EncounterPokemonRequirement, EncounterSceneRequirement } from "./mystery-encounter-requirements";
import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue"; import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue";
export type OptionPhaseCallback = (scene: BattleScene) => Promise<void | boolean>;
export default interface MysteryEncounterOption { export default interface MysteryEncounterOption {
requirements?: EncounterSceneRequirement[]; requirements?: EncounterSceneRequirement[];
primaryPokemonRequirements?: EncounterPokemonRequirement[]; primaryPokemonRequirements?: EncounterPokemonRequirement[];
@ -19,11 +22,11 @@ export default interface MysteryEncounterOption {
dialogue?: OptionTextDisplay; dialogue?: OptionTextDisplay;
// Executes before any following dialogue or business logic from option. Usually this will be for calculating dialogueTokens or performing scene/data updates // 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<void | boolean>; onPreOptionPhase?: OptionPhaseCallback;
// Business logic for option // Business logic for option
onOptionPhase?: (scene: BattleScene) => Promise<void | boolean>; onOptionPhase?: OptionPhaseCallback;
// Executes after the encounter is over. Usually this will be for calculating dialogueTokens or performing data updates // Executes after the encounter is over. Usually this will be for calculating dialogueTokens or performing data updates
onPostOptionPhase?: (scene: BattleScene) => Promise<void | boolean>; onPostOptionPhase?: OptionPhaseCallback;
} }
export default class MysteryEncounterOption implements MysteryEncounterOption { export default class MysteryEncounterOption implements MysteryEncounterOption {
@ -121,24 +124,24 @@ export class MysteryEncounterOptionBuilder implements Partial<MysteryEncounterOp
primaryPokemonRequirements?: EncounterPokemonRequirement[] = []; primaryPokemonRequirements?: EncounterPokemonRequirement[] = [];
secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = []; secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = [];
excludePrimaryFromSecondaryRequirements?: boolean; excludePrimaryFromSecondaryRequirements?: boolean;
onPreOptionPhase?: (scene: BattleScene) => Promise<void | boolean>; onPreOptionPhase?: OptionPhaseCallback;
onOptionPhase?: (scene: BattleScene) => Promise<void | boolean>; onOptionPhase?: OptionPhaseCallback;
onPostOptionPhase?: (scene: BattleScene) => Promise<void | boolean>; onPostOptionPhase?: OptionPhaseCallback;
withSceneRequirement(requirement: EncounterSceneRequirement): this & Required<Pick<MysteryEncounterOption, "requirements">> { withSceneRequirement(requirement: EncounterSceneRequirement): this & Required<Pick<MysteryEncounterOption, "requirements">> {
this.requirements.push(requirement); this.requirements.push(requirement);
return Object.assign(this, { requirements: this.requirements }); return Object.assign(this, { requirements: this.requirements });
} }
withPreOptionPhase(onPreOptionPhase: (scene: BattleScene) => Promise<void | boolean>): this & Required<Pick<MysteryEncounterOption, "onPreOptionPhase">> { withPreOptionPhase(onPreOptionPhase: OptionPhaseCallback): this & Required<Pick<MysteryEncounterOption, "onPreOptionPhase">> {
return Object.assign(this, { onPreOptionPhase: onPreOptionPhase }); return Object.assign(this, { onPreOptionPhase: onPreOptionPhase });
} }
withOptionPhase(onOptionPhase: (scene: BattleScene) => Promise<void | boolean>): this & Required<Pick<MysteryEncounterOption, "onOptionPhase">> { withOptionPhase(onOptionPhase: OptionPhaseCallback): this & Required<Pick<MysteryEncounterOption, "onOptionPhase">> {
return Object.assign(this, { onOptionPhase: onOptionPhase }); return Object.assign(this, { onOptionPhase: onOptionPhase });
} }
withPostOptionPhase(onPostOptionPhase: (scene: BattleScene) => Promise<void | boolean>): this & Required<Pick<MysteryEncounterOption, "onPostOptionPhase">> { withPostOptionPhase(onPostOptionPhase: OptionPhaseCallback): this & Required<Pick<MysteryEncounterOption, "onPostOptionPhase">> {
return Object.assign(this, { onPostOptionPhase: onPostOptionPhase }); return Object.assign(this, { onPostOptionPhase: onPostOptionPhase });
} }

View File

@ -4,7 +4,7 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import MysteryEncounterDialogue, { import MysteryEncounterDialogue, {
allMysteryEncounterDialogue allMysteryEncounterDialogue
} from "./mystery-encounter-dialogue"; } from "./mystery-encounter-dialogue";
import MysteryEncounterOption from "./mystery-encounter-option"; import MysteryEncounterOption, { MysteryEncounterOptionBuilder, OptionPhaseCallback } from "./mystery-encounter-option";
import { import {
EncounterPokemonRequirement, EncounterPokemonRequirement,
EncounterSceneRequirement, EncounterSceneRequirement,
@ -387,6 +387,17 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
} }
} }
/**
* 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 * 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 * Can be one or more sprites, recommended not to exceed 4