pokerogue/src/data/phase-priority-queue.ts
Dean ff9aefb0e5
[Bug] Activate PostSummon Abilities in Speed and Priority Order
https://github.com/pagefaultgames/pokerogue/pull/5513

* Add prependToPhaseWithCondition and use it in SummonPhase to determine speed order

* Move logic to PostSummonPhase

* Add test base

* Pivot to using sort strategy instead

* Add and update tests

* Support priority ability activations

* Ensure priority abilities are still activated on switch in

* Add test for priority

* Update to use priority numbers instead of a boolean

* Add ability priorities to constructors

* Move sorting to BattleScene

* Rename phase file

* Update import

* Move application to applyPostSummonAbAttrs and stop assuming no other phases in queue

* Ensure all PostSummonPhases from encounters are added at the same time

* Switch to priority queue approach

* Ensure that zero/negative priority activations happen after postsummonphase

* Revert 07646fe (not needed due to stable sort)

* Always create separate ability phases for passive and use boolean instead of priority number when applying

* Add test for dynamic updates

* Add BattlerIndex import

* Clear queues for testing

* Benjie suggestion

* Split files

* Update import in battlescene

* Remove extra spaces added by VSCode

* Fix other conflicts

* Update PhaseManager

* Update to use PhaseManager

* Immediately start postsummons

* Fix test

* Fix BattlerIndex import

* Remove unused imports

* Fix postsummon application

* Make priority readonly
2025-06-11 22:28:27 -07:00

98 lines
3.1 KiB
TypeScript

import { globalScene } from "#app/global-scene";
import type { Phase } from "#app/phase";
import { ActivatePriorityQueuePhase } from "#app/phases/activate-priority-queue-phase";
import type { PostSummonPhase } from "#app/phases/post-summon-phase";
import { PostSummonActivateAbilityPhase } from "#app/phases/post-summon-activate-ability-phase";
import { Stat } from "#enums/stat";
import { BooleanHolder } from "#app/utils/common";
import { TrickRoomTag } from "#app/data/arena-tag";
import { DynamicPhaseType } from "#enums/dynamic-phase-type";
/**
* Stores a list of {@linkcode Phase}s
*
* Dynamically updates ordering to always pop the highest "priority", based on implementation of {@linkcode reorder}
*/
export abstract class PhasePriorityQueue {
protected abstract queue: Phase[];
/**
* Sorts the elements in the queue
*/
public abstract reorder(): void;
/**
* Calls {@linkcode reorder} and shifts the queue
* @returns The front element of the queue after sorting
*/
public pop(): Phase | undefined {
this.reorder();
return this.queue.shift();
}
/**
* Adds a phase to the queue
* @param phase The phase to add
*/
public push(phase: Phase): void {
this.queue.push(phase);
}
/**
* Removes all phases from the queue
*/
public clear(): void {
this.queue.splice(0, this.queue.length);
}
}
/**
* Priority Queue for {@linkcode PostSummonPhase} and {@linkcode PostSummonActivateAbilityPhase}
*
* Orders phases first by ability priority, then by the {@linkcode Pokemon}'s effective speed
*/
export class PostSummonPhasePriorityQueue extends PhasePriorityQueue {
protected override queue: PostSummonPhase[] = [];
public override reorder(): void {
this.queue.sort((phaseA: PostSummonPhase, phaseB: PostSummonPhase) => {
if (phaseA.getPriority() === phaseB.getPriority()) {
return (
(phaseB.getPokemon().getEffectiveStat(Stat.SPD) - phaseA.getPokemon().getEffectiveStat(Stat.SPD)) *
(isTrickRoom() ? -1 : 1)
);
}
return phaseB.getPriority() - phaseA.getPriority();
});
}
public override push(phase: PostSummonPhase): void {
super.push(phase);
this.queueAbilityPhase(phase);
}
/**
* Queues all necessary {@linkcode PostSummonActivateAbilityPhase}s for each pushed {@linkcode PostSummonPhase}
* @param phase The {@linkcode PostSummonPhase} that was pushed onto the queue
*/
private queueAbilityPhase(phase: PostSummonPhase): void {
const phasePokemon = phase.getPokemon();
phasePokemon.getAbilityPriorities().forEach((priority, idx) => {
this.queue.push(new PostSummonActivateAbilityPhase(phasePokemon.getBattlerIndex(), priority, !!idx));
globalScene.phaseManager.appendToPhase(
new ActivatePriorityQueuePhase(DynamicPhaseType.POST_SUMMON),
"ActivatePriorityQueuePhase",
(p: ActivatePriorityQueuePhase) => p.getType() === DynamicPhaseType.POST_SUMMON,
);
});
}
}
function isTrickRoom(): boolean {
const speedReversed = new BooleanHolder(false);
globalScene.arena.applyTags(TrickRoomTag, false, speedReversed);
return speedReversed.value;
}