Remove getSpeedOrder from TurnStartPhase, fix references to getCommandOrder in tests

This commit is contained in:
Dean 2025-06-15 13:38:50 -07:00
parent f629dbdc10
commit b7200f9ecf
6 changed files with 101 additions and 218 deletions

View File

@ -1,83 +1,34 @@
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
import { allMoves } from "#app/data/data-lists";
import { AbilityId } from "#enums/ability-id";
import { Stat } from "#app/enums/stat";
import type Pokemon from "#app/field/pokemon";
import { PokemonMove } from "#app/data/moves/pokemon-move";
import { BypassSpeedChanceModifier } from "#app/modifier/modifier";
import { Command } from "#enums/command";
import { randSeedShuffle, BooleanHolder } from "#app/utils/common";
import { FieldPhase } from "./field-phase";
import { BattlerIndex } from "#enums/battler-index";
import { TrickRoomTag } from "#app/data/arena-tag";
import type { BattlerIndex } from "#enums/battler-index";
import { SwitchType } from "#enums/switch-type";
import { globalScene } from "#app/global-scene";
import { applyInSpeedOrder } from "#app/utils/speed-order";
import type Pokemon from "#app/field/pokemon";
export class TurnStartPhase extends FieldPhase {
public readonly phaseName = "TurnStartPhase";
/**
* This orders the active Pokemon on the field by speed into an BattlerIndex array and returns that array.
* It also checks for Trick Room and reverses the array if it is present.
* @returns {@linkcode BattlerIndex[]} the battle indices of all pokemon on the field ordered by speed
*/
getSpeedOrder(): BattlerIndex[] {
const playerField = globalScene.getPlayerField().filter(p => p.isActive()) as Pokemon[];
const enemyField = globalScene.getEnemyField().filter(p => p.isActive()) as Pokemon[];
// We shuffle the list before sorting so speed ties produce random results
let orderedTargets: Pokemon[] = playerField.concat(enemyField);
// We seed it with the current turn to prevent an inconsistency where it
// was varying based on how long since you last reloaded
globalScene.executeWithSeedOffset(
() => {
orderedTargets = randSeedShuffle(orderedTargets);
},
globalScene.currentBattle.turn,
globalScene.waveSeed,
);
// Next, a check for Trick Room is applied to determine sort order.
const speedReversed = new BooleanHolder(false);
globalScene.arena.applyTags(TrickRoomTag, false, speedReversed);
// Adjust the sort function based on whether Trick Room is active.
orderedTargets.sort((a: Pokemon, b: Pokemon) => {
const aSpeed = a?.getEffectiveStat(Stat.SPD) ?? 0;
const bSpeed = b?.getEffectiveStat(Stat.SPD) ?? 0;
return speedReversed.value ? aSpeed - bSpeed : bSpeed - aSpeed;
});
return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : BattlerIndex.PLAYER));
}
/**
* This takes the result of getSpeedOrder and applies priority / bypass speed attributes to it.
* This also considers the priority levels of various commands and changes the result of getSpeedOrder based on such.
* @returns {@linkcode BattlerIndex[]} the final sequence of commands for this turn
* Returns an ordering of the current field based on command priority
* @returns {@linkcode BattlerIndex[]} the sequence of commands for this turn
*/
getCommandOrder(): BattlerIndex[] {
let moveOrder = this.getSpeedOrder();
// The creation of the battlerBypassSpeed object contains checks for the ability Quick Draw and the held item Quick Claw
// The ability Mycelium Might disables Quick Claw's activation when using a status move
// This occurs before the main loop because of battles with more than two Pokemon
const battlerBypassSpeed = {};
globalScene.getField(true).map(p => {
const bypassSpeed = new BooleanHolder(false);
const canCheckHeldItems = new BooleanHolder(true);
applyAbAttrs("BypassSpeedChanceAbAttr", p, null, false, bypassSpeed);
applyAbAttrs("PreventBypassSpeedChanceAbAttr", p, null, false, bypassSpeed, canCheckHeldItems);
if (canCheckHeldItems.value) {
globalScene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p);
}
battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed;
});
const playerField = globalScene
.getPlayerField()
.filter(p => p.isActive())
.map(p => p.getBattlerIndex());
const enemyField = globalScene
.getEnemyField()
.filter(p => p.isActive())
.map(p => p.getBattlerIndex());
const orderedTargets: BattlerIndex[] = playerField.concat(enemyField);
// The function begins sorting orderedTargets based on command priority, move priority, and possible speed bypasses.
// Non-FIGHT commands (SWITCH, BALL, RUN) have a higher command priority and will always occur before any FIGHT commands.
moveOrder = moveOrder.slice(0);
moveOrder.sort((a, b) => {
orderedTargets.sort((a, b) => {
const aCommand = globalScene.currentBattle.turnCommands[a];
const bCommand = globalScene.currentBattle.turnCommands[b];
@ -88,40 +39,14 @@ export class TurnStartPhase extends FieldPhase {
if (bCommand?.command === Command.FIGHT) {
return -1;
}
} else if (aCommand?.command === Command.FIGHT) {
const aMove = allMoves[aCommand.move!.move];
const bMove = allMoves[bCommand!.move!.move];
const aUser = globalScene.getField(true).find(p => p.getBattlerIndex() === a)!;
const bUser = globalScene.getField(true).find(p => p.getBattlerIndex() === b)!;
const aPriority = aMove.getPriority(aUser, false);
const bPriority = bMove.getPriority(bUser, false);
// The game now checks for differences in priority levels.
// If the moves share the same original priority bracket, it can check for differences in battlerBypassSpeed and return the result.
// This conditional is used to ensure that Quick Claw can still activate with abilities like Stall and Mycelium Might (attack moves only)
// Otherwise, the game returns the user of the move with the highest priority.
const isSameBracket = Math.ceil(aPriority) - Math.ceil(bPriority) === 0;
if (aPriority !== bPriority) {
if (isSameBracket && battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) {
return battlerBypassSpeed[a].value ? -1 : 1;
}
return aPriority < bPriority ? 1 : -1;
}
}
// If there is no difference between the move's calculated priorities, the game checks for differences in battlerBypassSpeed and returns the result.
if (battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) {
return battlerBypassSpeed[a].value ? -1 : 1;
}
const aIndex = moveOrder.indexOf(a);
const bIndex = moveOrder.indexOf(b);
const aIndex = orderedTargets.indexOf(a);
const bIndex = orderedTargets.indexOf(b);
return aIndex < bIndex ? -1 : aIndex > bIndex ? 1 : 0;
});
return moveOrder;
return orderedTargets;
}
start() {
@ -132,19 +57,21 @@ export class TurnStartPhase extends FieldPhase {
let orderIndex = 0;
for (const o of this.getSpeedOrder()) {
const pokemon = field[o];
const preTurnCommand = globalScene.currentBattle.preTurnCommands[o];
applyInSpeedOrder(
field.filter(p => p?.isActive(true)),
(p: Pokemon) => {
const preTurnCommand = globalScene.currentBattle.preTurnCommands[p.getBattlerIndex()];
if (preTurnCommand?.skip) {
continue;
}
if (preTurnCommand?.skip) {
return;
}
switch (preTurnCommand?.command) {
case Command.TERA:
globalScene.phaseManager.pushNew("TeraPhase", pokemon);
}
}
switch (preTurnCommand?.command) {
case Command.TERA:
globalScene.phaseManager.pushNew("TeraPhase", p);
}
},
);
const phaseManager = globalScene.phaseManager;

View File

@ -8,6 +8,10 @@ export interface hasPokemon {
getPokemon(): Pokemon;
}
export function applyInSpeedOrder<T extends Pokemon>(pokemonList: T[], callback: (pokemon: Pokemon) => void): void {
sortInSpeedOrder(pokemonList).forEach(pokemon => callback(pokemon));
}
export function sortInSpeedOrder<T extends Pokemon | hasPokemon>(pokemonList: T[]): T[] {
pokemonList = shuffle(pokemonList);
sortBySpeed(pokemonList);

View File

@ -1,5 +1,3 @@
import { TurnEndPhase } from "#app/phases/turn-end-phase";
import { TurnStartPhase } from "#app/phases/turn-start-phase";
import GameManager from "#test/testUtils/gameManager";
import { AbilityId } from "#enums/ability-id";
import { Stat } from "#enums/stat";
@ -45,65 +43,50 @@ describe("Abilities - Mycelium Might", () => {
it("will move last in its priority bracket and ignore protective abilities", async () => {
await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
const enemyPokemon = game.scene.getEnemyPokemon();
const playerIndex = game.scene.getPlayerPokemon()?.getBattlerIndex();
const enemyIndex = enemyPokemon?.getBattlerIndex();
const enemy = game.scene.getEnemyPokemon()!;
const player = game.scene.getPlayerPokemon()!;
game.move.select(MoveId.BABY_DOLL_EYES);
await game.phaseInterceptor.to(TurnStartPhase, false);
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const speedOrder = phase.getSpeedOrder();
const commandOrder = phase.getCommandOrder();
await game.phaseInterceptor.to("MoveEndPhase", false);
// The opponent Pokemon (without Mycelium Might) goes first despite having lower speed than the player Pokemon.
// The player Pokemon (with Mycelium Might) goes last despite having higher speed than the opponent.
expect(speedOrder).toEqual([playerIndex, enemyIndex]);
expect(commandOrder).toEqual([enemyIndex, playerIndex]);
await game.phaseInterceptor.to(TurnEndPhase);
expect(player.hp).not.toEqual(player.getMaxHp());
await game.phaseInterceptor.to("TurnEndPhase");
// Despite the opponent's ability (Clear Body), its ATK stat stage is still reduced.
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1);
}, 20000);
expect(enemy.getStatStage(Stat.ATK)).toBe(-1);
});
it("will still go first if a status move that is in a higher priority bracket than the opponent's move is used", async () => {
game.override.enemyMoveset(MoveId.TACKLE);
await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
const enemyPokemon = game.scene.getEnemyPokemon();
const playerIndex = game.scene.getPlayerPokemon()?.getBattlerIndex();
const enemyIndex = enemyPokemon?.getBattlerIndex();
const enemy = game.scene.getEnemyPokemon()!;
const player = game.scene.getPlayerPokemon()!;
game.move.select(MoveId.BABY_DOLL_EYES);
await game.phaseInterceptor.to(TurnStartPhase, false);
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const speedOrder = phase.getSpeedOrder();
const commandOrder = phase.getCommandOrder();
await game.phaseInterceptor.to("MoveEndPhase", false);
// The player Pokemon (with M.M.) goes first because its move is still within a higher priority bracket than its opponent.
// The enemy Pokemon goes second because its move is in a lower priority bracket.
expect(speedOrder).toEqual([playerIndex, enemyIndex]);
expect(commandOrder).toEqual([playerIndex, enemyIndex]);
await game.phaseInterceptor.to(TurnEndPhase);
expect(player.hp).toEqual(player.getMaxHp());
await game.phaseInterceptor.to("TurnEndPhase");
// Despite the opponent's ability (Clear Body), its ATK stat stage is still reduced.
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1);
}, 20000);
expect(enemy.getStatStage(Stat.ATK)).toBe(-1);
});
it("will not affect non-status moves", async () => {
await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex();
const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex();
const player = game.scene.getPlayerPokemon()!;
game.move.select(MoveId.QUICK_ATTACK);
await game.phaseInterceptor.to(TurnStartPhase, false);
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const speedOrder = phase.getSpeedOrder();
const commandOrder = phase.getCommandOrder();
await game.phaseInterceptor.to("MoveEndPhase", false);
// The player Pokemon (with M.M.) goes first because it has a higher speed and did not use a status move.
// The enemy Pokemon (without M.M.) goes second because its speed is lower.
// This means that the commandOrder should be identical to the speedOrder
expect(speedOrder).toEqual([playerIndex, enemyIndex]);
expect(commandOrder).toEqual([playerIndex, enemyIndex]);
}, 20000);
expect(player.hp).toEqual(player.getMaxHp());
});
});

