Add dynamic queue manager

This commit is contained in:
Dean 2025-06-14 17:40:18 -07:00
parent 5f098545f5
commit 0121589d6f
6 changed files with 57 additions and 55 deletions

View File

@ -193,6 +193,7 @@ import { AiType } from "#enums/ai-type";
import type { MoveResult } from "#enums/move-result"; import type { MoveResult } from "#enums/move-result";
import { PokemonMove } from "#app/data/moves/pokemon-move"; import { PokemonMove } from "#app/data/moves/pokemon-move";
import type { AbAttrMap, AbAttrString } from "#app/@types/ability-types"; import type { AbAttrMap, AbAttrString } from "#app/@types/ability-types";
import type { TurnCommand } from "#app/battle";
/** Base typeclass for damage parameter methods, used for DRY */ /** Base typeclass for damage parameter methods, used for DRY */
type damageParams = { type damageParams = {
@ -6868,6 +6869,7 @@ export class PokemonWaveData {
* Resets at the start of a new turn, as well as on switch. * Resets at the start of a new turn, as well as on switch.
*/ */
export class PokemonTurnData { export class PokemonTurnData {
public turnCommand?: TurnCommand;
public flinched = false; public flinched = false;
public acted = false; public acted = false;
/** How many times the current move should hit the target(s) */ /** How many times the current move should hit the target(s) */

View File

@ -12,9 +12,8 @@ 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 { coerceArray, type Constructor } from "#app/utils/common"; import { coerceArray } 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";
@ -58,7 +57,6 @@ 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";
@ -99,6 +97,7 @@ import { UnavailablePhase } from "#app/phases/unavailable-phase";
import { UnlockPhase } from "#app/phases/unlock-phase"; import { UnlockPhase } from "#app/phases/unlock-phase";
import { VictoryPhase } from "#app/phases/victory-phase"; import { VictoryPhase } from "#app/phases/victory-phase";
import { WeatherEffectPhase } from "#app/phases/weather-effect-phase"; import { WeatherEffectPhase } from "#app/phases/weather-effect-phase";
import { DynamicQueueManager } from "#app/queues/dynamic-queue-manager";
/** /**
* Manager for phases used by battle scene. * Manager for phases used by battle scene.
@ -227,19 +226,11 @@ 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 dynamicQueueManager = new DynamicQueueManager();
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;
@ -269,7 +260,7 @@ 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 {
if (this.getDynamicPhaseType(phase) !== undefined) { if (this.dynamicQueueManager.isDynamicPhase(phase.phaseName)) {
this.pushDynamicPhase(phase); this.pushDynamicPhase(phase);
} else { } else {
(!defer ? this.phaseQueue : this.nextCommandPhaseQueue).push(phase); (!defer ? this.phaseQueue : this.nextCommandPhaseQueue).push(phase);
@ -302,7 +293,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.dynamicQueueManager.clearQueues();
this.currentPhase = null; this.currentPhase = null;
this.standbyPhase = null; this.standbyPhase = null;
this.clearPhaseQueueSplice(); this.clearPhaseQueueSplice();
@ -470,22 +461,6 @@ 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} * Pushes a phase onto its corresponding dynamic queue and marks the activation point in {@linkcode phaseQueue}
* *
@ -493,21 +468,16 @@ export class PhaseManager {
* @param phase The phase to push * @param phase The phase to push
*/ */
public pushDynamicPhase(phase: Phase): void { public pushDynamicPhase(phase: Phase): void {
const type = this.getDynamicPhaseType(phase); this.pushNew("ActivatePriorityQueuePhase", phase.phaseName);
if (type === undefined) { this.dynamicQueueManager.queueDynamicPhase(phase);
return;
}
this.pushPhase(new ActivatePriorityQueuePhase(type));
this.dynamicPhaseQueues[type].push(phase);
} }
/** /**
* Unshifts the top phase from the corresponding dynamic queue onto {@linkcode phaseQueue} * Unshifts the top phase from the corresponding dynamic queue onto {@linkcode phaseQueue}
* @param type {@linkcode DynamicPhaseType} The type of dynamic phase to start * @param type {@linkcode DynamicPhaseType} The type of dynamic phase to start
*/ */
public startDynamicPhaseType(type: DynamicPhaseType): void { public startNextDynamicPhaseOfType(type: PhaseString): void {
const phase = this.dynamicPhaseQueues[type].pop(); const phase = this.dynamicQueueManager.popNextPhaseOfType(type);
if (phase) { if (phase) {
this.unshiftPhase(phase); this.unshiftPhase(phase);
} }
@ -523,13 +493,8 @@ export class PhaseManager {
* @returns * @returns
*/ */
public startDynamicPhase(phase: Phase): void { public startDynamicPhase(phase: Phase): void {
const type = this.getDynamicPhaseType(phase); this.unshiftNew("ActivatePriorityQueuePhase", phase.phaseName);
if (type === undefined) { this.dynamicQueueManager.queueDynamicPhase(phase);
return;
}
this.unshiftPhase(new ActivatePriorityQueuePhase(type));
this.dynamicPhaseQueues[type].push(phase);
} }
/** /**

View File

@ -1,23 +1,23 @@
import type { DynamicPhaseType } from "#enums/dynamic-phase-type"; import type { PhaseString } from "#app/@types/phase-types";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { Phase } from "#app/phase"; import { Phase } from "#app/phase";
export class ActivatePriorityQueuePhase extends Phase { export class ActivatePriorityQueuePhase extends Phase {
public readonly phaseName = "ActivatePriorityQueuePhase"; public readonly phaseName = "ActivatePriorityQueuePhase";
private type: DynamicPhaseType; private readonly type: PhaseString;
constructor(type: DynamicPhaseType) { constructor(type: PhaseString) {
super(); super();
this.type = type; this.type = type;
} }
override start() { override start() {
super.start(); super.start();
globalScene.phaseManager.startDynamicPhaseType(this.type); globalScene.phaseManager.startNextDynamicPhaseOfType(this.type);
this.end(); this.end();
} }
public getType(): DynamicPhaseType { public getType(): PhaseString {
return this.type; return this.type;
} }
} }

View File

@ -19,7 +19,6 @@ import type Pokemon from "#app/field/pokemon";
import { MoveResult } from "#enums/move-result"; import { MoveResult } from "#enums/move-result";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { BattlePhase } from "#app/phases/battle-phase";
import { NumberHolder } from "#app/utils/common"; import { NumberHolder } from "#app/utils/common";
import { AbilityId } from "#enums/ability-id"; import { AbilityId } from "#enums/ability-id";
import { ArenaTagType } from "#enums/arena-tag-type"; import { ArenaTagType } from "#enums/arena-tag-type";
@ -28,8 +27,9 @@ import { MoveId } from "#enums/move-id";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import i18next from "i18next"; import i18next from "i18next";
import { frenzyMissFunc } from "#app/data/moves/move-utils"; import { frenzyMissFunc } from "#app/data/moves/move-utils";
import { PokemonPhase } from "#app/phases/pokemon-phase";
export class MovePhase extends BattlePhase { export class MovePhase extends PokemonPhase {
public readonly phaseName = "MovePhase"; public readonly phaseName = "MovePhase";
protected _pokemon: Pokemon; protected _pokemon: Pokemon;
protected _move: PokemonMove; protected _move: PokemonMove;
@ -81,7 +81,7 @@ export class MovePhase extends BattlePhase {
reflected = false, reflected = false,
forcedLast = false, forcedLast = false,
) { ) {
super(); super(pokemon.getBattlerIndex());
this.pokemon = pokemon; this.pokemon = pokemon;
this.targets = targets; this.targets = targets;

View File

@ -69,7 +69,7 @@ export class TurnStartPhase extends FieldPhase {
applyAbAttrs("BypassSpeedChanceAbAttr", p, null, false, bypassSpeed); applyAbAttrs("BypassSpeedChanceAbAttr", p, null, false, bypassSpeed);
applyAbAttrs("PreventBypassSpeedChanceAbAttr", p, null, false, bypassSpeed, canCheckHeldItems); applyAbAttrs("PreventBypassSpeedChanceAbAttr", p, null, false, bypassSpeed, canCheckHeldItems);
if (canCheckHeldItems.value) { if (canCheckHeldItems.value) {
globalScene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed); globalScene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p);
} }
battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed; battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed;
}); });

View File

@ -0,0 +1,35 @@
import type { PhaseString } from "#app/@types/phase-types";
import type { Phase } from "#app/phase";
import type { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
import { MovePhasePriorityQueue } from "#app/queues/move-phase-priority-queue";
import type { PhasePriorityQueue } from "#app/queues/phase-priority-queue";
import { PokemonPhasePriorityQueue } from "#app/queues/pokemon-phase-priority-queue";
import { PostSummonPhasePriorityQueue } from "#app/queues/post-summon-phase-priority-queue";
export class DynamicQueueManager {
private dynamicPhaseMap: Map<PhaseString, PhasePriorityQueue<Phase>>;
constructor() {
this.dynamicPhaseMap = new Map();
this.dynamicPhaseMap.set("SwitchSummonPhase", new PokemonPhasePriorityQueue<SwitchSummonPhase>());
this.dynamicPhaseMap.set("PostSummonPhase", new PostSummonPhasePriorityQueue());
this.dynamicPhaseMap.set("MovePhase", new MovePhasePriorityQueue());
}
public clearQueues(): void {
for (const queue of this.dynamicPhaseMap.values()) {
queue.clear();
}
}
public queueDynamicPhase(phase: Phase): void {
this.dynamicPhaseMap.get(phase.phaseName)?.push(phase);
}
public popNextPhaseOfType(type: PhaseString): Phase | undefined {
return this.dynamicPhaseMap.get(type)?.pop();
}
public isDynamicPhase(type: PhaseString): boolean {
return this.dynamicPhaseMap.has(type);
}
}