pokerogue/src/data/mystery-encounters/encounters/field-trip-encounter.ts
ImperialSympathizer 1c87532e64
[Bugs/Balance] Fix various ME bugs and small balance adjustments (#4369)
* various bug fixes for MEs

* various bug fixes for MEs

* fix final isTransferable rename that was missed

* change Trainer's test vouchers for second option

* change unit test skips

* cut down excess ME track length and loop properly

* ME bug fix cleanup

* updating AI for Slumbering Snorlax ME, and small ME balance changes

* fix ts error

* fix bug type superfan dialogue discrepancy

* ME bug fixes PR feedback

* ME PR nits and fixes

* update naming convention of sprites

* ME balance changes and bug fixes

* fix tests

* fix An Offer You Can't Refuse ME requirements

* clean up post-battle logic for Breeder ME

* party size requirement cleanup

* clean up challenge requirements for disabling certain MEs

---------

Co-authored-by: ImperialSympathizer <imperialsympathizer@gmail.com>
2024-09-22 05:47:32 +02:00

236 lines
9.6 KiB
TypeScript

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/utils/encounter-phase-utils";
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 "#app/battle-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { Stat } from "#enums/stat";
import i18next from "i18next";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** i18n namespace for the encounter */
const namespace = "mysteryEncounter:fieldTrip";
/**
* Field Trip encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3794 | GitHub Issue #3794}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const FieldTripEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withIntroSpriteConfigs([
{
spriteKey: "preschooler_m",
fileRoot: "trainer",
hasShadow: true,
},
{
spriteKey: "field_trip_teacher",
fileRoot: "mystery-encounters",
hasShadow: true,
},
{
spriteKey: "preschooler_f",
fileRoot: "trainer",
hasShadow: true,
},
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
},
{
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}.speaker`,
},
])
.withAutoHideIntroVisuals(false)
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
secondOptionPrompt: `${namespace}.second_option_prompt`,
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
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
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.physical`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.PHYSICAL);
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_STAGE_BOOSTER, [Stat.ATK])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.DEF])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!,
];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
}
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove);
})
.build()
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
secondOptionPrompt: `${namespace}.second_option_prompt`,
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
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
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.special`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.SPECIAL);
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_STAGE_BOOSTER, [Stat.SPATK])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPDEF])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!,
];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
}
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove);
})
.build()
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
secondOptionPrompt: `${namespace}.second_option_prompt`,
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
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
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.status`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.STATUS);
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_STAGE_BOOSTER, [Stat.ACC])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(scene, modifierTypes.GREAT_BALL)!,
generateModifierTypeOption(scene, modifierTypes.IV_SCANNER)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!,
];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
}
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove);
})
.build()
)
.build();
function pokemonAndMoveChosen(scene: BattleScene, pokemon: PlayerPokemon, move: PokemonMove, correctMoveCategory: MoveCategory) {
const encounter = scene.currentBattle.mysteryEncounter!;
const correctMove = move.getMove().category === correctMoveCategory;
encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
encounter.setDialogueToken("move", move.getName());
if (!correctMove) {
encounter.selectedOption!.dialogue!.selected = [
{
text: `${namespace}.option.selected`,
},
{
text: `${namespace}.incorrect`,
speaker: `${namespace}.speaker`,
},
{
text: `${namespace}.incorrect_exp`,
},
];
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
} else {
encounter.selectedOption!.dialogue!.selected = [
{
text: `${namespace}.option.selected`,
},
{
text: `${namespace}.correct`,
speaker: `${namespace}.speaker`,
},
{
text: `${namespace}.correct_exp`,
},
];
setEncounterExp(scene, [pokemon.id], 100);
}
encounter.misc = {
correctMove: correctMove,
};
}