mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-09-23 23:13:42 +02:00
* 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>
208 lines
7.0 KiB
TypeScript
208 lines
7.0 KiB
TypeScript
import { Status } from "#data/status-effect";
|
|
import { Button } from "#enums/buttons";
|
|
import { StatusEffect } from "#enums/status-effect";
|
|
import { UiMode } from "#enums/ui-mode";
|
|
// biome-ignore lint/performance/noNamespaceImport: Necessary for mocks
|
|
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
|
|
import { CommandPhase } from "#phases/command-phase";
|
|
import { MessagePhase } from "#phases/message-phase";
|
|
import {
|
|
MysteryEncounterBattlePhase,
|
|
MysteryEncounterOptionSelectedPhase,
|
|
MysteryEncounterRewardsPhase,
|
|
} from "#phases/mystery-encounter-phases";
|
|
import { VictoryPhase } from "#phases/victory-phase";
|
|
import type { GameManager } from "#test/test-utils/game-manager";
|
|
import type { MessageUiHandler } from "#ui/message-ui-handler";
|
|
import type { MysteryEncounterUiHandler } from "#ui/mystery-encounter-ui-handler";
|
|
import type { OptionSelectUiHandler } from "#ui/option-select-ui-handler";
|
|
import type { PartyUiHandler } from "#ui/party-ui-handler";
|
|
import { expect, vi } from "vitest";
|
|
|
|
/**
|
|
* Runs a {@linkcode MysteryEncounter} to either the start of a battle, or to the {@linkcode MysteryEncounterRewardsPhase}, depending on the option selected
|
|
* @param game
|
|
* @param optionNo Human number, not index
|
|
* @param secondaryOptionSelect
|
|
* @param isBattle If selecting option should lead to battle, set to `true`
|
|
*/
|
|
export async function runMysteryEncounterToEnd(
|
|
game: GameManager,
|
|
optionNo: number,
|
|
secondaryOptionSelect?: { pokemonNo: number; optionNo?: number },
|
|
isBattle = false,
|
|
) {
|
|
vi.spyOn(EncounterPhaseUtils, "selectPokemonForOption");
|
|
await runSelectMysteryEncounterOption(game, optionNo, secondaryOptionSelect);
|
|
|
|
// run the selected options phase
|
|
game.onNextPrompt(
|
|
"MysteryEncounterOptionSelectedPhase",
|
|
UiMode.MESSAGE,
|
|
() => {
|
|
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
|
uiHandler.processInput(Button.ACTION);
|
|
},
|
|
() => game.isCurrentPhase(MysteryEncounterBattlePhase) || game.isCurrentPhase(MysteryEncounterRewardsPhase),
|
|
);
|
|
|
|
if (isBattle) {
|
|
game.onNextPrompt(
|
|
"CheckSwitchPhase",
|
|
UiMode.CONFIRM,
|
|
() => {
|
|
game.setMode(UiMode.MESSAGE);
|
|
game.endPhase();
|
|
},
|
|
() => game.isCurrentPhase(CommandPhase),
|
|
);
|
|
|
|
game.onNextPrompt(
|
|
"CheckSwitchPhase",
|
|
UiMode.MESSAGE,
|
|
() => {
|
|
game.setMode(UiMode.MESSAGE);
|
|
game.endPhase();
|
|
},
|
|
() => game.isCurrentPhase(CommandPhase),
|
|
);
|
|
|
|
// If a battle is started, fast forward to end of the battle
|
|
game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => {
|
|
game.scene.phaseManager.clearPhaseQueue();
|
|
game.scene.phaseManager.unshiftPhase(new VictoryPhase(0));
|
|
game.endPhase();
|
|
});
|
|
|
|
// Handle end of battle trainer messages
|
|
game.onNextPrompt("TrainerVictoryPhase", UiMode.MESSAGE, () => {
|
|
const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
|
|
uiHandler.processInput(Button.ACTION);
|
|
});
|
|
|
|
// Handle egg hatch dialogue
|
|
game.onNextPrompt("EggLapsePhase", UiMode.MESSAGE, () => {
|
|
const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
|
|
uiHandler.processInput(Button.ACTION);
|
|
});
|
|
|
|
await game.toNextTurn();
|
|
} else {
|
|
await game.phaseInterceptor.to("MysteryEncounterRewardsPhase");
|
|
}
|
|
}
|
|
|
|
export async function runSelectMysteryEncounterOption(
|
|
game: GameManager,
|
|
optionNo: number,
|
|
secondaryOptionSelect?: { pokemonNo: number; optionNo?: number },
|
|
) {
|
|
// Handle any eventual queued messages (e.g. weather phase, etc.)
|
|
game.onNextPrompt(
|
|
"MessagePhase",
|
|
UiMode.MESSAGE,
|
|
() => {
|
|
const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
|
|
uiHandler.processInput(Button.ACTION);
|
|
},
|
|
() => game.isCurrentPhase(MysteryEncounterOptionSelectedPhase),
|
|
);
|
|
|
|
if (game.isCurrentPhase(MessagePhase)) {
|
|
await game.phaseInterceptor.to("MessagePhase");
|
|
}
|
|
|
|
// dispose of intro messages
|
|
game.onNextPrompt(
|
|
"MysteryEncounterPhase",
|
|
UiMode.MESSAGE,
|
|
() => {
|
|
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
|
uiHandler.processInput(Button.ACTION);
|
|
},
|
|
() => game.isCurrentPhase(MysteryEncounterOptionSelectedPhase),
|
|
);
|
|
|
|
await game.phaseInterceptor.to("MysteryEncounterPhase", true);
|
|
|
|
// select the desired option
|
|
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
|
uiHandler.unblockInput(); // input are blocked by 1s to prevent accidental input. Tests need to handle that
|
|
|
|
switch (optionNo) {
|
|
case 2:
|
|
uiHandler.processInput(Button.RIGHT);
|
|
break;
|
|
case 3:
|
|
uiHandler.processInput(Button.DOWN);
|
|
break;
|
|
case 4:
|
|
uiHandler.processInput(Button.RIGHT);
|
|
uiHandler.processInput(Button.DOWN);
|
|
break;
|
|
default:
|
|
// no movement needed. Default cursor position
|
|
break;
|
|
}
|
|
|
|
if (secondaryOptionSelect?.pokemonNo != null) {
|
|
await handleSecondaryOptionSelect(game, secondaryOptionSelect.pokemonNo, secondaryOptionSelect.optionNo);
|
|
} else {
|
|
uiHandler.processInput(Button.ACTION);
|
|
}
|
|
}
|
|
|
|
async function handleSecondaryOptionSelect(game: GameManager, pokemonNo: number, optionNo?: number) {
|
|
// Handle secondary option selections
|
|
const partyUiHandler = game.scene.ui.handlers[UiMode.PARTY] as PartyUiHandler;
|
|
vi.spyOn(partyUiHandler, "show");
|
|
|
|
const encounterUiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
|
encounterUiHandler.processInput(Button.ACTION);
|
|
|
|
await vi.waitFor(() => expect(partyUiHandler.show).toHaveBeenCalled());
|
|
|
|
for (let i = 1; i < pokemonNo; i++) {
|
|
partyUiHandler.processInput(Button.DOWN);
|
|
}
|
|
|
|
// Open options on Pokemon
|
|
partyUiHandler.processInput(Button.ACTION);
|
|
// Click "Select" on Pokemon options
|
|
partyUiHandler.processInput(Button.ACTION);
|
|
|
|
// If there is a second choice to make after selecting a Pokemon
|
|
if (optionNo != null) {
|
|
// Wait for Summary menu to close and second options to spawn
|
|
const secondOptionUiHandler = game.scene.ui.handlers[UiMode.OPTION_SELECT] as OptionSelectUiHandler;
|
|
vi.spyOn(secondOptionUiHandler, "show");
|
|
await vi.waitFor(() => expect(secondOptionUiHandler.show).toHaveBeenCalled());
|
|
|
|
// Navigate down to the correct option
|
|
for (let i = 1; i < optionNo!; i++) {
|
|
secondOptionUiHandler.processInput(Button.DOWN);
|
|
}
|
|
|
|
// Select the option
|
|
secondOptionUiHandler.processInput(Button.ACTION);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* For any {@linkcode MysteryEncounter} that has a battle, can call this to skip battle and proceed to {@linkcode MysteryEncounterRewardsPhase}
|
|
* @param game
|
|
* @param runRewardsPhase
|
|
*/
|
|
export async function skipBattleRunMysteryEncounterRewardsPhase(game: GameManager, runRewardsPhase = true) {
|
|
game.scene.phaseManager.clearPhaseQueue();
|
|
game.scene.getEnemyParty().forEach(p => {
|
|
p.hp = 0;
|
|
p.status = new Status(StatusEffect.FAINT);
|
|
game.scene.field.remove(p);
|
|
});
|
|
game.scene.phaseManager.pushPhase(new VictoryPhase(0));
|
|
game.endPhase();
|
|
game.setMode(UiMode.MESSAGE);
|
|
await game.phaseInterceptor.to("MysteryEncounterRewardsPhase", runRewardsPhase);
|
|
}
|