diff --git a/src/data/ability.ts b/src/data/ability.ts index 25ffa797140..818c996d103 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -946,7 +946,7 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status + if (move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && !attacker.status && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !move.hitsSubstitute(attacker, pokemon)) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; if (simulated) { @@ -987,7 +987,7 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance && !move.hitsSubstitute(attacker, pokemon)) { + if (move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && pokemon.randSeedInt(100) < this.chance && !move.hitsSubstitute(attacker, pokemon)) { if (simulated) { return attacker.canAddTag(this.tagType); } else { @@ -1037,7 +1037,7 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (!simulated && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) + if (!simulated && move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && !move.hitsSubstitute(attacker, pokemon)) { attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); @@ -1071,8 +1071,8 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !move.hitsSubstitute(attacker, pokemon)) { - if (attacker.getTag(BattlerTagType.PERISH_SONG)) { + if (move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && !move.hitsSubstitute(attacker, pokemon)) { + if (pokemon.getTag(BattlerTagType.PERISH_SONG) || attacker.getTag(BattlerTagType.PERISH_SONG)) { return false; } else { if (!simulated) { @@ -1122,7 +1122,7 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) + if (move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && !attacker.getAbility().hasAttr(UnswappableAbilityAbAttr) && !move.hitsSubstitute(attacker, pokemon)) { if (!simulated) { const tempAbility = attacker.getAbility(); @@ -1149,7 +1149,7 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) + if (move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr) && !move.hitsSubstitute(attacker, pokemon)) { if (!simulated) { attacker.setTempAbility(allAbilities[this.ability]); @@ -1182,7 +1182,7 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { if (attacker.getTag(BattlerTagType.DISABLED) === null && !move.hitsSubstitute(attacker, pokemon)) { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) { + if (move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) { if (simulated) { return true; } @@ -1831,7 +1831,7 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr { } /**Status inflicted by abilities post attacking are also considered additional effects.*/ - if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && !simulated && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) { + if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && !simulated && pokemon !== attacker && (!this.contactRequired || move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; return attacker.trySetStatus(effect, true, pokemon); } @@ -1862,7 +1862,7 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: 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) { + if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && (!this.contactRequired || move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: 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)]; return simulated || attacker.addTag(effect); } @@ -4528,7 +4528,7 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { } applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { - if (move !== undefined && attacker !== undefined && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) { //If the mon didn't die to indirect damage + if (move !== undefined && attacker !== undefined && move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })) { //If the mon didn't die to indirect damage const cancelled = new Utils.BooleanHolder(false); globalScene.getField(true).map(p => applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled, simulated)); if (cancelled.value || attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index f2157ab65b7..47f0b076666 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -621,12 +621,30 @@ export default class Move implements Localizable { /** * Checks if the move flag applies to the pokemon(s) using/receiving the move + * + * This method will take the `user`'s ability into account when reporting flags, e.g. + * calling this method for {@linkcode MoveFlags.MAKES_CONTACT | MAKES_CONTACT} + * will return `false` if the user has a {@linkcode Abilities.LONG_REACH} that is not being suppressed. + * + * **Note:** This method only checks if the move should have effectively have the flag applied to its use. + * It does *not* check whether the flag will trigger related effects. + * For example using this method to check {@linkcode MoveFlags.WIND_MOVE} + * will not consider {@linkcode Abilities.WIND_RIDER | Wind Rider }. + * + * To simply check whether the move has a flag, use {@linkcode hasFlag}. * @param flag {@linkcode MoveFlags} MoveFlag to check on user and/or target * @param user {@linkcode Pokemon} the Pokemon using the move * @param target {@linkcode Pokemon} the Pokemon receiving the move + * @param isFollowUp (defaults to `false) `true` if the move was used as a follow up * @returns boolean - */ - checkFlag(flag: MoveFlags, user: Pokemon, target: Pokemon | null): boolean { + * @see {@linkcode hasFlag} + */ + doesFlagEffectApply({ flag, user, target = null, isFollowUp = false }: { + flag: MoveFlags; + user: Pokemon; + target?: Pokemon | null; + isFollowUp?: boolean; + }): boolean { // special cases below, eg: if the move flag is MAKES_CONTACT, and the user pokemon has an ability that ignores contact (like "Long Reach"), then overrides and move does not make contact switch (flag) { case MoveFlags.MAKES_CONTACT: @@ -641,11 +659,13 @@ export default class Move implements Localizable { if (abilityEffectsIgnored.value) { return true; } + // Sunsteel strike, Moongeist beam, and photon geyser will not ignore abilities if invoked + // by another move, such as via metronome. } - break; + return this.hasFlag(MoveFlags.IGNORE_ABILITIES) && !isFollowUp; case MoveFlags.IGNORE_PROTECT: if (user.hasAbilityWithAttr(IgnoreProtectOnContactAbAttr) - && this.checkFlag(MoveFlags.MAKES_CONTACT, user, null)) { + && this.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user, target: null })) { return true; } break; @@ -1212,7 +1232,7 @@ export class MoveEffectAttr extends MoveAttr { canApply(user: Pokemon, target: Pokemon, move: Move, args?: any[]) { return !! (this.selfTarget ? user.hp && !user.getTag(BattlerTagType.FRENZY) : target.hp) && (this.selfTarget || !target.getTag(BattlerTagType.PROTECTED) || - move.checkFlag(MoveFlags.IGNORE_PROTECT, user, target)); + move.doesFlagEffectApply({ flag: MoveFlags.IGNORE_PROTECT, user, target })); } /** Applies move effects so long as they are able based on {@linkcode canApply} */ diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index 4152fc243f0..186aa08ca85 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -288,7 +288,8 @@ export class MoveEffectPhase extends PokemonPhase { /** Is the target protected by Protect, etc. or a relevant conditional protection effect? */ const isProtected = ![MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES].includes(this.move.getMove().moveTarget) && - (bypassIgnoreProtect.value || !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target)) && + (bypassIgnoreProtect.value || + !this.move.getMove().doesFlagEffectApply({ flag: MoveFlags.IGNORE_PROTECT, user, target })) && (hasConditionalProtectApplied.value || (!target.findTags(t => t instanceof DamageProtectedTag).length && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))) || @@ -306,7 +307,7 @@ export class MoveEffectPhase extends PokemonPhase { /** Is the target's magic bounce ability not ignored and able to reflect this move? */ const canMagicBounce = !isReflecting && - !move.checkFlag(MoveFlags.IGNORE_ABILITIES, user, target) && + !move.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user, target }) && target.hasAbilityWithAttr(ReflectStatusMoveAbAttr); const semiInvulnerableTag = target.getTag(SemiInvulnerableTag); diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index f8edaa56981..35b27375873 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -169,7 +169,7 @@ export class MovePhase extends BattlePhase { // Check move to see if arena.ignoreAbilities should be true. if (!this.followUp || this.reflected) { - if (this.move.getMove().checkFlag(MoveFlags.IGNORE_ABILITIES, this.pokemon, null)) { + if (this.move.getMove().doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user: this.pokemon, isFollowUp: this.followUp })) { globalScene.arena.setIgnoreAbilities(true, this.pokemon.getBattlerIndex()); } }