diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 2086d3bbb65..7bf88227073 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -908,7 +908,9 @@ export class DelayedAttackTag extends ArenaTag { } /** - * Tick down all existing delayed attacks, activating them if they et. */ + * Tick down all existing delayed attacks, activating them if their timers have elapsed. + * @returns `true` if at least 1 delayed attack has not been completed + */ override lapse(_arena: Arena): boolean { for (const attack of this.delayedAttacks) { const source = globalScene.getPokemonById(attack.sourceId); diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 1f2fdeb5630..95ec7830266 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -1400,15 +1400,13 @@ export class PreMoveMessageAttr extends MoveAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const message = typeof this.message === "string" - ? this.message + ? this.message as string : this.message(user, target, move); if (message) { globalScene.phaseManager.queueMessage(message, 500); return true; } - - globalScene.queueMessage(message, 500); - return true; + return false; } } @@ -3139,7 +3137,7 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr { // uncomment if any new delayed moves actually use target in the move text. {pokemonName: getPokemonNameWithAffix(user)/*, targetName: getPokemonNameWithAffix(target) */})) - user.pushMoveHistory({move: move.id, targets: [target.getBattlerIndex()], result: MoveResult.OTHER, useType: useMode, turn: globalScene.currentBattle.turn}) + user.pushMoveHistory({move: move.id, targets: [target.getBattlerIndex()], result: MoveResult.OTHER, useMode: useMode, turn: globalScene.currentBattle.turn}) // Add a Delayed Attack tag to the arena if it doesn't already exist and queue up an extra attack. // TODO: Remove unused params once signature is tweaked to make more sense (none of these get used) @@ -9207,10 +9205,13 @@ export function initMoves() { .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1) .ballBombMove(), new AttackMove(MoveId.FUTURE_SIGHT, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 120, 100, 10, -1, 0, 2) - .attr(DelayedAttackAttr, ArenaTagType.DELAYED_ATTACK, ChargeAnim.FUTURE_SIGHT_CHARGING, "moveTriggers:foresawAnAttack")) + .attr(DelayedAttackAttr, ChargeAnim.FUTURE_SIGHT_CHARGING, "moveTriggers:foresawAnAttack") .ignoresProtect() + /* + * Should not apply abilities or held items if user is off the field + * Triggered move phase occurs after Electrify tag is removed + */ .edgeCase(), - // should not apply abilities or held items if user is off the field new AttackMove(MoveId.ROCK_SMASH, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 15, 50, 0, 2) .attr(StatStageChangeAttr, [ Stat.DEF ], -1), new AttackMove(MoveId.WHIRLPOOL, PokemonType.WATER, MoveCategory.SPECIAL, 35, 85, 15, -1, 0, 2) @@ -9546,10 +9547,13 @@ export function initMoves() { .attr(ConfuseAttr) .pulseMove(), new AttackMove(MoveId.DOOM_DESIRE, PokemonType.STEEL, MoveCategory.SPECIAL, 140, 100, 5, -1, 0, 3) - .attr(DelayedAttackAttr, ArenaTagType.DELAYED_ATTACK, ChargeAnim.DOOM_DESIRE_CHARGING, "moveTriggers:choseDoomDesireAsDestiny") + .attr(DelayedAttackAttr, ChargeAnim.DOOM_DESIRE_CHARGING, "moveTriggers:choseDoomDesireAsDestiny") .ignoresProtect() + /* + * Should not apply abilities or held items if user is off the field + * Triggered move phase occurs after Electrify tag is removed + */ .edgeCase(), - // should not apply abilities or held items if user is off the field new AttackMove(MoveId.PSYCHO_BOOST, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 140, 90, 5, -1, 0, 3) .attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true), new SelfStatusMove(MoveId.ROOST, PokemonType.FLYING, -1, 5, -1, 0, 4) diff --git a/src/field/arena.ts b/src/field/arena.ts index 9374adac522..62cb421060c 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -694,6 +694,8 @@ export class Arena { * @param sourceMove - The {@linkcode MoveId} of the move creating the tag, or `undefined` if not from a move. * @param side - The {@linkcode ArenaTagSide}(s) to which the tag should apply; default `ArenaTagSide.BOTH`. * @param quiet - Whether to suppress messages produced by tag addition; default `false`. + * @returns `true` if the tag was successfully added without overlapping. + // TODO: Do we need the return value here? literally nothing uses it */ addTag( tagType: ArenaTagType, @@ -702,7 +704,7 @@ export class Arena { sourceId: number, side: ArenaTagSide = ArenaTagSide.BOTH, quiet = false, - ): void { + ): boolean { const existingTag = this.getTagOnSide(tagType, side); if (existingTag) { existingTag.onOverlap(this, globalScene.getPokemonById(sourceId)); @@ -731,7 +733,6 @@ export class Arena { return true; } - // TODO: This should take a map of `BattlerTagType`s to /** * Attempt to get a tag from the Arena via {@linkcode getTagOnSide} that applies to both sides * @param tagType The {@linkcode ArenaTagType} to retrieve diff --git a/test/moves/delayed_attack.test.ts b/test/moves/delayed_attack.test.ts index e1e9a403d7b..0a93af270ac 100644 --- a/test/moves/delayed_attack.test.ts +++ b/test/moves/delayed_attack.test.ts @@ -15,7 +15,6 @@ import i18next from "i18next"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { BattleType } from "#enums/battle-type"; -import { MoveTypePowerBoostAbAttr } from "#app/data/abilities/ability"; describe("Moves - Delayed Attacks", () => { let phaserGame: Phaser.Game; @@ -232,7 +231,8 @@ describe("Moves - Delayed Attacks", () => { ); }); - // TODO: The move phase unshifting happens after Electrify has been removed + // TODO: ArenaTags currently proc concurrently with battler tag removal in `TurnEndPhase`, + // meaning the queued `MoveEffectPhase` no longer has Electrify applied to it it.todo("should consider type changes at moment of execution & ignore Lightning Rod redirection", async () => { game.override.battleStyle("double"); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);