View File

@ -4,7 +4,6 @@ import { SpeciesId } from "#enums/species-id";
import GameManager from "#test/testUtils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { TurnStartPhase } from "#app/phases/turn-start-phase";
describe("Abilities - Stall", () => {
let phaserGame: Phaser.Game;
@ -40,56 +39,41 @@ describe("Abilities - Stall", () => {
it("Pokemon with Stall should move last in its priority bracket regardless of speed", async () => {
await game.classicMode.startBattle([SpeciesId.SHUCKLE]);
const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex();
const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex();
const player = game.scene.getPlayerPokemon()!;
game.move.select(MoveId.QUICK_ATTACK);
await game.phaseInterceptor.to(TurnStartPhase, false);
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const speedOrder = phase.getSpeedOrder();
const commandOrder = phase.getCommandOrder();
await game.phaseInterceptor.to("MoveEndPhase", false);
// The player Pokemon (without Stall) goes first despite having lower speed than the opponent.
// The opponent Pokemon (with Stall) goes last despite having higher speed than the player Pokemon.
expect(speedOrder).toEqual([enemyIndex, playerIndex]);
expect(commandOrder).toEqual([playerIndex, enemyIndex]);
}, 20000);
expect(player.hp).toEqual(player.getMaxHp());
});
it("Pokemon with Stall will go first if a move that is in a higher priority bracket than the opponent's move is used", async () => {
await game.classicMode.startBattle([SpeciesId.SHUCKLE]);
const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex();
const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex();
const player = game.scene.getPlayerPokemon()!;
game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.to(TurnStartPhase, false);
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const speedOrder = phase.getSpeedOrder();
const commandOrder = phase.getCommandOrder();
await game.phaseInterceptor.to("MoveEndPhase", false);
// The opponent Pokemon (with Stall) goes first because its move is still within a higher priority bracket than its opponent.
// The player Pokemon goes second because its move is in a lower priority bracket.
expect(speedOrder).toEqual([enemyIndex, playerIndex]);
expect(commandOrder).toEqual([enemyIndex, playerIndex]);
}, 20000);
expect(player.hp).not.toEqual(player.getMaxHp());
});
it("If both Pokemon have stall and use the same move, speed is used to determine who goes first.", async () => {
game.override.ability(AbilityId.STALL);
await game.classicMode.startBattle([SpeciesId.SHUCKLE]);
const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex();
const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex();
const player = game.scene.getPlayerPokemon()!;
game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.to(TurnStartPhase, false);
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const speedOrder = phase.getSpeedOrder();
const commandOrder = phase.getCommandOrder();
await game.phaseInterceptor.to("MoveEndPhase", false);
// The opponent Pokemon (with Stall) goes first because it has a higher speed.
// The player Pokemon (with Stall) goes second because its speed is lower.
expect(speedOrder).toEqual([enemyIndex, playerIndex]);
expect(commandOrder).toEqual([enemyIndex, playerIndex]);
}, 20000);
expect(player.hp).not.toEqual(player.getMaxHp());
});
});

