Merge branch 'beta' into dancer-fix

This commit is contained in:
NightKev 2025-06-06 16:50:38 -07:00 committed by GitHub
commit 21f58d6031
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
108 changed files with 773 additions and 401 deletions

View File

@ -12,3 +12,8 @@ post-merge:
commands: commands:
update-submodules: update-submodules:
run: git submodule update --init --recursive run: git submodule update --init --recursive
post-checkout:
commands:
update-submodules:
run: git submodule update --init --recursive

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

286
src/@types/phase-types.ts Normal file
View File

@ -0,0 +1,286 @@
import type { MoveAnim } from "#app/data/battle-anims";
import type { AddEnemyBuffModifierPhase } from "#app/phases/add-enemy-buff-modifier-phase";
import type { AttemptCapturePhase } from "#app/phases/attempt-capture-phase";
import type { AttemptRunPhase } from "#app/phases/attempt-run-phase";
import type { BattleEndPhase } from "#app/phases/battle-end-phase";
import type { BerryPhase } from "#app/phases/berry-phase";
import type { CheckStatusEffectPhase } from "#app/phases/check-status-effect-phase";
import type { CheckSwitchPhase } from "#app/phases/check-switch-phase";
import type { CommandPhase } from "#app/phases/command-phase";
import type { CommonAnimPhase } from "#app/phases/common-anim-phase";
import type { DamageAnimPhase } from "#app/phases/damage-anim-phase";
import type { EggHatchPhase } from "#app/phases/egg-hatch-phase";
import type { EggLapsePhase } from "#app/phases/egg-lapse-phase";
import type { EggSummaryPhase } from "#app/phases/egg-summary-phase";
import type { EncounterPhase } from "#app/phases/encounter-phase";
import type { EndCardPhase } from "#app/phases/end-card-phase";
import type { EndEvolutionPhase } from "#app/phases/end-evolution-phase";
import type { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
import type { EvolutionPhase } from "#app/phases/evolution-phase";
import type { ExpPhase } from "#app/phases/exp-phase";
import type { FaintPhase } from "#app/phases/faint-phase";
import type { FormChangePhase } from "#app/phases/form-change-phase";
import type { GameOverModifierRewardPhase } from "#app/phases/game-over-modifier-reward-phase";
import type { GameOverPhase } from "#app/phases/game-over-phase";
import type { HideAbilityPhase } from "#app/phases/hide-ability-phase";
import type { HidePartyExpBarPhase } from "#app/phases/hide-party-exp-bar-phase";
import type { LearnMovePhase } from "#app/phases/learn-move-phase";
import type { LevelCapPhase } from "#app/phases/level-cap-phase";
import type { LevelUpPhase } from "#app/phases/level-up-phase";
import type { LoadMoveAnimPhase } from "#app/phases/load-move-anim-phase";
import type { LoginPhase } from "#app/phases/login-phase";
import type { MessagePhase } from "#app/phases/message-phase";
import type { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import type { MoneyRewardPhase } from "#app/phases/money-reward-phase";
import type { MoveAnimPhase } from "#app/phases/move-anim-phase";
import type { MoveChargePhase } from "#app/phases/move-charge-phase";
import type { MoveEffectPhase } from "#app/phases/move-effect-phase";
import type { MoveEndPhase } from "#app/phases/move-end-phase";
import type { MoveHeaderPhase } from "#app/phases/move-header-phase";
import type { MovePhase } from "#app/phases/move-phase";
import type {
MysteryEncounterPhase,
MysteryEncounterOptionSelectedPhase,
MysteryEncounterBattlePhase,
MysteryEncounterRewardsPhase,
PostMysteryEncounterPhase,
MysteryEncounterBattleStartCleanupPhase,
} from "#app/phases/mystery-encounter-phases";
import type { NewBattlePhase } from "#app/phases/new-battle-phase";
import type { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase";
import type { NextEncounterPhase } from "#app/phases/next-encounter-phase";
import type { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase";
import type { PartyExpPhase } from "#app/phases/party-exp-phase";
import type { PartyHealPhase } from "#app/phases/party-heal-phase";
import type { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase";
import type { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import type { PokemonTransformPhase } from "#app/phases/pokemon-transform-phase";
import type { PostGameOverPhase } from "#app/phases/post-game-over-phase";
import type { PostSummonPhase } from "#app/phases/post-summon-phase";
import type { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase";
import type { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase";
import type { ReloadSessionPhase } from "#app/phases/reload-session-phase";
import type { ResetStatusPhase } from "#app/phases/reset-status-phase";
import type { ReturnPhase } from "#app/phases/return-phase";
import type { RevivalBlessingPhase } from "#app/phases/revival-blessing-phase";
import type { RibbonModifierRewardPhase } from "#app/phases/ribbon-modifier-reward-phase";
import type { ScanIvsPhase } from "#app/phases/scan-ivs-phase";
import type { SelectBiomePhase } from "#app/phases/select-biome-phase";
import type { SelectChallengePhase } from "#app/phases/select-challenge-phase";
import type { SelectGenderPhase } from "#app/phases/select-gender-phase";
import type { SelectModifierPhase } from "#app/phases/select-modifier-phase";
import type { SelectStarterPhase } from "#app/phases/select-starter-phase";
import type { SelectTargetPhase } from "#app/phases/select-target-phase";
import type { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase";
import type { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import type { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
import type { ShowTrainerPhase } from "#app/phases/show-trainer-phase";
import type { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import type { SummonMissingPhase } from "#app/phases/summon-missing-phase";
import type { SummonPhase } from "#app/phases/summon-phase";
import type { SwitchBiomePhase } from "#app/phases/switch-biome-phase";
import type { SwitchPhase } from "#app/phases/switch-phase";
import type { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
import type { TeraPhase } from "#app/phases/tera-phase";
import type { TitlePhase } from "#app/phases/title-phase";
import type { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase";
import type { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase";
import type { TurnEndPhase } from "#app/phases/turn-end-phase";
import type { TurnInitPhase } from "#app/phases/turn-init-phase";
import type { TurnStartPhase } from "#app/phases/turn-start-phase";
import type { UnavailablePhase } from "#app/phases/unavailable-phase";
import type { UnlockPhase } from "#app/phases/unlock-phase";
import type { VictoryPhase } from "#app/phases/victory-phase";
import type { WeatherEffectPhase } from "#app/phases/weather-effect-phase";
export type PhaseClass =
| typeof AddEnemyBuffModifierPhase
| typeof AttemptCapturePhase
| typeof AttemptRunPhase
| typeof BattleEndPhase
| typeof BerryPhase
| typeof CheckStatusEffectPhase
| typeof CheckSwitchPhase
| typeof CommandPhase
| typeof CommonAnimPhase
| typeof DamageAnimPhase
| typeof EggHatchPhase
| typeof EggLapsePhase
| typeof EggSummaryPhase
| typeof EncounterPhase
| typeof EndCardPhase
| typeof EndEvolutionPhase
| typeof EnemyCommandPhase
| typeof EvolutionPhase
| typeof FormChangePhase
| typeof ExpPhase
| typeof FaintPhase
| typeof FormChangePhase
| typeof GameOverPhase
| typeof GameOverModifierRewardPhase
| typeof HideAbilityPhase
| typeof HidePartyExpBarPhase
| typeof LearnMovePhase
| typeof LevelUpPhase
| typeof LevelCapPhase
| typeof LoadMoveAnimPhase
| typeof LoginPhase
| typeof MessagePhase
| typeof ModifierRewardPhase
| typeof MoneyRewardPhase
| typeof MoveAnimPhase
| typeof MoveChargePhase
| typeof MoveEffectPhase
| typeof MoveEndPhase
| typeof MoveHeaderPhase
| typeof MovePhase
| typeof MysteryEncounterPhase
| typeof MysteryEncounterOptionSelectedPhase
| typeof MysteryEncounterBattlePhase
| typeof MysteryEncounterRewardsPhase
| typeof MysteryEncounterBattleStartCleanupPhase
| typeof MysteryEncounterRewardsPhase
| typeof PostMysteryEncounterPhase
| typeof NewBattlePhase
| typeof NewBiomeEncounterPhase
| typeof NextEncounterPhase
| typeof ObtainStatusEffectPhase
| typeof PartyExpPhase
| typeof PartyHealPhase
| typeof PokemonAnimPhase
| typeof PokemonHealPhase
| typeof PokemonTransformPhase
| typeof PostGameOverPhase
| typeof PostSummonPhase
| typeof PostTurnStatusEffectPhase
| typeof QuietFormChangePhase
| typeof ReloadSessionPhase
| typeof ResetStatusPhase
| typeof ReturnPhase
| typeof RevivalBlessingPhase
| typeof RibbonModifierRewardPhase
| typeof ScanIvsPhase
| typeof SelectBiomePhase
| typeof SelectChallengePhase
| typeof SelectGenderPhase
| typeof SelectModifierPhase
| typeof SelectStarterPhase
| typeof SelectTargetPhase
| typeof ShinySparklePhase
| typeof ShowAbilityPhase
| typeof ShowTrainerPhase
| typeof ShowPartyExpBarPhase
| typeof StatStageChangePhase
| typeof SummonMissingPhase
| typeof SummonPhase
| typeof SwitchBiomePhase
| typeof SwitchPhase
| typeof SwitchSummonPhase
| typeof TeraPhase
| typeof TitlePhase
| typeof ToggleDoublePositionPhase
| typeof TrainerVictoryPhase
| typeof TurnEndPhase
| typeof TurnInitPhase
| typeof TurnStartPhase
| typeof UnavailablePhase
| typeof UnlockPhase
| typeof VictoryPhase
| typeof WeatherEffectPhase;
/** Typescript map used to map a string phase to the actual phase type */
export type PhaseMap = {
AddEnemyBuffModifierPhase: AddEnemyBuffModifierPhase;
AttemptCapturePhase: AttemptCapturePhase;
AttemptRunPhase: AttemptRunPhase;
BattleEndPhase: BattleEndPhase;
BerryPhase: BerryPhase;
CheckStatusEffectPhase: CheckStatusEffectPhase;
CheckSwitchPhase: CheckSwitchPhase;
CommandPhase: CommandPhase;
CommonAnimPhase: CommonAnimPhase;
DamageAnimPhase: DamageAnimPhase;
EggHatchPhase: EggHatchPhase;
EggLapsePhase: EggLapsePhase;
EggSummaryPhase: EggSummaryPhase;
EncounterPhase: EncounterPhase;
EndCardPhase: EndCardPhase;
EndEvolutionPhase: EndEvolutionPhase;
EnemyCommandPhase: EnemyCommandPhase;
EvolutionPhase: EvolutionPhase;
ExpPhase: ExpPhase;
FaintPhase: FaintPhase;
FormChangePhase: FormChangePhase;
GameOverPhase: GameOverPhase;
GameOverModifierRewardPhase: GameOverModifierRewardPhase;
HideAbilityPhase: HideAbilityPhase;
HidePartyExpBarPhase: HidePartyExpBarPhase;
LearnMovePhase: LearnMovePhase;
LevelCapPhase: LevelCapPhase;
LevelUpPhase: LevelUpPhase;
LoadMoveAnimPhase: LoadMoveAnimPhase;
LoginPhase: LoginPhase;
MessagePhase: MessagePhase;
ModifierRewardPhase: ModifierRewardPhase;
MoneyRewardPhase: MoneyRewardPhase;
MoveAnimPhase: MoveAnimPhase<MoveAnim>;
MoveChargePhase: MoveChargePhase;
MoveEffectPhase: MoveEffectPhase;
MoveEndPhase: MoveEndPhase;
MoveHeaderPhase: MoveHeaderPhase;
MovePhase: MovePhase;
MysteryEncounterPhase: MysteryEncounterPhase;
MysteryEncounterOptionSelectedPhase: MysteryEncounterOptionSelectedPhase;
MysteryEncounterBattlePhase: MysteryEncounterBattlePhase;
MysteryEncounterBattleStartCleanupPhase: MysteryEncounterBattleStartCleanupPhase;
MysteryEncounterRewardsPhase: MysteryEncounterRewardsPhase;
PostMysteryEncounterPhase: PostMysteryEncounterPhase;
NewBattlePhase: NewBattlePhase;
NewBiomeEncounterPhase: NewBiomeEncounterPhase;
NextEncounterPhase: NextEncounterPhase;
ObtainStatusEffectPhase: ObtainStatusEffectPhase;
PartyExpPhase: PartyExpPhase;
PartyHealPhase: PartyHealPhase;
PokemonAnimPhase: PokemonAnimPhase;
PokemonHealPhase: PokemonHealPhase;
PokemonTransformPhase: PokemonTransformPhase;
PostGameOverPhase: PostGameOverPhase;
PostSummonPhase: PostSummonPhase;
PostTurnStatusEffectPhase: PostTurnStatusEffectPhase;
QuietFormChangePhase: QuietFormChangePhase;
ReloadSessionPhase: ReloadSessionPhase;
ResetStatusPhase: ResetStatusPhase;
ReturnPhase: ReturnPhase;
RevivalBlessingPhase: RevivalBlessingPhase;
RibbonModifierRewardPhase: RibbonModifierRewardPhase;
ScanIvsPhase: ScanIvsPhase;
SelectBiomePhase: SelectBiomePhase;
SelectChallengePhase: SelectChallengePhase;
SelectGenderPhase: SelectGenderPhase;
SelectModifierPhase: SelectModifierPhase;
SelectStarterPhase: SelectStarterPhase;
SelectTargetPhase: SelectTargetPhase;
ShinySparklePhase: ShinySparklePhase;
ShowAbilityPhase: ShowAbilityPhase;
ShowPartyExpBarPhase: ShowPartyExpBarPhase;
ShowTrainerPhase: ShowTrainerPhase;
StatStageChangePhase: StatStageChangePhase;
SummonMissingPhase: SummonMissingPhase;
SummonPhase: SummonPhase;
SwitchBiomePhase: SwitchBiomePhase;
SwitchPhase: SwitchPhase;
SwitchSummonPhase: SwitchSummonPhase;
TeraPhase: TeraPhase;
TitlePhase: TitlePhase;
ToggleDoublePositionPhase: ToggleDoublePositionPhase;
TrainerVictoryPhase: TrainerVictoryPhase;
TurnEndPhase: TurnEndPhase;
TurnInitPhase: TurnInitPhase;
TurnStartPhase: TurnStartPhase;
UnavailablePhase: UnavailablePhase;
UnlockPhase: UnlockPhase;
VictoryPhase: VictoryPhase;
WeatherEffectPhase: WeatherEffectPhase;
};
export type PhaseString = keyof PhaseMap;

View File

@ -145,7 +145,7 @@ import { LoadingScene } from "#app/loading-scene";
import { LevelCapPhase } from "#app/phases/level-cap-phase"; import { LevelCapPhase } from "#app/phases/level-cap-phase";
import { LoginPhase } from "#app/phases/login-phase"; import { LoginPhase } from "#app/phases/login-phase";
import { MessagePhase } from "#app/phases/message-phase"; import { MessagePhase } from "#app/phases/message-phase";
import { MovePhase } from "#app/phases/move-phase"; import type { MovePhase } from "#app/phases/move-phase";
import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase"; import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase";
import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; import { NextEncounterPhase } from "#app/phases/next-encounter-phase";
import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase"; import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase";
@ -153,7 +153,6 @@ import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase";
import { ReturnPhase } from "#app/phases/return-phase"; import { ReturnPhase } from "#app/phases/return-phase";
import { ShowTrainerPhase } from "#app/phases/show-trainer-phase"; import { ShowTrainerPhase } from "#app/phases/show-trainer-phase";
import { SummonPhase } from "#app/phases/summon-phase"; import { SummonPhase } from "#app/phases/summon-phase";
import { SwitchPhase } from "#app/phases/switch-phase";
import { TitlePhase } from "#app/phases/title-phase"; import { TitlePhase } from "#app/phases/title-phase";
import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase";
import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase";
@ -901,7 +900,7 @@ export default class BattleScene extends SceneBase {
do { do {
targetingMovePhase = this.findPhase( targetingMovePhase = this.findPhase(
mp => mp =>
mp instanceof MovePhase && mp.is("MovePhase") &&
mp.targets.length === 1 && mp.targets.length === 1 &&
mp.targets[0] === removedPokemon.getBattlerIndex() && mp.targets[0] === removedPokemon.getBattlerIndex() &&
mp.pokemon.isPlayer() !== allyPokemon.isPlayer(), mp.pokemon.isPlayer() !== allyPokemon.isPlayer(),
@ -1450,7 +1449,7 @@ export default class BattleScene extends SceneBase {
} }
if (lastBattle?.double && !newDouble) { if (lastBattle?.double && !newDouble) {
this.tryRemovePhase(p => p instanceof SwitchPhase); this.tryRemovePhase((p: Phase) => p.is("SwitchPhase"));
for (const p of this.getPlayerField()) { for (const p of this.getPlayerField()) {
p.lapseTag(BattlerTagType.COMMANDED); p.lapseTag(BattlerTagType.COMMANDED);
} }
@ -1588,9 +1587,7 @@ export default class BattleScene extends SceneBase {
return 0; return 0;
} }
const isEggPhase: boolean = ["EggLapsePhase", "EggHatchPhase"].includes( const isEggPhase: boolean = ["EggLapsePhase", "EggHatchPhase"].includes(this.getCurrentPhase()?.phaseName ?? "");
this.getCurrentPhase()?.constructor.name ?? "",
);
if ( if (
// Give trainers with specialty types an appropriately-typed form for Wormadam, Rotom, Arceus, Oricorio, Silvally, or Paldean Tauros. // Give trainers with specialty types an appropriately-typed form for Wormadam, Rotom, Arceus, Oricorio, Silvally, or Paldean Tauros.

View File

@ -2828,7 +2828,7 @@ export class CommanderAbAttr extends AbAttr {
// Apply boosts from this effect to the ally Dondozo // Apply boosts from this effect to the ally Dondozo
pokemon.getAlly()?.addTag(BattlerTagType.COMMANDED, 0, MoveId.NONE, pokemon.id); pokemon.getAlly()?.addTag(BattlerTagType.COMMANDED, 0, MoveId.NONE, pokemon.id);
// Cancel the source Pokemon's next move (if a move is queued) // Cancel the source Pokemon's next move (if a move is queued)
globalScene.tryRemovePhase((phase) => phase instanceof MovePhase && phase.pokemon === pokemon); globalScene.tryRemovePhase((phase) => phase.is("MovePhase") && phase.pokemon === pokemon);
} }
} }
} }
@ -6898,7 +6898,7 @@ export function initAbilities() {
.ignorable(), .ignorable(),
new Ability(AbilityId.ANALYTIC, 5) new Ability(AbilityId.ANALYTIC, 5)
.attr(MovePowerBoostAbAttr, (user, target, move) => { .attr(MovePowerBoostAbAttr, (user, target, move) => {
const movePhase = globalScene.findPhase((phase) => phase instanceof MovePhase && phase.pokemon.id !== user?.id); const movePhase = globalScene.findPhase((phase) => phase.is("MovePhase") && phase.pokemon.id !== user?.id);
return isNullOrUndefined(movePhase); return isNullOrUndefined(movePhase);
}, 1.3), }, 1.3),
new Ability(AbilityId.ILLUSION, 5) new Ability(AbilityId.ILLUSION, 5)

View File

@ -383,7 +383,7 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (_arena, moveId) => {
const move = allMoves[moveId]; const move = allMoves[moveId];
const effectPhase = globalScene.getCurrentPhase(); const effectPhase = globalScene.getCurrentPhase();
if (effectPhase instanceof MoveEffectPhase) { if (effectPhase?.is("MoveEffectPhase")) {
const attacker = effectPhase.getUserPokemon(); const attacker = effectPhase.getUserPokemon();
if (attacker) { if (attacker) {
return move.getPriority(attacker) > 0; return move.getPriority(attacker) > 0;

View File

@ -28,7 +28,7 @@ import type Pokemon from "#app/field/pokemon";
import { HitResult, MoveResult } from "#app/field/pokemon"; import { HitResult, MoveResult } from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { CommonAnimPhase } from "#app/phases/common-anim-phase"; import { CommonAnimPhase } from "#app/phases/common-anim-phase";
import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import type { MoveEffectPhase } from "#app/phases/move-effect-phase";
import { MovePhase } from "#app/phases/move-phase"; import { MovePhase } from "#app/phases/move-phase";
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import type { StatStageChangeCallback } from "#app/phases/stat-stage-change-phase"; import type { StatStageChangeCallback } from "#app/phases/stat-stage-change-phase";
@ -553,9 +553,9 @@ export class ShellTrapTag extends BattlerTag {
// Trap should only be triggered by opponent's Physical moves // Trap should only be triggered by opponent's Physical moves
if (phaseData?.move.category === MoveCategory.PHYSICAL && pokemon.isOpponent(phaseData.attacker)) { if (phaseData?.move.category === MoveCategory.PHYSICAL && pokemon.isOpponent(phaseData.attacker)) {
const shellTrapPhaseIndex = globalScene.phaseQueue.findIndex( const shellTrapPhaseIndex = globalScene.phaseQueue.findIndex(
phase => phase instanceof MovePhase && phase.pokemon === pokemon, phase => phase.is("MovePhase") && phase.pokemon === pokemon,
); );
const firstMovePhaseIndex = globalScene.phaseQueue.findIndex(phase => phase instanceof MovePhase); const firstMovePhaseIndex = globalScene.phaseQueue.findIndex(phase => phase.is("MovePhase"));
// Only shift MovePhase timing if it's not already next up // Only shift MovePhase timing if it's not already next up
if (shellTrapPhaseIndex !== -1 && shellTrapPhaseIndex !== firstMovePhaseIndex) { if (shellTrapPhaseIndex !== -1 && shellTrapPhaseIndex !== firstMovePhaseIndex) {
@ -1023,7 +1023,7 @@ export class PowderTag extends BattlerTag {
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
if (lapseType === BattlerTagLapseType.PRE_MOVE) { if (lapseType === BattlerTagLapseType.PRE_MOVE) {
const movePhase = globalScene.getCurrentPhase(); const movePhase = globalScene.getCurrentPhase();
if (movePhase instanceof MovePhase) { if (movePhase?.is("MovePhase")) {
const move = movePhase.move.getMove(); const move = movePhase.move.getMove();
const weather = globalScene.arena.weather; const weather = globalScene.arena.weather;
if ( if (
@ -1179,13 +1179,13 @@ export class EncoreTag extends MoveRestrictionBattlerTag {
}), }),
); );
const movePhase = globalScene.findPhase(m => m instanceof MovePhase && m.pokemon === pokemon); const movePhase = globalScene.findPhase(m => m.is("MovePhase") && m.pokemon === pokemon);
if (movePhase) { if (movePhase) {
const movesetMove = pokemon.getMoveset().find(m => m.moveId === this.moveId); const movesetMove = pokemon.getMoveset().find(m => m.moveId === this.moveId);
if (movesetMove) { if (movesetMove) {
const lastMove = pokemon.getLastXMoves(1)[0]; const lastMove = pokemon.getLastXMoves(1)[0];
globalScene.tryReplacePhase( globalScene.tryReplacePhase(
m => m instanceof MovePhase && m.pokemon === pokemon, m => m.is("MovePhase") && m.pokemon === pokemon,
new MovePhase(pokemon, lastMove.targets ?? [], movesetMove), new MovePhase(pokemon, lastMove.targets ?? [], movesetMove),
); );
} }
@ -1620,7 +1620,7 @@ export class ProtectedTag extends BattlerTag {
// Stop multi-hit moves early // Stop multi-hit moves early
const effectPhase = globalScene.getCurrentPhase(); const effectPhase = globalScene.getCurrentPhase();
if (effectPhase instanceof MoveEffectPhase) { if (effectPhase?.is("MoveEffectPhase")) {
effectPhase.stopMultiHit(pokemon); effectPhase.stopMultiHit(pokemon);
} }
return true; return true;
@ -2642,7 +2642,7 @@ export class GulpMissileTag extends BattlerTag {
} }
const moveEffectPhase = globalScene.getCurrentPhase(); const moveEffectPhase = globalScene.getCurrentPhase();
if (moveEffectPhase instanceof MoveEffectPhase) { if (moveEffectPhase?.is("MoveEffectPhase")) {
const attacker = moveEffectPhase.getUserPokemon(); const attacker = moveEffectPhase.getUserPokemon();
if (!attacker) { if (!attacker) {
@ -3000,7 +3000,7 @@ export class SubstituteTag extends BattlerTag {
/** If the Substitute redirects damage, queue a message to indicate it. */ /** If the Substitute redirects damage, queue a message to indicate it. */
onHit(pokemon: Pokemon): void { onHit(pokemon: Pokemon): void {
const moveEffectPhase = globalScene.getCurrentPhase(); const moveEffectPhase = globalScene.getCurrentPhase();
if (moveEffectPhase instanceof MoveEffectPhase) { if (moveEffectPhase?.is("MoveEffectPhase")) {
const attacker = moveEffectPhase.getUserPokemon(); const attacker = moveEffectPhase.getUserPokemon();
if (!attacker) { if (!attacker) {
return; return;
@ -3689,7 +3689,7 @@ export function loadBattlerTag(source: BattlerTag | any): BattlerTag {
*/ */
function getMoveEffectPhaseData(_pokemon: Pokemon): { phase: MoveEffectPhase; attacker: Pokemon; move: Move } | null { function getMoveEffectPhaseData(_pokemon: Pokemon): { phase: MoveEffectPhase; attacker: Pokemon; move: Move } | null {
const phase = globalScene.getCurrentPhase(); const phase = globalScene.getCurrentPhase();
if (phase instanceof MoveEffectPhase) { if (phase?.is("MoveEffectPhase")) {
return { return {
phase: phase, phase: phase,
attacker: phase.getPokemon(), attacker: phase.getPokemon(),

View File

@ -3109,7 +3109,7 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr {
const overridden = args[0] as BooleanHolder; const overridden = args[0] as BooleanHolder;
const allyMovePhase = globalScene.findPhase<MovePhase>((phase) => phase instanceof MovePhase && phase.pokemon.isPlayer() === user.isPlayer()); const allyMovePhase = globalScene.findPhase<MovePhase>((phase) => phase.is("MovePhase") && phase.pokemon.isPlayer() === user.isPlayer());
if (allyMovePhase) { if (allyMovePhase) {
const allyMove = allyMovePhase.move.getMove(); const allyMove = allyMovePhase.move.getMove();
if (allyMove !== move && allyMove.hasAttr(AwaitCombinedPledgeAttr)) { if (allyMove !== move && allyMove.hasAttr(AwaitCombinedPledgeAttr)) {
@ -3123,7 +3123,7 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr {
// Move the ally's MovePhase (if needed) so that the ally moves next // Move the ally's MovePhase (if needed) so that the ally moves next
const allyMovePhaseIndex = globalScene.phaseQueue.indexOf(allyMovePhase); const allyMovePhaseIndex = globalScene.phaseQueue.indexOf(allyMovePhase);
const firstMovePhaseIndex = globalScene.phaseQueue.findIndex((phase) => phase instanceof MovePhase); const firstMovePhaseIndex = globalScene.phaseQueue.findIndex((phase) => phase.is("MovePhase"));
if (allyMovePhaseIndex !== firstMovePhaseIndex) { if (allyMovePhaseIndex !== firstMovePhaseIndex) {
globalScene.prependToPhase(globalScene.phaseQueue.splice(allyMovePhaseIndex, 1)[0], MovePhase); globalScene.prependToPhase(globalScene.phaseQueue.splice(allyMovePhaseIndex, 1)[0], MovePhase);
} }
@ -4477,7 +4477,7 @@ export class CueNextRoundAttr extends MoveEffectAttr {
override apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean { override apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean {
const nextRoundPhase = globalScene.findPhase<MovePhase>(phase => const nextRoundPhase = globalScene.findPhase<MovePhase>(phase =>
phase instanceof MovePhase && phase.move.moveId === MoveId.ROUND phase.is("MovePhase") && phase.move.moveId === MoveId.ROUND
); );
if (!nextRoundPhase) { if (!nextRoundPhase) {
@ -4486,7 +4486,7 @@ export class CueNextRoundAttr extends MoveEffectAttr {
// Update the phase queue so that the next Pokemon using Round moves next // Update the phase queue so that the next Pokemon using Round moves next
const nextRoundIndex = globalScene.phaseQueue.indexOf(nextRoundPhase); const nextRoundIndex = globalScene.phaseQueue.indexOf(nextRoundPhase);
const nextMoveIndex = globalScene.phaseQueue.findIndex(phase => phase instanceof MovePhase); const nextMoveIndex = globalScene.phaseQueue.findIndex(phase => phase.is("MovePhase"));
if (nextRoundIndex !== nextMoveIndex) { if (nextRoundIndex !== nextMoveIndex) {
globalScene.prependToPhase(globalScene.phaseQueue.splice(nextRoundIndex, 1)[0], MovePhase); globalScene.prependToPhase(globalScene.phaseQueue.splice(nextRoundIndex, 1)[0], MovePhase);
} }
@ -6177,7 +6177,7 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
// Handle cases where revived pokemon needs to get switched in on same turn // Handle cases where revived pokemon needs to get switched in on same turn
if (allyPokemon.isFainted() || allyPokemon === pokemon) { if (allyPokemon.isFainted() || allyPokemon === pokemon) {
// Enemy switch phase should be removed and replaced with the revived pkmn switching in // Enemy switch phase should be removed and replaced with the revived pkmn switching in
globalScene.tryRemovePhase((phase: SwitchSummonPhase) => phase instanceof SwitchSummonPhase && phase.getPokemon() === pokemon); globalScene.tryRemovePhase((phase: SwitchSummonPhase) => phase.is("SwitchSummonPhase") && phase.getPokemon() === pokemon);
// If the pokemon being revived was alive earlier in the turn, cancel its move // If the pokemon being revived was alive earlier in the turn, cancel its move
// (revived pokemon can't move in the turn they're brought back) // (revived pokemon can't move in the turn they're brought back)
globalScene.findPhase((phase: MovePhase) => phase.pokemon === pokemon)?.cancel(); globalScene.findPhase((phase: MovePhase) => phase.pokemon === pokemon)?.cancel();
@ -7896,7 +7896,7 @@ export class ForceLastAttr extends MoveEffectAttr {
// Either the end of the turn or in front of another, slower move which has also been forced last // Either the end of the turn or in front of another, slower move which has also been forced last
const prependPhase = globalScene.findPhase((phase) => const prependPhase = globalScene.findPhase((phase) =>
[ MovePhase, MoveEndPhase ].every(cls => !(phase instanceof cls)) [ MovePhase, MoveEndPhase ].every(cls => !(phase instanceof cls))
|| (phase instanceof MovePhase) && phaseForcedSlower(phase, target, !!globalScene.arena.getTag(ArenaTagType.TRICK_ROOM)) || (phase.is("MovePhase")) && phaseForcedSlower(phase, target, !!globalScene.arena.getTag(ArenaTagType.TRICK_ROOM))
); );
if (prependPhase) { if (prependPhase) {
globalScene.phaseQueue.splice( globalScene.phaseQueue.splice(
@ -7942,7 +7942,7 @@ const userSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target:
const targetSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => target.status?.effect === StatusEffect.SLEEP || target.hasAbility(AbilityId.COMATOSE); const targetSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => target.status?.effect === StatusEffect.SLEEP || target.hasAbility(AbilityId.COMATOSE);
const failIfLastCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => globalScene.phaseQueue.find(phase => phase instanceof MovePhase) !== undefined; const failIfLastCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => globalScene.phaseQueue.find(phase => phase.is("MovePhase")) !== undefined;
const failIfLastInPartyCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => { const failIfLastInPartyCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => {
const party: Pokemon[] = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); const party: Pokemon[] = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty();

View File

@ -769,7 +769,7 @@ export function setEncounterRewards(
if (customShopRewards) { if (customShopRewards) {
globalScene.unshiftPhase(new SelectModifierPhase(0, undefined, customShopRewards)); globalScene.unshiftPhase(new SelectModifierPhase(0, undefined, customShopRewards));
} else { } else {
globalScene.tryRemovePhase(p => p instanceof SelectModifierPhase); globalScene.tryRemovePhase(p => p.is("MysteryEncounterRewardsPhase"));
} }
if (eggRewards) { if (eggRewards) {

View File

@ -232,7 +232,6 @@ import { getPokemonNameWithAffix } from "#app/messages";
import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
import { FaintPhase } from "#app/phases/faint-phase"; import { FaintPhase } from "#app/phases/faint-phase";
import { LearnMovePhase } from "#app/phases/learn-move-phase"; import { LearnMovePhase } from "#app/phases/learn-move-phase";
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
import { MoveEndPhase } from "#app/phases/move-end-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase";
import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase"; import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
@ -1300,7 +1299,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
// During the Pokemon's MoveEffect phase, the offset is removed to put the Pokemon "in focus" // During the Pokemon's MoveEffect phase, the offset is removed to put the Pokemon "in focus"
const currentPhase = globalScene.getCurrentPhase(); const currentPhase = globalScene.getCurrentPhase();
if (currentPhase instanceof MoveEffectPhase && currentPhase.getPokemon() === this) { if (currentPhase?.is("MoveEffectPhase") && currentPhase.getPokemon() === this) {
return false; return false;
} }
return true; return true;
@ -4775,7 +4774,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
*/ */
if (effect === StatusEffect.SLEEP || effect === StatusEffect.FREEZE) { if (effect === StatusEffect.SLEEP || effect === StatusEffect.FREEZE) {
const currentPhase = globalScene.getCurrentPhase(); const currentPhase = globalScene.getCurrentPhase();
if (currentPhase instanceof MoveEffectPhase && currentPhase.getUserPokemon() === this) { if (currentPhase?.is("MoveEffectPhase") && currentPhase.getUserPokemon() === this) {
this.turnData.hitCount = 1; this.turnData.hitCount = 1;
this.turnData.hitsLeft = 1; this.turnData.hitsLeft = 1;
} }

View File

@ -1,9 +1,33 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import type { PhaseMap, PhaseString } from "./@types/phase-types";
export class Phase { export abstract class Phase {
start() {} start() {}
end() { end() {
globalScene.shiftPhase(); globalScene.shiftPhase();
} }
/**
* The string name of the phase, used to identify the phase type for {@linkcode is}
*
* @privateremarks
*
* When implementing a phase, you must set the `phaseName` property to the name of the phase.
*/
public abstract readonly phaseName: PhaseString;
/**
* Check if the phase is of the given type without requiring `instanceof`.
*
* @param phase - The string name of the phase to check.
* @returns Whether this phase is of the provided type.
*
* @remarks
* This does not check for subclasses! It only checks if the phase is *exactly* the given type.
* This method exists to avoid circular import issues, as using `instanceof` would require importing each phase.
*/
is<K extends keyof PhaseMap>(phase: K): this is PhaseMap[K] {
return this.phaseName === phase;
}
} }

View File

@ -9,6 +9,7 @@ import { Phase } from "#app/phase";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
export class AddEnemyBuffModifierPhase extends Phase { export class AddEnemyBuffModifierPhase extends Phase {
public readonly phaseName = "AddEnemyBuffModifierPhase";
start() { start() {
super.start(); super.start();

View File

@ -27,6 +27,7 @@ import { globalScene } from "#app/global-scene";
import { Gender } from "#app/data/gender"; import { Gender } from "#app/data/gender";
export class AttemptCapturePhase extends PokemonPhase { export class AttemptCapturePhase extends PokemonPhase {
public readonly phaseName = "AttemptCapturePhase";
private pokeballType: PokeballType; private pokeballType: PokeballType;
private pokeball: Phaser.GameObjects.Sprite; private pokeball: Phaser.GameObjects.Sprite;
private originalY: number; private originalY: number;

View File

@ -17,6 +17,7 @@ import { globalScene } from "#app/global-scene";
import { SelectBiomePhase } from "./select-biome-phase"; import { SelectBiomePhase } from "./select-biome-phase";
export class AttemptRunPhase extends PokemonPhase { export class AttemptRunPhase extends PokemonPhase {
public readonly phaseName = "AttemptRunPhase";
/** For testing purposes: this is to force the pokemon to fail and escape */ /** For testing purposes: this is to force the pokemon to fail and escape */
public forceFailEscape = false; public forceFailEscape = false;

View File

@ -5,6 +5,7 @@ import { BattlePhase } from "./battle-phase";
import { GameOverPhase } from "./game-over-phase"; import { GameOverPhase } from "./game-over-phase";
export class BattleEndPhase extends BattlePhase { export class BattleEndPhase extends BattlePhase {
public readonly phaseName = "BattleEndPhase";
/** If true, will increment battles won */ /** If true, will increment battles won */
isVictory: boolean; isVictory: boolean;
@ -19,7 +20,7 @@ export class BattleEndPhase extends BattlePhase {
// cull any extra `BattleEnd` phases from the queue. // cull any extra `BattleEnd` phases from the queue.
globalScene.phaseQueue = globalScene.phaseQueue.filter(phase => { globalScene.phaseQueue = globalScene.phaseQueue.filter(phase => {
if (phase instanceof BattleEndPhase) { if (phase.is("BattleEndPhase")) {
this.isVictory ||= phase.isVictory; this.isVictory ||= phase.isVictory;
return false; return false;
} }
@ -28,7 +29,7 @@ export class BattleEndPhase extends BattlePhase {
// `phaseQueuePrepend` is private, so we have to use this inefficient loop. // `phaseQueuePrepend` is private, so we have to use this inefficient loop.
while ( while (
globalScene.tryRemoveUnshiftedPhase(phase => { globalScene.tryRemoveUnshiftedPhase(phase => {
if (phase instanceof BattleEndPhase) { if (phase.is("BattleEndPhase")) {
this.isVictory ||= phase.isVictory; this.isVictory ||= phase.isVictory;
return true; return true;
} }

View File

@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene";
import { TrainerSlot } from "#enums/trainer-slot"; import { TrainerSlot } from "#enums/trainer-slot";
import { Phase } from "#app/phase"; import { Phase } from "#app/phase";
export class BattlePhase extends Phase { export abstract class BattlePhase extends Phase {
showEnemyTrainer(trainerSlot: TrainerSlot = TrainerSlot.NONE): void { showEnemyTrainer(trainerSlot: TrainerSlot = TrainerSlot.NONE): void {
if (!globalScene.currentBattle.trainer) { if (!globalScene.currentBattle.trainer) {
console.warn("Enemy trainer is missing!"); console.warn("Enemy trainer is missing!");

View File

@ -20,6 +20,7 @@ import type Pokemon from "#app/field/pokemon";
* Also triggers Cud Chew's "repeat berry use" effects * Also triggers Cud Chew's "repeat berry use" effects
*/ */
export class BerryPhase extends FieldPhase { export class BerryPhase extends FieldPhase {
public readonly phaseName = "BerryPhase";
start() { start() {
super.start(); super.start();

View File

@ -4,6 +4,7 @@ import type { BattlerIndex } from "#app/battle";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
export class CheckStatusEffectPhase extends Phase { export class CheckStatusEffectPhase extends Phase {
public readonly phaseName = "CheckStatusEffectPhase";
private order: BattlerIndex[]; private order: BattlerIndex[];
constructor(order: BattlerIndex[]) { constructor(order: BattlerIndex[]) {
super(); super();

View File

@ -10,6 +10,7 @@ import { SwitchPhase } from "./switch-phase";
import { SwitchType } from "#enums/switch-type"; import { SwitchType } from "#enums/switch-type";
export class CheckSwitchPhase extends BattlePhase { export class CheckSwitchPhase extends BattlePhase {
public readonly phaseName = "CheckSwitchPhase";
protected fieldIndex: number; protected fieldIndex: number;
protected useName: boolean; protected useName: boolean;

View File

@ -25,6 +25,7 @@ import { ArenaTagSide } from "#app/data/arena-tag";
import { ArenaTagType } from "#app/enums/arena-tag-type"; import { ArenaTagType } from "#app/enums/arena-tag-type";
export class CommandPhase extends FieldPhase { export class CommandPhase extends FieldPhase {
public readonly phaseName = "CommandPhase";
protected fieldIndex: number; protected fieldIndex: number;
constructor(fieldIndex: number) { constructor(fieldIndex: number) {

View File

@ -5,6 +5,9 @@ import { CommonBattleAnim } from "#app/data/battle-anims";
import { PokemonPhase } from "./pokemon-phase"; import { PokemonPhase } from "./pokemon-phase";
export class CommonAnimPhase extends PokemonPhase { export class CommonAnimPhase extends PokemonPhase {
// PokemonHealPhase extends CommonAnimPhase, and to make typescript happy,
// we need to allow phaseName to be a union of the two
public readonly phaseName: "CommonAnimPhase" | "PokemonHealPhase" | "WeatherEffectPhase" = "CommonAnimPhase";
private anim: CommonAnim | null; private anim: CommonAnim | null;
private targetIndex?: BattlerIndex; private targetIndex?: BattlerIndex;
private playOnEmptyField: boolean; private playOnEmptyField: boolean;

View File

@ -6,6 +6,7 @@ import { fixedInt } from "#app/utils/common";
import { PokemonPhase } from "#app/phases/pokemon-phase"; import { PokemonPhase } from "#app/phases/pokemon-phase";
export class DamageAnimPhase extends PokemonPhase { export class DamageAnimPhase extends PokemonPhase {
public readonly phaseName = "DamageAnimPhase";
private amount: number; private amount: number;
private damageResult: DamageResult; private damageResult: DamageResult;
private critical: boolean; private critical: boolean;

View File

@ -20,6 +20,7 @@ import { doShinySparkleAnim } from "#app/field/anims";
* Class that represents egg hatching * Class that represents egg hatching
*/ */
export class EggHatchPhase extends Phase { export class EggHatchPhase extends Phase {
public readonly phaseName = "EggHatchPhase";
/** The egg that is hatching */ /** The egg that is hatching */
private egg: Egg; private egg: Egg;
/** The new EggHatchData for the egg/pokemon that hatches */ /** The new EggHatchData for the egg/pokemon that hatches */
@ -224,7 +225,7 @@ export class EggHatchPhase extends Phase {
} }
end() { end() {
if (globalScene.findPhase(p => p instanceof EggHatchPhase)) { if (globalScene.findPhase(p => p.is("EggHatchPhase"))) {
this.eggHatchHandler.clear(); this.eggHatchHandler.clear();
} else { } else {
globalScene.time.delayedCall(250, () => globalScene.setModifiersVisible(true)); globalScene.time.delayedCall(250, () => globalScene.setModifiersVisible(true));

View File

@ -16,6 +16,7 @@ import { EggHatchData } from "#app/data/egg-hatch-data";
* Also handles prompts for skipping animation, and calling the egg summary phase * Also handles prompts for skipping animation, and calling the egg summary phase
*/ */
export class EggLapsePhase extends Phase { export class EggLapsePhase extends Phase {
public readonly phaseName = "EggLapsePhase";
private eggHatchData: EggHatchData[] = []; private eggHatchData: EggHatchData[] = [];
private readonly minEggsToSkip: number = 2; private readonly minEggsToSkip: number = 2;

View File

@ -9,6 +9,7 @@ import type { EggHatchData } from "#app/data/egg-hatch-data";
* Phase is handled mostly by the egg-hatch-scene-handler UI * Phase is handled mostly by the egg-hatch-scene-handler UI
*/ */
export class EggSummaryPhase extends Phase { export class EggSummaryPhase extends Phase {
public readonly phaseName = "EggSummaryPhase";
private eggHatchData: EggHatchData[]; private eggHatchData: EggHatchData[];
constructor(eggHatchData: EggHatchData[]) { constructor(eggHatchData: EggHatchData[]) {

View File

@ -47,6 +47,8 @@ import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mys
import { getNatureName } from "#app/data/nature"; import { getNatureName } from "#app/data/nature";
export class EncounterPhase extends BattlePhase { export class EncounterPhase extends BattlePhase {
// Union type is necessary as this is subclassed, and typescript will otherwise complain
public readonly phaseName: "EncounterPhase" | "NextEncounterPhase" | "NewBiomeEncounterPhase" = "EncounterPhase";
private loaded: boolean; private loaded: boolean;
constructor(loaded = false) { constructor(loaded = false) {

View File

@ -5,6 +5,7 @@ import { addTextObject, TextStyle } from "#app/ui/text";
import i18next from "i18next"; import i18next from "i18next";
export class EndCardPhase extends Phase { export class EndCardPhase extends Phase {
public readonly phaseName = "EndCardPhase";
public endCard: Phaser.GameObjects.Image; public endCard: Phaser.GameObjects.Image;
public text: Phaser.GameObjects.Text; public text: Phaser.GameObjects.Text;
start(): void { start(): void {

View File

@ -3,6 +3,7 @@ import { Phase } from "#app/phase";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
export class EndEvolutionPhase extends Phase { export class EndEvolutionPhase extends Phase {
public readonly phaseName = "EndEvolutionPhase";
start() { start() {
super.start(); super.start();

View File

@ -15,6 +15,7 @@ import { BattlerTagType } from "#enums/battler-tag-type";
* @see {@linkcode EnemyPokemon.getNextMove} * @see {@linkcode EnemyPokemon.getNextMove}
*/ */
export class EnemyCommandPhase extends FieldPhase { export class EnemyCommandPhase extends FieldPhase {
public readonly phaseName = "EnemyCommandPhase";
protected fieldIndex: number; protected fieldIndex: number;
protected skipTurn = false; protected skipTurn = false;

View File

@ -19,6 +19,9 @@ import { EndEvolutionPhase } from "#app/phases/end-evolution-phase";
import { EVOLVE_MOVE } from "#app/data/balance/pokemon-level-moves"; import { EVOLVE_MOVE } from "#app/data/balance/pokemon-level-moves";
export class EvolutionPhase extends Phase { export class EvolutionPhase extends Phase {
// FormChangePhase inherits from this, but EvolutionPhase is not abstract.
// We have to use the union here
public readonly phaseName: "EvolutionPhase" | "FormChangePhase" = "EvolutionPhase";
protected pokemon: PlayerPokemon; protected pokemon: PlayerPokemon;
protected lastLevel: number; protected lastLevel: number;

View File

@ -7,6 +7,7 @@ import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-pha
import { LevelUpPhase } from "./level-up-phase"; import { LevelUpPhase } from "./level-up-phase";
export class ExpPhase extends PlayerPartyMemberPokemonPhase { export class ExpPhase extends PlayerPartyMemberPokemonPhase {
public readonly phaseName = "ExpPhase";
private expValue: number; private expValue: number;
constructor(partyMemberIndex: number, expValue: number) { constructor(partyMemberIndex: number, expValue: number) {

View File

@ -35,6 +35,7 @@ import { FRIENDSHIP_LOSS_FROM_FAINT } from "#app/data/balance/starters";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
export class FaintPhase extends PokemonPhase { export class FaintPhase extends PokemonPhase {
public readonly phaseName = "FaintPhase";
/** /**
* Whether or not instant revive should be prevented * Whether or not instant revive should be prevented
*/ */

View File

@ -13,6 +13,7 @@ import { BattlerTagType } from "#enums/battler-tag-type";
import { SpeciesFormKey } from "#enums/species-form-key"; import { SpeciesFormKey } from "#enums/species-form-key";
export class FormChangePhase extends EvolutionPhase { export class FormChangePhase extends EvolutionPhase {
public readonly phaseName = "FormChangePhase";
private formChange: SpeciesFormChange; private formChange: SpeciesFormChange;
private modal: boolean; private modal: boolean;

View File

@ -4,6 +4,7 @@ import i18next from "i18next";
import { ModifierRewardPhase } from "./modifier-reward-phase"; import { ModifierRewardPhase } from "./modifier-reward-phase";
export class GameOverModifierRewardPhase extends ModifierRewardPhase { export class GameOverModifierRewardPhase extends ModifierRewardPhase {
public readonly phaseName = "GameOverModifierRewardPhase";
doReward(): Promise<void> { doReward(): Promise<void> {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const newModifier = this.modifierType.newModifier(); const newModifier = this.modifierType.newModifier();

View File

@ -34,6 +34,7 @@ import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
import { MessagePhase } from "./message-phase"; import { MessagePhase } from "./message-phase";
export class GameOverPhase extends BattlePhase { export class GameOverPhase extends BattlePhase {
public readonly phaseName = "GameOverPhase";
private isVictory: boolean; private isVictory: boolean;
private firstRibbons: PokemonSpecies[] = []; private firstRibbons: PokemonSpecies[] = [];

View File

@ -2,6 +2,7 @@ import { globalScene } from "#app/global-scene";
import { Phase } from "#app/phase"; import { Phase } from "#app/phase";
export class HideAbilityPhase extends Phase { export class HideAbilityPhase extends Phase {
public readonly phaseName = "HideAbilityPhase";
start() { start() {
super.start(); super.start();

View File

@ -2,6 +2,7 @@ import { globalScene } from "#app/global-scene";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
export class HidePartyExpBarPhase extends BattlePhase { export class HidePartyExpBarPhase extends BattlePhase {
public readonly phaseName = "HidePartyExpBarPhase";
start() { start() {
super.start(); super.start();

View File

@ -12,7 +12,6 @@ import { UiMode } from "#enums/ui-mode";
import i18next from "i18next"; import i18next from "i18next";
import { PlayerPartyMemberPokemonPhase } from "#app/phases/player-party-member-pokemon-phase"; import { PlayerPartyMemberPokemonPhase } from "#app/phases/player-party-member-pokemon-phase";
import type Pokemon from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
export enum LearnMoveType { export enum LearnMoveType {
/** For learning a move via level-up, evolution, or other non-item-based event */ /** For learning a move via level-up, evolution, or other non-item-based event */
@ -24,6 +23,7 @@ export enum LearnMoveType {
} }
export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
public readonly phaseName = "LearnMovePhase";
private moveId: MoveId; private moveId: MoveId;
private messageMode: UiMode; private messageMode: UiMode;
private learnMoveType: LearnMoveType; private learnMoveType: LearnMoveType;
@ -195,7 +195,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
pokemon.usedTMs = []; pokemon.usedTMs = [];
} }
pokemon.usedTMs.push(this.moveId); pokemon.usedTMs.push(this.moveId);
globalScene.tryRemovePhase(phase => phase instanceof SelectModifierPhase); globalScene.tryRemovePhase(phase => phase.is("SelectModifierPhase"));
} else if (this.learnMoveType === LearnMoveType.MEMORY) { } else if (this.learnMoveType === LearnMoveType.MEMORY) {
if (this.cost !== -1) { if (this.cost !== -1) {
if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) {
@ -205,7 +205,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
} }
globalScene.playSound("se/buy"); globalScene.playSound("se/buy");
} else { } else {
globalScene.tryRemovePhase(phase => phase instanceof SelectModifierPhase); globalScene.tryRemovePhase(phase => phase.is("SelectModifierPhase"));
} }
} }
pokemon.setMove(index, this.moveId); pokemon.setMove(index, this.moveId);

View File

@ -4,6 +4,7 @@ import i18next from "i18next";
import { FieldPhase } from "./field-phase"; import { FieldPhase } from "./field-phase";
export class LevelCapPhase extends FieldPhase { export class LevelCapPhase extends FieldPhase {
public readonly phaseName = "LevelCapPhase";
start(): void { start(): void {
super.start(); super.start();

View File

@ -10,6 +10,7 @@ import { NumberHolder } from "#app/utils/common";
import i18next from "i18next"; import i18next from "i18next";
export class LevelUpPhase extends PlayerPartyMemberPokemonPhase { export class LevelUpPhase extends PlayerPartyMemberPokemonPhase {
public readonly phaseName = "LevelUpPhase";
protected lastLevel: number; protected lastLevel: number;
protected level: number; protected level: number;
protected pokemon: PlayerPokemon = this.getPlayerPokemon(); protected pokemon: PlayerPokemon = this.getPlayerPokemon();

View File

@ -8,6 +8,7 @@ import { Phase } from "#app/phase";
* isn't already loaded (e.g. for Metronome) * isn't already loaded (e.g. for Metronome)
*/ */
export class LoadMoveAnimPhase extends Phase { export class LoadMoveAnimPhase extends Phase {
public readonly phaseName = "LoadMoveAnimPhase";
constructor(protected moveId: MoveId) { constructor(protected moveId: MoveId) {
super(); super();
} }

View File

@ -11,6 +11,7 @@ import { SelectGenderPhase } from "./select-gender-phase";
import { UnavailablePhase } from "./unavailable-phase"; import { UnavailablePhase } from "./unavailable-phase";
export class LoginPhase extends Phase { export class LoginPhase extends Phase {
public readonly phaseName = "LoginPhase";
private showText: boolean; private showText: boolean;
constructor(showText = true) { constructor(showText = true) {

View File

@ -2,6 +2,7 @@ import { globalScene } from "#app/global-scene";
import { Phase } from "#app/phase"; import { Phase } from "#app/phase";
export class MessagePhase extends Phase { export class MessagePhase extends Phase {
public readonly phaseName = "MessagePhase";
private text: string; private text: string;
private callbackDelay?: number | null; private callbackDelay?: number | null;
private prompt?: boolean | null; private prompt?: boolean | null;

View File

@ -5,6 +5,10 @@ import i18next from "i18next";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
export class ModifierRewardPhase extends BattlePhase { export class ModifierRewardPhase extends BattlePhase {
// RibbonModifierRewardPhase extends ModifierRewardPhase and to make typescript happy
// we need to use a union type here
public readonly phaseName: "ModifierRewardPhase" | "RibbonModifierRewardPhase" | "GameOverModifierRewardPhase" =
"ModifierRewardPhase";
protected modifierType: ModifierType; protected modifierType: ModifierType;
constructor(modifierTypeFunc: ModifierTypeFunc) { constructor(modifierTypeFunc: ModifierTypeFunc) {

View File

@ -6,6 +6,7 @@ import { NumberHolder } from "#app/utils/common";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
export class MoneyRewardPhase extends BattlePhase { export class MoneyRewardPhase extends BattlePhase {
public readonly phaseName = "MoneyRewardPhase";
private moneyMultiplier: number; private moneyMultiplier: number;
constructor(moneyMultiplier: number) { constructor(moneyMultiplier: number) {

View File

@ -5,6 +5,8 @@ import { Phase } from "#app/phase";
* Plays the given {@linkcode MoveAnim} sequentially. * Plays the given {@linkcode MoveAnim} sequentially.
*/ */
export class MoveAnimPhase<Anim extends MoveAnim> extends Phase { export class MoveAnimPhase<Anim extends MoveAnim> extends Phase {
public readonly phaseName = "MoveAnimPhase";
constructor( constructor(
protected anim: Anim, protected anim: Anim,
protected onSubstitute = false, protected onSubstitute = false,

View File

@ -9,13 +9,13 @@ import { BooleanHolder } from "#app/utils/common";
import { MovePhase } from "#app/phases/move-phase"; import { MovePhase } from "#app/phases/move-phase";
import { PokemonPhase } from "#app/phases/pokemon-phase"; import { PokemonPhase } from "#app/phases/pokemon-phase";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { MoveEndPhase } from "#app/phases/move-end-phase";
/** /**
* Phase for the "charging turn" of two-turn moves (e.g. Dig). * Phase for the "charging turn" of two-turn moves (e.g. Dig).
* @extends {@linkcode PokemonPhase} * @extends {@linkcode PokemonPhase}
*/ */
export class MoveChargePhase extends PokemonPhase { export class MoveChargePhase extends PokemonPhase {
public readonly phaseName = "MoveChargePhase";
/** The move instance that this phase applies */ /** The move instance that this phase applies */
public move: PokemonMove; public move: PokemonMove;
/** The field index targeted by the move (Charging moves assume single target) */ /** The field index targeted by the move (Charging moves assume single target) */
@ -62,7 +62,7 @@ export class MoveChargePhase extends PokemonPhase {
if (instantCharge.value) { if (instantCharge.value) {
// this MoveEndPhase will be duplicated by the queued MovePhase if not removed // this MoveEndPhase will be duplicated by the queued MovePhase if not removed
globalScene.tryRemovePhase(phase => phase instanceof MoveEndPhase && phase.getPokemon() === user); globalScene.tryRemovePhase(phase => phase.is("MoveEndPhase") && phase.getPokemon() === user);
// queue a new MovePhase for this move's attack phase // queue a new MovePhase for this move's attack phase
globalScene.unshiftPhase(new MovePhase(user, [this.targetIndex], this.move, false)); globalScene.unshiftPhase(new MovePhase(user, [this.targetIndex], this.move, false));
} else { } else {

View File

@ -82,6 +82,7 @@ import { DamageAchv } from "#app/system/achv";
type HitCheckEntry = [HitCheckResult, TypeDamageMultiplier]; type HitCheckEntry = [HitCheckResult, TypeDamageMultiplier];
export class MoveEffectPhase extends PokemonPhase { export class MoveEffectPhase extends PokemonPhase {
public readonly phaseName = "MoveEffectPhase";
public move: Move; public move: Move;
private virtual = false; private virtual = false;
protected targets: BattlerIndex[]; protected targets: BattlerIndex[];

View File

@ -6,6 +6,7 @@ import { applyPostSummonAbAttrs, PostSummonRemoveEffectAbAttr } from "#app/data/
import type Pokemon from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
export class MoveEndPhase extends PokemonPhase { export class MoveEndPhase extends PokemonPhase {
public readonly phaseName = "MoveEndPhase";
private wasFollowUp: boolean; private wasFollowUp: boolean;
/** Targets from the preceding MovePhase */ /** Targets from the preceding MovePhase */

View File

@ -4,6 +4,7 @@ import type Pokemon from "#app/field/pokemon";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
export class MoveHeaderPhase extends BattlePhase { export class MoveHeaderPhase extends BattlePhase {
public readonly phaseName = "MoveHeaderPhase";
public pokemon: Pokemon; public pokemon: Pokemon;
public move: PokemonMove; public move: PokemonMove;

View File

@ -52,6 +52,7 @@ import { StatusEffect } from "#enums/status-effect";
import i18next from "i18next"; import i18next from "i18next";
export class MovePhase extends BattlePhase { export class MovePhase extends BattlePhase {
public readonly phaseName = "MovePhase";
protected _pokemon: Pokemon; protected _pokemon: Pokemon;
protected _move: PokemonMove; protected _move: PokemonMove;
protected _targets: BattlerIndex[]; protected _targets: BattlerIndex[];

View File

@ -6,7 +6,6 @@ import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-d
import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
import { GameOverPhase } from "#app/phases/game-over-phase"; import { GameOverPhase } from "#app/phases/game-over-phase";
import { NewBattlePhase } from "#app/phases/new-battle-phase"; import { NewBattlePhase } from "#app/phases/new-battle-phase";
import { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase";
import { ReturnPhase } from "#app/phases/return-phase"; import { ReturnPhase } from "#app/phases/return-phase";
import { ScanIvsPhase } from "#app/phases/scan-ivs-phase"; import { ScanIvsPhase } from "#app/phases/scan-ivs-phase";
import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
@ -39,6 +38,7 @@ import { SelectBiomePhase } from "./select-biome-phase";
* - Queuing of the {@linkcode MysteryEncounterOptionSelectedPhase} * - Queuing of the {@linkcode MysteryEncounterOptionSelectedPhase}
*/ */
export class MysteryEncounterPhase extends Phase { export class MysteryEncounterPhase extends Phase {
public readonly phaseName = "MysteryEncounterPhase";
private readonly FIRST_DIALOGUE_PROMPT_DELAY = 300; private readonly FIRST_DIALOGUE_PROMPT_DELAY = 300;
optionSelectSettings?: OptionSelectSettings; optionSelectSettings?: OptionSelectSettings;
@ -180,6 +180,7 @@ export class MysteryEncounterPhase extends Phase {
* Any phase that is meant to follow this one MUST be queued via the onOptionSelect() logic of the selected option * Any phase that is meant to follow this one MUST be queued via the onOptionSelect() logic of the selected option
*/ */
export class MysteryEncounterOptionSelectedPhase extends Phase { export class MysteryEncounterOptionSelectedPhase extends Phase {
public readonly phaseName = "MysteryEncounterOptionSelectedPhase";
onOptionSelect: OptionPhaseCallback; onOptionSelect: OptionPhaseCallback;
constructor() { constructor() {
@ -221,6 +222,7 @@ export class MysteryEncounterOptionSelectedPhase extends Phase {
* See {@linkcode TurnEndPhase} for more details * See {@linkcode TurnEndPhase} for more details
*/ */
export class MysteryEncounterBattleStartCleanupPhase extends Phase { export class MysteryEncounterBattleStartCleanupPhase extends Phase {
public readonly phaseName = "MysteryEncounterBattleStartCleanupPhase";
/** /**
* Cleans up `TURN_END` tags, any {@linkcode PostTurnStatusEffectPhase}s, checks for Pokemon switches, then continues * Cleans up `TURN_END` tags, any {@linkcode PostTurnStatusEffectPhase}s, checks for Pokemon switches, then continues
*/ */
@ -245,8 +247,8 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase {
}); });
// Remove any status tick phases // Remove any status tick phases
while (globalScene.findPhase(p => p instanceof PostTurnStatusEffectPhase)) { while (globalScene.findPhase(p => p.is("PostTurnStatusEffectPhase"))) {
globalScene.tryRemovePhase(p => p instanceof PostTurnStatusEffectPhase); globalScene.tryRemovePhase(p => p.is("PostTurnStatusEffectPhase"));
} }
// The total number of Pokemon in the player's party that can legally fight // The total number of Pokemon in the player's party that can legally fight
@ -284,6 +286,7 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase {
* - Queue the {@linkcode SummonPhase}s, {@linkcode PostSummonPhase}s, etc., required to initialize the phase queue for a battle * - Queue the {@linkcode SummonPhase}s, {@linkcode PostSummonPhase}s, etc., required to initialize the phase queue for a battle
*/ */
export class MysteryEncounterBattlePhase extends Phase { export class MysteryEncounterBattlePhase extends Phase {
public readonly phaseName = "MysteryEncounterBattlePhase";
disableSwitch: boolean; disableSwitch: boolean;
constructor(disableSwitch = false) { constructor(disableSwitch = false) {
@ -513,6 +516,7 @@ export class MysteryEncounterBattlePhase extends Phase {
* - Queuing of the {@linkcode PostMysteryEncounterPhase} * - Queuing of the {@linkcode PostMysteryEncounterPhase}
*/ */
export class MysteryEncounterRewardsPhase extends Phase { export class MysteryEncounterRewardsPhase extends Phase {
public readonly phaseName = "MysteryEncounterRewardsPhase";
addHealPhase: boolean; addHealPhase: boolean;
constructor(addHealPhase = false) { constructor(addHealPhase = false) {
@ -558,7 +562,7 @@ export class MysteryEncounterRewardsPhase extends Phase {
if (encounter.doEncounterRewards) { if (encounter.doEncounterRewards) {
encounter.doEncounterRewards(); encounter.doEncounterRewards();
} else if (this.addHealPhase) { } else if (this.addHealPhase) {
globalScene.tryRemovePhase(p => p instanceof SelectModifierPhase); globalScene.tryRemovePhase(p => p.is("SelectModifierPhase"));
globalScene.unshiftPhase( globalScene.unshiftPhase(
new SelectModifierPhase(0, undefined, { new SelectModifierPhase(0, undefined, {
fillRemaining: false, fillRemaining: false,
@ -580,6 +584,7 @@ export class MysteryEncounterRewardsPhase extends Phase {
* - Queuing of the next wave * - Queuing of the next wave
*/ */
export class PostMysteryEncounterPhase extends Phase { export class PostMysteryEncounterPhase extends Phase {
public readonly phaseName = "PostMysteryEncounterPhase";
private readonly FIRST_DIALOGUE_PROMPT_DELAY = 750; private readonly FIRST_DIALOGUE_PROMPT_DELAY = 750;
onPostOptionSelect?: OptionPhaseCallback; onPostOptionSelect?: OptionPhaseCallback;

View File

@ -2,13 +2,14 @@ import { globalScene } from "#app/global-scene";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
export class NewBattlePhase extends BattlePhase { export class NewBattlePhase extends BattlePhase {
public readonly phaseName = "NewBattlePhase";
start() { start() {
super.start(); super.start();
// cull any extra `NewBattle` phases from the queue. // cull any extra `NewBattle` phases from the queue.
globalScene.phaseQueue = globalScene.phaseQueue.filter(phase => !(phase instanceof NewBattlePhase)); globalScene.phaseQueue = globalScene.phaseQueue.filter(phase => !phase.is("NewBattlePhase"));
// `phaseQueuePrepend` is private, so we have to use this inefficient loop. // `phaseQueuePrepend` is private, so we have to use this inefficient loop.
while (globalScene.tryRemoveUnshiftedPhase(phase => phase instanceof NewBattlePhase)) {} while (globalScene.tryRemoveUnshiftedPhase(phase => phase.is("NewBattlePhase"))) {}
globalScene.newBattle(); globalScene.newBattle();

View File

@ -4,6 +4,7 @@ import { getRandomWeatherType } from "#app/data/weather";
import { NextEncounterPhase } from "./next-encounter-phase"; import { NextEncounterPhase } from "./next-encounter-phase";
export class NewBiomeEncounterPhase extends NextEncounterPhase { export class NewBiomeEncounterPhase extends NextEncounterPhase {
public readonly phaseName = "NewBiomeEncounterPhase";
doEncounter(): void { doEncounter(): void {
globalScene.playBgm(undefined, true); globalScene.playBgm(undefined, true);

View File

@ -6,6 +6,7 @@ import { EncounterPhase } from "./encounter-phase";
* Handles generating, loading and preparing for it. * Handles generating, loading and preparing for it.
*/ */
export class NextEncounterPhase extends EncounterPhase { export class NextEncounterPhase extends EncounterPhase {
public readonly phaseName: "NextEncounterPhase" | "NewBiomeEncounterPhase" = "NextEncounterPhase";
start() { start() {
super.start(); super.start();
} }

View File

@ -11,6 +11,7 @@ import { applyPostSetStatusAbAttrs, PostSetStatusAbAttr } from "#app/data/abilit
import { isNullOrUndefined } from "#app/utils/common"; import { isNullOrUndefined } from "#app/utils/common";
export class ObtainStatusEffectPhase extends PokemonPhase { export class ObtainStatusEffectPhase extends PokemonPhase {
public readonly phaseName = "ObtainStatusEffectPhase";
private statusEffect?: StatusEffect; private statusEffect?: StatusEffect;
private turnsRemaining?: number; private turnsRemaining?: number;
private sourceText?: string | null; private sourceText?: string | null;

View File

@ -6,6 +6,7 @@ import { Phase } from "#app/phase";
* Intended to be used as a more 1-off phase to provide exp to the party (such as during MEs), rather than cleanup a battle entirely * Intended to be used as a more 1-off phase to provide exp to the party (such as during MEs), rather than cleanup a battle entirely
*/ */
export class PartyExpPhase extends Phase { export class PartyExpPhase extends Phase {
public readonly phaseName = "PartyExpPhase";
expValue: number; expValue: number;
useWaveIndexMultiplier?: boolean; useWaveIndexMultiplier?: boolean;
pokemonParticipantIds?: Set<number>; pokemonParticipantIds?: Set<number>;

View File

@ -3,6 +3,7 @@ import { fixedInt } from "#app/utils/common";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
export class PartyHealPhase extends BattlePhase { export class PartyHealPhase extends BattlePhase {
public readonly phaseName = "PartyHealPhase";
private resumeBgm: boolean; private resumeBgm: boolean;
constructor(resumeBgm: boolean) { constructor(resumeBgm: boolean) {

View File

@ -7,6 +7,7 @@ import { PokemonAnimType } from "#enums/pokemon-anim-type";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
export class PokemonAnimPhase extends BattlePhase { export class PokemonAnimPhase extends BattlePhase {
public readonly phaseName = "PokemonAnimPhase";
/** The type of animation to play in this phase */ /** The type of animation to play in this phase */
protected key: PokemonAnimType; protected key: PokemonAnimType;
/** The Pokemon to which this animation applies */ /** The Pokemon to which this animation applies */

View File

@ -14,6 +14,7 @@ import { BattlerTagType } from "#app/enums/battler-tag-type";
import type { HealBlockTag } from "#app/data/battler-tags"; import type { HealBlockTag } from "#app/data/battler-tags";
export class PokemonHealPhase extends CommonAnimPhase { export class PokemonHealPhase extends CommonAnimPhase {
public readonly phaseName = "PokemonHealPhase";
private hpHealed: number; private hpHealed: number;
private message: string | null; private message: string | null;
private showFullHpMessage: boolean; private showFullHpMessage: boolean;

View File

@ -13,6 +13,7 @@ import i18next from "i18next";
* Used for Transform (move) and Imposter (ability) * Used for Transform (move) and Imposter (ability)
*/ */
export class PokemonTransformPhase extends PokemonPhase { export class PokemonTransformPhase extends PokemonPhase {
public readonly phaseName = "PokemonTransformPhase";
protected targetIndex: BattlerIndex; protected targetIndex: BattlerIndex;
private playSound: boolean; private playSound: boolean;

View File

@ -4,6 +4,7 @@ import type { EndCardPhase } from "./end-card-phase";
import { TitlePhase } from "./title-phase"; import { TitlePhase } from "./title-phase";
export class PostGameOverPhase extends Phase { export class PostGameOverPhase extends Phase {
public readonly phaseName = "PostGameOverPhase";
private endCardPhase?: EndCardPhase; private endCardPhase?: EndCardPhase;
constructor(endCardPhase?: EndCardPhase) { constructor(endCardPhase?: EndCardPhase) {

View File

@ -7,6 +7,7 @@ import { MysteryEncounterPostSummonTag } from "#app/data/battler-tags";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
export class PostSummonPhase extends PokemonPhase { export class PostSummonPhase extends PokemonPhase {
public readonly phaseName = "PostSummonPhase";
start() { start() {
super.start(); super.start();

View File

@ -17,6 +17,7 @@ import { BooleanHolder, NumberHolder } from "#app/utils/common";
import { PokemonPhase } from "./pokemon-phase"; import { PokemonPhase } from "./pokemon-phase";
export class PostTurnStatusEffectPhase extends PokemonPhase { export class PostTurnStatusEffectPhase extends PokemonPhase {
public readonly phaseName = "PostTurnStatusEffectPhase";
// biome-ignore lint/complexity/noUselessConstructor: Not unnecessary as it makes battlerIndex required // biome-ignore lint/complexity/noUselessConstructor: Not unnecessary as it makes battlerIndex required
constructor(battlerIndex: BattlerIndex) { constructor(battlerIndex: BattlerIndex) {
super(battlerIndex); super(battlerIndex);

View File

@ -9,7 +9,7 @@ import type Pokemon from "#app/field/pokemon";
import { EnemyPokemon } from "#app/field/pokemon"; import { EnemyPokemon } from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
import { MovePhase } from "./move-phase"; import type { MovePhase } from "./move-phase";
import { PokemonHealPhase } from "./pokemon-heal-phase"; import { PokemonHealPhase } from "./pokemon-heal-phase";
import { import {
applyAbAttrs, applyAbAttrs,
@ -19,6 +19,7 @@ import {
} from "#app/data/abilities/ability"; } from "#app/data/abilities/ability";
export class QuietFormChangePhase extends BattlePhase { export class QuietFormChangePhase extends BattlePhase {
public readonly phaseName = "QuietFormChangePhase";
protected pokemon: Pokemon; protected pokemon: Pokemon;
protected formChange: SpeciesFormChange; protected formChange: SpeciesFormChange;
@ -168,7 +169,7 @@ export class QuietFormChangePhase extends BattlePhase {
this.pokemon.initBattleInfo(); this.pokemon.initBattleInfo();
this.pokemon.cry(); this.pokemon.cry();
const movePhase = globalScene.findPhase(p => p instanceof MovePhase && p.pokemon === this.pokemon) as MovePhase; const movePhase = globalScene.findPhase(p => p.is("MovePhase") && p.pokemon === this.pokemon) as MovePhase;
if (movePhase) { if (movePhase) {
movePhase.cancel(); movePhase.cancel();
} }

View File

@ -4,6 +4,7 @@ import { UiMode } from "#enums/ui-mode";
import { fixedInt } from "#app/utils/common"; import { fixedInt } from "#app/utils/common";
export class ReloadSessionPhase extends Phase { export class ReloadSessionPhase extends Phase {
public readonly phaseName = "ReloadSessionPhase";
private systemDataStr?: string; private systemDataStr?: string;
constructor(systemDataStr?: string) { constructor(systemDataStr?: string) {

View File

@ -7,6 +7,7 @@ import { BattlePhase } from "#app/phases/battle-phase";
* This is necessary to perform in a phase primarly to ensure that the status icon disappears at the correct time in the battle * This is necessary to perform in a phase primarly to ensure that the status icon disappears at the correct time in the battle
*/ */
export class ResetStatusPhase extends BattlePhase { export class ResetStatusPhase extends BattlePhase {
public readonly phaseName = "ResetStatusPhase";
private readonly pokemon: Pokemon; private readonly pokemon: Pokemon;
private readonly affectConfusion: boolean; private readonly affectConfusion: boolean;
private readonly reloadAssets: boolean; private readonly reloadAssets: boolean;

View File

@ -4,6 +4,7 @@ import { SwitchType } from "#enums/switch-type";
import { SwitchSummonPhase } from "./switch-summon-phase"; import { SwitchSummonPhase } from "./switch-summon-phase";
export class ReturnPhase extends SwitchSummonPhase { export class ReturnPhase extends SwitchSummonPhase {
public readonly phaseName = "ReturnPhase";
constructor(fieldIndex: number) { constructor(fieldIndex: number) {
super(SwitchType.SWITCH, fieldIndex, -1, true); super(SwitchType.SWITCH, fieldIndex, -1, true);
} }

View File

@ -15,6 +15,7 @@ import type { PlayerPokemon } from "#app/field/pokemon";
* when used by one of the player's Pokemon. * when used by one of the player's Pokemon.
*/ */
export class RevivalBlessingPhase extends BattlePhase { export class RevivalBlessingPhase extends BattlePhase {
public readonly phaseName = "RevivalBlessingPhase";
constructor(protected user: PlayerPokemon) { constructor(protected user: PlayerPokemon) {
super(); super();
} }

View File

@ -6,6 +6,7 @@ import i18next from "i18next";
import { ModifierRewardPhase } from "./modifier-reward-phase"; import { ModifierRewardPhase } from "./modifier-reward-phase";
export class RibbonModifierRewardPhase extends ModifierRewardPhase { export class RibbonModifierRewardPhase extends ModifierRewardPhase {
public readonly phaseName = "RibbonModifierRewardPhase";
private species: PokemonSpecies; private species: PokemonSpecies;
constructor(modifierTypeFunc: ModifierTypeFunc, species: PokemonSpecies) { constructor(modifierTypeFunc: ModifierTypeFunc, species: PokemonSpecies) {

View File

@ -8,6 +8,7 @@ import i18next from "i18next";
import { PokemonPhase } from "./pokemon-phase"; import { PokemonPhase } from "./pokemon-phase";
export class ScanIvsPhase extends PokemonPhase { export class ScanIvsPhase extends PokemonPhase {
public readonly phaseName = "ScanIvsPhase";
// biome-ignore lint/complexity/noUselessConstructor: This changes `battlerIndex` to be required // biome-ignore lint/complexity/noUselessConstructor: This changes `battlerIndex` to be required
constructor(battlerIndex: BattlerIndex) { constructor(battlerIndex: BattlerIndex) {
super(battlerIndex); super(battlerIndex);

View File

@ -10,6 +10,7 @@ import { PartyHealPhase } from "./party-heal-phase";
import { SwitchBiomePhase } from "./switch-biome-phase"; import { SwitchBiomePhase } from "./switch-biome-phase";
export class SelectBiomePhase extends BattlePhase { export class SelectBiomePhase extends BattlePhase {
public readonly phaseName = "SelectBiomePhase";
start() { start() {
super.start(); super.start();

View File

@ -3,6 +3,7 @@ import { Phase } from "#app/phase";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
export class SelectChallengePhase extends Phase { export class SelectChallengePhase extends Phase {
public readonly phaseName = "SelectChallengePhase";
start() { start() {
super.start(); super.start();

View File

@ -6,6 +6,7 @@ import { UiMode } from "#enums/ui-mode";
import i18next from "i18next"; import i18next from "i18next";
export class SelectGenderPhase extends Phase { export class SelectGenderPhase extends Phase {
public readonly phaseName = "SelectGenderPhase";
start(): void { start(): void {
super.start(); super.start();

View File

@ -32,6 +32,7 @@ import type { CustomModifierSettings } from "#app/modifier/modifier-type";
import { isNullOrUndefined, NumberHolder } from "#app/utils/common"; import { isNullOrUndefined, NumberHolder } from "#app/utils/common";
export class SelectModifierPhase extends BattlePhase { export class SelectModifierPhase extends BattlePhase {
public readonly phaseName = "SelectModifierPhase";
private rerollCount: number; private rerollCount: number;
private modifierTiers?: ModifierTier[]; private modifierTiers?: ModifierTier[];
private customModifierSettings?: CustomModifierSettings; private customModifierSettings?: CustomModifierSettings;

View File

@ -15,6 +15,7 @@ import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import { isNullOrUndefined } from "#app/utils/common"; import { isNullOrUndefined } from "#app/utils/common";
export class SelectStarterPhase extends Phase { export class SelectStarterPhase extends Phase {
public readonly phaseName = "SelectStarterPhase";
start() { start() {
super.start(); super.start();

View File

@ -8,6 +8,7 @@ import i18next from "#app/plugins/i18n";
import { allMoves } from "#app/data/data-lists"; import { allMoves } from "#app/data/data-lists";
export class SelectTargetPhase extends PokemonPhase { export class SelectTargetPhase extends PokemonPhase {
public readonly phaseName = "SelectTargetPhase";
// biome-ignore lint/complexity/noUselessConstructor: This makes `fieldIndex` required // biome-ignore lint/complexity/noUselessConstructor: This makes `fieldIndex` required
constructor(fieldIndex: number) { constructor(fieldIndex: number) {
super(fieldIndex); super(fieldIndex);

View File

@ -3,6 +3,7 @@ import type { BattlerIndex } from "#app/battle";
import { PokemonPhase } from "./pokemon-phase"; import { PokemonPhase } from "./pokemon-phase";
export class ShinySparklePhase extends PokemonPhase { export class ShinySparklePhase extends PokemonPhase {
public readonly phaseName = "ShinySparklePhase";
// biome-ignore lint/complexity/noUselessConstructor: This makes `battlerIndex` required // biome-ignore lint/complexity/noUselessConstructor: This makes `battlerIndex` required
constructor(battlerIndex: BattlerIndex) { constructor(battlerIndex: BattlerIndex) {
super(battlerIndex); super(battlerIndex);

View File

@ -5,6 +5,7 @@ import { getPokemonNameWithAffix } from "#app/messages";
import { HideAbilityPhase } from "#app/phases/hide-ability-phase"; import { HideAbilityPhase } from "#app/phases/hide-ability-phase";
export class ShowAbilityPhase extends PokemonPhase { export class ShowAbilityPhase extends PokemonPhase {
public readonly phaseName = "ShowAbilityPhase";
private passive: boolean; private passive: boolean;
private pokemonName: string; private pokemonName: string;
private abilityName: string; private abilityName: string;

View File

@ -8,6 +8,7 @@ import { LevelUpPhase } from "./level-up-phase";
import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase";
export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase { export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase {
public readonly phaseName = "ShowPartyExpBarPhase";
private expValue: number; private expValue: number;
constructor(partyMemberIndex: number, expValue: number) { constructor(partyMemberIndex: number, expValue: number) {

View File

@ -3,6 +3,7 @@ import { PlayerGender } from "#app/enums/player-gender";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
export class ShowTrainerPhase extends BattlePhase { export class ShowTrainerPhase extends BattlePhase {
public readonly phaseName = "ShowTrainerPhase";
start() { start() {
super.start(); super.start();

View File

@ -31,6 +31,7 @@ export type StatStageChangeCallback = (
) => void; ) => void;
export class StatStageChangePhase extends PokemonPhase { export class StatStageChangePhase extends PokemonPhase {
public readonly phaseName = "StatStageChangePhase";
private stats: BattleStat[]; private stats: BattleStat[];
private selfTarget: boolean; private selfTarget: boolean;
private stages: number; private stages: number;
@ -235,9 +236,9 @@ export class StatStageChangePhase extends PokemonPhase {
// Look for any other stat change phases; if this is the last one, do White Herb check // Look for any other stat change phases; if this is the last one, do White Herb check
const existingPhase = globalScene.findPhase( const existingPhase = globalScene.findPhase(
p => p instanceof StatStageChangePhase && p.battlerIndex === this.battlerIndex, p => p.is("StatStageChangePhase") && p.battlerIndex === this.battlerIndex,
); );
if (!(existingPhase instanceof StatStageChangePhase)) { if (!existingPhase?.is("StatStageChangePhase")) {
// Apply White Herb if needed // Apply White Herb if needed
const whiteHerb = globalScene.applyModifier( const whiteHerb = globalScene.applyModifier(
ResetNegativeStatStageModifier, ResetNegativeStatStageModifier,
@ -316,7 +317,7 @@ export class StatStageChangePhase extends PokemonPhase {
while ( while (
(existingPhase = globalScene.findPhase( (existingPhase = globalScene.findPhase(
p => p =>
p instanceof StatStageChangePhase && p.is("StatStageChangePhase") &&
p.battlerIndex === this.battlerIndex && p.battlerIndex === this.battlerIndex &&
p.stats.length === 1 && p.stats.length === 1 &&
p.stats[0] === this.stats[0] && p.stats[0] === this.stats[0] &&
@ -335,7 +336,7 @@ export class StatStageChangePhase extends PokemonPhase {
while ( while (
(existingPhase = globalScene.findPhase( (existingPhase = globalScene.findPhase(
p => p =>
p instanceof StatStageChangePhase && p.is("StatStageChangePhase") &&
p.battlerIndex === this.battlerIndex && p.battlerIndex === this.battlerIndex &&
p.selfTarget === this.selfTarget && p.selfTarget === this.selfTarget &&
accEva.some(s => p.stats.includes(s)) === isAccEva && accEva.some(s => p.stats.includes(s)) === isAccEva &&

View File

@ -4,6 +4,7 @@ import { SummonPhase } from "./summon-phase";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
export class SummonMissingPhase extends SummonPhase { export class SummonMissingPhase extends SummonPhase {
public readonly phaseName = "SummonMissingPhase";
preSummon(): void { preSummon(): void {
globalScene.ui.showText( globalScene.ui.showText(
i18next.t("battle:sendOutPokemon", { i18next.t("battle:sendOutPokemon", {

View File

@ -17,6 +17,8 @@ import { applyPreSummonAbAttrs, PreSummonAbAttr } from "#app/data/abilities/abil
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
export class SummonPhase extends PartyMemberPokemonPhase { export class SummonPhase extends PartyMemberPokemonPhase {
// The union type is needed to keep typescript happy as these phases extend from SummonPhase
public readonly phaseName: "SummonPhase" | "SummonMissingPhase" | "SwitchSummonPhase" | "ReturnPhase" = "SummonPhase";
private loaded: boolean; private loaded: boolean;
constructor(fieldIndex: number, player = true, loaded = false) { constructor(fieldIndex: number, player = true, loaded = false) {

View File

@ -4,6 +4,7 @@ import { getBiomeKey } from "#app/field/arena";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
export class SwitchBiomePhase extends BattlePhase { export class SwitchBiomePhase extends BattlePhase {
public readonly phaseName = "SwitchBiomePhase";
private nextBiome: BiomeId; private nextBiome: BiomeId;
constructor(nextBiome: BiomeId) { constructor(nextBiome: BiomeId) {

View File

@ -3,7 +3,6 @@ import PartyUiHandler, { PartyOption, PartyUiMode } from "#app/ui/party-ui-handl
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import { SwitchType } from "#enums/switch-type"; import { SwitchType } from "#enums/switch-type";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
import { PostSummonPhase } from "./post-summon-phase";
import { SwitchSummonPhase } from "./switch-summon-phase"; import { SwitchSummonPhase } from "./switch-summon-phase";
/** /**
@ -11,6 +10,7 @@ import { SwitchSummonPhase } from "./switch-summon-phase";
* for the player (if a switch would be valid for the current battle state). * for the player (if a switch would be valid for the current battle state).
*/ */
export class SwitchPhase extends BattlePhase { export class SwitchPhase extends BattlePhase {
public readonly phaseName = "SwitchPhase";
protected readonly fieldIndex: number; protected readonly fieldIndex: number;
private readonly switchType: SwitchType; private readonly switchType: SwitchType;
private readonly isModal: boolean; private readonly isModal: boolean;
@ -76,7 +76,7 @@ export class SwitchPhase extends BattlePhase {
if (slotIndex >= globalScene.currentBattle.getBattlerCount() && slotIndex < 6) { if (slotIndex >= globalScene.currentBattle.getBattlerCount() && slotIndex < 6) {
// Remove any pre-existing PostSummonPhase under the same field index. // Remove any pre-existing PostSummonPhase under the same field index.
// Pre-existing PostSummonPhases may occur when this phase is invoked during a prompt to switch at the start of a wave. // Pre-existing PostSummonPhases may occur when this phase is invoked during a prompt to switch at the start of a wave.
globalScene.tryRemovePhase(p => p instanceof PostSummonPhase && p.player && p.fieldIndex === this.fieldIndex); globalScene.tryRemovePhase(p => p.is("PostSummonPhase") && p.player && p.fieldIndex === this.fieldIndex);
const switchType = option === PartyOption.PASS_BATON ? SwitchType.BATON_PASS : this.switchType; const switchType = option === PartyOption.PASS_BATON ? SwitchType.BATON_PASS : this.switchType;
globalScene.unshiftPhase(new SwitchSummonPhase(switchType, fieldIndex, slotIndex, this.doReturn)); globalScene.unshiftPhase(new SwitchSummonPhase(switchType, fieldIndex, slotIndex, this.doReturn));
} }

View File

@ -22,6 +22,7 @@ import { SubstituteTag } from "#app/data/battler-tags";
import { SwitchType } from "#enums/switch-type"; import { SwitchType } from "#enums/switch-type";
export class SwitchSummonPhase extends SummonPhase { export class SwitchSummonPhase extends SummonPhase {
public readonly phaseName: "SwitchSummonPhase" | "ReturnPhase" = "SwitchSummonPhase";
private readonly switchType: SwitchType; private readonly switchType: SwitchType;
private readonly slotIndex: number; private readonly slotIndex: number;
private readonly doReturn: boolean; private readonly doReturn: boolean;

View File

@ -9,6 +9,7 @@ import { SpeciesFormChangeTeraTrigger } from "#app/data/pokemon-forms";
import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims"; import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims";
export class TeraPhase extends BattlePhase { export class TeraPhase extends BattlePhase {
public readonly phaseName = "TeraPhase";
public pokemon: Pokemon; public pokemon: Pokemon;
constructor(pokemon: Pokemon) { constructor(pokemon: Pokemon) {

View File

@ -29,6 +29,7 @@ import { globalScene } from "#app/global-scene";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
export class TitlePhase extends Phase { export class TitlePhase extends Phase {
public readonly phaseName = "TitlePhase";
private loaded = false; private loaded = false;
private lastSessionData: SessionSaveData; private lastSessionData: SessionSaveData;
public gameMode: GameModes; public gameMode: GameModes;

View File

@ -3,6 +3,7 @@ import { FieldPosition } from "#app/field/pokemon";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
export class ToggleDoublePositionPhase extends BattlePhase { export class ToggleDoublePositionPhase extends BattlePhase {
public readonly phaseName = "ToggleDoublePositionPhase";
private double: boolean; private double: boolean;
constructor(double: boolean) { constructor(double: boolean) {

View File

@ -14,6 +14,7 @@ import { achvs } from "#app/system/achv";
import { timedEventManager } from "#app/global-event-manager"; import { timedEventManager } from "#app/global-event-manager";
export class TrainerVictoryPhase extends BattlePhase { export class TrainerVictoryPhase extends BattlePhase {
public readonly phaseName = "TrainerVictoryPhase";
start() { start() {
globalScene.disableMenu = true; globalScene.disableMenu = true;

View File

@ -18,6 +18,7 @@ import { PokemonHealPhase } from "./pokemon-heal-phase";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
export class TurnEndPhase extends FieldPhase { export class TurnEndPhase extends FieldPhase {
public readonly phaseName = "TurnEndPhase";
start() { start() {
super.start(); super.start();

View File

@ -15,6 +15,7 @@ import { TurnStartPhase } from "./turn-start-phase";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
export class TurnInitPhase extends FieldPhase { export class TurnInitPhase extends FieldPhase {
public readonly phaseName = "TurnInitPhase";
start() { start() {
super.start(); super.start();

View File

@ -25,6 +25,7 @@ import { globalScene } from "#app/global-scene";
import { TeraPhase } from "./tera-phase"; import { TeraPhase } from "./tera-phase";
export class TurnStartPhase extends FieldPhase { export class TurnStartPhase extends FieldPhase {
public readonly phaseName = "TurnStartPhase";
/** /**
* This orders the active Pokemon on the field by speed into an BattlerIndex array and returns that array. * This orders the active Pokemon on the field by speed into an BattlerIndex array and returns that array.
* It also checks for Trick Room and reverses the array if it is present. * It also checks for Trick Room and reverses the array if it is present.

View File

@ -4,6 +4,7 @@ import { UiMode } from "#enums/ui-mode";
import { LoginPhase } from "./login-phase"; import { LoginPhase } from "./login-phase";
export class UnavailablePhase extends Phase { export class UnavailablePhase extends Phase {
public readonly phaseName = "UnavailablePhase";
start(): void { start(): void {
globalScene.ui.setMode(UiMode.UNAVAILABLE, () => { globalScene.ui.setMode(UiMode.UNAVAILABLE, () => {
globalScene.unshiftPhase(new LoginPhase(true)); globalScene.unshiftPhase(new LoginPhase(true));

View File

@ -6,6 +6,7 @@ import { UiMode } from "#enums/ui-mode";
import i18next from "i18next"; import i18next from "i18next";
export class UnlockPhase extends Phase { export class UnlockPhase extends Phase {
public readonly phaseName = "UnlockPhase";
private unlockable: Unlockables; private unlockable: Unlockables;
constructor(unlockable: Unlockables) { constructor(unlockable: Unlockables) {

View File

@ -18,6 +18,7 @@ import { timedEventManager } from "#app/global-event-manager";
import { SelectBiomePhase } from "./select-biome-phase"; import { SelectBiomePhase } from "./select-biome-phase";
export class VictoryPhase extends PokemonPhase { export class VictoryPhase extends PokemonPhase {
public readonly phaseName = "VictoryPhase";
/** If true, indicates that the phase is intended for EXP purposes only, and not to continue a battle to next phase */ /** If true, indicates that the phase is intended for EXP purposes only, and not to continue a battle to next phase */
isExpOnly: boolean; isExpOnly: boolean;

View File

@ -19,6 +19,7 @@ import { BooleanHolder, toDmgValue } from "#app/utils/common";
import { CommonAnimPhase } from "./common-anim-phase"; import { CommonAnimPhase } from "./common-anim-phase";
export class WeatherEffectPhase extends CommonAnimPhase { export class WeatherEffectPhase extends CommonAnimPhase {
public readonly phaseName = "WeatherEffectPhase";
public weather: Weather | null; public weather: Weather | null;
constructor() { constructor() {

View File

@ -179,11 +179,11 @@ interface Unlocks {
[key: number]: boolean; [key: number]: boolean;
} }
interface AchvUnlocks { export interface AchvUnlocks {
[key: string]: number; [key: string]: number;
} }
interface VoucherUnlocks { export interface VoucherUnlocks {
[key: string]: number; [key: string]: number;
} }

View File

@ -11,20 +11,20 @@ import { addWindow } from "#app/ui/ui-theme";
import { ScrollBar } from "#app/ui/scroll-bar"; import { ScrollBar } from "#app/ui/scroll-bar";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import type { AchvUnlocks, VoucherUnlocks } from "#app/system/game-data";
enum Page { const Page = {
ACHIEVEMENTS, ACHIEVEMENTS: 0,
VOUCHERS, VOUCHERS: 1,
} } as const;
type Page = (typeof Page)[keyof typeof Page];
interface LanguageSetting { interface LanguageSetting {
TextSize: string; TextSize: string;
} }
const languageSettings: { [key: string]: LanguageSetting } = { const languageSettings: { [key: string]: LanguageSetting } = {
de: { de: { TextSize: "80px" },
TextSize: "80px",
},
}; };
export default class AchvsUiHandler extends MessageUiHandler { export default class AchvsUiHandler extends MessageUiHandler {
@ -70,44 +70,35 @@ export default class AchvsUiHandler extends MessageUiHandler {
setup() { setup() {
const ui = this.getUi(); const ui = this.getUi();
this.mainContainer = globalScene.add.container(1, -(globalScene.game.canvas.height / 6) + 1); /** Width of the global canvas / 6 */
const WIDTH = globalScene.game.canvas.width / 6;
/** Height of the global canvas / 6 */
const HEIGHT = globalScene.game.canvas.height / 6;
this.mainContainer.setInteractive( this.mainContainer = globalScene.add.container(1, -HEIGHT + 1);
new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6),
Phaser.Geom.Rectangle.Contains,
);
this.headerBg = addWindow(0, 0, globalScene.game.canvas.width / 6 - 2, 24); this.mainContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, WIDTH, HEIGHT), Phaser.Geom.Rectangle.Contains);
this.headerBg.setOrigin(0, 0);
this.headerText = addTextObject(0, 0, "", TextStyle.SETTINGS_LABEL); this.headerBg = addWindow(0, 0, WIDTH - 2, 24);
this.headerText.setOrigin(0, 0);
this.headerText.setPositionRelative(this.headerBg, 8, 4); this.headerText = addTextObject(0, 0, "", TextStyle.SETTINGS_LABEL)
this.headerActionButton = new Phaser.GameObjects.Sprite(globalScene, 0, 0, "keyboard", "ACTION.png"); .setOrigin(0)
this.headerActionButton.setOrigin(0, 0); .setPositionRelative(this.headerBg, 8, 4);
this.headerActionButton.setPositionRelative(this.headerBg, 236, 6); this.headerActionButton = new Phaser.GameObjects.Sprite(globalScene, 0, 0, "keyboard", "ACTION.png")
this.headerActionText = addTextObject(0, 0, "", TextStyle.WINDOW, { .setOrigin(0)
fontSize: "60px", .setPositionRelative(this.headerBg, 236, 6);
}); this.headerActionText = addTextObject(0, 0, "", TextStyle.WINDOW, { fontSize: "60px" })
this.headerActionText.setOrigin(0, 0); .setOrigin(0)
this.headerActionText.setPositionRelative(this.headerBg, 264, 8); .setPositionRelative(this.headerBg, 264, 8);
// We need to get the player gender from the game data to add the correct prefix to the achievement name // We need to get the player gender from the game data to add the correct prefix to the achievement name
const genderIndex = globalScene.gameData.gender ?? PlayerGender.MALE; const genderIndex = globalScene.gameData.gender ?? PlayerGender.MALE;
const genderStr = PlayerGender[genderIndex].toLowerCase(); const genderStr = PlayerGender[genderIndex].toLowerCase();
this.achvsName = i18next.t("achv:Achievements.name", { this.achvsName = i18next.t("achv:Achievements.name", { context: genderStr });
context: genderStr,
});
this.vouchersName = i18next.t("voucher:vouchers"); this.vouchersName = i18next.t("voucher:vouchers");
this.iconsBg = addWindow( this.iconsBg = addWindow(0, this.headerBg.height, WIDTH - 2, HEIGHT - this.headerBg.height - 68).setOrigin(0);
0,
this.headerBg.height,
globalScene.game.canvas.width / 6 - 2,
globalScene.game.canvas.height / 6 - this.headerBg.height - 68,
);
this.iconsBg.setOrigin(0, 0);
const yOffset = 6; const yOffset = 6;
this.scrollBar = new ScrollBar( this.scrollBar = new ScrollBar(
@ -126,68 +117,59 @@ export default class AchvsUiHandler extends MessageUiHandler {
const x = (a % this.COLS) * 18; const x = (a % this.COLS) * 18;
const y = Math.floor(a / this.COLS) * 18; const y = Math.floor(a / this.COLS) * 18;
const icon = globalScene.add.sprite(x, y, "items", "unknown"); const icon = globalScene.add.sprite(x, y, "items", "unknown").setOrigin(0).setScale(0.5);
icon.setOrigin(0, 0);
icon.setScale(0.5);
this.icons.push(icon); this.icons.push(icon);
this.iconsContainer.add(icon); this.iconsContainer.add(icon);
} }
const titleBg = addWindow(0, this.headerBg.height + this.iconsBg.height, 174, 24); const titleBg = addWindow(0, this.headerBg.height + this.iconsBg.height, 174, 24);
titleBg.setOrigin(0, 0);
this.titleBg = titleBg; this.titleBg = titleBg;
this.titleText = addTextObject(0, 0, "", TextStyle.WINDOW); this.titleText = addTextObject(0, 0, "", TextStyle.WINDOW).setOrigin();
const textSize = languageSettings[i18next.language]?.TextSize ?? this.titleText.style.fontSize; const textSize = languageSettings[i18next.language]?.TextSize ?? this.titleText.style.fontSize;
this.titleText.setFontSize(textSize); this.titleText.setFontSize(textSize);
const titleBgCenterX = titleBg.x + titleBg.width / 2; const titleBgCenterX = titleBg.x + titleBg.width / 2;
const titleBgCenterY = titleBg.y + titleBg.height / 2; const titleBgCenterY = titleBg.y + titleBg.height / 2;
this.titleText.setOrigin(0.5, 0.5);
this.titleText.setPosition(titleBgCenterX, titleBgCenterY); this.titleText.setPosition(titleBgCenterX, titleBgCenterY);
this.scoreContainer = globalScene.add.container(titleBg.x + titleBg.width, titleBg.y); this.scoreContainer = globalScene.add.container(titleBg.x + titleBg.width, titleBg.y);
const scoreBg = addWindow(0, 0, 46, 24); const scoreBg = addWindow(0, 0, 46, 24);
scoreBg.setOrigin(0, 0);
this.scoreContainer.add(scoreBg);
this.scoreText = addTextObject(scoreBg.width / 2, scoreBg.height / 2, "", TextStyle.WINDOW); this.scoreText = addTextObject(scoreBg.width / 2, scoreBg.height / 2, "", TextStyle.WINDOW).setOrigin();
this.scoreText.setOrigin(0.5, 0.5); this.scoreContainer.add([scoreBg, this.scoreText]);
this.scoreContainer.add(this.scoreText);
const unlockBg = addWindow(this.scoreContainer.x + scoreBg.width, titleBg.y, 98, 24); const unlockBg = addWindow(this.scoreContainer.x + scoreBg.width, titleBg.y, 98, 24);
unlockBg.setOrigin(0, 0);
this.unlockText = addTextObject(0, 0, "", TextStyle.WINDOW); this.unlockText = addTextObject(0, 0, "", TextStyle.WINDOW)
this.unlockText.setOrigin(0.5, 0.5); .setPositionRelative(unlockBg, unlockBg.width / 2, unlockBg.height / 2)
this.unlockText.setPositionRelative(unlockBg, unlockBg.width / 2, unlockBg.height / 2); .setOrigin();
const descriptionBg = addWindow(0, titleBg.y + titleBg.height, globalScene.game.canvas.width / 6 - 2, 42); const descriptionBg = addWindow(0, titleBg.y + titleBg.height, WIDTH - 2, 42);
descriptionBg.setOrigin(0, 0);
const descriptionText = addTextObject(0, 0, "", TextStyle.WINDOW, { const descriptionText = addTextObject(0, 0, "", TextStyle.WINDOW, { maxLines: 2 })
maxLines: 2, .setWordWrapWidth(1870)
}); .setOrigin(0)
descriptionText.setWordWrapWidth(1870); .setPositionRelative(descriptionBg, 8, 4);
descriptionText.setOrigin(0, 0);
descriptionText.setPositionRelative(descriptionBg, 8, 4);
this.message = descriptionText; this.message = descriptionText;
this.mainContainer.add(this.headerBg); this.mainContainer.add([
this.mainContainer.add(this.headerActionButton); this.headerBg,
this.mainContainer.add(this.headerText); this.headerActionButton,
this.mainContainer.add(this.headerActionText); this.headerText,
this.mainContainer.add(this.iconsBg); this.headerActionText,
this.mainContainer.add(this.scrollBar); this.iconsBg,
this.mainContainer.add(this.iconsContainer); this.scrollBar,
this.mainContainer.add(titleBg); this.iconsContainer,
this.mainContainer.add(this.titleText); titleBg,
this.mainContainer.add(this.scoreContainer); this.titleText,
this.mainContainer.add(unlockBg); this.scoreContainer,
this.mainContainer.add(this.unlockText); unlockBg,
this.mainContainer.add(descriptionBg); this.unlockText,
this.mainContainer.add(descriptionText); descriptionBg,
descriptionText,
]);
ui.add(this.mainContainer); ui.add(this.mainContainer);
@ -246,13 +228,12 @@ export default class AchvsUiHandler extends MessageUiHandler {
); );
} }
processInput(button: Button): boolean { // #region Input Processing
const ui = this.getUi(); /**
* Submethod of {@linkcode processInput} that handles the action button input
let success = false; * @returns Whether the success sound should be played
*/
if (button === Button.ACTION) { private processActionInput(): true {
success = true;
this.setScrollCursor(0); this.setScrollCursor(0);
if (this.currentPage === Page.ACHIEVEMENTS) { if (this.currentPage === Page.ACHIEVEMENTS) {
this.currentPage = Page.VOUCHERS; this.currentPage = Page.VOUCHERS;
@ -265,68 +246,114 @@ export default class AchvsUiHandler extends MessageUiHandler {
this.scrollBar.setTotalRows(Math.ceil(this.currentTotal / this.COLS)); this.scrollBar.setTotalRows(Math.ceil(this.currentTotal / this.COLS));
this.scrollBar.setScrollCursor(0); this.scrollBar.setScrollCursor(0);
this.mainContainer.update(); this.mainContainer.update();
return true;
}
/**
* Submethod of {@linkcode processInput} that handles the up button input
* @returns Whether the success sound should be played
*/
private processUpInput(): boolean {
if (this.cursor >= this.COLS) {
return this.setCursor(this.cursor - this.COLS);
} }
if (button === Button.CANCEL) {
success = true;
globalScene.ui.revertMode();
} else {
const rowIndex = Math.floor(this.cursor / this.COLS);
const itemOffset = this.scrollCursor * this.COLS;
switch (button) {
case Button.UP:
if (this.cursor < this.COLS) {
if (this.scrollCursor) { if (this.scrollCursor) {
success = this.setScrollCursor(this.scrollCursor - 1); return this.setScrollCursor(this.scrollCursor - 1);
} else { }
// Wrap around to the last row // Wrap around to the last row
success = this.setScrollCursor(Math.ceil(this.currentTotal / this.COLS) - this.ROWS); const success = this.setScrollCursor(Math.ceil(this.currentTotal / this.COLS) - this.ROWS);
let newCursorIndex = this.cursor + (this.ROWS - 1) * this.COLS; let newCursorIndex = this.cursor + (this.ROWS - 1) * this.COLS;
if (newCursorIndex > this.currentTotal - this.scrollCursor * this.COLS - 1) { if (newCursorIndex > this.currentTotal - this.scrollCursor * this.COLS - 1) {
newCursorIndex -= this.COLS; newCursorIndex -= this.COLS;
} }
success = success && this.setCursor(newCursorIndex); return success && this.setCursor(newCursorIndex);
} }
} else {
success = this.setCursor(this.cursor - this.COLS); /**
} * Submethod of {@linkcode processInput} that handles the down button input
break; * @returns Whether the success sound should be played
case Button.DOWN: */
private processDownInput(): boolean {
const rowIndex = Math.floor(this.cursor / this.COLS);
const itemOffset = this.scrollCursor * this.COLS;
const canMoveDown = itemOffset + 1 < this.currentTotal; const canMoveDown = itemOffset + 1 < this.currentTotal;
if (rowIndex >= this.ROWS - 1) { if (rowIndex >= this.ROWS - 1) {
if (this.scrollCursor < Math.ceil(this.currentTotal / this.COLS) - this.ROWS && canMoveDown) { if (this.scrollCursor < Math.ceil(this.currentTotal / this.COLS) - this.ROWS && canMoveDown) {
// scroll down one row // scroll down one row
success = this.setScrollCursor(this.scrollCursor + 1); return this.setScrollCursor(this.scrollCursor + 1);
} else { }
// wrap back to the first row // wrap back to the first row
success = this.setScrollCursor(0) && this.setCursor(this.cursor % this.COLS); return this.setScrollCursor(0) && this.setCursor(this.cursor % this.COLS);
} }
} else if (canMoveDown) { if (canMoveDown) {
success = this.setCursor(Math.min(this.cursor + this.COLS, this.currentTotal - itemOffset - 1)); return this.setCursor(Math.min(this.cursor + this.COLS, this.currentTotal - itemOffset - 1));
} }
return false;
}
/**
* Submethod of {@linkcode processInput} that handles the left button input
* @returns Whether the success sound should be played
*/
private processLeftInput(): boolean {
const itemOffset = this.scrollCursor * this.COLS;
if (this.cursor % this.COLS === 0) {
return this.setCursor(Math.min(this.cursor + this.COLS - 1, this.currentTotal - itemOffset - 1));
}
return this.setCursor(this.cursor - 1);
}
/**
* Submethod of {@linkcode processInput} that handles the right button input
* @returns Whether the success sound should be played
*/
private processRightInput(): boolean {
const itemOffset = this.scrollCursor * this.COLS;
if ((this.cursor + 1) % this.COLS === 0 || this.cursor + itemOffset === this.currentTotal - 1) {
return this.setCursor(this.cursor - (this.cursor % this.COLS));
}
return this.setCursor(this.cursor + 1);
}
/**
* Process user input to navigate through the achievements and vouchers UI.
* @param button - The button that was pressed
* @returns Whether an action was successfully processed
*/
processInput(button: Button): boolean {
let success = false;
switch (button) {
case Button.ACTION:
success = this.processActionInput();
break;
case Button.CANCEL:
success = true;
globalScene.ui.revertMode();
break;
case Button.UP:
success = this.processUpInput();
break;
case Button.DOWN:
success = this.processDownInput();
break; break;
case Button.LEFT: case Button.LEFT:
if (this.cursor % this.COLS === 0) { success = this.processLeftInput();
success = this.setCursor(Math.min(this.cursor + this.COLS - 1, this.currentTotal - itemOffset - 1));
} else {
success = this.setCursor(this.cursor - 1);
}
break; break;
case Button.RIGHT: case Button.RIGHT:
if ((this.cursor + 1) % this.COLS === 0 || this.cursor + itemOffset === this.currentTotal - 1) { success = this.processRightInput();
success = this.setCursor(this.cursor - (this.cursor % this.COLS));
} else {
success = this.setCursor(this.cursor + 1);
}
break; break;
} }
}
if (success) { if (success) {
ui.playSelect(); this.getUi().playSelect();
} }
return success; return success;
} }
// #endregion Input Processing
setCursor(cursor: number, pageChange?: boolean): boolean { setCursor(cursor: number, pageChange?: boolean): boolean {
const ret = super.setCursor(cursor); const ret = super.setCursor(cursor);
@ -334,15 +361,18 @@ export default class AchvsUiHandler extends MessageUiHandler {
let update = ret; let update = ret;
if (!this.cursorObj) { if (!this.cursorObj) {
this.cursorObj = globalScene.add.nineslice(0, 0, "select_cursor_highlight", undefined, 16, 16, 1, 1, 1, 1); this.cursorObj = globalScene.add
this.cursorObj.setOrigin(0, 0); .nineslice(0, 0, "select_cursor_highlight", undefined, 16, 16, 1, 1, 1, 1)
.setOrigin(0);
this.iconsContainer.add(this.cursorObj); this.iconsContainer.add(this.cursorObj);
update = true; update = true;
} }
this.cursorObj.setPositionRelative(this.icons[this.cursor], 0, 0); this.cursorObj.setPositionRelative(this.icons[this.cursor], 0, 0);
if (!update && !pageChange) {
return ret;
}
if (update || pageChange) {
switch (this.currentPage) { switch (this.currentPage) {
case Page.ACHIEVEMENTS: case Page.ACHIEVEMENTS:
if (pageChange) { if (pageChange) {
@ -361,7 +391,6 @@ export default class AchvsUiHandler extends MessageUiHandler {
this.showVoucher(vouchers[Object.keys(vouchers)[cursor + this.scrollCursor * this.COLS]]); this.showVoucher(vouchers[Object.keys(vouchers)[cursor + this.scrollCursor * this.COLS]]);
break; break;
} }
}
return ret; return ret;
} }
@ -399,30 +428,50 @@ export default class AchvsUiHandler extends MessageUiHandler {
} }
/** /**
* updateAchvIcons(): void * Updates the icons displayed on the UI based on the current page and scroll cursor.
* Determines what data is to be displayed on the UI and updates it accordingly based on the current value of this.scrollCursor * @param items - The items to display (achievements or vouchers).
* @param unlocks - The unlocks data for the items.
* @param getIconFrame - A function to determine the frame for each item.
* @param headerText - The text for the header.
* @param actionText - The text for the action button.
* @param totalItems - The total number of items.
* @param forAchievements - `True` when updating icons for the achievements page, `false` for the vouchers page.
*/ */
updateAchvIcons(): void { private updateIcons<T extends boolean>(
this.headerText.text = this.achvsName; items: T extends true ? Achv[] : Voucher[],
this.headerActionText.text = this.vouchersName; unlocks: T extends true ? AchvUnlocks : VoucherUnlocks,
headerText: string,
actionText: string,
totalItems: number,
forAchievements: T,
): void {
// type ItemType = T extends true ? Achv : Voucher;
// type RangeType = ItemType[];
this.headerText.text = headerText;
this.headerActionText.text = actionText;
const textPosition = this.headerBgX - this.headerActionText.displayWidth - 8; const textPosition = this.headerBgX - this.headerActionText.displayWidth - 8;
this.headerActionText.setX(textPosition); this.headerActionText.setX(textPosition);
this.headerActionButton.setX(textPosition - this.headerActionButton.displayWidth - 4); this.headerActionButton.setX(textPosition - this.headerActionButton.displayWidth - 4);
const achvUnlocks = globalScene.gameData.achvUnlocks;
const itemOffset = this.scrollCursor * this.COLS; const itemOffset = this.scrollCursor * this.COLS;
const itemLimit = this.ROWS * this.COLS; const itemLimit = this.ROWS * this.COLS;
const achvRange = Object.values(achvs).slice(itemOffset, itemLimit + itemOffset); const itemRange = items.slice(itemOffset, itemLimit + itemOffset);
achvRange.forEach((achv: Achv, i: number) => { itemRange.forEach((item: (typeof itemRange)[0], i: number) => {
const icon = this.icons[i]; const icon = this.icons[i];
const unlocked = achvUnlocks.hasOwnProperty(achv.id); const unlocked = unlocks.hasOwnProperty(item.id);
const hidden = !unlocked && achv.secret && (!achv.parentId || !achvUnlocks.hasOwnProperty(achv.parentId)); let tinted = !unlocked;
const tinted = !hidden && !unlocked; if (forAchievements) {
// Typescript cannot properly infer the type of `item` here, so we need to cast it
const achv = item as Achv;
const hidden = !unlocked && achv.secret && (!achv.parentId || !unlocks.hasOwnProperty(achv.parentId));
tinted &&= !hidden;
icon.setFrame(!hidden ? achv.iconImage : "unknown"); icon.setFrame(!hidden ? achv.iconImage : "unknown");
} else {
icon.setFrame(getVoucherTypeIcon((item as Voucher).voucherType));
}
icon.setVisible(true); icon.setVisible(true);
if (tinted) { if (tinted) {
icon.setTintFill(0); icon.setTintFill(0);
@ -431,48 +480,39 @@ export default class AchvsUiHandler extends MessageUiHandler {
} }
}); });
if (achvRange.length < this.icons.length) { if (itemRange.length < this.icons.length) {
this.icons.slice(achvRange.length).map(i => i.setVisible(false)); this.icons.slice(itemRange.length).forEach(i => i.setVisible(false));
} }
this.currentTotal = this.achvsTotal; this.currentTotal = totalItems;
} }
/** /**
* updateVoucherIcons(): void * Update the achievement icons displayed on the UI based on the current scroll cursor.
* Determines what data is to be displayed on the UI and updates it accordingly based on the current value of this.scrollCursor */
updateAchvIcons(): void {
this.updateIcons(
Object.values(achvs),
globalScene.gameData.achvUnlocks,
this.achvsName,
this.vouchersName,
this.achvsTotal,
true,
);
}
/**
* Update the voucher icons displayed on the UI based on the current scroll cursor.
*/ */
updateVoucherIcons(): void { updateVoucherIcons(): void {
this.headerText.text = this.vouchersName; this.updateIcons(
this.headerActionText.text = this.achvsName; Object.values(vouchers),
const textPosition = this.headerBgX - this.headerActionText.displayWidth - 8; globalScene.gameData.voucherUnlocks,
this.headerActionText.setX(textPosition); this.vouchersName,
this.headerActionButton.setX(textPosition - this.headerActionButton.displayWidth - 4); this.achvsName,
this.vouchersTotal,
const voucherUnlocks = globalScene.gameData.voucherUnlocks; false,
);
const itemOffset = this.scrollCursor * this.COLS;
const itemLimit = this.ROWS * this.COLS;
const voucherRange = Object.values(vouchers).slice(itemOffset, itemLimit + itemOffset);
voucherRange.forEach((voucher: Voucher, i: number) => {
const icon = this.icons[i];
const unlocked = voucherUnlocks.hasOwnProperty(voucher.id);
icon.setFrame(getVoucherTypeIcon(voucher.voucherType));
icon.setVisible(true);
if (!unlocked) {
icon.setTintFill(0);
} else {
icon.clearTint();
}
});
if (voucherRange.length < this.icons.length) {
this.icons.slice(voucherRange.length).map(i => i.setVisible(false));
}
this.currentTotal = this.vouchersTotal;
} }
clear() { clear() {

Some files were not shown because too many files have changed in this diff Show More