diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 5eddc685ca2..b82fe51d208 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -1068,7 +1068,7 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { } override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return !isNullOrUndefined(attacker.getTag(BattlerTagType.DISABLED)) + return isNullOrUndefined(attacker.getTag(BattlerTagType.DISABLED)) && move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance); } diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index cee247b2bc6..7d2908c7673 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -892,8 +892,11 @@ export class DelayedAttackTag extends ArenaTag { const ret = super.lapse(arena); if (!ret) { + // TODO: When does Future Sight add to move history: on the turn it's used or actually hits? + // We currently only add the entry on the turn of the _attack_ and always make it a follow up + // (meaning it's never targetable by Spite) globalScene.unshiftPhase( - new MoveEffectPhase(this.sourceId!, [this.targetIndex], allMoves[this.sourceMove!], MoveUseType.REFLECTED), // Reflected ensures this doesn't check status, use PP or be copied + new MoveEffectPhase(this.sourceId!, [this.targetIndex], allMoves[this.sourceMove!], MoveUseType.FOLLOW_UP), ); // TODO: are those bangs correct? } diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 554b4e5022d..873f0b82bf6 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -385,9 +385,10 @@ export class GorillaTacticsTag extends MoveRestrictionBattlerTag { override canAdd(pokemon: Pokemon): boolean { // Choice items ignore struggle // TODO: Check if struggle also gets the 50% power boost - const lastSelectedMove = pokemon.getLastNonVirtualMove(false); + const lastSelectedMove = pokemon.getLastNonVirtualMove(); return ( - (isNullOrUndefined(lastSelectedMove) || lastSelectedMove.move === Moves.STRUGGLE) && + !isNullOrUndefined(lastSelectedMove) && + lastSelectedMove.move !== Moves.STRUGGLE && !pokemon.getTag(GorillaTacticsTag) ); } @@ -398,7 +399,7 @@ export class GorillaTacticsTag extends MoveRestrictionBattlerTag { */ override onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - this.moveId = pokemon.getLastNonVirtualMove(false)!.move; // `canAdd` returns false if no move + this.moveId = pokemon.getLastNonVirtualMove()!.move; // `canAdd` returns false if no move pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.ATK, false) * 1.5, false); } diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 157359a0f99..4d79189fda0 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -5423,13 +5423,11 @@ export class FrenzyAttr extends MoveEffectAttr { // Otherwise, tick down the existing tag. if (!user.getTag(BattlerTagType.FRENZY) && user.getMoveQueue().length === 0) { const turnCount = user.randSeedIntRange(1, 2); - for (let x = 0; x < turnCount; x++) { - user.pushMoveQueue({move: move.id, targets: [target.getBattlerIndex()], useType: MoveUseType.IGNORE_PP}) - } + new Array(turnCount).fill(null).map(() => user.getMoveQueue().push({ move: move.id, targets: [ target.getBattlerIndex() ], useType: MoveUseType.IGNORE_PP })); user.addTag(BattlerTagType.FRENZY, turnCount, move.id, user.id); } else { applyMoveAttrs(AddBattlerTagAttr, user, target, move, args); - user.lapseTag(BattlerTagType.FRENZY); + user.lapseTag(BattlerTagType.FRENZY); // if FRENZY is already in effect (moveQueue.length > 0), lapse the tag } return true; diff --git a/src/enums/move-use-type.ts b/src/enums/move-use-type.ts index 755168565f3..fab237f75a7 100644 --- a/src/enums/move-use-type.ts +++ b/src/enums/move-use-type.ts @@ -51,9 +51,6 @@ export enum MoveUseType { * Reflected moves ignore all the same cancellation checks as {@linkcode MoveUseType.INDIRECT} * and retain the same copy prevention as {@linkcode MoveUseType.FOLLOW_UP}, but additionally * **cannot be reflected by other reflecting effects**. - - * Also used for the "attack" portion of Future Sight and Doom Desire - * (in which case the reflection blockage is completely irrelevant.) */ REFLECTED } diff --git a/src/phases/pokemon-phase.ts b/src/phases/pokemon-phase.ts index cc09f811cd7..46ec523c85e 100644 --- a/src/phases/pokemon-phase.ts +++ b/src/phases/pokemon-phase.ts @@ -4,7 +4,11 @@ import type Pokemon from "#app/field/pokemon"; import { FieldPhase } from "./field-phase"; export abstract class PokemonPhase extends FieldPhase { - protected battlerIndex: BattlerIndex; + /** + * The battler index this phase refers to, or the pokemon ID if greater than 3. + * TODO: Make this either use IDs or `BattlerIndex`es, not a weird mix of both + */ + protected battlerIndex: BattlerIndex | number; public player: boolean; public fieldIndex: number; diff --git a/test/moves/after_you.test.ts b/test/moves/after_you.test.ts index cf630a71fde..ced4f9150b4 100644 --- a/test/moves/after_you.test.ts +++ b/test/moves/after_you.test.ts @@ -62,7 +62,10 @@ describe("Moves - After You", () => { expect(game.scene.getPlayerField()[1].getLastXMoves(1)[0].result).toBe(MoveResult.FAIL); }); - it("should maintain PP ignore status of rampaging moves", async () => { + // TODO: Enable once rampaging moves and move queue are fixed. + // Currently does literally nothing because `MoveUseType` is overridden from move queue + // within `MovePhase`, but should be enabled once that jank is removed + it.todo("should maintain PP ignore status of rampaging moves", async () => { game.override.moveset([]); await game.classicMode.startBattle([Species.ACCELGOR, Species.RATTATA]); diff --git a/test/moves/disable.test.ts b/test/moves/disable.test.ts index 985e3cd8ab0..fa19e5d66c9 100644 --- a/test/moves/disable.test.ts +++ b/test/moves/disable.test.ts @@ -132,7 +132,7 @@ describe("Moves - Disable", () => { game.override.enemyMoveset(moveId); await game.classicMode.startBattle([Species.PIKACHU]); - const playerMon = game.scene.getEnemyPokemon()!; + const playerMon = game.scene.getPlayerPokemon()!; playerMon.pushMoveHistory({ move: Moves.SPLASH, targets: [BattlerIndex.ENEMY], useType: MoveUseType.NORMAL }); game.scene.currentBattle.lastMove = Moves.SPLASH; @@ -141,8 +141,8 @@ describe("Moves - Disable", () => { await game.toNextTurn(); const enemyMon = game.scene.getEnemyPokemon()!; - expect.soft(enemyMon.isMoveRestricted(moveId), `calling move ${Moves[moveId]} was not disabled`).toBe(true); - expect.soft(enemyMon.getLastXMoves(-1)).toHaveLength(2); + expect(enemyMon.isMoveRestricted(moveId), `calling move ${Moves[moveId]} was not disabled`).toBe(true); + expect(enemyMon.getLastXMoves(-1)).toHaveLength(2); const calledMove = enemyMon.getLastXMoves()[0].move; expect( enemyMon.isMoveRestricted(calledMove), diff --git a/test/moves/quash.test.ts b/test/moves/quash.test.ts index c72248eaac7..8d87e9ef171 100644 --- a/test/moves/quash.test.ts +++ b/test/moves/quash.test.ts @@ -59,7 +59,10 @@ describe("Moves - Quash", () => { expect(game.scene.getPlayerField()[1].getLastXMoves(1)[0].result).toBe(MoveResult.FAIL); }); - it("should maintain PP ignore status of rampaging moves", async () => { + // TODO: Enable once rampaging moves and move queue are fixed. + // Currently does literally nothing because `MoveUseType` is overridden from move queue + // within `MovePhase`, but should be enabled once that jank is removed + it.todo("should maintain PP ignore status of rampaging moves", async () => { game.override.moveset([]); await game.classicMode.startBattle([Species.ACCELGOR, Species.RATTATA]);