View File

@ -1,7 +1,6 @@
import { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
import { SelectTargetPhase } from "#app/phases/select-target-phase";
import { TurnStartPhase } from "#app/phases/turn-start-phase";
import type { MovePhase } from "#app/phases/move-phase";
import { AbilityId } from "#enums/ability-id";
import { BattlerIndex } from "#enums/battler-index";
import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import GameManager from "#test/testUtils/gameManager";
@ -35,63 +34,60 @@ describe("Battle order", () => {
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerStartHp = playerPokemon.hp;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyStartHp = enemyPokemon.hp;
vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set playerPokemon's speed to 50
vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set enemyPokemon's speed to 150
game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.run(EnemyCommandPhase);
const playerPokemonIndex = playerPokemon.getBattlerIndex();
const enemyPokemonIndex = enemyPokemon.getBattlerIndex();
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const order = phase.getCommandOrder();
expect(order[0]).toBe(enemyPokemonIndex);
expect(order[1]).toBe(playerPokemonIndex);
}, 20000);
await game.phaseInterceptor.to("MoveEndPhase", false);
expect(playerPokemon.hp).not.toEqual(playerStartHp);
expect(enemyPokemon.hp).toEqual(enemyStartHp);
});
it("Player faster than opponent 150 vs 50", async () => {
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerStartHp = playerPokemon.hp;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyStartHp = enemyPokemon.hp;
vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set playerPokemon's speed to 150
vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set enemyPokemon's speed to 50
game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.run(EnemyCommandPhase);
const playerPokemonIndex = playerPokemon.getBattlerIndex();
const enemyPokemonIndex = enemyPokemon.getBattlerIndex();
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const order = phase.getCommandOrder();
expect(order[0]).toBe(playerPokemonIndex);
expect(order[1]).toBe(enemyPokemonIndex);
}, 20000);
await game.phaseInterceptor.to("MoveEndPhase", false);
expect(playerPokemon.hp).toEqual(playerStartHp);
expect(enemyPokemon.hp).not.toEqual(enemyStartHp);
});
it("double - both opponents faster than player 50/50 vs 150/150", async () => {
game.override.battleStyle("double");
await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.BLASTOISE]);
const playerPokemon = game.scene.getPlayerField();
const playerHps = playerPokemon.map(p => p.hp);
const enemyPokemon = game.scene.getEnemyField();
const enemyHps = enemyPokemon.map(p => p.hp);
playerPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50])); // set both playerPokemons' speed to 50
enemyPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150])); // set both enemyPokemons' speed to 150
const playerIndices = playerPokemon.map(p => p?.getBattlerIndex());
const enemyIndices = enemyPokemon.map(p => p?.getBattlerIndex());
game.move.select(MoveId.TACKLE);
game.move.select(MoveId.TACKLE, 1);
await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false);
await game.move.selectEnemyMove(MoveId.TACKLE, BattlerIndex.PLAYER);
await game.move.selectEnemyMove(MoveId.TACKLE, BattlerIndex.PLAYER_2);
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const order = phase.getCommandOrder();
expect(order.slice(0, 2).includes(enemyIndices[0])).toBe(true);
expect(order.slice(0, 2).includes(enemyIndices[1])).toBe(true);
expect(order.slice(2, 4).includes(playerIndices[0])).toBe(true);
expect(order.slice(2, 4).includes(playerIndices[1])).toBe(true);
}, 20000);
await game.phaseInterceptor.to("MoveEndPhase", true);
await game.phaseInterceptor.to("MoveEndPhase", false);
for (let i = 0; i < 2; i++) {
expect(playerPokemon[i].hp).not.toEqual(playerHps[i]);
expect(enemyPokemon[i].hp).toEqual(enemyHps[i]);
}
});
it("double - speed tie except 1 - 100/100 vs 100/150", async () => {
game.override.battleStyle("double");
@ -102,20 +98,14 @@ describe("Battle order", () => {
playerPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100])); //set both playerPokemons' speed to 100
vi.spyOn(enemyPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100]); // set enemyPokemon's speed to 100
vi.spyOn(enemyPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set enemyPokemon's speed to 150
const playerIndices = playerPokemon.map(p => p?.getBattlerIndex());
const enemyIndices = enemyPokemon.map(p => p?.getBattlerIndex());
game.move.select(MoveId.TACKLE);
game.move.select(MoveId.TACKLE, 1);
await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false);
await game.phaseInterceptor.to("MovePhase", false);
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const order = phase.getCommandOrder();
expect(order[0]).toBe(enemyIndices[1]);
expect(order.slice(1, 4).includes(enemyIndices[0])).toBe(true);
expect(order.slice(1, 4).includes(playerIndices[0])).toBe(true);
expect(order.slice(1, 4).includes(playerIndices[1])).toBe(true);
}, 20000);
const phase = game.scene.phaseManager.getCurrentPhase() as MovePhase;
expect(phase.pokemon).toEqual(enemyPokemon[1]);
});
it("double - speed tie 100/150 vs 100/150", async () => {
game.override.battleStyle("double");
@ -127,18 +117,13 @@ describe("Battle order", () => {
vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set other playerPokemon's speed to 150
vi.spyOn(enemyPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100]); // set one enemyPokemon's speed to 100
vi.spyOn(enemyPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set other enemyPokemon's speed to 150
const playerIndices = playerPokemon.map(p => p?.getBattlerIndex());
const enemyIndices = enemyPokemon.map(p => p?.getBattlerIndex());
game.move.select(MoveId.TACKLE);
game.move.select(MoveId.TACKLE, 1);
await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false);
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const order = phase.getCommandOrder();
expect(order.slice(0, 2).includes(playerIndices[1])).toBe(true);
expect(order.slice(0, 2).includes(enemyIndices[1])).toBe(true);
expect(order.slice(2, 4).includes(playerIndices[0])).toBe(true);
expect(order.slice(2, 4).includes(enemyIndices[0])).toBe(true);
}, 20000);
await game.phaseInterceptor.to("MovePhase", false);
const phase = game.scene.phaseManager.getCurrentPhase() as MovePhase;
expect(enemyPokemon[1] === phase.pokemon || playerPokemon[1] === phase.pokemon);
});
});

View File

@ -519,7 +519,7 @@ export default class GameManager {
}
/**
* Intercepts `TurnStartPhase` and mocks {@linkcode TurnStartPhase.getSpeedOrder}'s return value.
* Modifies the queue manager to return move phases in a particular order
* Used to manually modify Pokemon turn order.
* Note: This *DOES NOT* account for priority.
* @param order - The turn order to set as an array of {@linkcode BattlerIndex}es.