This commit is contained in:
Acelynn Zhang 2025-09-22 21:44:46 -04:00 committed by GitHub
commit d7a03eda1d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 47 additions and 16 deletions

View File

@ -98,15 +98,16 @@ export class MovePhase extends PokemonPhase {
} }
/** /**
* Checks if the pokemon is active, if the move is usable, and that the move is targeting something. * Check if the current Move is usable and targeting at least 1 active pokemon.
* @param ignoreDisableTags `true` to not check if the move is disabled * @param ignoreDisableTags `true` to not check if the move is disabled
* @returns `true` if all the checks pass * @returns `true` if all the checks pass
*/ */
public canMove(ignoreDisableTags = false): boolean { public canMove(ignoreDisableTags = false): boolean {
const targets = this.getActiveTargetPokemon();
return ( return (
this.pokemon.isActive(true) this.pokemon.isActive(true)
&& this.move.isUsable(this.pokemon, isIgnorePP(this.useMode), ignoreDisableTags) && this.move.isUsable(this.pokemon, isIgnorePP(this.useMode), ignoreDisableTags)
&& this.targets.length > 0 && (targets.length > 0 || this.move.getMove().hasAttr("AddArenaTrapTagAttr"))
); );
} }
@ -128,15 +129,9 @@ export class MovePhase extends PokemonPhase {
`color:${MOVE_COLOR}`, `color:${MOVE_COLOR}`,
); );
// Check if move is unusable (e.g. running out of PP due to a mid-turn Spite // If the target isn't on field (such as due to leaving the field from Whirlwind/etc), do nothing.
// or the user no longer being on field), ending the phase early if not. if (!this.pokemon.isActive(true)) {
if (!this.canMove(true)) { super.end();
if (this.pokemon.isActive(true)) {
this.fail();
this.showMoveText();
this.showFailedText();
}
this.end();
return; return;
} }
@ -163,6 +158,7 @@ export class MovePhase extends PokemonPhase {
this.resolveCounterAttackTarget(); this.resolveCounterAttackTarget();
// Check status cancellation from sleep, freeze, etc.
this.resolvePreMoveStatusEffects(); this.resolvePreMoveStatusEffects();
this.lapsePreMoveAndMoveTags(); this.lapsePreMoveAndMoveTags();
@ -183,18 +179,29 @@ export class MovePhase extends PokemonPhase {
this.end(); this.end();
} }
/** Check for cancellation edge cases - no targets remaining, or {@linkcode MoveId.NONE} is in the queue */ /** Check for cancellation edge cases - no targets remaining, out of PP, or {@linkcode MoveId.NONE} is in the queue */
protected resolveFinalPreMoveCancellationChecks(): void { protected resolveFinalPreMoveCancellationChecks(): void {
const targets = this.getActiveTargetPokemon();
const moveQueue = this.pokemon.getMoveQueue(); const moveQueue = this.pokemon.getMoveQueue();
// Check if move is unusable (e.g. running out of PP due to a mid-turn Spite
// or the user no longer being on field)
if (!this.canMove(true)) {
if (this.pokemon.isActive(true)) {
this.fail();
this.showMoveText();
this.showFailedText();
}
return;
}
if ( if (
(targets.length === 0 && !this.move.getMove().hasAttr("AddArenaTrapTagAttr")) (this.targets.length === 0 && !this.move.getMove().hasAttr("AddArenaTrapTagAttr"))
|| (moveQueue.length > 0 && moveQueue[0].move === MoveId.NONE) || (moveQueue.length > 0 && moveQueue[0].move === MoveId.NONE)
) { ) {
this.showMoveText(); this.showMoveText();
this.showFailedText(); this.showFailedText();
this.cancel(); this.fail();
} }
} }

View File

@ -353,7 +353,7 @@ describe("Status Effects", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override game.override
.moveset([MoveId.SPLASH]) .moveset([MoveId.SPLASH, MoveId.DRAGON_CHEER])
.ability(AbilityId.BALL_FETCH) .ability(AbilityId.BALL_FETCH)
.battleStyle("single") .battleStyle("single")
.criticalHits(false) .criticalHits(false)
@ -390,6 +390,30 @@ describe("Status Effects", () => {
expect(player.status).toBeFalsy(); expect(player.status).toBeFalsy();
expect(player.getLastXMoves(1)[0].result).toBe(MoveResult.SUCCESS); expect(player.getLastXMoves(1)[0].result).toBe(MoveResult.SUCCESS);
}); });
it("Sleep turns should tick down when failing to use ally-targeting moves", async () => {
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
const player = game.field.getPlayerPokemon();
// Set sleep turns to 2 for brevity
player.status = new Status(StatusEffect.SLEEP, 0, 2);
game.move.changeMoveset(player, MoveId.DRAGON_CHEER);
game.move.select(MoveId.DRAGON_CHEER);
await game.toNextTurn();
expect(player.status.effect).toBe(StatusEffect.SLEEP);
expect(player.getMoveset()[0].ppUsed).toBe(0);
expect(player.getLastXMoves(1)[0].result).toBe(MoveResult.FAIL);
game.move.select(MoveId.DRAGON_CHEER);
await game.toNextTurn();
// Sleep was cured, move failed as normal and consumed PP
expect(player.status).toBeFalsy();
expect(player.getMoveset()[0].ppUsed).toBe(1);
expect(player.getLastXMoves(1)[0].result).toBe(MoveResult.FAIL);
});
}); });
describe("Behavior", () => { describe("Behavior", () => {