Breakup commandPhase#start

Co-authored-by: Bertie690 <136088738+Bertie690@users.noreply.github.com>
This commit is contained in:
Sirz Benjie 2025-05-26 15:56:14 -05:00
parent 74c20bbcb8
commit 11c9b0236c
No known key found for this signature in database
GPG Key ID: 38AC42D68CF5E138

View File

@ -1,7 +1,6 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import type { TurnCommand } from "#app/battle"; import type { TurnCommand } from "#app/battle";
import { BattleType } from "#enums/battle-type"; import { BattleType } from "#enums/battle-type";
import type { EncoreTag } from "#app/data/battler-tags";
import { TrappedTag } from "#app/data/battler-tags"; import { TrappedTag } from "#app/data/battler-tags";
import type { MoveTargetSet } from "#app/data/moves/move"; import type { MoveTargetSet } from "#app/data/moves/move";
import { getMoveTargets } from "#app/data/moves/move-utils"; import { getMoveTargets } from "#app/data/moves/move-utils";
@ -38,11 +37,13 @@ export class CommandPhase extends FieldPhase {
this.fieldIndex = fieldIndex; this.fieldIndex = fieldIndex;
} }
start() { /**
super.start(); * Resets the cursor to the position of {@linkcode Command.FIGHT} if any of the following are true
* - The setting to remember the last action is not enabled
globalScene.updateGameInfo(); * - This is the first turn of a mystery encounter, trainer battle, or the END biome
* - The cursor is currently on the POKEMON command
*/
private resetCursorIfNeeded() {
const commandUiHandler = globalScene.ui.handlers[UiMode.COMMAND]; const commandUiHandler = globalScene.ui.handlers[UiMode.COMMAND];
// If one of these conditions is true, we always reset the cursor to Command.FIGHT // If one of these conditions is true, we always reset the cursor to Command.FIGHT
@ -51,33 +52,42 @@ export class CommandPhase extends FieldPhase {
globalScene.currentBattle.battleType === BattleType.TRAINER || globalScene.currentBattle.battleType === BattleType.TRAINER ||
globalScene.arena.biomeType === BiomeId.END; globalScene.arena.biomeType === BiomeId.END;
if (commandUiHandler) { if (
if ( (commandUiHandler &&
(globalScene.currentBattle.turn === 1 && (!globalScene.commandCursorMemory || cursorResetEvent)) || globalScene.currentBattle.turn === 1 &&
commandUiHandler.getCursor() === Command.POKEMON (!globalScene.commandCursorMemory || cursorResetEvent)) ||
) { commandUiHandler.getCursor() === Command.POKEMON
commandUiHandler.setCursor(Command.FIGHT); ) {
} else { commandUiHandler.setCursor(Command.FIGHT);
commandUiHandler.setCursor(commandUiHandler.getCursor()); }
} }
/**
* Submethod of {@linkcode start} that validates field index logic for nonzero field indices.
* Must only be called if the field index is nonzero.
*/
private handleFieldIndexLogic() {
// If we somehow are attempting to check the right pokemon but there's only one pokemon out
// Switch back to the center pokemon. This can happen rarely in double battles with mid turn switching
// TODO: Prevent this from happening in the first place
if (globalScene.getPlayerField().filter(p => p.isActive()).length === 1) {
this.fieldIndex = FieldPosition.CENTER;
return;
} }
if (this.fieldIndex) { const allyCommand = globalScene.currentBattle.turnCommands[this.fieldIndex - 1];
// If we somehow are attempting to check the right pokemon but there's only one pokemon out if (allyCommand?.command === Command.BALL || allyCommand?.command === Command.RUN) {
// Switch back to the center pokemon. This can happen rarely in double battles with mid turn switching globalScene.currentBattle.turnCommands[this.fieldIndex] = {
if (globalScene.getPlayerField().filter(p => p.isActive()).length === 1) { command: allyCommand?.command,
this.fieldIndex = FieldPosition.CENTER; skip: true,
} else { };
const allyCommand = globalScene.currentBattle.turnCommands[this.fieldIndex - 1];
if (allyCommand?.command === Command.BALL || allyCommand?.command === Command.RUN) {
globalScene.currentBattle.turnCommands[this.fieldIndex] = {
command: allyCommand?.command,
skip: true,
};
}
}
} }
}
/** Submethod of {@linkcode start} that sets the turn command to skip if this pokemon is commanding its ally
* via {@linkcode Abilities.COMMANDER}.
*/
private checkCommander() {
// If the Pokemon has applied Commander's effects to its ally, skip this command // If the Pokemon has applied Commander's effects to its ally, skip this command
if ( if (
globalScene.currentBattle?.double && globalScene.currentBattle?.double &&
@ -89,64 +99,99 @@ export class CommandPhase extends FieldPhase {
skip: true, skip: true,
}; };
} }
}
// Checks if the Pokemon is under the effects of Encore. If so, Encore can end early if the encored move has no more PP. /**
const encoreTag = this.getPokemon().getTag(BattlerTagType.ENCORE) as EncoreTag; * Clear out all unusable moves in front of the currently acting pokemon's move queue.
if (encoreTag) { * TODO: Refactor move queue handling to ensure that this method is not necessary.
this.getPokemon().lapseTag(BattlerTagType.ENCORE); */
private clearUnusuableMoves() {
const playerPokemon = this.getPokemon();
const moveQueue = playerPokemon.getMoveQueue();
if (moveQueue.length === 0) {
return;
} }
let entriesToDelete = 0;
const moveset = playerPokemon.getMoveset();
for (const queuedMove of moveQueue) {
const movesetQueuedMove = moveset.find(m => m.moveId === queuedMove.move);
if (
queuedMove.move !== MoveId.NONE &&
!isVirtual(queuedMove.useMode) &&
!movesetQueuedMove?.isUsable(playerPokemon, isIgnorePP(queuedMove.useMode))
) {
entriesToDelete++;
} else {
break;
}
}
if (entriesToDelete) {
moveQueue.splice(0, entriesToDelete);
}
}
/**
* Attempt to execute the first usable move in this Pokemon's move queue
* @returns Whether a queued move was successfully set to be executed.
*/
private tryExecuteQueuedMove(): boolean {
this.clearUnusuableMoves();
const playerPokemon = globalScene.getPlayerField()[this.fieldIndex];
const moveQueue = playerPokemon.getMoveQueue();
if (moveQueue.length === 0) {
return false;
}
const queuedMove = moveQueue[0];
if (queuedMove.move === MoveId.NONE) {
this.handleCommand(Command.FIGHT, -1);
return true;
}
const moveIndex = playerPokemon.getMoveset().findIndex(m => m.moveId === queuedMove.move);
if (!isVirtual(queuedMove.useMode) && moveIndex === -1) {
globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex);
} else {
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.useMode, queuedMove);
}
return true;
}
start() {
super.start();
globalScene.updateGameInfo();
this.resetCursorIfNeeded();
if (this.fieldIndex) {
this.handleFieldIndexLogic();
}
this.checkCommander();
const playerPokemon = this.getPokemon();
// Note: It is OK to call this if the target is not under the effect of encore; it will simply do nothing.
playerPokemon.lapseTag(BattlerTagType.ENCORE);
if (globalScene.currentBattle.turnCommands[this.fieldIndex]?.skip) { if (globalScene.currentBattle.turnCommands[this.fieldIndex]?.skip) {
return this.end(); return this.end();
} }
const playerPokemon = globalScene.getPlayerField()[this.fieldIndex]; if (this.tryExecuteQueuedMove()) {
return;
const moveQueue = playerPokemon.getMoveQueue();
while (
moveQueue.length &&
moveQueue[0] &&
moveQueue[0].move &&
!isVirtual(moveQueue[0].useMode) &&
(!playerPokemon.getMoveset().find(m => m.moveId === moveQueue[0].move) ||
!playerPokemon
.getMoveset()
[playerPokemon.getMoveset().findIndex(m => m.moveId === moveQueue[0].move)].isUsable(
playerPokemon,
isIgnorePP(moveQueue[0].useMode),
))
) {
moveQueue.shift();
} }
// TODO: Refactor this. I did a few simple find/replace matches but this is just ABHORRENTLY structured if (
if (moveQueue.length > 0) { globalScene.currentBattle.isBattleMysteryEncounter() &&
const queuedMove = moveQueue[0]; globalScene.currentBattle.mysteryEncounter?.skipToFightInput
if (!queuedMove.move) { ) {
this.handleCommand(Command.FIGHT, -1, MoveUseMode.NORMAL); globalScene.ui.clearText();
} else { globalScene.ui.setMode(UiMode.FIGHT, this.fieldIndex);
const moveIndex = playerPokemon.getMoveset().findIndex(m => m.moveId === queuedMove.move);
if (
(moveIndex > -1 &&
playerPokemon.getMoveset()[moveIndex].isUsable(playerPokemon, isIgnorePP(queuedMove.useMode))) ||
isVirtual(queuedMove.useMode)
) {
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.useMode, queuedMove);
} else {
globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex);
}
}
} else { } else {
if ( globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex);
globalScene.currentBattle.isBattleMysteryEncounter() &&
globalScene.currentBattle.mysteryEncounter?.skipToFightInput
) {
globalScene.ui.clearText();
globalScene.ui.setMode(UiMode.FIGHT, this.fieldIndex);
} else {
globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex);
}
} }
} }
@ -200,7 +245,7 @@ export class CommandPhase extends FieldPhase {
useMode: MoveUseMode = MoveUseMode.NORMAL, useMode: MoveUseMode = MoveUseMode.NORMAL,
move?: TurnMove, move?: TurnMove,
): boolean { ): boolean {
const playerPokemon = globalScene.getPlayerField()[this.fieldIndex]; const playerPokemon = this.getPokemon();
const ignorePP = isIgnorePP(useMode); const ignorePP = isIgnorePP(useMode);
/** Whether or not to display an error message instead of attempting to initiate the command selection process */ /** Whether or not to display an error message instead of attempting to initiate the command selection process */
@ -381,7 +426,7 @@ export class CommandPhase extends FieldPhase {
* @returns Whether the pokemon is currently trapped * @returns Whether the pokemon is currently trapped
*/ */
private handleTrap(): boolean { private handleTrap(): boolean {
const playerPokemon = globalScene.getPlayerField()[this.fieldIndex]; const playerPokemon = this.getPokemon();
const trappedAbMessages: string[] = []; const trappedAbMessages: string[] = [];
const isSwitch = this.isSwitch; const isSwitch = this.isSwitch;
if (!playerPokemon.isTrapped(trappedAbMessages)) { if (!playerPokemon.isTrapped(trappedAbMessages)) {