pokerogue/src/queues/move-phase-priority-queue.ts
Dean 87e6095a00
[Misc/Feature] Add dynamic turn order (#6036)
* Add new priority queues

* Add dynamic queue manager

* Add timing modifier and fix post speed ordering

* Make `phaseQueue` private

* Fix `gameManager.setTurnOrder`

* Update `findPhase` to also check dynamic queues

* Modify existing phase manager methods to check dynamic queues

* Fix move order persisting through tests

* Fix magic coat/bounce

* Use append for magic coat/bounce

* Remove `getSpeedOrder` from `TurnStartPhase`, fix references to `getCommandOrder` in tests

* Fix round queuing last instead of next

* Add quick draw application

* Add quick claw activation

* Fix turn order tracking

* Add move header queue to fix ordering

* Fix abilities activating immediately on summon

* Fix `postsummonphases` being shuffled (need to handle speed ties differently here)

* Update speed order function

* Add `StaticSwitchSummonPhase`

* Fix magic coat/bounce error from conflict resolution

* Remove conditional queue

* Fix dancer and baton pass tests

* Automatically queue consecutive Pokémon phases as dynamic

* Move turn end phases queuing back to `TurnStartPhase`

* Fix `LearnMovePhase`

* Remove `PrependSplice`

* Move DQM to phase manager

* Fix various phases being pushed instead of unshifted

* Remove `StaticSwitchSummonPhase`

* Ensure the top queue is always at length - 1

* Fix encounter `PostSummonPhase`s and Revival Blessing

* Fix move headers

* Remove implicit ordering from DQM

* Fix `PostSummonPhase`s in encounters running too early

* Fix `tryRemovePhase` usages

* Add `MovePhase` after `MoveEndPhase` automatically

* Implement an `inSpeedOrder` function

* Merge fixes

* Fix encounter rewards

* Defer `FaintPhase`s where splice was used previously

* Separate speed order utils to avoid circular imports

* Temporarily disable lunar dance test

* Simplify deferral

* Remove move priority modifier

* Fix TS errors in code files

* Fix ts errors in tests

* Fix more test files

* Fix postsummon + checkswitch ability activations

* Fix `removeAll`

* Reposition `positionalTagPhase`

* Re-add `startCurrentPhase`

* Avoid overwriting `currentPhase` after `turnStart`

* Delete `switchSummonPhasePriorityQueue`

* Update `phase-manager.ts`

* Remove uses of `isNullOrUndefined`

* Rename deferral methods

* Update docs and use `getPlayerField(true)` in turn start phase

* Use `.getEnemyField(true)`

* Update docs for post summon phase priority queue (psppq)

* Update speed order utils

* Remove null from `nextPhase`

* Update move phase timing modifier docs

* Remove mention of phases from base priority queue class

* Remove and replace `applyInSpeedOrder`

* Don't sort weather effect phases

* Order priority queues before removing

- Add some `readonly` and `public` modifiers

- Remove unused `queuedPhases` field from `MoveEffectPhase`

* Fix linting in `phase-manager.ts`

* Remove unnecessary turn order modification in Rage Fist test

---------

Co-authored-by: Bertie690 <136088738+Bertie690@users.noreply.github.com>
Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com>
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2025-09-20 17:49:40 -05:00

104 lines
3.1 KiB
TypeScript

import type { PokemonMove } from "#app/data/moves/pokemon-move";
import type { Pokemon } from "#app/field/pokemon";
import { globalScene } from "#app/global-scene";
import type { MovePhase } from "#app/phases/move-phase";
import { PokemonPhasePriorityQueue } from "#app/queues/pokemon-phase-priority-queue";
import type { BattlerIndex } from "#enums/battler-index";
import type { MovePhaseTimingModifier } from "#enums/move-phase-timing-modifier";
import type { PhaseConditionFunc } from "#types/phase-types";
/** A priority queue responsible for the ordering of {@linkcode MovePhase}s */
export class MovePhasePriorityQueue extends PokemonPhasePriorityQueue<MovePhase> {
private lastTurnOrder: Pokemon[] = [];
protected override reorder(): void {
super.reorder();
this.sortPostSpeed();
}
public cancelMove(condition: PhaseConditionFunc<"MovePhase">): void {
this.queue.find(p => condition(p))?.cancel();
}
public setTimingModifier(condition: PhaseConditionFunc<"MovePhase">, modifier: MovePhaseTimingModifier): void {
const phase = this.queue.find(p => condition(p));
if (phase != null) {
phase.timingModifier = modifier;
}
}
public setMoveForPhase(condition: PhaseConditionFunc<"MovePhase">, move: PokemonMove) {
const phase = this.queue.find(p => condition(p));
if (phase != null) {
phase.move = move;
}
}
public redirectMoves(removedPokemon: Pokemon, allyPokemon: Pokemon): void {
// failsafe: if not a double battle just return
if (!globalScene.currentBattle.double) {
return;
}
// TODO: simplify later
if (allyPokemon?.isActive(true)) {
this.queue
.filter(
mp =>
mp.targets.length === 1
&& mp.targets[0] === removedPokemon.getBattlerIndex()
&& mp.pokemon.isPlayer() !== allyPokemon.isPlayer(),
)
.forEach(targetingMovePhase => {
if (targetingMovePhase && targetingMovePhase.targets[0] !== allyPokemon.getBattlerIndex()) {
targetingMovePhase.targets[0] = allyPokemon.getBattlerIndex();
}
});
}
}
public setMoveOrder(order: BattlerIndex[]) {
this.setOrder = order;
}
public override pop(): MovePhase | undefined {
this.reorder();
const phase = this.queue.shift();
if (phase) {
this.lastTurnOrder.push(phase.pokemon);
}
return phase;
}
public getTurnOrder(): Pokemon[] {
return this.lastTurnOrder;
}
public clearTurnOrder(): void {
this.lastTurnOrder = [];
}
public override clear(): void {
this.setOrder = undefined;
this.lastTurnOrder = [];
super.clear();
}
private sortPostSpeed(): void {
this.queue.sort((a: MovePhase, b: MovePhase) => {
const priority = [a, b].map(movePhase => {
const move = movePhase.move.getMove();
return move.getPriority(movePhase.pokemon, true);
});
const timingModifiers = [a, b].map(movePhase => movePhase.timingModifier);
if (timingModifiers[0] !== timingModifiers[1]) {
return timingModifiers[1] - timingModifiers[0];
}
return priority[1] - priority[0];
});
}
}