[Bug] Gorilla Tactics now activates on protect and miss (#5567)

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

* Apply small fixes from code review

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

---------

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
Tiago Rodrigues 2025-06-08 17:52:48 +01:00 committed by GitHub
parent 1c4edabd1d
commit 37767799cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 76 additions and 38 deletions

View File

@ -2534,48 +2534,38 @@ export class AllyStatMultiplierAbAttr extends AbAttr {
} }
/** /**
* Ability attribute for Gorilla Tactics * Takes effect whenever a move succesfully executes, such as gorilla tactics' move-locking.
* @extends PostAttackAbAttr * (More specifically, whenever a move is pushed to the move history)
* @extends AbAttr
*/ */
export class GorillaTacticsAbAttr extends PostAttackAbAttr { export class ExecutedMoveAbAttr extends AbAttr {
constructor() { canApplyExecutedMove(
super((_user, _target, _move) => true, false); _pokemon: Pokemon,
} _simulated: boolean,
override canApplyPostAttack(
pokemon: Pokemon,
passive: boolean,
simulated: boolean,
defender: Pokemon,
move: Move,
hitResult: HitResult | null,
args: any[],
): boolean { ): boolean {
return ( return true;
(super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args) && simulated) ||
!pokemon.getTag(BattlerTagType.GORILLA_TACTICS)
);
} }
/** applyExecutedMove(
* _pokemon: Pokemon,
* @param {Pokemon} pokemon the {@linkcode Pokemon} with this ability _simulated: boolean,
* @param _passive n/a ): void {}
* @param simulated whether the ability is being simulated }
* @param _defender n/a
* @param _move n/a /**
* @param _hitResult n/a * Ability attribute for Gorilla Tactics
* @param _args n/a * @extends ExecutedMoveAbAttr
*/ */
override applyPostAttack( export class GorillaTacticsAbAttr extends ExecutedMoveAbAttr {
pokemon: Pokemon, constructor(showAbility: boolean = false) {
_passive: boolean, super(showAbility);
simulated: boolean, }
_defender: Pokemon,
_move: Move, override canApplyExecutedMove(pokemon: Pokemon, simulated: boolean): boolean {
_hitResult: HitResult | null, return simulated || !pokemon.getTag(BattlerTagType.GORILLA_TACTICS);
_args: any[], }
): void {
override applyExecutedMove(pokemon: Pokemon, simulated: boolean): void {
if (!simulated) { if (!simulated) {
pokemon.addTag(BattlerTagType.GORILLA_TACTICS); pokemon.addTag(BattlerTagType.GORILLA_TACTICS);
} }
@ -7792,6 +7782,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( export function applyPostAttackAbAttrs(
attrType: Constructor<PostAttackAbAttr>, attrType: Constructor<PostAttackAbAttr>,
pokemon: Pokemon, pokemon: Pokemon,

View File

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

View File

@ -73,9 +73,38 @@ describe("Abilities - Gorilla Tactics", () => {
await game.toNextTurn(); await game.toNextTurn();
game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE);
await game.move.forceEnemyMove(MoveId.SPLASH); //prevent protect from being used by the enemy
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("MoveEndPhase"); await game.phaseInterceptor.to("MoveEndPhase");
expect(darmanitan.hp).toBeLessThan(darmanitan.getMaxHp()); expect(darmanitan.hp).toBeLessThan(darmanitan.getMaxHp());
}); });
it("should activate when the opponenet protects", async () => {
await game.classicMode.startBattle([SpeciesId.GALAR_DARMANITAN]);
const darmanitan = game.field.getPlayerPokemon();
game.move.select(MoveId.TACKLE);
await game.move.selectEnemyMove(MoveId.PROTECT);
await game.toEndOfTurn();
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.field.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.toEndOfTurn();
expect(darmanitan.isMoveRestricted(MoveId.SPLASH)).toBe(true);
expect(darmanitan.isMoveRestricted(MoveId.TACKLE)).toBe(false);
});
}); });