Compare commits

...

4 Commits

Author SHA1 Message Date
Bertie690
d10b1bce42
Merge 04734bd9bf into 0da37a0f0c 2025-08-13 13:09:55 -04:00
Bertie690
0da37a0f0c
[Move] Added laser focus locales (#6202)
* Added Laser Focus locales

* Fixed key for locales text

* Added `MessageAttr`; cleaned up a lot of other jank move attrs
2025-08-13 08:16:08 -07:00
Bertie690
04734bd9bf Removed bangs from various calls to phase manager 2025-08-09 11:18:31 -04:00
Bertie690
e97ef78497 Added toBeAtPhase + removed null from phase manager current phase signature 2025-08-09 10:33:21 -04:00
51 changed files with 309 additions and 254 deletions

View File

@ -1,13 +1,24 @@
import type { Pokemon } from "#field/pokemon";
import type {
AttackMove,
ChargingAttackMove,
ChargingSelfStatusMove,
Move,
MoveAttr,
MoveAttrConstructorMap,
SelfStatusMove,
StatusMove,
} from "#moves/move";
/**
* A generic function producing a message during a Move's execution.
* @param user - The {@linkcode Pokemon} using the move
* @param target - The {@linkcode Pokemon} targeted by the move
* @param move - The {@linkcode Move} being used
* @returns a string
*/
export type MoveMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => string;
export type MoveAttrFilter = (attr: MoveAttr) => boolean;
export type * from "#moves/move";

View File

@ -1569,9 +1569,9 @@ export class BattleScene extends SceneBase {
return 0;
}
const isEggPhase: boolean = ["EggLapsePhase", "EggHatchPhase"].includes(
this.phaseManager.getCurrentPhase()?.phaseName ?? "",
);
const isEggPhase =
this.phaseManager.getCurrentPhase().is("EggLapsePhase") ||
this.phaseManager.getCurrentPhase().is("EggHatchPhase");
if (
// Give trainers with specialty types an appropriately-typed form for Wormadam, Rotom, Arceus, Oricorio, Silvally, or Paldean Tauros.

View File

@ -1670,6 +1670,7 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr {
constructor(
private newType: PokemonType,
private powerMultiplier: number,
// TODO: all moves with this attr solely check the move being used...
private condition?: PokemonAttackCondition,
) {
super(false);

View File

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

View File

@ -227,11 +227,15 @@ interface GenericSerializableBattlerTag<T extends BattlerTagType> extends Serial
* Descendants can override {@linkcode isMoveRestricted} to restrict moves that
* match a condition. A restricted move gets cancelled before it is used.
* Players and enemies should not be allowed to select restricted moves.
* @todo Require descendant subclasses to inherit a `PRE_MOVE` lapse type
*/
export abstract class MoveRestrictionBattlerTag extends SerializableBattlerTag {
public declare readonly tagType: MoveRestrictionBattlerTagType;
override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
if (lapseType === BattlerTagLapseType.PRE_MOVE) {
if (lapseType !== BattlerTagLapseType.PRE_MOVE) {
return super.lapse(pokemon, lapseType);
}
// Cancel the affected pokemon's selected move
const phase = globalScene.phaseManager.getCurrentPhase() as MovePhase;
const move = phase.move;
@ -246,9 +250,6 @@ export abstract class MoveRestrictionBattlerTag extends SerializableBattlerTag {
return true;
}
return super.lapse(pokemon, lapseType);
}
/**
* Determine whether a move's usage is restricted by this tag
*

View File

@ -86,7 +86,7 @@ import { PokemonHealPhase } from "#phases/pokemon-heal-phase";
import { SwitchSummonPhase } from "#phases/switch-summon-phase";
import type { AttackMoveResult } from "#types/attack-move-result";
import type { Localizable } from "#types/locales";
import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindString } from "#types/move-types";
import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindString, MoveMessageFunc } from "#types/move-types";
import type { TurnMove } from "#types/turn-move";
import { BooleanHolder, type Constructor, isNullOrUndefined, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common";
import { getEnumValues } from "#utils/enums";
@ -1357,20 +1357,20 @@ export class MoveHeaderAttr extends MoveAttr {
/**
* Header attribute to queue a message at the beginning of a turn.
* @see {@link MoveHeaderAttr}
*/
export class MessageHeaderAttr extends MoveHeaderAttr {
private message: string | ((user: Pokemon, move: Move) => string);
/** The message to display, or a function producing one. */
private message: string | MoveMessageFunc;
constructor(message: string | ((user: Pokemon, move: Move) => string)) {
constructor(message: string | MoveMessageFunc) {
super();
this.message = message;
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
apply(user: Pokemon, target: Pokemon, move: Move): boolean {
const message = typeof this.message === "string"
? this.message
: this.message(user, move);
: this.message(user, target, move);
if (message) {
globalScene.phaseManager.queueMessage(message);
@ -1418,21 +1418,21 @@ export class BeakBlastHeaderAttr extends AddBattlerTagHeaderAttr {
*/
export class PreMoveMessageAttr extends MoveAttr {
/** The message to display or a function returning one */
private message: string | ((user: Pokemon, target: Pokemon, move: Move) => string | undefined);
private message: string | MoveMessageFunc;
/**
* Create a new {@linkcode PreMoveMessageAttr} to display a message before move execution.
* @param message - The message to display before move use, either as a string or a function producing one.
* @param message - The message to display before move use, either` a literal string or a function producing one.
* @remarks
* If {@linkcode message} evaluates to an empty string (`''`), no message will be displayed
* If {@linkcode message} evaluates to an empty string (`""`), no message will be displayed
* (though the move will still succeed).
*/
constructor(message: string | ((user: Pokemon, target: Pokemon, move: Move) => string)) {
constructor(message: string | MoveMessageFunc) {
super();
this.message = message;
}
apply(user: Pokemon, target: Pokemon, move: Move, _args: any[]): boolean {
apply(user: Pokemon, target: Pokemon, move: Move): boolean {
const message = typeof this.message === "function"
? this.message(user, target, move)
: this.message;
@ -1453,18 +1453,17 @@ export class PreMoveMessageAttr extends MoveAttr {
* @extends MoveAttr
*/
export class PreUseInterruptAttr extends MoveAttr {
protected message?: string | ((user: Pokemon, target: Pokemon, move: Move) => string);
protected overridesFailedMessage: boolean;
protected message: string | MoveMessageFunc;
protected conditionFunc: MoveConditionFunc;
/**
* Create a new MoveInterruptedMessageAttr.
* @param message The message to display when the move is interrupted, or a function that formats the message based on the user, target, and move.
*/
constructor(message?: string | ((user: Pokemon, target: Pokemon, move: Move) => string), conditionFunc?: MoveConditionFunc) {
constructor(message: string | MoveMessageFunc, conditionFunc: MoveConditionFunc) {
super();
this.message = message;
this.conditionFunc = conditionFunc ?? (() => true);
this.conditionFunc = conditionFunc;
}
/**
@ -1485,11 +1484,9 @@ export class PreUseInterruptAttr extends MoveAttr {
*/
override getFailedText(user: Pokemon, target: Pokemon, move: Move): string | undefined {
if (this.message && this.conditionFunc(user, target, move)) {
const message =
typeof this.message === "string"
? (this.message as string)
return typeof this.message === "string"
? this.message
: this.message(user, target, move);
return message;
}
}
}
@ -1694,17 +1691,30 @@ export class SurviveDamageAttr extends ModifiedDamageAttr {
}
}
export class SplashAttr extends MoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:splash"));
/**
* Move attribute to display arbitrary text during a move's execution.
*/
export class MessageAttr extends MoveEffectAttr {
/** The message to display, either as a string or a function returning one. */
private message: string | MoveMessageFunc;
constructor(message: string | MoveMessageFunc, options?: MoveEffectAttrOptions) {
// TODO: Do we need to respect `selfTarget` if we're just displaying text?
super(false, options)
this.message = message;
}
override apply(user: Pokemon, target: Pokemon, move: Move): boolean {
const message = typeof this.message === "function"
? this.message(user, target, move)
: this.message;
// TODO: Consider changing if/when MoveAttr `apply` return values become significant
if (message) {
globalScene.phaseManager.queueMessage(message, 500);
return true;
}
}
export class CelebrateAttr extends MoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:celebrate", { playerName: loggedInUser?.username }));
return true;
return false;
}
}
@ -5931,38 +5941,6 @@ export class ProtectAttr extends AddBattlerTagAttr {
}
}
export class IgnoreAccuracyAttr extends AddBattlerTagAttr {
constructor() {
super(BattlerTagType.IGNORE_ACCURACY, true, false, 2);
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args)) {
return false;
}
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:tookAimAtTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) }));
return true;
}
}
export class FaintCountdownAttr extends AddBattlerTagAttr {
constructor() {
super(BattlerTagType.PERISH_SONG, false, true, 4);
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args)) {
return false;
}
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:faintCountdown", { pokemonName: getPokemonNameWithAffix(target), turnCount: this.turnCountMin - 1 }));
return true;
}
}
/**
* Attribute to remove all Substitutes from the field.
* @extends MoveEffectAttr
@ -6603,8 +6581,10 @@ export class ChillyReceptionAttr extends ForceSwitchOutAttr {
return (user, target, move) => globalScene.arena.weather?.weatherType !== WeatherType.SNOW || super.getSwitchOutCondition()(user, target, move);
}
}
export class RemoveTypeAttr extends MoveEffectAttr {
// TODO: Remove the message callback
private removedType: PokemonType;
private messageCallback: ((user: Pokemon) => void) | undefined;
@ -8299,8 +8279,6 @@ const MoveAttrs = Object.freeze({
RandomLevelDamageAttr,
ModifiedDamageAttr,
SurviveDamageAttr,
SplashAttr,
CelebrateAttr,
RecoilAttr,
SacrificialAttr,
SacrificialAttrOnHit,
@ -8443,8 +8421,7 @@ const MoveAttrs = Object.freeze({
RechargeAttr,
TrapAttr,
ProtectAttr,
IgnoreAccuracyAttr,
FaintCountdownAttr,
MessageAttr,
RemoveAllSubstitutesAttr,
HitsTagAttr,
HitsTagForDoubleDamageAttr,
@ -8938,7 +8915,7 @@ export function initMoves() {
new AttackMove(MoveId.PSYWAVE, PokemonType.PSYCHIC, MoveCategory.SPECIAL, -1, 100, 15, -1, 0, 1)
.attr(RandomLevelDamageAttr),
new SelfStatusMove(MoveId.SPLASH, PokemonType.NORMAL, -1, 40, -1, 0, 1)
.attr(SplashAttr)
.attr(MessageAttr, i18next.t("moveTriggers:splash"))
.condition(failOnGravityCondition),
new SelfStatusMove(MoveId.ACID_ARMOR, PokemonType.POISON, -1, 20, -1, 0, 1)
.attr(StatStageChangeAttr, [ Stat.DEF ], 2, true),
@ -9000,7 +8977,10 @@ export function initMoves() {
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1)
.reflectable(),
new StatusMove(MoveId.MIND_READER, PokemonType.NORMAL, -1, 5, -1, 0, 2)
.attr(IgnoreAccuracyAttr),
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_ACCURACY, true, false, 2)
.attr(MessageAttr, (user, target) =>
i18next.t("moveTriggers:tookAimAtTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })
),
new StatusMove(MoveId.NIGHTMARE, PokemonType.GHOST, 100, 15, -1, 0, 2)
.attr(AddBattlerTagAttr, BattlerTagType.NIGHTMARE)
.condition(targetSleptOrComatoseCondition),
@ -9088,7 +9068,9 @@ export function initMoves() {
return lastTurnMove.length === 0 || lastTurnMove[0].move !== move.id || lastTurnMove[0].result !== MoveResult.SUCCESS;
}),
new StatusMove(MoveId.PERISH_SONG, PokemonType.NORMAL, -1, 5, -1, 0, 2)
.attr(FaintCountdownAttr)
.attr(AddBattlerTagAttr, BattlerTagType.PERISH_SONG, false, true, 4)
.attr(MessageAttr, (_user, target) =>
i18next.t("moveTriggers:faintCountdown", { pokemonName: getPokemonNameWithAffix(target), turnCount: 3 }))
.ignoresProtect()
.soundBased()
.condition(failOnBossCondition)
@ -9104,7 +9086,10 @@ export function initMoves() {
.attr(MultiHitAttr)
.makesContact(false),
new StatusMove(MoveId.LOCK_ON, PokemonType.NORMAL, -1, 5, -1, 0, 2)
.attr(IgnoreAccuracyAttr),
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_ACCURACY, true, false, 2)
.attr(MessageAttr, (user, target) =>
i18next.t("moveTriggers:tookAimAtTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })
),
new AttackMove(MoveId.OUTRAGE, PokemonType.DRAGON, MoveCategory.PHYSICAL, 120, 100, 10, -1, 0, 2)
.attr(FrenzyAttr)
.attr(MissEffectAttr, frenzyMissFunc)
@ -9331,8 +9316,8 @@ export function initMoves() {
&& (user.status.effect === StatusEffect.BURN || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.PARALYSIS) ? 2 : 1)
.attr(BypassBurnDamageReductionAttr),
new AttackMove(MoveId.FOCUS_PUNCH, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 20, -1, -3, 3)
.attr(MessageHeaderAttr, (user, move) => i18next.t("moveTriggers:isTighteningFocus", { pokemonName: getPokemonNameWithAffix(user) }))
.attr(PreUseInterruptAttr, (user, target, move) => i18next.t("moveTriggers:lostFocus", { pokemonName: getPokemonNameWithAffix(user) }), user => !!user.turnData.attacksReceived.find(r => r.damage))
.attr(MessageHeaderAttr, (user) => i18next.t("moveTriggers:isTighteningFocus", { pokemonName: getPokemonNameWithAffix(user) }))
.attr(PreUseInterruptAttr, (user) => i18next.t("moveTriggers:lostFocus", { pokemonName: getPokemonNameWithAffix(user) }), user => user.turnData.attacksReceived.some(r => r.damage > 0))
.punchingMove(),
new AttackMove(MoveId.SMELLING_SALTS, PokemonType.NORMAL, MoveCategory.PHYSICAL, 70, 100, 10, -1, 0, 3)
.attr(MovePowerMultiplierAttr, (user, target, move) => target.status?.effect === StatusEffect.PARALYSIS ? 2 : 1)
@ -10433,7 +10418,8 @@ export function initMoves() {
new AttackMove(MoveId.DAZZLING_GLEAM, PokemonType.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 6)
.target(MoveTarget.ALL_NEAR_ENEMIES),
new SelfStatusMove(MoveId.CELEBRATE, PokemonType.NORMAL, -1, 40, -1, 0, 6)
.attr(CelebrateAttr),
// NB: This needs a lambda function as the user will not be logged in by the time the moves are initialized
.attr(MessageAttr, () => i18next.t("moveTriggers:celebrate", { playerName: loggedInUser?.username })),
new StatusMove(MoveId.HOLD_HANDS, PokemonType.NORMAL, -1, 40, -1, 0, 6)
.ignoresSubstitute()
.target(MoveTarget.NEAR_ALLY),
@ -10608,7 +10594,12 @@ export function initMoves() {
.attr(StatStageChangeAttr, [ Stat.SPD ], -1)
.reflectable(),
new SelfStatusMove(MoveId.LASER_FOCUS, PokemonType.NORMAL, -1, 30, -1, 0, 7)
.attr(AddBattlerTagAttr, BattlerTagType.ALWAYS_CRIT, true, false),
.attr(AddBattlerTagAttr, BattlerTagType.ALWAYS_CRIT, true, false)
.attr(MessageAttr, (user) =>
i18next.t("battlerTags:laserFocusOnAdd", {
pokemonNameWithAffix: getPokemonNameWithAffix(user),
}),
),
new StatusMove(MoveId.GEAR_UP, PokemonType.STEEL, -1, 20, -1, 0, 7)
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, false, { condition: (user, target, move) => !![ AbilityId.PLUS, AbilityId.MINUS ].find(a => target.hasAbility(a, false)) })
.ignoresSubstitute()

View File

@ -1245,7 +1245,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
}
// During the Pokemon's MoveEffect phase, the offset is removed to put the Pokemon "in focus"
const currentPhase = globalScene.phaseManager.getCurrentPhase();
return !(currentPhase?.is("MoveEffectPhase") && currentPhase.getPokemon() === this);
return !(currentPhase.is("MoveEffectPhase") && currentPhase.getPokemon() === this);
}
/** If this Pokemon has a Substitute on the field, removes its sprite from the field. */
@ -4929,7 +4929,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
*/
if (effect === StatusEffect.SLEEP || effect === StatusEffect.FREEZE) {
const currentPhase = globalScene.phaseManager.getCurrentPhase();
if (currentPhase?.is("MoveEffectPhase") && currentPhase.getUserPokemon() === this) {
if (currentPhase.is("MoveEffectPhase") && currentPhase.getUserPokemon() === this) {
this.turnData.hitCount = 1;
this.turnData.hitsLeft = 1;
}

View File

@ -236,7 +236,7 @@ export class PhaseManager {
/** Parallel array to {@linkcode dynamicPhaseQueues} - matches phase types to their queues */
private dynamicPhaseTypes: Constructor<Phase>[];
private currentPhase: Phase | null = null;
private currentPhase: Phase;
private standbyPhase: Phase | null = null;
constructor() {
@ -260,7 +260,12 @@ export class PhaseManager {
}
/* Phase Functions */
getCurrentPhase(): Phase | null {
/**
* Return the currently running {@linkcode Phase}.
* @returns The Phase currently in the process of running.
*/
getCurrentPhase(): Phase {
return this.currentPhase;
}
@ -363,13 +368,20 @@ export class PhaseManager {
}
}
}
// If no phases are left to run, add phases to start a new turn.
if (!this.phaseQueue.length) {
this.populatePhaseQueue();
// Clear the conditionalQueue if there are no phases left in the phaseQueue
this.conditionalQueue = [];
}
this.currentPhase = this.phaseQueue.shift() ?? null;
// Bang is justified as `populatePhaseQueue` ensures we always have _something_ in the queue at all times
this.currentPhase = this.phaseQueue.shift()!;
// TODO: Remove proof-of-concept error throw after test suite passes
if (!this.currentPhase) {
throw new Error("currentPhase was null after being started!");
}
const unactivatedConditionalPhases: [() => boolean, Phase][] = [];
// Check if there are any conditional phases queued
@ -389,10 +401,15 @@ export class PhaseManager {
}
this.conditionalQueue.push(...unactivatedConditionalPhases);
if (this.currentPhase) {
console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;");
this.currentPhase.start();
this.startCurrentPhase();
}
/**
* Helper method to start and log the current phase.
*/
private startCurrentPhase(): void {
console.log(`%cStart Phase ${this.currentPhase.phaseName}`, "color:green;");
this.currentPhase.start();
}
overridePhase(phase: Phase): boolean {
@ -402,8 +419,7 @@ export class PhaseManager {
this.standbyPhase = this.currentPhase;
this.currentPhase = phase;
console.log(`%cStart Phase ${phase.constructor.name}`, "color:green;");
phase.start();
this.startCurrentPhase();
return true;
}

View File

@ -383,14 +383,14 @@ export class GameChallengesUiHandler extends UiHandler {
this.updateChallengeArrows(this.startCursor.visible);
} else {
globalScene.phaseManager.toTitleScreen();
globalScene.phaseManager.getCurrentPhase()?.end();
globalScene.phaseManager.getCurrentPhase().end();
}
success = true;
} else if (button === Button.SUBMIT || button === Button.ACTION) {
if (this.hasSelectedChallenge) {
if (this.startCursor.visible) {
globalScene.phaseManager.unshiftNew("SelectStarterPhase");
globalScene.phaseManager.getCurrentPhase()?.end();
globalScene.phaseManager.getCurrentPhase().end();
} else {
this.startCursor.setVisible(true);
this.cursorObj?.setVisible(false);

View File

@ -45,7 +45,7 @@ export class EggHatchSceneHandler extends UiHandler {
processInput(button: Button): boolean {
if (button === Button.ACTION || button === Button.CANCEL) {
const phase = globalScene.phaseManager.getCurrentPhase();
if (phase?.is("EggHatchPhase") && phase.trySkip()) {
if (phase.is("EggHatchPhase") && phase.trySkip()) {
return true;
}
}

View File

@ -222,7 +222,7 @@ export class EggSummaryUiHandler extends MessageUiHandler {
if (button === Button.CANCEL) {
if (!this.blockExit) {
const phase = globalScene.phaseManager.getCurrentPhase();
if (phase?.is("EggSummaryPhase")) {
if (phase.is("EggSummaryPhase")) {
phase.end();
}
success = true;

View File

@ -125,7 +125,7 @@ export class MenuUiHandler extends MessageUiHandler {
const ui = this.getUi();
this.excludedMenus = () => [
{
condition: !!globalScene.phaseManager.getCurrentPhase()?.is("SelectModifierPhase"),
condition: globalScene.phaseManager.getCurrentPhase().is("SelectModifierPhase"),
options: [MenuOptions.EGG_GACHA],
},
{ condition: bypassLogin, options: [MenuOptions.LOG_OUT] },

View File

@ -816,7 +816,7 @@ export class PartyUiHandler extends MessageUiHandler {
// TODO: This risks hitting the other options (.MOVE_i and ALL) so does it? Do we need an extra check?
if (
option >= PartyOption.FORM_CHANGE_ITEM &&
globalScene.phaseManager.getCurrentPhase()?.is("SelectModifierPhase") &&
globalScene.phaseManager.getCurrentPhase().is("SelectModifierPhase") &&
this.partyUiMode === PartyUiMode.CHECK
) {
const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon);
@ -1504,7 +1504,7 @@ export class PartyUiHandler extends MessageUiHandler {
break;
case PartyUiMode.CHECK:
this.addCommonOptions(pokemon);
if (globalScene.phaseManager.getCurrentPhase()?.is("SelectModifierPhase")) {
if (globalScene.phaseManager.getCurrentPhase().is("SelectModifierPhase")) {
const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon);
for (let i = 0; i < formChangeItemModifiers.length; i++) {
this.options.push(PartyOption.FORM_CHANGE_ITEM + i);

View File

@ -705,7 +705,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
show(args: any[]): boolean {
// Allow the use of candies if we are in one of the whitelisted phases
this.canUseCandies = ["TitlePhase", "SelectStarterPhase", "CommandPhase"].includes(
globalScene.phaseManager.getCurrentPhase()?.phaseName ?? "",
globalScene.phaseManager.getCurrentPhase().phaseName,
);
if (args.length >= 1 && args[0] === "refresh") {

View File

@ -4271,7 +4271,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
globalScene.phaseManager.pushNew("EncounterPhase");
}
this.clearText();
globalScene.phaseManager.getCurrentPhase()?.end();
globalScene.phaseManager.getCurrentPhase().end();
},
cancel,
null,

View File

@ -15,6 +15,8 @@ import type { AtLeastOne } from "#types/type-helpers";
import type { expect } from "vitest";
import type Overrides from "#app/overrides";
import type { PokemonMove } from "#moves/pokemon-move";
import type { PhaseString } from "#types/phase-types";
import type { Phase } from "#app/phase";
declare module "vitest" {
interface Assertion {
@ -29,6 +31,12 @@ declare module "vitest" {
*/
toEqualArrayUnsorted<E>(expected: E[]): void;
/**
* Check if the currently running {@linkcode Phase} is of the given type.
* @param expectedPhase - The expected {@linkcode PhaseString}
*/
toBeAtPhase(expectedPhase: PhaseString): void;
/**
* Check whether a {@linkcode Pokemon}'s current typing includes the given types.
*

View File

@ -196,7 +196,7 @@ describe("Abilities - Disguise", () => {
game.move.select(MoveId.SHADOW_SNEAK);
await game.toNextWave();
expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe("CommandPhase");
expect(game).toBeAtPhase("CommandPhase");
expect(game.scene.currentBattle.waveIndex).toBe(2);
});

View File

@ -86,7 +86,7 @@ describe("Phase - Battle Phase", () => {
it("newGame one-liner", async () => {
await game.classicMode.startBattle();
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
expect(game).toBeAtPhase("CommandPhase");
});
it("do attack wave 3 - single battle - regular - OHKO", async () => {

View File

@ -36,62 +36,62 @@ describe("Test Battle Phase", () => {
game.override.battleStyle("single").startingWave(10);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 2vs2 boss", async () => {
game.override.battleStyle("double").startingWave(10);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 2vs2 trainer", async () => {
game.override.battleStyle("double").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 2vs1 trainer", async () => {
game.override.battleStyle("single").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 2vs1 rival", async () => {
game.override.battleStyle("single").startingWave(8);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 2vs2 rival", async () => {
game.override.battleStyle("double").startingWave(8);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 1vs1 trainer", async () => {
game.override.battleStyle("single").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 2vs2 trainer", async () => {
game.override.battleStyle("double").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
expect(game).toBeAtPhase("CommandPhase");
});
it("startBattle 4vs2 trainer", async () => {
game.override.battleStyle("double").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD, SpeciesId.DARKRAI, SpeciesId.GABITE]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
expect(game).toBeAtPhase("CommandPhase");
});
});

View File

@ -1,3 +1,4 @@
import { toBeAtPhase } from "#test/test-utils/matchers/to-be-at-phase";
import { toEqualArrayUnsorted } from "#test/test-utils/matchers/to-equal-array-unsorted";
import { toHaveAbilityApplied } from "#test/test-utils/matchers/to-have-ability-applied";
import { toHaveBattlerTag } from "#test/test-utils/matchers/to-have-battler-tag";
@ -22,6 +23,7 @@ import { expect } from "vitest";
expect.extend({
toEqualArrayUnsorted,
toBeAtPhase,
toHaveTypes,
toHaveUsedMove,
toHaveEffectiveStat,

View File

@ -212,7 +212,7 @@ describe("Transforming Effects", () => {
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.toNextWave();
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
expect(game).toBeAtPhase("CommandPhase");
expect(game.scene.currentBattle.waveIndex).toBe(2);
await game.reload.reloadSession();
@ -242,7 +242,7 @@ describe("Transforming Effects", () => {
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.toNextWave();
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
expect(game).toBeAtPhase("CommandPhase");
expect(game.scene.currentBattle.waveIndex).toBe(2);
expect(player.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId);

View File

@ -1,4 +1,3 @@
import { globalScene } from "#app/global-scene";
import { Status } from "#data/status-effect";
import { AbilityId } from "#enums/ability-id";
import { BattleType } from "#enums/battle-type";
@ -179,18 +178,13 @@ describe("Moves - Whirlwind", () => {
const eligibleEnemy = enemyParty.filter(p => p.hp > 0 && p.isAllowedInBattle());
expect(eligibleEnemy.length).toBe(1);
// Spy on the queueMessage function
const queueSpy = vi.spyOn(globalScene.phaseManager, "queueMessage");
// Player uses Whirlwind; opponent uses Splash
game.move.select(MoveId.WHIRLWIND);
await game.move.selectEnemyMove(MoveId.SPLASH);
await game.toNextTurn();
// Verify that the failure message is displayed for Whirlwind
expect(queueSpy).toHaveBeenCalledWith(expect.stringContaining("But it failed"));
// Verify the opponent's Splash message
expect(queueSpy).toHaveBeenCalledWith(expect.stringContaining("But nothing happened!"));
const player = game.field.getPlayerPokemon();
expect(player).toHaveUsedMove({ move: MoveId.WHIRLWIND, result: MoveResult.FAIL });
});
it("should not pull in the other trainer's pokemon in a partner trainer battle", async () => {

View File

@ -9,7 +9,6 @@ import { ATrainersTestEncounter } from "#mystery-encounters/a-trainers-test-enco
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import { PartyHealPhase } from "#phases/party-heal-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
@ -106,7 +105,7 @@ describe("A Trainer's Test - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(scene.currentBattle.trainer).toBeDefined();
expect(
@ -131,7 +130,7 @@ describe("A Trainer's Test - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs;
expect(eggsAfter).toBeDefined();
@ -179,7 +178,7 @@ describe("A Trainer's Test - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs;
expect(eggsAfter).toBeDefined();

View File

@ -10,7 +10,6 @@ import { BerryModifier, PokemonHeldItemModifier } from "#modifiers/modifier";
import { AbsoluteAvariceEncounter } from "#mystery-encounters/absolute-avarice-encounter";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
@ -132,7 +131,7 @@ describe("Absolute Avarice - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.GREEDENT);
const moveset = enemyField[0].moveset.map(m => m.moveId);
@ -148,7 +147,7 @@ describe("Absolute Avarice - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
for (const partyPokemon of scene.getPlayerParty()) {
const pokemonId = partyPokemon.id;

View File

@ -11,7 +11,6 @@ import { BerriesAboundEncounter } from "#mystery-encounters/berries-abound-encou
import * as EncounterDialogueUtils from "#mystery-encounters/encounter-dialogue-utils";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
runMysteryEncounterToEnd,
@ -114,7 +113,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
});
@ -135,7 +134,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
const berriesAfter = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
const berriesAfterCount = berriesAfter.reduce((a, b) => a + b.stackCount, 0);
@ -148,7 +147,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -188,7 +187,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
@ -212,7 +211,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
@ -233,7 +232,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -12,7 +12,6 @@ import { PokemonMove } from "#moves/pokemon-move";
import { BugTypeSuperfanEncounter } from "#mystery-encounters/bug-type-superfan-encounter";
import * as encounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#phases/mystery-encounter-phases";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
@ -231,7 +230,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(2);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -244,7 +243,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(3);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -258,7 +257,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(4);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -273,7 +272,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -289,7 +288,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -307,7 +306,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -325,7 +324,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -343,7 +342,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -365,7 +364,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterRewardsPhase.name);
expect(game).toBeAtPhase("MysteryEncounterRewardsPhase");
game.phaseInterceptor["prompts"] = []; // Clear out prompt handlers
game.onNextPrompt("MysteryEncounterRewardsPhase", UiMode.OPTION_SELECT, () => {
game.phaseInterceptor.superEndPhase();
@ -406,7 +405,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -416,7 +415,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, defaultParty);
await runMysteryEncounterToEnd(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -435,7 +434,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
]);
await runMysteryEncounterToEnd(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -457,7 +456,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
]);
await runMysteryEncounterToEnd(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -481,7 +480,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
]);
await runMysteryEncounterToEnd(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -542,7 +541,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -557,7 +556,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -22,7 +22,6 @@ import { ClowningAroundEncounter } from "#mystery-encounters/clowning-around-enc
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import { generateModifierType } from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase";
import { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { NewBattlePhase } from "#phases/new-battle-phase";
@ -172,7 +171,7 @@ describe("Clowning Around - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(2);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.MR_MIME);
expect(enemyField[0].moveset).toEqual([
@ -201,7 +200,7 @@ describe("Clowning Around - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
const abilityToTrain = scene.currentBattle.mysteryEncounter?.misc.ability;
@ -216,7 +215,7 @@ describe("Clowning Around - Mystery Encounter", () => {
vi.spyOn(partyUiHandler, "show");
game.endPhase();
await game.phaseInterceptor.to(PostMysteryEncounterPhase);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(PostMysteryEncounterPhase.name);
expect(game).toBeAtPhase("PostMysteryEncounterPhase");
// Wait for Yes/No confirmation to appear
await vi.waitFor(() => expect(optionSelectUiHandler.show).toHaveBeenCalled());

View File

@ -10,7 +10,6 @@ import { PokemonMove } from "#moves/pokemon-move";
import { DancingLessonsEncounter } from "#mystery-encounters/dancing-lessons-encounter";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import { LearnMovePhase } from "#phases/learn-move-phase";
import { MovePhase } from "#phases/move-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
@ -106,7 +105,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.ORICORIO);
expect(enemyField[0].summonData.statStages).toEqual([1, 1, 1, 1, 0, 0, 0]);
@ -127,7 +126,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -227,7 +226,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3);
const partyCountAfter = scene.getPlayerParty().length;
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();

View File

@ -161,7 +161,7 @@ describe("Delibird-y - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -316,7 +316,7 @@ describe("Delibird-y - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -449,7 +449,7 @@ describe("Delibird-y - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();

View File

@ -93,7 +93,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
it("should have shop with only TMs", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
await runMysteryEncounterToEnd(game, 1);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -130,7 +130,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
it("should have shop with only Vitamins", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
await runMysteryEncounterToEnd(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -170,7 +170,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
it("should have shop with only X Items", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
await runMysteryEncounterToEnd(game, 3);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -210,7 +210,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
it("should have shop with only Pokeballs", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
await runMysteryEncounterToEnd(game, 4);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -16,7 +16,6 @@ import { AttackTypeBoosterModifier, PokemonHeldItemModifier } from "#modifiers/m
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import { FieryFalloutEncounter } from "#mystery-encounters/fiery-fallout-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
@ -161,7 +160,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(2);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.VOLCARONA);
expect(enemyField[1].species.speciesId).toBe(SpeciesId.VOLCARONA);
@ -177,7 +176,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
const leadPokemonId = scene.getPlayerParty()?.[0].id;
const leadPokemonItems = scene.findModifiers(
@ -266,7 +265,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty);
await runMysteryEncounterToEnd(game, 3);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
const leadPokemonItems = scene.getPlayerParty()[0].getHeldItems() as PokemonHeldItemModifier[];
const item = leadPokemonItems.find(i => i instanceof AttackTypeBoosterModifier);
@ -292,7 +291,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(continueEncounterSpy).not.toHaveBeenCalled();
});
});

View File

@ -10,7 +10,6 @@ import { PokemonMove } from "#moves/pokemon-move";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import { FightOrFlightEncounter } from "#mystery-encounters/fight-or-flight-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
@ -110,7 +109,7 @@ describe("Fight or Flight - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
});
@ -123,7 +122,7 @@ describe("Fight or Flight - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -166,7 +165,7 @@ describe("Fight or Flight - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -183,7 +182,7 @@ describe("Fight or Flight - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -14,7 +14,7 @@ import { FunAndGamesEncounter } from "#mystery-encounters/fun-and-games-encounte
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import type { CommandPhase } from "#phases/command-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
@ -131,7 +131,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -143,7 +143,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(game.field.getEnemyPokemon().species.speciesId).toBe(SpeciesId.WOBBUFFET);
expect(game.field.getEnemyPokemon().ivs).toEqual([0, 0, 0, 0, 0, 0]);
expect(game.field.getEnemyPokemon().nature).toBe(Nature.MILD);
@ -165,7 +165,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to(SelectModifierPhase, false);
// Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
});
it("should have no items in rewards if Wubboffet doesn't take enough damage", async () => {
@ -173,7 +173,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => {
game.endPhase();
});
@ -184,7 +184,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to(SelectModifierPhase, false);
// Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -200,7 +200,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => {
game.endPhase();
});
@ -213,7 +213,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to(SelectModifierPhase, false);
// Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -230,7 +230,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => {
game.endPhase();
});
@ -243,7 +243,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to(SelectModifierPhase, false);
// Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -260,7 +260,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => {
game.endPhase();
});
@ -273,7 +273,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to(SelectModifierPhase, false);
// Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -226,7 +226,7 @@ describe("Global Trade System - Mystery Encounter", () => {
await scene.updateModifiers(true);
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -147,7 +147,7 @@ describe("Lost at Sea - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -212,7 +212,7 @@ describe("Lost at Sea - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();

View File

@ -12,7 +12,6 @@ import { MysteriousChallengersEncounter } from "#mystery-encounters/mysterious-c
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
runMysteryEncounterToEnd,
@ -152,7 +151,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
await runMysteryEncounterToEnd(game, 1, undefined, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
});
@ -162,7 +161,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -196,7 +195,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
await runMysteryEncounterToEnd(game, 2, undefined, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
});
@ -206,7 +205,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -253,7 +252,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
await runMysteryEncounterToEnd(game, 3, undefined, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
});
@ -263,7 +262,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 3, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -245,7 +245,7 @@ describe("Part-Timer - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();

View File

@ -119,7 +119,7 @@ describe("Safari Zone - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();

View File

@ -8,7 +8,6 @@ import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { TeleportingHijinksEncounter } from "#mystery-encounters/teleporting-hijinks-encounter";
import { CommandPhase } from "#phases/command-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
@ -157,7 +156,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -167,7 +166,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
await runMysteryEncounterToEnd(game, 1, undefined, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
});
it("should transport to a new area", async () => {
@ -229,7 +228,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -239,7 +238,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, [SpeciesId.METAGROSS]);
await runMysteryEncounterToEnd(game, 2, undefined, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
});
it("should transport to a new area", async () => {
@ -300,7 +299,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 3, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -12,7 +12,6 @@ import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
import { TheExpertPokemonBreederEncounter } from "#mystery-encounters/the-expert-pokemon-breeder-encounter";
import { CommandPhase } from "#phases/command-phase";
import { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
@ -158,7 +157,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
expect(successfullyLoaded).toBe(true);
// Check usual battle stuff
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
expect(scene.getPlayerParty().length).toBe(1);
@ -177,7 +176,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs;
const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon1CommonEggs;
@ -243,7 +242,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
expect(successfullyLoaded).toBe(true);
// Check usual battle stuff
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
expect(scene.getPlayerParty().length).toBe(1);
@ -262,7 +261,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs;
const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon2CommonEggs;
@ -325,7 +324,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
expect(successfullyLoaded).toBe(true);
// Check usual battle stuff
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
expect(scene.getPlayerParty().length).toBe(1);
@ -344,7 +343,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 3, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs;
const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon3CommonEggs;

View File

@ -182,7 +182,7 @@ describe("The Pokemon Salesman - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();

View File

@ -17,7 +17,6 @@ import { PokemonMove } from "#moves/pokemon-move";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { TheStrongStuffEncounter } from "#mystery-encounters/the-strong-stuff-encounter";
import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
@ -192,7 +191,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.SHUCKLE);
expect(enemyField[0].summonData.statStages).toEqual([0, 1, 0, 1, 0, 0, 0]);
@ -230,7 +229,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -15,7 +15,6 @@ import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
import { TheWinstrateChallengeEncounter } from "#mystery-encounters/the-winstrate-challenge-encounter";
import { CommandPhase } from "#phases/command-phase";
import { MysteryEncounterRewardsPhase } from "#phases/mystery-encounter-phases";
import { PartyHealPhase } from "#phases/party-heal-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
@ -263,7 +262,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.THE_WINSTRATE_CHALLENGE, defaultParty);
await runMysteryEncounterToEnd(game, 1, undefined, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.trainer!.config.trainerType).toBe(TrainerType.VICTOR);
expect(scene.currentBattle.mysteryEncounter?.enemyPartyConfigs.length).toBe(4);
@ -296,7 +295,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => {
// Should have Macho Brace in the rewards
await skipBattleToNextBattle(game, true);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -338,7 +337,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => {
it("should have a Rarer Candy in the rewards", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.THE_WINSTRATE_CHALLENGE, defaultParty);
await runMysteryEncounterToEnd(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -20,7 +20,6 @@ import {
} from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { TrashToTreasureEncounter } from "#mystery-encounters/trash-to-treasure-encounter";
import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
@ -173,7 +172,7 @@ describe("Trash to Treasure - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty);
await runMysteryEncounterToEnd(game, 1);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
const leftovers = scene.findModifier(m => m instanceof TurnHealModifier) as TurnHealModifier;
expect(leftovers).toBeDefined();
@ -221,7 +220,7 @@ describe("Trash to Treasure - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.GARBODOR);
expect(enemyField[0].moveset).toEqual([
@ -243,7 +242,7 @@ describe("Trash to Treasure - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -16,7 +16,6 @@ import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"
import { generateModifierType } from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { UncommonBreedEncounter } from "#mystery-encounters/uncommon-breed-encounter";
import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { StatStageChangePhase } from "#phases/stat-stage-change-phase";
@ -121,7 +120,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
@ -148,7 +147,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
@ -200,7 +199,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -260,7 +259,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();

View File

@ -10,7 +10,6 @@ import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"
import * as EncounterTransformationSequence from "#mystery-encounters/encounter-transformation-sequence";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { WeirdDreamEncounter } from "#mystery-encounters/weird-dream-encounter";
import { CommandPhase } from "#phases/command-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase";
import {
runMysteryEncounterToEnd,
@ -117,7 +116,7 @@ describe("Weird Dream - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
const pokemonAfter = scene.getPlayerParty();
const bstsAfter = pokemonAfter.map(pokemon => pokemon.getSpeciesForm().getBaseStatTotal());
@ -140,7 +139,7 @@ describe("Weird Dream - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
await runMysteryEncounterToEnd(game, 1);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -187,7 +186,7 @@ describe("Weird Dream - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1);
expect(scene.getEnemyParty().length).toBe(scene.getPlayerParty().length);
});
@ -197,7 +196,7 @@ describe("Weird Dream - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -34,7 +34,7 @@ describe("Mystery Encounters", () => {
]);
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
});
it("Encounters should not run on X1 waves", async () => {

View File

@ -38,7 +38,7 @@ describe("Mystery Encounter Phases", () => {
]);
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
expect(game).toBeAtPhase("MysteryEncounterPhase");
});
it("Runs MysteryEncounterPhase", async () => {

View File

@ -46,6 +46,7 @@ import type { InputsHandler } from "#test/test-utils/inputs-handler";
import { MockFetch } from "#test/test-utils/mocks/mock-fetch";
import { PhaseInterceptor } from "#test/test-utils/phase-interceptor";
import { TextInterceptor } from "#test/test-utils/text-interceptor";
import type { PhaseClass, PhaseString } from "#types/phase-types";
import type { BallUiHandler } from "#ui/ball-ui-handler";
import type { BattleMessageUiHandler } from "#ui/battle-message-ui-handler";
import type { CommandUiHandler } from "#ui/command-ui-handler";
@ -162,7 +163,7 @@ export class GameManager {
* End the currently running phase immediately.
*/
endPhase() {
this.scene.phaseManager.getCurrentPhase()?.end();
this.scene.phaseManager.getCurrentPhase().end();
}
/**
@ -412,10 +413,11 @@ export class GameManager {
* Checks if the current phase matches the target phase.
* @param phaseTarget - The target phase.
* @returns Whether the current phase matches the target phase
* @todo Remove `phaseClass` from signature
*/
isCurrentPhase(phaseTarget) {
isCurrentPhase(phaseTarget: PhaseClass | PhaseString) {
const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name;
return this.scene.phaseManager.getCurrentPhase()?.constructor.name === targetName;
return this.scene.phaseManager.getCurrentPhase().phaseName === targetName;
}
/**

View File

@ -0,0 +1,45 @@
/** biome-ignore-start lint/correctness/noUnusedImports: TSDoc imports */
import type { Phase } from "#app/phase";
import type { GameManager } from "#test/test-utils/game-manager";
// biome-ignore-end lint/correctness/noUnusedImports: TSDoc
import { isGameManagerInstance, receivedStr } from "#test/test-utils/test-utils";
import type { PhaseString } from "#types/phase-types";
import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
/**
* Matcher that checks if the current {@linkcode Phase} is of the given type.
* @param received - The object to check. Should be the current {@linkcode GameManager}
* @param expectedPhase - The expected {@linkcode PhaseString}
* @returns The result of the matching
*/
export function toBeAtPhase(this: MatcherState, received: unknown, expectedPhase: PhaseString): SyncExpectationResult {
if (!isGameManagerInstance(received)) {
return {
pass: this.isNot,
message: () => `Expected to receive a GameManager, but got ${receivedStr(received)}!`,
};
}
if (!received.scene?.phaseManager) {
return {
pass: this.isNot,
message: () => `Expected GameManager.${received.scene ? "scene.phaseManager" : "scene"} to be defined!`,
};
}
const currPhase = received.scene.phaseManager.getCurrentPhase();
const pass = currPhase.is(expectedPhase);
const actual = currPhase.phaseName;
return {
pass,
message: () =>
pass
? `Expected the current phase to NOT be ${expectedPhase}, but it was!`
: `Expected the current phase to be ${expectedPhase}, but got ${actual} instead!`,
expected: expectedPhase,
actual,
};
}

View File

@ -416,7 +416,7 @@ export class PhaseInterceptor {
const actionForNextPrompt = this.prompts[0];
const expireFn = actionForNextPrompt.expireFn?.();
const currentMode = this.scene.ui.getMode();
const currentPhase = this.scene.phaseManager.getCurrentPhase()?.constructor.name;
const currentPhase = this.scene.phaseManager.getCurrentPhase().phaseName;
const currentHandler = this.scene.ui.getHandler();
if (expireFn) {
this.prompts.shift();