diff --git a/src/data/ability.ts b/src/data/ability.ts index 8a244b85b..38e2d2da4 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1646,6 +1646,29 @@ export class ProtectStatAbAttr extends PreStatChangeAbAttr { } } +export class AllyProtectStatAbAttr extends PreStatChangeAbAttr { + private protectedStat: BattleStat; + + constructor(protectedStat?: BattleStat) { + super(); + + this.protectedStat = protectedStat; + } + + applyPreStatChange(pokemon: Pokemon, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (this.protectedStat === undefined || stat === this.protectedStat) { + cancelled.value = true; + return true; + } + + return false; + } + + getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + return getPokemonMessage(pokemon, `'s ${abilityName}\nprevents lowering its ${this.protectedStat !== undefined ? getBattleStatName(this.protectedStat) : 'stats'}!`); + } +} + export class PreSetStatusAbAttr extends AbAttr { applyPreSetStatus(pokemon: Pokemon, passive: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; @@ -1675,6 +1698,29 @@ export class StatusEffectImmunityAbAttr extends PreSetStatusAbAttr { } } +export class AllyStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { + private immuneEffects: StatusEffect[]; + + constructor(...immuneEffects: StatusEffect[]) { + super(); + + this.immuneEffects = immuneEffects; + } + + applyPreSetStatus(pokemon: Pokemon, passive: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (!this.immuneEffects.length || this.immuneEffects.indexOf(effect) > -1) { + cancelled.value = true; + return true; + } + + return false; + } + + getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + return getPokemonMessage(pokemon, `'s ${abilityName}\nprevents ${this.immuneEffects.length ? getStatusEffectDescriptor(args[0] as StatusEffect) : 'status problems'}!`); + } +} + export class PreApplyBattlerTagAbAttr extends AbAttr { applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; @@ -1704,6 +1750,29 @@ export class BattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr { } } +export class AllyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr { + private immuneTagType: BattlerTagType; + + constructor(immuneTagType: BattlerTagType) { + super(); + + this.immuneTagType = immuneTagType; + } + + applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (tag.tagType === this.immuneTagType) { + cancelled.value = true; + return true; + } + + return false; + } + + getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + return getPokemonMessage(pokemon, `'s ${abilityName}\nprevents ${(args[0] as BattlerTag).getDescriptor()}!`); + } +} + export class BlockCritAbAttr extends AbAttr { apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.BooleanHolder).value = true; @@ -3232,9 +3301,16 @@ export function initAbilities() { new Ability(Abilities.AROMA_VEIL, 6) .ignorable() .unimplemented(), - new Ability(Abilities.FLOWER_VEIL, 6) + new Ability(Abilities.FLOWER_VEIL, 6) // TODO: Check against Spectral Thief once implemented, check against Toxic orb and flame orb once implemented. + // Also needs to not prevent the user from using Rest. + .conditionalAttr(p => (p.isOfType(Type.GRASS)), ProtectStatAbAttr) + .conditionalAttr(p => (p.isOfType(Type.GRASS)), AllyProtectStatAbAttr) + .conditionalAttr(p => (p.isOfType(Type.GRASS)), StatusEffectImmunityAbAttr) + .conditionalAttr(p => (p.isOfType(Type.GRASS)), AllyStatusEffectImmunityAbAttr) + .conditionalAttr(p => (p.isOfType(Type.GRASS)), BattlerTagImmunityAbAttr, BattlerTagType.DROWSY) + .conditionalAttr(p => (p.isOfType(Type.GRASS)), AllyBattlerTagImmunityAbAttr, BattlerTagType.DROWSY) .ignorable() - .unimplemented(), + .partial(), new Ability(Abilities.CHEEK_POUCH, 6) .unimplemented(), new Ability(Abilities.PROTEAN, 6) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 8942cfe4b..42ae8215c 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -9,7 +9,7 @@ import { StatusEffect } from "./status-effect"; import { BattlerIndex } from "../battle"; import { Moves } from "./enums/moves"; import { ArenaTagType } from "./enums/arena-tag-type"; -import { BlockNonDirectDamageAbAttr, ProtectStatAbAttr, applyAbAttrs } from "./ability"; +import { AllyProtectStatAbAttr, BlockNonDirectDamageAbAttr, ProtectStatAbAttr, applyAbAttrs } from "./ability"; import { BattleStat } from "./battle-stat"; export enum ArenaTagSide { @@ -436,6 +436,10 @@ class StickyWebTag extends ArenaTrapTag { if (pokemon.isGrounded()) { const cancelled = new Utils.BooleanHolder(false); applyAbAttrs(ProtectStatAbAttr, pokemon, cancelled); + if (!cancelled.value) { + applyAbAttrs(AllyProtectStatAbAttr, pokemon.getAlly(), cancelled); + } + if (!cancelled.value) { pokemon.scene.queueMessage(`The opposing ${pokemon.name} was caught in a sticky web!`); const statLevels = new Utils.NumberHolder(-1); diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index e11a05032..602354f75 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -27,7 +27,7 @@ import { TempBattleStat } from '../data/temp-battle-stat'; import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag'; import { ArenaTagType } from "../data/enums/arena-tag-type"; import { Biome } from "../data/enums/biome"; -import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr } from '../data/ability'; +import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, AllyStatusEffectImmunityAbAttr } from '../data/ability'; import { Abilities } from "#app/data/enums/abilities"; import PokemonData from '../system/pokemon-data'; import Battle, { BattlerIndex } from '../battle'; @@ -2021,6 +2021,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const cancelled = new Utils.BooleanHolder(false); applyPreSetStatusAbAttrs(StatusEffectImmunityAbAttr, this, effect, cancelled, quiet); + if (!cancelled.value) + applyPreSetStatusAbAttrs(AllyStatusEffectImmunityAbAttr, this.getAlly(), effect, cancelled, quiet); if (cancelled.value) return false; diff --git a/src/phases.ts b/src/phases.ts index d8ce55e95..e34670e7b 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, AllyProtectStatAbAttr } from "./data/ability"; import { Unlockables, getUnlockableName } from "./system/unlockables"; import { getBiomeKey } from "./field/arena"; import { BattleType, BattlerIndex, TurnCommand } from "./battle"; @@ -2740,6 +2740,8 @@ export class StatChangePhase extends PokemonPhase { if (!cancelled.value && !this.selfTarget && this.levels < 0) applyPreStatChangeAbAttrs(ProtectStatAbAttr, this.getPokemon(), stat, cancelled); + if (!cancelled.value && !this.selfTarget) + applyPreStatChangeAbAttrs(AllyProtectStatAbAttr, this.getPokemon().getAlly(), stat, cancelled); return !cancelled.value; });