diff --git a/src/data/ability.ts b/src/data/ability.ts index 8f01e98e2be..a811da27b9f 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1531,23 +1531,50 @@ export class BattleStatMultiplierAbAttr extends AbAttr { } export class PostAttackAbAttr extends AbAttr { + private attackCondition: PokemonAttackCondition; + + /** The default attackCondition requires that the selected move is a damaging move */ + constructor(attackCondition: PokemonAttackCondition = (user, target, move) => (move.category !== MoveCategory.STATUS)) { + super(); + + this.attackCondition = attackCondition; + } + + /** + * Please override {@link applyPostAttackAfterMoveTypeCheck} instead of this method. By default, this method checks that the move used is a damaging attack before + * applying the effect of any inherited class. This can be changed by providing a different {@link attackCondition} to the constructor. See {@link ConfusionOnStatusEffectAbAttr} + * for an example of an effect that does not require a damaging move. + */ applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { + // When attackRequired is true, we require the move to be an attack move and to deal damage before checking secondary requirements. + // If attackRequired is false, we always defer to the secondary requirements. + if (this.attackCondition(pokemon, defender, move)) { + return this.applyPostAttackAfterMoveTypeCheck(pokemon, passive, defender, move, hitResult, args); + } else { + return false; + } + } + + /** + * This method is only called after {@link applyPostAttack} has already been applied. Use this for handling checks specific to the ability in question. + */ + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { return false; } } export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { - private condition: PokemonAttackCondition; + private stealCondition: PokemonAttackCondition; - constructor(condition?: PokemonAttackCondition) { + constructor(stealCondition?: PokemonAttackCondition) { super(); - this.condition = condition; + this.stealCondition = stealCondition; } - applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { return new Promise(resolve => { - if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, defender, move))) { + if (hitResult < HitResult.NO_EFFECT && (!this.stealCondition || this.stealCondition(pokemon, defender, move))) { const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferrable); if (heldItems.length) { const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; @@ -1583,7 +1610,7 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr { this.effects = effects; } - applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { /**Status inflicted by abilities post attacking are also considered additional effects.*/ if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; @@ -1614,7 +1641,7 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { this.effects = effects; } - applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { /**Battler tags inflicted by abilities post attacking are also considered additional effects.*/ if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; @@ -2414,7 +2441,8 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr { private effects: StatusEffect[]; constructor(...effects: StatusEffect[]) { - super(); + /** This effect does not require a damaging move */ + super((user, target, move) => true); this.effects = effects; } /** @@ -2427,7 +2455,7 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr { * @param args [0] {@linkcode StatusEffect} applied by move * @returns true if defender is confused */ - applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (this.effects.indexOf(args[0]) > -1 && !defender.isFainted()) { return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedInt(3,2), move.id, defender.id); } @@ -4273,7 +4301,7 @@ export const allAbilities = [ new Ability(Abilities.NONE, 3) ]; export function initAbilities() { allAbilities.push( new Ability(Abilities.STENCH, 3) - .attr(PostAttackApplyBattlerTagAbAttr, false, (user, target, move) => (move.category !== MoveCategory.STATUS && !move.hasAttr(FlinchAttr)) ? 10 : 0, BattlerTagType.FLINCHED), + .attr(PostAttackApplyBattlerTagAbAttr, false, (user, target, move) => !move.hasAttr(FlinchAttr) ? 10 : 0, BattlerTagType.FLINCHED), new Ability(Abilities.DRIZZLE, 3) .attr(PostSummonWeatherChangeAbAttr, WeatherType.RAIN) .attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.RAIN),