diff --git a/src/data/ability.ts b/src/data/ability.ts index a257525ef..c2617171a 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1702,7 +1702,7 @@ export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr { } export class PreStatChangeAbAttr extends AbAttr { - applyPreStatChange(pokemon: Pokemon, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreStatChange(pokemon: Pokemon, user: Pokemon, passive: boolean, stat: BattleStat, levels: integer, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } @@ -1716,7 +1716,7 @@ export class ProtectStatAbAttr extends PreStatChangeAbAttr { this.protectedStat = protectedStat; } - applyPreStatChange(pokemon: Pokemon, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreStatChange(pokemon: Pokemon, user: Pokemon, passive: boolean, stat: BattleStat, levels: integer, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.protectedStat === undefined || stat === this.protectedStat) { cancelled.value = true; return true; @@ -1730,6 +1730,28 @@ export class ProtectStatAbAttr extends PreStatChangeAbAttr { } } +export class ReflectStatAbAttr extends PreStatChangeAbAttr { + private reflectedStat: BattleStat; + + constructor(reflectedStat?: BattleStat) { + super(); + + this.reflectedStat = reflectedStat; + } + + applyPreStatChange(pokemon: Pokemon, sourcePokemon: Pokemon, passive: boolean, stat: BattleStat, levels: integer, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if ((this.reflectedStat === undefined || stat === this.reflectedStat) && pokemon !== sourcePokemon) { + cancelled.value = true; + + const statChangePhase = new StatChangePhase(pokemon.scene, sourcePokemon.getBattlerIndex(), false, [stat], levels); + pokemon.scene.unshiftPhase(statChangePhase); + return true; + } + + return false; + } +} + export class PreSetStatusAbAttr extends AbAttr { applyPreSetStatus(pokemon: Pokemon, passive: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; @@ -2813,8 +2835,8 @@ export function applyPreSwitchOutAbAttrs(attrType: { new(...args: any[]): PreSwi } export function applyPreStatChangeAbAttrs(attrType: { new(...args: any[]): PreStatChangeAbAttr }, - pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreStatChange(pokemon, passive, stat, cancelled, args), args); + pokemon: Pokemon, sourcePokemon: Pokemon, stat: BattleStat, levels: integer, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreStatChange(pokemon, sourcePokemon, passive, stat, levels, cancelled, args), args); } export function applyPostStatChangeAbAttrs(attrType: { new(...args: any[]): PostStatChangeAbAttr }, @@ -3604,8 +3626,8 @@ export function initAbilities() { new Ability(Abilities.PROPELLER_TAIL, 8) .attr(BlockRedirectAbAttr), new Ability(Abilities.MIRROR_ARMOR, 8) - .ignorable() - .unimplemented(), + .attr(ReflectStatAbAttr) + .ignorable(), new Ability(Abilities.GULP_MISSILE, 8) .attr(UnsuppressableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 63ed157b5..3cc7f8df6 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -439,7 +439,7 @@ class StickyWebTag extends ArenaTrapTag { if (!cancelled.value) { pokemon.scene.queueMessage(`The opposing ${pokemon.name} was caught in a sticky web!`); const statLevels = new Utils.NumberHolder(-1); - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, [BattleStat.SPD], statLevels.value)); + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, [BattleStat.SPD], statLevels.value, true, false, true, pokemon.scene.getPokemonById(this.sourceId))); } } diff --git a/src/data/move.ts b/src/data/move.ts index 46216eb75..bf8fcf4ee 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1733,7 +1733,7 @@ export class StatChangeAttr extends MoveEffectAttr { if (move.chance < 0 || move.chance === 100 || user.randSeedInt(100) < move.chance) { const levels = this.getLevels(user); - user.scene.unshiftPhase(new StatChangePhase(user.scene, (this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, levels, this.showMessage)); + user.scene.unshiftPhase(new StatChangePhase(user.scene, (this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, levels, this.showMessage, false, true, user)); return true; } @@ -1813,7 +1813,7 @@ export class AcupressureStatChangeAttr extends MoveEffectAttr { randStats = randStats.filter(s => target.summonData.battleStats[s] < 6); if (randStats.length > 0) { let boostStat = [randStats[Utils.randInt(randStats.length)]]; - user.scene.unshiftPhase(new StatChangePhase(user.scene, target.getBattlerIndex(), this.selfTarget, boostStat, 2)); + user.scene.unshiftPhase(new StatChangePhase(user.scene, target.getBattlerIndex(), this.selfTarget, boostStat, 2, true, false, true, user)); return true; } return false; diff --git a/src/phases.ts b/src/phases.ts index 88957ba16..628f92eca 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -30,7 +30,7 @@ import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, get import { TempBattleStat } from "./data/temp-battle-stat"; import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag"; import { ArenaTagType } from "./data/enums/arena-tag-type"; -import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr } from "./data/ability"; +import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, PreStatChangeAbAttr } from "./data/ability"; import { Unlockables, getUnlockableName } from "./system/unlockables"; import { getBiomeKey } from "./field/arena"; import { BattleType, BattlerIndex, TurnCommand } from "./battle"; @@ -2739,8 +2739,19 @@ export class StatChangePhase extends PokemonPhase { private showMessage: boolean; private ignoreAbilities: boolean; private canBeCopied: boolean; + private sourcePokemon: Pokemon; - constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], levels: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true) { + constructor( + scene: BattleScene, + battlerIndex: BattlerIndex, + selfTarget: boolean, + stats: BattleStat[], + levels: integer, + showMessage: boolean = true, + ignoreAbilities: boolean = false, + canBeCopied: boolean = true, + sourcePokemon?: Pokemon, + ) { super(scene, battlerIndex); this.selfTarget = selfTarget; @@ -2749,6 +2760,7 @@ export class StatChangePhase extends PokemonPhase { this.showMessage = showMessage; this.ignoreAbilities = ignoreAbilities; this.canBeCopied = canBeCopied; + this.sourcePokemon = sourcePokemon; } start() { @@ -2773,7 +2785,7 @@ export class StatChangePhase extends PokemonPhase { this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, cancelled); if (!cancelled.value && !this.selfTarget && this.levels < 0) - applyPreStatChangeAbAttrs(ProtectStatAbAttr, this.getPokemon(), stat, cancelled); + applyPreStatChangeAbAttrs(PreStatChangeAbAttr, this.getPokemon(), this.sourcePokemon, stat, this.levels, cancelled); return !cancelled.value; });