mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-03 23:12:20 +02:00
Remove getSpeedOrder from TurnStartPhase, fix references to getCommandOrder in tests
This commit is contained in:
parent
f629dbdc10
commit
b7200f9ecf
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
});
|
||||
});
|
||||
|
@ -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());
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user