Move cancellation logic out of lapse; use use TURN_END for lapse type

This commit is contained in:
Zach Day 2024-08-09 12:27:09 -04:00
parent 49723228b6
commit a628f77547
3 changed files with 26 additions and 44 deletions

View File

@ -1,5 +1,5 @@
import { CommonAnim, CommonBattleAnim, MoveChargeAnim, ChargeAnim } from "./battle-anims"; import { CommonAnim, CommonBattleAnim, MoveChargeAnim, ChargeAnim } from "./battle-anims";
import { CommonAnimPhase, MessagePhase, MoveEffectPhase, MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangeCallback, StatChangePhase, TurnEndPhase } from "../phases"; import { CommonAnimPhase, MoveEffectPhase, MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangeCallback, StatChangePhase } from "../phases";
import { getPokemonNameWithAffix } from "../messages"; import { getPokemonNameWithAffix } from "../messages";
import Pokemon, { MoveResult, HitResult } from "../field/pokemon"; import Pokemon, { MoveResult, HitResult } from "../field/pokemon";
import { Stat, getStatName } from "./pokemon-stat"; import { Stat, getStatName } from "./pokemon-stat";
@ -101,7 +101,7 @@ export abstract class DisablingBattlerTag extends BattlerTag {
public abstract moveIsDisabled(move: Moves): boolean; public abstract moveIsDisabled(move: Moves): boolean;
constructor(tagType: BattlerTagType, turnCount: integer, sourceMove?: Moves, sourceId?: integer) { constructor(tagType: BattlerTagType, turnCount: integer, sourceMove?: Moves, sourceId?: integer) {
super(tagType, BattlerTagLapseType.PRE_MOVE, turnCount, sourceMove, sourceId); super(tagType, BattlerTagLapseType.TURN_END, turnCount, sourceMove, sourceId);
} }
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
@ -110,43 +110,15 @@ export abstract class DisablingBattlerTag extends BattlerTag {
return false; return false;
} }
// If the subject selected their move at the start of the turn before it got disabled, cancel it
const movePhase = pokemon.scene.getCurrentPhase() as MovePhase;
if (movePhase && this.moveIsDisabled(movePhase.move.moveId)) {
movePhase.cancel();
const interruptedText = this.interruptedText(pokemon, movePhase.move.moveId);
if (interruptedText !== null) {
pokemon.scene.queueMessage(interruptedText);
}
}
return true; return true;
} }
/** Called when the disable expires due to duration. */
onRemove(pokemon: Pokemon): void {
const text = this.finishedText(pokemon);
if (text === null) {
return;
}
// In the games, disable effects always show their finish message at the end of a turn, if they have one. This tag
// lapses on PRE_MOVE, so we must manually insert a message phase after the next end of turn.
const turnEndPhaseIndex = pokemon.scene.phaseQueue.findIndex(p => p instanceof TurnEndPhase);
if (turnEndPhaseIndex >= 0) {
pokemon.scene.phaseQueue.splice(turnEndPhaseIndex, 0, new MessagePhase(pokemon.scene, text));
}
}
/** The text to display when the disable finishes. Can return {@link null}, in which case no message will be displayed. */
protected abstract finishedText(pokemon: Pokemon): string | null;
/** /**
* The text to display when a move is prevented as a result of the disable. Can return null, in which case * The text to display when a move's execution is prevented as a result of the disable.
* no message will be displayed. * Because disabling effects also prevent selection of the move, this situation can only arise if a
* pokemon first selects a move, then gets outsped by a pokemon using a move that disables the selected move.
*/ */
protected abstract interruptedText(pokemon: Pokemon, move: Moves): string | null; abstract interruptedText(pokemon: Pokemon, move: Moves): string;
} }
/** /**
@ -165,7 +137,7 @@ export class DisabledTag extends DisablingBattlerTag {
super(BattlerTagType.DISABLED, 4, Moves.DISABLE, sourceId); super(BattlerTagType.DISABLED, 4, Moves.DISABLE, sourceId);
} }
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
if (!super.lapse(pokemon, lapseType)) { if (!super.lapse(pokemon, lapseType)) {
return false; return false;
} }
@ -182,7 +154,7 @@ export class DisabledTag extends DisablingBattlerTag {
* Ensures that move history exists and has a valid move. If so, sets the {@link moveId} and shows a message. * Ensures that move history exists and has a valid move. If so, sets the {@link moveId} and shows a message.
* Otherwise, something has gone wrong, so the move ID will not get assigned and this tag will get removed next turn. * Otherwise, something has gone wrong, so the move ID will not get assigned and this tag will get removed next turn.
*/ */
public override onAdd(pokemon: Pokemon): void { override onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon); super.onAdd(pokemon);
const history = pokemon.getLastXMoves(); const history = pokemon.getLastXMoves();
@ -197,15 +169,17 @@ export class DisabledTag extends DisablingBattlerTag {
this.moveId = move.move; this.moveId = move.move;
pokemon.scene.queueMessage(i18next.t("battle:battlerTagsDisabledOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName() })); pokemon.scene.queueMessage(i18next.t("battle:battlerTagsDisabledOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[this.moveId].name }));
} }
protected override interruptedText(pokemon: Pokemon, move: Moves): string { override onRemove(pokemon: Pokemon): void {
return i18next.t("battle:disableInterruptedMove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName() }); super.onRemove(pokemon);
pokemon.scene.queueMessage(i18next.t("battle:battlerTagsDisabledLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[this.moveId].name }));
} }
protected override finishedText(pokemon: Pokemon): string { override interruptedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:battlerTagsDisabledLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName() }); return i18next.t("battle:disableInterruptedMove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
} }
} }

