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 { PokemonMove } from "#app/data/moves/pokemon-move";
import type { AbAttrMap, AbAttrString } from "#app/@types/ability-types";
import type { TurnCommand } from "#app/battle";
/** Base typeclass for damage parameter methods, used for DRY */
type damageParams = {
@ -6868,6 +6869,7 @@ export class PokemonWaveData {
* Resets at the start of a new turn, as well as on switch.
*/
export class PokemonTurnData {
public turnCommand?: TurnCommand;
public flinched = false;
public acted = false;
/** 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 { CommandPhase } from "#app/phases/command-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 type { DynamicPhaseType } from "#enums/dynamic-phase-type";
import { EggHatchPhase } from "#app/phases/egg-hatch-phase";
import { EggLapsePhase } from "#app/phases/egg-lapse-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 { PartyExpPhase } from "#app/phases/party-exp-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 { PokemonHealPhase } from "#app/phases/pokemon-heal-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 { VictoryPhase } from "#app/phases/victory-phase";
import { WeatherEffectPhase } from "#app/phases/weather-effect-phase";
import { DynamicQueueManager } from "#app/queues/dynamic-queue-manager";
/**
* Manager for phases used by battle scene.
@ -227,19 +226,11 @@ export class PhaseManager {
private phaseQueuePrependSpliceIndex = -1;
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 dynamicQueueManager = new DynamicQueueManager();
private currentPhase: Phase | null = null;
private standbyPhase: Phase | null = null;
constructor() {
this.dynamicPhaseQueues = [new PostSummonPhasePriorityQueue()];
this.dynamicPhaseTypes = [PostSummonPhase];
}
/* Phase Functions */
getCurrentPhase(): Phase | null {
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
*/
pushPhase(phase: Phase, defer = false): void {
if (this.getDynamicPhaseType(phase) !== undefined) {
if (this.dynamicQueueManager.isDynamicPhase(phase.phaseName)) {
this.pushDynamicPhase(phase);
} else {
(!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]) {
queue.splice(0, queue.length);
}
this.dynamicPhaseQueues.forEach(queue => queue.clear());
this.dynamicQueueManager.clearQueues();
this.currentPhase = null;
this.standbyPhase = null;
this.clearPhaseQueueSplice();
@ -470,22 +461,6 @@ export class PhaseManager {
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}
*
@ -493,21 +468,16 @@ export class PhaseManager {
* @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);
this.pushNew("ActivatePriorityQueuePhase", phase.phaseName);
this.dynamicQueueManager.queueDynamicPhase(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();
public startNextDynamicPhaseOfType(type: PhaseString): void {
const phase = this.dynamicQueueManager.popNextPhaseOfType(type);
if (phase) {
this.unshiftPhase(phase);
}
@ -523,13 +493,8 @@ export class PhaseManager {
* @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);
this.unshiftNew("ActivatePriorityQueuePhase", phase.phaseName);
this.dynamicQueueManager.queueDynamicPhase(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 { Phase } from "#app/phase";
export class ActivatePriorityQueuePhase extends Phase {
public readonly phaseName = "ActivatePriorityQueuePhase";
private type: DynamicPhaseType;
private readonly type: PhaseString;
constructor(type: DynamicPhaseType) {
constructor(type: PhaseString) {
super();
this.type = type;
}
override start() {
super.start();
globalScene.phaseManager.startDynamicPhaseType(this.type);
globalScene.phaseManager.startNextDynamicPhaseOfType(this.type);
this.end();
}
public getType(): DynamicPhaseType {
public getType(): PhaseString {
return this.type;
}
}

View File

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

View File

@ -69,7 +69,7 @@ export class TurnStartPhase extends FieldPhase {
applyAbAttrs("BypassSpeedChanceAbAttr", p, null, false, bypassSpeed);
applyAbAttrs("PreventBypassSpeedChanceAbAttr", p, null, false, bypassSpeed, canCheckHeldItems);
if (canCheckHeldItems.value) {
globalScene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed);
globalScene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p);
}
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);
}
}