diff --git a/src/data/ability.ts b/src/data/ability.ts index c4b4a18a62b..af606053741 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -2710,7 +2710,6 @@ export class PreStatStageChangeAbAttr extends AbAttr { */ // TODO: CODE INTERACTION WITH MAGIC BOUNCE AS WELL // TODO: CODE INTERACTION WITH STICKY WEB -// TODO: PREVENT REFLECTION FROM OPPONENT MIRROR ARMOR FOR INFINITE LOOP export class ReflectStatStageChangeAbAttr extends PreStatStageChangeAbAttr { /** {@linkcode BattleStat} to reflect */ private reflectedStat? : BattleStat; @@ -2733,7 +2732,7 @@ export class ReflectStatStageChangeAbAttr extends PreStatStageChangeAbAttr { const stages = _args[1]; this.reflectedStat = stat; if (!_simulated) { - attacker.scene.unshiftPhase(new StatStageChangePhase(attacker.scene, attacker.getBattlerIndex(), false, [ stat ], stages)); + attacker.scene.unshiftPhase(new StatStageChangePhase(attacker.scene, attacker.getBattlerIndex(), false, [ stat ], stages, true, false, true, null, true)); } cancelled.value = true; return true; diff --git a/src/data/move.ts b/src/data/move.ts index c5b14304fb2..c86a909b09a 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -8087,8 +8087,8 @@ export function initMoves() { new StatusMove(Moves.WILL_O_WISP, Type.FIRE, 85, 15, -1, 0, 3) .attr(StatusEffectAttr, StatusEffect.BURN), new StatusMove(Moves.MEMENTO, Type.DARK, 100, 10, -1, 0, 3) - .attr(SacrificialAttrOnHit) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -2), + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -2) + .attr(SacrificialAttrOnHit), new AttackMove(Moves.FACADE, Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, -1, 0, 3) .attr(MovePowerMultiplierAttr, (user, target, move) => user.status && (user.status.effect === StatusEffect.BURN || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.PARALYSIS) ? 2 : 1) diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts index fd71724be48..aa3dddefe6f 100644 --- a/src/phases/stat-stage-change-phase.ts +++ b/src/phases/stat-stage-change-phase.ts @@ -22,9 +22,10 @@ export class StatStageChangePhase extends PokemonPhase { private ignoreAbilities: boolean; private canBeCopied: boolean; private onChange: StatStageChangeCallback | null; + private comingFromMirrorArmorUser: boolean; - constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], stages: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true, onChange: StatStageChangeCallback | null = null) { + constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], stages: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true, onChange: StatStageChangeCallback | null = null, comingFromMirrorArmorUser: boolean = false) { super(scene, battlerIndex); this.selfTarget = selfTarget; @@ -34,6 +35,7 @@ export class StatStageChangePhase extends PokemonPhase { this.ignoreAbilities = ignoreAbilities; this.canBeCopied = canBeCopied; this.onChange = onChange; + this.comingFromMirrorArmorUser = comingFromMirrorArmorUser; } start() { @@ -42,7 +44,7 @@ export class StatStageChangePhase extends PokemonPhase { if (this.stats.length > 1) { for (let i = 0; i < this.stats.length; i++) { const stat = [ this.stats[i] ]; - this.scene.unshiftPhase(new StatStageChangePhase(this.scene, this.battlerIndex, this.selfTarget, stat, this.stages, this.showMessage, this.ignoreAbilities, this.canBeCopied, this.onChange)); + this.scene.unshiftPhase(new StatStageChangePhase(this.scene, this.battlerIndex, this.selfTarget, stat, this.stages, this.showMessage, this.ignoreAbilities, this.canBeCopied, this.onChange, this.comingFromMirrorArmorUser)); } return this.end(); } @@ -62,6 +64,7 @@ export class StatStageChangePhase extends PokemonPhase { if (this.scene.currentBattle.double && this.scene.getEnemyField().length === 2) { opponentPokemon = this.scene.getEnemyField()[this.scene.currentBattle.lastEnemyInvolved]; } else { + console.log("ENEMY POKEMANS", this.scene.getEnemyField()); opponentPokemon = this.scene.getEnemyPokemon(); } } else { @@ -95,13 +98,13 @@ export class StatStageChangePhase extends PokemonPhase { if (!cancelled.value && !this.selfTarget && stages.value < 0) { applyPreStatStageChangeAbAttrs(ProtectStatAbAttr, pokemon, stat, cancelled, simulate); - // TODO: CODE INTERACTION WITH MAGIC BOUNCE AS WELL // TODO: CODE INTERACTION WITH STICKY WEB - // TODO: PREVENT REFLECTION FROM OPPONENT MIRROR ARMOR FOR INFINITE LOOP // TODO: FIX INTERACTION WITH MEMENTO, SHOULD LOWER OPPONENT STATS THEN DIE /** Potential stat reflection due to Mirror Armor, does not apply to Octolock end of turn effect */ - if (opponentPokemon !== undefined && !pokemon.findTag(t => t instanceof OctolockTag)) { + console.log("I AM HEREE", opponentPokemon); + + if (opponentPokemon !== undefined && !pokemon.findTag(t => t instanceof OctolockTag) && !this.comingFromMirrorArmorUser) { applyPreStatStageChangeAbAttrs(ReflectStatStageChangeAbAttr, pokemon, stat, cancelled, simulate, opponentPokemon, this.stages); } } diff --git a/src/test/abilities/mirror_armor.test.ts b/src/test/abilities/mirror_armor.test.ts index 30fd9c3902d..bb715d80e8e 100644 --- a/src/test/abilities/mirror_armor.test.ts +++ b/src/test/abilities/mirror_armor.test.ts @@ -1,4 +1,5 @@ //test memnto as well and double battles and multiple stats and octolock +// test mirror armor infinite loop as well import { Stat } from "#enums/stat"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; @@ -49,7 +50,7 @@ describe("Ability - Mirror Armor", () => { expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); expect(userPokemon.getStatStage(Stat.ATK)).toBe(0); - }); + }, 20000); it("Enemy side + single battle Intimidate - player loses stats", async () => { game.override.enemyAbility(Abilities.MIRROR_ARMOR); @@ -66,7 +67,7 @@ describe("Ability - Mirror Armor", () => { expect(userPokemon.getStatStage(Stat.ATK)).toBe(-1); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); - }); + }, 20000); it("Player side + double battle Intimidate - opponents each lose -2 atk", async () => { game.override.battleType("double"); @@ -88,7 +89,7 @@ describe("Ability - Mirror Armor", () => { expect(enemy2.getStatStage(Stat.ATK)).toBe(-2); expect(player1.getStatStage(Stat.ATK)).toBe(0); expect(player2.getStatStage(Stat.ATK)).toBe(0); - }); + }, 20000); it("Enemy side + double battle Intimidate - players each lose -2 atk", async () => { game.override.battleType("double"); @@ -110,7 +111,7 @@ describe("Ability - Mirror Armor", () => { expect(enemy2.getStatStage(Stat.ATK)).toBe(0); expect(player1.getStatStage(Stat.ATK)).toBe(-2); expect(player2.getStatStage(Stat.ATK)).toBe(-2); - }); + }, 20000); it("Player side + single battle Intimidate + Tickle - opponent loses stats", async () => { game.override.ability(Abilities.MIRROR_ARMOR); @@ -129,7 +130,7 @@ describe("Ability - Mirror Armor", () => { expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-2); expect(userPokemon.getStatStage(Stat.ATK)).toBe(0); expect(userPokemon.getStatStage(Stat.DEF)).toBe(0); - }); + }, 20000); it("Player side + double battle Intimidate + Tickle - opponents each lose -3 atk, -1 def", async () => { game.override.battleType("double"); @@ -155,7 +156,7 @@ describe("Ability - Mirror Armor", () => { expect(enemy2.getStatStage(Stat.ATK)).toBe(-3); expect(enemy2.getStatStage(Stat.DEF)).toBe(-1); - }); + }, 20000); it("Enemy side + single battle Intimidate + Tickle - player loses stats", async () => { game.override.enemyAbility(Abilities.MIRROR_ARMOR); @@ -174,7 +175,7 @@ describe("Ability - Mirror Armor", () => { expect(userPokemon.getStatStage(Stat.ATK)).toBe(-2); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0); - }); + }, 20000); it("Player side + single battle Intimidate + oppoenent has white smoke - no one loses stats", async () => { game.override.enemyAbility(Abilities.WHITE_SMOKE); @@ -193,7 +194,7 @@ describe("Ability - Mirror Armor", () => { expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); expect(userPokemon.getStatStage(Stat.ATK)).toBe(0); expect(userPokemon.getStatStage(Stat.DEF)).toBe(0); - }); + }, 20000); it("Enemy side + single battle Intimidate + player has white smoke - no one loses stats", async () => { game.override.ability(Abilities.WHITE_SMOKE); @@ -212,7 +213,7 @@ describe("Ability - Mirror Armor", () => { expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); expect(userPokemon.getStatStage(Stat.ATK)).toBe(0); expect(userPokemon.getStatStage(Stat.DEF)).toBe(0); - }); + }, 20000); it("Player side + single battle + opponent uses octolock - does not interact with mirror armor, player loses stats", async () => { game.override.ability(Abilities.MIRROR_ARMOR); @@ -230,7 +231,7 @@ describe("Ability - Mirror Armor", () => { expect(enemyPokemon.getStatStage(Stat.SPDEF)).toBe(0); expect(userPokemon.getStatStage(Stat.DEF)).toBe(-1); expect(userPokemon.getStatStage(Stat.SPDEF)).toBe(-1); - }); + }, 20000); it("Enemy side + single battle + player uses octolock - does not interact with mirror armor, opponent loses stats", async () => { game.override.enemyAbility(Abilities.MIRROR_ARMOR); @@ -248,7 +249,7 @@ describe("Ability - Mirror Armor", () => { expect(userPokemon.getStatStage(Stat.SPDEF)).toBe(0); expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(-1); expect(enemyPokemon.getStatStage(Stat.SPDEF)).toBe(-1); - }); + }, 20000); // it("traps the target pokemon", async () => { // await game.classicMode.startBattle([ Species.GRAPPLOCT ]);