mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-09-24 15:33:29 +02:00
Doc fixes & test changes
This commit is contained in:
parent
cf5e7fd981
commit
0dbb68c3ba
@ -7,7 +7,6 @@ import { MovePhasePriorityQueue } from "#app/queues/move-phase-priority-queue";
|
||||
import { PokemonPhasePriorityQueue } from "#app/queues/pokemon-phase-priority-queue";
|
||||
import { PostSummonPhasePriorityQueue } from "#app/queues/post-summon-phase-priority-queue";
|
||||
import type { PriorityQueue } from "#app/queues/priority-queue";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import type { MovePhaseTimingModifier } from "#enums/move-phase-timing-modifier";
|
||||
|
||||
// TODO: might be easier to define which phases should be dynamic instead
|
||||
@ -71,8 +70,8 @@ export class DynamicQueueManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highest-priority (generally by speed) {@linkcode Phase} of the specified type
|
||||
* @param type - The {@linkcode PhaseString | type} to pop
|
||||
* Returns the highest-priority (generally by speed) {@linkcode Phase} of the specified type.
|
||||
* @param type - The {@linkcode PhaseString | type} of phase to access
|
||||
* @returns The popped {@linkcode Phase}, or `undefined` if none of the specified type exist
|
||||
*/
|
||||
public popNextPhase(type: PhaseString): Phase | undefined {
|
||||
@ -85,7 +84,7 @@ export class DynamicQueueManager {
|
||||
* @param condition - An optional {@linkcode PhaseConditionFunc} to add conditions to the search
|
||||
* @returns Whether a matching phase exists
|
||||
*/
|
||||
public exists<T extends PhaseString>(type: T, condition?: PhaseConditionFunc<T>): boolean {
|
||||
public exists<T extends PhaseString>(type: T, condition: PhaseConditionFunc<T> = () => true): boolean {
|
||||
return !!this.dynamicPhaseMap.get(type)?.has(condition);
|
||||
}
|
||||
|
||||
@ -136,21 +135,13 @@ export class DynamicQueueManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and cancels a {@linkcode MovePhase} meeting the condition
|
||||
* @param phaseCondition - The {@linkcode PhaseConditionFunc | condition} function
|
||||
* Find and cancel a {@linkcode MovePhase} meeting the condition
|
||||
* @param phaseCondition - The {@linkcode PhaseConditionFunc | condition} function to filter phases by
|
||||
*/
|
||||
public cancelMovePhase(condition: PhaseConditionFunc<"MovePhase">): void {
|
||||
this.getMovePhaseQueue().cancelMove(condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the move order to a static array rather than a dynamic queue
|
||||
* @param order - The order of {@linkcode BattlerIndex}s
|
||||
*/
|
||||
public setMoveOrder(order: BattlerIndex[]): void {
|
||||
this.getMovePhaseQueue().setMoveOrder(order);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns An in-order array of {@linkcode Pokemon}, representing the turn order as played out in the most recent turn
|
||||
*/
|
||||
@ -171,7 +162,7 @@ export class DynamicQueueManager {
|
||||
/**
|
||||
* Internal helper to determine if a phase is dynamic.
|
||||
* @param phase - The {@linkcode Phase} to check
|
||||
* @returns Whether `phase` is dynamic
|
||||
* @returns Whether `phase` is dynamic.
|
||||
* @privateRemarks
|
||||
* Currently, this checks that `phase` has a `getPokemon` method
|
||||
* and is not blacklisted in `nonDynamicPokemonPhases`.
|
||||
|
@ -3216,9 +3216,9 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the specified Pokémon is an opponent
|
||||
* Check whether 2 Pokémon oppose one another during battle.
|
||||
* @param target - The {@linkcode Pokemon} to compare against
|
||||
* @returns `true` if the two pokemon are allies, `false` otherwise
|
||||
* @returns Whether this Pokemon is an opponent of `target` (one is player and the other enemy).
|
||||
*/
|
||||
public isOpponent(target: Pokemon): boolean {
|
||||
return this.isPlayer() !== target.isPlayer();
|
||||
|
@ -3,7 +3,6 @@ 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";
|
||||
|
||||
@ -17,18 +16,18 @@ export class MovePhasePriorityQueue extends PokemonPhasePriorityQueue<MovePhase>
|
||||
}
|
||||
|
||||
public cancelMove(condition: PhaseConditionFunc<"MovePhase">): void {
|
||||
this.queue.find(p => condition(p))?.cancel();
|
||||
this.queue.find(condition)?.cancel();
|
||||
}
|
||||
|
||||
public setTimingModifier(condition: PhaseConditionFunc<"MovePhase">, modifier: MovePhaseTimingModifier): void {
|
||||
const phase = this.queue.find(p => condition(p));
|
||||
const phase = this.queue.find(condition);
|
||||
if (phase != null) {
|
||||
phase.timingModifier = modifier;
|
||||
}
|
||||
}
|
||||
|
||||
public setMoveForPhase(condition: PhaseConditionFunc<"MovePhase">, move: PokemonMove) {
|
||||
const phase = this.queue.find(p => condition(p));
|
||||
const phase = this.queue.find(condition);
|
||||
if (phase != null) {
|
||||
phase.move = move;
|
||||
}
|
||||
@ -47,7 +46,7 @@ export class MovePhasePriorityQueue extends PokemonPhasePriorityQueue<MovePhase>
|
||||
mp =>
|
||||
mp.targets.length === 1
|
||||
&& mp.targets[0] === removedPokemon.getBattlerIndex()
|
||||
&& mp.pokemon.isPlayer() !== allyPokemon.isPlayer(),
|
||||
&& mp.pokemon.isOpponent(allyPokemon),
|
||||
)
|
||||
.forEach(targetingMovePhase => {
|
||||
if (targetingMovePhase && targetingMovePhase.targets[0] !== allyPokemon.getBattlerIndex()) {
|
||||
@ -57,10 +56,6 @@ export class MovePhasePriorityQueue extends PokemonPhasePriorityQueue<MovePhase>
|
||||
}
|
||||
}
|
||||
|
||||
public setMoveOrder(order: BattlerIndex[]) {
|
||||
this.setOrder = order;
|
||||
}
|
||||
|
||||
public override pop(): MovePhase | undefined {
|
||||
this.reorder();
|
||||
const phase = this.queue.shift();
|
||||
@ -79,25 +74,20 @@ export class MovePhasePriorityQueue extends PokemonPhasePriorityQueue<MovePhase>
|
||||
}
|
||||
|
||||
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];
|
||||
});
|
||||
this.queue.sort(
|
||||
(a: MovePhase, b: MovePhase) =>
|
||||
// formatting
|
||||
b.timingModifier - a.timingModifier || getPriorityForMP(b) - getPriorityForMP(a),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getPriorityForMP(mp: MovePhase): number {
|
||||
const move = mp.move.getMove();
|
||||
return move.getPriority(mp.pokemon, true);
|
||||
}
|
||||
|
@ -1,20 +1,10 @@
|
||||
import type { DynamicPhase } from "#app/@types/phase-types";
|
||||
import { PriorityQueue } from "#app/queues/priority-queue";
|
||||
import { sortInSpeedOrder } from "#app/utils/speed-order";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
|
||||
/** A generic speed-based priority queue of {@linkcode DynamicPhase}s */
|
||||
/** A generic speed-based priority queue of {@linkcode DynamicPhase}s. */
|
||||
export class PokemonPhasePriorityQueue<T extends DynamicPhase> extends PriorityQueue<T> {
|
||||
protected setOrder: BattlerIndex[] | undefined;
|
||||
protected override reorder(): void {
|
||||
const setOrder = this.setOrder;
|
||||
if (setOrder) {
|
||||
this.queue.sort(
|
||||
(a, b) =>
|
||||
setOrder.indexOf(a.getPokemon().getBattlerIndex()) - setOrder.indexOf(b.getPokemon().getBattlerIndex()),
|
||||
);
|
||||
} else {
|
||||
this.queue = sortInSpeedOrder(this.queue);
|
||||
}
|
||||
sortInSpeedOrder(this.queue);
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import { sortInSpeedOrder } from "#app/utils/speed-order";
|
||||
*/
|
||||
export class PostSummonPhasePriorityQueue extends PokemonPhasePriorityQueue<PostSummonPhase> {
|
||||
protected override reorder(): void {
|
||||
this.queue = sortInSpeedOrder(this.queue, false);
|
||||
sortInSpeedOrder(this.queue, false);
|
||||
this.queue.sort((phaseA, phaseB) => phaseB.getPriority() - phaseA.getPriority());
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,24 @@
|
||||
/**
|
||||
* Stores a list of elements.
|
||||
* Abstract class representing a {@link https://en.wikipedia.org/wiki/Priority_queue#Min-priority_queue | Min-priority queue}.
|
||||
*
|
||||
* Dynamically updates ordering to always pop the highest "priority", based on implementation of {@linkcode reorder}.
|
||||
* Dynamically updates ordering to always return the highest "priority" item,
|
||||
* based on the implementation of {@linkcode reorder}.
|
||||
*/
|
||||
export abstract class PriorityQueue<T> {
|
||||
/** The items in the queue. */
|
||||
protected queue: T[] = [];
|
||||
|
||||
/**
|
||||
* Sorts the elements in the queue
|
||||
* Sort the elements in the queue.
|
||||
* @remarks
|
||||
* When sorting, earlier elements will be accessed before later ones.
|
||||
*/
|
||||
protected abstract reorder(): void;
|
||||
|
||||
/**
|
||||
* Calls {@linkcode reorder} and shifts the queue
|
||||
* @returns The front element of the queue after sorting, or `undefined` if the queue is empty
|
||||
* Reorder the queue before removing and returning the highest priority element.
|
||||
* @returns The front-most element of the queue after sorting,
|
||||
* or `undefined` if the queue is empty.
|
||||
* @sealed
|
||||
*/
|
||||
public pop(): T | undefined {
|
||||
@ -34,7 +39,7 @@ export abstract class PriorityQueue<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all elements from the queue
|
||||
* Remove all elements from the queue.
|
||||
* @sealed
|
||||
*/
|
||||
public clear(): void {
|
||||
@ -50,8 +55,8 @@ export abstract class PriorityQueue<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the first element matching the condition
|
||||
* @param condition - An optional condition function (defaults to a function that always returns `true`)
|
||||
* Remove the first element matching the condition
|
||||
* @param condition - If provided, will restrict the removal to only phases matching the condition
|
||||
* @returns Whether a removal occurred
|
||||
*/
|
||||
public remove(condition: (t: T) => boolean = () => true): boolean {
|
||||
@ -67,12 +72,12 @@ export abstract class PriorityQueue<T> {
|
||||
}
|
||||
|
||||
/** @returns An element matching the condition function */
|
||||
public find(condition?: (t: T) => boolean): T | undefined {
|
||||
return this.queue.find(e => !condition || condition(e));
|
||||
public find(condition: (t: T) => boolean): T | undefined {
|
||||
return this.queue.find(condition);
|
||||
}
|
||||
|
||||
/** @returns Whether an element matching the condition function exists */
|
||||
public has(condition?: (t: T) => boolean): boolean {
|
||||
return this.queue.some(e => !condition || condition(e));
|
||||
public has(condition: (t: T) => boolean): boolean {
|
||||
return this.queue.some(condition);
|
||||
}
|
||||
}
|
||||
|
@ -135,8 +135,10 @@ export function randSeedItem<T>(items: T[]): T {
|
||||
|
||||
/**
|
||||
* Shuffle a list using the seeded rng. Utilises the Fisher-Yates algorithm.
|
||||
* @param items An array of items.
|
||||
* @param items - The array of items to shuffle.
|
||||
* @returns A new shuffled array of items.
|
||||
* @remarks
|
||||
* This does _not_ mutate the array (unlike {@linkcode Array.sort}).
|
||||
*/
|
||||
export function randSeedShuffle<T>(items: T[]): T[] {
|
||||
if (items.length <= 1) {
|
||||
|
@ -5,7 +5,8 @@ import type { Pokemon } from "#field/pokemon";
|
||||
|
||||
/**
|
||||
* A generator function which uses a priority queue to yield each pokemon from a given side of the field in speed order.
|
||||
* @param side - The {@linkcode ArenaTagSide | side} of the field to use
|
||||
* @param side - The {@linkcode ArenaTagSide | side} of the field to use;
|
||||
* default `ArenaTagSide.BOTH`
|
||||
* @returns A {@linkcode Generator} of {@linkcode Pokemon}
|
||||
*
|
||||
* @remarks
|
||||
|
@ -10,20 +10,23 @@ interface hasPokemon {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts an array of {@linkcode Pokemon} by speed, taking Trick Room into account.
|
||||
* @param pokemonList - The list of Pokemon or objects containing Pokemon
|
||||
* @param shuffleFirst - Whether to shuffle the list before sorting (to handle speed ties). Default `true`.
|
||||
* @returns The sorted array of {@linkcode Pokemon}
|
||||
* Sort an array of {@linkcode Pokemon} in _ascending_ speed order, taking Trick Room into account.
|
||||
* @param pokemonList - An array of `Pokemon` or objects containing `Pokemon` to sort;
|
||||
* will be mutated and sorted in place.
|
||||
* @param shuffleFirst - Whether to shuffle the list before sorting (to handle speed ties); default `true`.
|
||||
* If `false`, will sort speed ties in ascending order of `BattlerIndex`es.
|
||||
*/
|
||||
export function sortInSpeedOrder<T extends Pokemon | hasPokemon>(pokemonList: T[], shuffleFirst = true): T[] {
|
||||
pokemonList = shuffleFirst ? shufflePokemonList(pokemonList) : pokemonList;
|
||||
export function sortInSpeedOrder<T extends Pokemon | hasPokemon>(pokemonList: T[], shuffleFirst = true): void {
|
||||
if (shuffleFirst) {
|
||||
pokemonList = shufflePokemonList(pokemonList);
|
||||
}
|
||||
sortBySpeed(pokemonList);
|
||||
return pokemonList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pokemonList - The array of Pokemon or objects containing Pokemon
|
||||
* @returns The shuffled array
|
||||
* Helper function to randomly shuffle an array of Pokemon.
|
||||
* @param pokemonList - The array of Pokemon or objects containing Pokemon to shuffle
|
||||
* @returns The shuffled array.
|
||||
*/
|
||||
function shufflePokemonList<T extends Pokemon | hasPokemon>(pokemonList: T[]): T[] {
|
||||
// This is seeded with the current turn to prevent an inconsistency where it
|
||||
@ -38,7 +41,7 @@ function shufflePokemonList<T extends Pokemon | hasPokemon>(pokemonList: T[]): T
|
||||
return pokemonList;
|
||||
}
|
||||
|
||||
/** Sorts an array of {@linkcode Pokemon} by speed (without shuffling) */
|
||||
/** Sort an array of {@linkcode Pokemon} in descending speed order (without shuffling) */
|
||||
function sortBySpeed<T extends Pokemon | hasPokemon>(pokemonList: T[]): void {
|
||||
pokemonList.sort((a, b) => {
|
||||
const aSpeed = (a instanceof Pokemon ? a : a.getPokemon()).getEffectiveStat(Stat.SPD);
|
||||
|
@ -14,7 +14,7 @@ import { PlayerGender } from "#enums/player-gender";
|
||||
import type { PokeballType } from "#enums/pokeball";
|
||||
import type { SpeciesId } from "#enums/species-id";
|
||||
import { UiMode } from "#enums/ui-mode";
|
||||
import type { EnemyPokemon, PlayerPokemon } from "#field/pokemon";
|
||||
import { type EnemyPokemon, type PlayerPokemon, Pokemon } from "#field/pokemon";
|
||||
import { Trainer } from "#field/trainer";
|
||||
import { ModifierTypeOption } from "#modifiers/modifier-type";
|
||||
import { CheckSwitchPhase } from "#phases/check-switch-phase";
|
||||
@ -52,10 +52,13 @@ import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
|
||||
import type { PartyUiHandler } from "#ui/party-ui-handler";
|
||||
import type { StarterSelectUiHandler } from "#ui/starter-select-ui-handler";
|
||||
import type { TargetSelectUiHandler } from "#ui/target-select-ui-handler";
|
||||
import { sortInSpeedOrder } from "#utils/speed-order";
|
||||
import fs from "node:fs";
|
||||
import { AES, enc } from "crypto-js";
|
||||
import { expect, vi } from "vitest";
|
||||
|
||||
vi.mock(import("#utils/speed-order"), { spy: true });
|
||||
|
||||
/**
|
||||
* Class to manage the game state and transitions between phases.
|
||||
*/
|
||||
@ -536,19 +539,39 @@ export class GameManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the queue manager to return move phases in a particular order
|
||||
* Override the speed order of the battle's current combatants.
|
||||
* 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.
|
||||
* @param order - The turn order to set as an array of {@linkcode BattlerIndex}es
|
||||
* @example
|
||||
* ```ts
|
||||
* await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2]);
|
||||
* game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2]);
|
||||
* ```
|
||||
* @throws Fails test immediately if `order` does not contain all non-fainted combatants' `BattlerIndex`es.
|
||||
* @remarks
|
||||
* This does not account for priority, the battlers' relative speed stats.
|
||||
* @todo What should happen if the number of active battlers changes mid-test?
|
||||
*/
|
||||
async setTurnOrder(order: BattlerIndex[]): Promise<void> {
|
||||
await this.phaseInterceptor.to("TurnStartPhase", false);
|
||||
public setTurnOrder(order: Exclude<BattlerIndex, BattlerIndex.ATTACKER>[]): void {
|
||||
// TODO: Remove type assertions once `BattlerIndex.ATTACKER` ceases to exist
|
||||
expect(order).toEqualUnsorted(
|
||||
this.scene.getField(true).map(p => p.getBattlerIndex() as Exclude<BattlerIndex, BattlerIndex.ATTACKER>),
|
||||
);
|
||||
|
||||
this.scene.phaseManager.dynamicQueueManager.setMoveOrder(order);
|
||||
expect(vi.isMockFunction(sortInSpeedOrder)).toBe(true);
|
||||
vi.mocked(sortInSpeedOrder).mockImplementation(list => {
|
||||
list.sort((a, b) => {
|
||||
const aBattlerIndex = (a instanceof Pokemon ? a : a.getPokemon()).getBattlerIndex() as Exclude<
|
||||
BattlerIndex,
|
||||
BattlerIndex.ATTACKER
|
||||
>;
|
||||
const bBattlerIndex = (b instanceof Pokemon ? b : b.getPokemon()).getBattlerIndex() as Exclude<
|
||||
BattlerIndex,
|
||||
BattlerIndex.ATTACKER
|
||||
>;
|
||||
|
||||
return order.indexOf(bBattlerIndex) - order.indexOf(aBattlerIndex);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user