[Bug] Fix #5112: Gorilla Tactics only registers succesful moves as move usage

This commit is contained in:
Tiago Rodrigues 2025-06-05 14:37:16 +01:00
parent fb6d6f5b69
commit b7f893fbcc
3 changed files with 74 additions and 31 deletions

View File

@ -1670,44 +1670,39 @@ export class AllyStatMultiplierAbAttr extends AbAttr {
}
/**
* Ability attribute for Gorilla Tactics
* @extends PostAttackAbAttr
* Takes effect whenever a move succesfully executes, such as gorilla tactics' move-locking.
* (More specifically, whenever a move is pushed to the move history)
* @extends AbAttr
*/
export class GorillaTacticsAbAttr extends PostAttackAbAttr {
constructor() {
super((user, target, move) => true, false);
export class ExecutedMoveAbAttr extends AbAttr {
canApplyExecutedMove(
pokemon: Pokemon,
simulated: boolean,
): boolean {
return true;
}
override canApplyPostAttack(
applyExecutedMove(
pokemon: Pokemon,
passive: boolean,
simulated: boolean,
defender: Pokemon,
move: Move,
hitResult: HitResult | null,
args: any[]): boolean {
return super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args)
&& simulated || !pokemon.getTag(BattlerTagType.GORILLA_TACTICS);
): void {}
}
/**
*
* @param {Pokemon} pokemon the {@linkcode Pokemon} with this ability
* @param passive n/a
* @param simulated whether the ability is being simulated
* @param defender n/a
* @param move n/a
* @param hitResult n/a
* @param args n/a
* Ability attribute for Gorilla Tactics
* @extends ExecutedMoveAbAttr
*/
override applyPostAttack(
pokemon: Pokemon,
passive: boolean,
simulated: boolean,
defender: Pokemon,
move: Move,
hitResult: HitResult | null,
args: any[]): void {
export class GorillaTacticsAbAttr extends ExecutedMoveAbAttr {
constructor(showAbility: boolean = false) {
super(showAbility);
}
override canApplyExecutedMove(pokemon: Pokemon, simulated: boolean): boolean {
return super.canApplyExecutedMove(pokemon, simulated) &&
simulated || !pokemon.getTag(BattlerTagType.GORILLA_TACTICS);
}
override applyExecutedMove(pokemon: Pokemon, simulated: boolean): void {
if (!simulated) {
pokemon.addTag(BattlerTagType.GORILLA_TACTICS);
}
@ -6022,6 +6017,22 @@ export function applyPreAttackAbAttrs(
);
}
export function applyExecutedMoveAbAttrs(
attrType: Constructor<ExecutedMoveAbAttr>,
pokemon: Pokemon,
simulated: boolean = false,
...args: any[]
): void {
applyAbAttrsInternal<ExecutedMoveAbAttr>(
attrType,
pokemon,
attr => attr.applyExecutedMove(pokemon, simulated),
attr => attr.canApplyExecutedMove(pokemon, simulated),
args,
simulated,
);
}
export function applyPostAttackAbAttrs(
attrType: Constructor<PostAttackAbAttr>,
pokemon: Pokemon,

View File

@ -3,10 +3,12 @@ import { globalScene } from "#app/global-scene";
import {
AddSecondStrikeAbAttr,
AlwaysHitAbAttr,
applyExecutedMoveAbAttrs,
applyPostAttackAbAttrs,
applyPostDamageAbAttrs,
applyPostDefendAbAttrs,
applyPreAttackAbAttrs,
ExecutedMoveAbAttr,
IgnoreMoveEffectsAbAttr,
MaxMultiHitAbAttr,
PostAttackAbAttr,
@ -370,6 +372,7 @@ export class MoveEffectPhase extends PokemonPhase {
// Add to the move history entry
if (this.firstHit) {
user.pushMoveHistory(this.moveHistoryEntry);
applyExecutedMoveAbAttrs(ExecutedMoveAbAttr, user);
}
try {

View File

@ -73,9 +73,38 @@ describe("Abilities - Gorilla Tactics", () => {
await game.toNextTurn();
game.move.select(MoveId.TACKLE);
await game.selectTarget(MoveId.SPLASH); //prevent protect from being used by the enemy
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("MoveEndPhase");
expect(darmanitan.hp).toBeLessThan(darmanitan.getMaxHp());
});
it("should activate when the opponenet protects", async () => {
await game.classicMode.startBattle([SpeciesId.GALAR_DARMANITAN]);
const darmanitan = game.scene.getPlayerPokemon()!;
game.move.select(MoveId.TACKLE);
await game.move.selectEnemyMove(MoveId.PROTECT);
await game.phaseInterceptor.to("TurnEndPhase");
expect(darmanitan.isMoveRestricted(MoveId.SPLASH)).toBe(true);
expect(darmanitan.isMoveRestricted(MoveId.TACKLE)).toBe(false);
});
it("should activate when a move is succesfully executed but misses", async () => {
await game.classicMode.startBattle([SpeciesId.GALAR_DARMANITAN]);
const darmanitan = game.scene.getPlayerPokemon()!;
game.move.select(MoveId.TACKLE);
await game.move.selectEnemyMove(MoveId.SPLASH);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.move.forceMiss();
await game.phaseInterceptor.to("TurnEndPhase");
expect(darmanitan.isMoveRestricted(MoveId.SPLASH)).toBe(true);
expect(darmanitan.isMoveRestricted(MoveId.TACKLE)).toBe(false);
});
});