Update PhaseManager

This commit is contained in:
Dean 2025-06-10 21:22:47 -07:00
parent 69d88b7cd9
commit 9fcd35e2b7

View File

@ -2,6 +2,7 @@ import type { Phase } from "#app/phase";
import type { default as Pokemon } from "#app/field/pokemon"; import type { default as Pokemon } from "#app/field/pokemon";
import type { PhaseMap, PhaseString } from "./@types/phase-types"; import type { PhaseMap, PhaseString } from "./@types/phase-types";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { ActivatePriorityQueuePhase } from "#app/phases/activate-priority-queue-phase";
import { AddEnemyBuffModifierPhase } from "#app/phases/add-enemy-buff-modifier-phase"; import { AddEnemyBuffModifierPhase } from "#app/phases/add-enemy-buff-modifier-phase";
import { AttemptCapturePhase } from "#app/phases/attempt-capture-phase"; import { AttemptCapturePhase } from "#app/phases/attempt-capture-phase";
import { AttemptRunPhase } from "#app/phases/attempt-run-phase"; import { AttemptRunPhase } from "#app/phases/attempt-run-phase";
@ -11,7 +12,9 @@ import { CheckStatusEffectPhase } from "#app/phases/check-status-effect-phase";
import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
import { CommandPhase } from "#app/phases/command-phase"; import { CommandPhase } from "#app/phases/command-phase";
import { CommonAnimPhase } from "#app/phases/common-anim-phase"; import { CommonAnimPhase } from "#app/phases/common-anim-phase";
import type { Constructor } from "#app/utils/common";
import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
import type { DynamicPhaseType } from "#enums/dynamic-phase-type";
import { EggHatchPhase } from "#app/phases/egg-hatch-phase"; import { EggHatchPhase } from "#app/phases/egg-hatch-phase";
import { EggLapsePhase } from "#app/phases/egg-lapse-phase"; import { EggLapsePhase } from "#app/phases/egg-lapse-phase";
import { EggSummaryPhase } from "#app/phases/egg-summary-phase"; import { EggSummaryPhase } from "#app/phases/egg-summary-phase";
@ -55,6 +58,7 @@ import { NextEncounterPhase } from "#app/phases/next-encounter-phase";
import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase"; import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase";
import { PartyExpPhase } from "#app/phases/party-exp-phase"; import { PartyExpPhase } from "#app/phases/party-exp-phase";
import { PartyHealPhase } from "#app/phases/party-heal-phase"; import { PartyHealPhase } from "#app/phases/party-heal-phase";
import { type PhasePriorityQueue, PostSummonPhasePriorityQueue } from "#app/data/phase-priority-queue";
import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase"; import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase";
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { PokemonTransformPhase } from "#app/phases/pokemon-transform-phase"; import { PokemonTransformPhase } from "#app/phases/pokemon-transform-phase";
@ -222,9 +226,19 @@ export class PhaseManager {
private phaseQueuePrependSpliceIndex = -1; private phaseQueuePrependSpliceIndex = -1;
private nextCommandPhaseQueue: Phase[] = []; private nextCommandPhaseQueue: Phase[] = [];
/** Storage for {@linkcode PhasePriorityQueue}s which hold phases whose order dynamically changes */
private dynamicPhaseQueues: PhasePriorityQueue[];
/** Parallel array to {@linkcode dynamicPhaseQueues} - matches phase types to their queues */
private dynamicPhaseTypes: Constructor<Phase>[];
private currentPhase: Phase | null = null; private currentPhase: Phase | null = null;
private standbyPhase: Phase | null = null; private standbyPhase: Phase | null = null;
constructor() {
this.dynamicPhaseQueues = [new PostSummonPhasePriorityQueue()];
this.dynamicPhaseTypes = [PostSummonPhase];
}
/* Phase Functions */ /* Phase Functions */
getCurrentPhase(): Phase | null { getCurrentPhase(): Phase | null {
return this.currentPhase; return this.currentPhase;
@ -254,7 +268,11 @@ export class PhaseManager {
* @param defer boolean on which queue to add to, defaults to false, and adds to phaseQueue * @param defer boolean on which queue to add to, defaults to false, and adds to phaseQueue
*/ */
pushPhase(phase: Phase, defer = false): void { pushPhase(phase: Phase, defer = false): void {
(!defer ? this.phaseQueue : this.nextCommandPhaseQueue).push(phase); if (this.getDynamicPhaseType(phase) !== undefined) {
this.pushDynamicPhase(phase);
} else {
(!defer ? this.phaseQueue : this.nextCommandPhaseQueue).push(phase);
}
} }
/** /**
@ -283,6 +301,7 @@ export class PhaseManager {
for (const queue of [this.phaseQueue, this.phaseQueuePrepend, this.conditionalQueue, this.nextCommandPhaseQueue]) { for (const queue of [this.phaseQueue, this.phaseQueuePrepend, this.conditionalQueue, this.nextCommandPhaseQueue]) {
queue.splice(0, queue.length); queue.splice(0, queue.length);
} }
this.dynamicPhaseQueues.forEach(queue => queue.clear());
this.currentPhase = null; this.currentPhase = null;
this.standbyPhase = null; this.standbyPhase = null;
this.clearPhaseQueueSplice(); this.clearPhaseQueueSplice();
@ -333,8 +352,9 @@ export class PhaseManager {
this.currentPhase = this.phaseQueue.shift() ?? null; this.currentPhase = this.phaseQueue.shift() ?? null;
const unactivatedConditionalPhases: [() => boolean, Phase][] = [];
// Check if there are any conditional phases queued // Check if there are any conditional phases queued
if (this.conditionalQueue?.length) { while (this.conditionalQueue?.length) {
// Retrieve the first conditional phase from the queue // Retrieve the first conditional phase from the queue
const conditionalPhase = this.conditionalQueue.shift(); const conditionalPhase = this.conditionalQueue.shift();
// Evaluate the condition associated with the phase // Evaluate the condition associated with the phase
@ -343,11 +363,12 @@ export class PhaseManager {
this.pushPhase(conditionalPhase[1]); this.pushPhase(conditionalPhase[1]);
} else if (conditionalPhase) { } else if (conditionalPhase) {
// If the condition is not met, re-add the phase back to the front of the conditional queue // If the condition is not met, re-add the phase back to the front of the conditional queue
this.conditionalQueue.unshift(conditionalPhase); unactivatedConditionalPhases.push(conditionalPhase);
} else { } else {
console.warn("condition phase is undefined/null!", conditionalPhase); console.warn("condition phase is undefined/null!", conditionalPhase);
} }
} }
this.conditionalQueue.push(...unactivatedConditionalPhases);
if (this.currentPhase) { if (this.currentPhase) {
console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;"); console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;");
@ -431,17 +452,18 @@ export class PhaseManager {
} }
/** /**
* Attempt to add the input phase(s) to index after target phase in the {@linkcode phaseQueue}, else simply calls {@linkcode unshiftPhase()} * Tries to add the input phase(s) to index after target phase in the {@linkcode phaseQueue}, else simply calls {@linkcode unshiftPhase()}
* @param phase - The phase(s) to be added * @param phase {@linkcode Phase} the phase(s) to be added
* @param targetPhase - The phase to search for in phaseQueue * @param targetPhase {@linkcode Phase} the type of phase to search for in {@linkcode phaseQueue}
* @param condition Condition the target phase must meet to be appended to
* @returns `true` if a `targetPhase` was found to append to * @returns `true` if a `targetPhase` was found to append to
*/ */
appendToPhase(phase: Phase | Phase[], targetPhase: PhaseString): boolean { appendToPhase(phase: Phase | Phase[], targetPhase: PhaseString, condition?: (p: Phase) => boolean): boolean {
if (!Array.isArray(phase)) { if (!Array.isArray(phase)) {
phase = [phase]; phase = [phase];
} }
const target = PHASES[targetPhase]; const target = PHASES[targetPhase];
const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof target); const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof target && (!condition || condition(ph)));
if (targetIndex !== -1 && this.phaseQueue.length > targetIndex) { if (targetIndex !== -1 && this.phaseQueue.length > targetIndex) {
this.phaseQueue.splice(targetIndex + 1, 0, ...phase); this.phaseQueue.splice(targetIndex + 1, 0, ...phase);
@ -451,6 +473,68 @@ export class PhaseManager {
return false; return false;
} }
/**
* Checks a phase and returns the matching {@linkcode DynamicPhaseType}, or undefined if it does not match one
* @param phase The phase to check
* @returns The corresponding {@linkcode DynamicPhaseType} or `undefined`
*/
public getDynamicPhaseType(phase: Phase | null): DynamicPhaseType | undefined {
let phaseType: DynamicPhaseType | undefined;
this.dynamicPhaseTypes.forEach((cls, index) => {
if (phase instanceof cls) {
phaseType = index;
}
});
return phaseType;
}
/**
* Pushes a phase onto its corresponding dynamic queue and marks the activation point in {@linkcode phaseQueue}
*
* The {@linkcode ActivatePriorityQueuePhase} will run the top phase in the dynamic queue (not necessarily {@linkcode phase})
* @param phase The phase to push
*/
public pushDynamicPhase(phase: Phase): void {
const type = this.getDynamicPhaseType(phase);
if (type === undefined) {
return;
}
this.pushPhase(new ActivatePriorityQueuePhase(type));
this.dynamicPhaseQueues[type].push(phase);
}
/**
* Unshifts the top phase from the corresponding dynamic queue onto {@linkcode phaseQueue}
* @param type {@linkcode DynamicPhaseType} The type of dynamic phase to start
*/
public startDynamicPhaseType(type: DynamicPhaseType): void {
const phase = this.dynamicPhaseQueues[type].pop();
if (phase) {
this.unshiftPhase(phase);
}
}
/**
* Unshifts an {@linkcode ActivatePriorityQueuePhase} for {@linkcode phase}, then pushes {@linkcode phase} to its dynamic queue
*
* This is the same as {@linkcode pushDynamicPhase}, except the activation phase is unshifted
*
* {@linkcode phase} is not guaranteed to be the next phase from the queue to run (if the queue is not empty)
* @param phase The phase to add
* @returns
*/
public startDynamicPhase(phase: Phase): void {
const type = this.getDynamicPhaseType(phase);
if (type === undefined) {
return;
}
this.unshiftPhase(new ActivatePriorityQueuePhase(type));
this.dynamicPhaseQueues[type].push(phase);
}
/** /**
* Adds a MessagePhase, either to PhaseQueuePrepend or nextCommandPhaseQueue * Adds a MessagePhase, either to PhaseQueuePrepend or nextCommandPhaseQueue
* @param message - string for MessagePhase * @param message - string for MessagePhase