View File

@ -2453,12 +2453,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @see {@linkcode DisablingBattlerTag} * @see {@linkcode DisablingBattlerTag}
*/ */
isMoveDisabled(moveId: Moves): boolean { isMoveDisabled(moveId: Moves): boolean {
return this.getDisablingTag(moveId) !== null;
}
/**
* Gets the {@link DisablingBattlerTag} that is disabling the given move, or null if that move is not disabled.
*/
getDisablingTag(moveId: Moves): DisablingBattlerTag | null {
for (const tag of this.findTags(t => t instanceof DisablingBattlerTag)) { for (const tag of this.findTags(t => t instanceof DisablingBattlerTag)) {
if ((tag as DisablingBattlerTag).moveIsDisabled(moveId)) { if ((tag as DisablingBattlerTag).moveIsDisabled(moveId)) {
return true; return tag as DisablingBattlerTag;
} }
} }
return false; return null;
} }
getMoveHistory(): TurnMove[] { getMoveHistory(): TurnMove[] {

View File

@ -2682,7 +2682,8 @@ export class MovePhase extends BattlePhase {
if (!this.canMove()) { if (!this.canMove()) {
if (this.move.moveId && this.pokemon.isMoveDisabled(this.move.moveId)) { if (this.move.moveId && this.pokemon.isMoveDisabled(this.move.moveId)) {
this.scene.queueMessage(i18next.t("battle:disableInterruptedMove", { pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon), moveName: this.move.getName() })); const interruptingTag = this.pokemon.getDisablingTag(this.move.moveId)!;
this.scene.queueMessage(interruptingTag.interruptedText(this.pokemon, this.move.moveId));
this.pokemon.pushMoveHistory({ move: this.move.moveId, result: MoveResult.FAIL, virtual: false }); this.pokemon.pushMoveHistory({ move: this.move.moveId, result: MoveResult.FAIL, virtual: false });
this.fail(); this.fail();
} else if (this.pokemon.isActive(true) && this.move.ppUsed >= this.move.getMovePp()) { // if the move PP was reduced from Spite or otherwise, the move fails } else if (this.pokemon.isActive(true) && this.move.ppUsed >= this.move.getMovePp()) { // if the move PP was reduced from Spite or otherwise, the move fails