pokerogue/src/data/mystery-encounters/encounters/field-trip-encounter.ts
Sirz Benjie 48e911e03c
[Refactor] Remove circular deps 3 (#5959)
* Move game-mode to its own file

Reduces circular imports to 325

* Move battler-index to own file

Reduces circular deps to 314

* Move trainer-variant to own file

Reduces circ deps to 313

* Move enums in pokemon to their own file

* Move arena-tag-type to its own file

* Move pokemon-moves to its own file

* Move command to own file

* Move learnMoveType to own file

* Move form change item to own file

* Move battlerTagLapseType to own file

* Move anim enums to own shared file

* Move enums out of challenges

* Move species form change triggers to own file

Reduces circ imports to 291

* Update test importing pokemon move

* Replace move attribute imports with string names

* Untangle circular deps from game data

* Fix missing string call in switch summon phase

* Apply kev's suggestions from code review

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

* Ensure ChargeMove's is method calls super

* Use InstanceType for proper narrowing

* Apply kev's suggestions from code review

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

---------

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2025-06-09 16:24:13 -07:00

255 lines
9.3 KiB
TypeScript

import { MoveCategory } from "#enums/MoveCategory";
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 type { PlayerPokemon } from "#app/field/pokemon";
import type { PokemonMove } from "#app/data/moves/pokemon-move";
import { modifierTypes } from "#app/modifier/modifier-type";
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { globalScene } from "#app/global-scene";
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { 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/constants";
/** i18n namespace for the encounter */
const namespace = "mysteryEncounters/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[0], 100)
.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)
.setLocalizationKey(`${namespace}`)
.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 (): Promise<boolean> => {
const encounter = globalScene.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(pokemon, move, MoveCategory.PHYSICAL);
return true;
},
};
return option;
});
};
return selectPokemonForOption(onPokemonSelected);
})
.withOptionPhase(async () => {
const encounter = globalScene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) {
const modifiers = [
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.ATK])!,
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.DEF])!,
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(modifierTypes.DIRE_HIT)!,
generateModifierTypeOption(modifierTypes.RARER_CANDY)!,
];
setEncounterRewards({
guaranteedModifierTypeOptions: modifiers,
fillRemaining: false,
});
}
leaveEncounterWithoutBattle(!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 (): Promise<boolean> => {
const encounter = globalScene.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(pokemon, move, MoveCategory.SPECIAL);
return true;
},
};
return option;
});
};
return selectPokemonForOption(onPokemonSelected);
})
.withOptionPhase(async () => {
const encounter = globalScene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) {
const modifiers = [
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPATK])!,
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPDEF])!,
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(modifierTypes.DIRE_HIT)!,
generateModifierTypeOption(modifierTypes.RARER_CANDY)!,
];
setEncounterRewards({
guaranteedModifierTypeOptions: modifiers,
fillRemaining: false,
});
}
leaveEncounterWithoutBattle(!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 (): Promise<boolean> => {
const encounter = globalScene.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(pokemon, move, MoveCategory.STATUS);
return true;
},
};
return option;
});
};
return selectPokemonForOption(onPokemonSelected);
})
.withOptionPhase(async () => {
const encounter = globalScene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) {
const modifiers = [
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.ACC])!,
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(modifierTypes.GREAT_BALL)!,
generateModifierTypeOption(modifierTypes.IV_SCANNER)!,
generateModifierTypeOption(modifierTypes.RARER_CANDY)!,
];
setEncounterRewards({
guaranteedModifierTypeOptions: modifiers,
fillRemaining: false,
});
}
leaveEncounterWithoutBattle(!encounter.misc.correctMove);
})
.build(),
)
.build();
function pokemonAndMoveChosen(pokemon: PlayerPokemon, move: PokemonMove, correctMoveCategory: MoveCategory) {
const encounter = globalScene.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(
globalScene.getPlayerParty().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([pokemon.id], 100);
}
encounter.misc = {
correctMove: correctMove,
};
}