diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 37de1af2efa..a0c36649e84 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -29,6 +29,7 @@ export abstract class ArenaTag { public sourceId: integer; public side: ArenaTagSide; + constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId?: integer, side: ArenaTagSide = ArenaTagSide.BOTH) { this.tagType = tagType; this.turnCount = turnCount; @@ -41,10 +42,12 @@ export abstract class ArenaTag { return true; } - onAdd(arena: Arena): void { } + onAdd(arena: Arena, quiet: boolean = false): void { } - onRemove(arena: Arena): void { - arena.scene.queueMessage(`${this.getMoveName()}\'s effect wore off${this.side === ArenaTagSide.PLAYER ? "\non your side" : this.side === ArenaTagSide.ENEMY ? "\non the foe's side" : ""}.`); + onRemove(arena: Arena, quiet: boolean = false): void { + if (!quiet) { + arena.scene.queueMessage(`${this.getMoveName()}\'s effect wore off${this.side === ArenaTagSide.PLAYER ? "\non your side" : this.side === ArenaTagSide.ENEMY ? "\non the foe's side" : ""}.`); + } } onOverlap(arena: Arena): void { } @@ -65,11 +68,13 @@ export class MistTag extends ArenaTag { super(ArenaTagType.MIST, turnCount, Moves.MIST, sourceId, side); } - onAdd(arena: Arena): void { + onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); const source = arena.scene.getPokemonById(this.sourceId); - arena.scene.queueMessage(getPokemonMessage(source, "'s team became\nshrouded in mist!")); + if (!quiet) { + arena.scene.queueMessage(getPokemonMessage(source, "'s team became\nshrouded in mist!")); + } } apply(arena: Arena, args: any[]): boolean { @@ -113,8 +118,10 @@ class ReflectTag extends WeakenMoveScreenTag { return false; } - onAdd(arena: Arena): void { - arena.scene.queueMessage(`Reflect reduced the damage of physical moves${this.side === ArenaTagSide.PLAYER ? "\non your side" : this.side === ArenaTagSide.ENEMY ? "\non the foe's side" : ""}.`); + onAdd(arena: Arena, quiet: boolean = false): void { + if (!quiet) { + arena.scene.queueMessage(`Reflect reduced the damage of physical moves${this.side === ArenaTagSide.PLAYER ? "\non your side" : this.side === ArenaTagSide.ENEMY ? "\non the foe's side" : ""}.`); + } } } @@ -135,8 +142,10 @@ class LightScreenTag extends WeakenMoveScreenTag { return false; } - onAdd(arena: Arena): void { - arena.scene.queueMessage(`Light Screen reduced the damage of special moves${this.side === ArenaTagSide.PLAYER ? "\non your side" : this.side === ArenaTagSide.ENEMY ? "\non the foe's side" : ""}.`); + onAdd(arena: Arena, quiet: boolean = false): void { + if (!quiet) { + arena.scene.queueMessage(`Light Screen reduced the damage of special moves${this.side === ArenaTagSide.PLAYER ? "\non your side" : this.side === ArenaTagSide.ENEMY ? "\non the foe's side" : ""}.`); + } } } @@ -145,8 +154,10 @@ class AuroraVeilTag extends WeakenMoveScreenTag { super(ArenaTagType.AURORA_VEIL, turnCount, Moves.AURORA_VEIL, sourceId, side); } - onAdd(arena: Arena): void { - arena.scene.queueMessage(`Aurora Veil reduced the damage of moves${this.side === ArenaTagSide.PLAYER ? "\non your side" : this.side === ArenaTagSide.ENEMY ? "\non the foe's side" : ""}.`); + onAdd(arena: Arena, quiet: boolean = false): void { + if (!quiet) { + arena.scene.queueMessage(`Aurora Veil reduced the damage of moves${this.side === ArenaTagSide.PLAYER ? "\non your side" : this.side === ArenaTagSide.ENEMY ? "\non the foe's side" : ""}.`); + } } } @@ -386,11 +397,13 @@ class SpikesTag extends ArenaTrapTag { super(ArenaTagType.SPIKES, Moves.SPIKES, sourceId, side, 3); } - onAdd(arena: Arena): void { + onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); const source = arena.scene.getPokemonById(this.sourceId); - arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${source.getOpponentDescriptor()}'s feet!`); + if (!quiet) { + arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${source.getOpponentDescriptor()}'s feet!`); + } } activateTrap(pokemon: Pokemon): boolean { @@ -423,11 +436,13 @@ class ToxicSpikesTag extends ArenaTrapTag { this.neutralized = false; } - onAdd(arena: Arena): void { + onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); const source = arena.scene.getPokemonById(this.sourceId); - arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${source.getOpponentDescriptor()}'s feet!`); + if (!quiet) { + arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${source.getOpponentDescriptor()}'s feet!`); + } } onRemove(arena: Arena): void { @@ -493,11 +508,13 @@ class StealthRockTag extends ArenaTrapTag { super(ArenaTagType.STEALTH_ROCK, Moves.STEALTH_ROCK, sourceId, side, 1); } - onAdd(arena: Arena): void { + onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); const source = arena.scene.getPokemonById(this.sourceId); - arena.scene.queueMessage(`Pointed stones float in the air\naround ${source.getOpponentDescriptor()}!`); + if (!quiet) { + arena.scene.queueMessage(`Pointed stones float in the air\naround ${source.getOpponentDescriptor()}!`); + } } getDamageHpRatio(pokemon: Pokemon): number { @@ -562,13 +579,15 @@ class StickyWebTag extends ArenaTrapTag { super(ArenaTagType.STICKY_WEB, Moves.STICKY_WEB, sourceId, side, 1); } - onAdd(arena: Arena): void { + onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); // does not seem to be used anywhere // eslint-disable-next-line @typescript-eslint/no-unused-vars const source = arena.scene.getPokemonById(this.sourceId); - arena.scene.queueMessage(`A ${this.getMoveName()} has been laid out on the ground around the opposing team!`); + if (!quiet) { + arena.scene.queueMessage(`A ${this.getMoveName()} has been laid out on the ground around the opposing team!`); + } } activateTrap(pokemon: Pokemon): boolean { @@ -626,8 +645,10 @@ class TailwindTag extends ArenaTag { super(ArenaTagType.TAILWIND, turnCount, Moves.TAILWIND, sourceId, side); } - onAdd(arena: Arena): void { - arena.scene.queueMessage(`The Tailwind blew from behind${this.side === ArenaTagSide.PLAYER ? "\nyour" : this.side === ArenaTagSide.ENEMY ? "\nthe opposing" : ""} team!`); + onAdd(arena: Arena, quiet: boolean = false): void { + if (!quiet) { + arena.scene.queueMessage(`The Tailwind blew from behind${this.side === ArenaTagSide.PLAYER ? "\nyour" : this.side === ArenaTagSide.ENEMY ? "\nthe opposing" : ""} team!`); + } const source = arena.scene.getPokemonById(this.sourceId); const party = source.isPlayer() ? source.scene.getPlayerField() : source.scene.getEnemyField(); @@ -646,8 +667,10 @@ class TailwindTag extends ArenaTag { } } - onRemove(arena: Arena): void { - arena.scene.queueMessage(`${this.side === ArenaTagSide.PLAYER ? "Your" : this.side === ArenaTagSide.ENEMY ? "The opposing" : ""} team's Tailwind petered out!`); + onRemove(arena: Arena, quiet: boolean = false): void { + if (!quiet) { + arena.scene.queueMessage(`${this.side === ArenaTagSide.PLAYER ? "Your" : this.side === ArenaTagSide.ENEMY ? "The opposing" : ""} team's Tailwind petered out!`); + } } } diff --git a/src/data/move.ts b/src/data/move.ts index 6b189d0d3e3..9bdfc90903f 100755 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -2195,7 +2195,7 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr { (args[0] as Utils.BooleanHolder).value = true; user.scene.queueMessage(getPokemonMessage(user, ` ${this.chargeText.replace("{TARGET}", target.name)}`)); user.pushMoveHistory({ move: move.id, targets: [ target.getBattlerIndex() ], result: MoveResult.OTHER }); - user.scene.arena.addTag(this.tagType, 3, move.id, user.id, ArenaTagSide.BOTH, target.getBattlerIndex()); + user.scene.arena.addTag(this.tagType, 3, move.id, user.id, ArenaTagSide.BOTH, false, target.getBattlerIndex()); resolve(true); }); @@ -4224,6 +4224,48 @@ export class RemoveScreensAttr extends MoveEffectAttr { } } + +/*Swaps arena effects between the player and enemy side + * @extends MoveEffectAttr + * @see {@linkcode apply} +*/ +export class SwapArenaTagsAttr extends MoveEffectAttr { + public SwapTags: ArenaTagType[]; + + + constructor(SwapTags: ArenaTagType[]) { + super(true, MoveEffectTrigger.POST_APPLY); + this.SwapTags = SwapTags; + } + + apply(user:Pokemon, target:Pokemon, move:Move, args: any[]): boolean { + if (!super.apply(user, target, move, args)) { + return false; + } + + const tagPlayerTemp = user.scene.arena.findTagsOnSide((t => this.SwapTags.includes(t.tagType)), ArenaTagSide.PLAYER); + const tagEnemyTemp = user.scene.arena.findTagsOnSide((t => this.SwapTags.includes(t.tagType)), ArenaTagSide.ENEMY); + + + if (tagPlayerTemp) { + for (const swapTagsType of tagPlayerTemp) { + user.scene.arena.removeTagOnSide(swapTagsType.tagType, ArenaTagSide.PLAYER, true); + user.scene.arena.addTag(swapTagsType.tagType, swapTagsType.turnCount, swapTagsType.sourceMove, swapTagsType.sourceId, ArenaTagSide.ENEMY, true); + } + } + if (tagEnemyTemp) { + for (const swapTagsType of tagEnemyTemp) { + user.scene.arena.removeTagOnSide(swapTagsType.tagType, ArenaTagSide.ENEMY, true); + user.scene.arena.addTag(swapTagsType.tagType, swapTagsType.turnCount, swapTagsType.sourceMove, swapTagsType.sourceId, ArenaTagSide.PLAYER, true); + } + } + + + user.scene.queueMessage( `${user.name} swapped the battle effects affecting each side of the field!`); + return true; + } +} + /** * Attribute used for Revival Blessing. * @extends MoveEffectAttr @@ -7461,9 +7503,7 @@ export function initMoves() { .attr(FirstAttackDoublePowerAttr) .bitingMove(), new StatusMove(Moves.COURT_CHANGE, Type.NORMAL, 100, 10, -1, 0, 8) - .target(MoveTarget.BOTH_SIDES) - .unimplemented(), - /* Unused */ + .attr(SwapArenaTagsAttr, [ArenaTagType.AURORA_VEIL, ArenaTagType.LIGHT_SCREEN, ArenaTagType.MIST, ArenaTagType.REFLECT, ArenaTagType.SPIKES, ArenaTagType.STEALTH_ROCK, ArenaTagType.STICKY_WEB, ArenaTagType.TAILWIND, ArenaTagType.TOXIC_SPIKES]), new AttackMove(Moves.MAX_FLARE, Type.FIRE, MoveCategory.PHYSICAL, 10, -1, 10, -1, 0, 8) .target(MoveTarget.NEAR_ENEMY) .unimplemented() diff --git a/src/field/arena.ts b/src/field/arena.ts index 6999eb39785..d34cd2c9bea 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -545,7 +545,7 @@ export class Arena { this.applyTagsForSide(tagType, ArenaTagSide.BOTH, ...args); } - addTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide = ArenaTagSide.BOTH, targetIndex?: BattlerIndex): boolean { + addTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide = ArenaTagSide.BOTH, quiet: boolean = false, targetIndex?: BattlerIndex): boolean { const existingTag = this.getTagOnSide(tagType, side); if (existingTag) { existingTag.onOverlap(this); @@ -554,7 +554,7 @@ export class Arena { const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, targetIndex, side); this.tags.push(newTag); - newTag.onAdd(this); + newTag.onAdd(this, quiet); this.eventTarget.dispatchEvent(new TagAddedEvent(newTag.tagType, newTag.side, newTag.turnCount)); @@ -600,10 +600,10 @@ export class Arena { return !!tag; } - removeTagOnSide(tagType: ArenaTagType, side: ArenaTagSide): boolean { + removeTagOnSide(tagType: ArenaTagType, side: ArenaTagSide, quiet: boolean = false): boolean { const tag = this.getTagOnSide(tagType, side); if (tag) { - tag.onRemove(this); + tag.onRemove(this, quiet); this.tags.splice(this.tags.indexOf(tag), 1); this.eventTarget.dispatchEvent(new TagRemovedEvent(tag.tagType, tag.side, tag.turnCount));