diff --git a/src/data/ability.ts b/src/data/ability.ts index 42787132b21..9cd8c61d47f 100755 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -55,8 +55,22 @@ export class Ability implements Localizable { this.description = this.id ? i18next.t(`ability:${i18nKey}.description`) as string : ""; } - getAttrs(attrType: { new(...args: any[]): AbAttr }): AbAttr[] { - return this.attrs.filter(a => a instanceof attrType); + /** + * Get all ability attributes that match `attrType` + * @param attrType any attribute that extends {@linkcode AbAttr} + * @returns Array of attributes that match `attrType`, Empty Array if none match. + */ + getAttrs(attrType: new(...args: any[]) => T ): T[] { + return this.attrs.filter((a): a is T => a instanceof attrType); + } + + /** + * Check if an ability has an attribute that matches `attrType` + * @param attrType any attribute that extends {@linkcode AbAttr} + * @returns true if the ability has attribute `attrType` + */ + hasAttr(attrType: new(...args: any[]) => T): boolean { + return this.attrs.some((attr) => attr instanceof attrType); } attr AbAttr>(AttrType: T, ...args: ConstructorParameters): Ability { @@ -74,10 +88,6 @@ export class Ability implements Localizable { return this; } - hasAttr(attrType: { new(...args: any[]): AbAttr }): boolean { - return !!this.getAttrs(attrType).length; - } - bypassFaint(): Ability { this.isBypassFaint = true; return this; @@ -341,7 +351,7 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { } applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if ((move.getMove() instanceof AttackMove || move.getMove().getAttrs(StatusMoveTypeImmunityAttr).find(attr => (attr as StatusMoveTypeImmunityAttr).immuneType === this.immuneType)) && move.getMove().type === this.immuneType) { + if ((move.getMove() instanceof AttackMove || move.getMove().getAttrs(StatusMoveTypeImmunityAttr).find(attr => attr.immuneType === this.immuneType)) && move.getMove().type === this.immuneType) { (args[0] as Utils.NumberHolder).value = 0; return true; } @@ -568,7 +578,7 @@ export class MoveImmunityStatChangeAbAttr extends MoveImmunityAbAttr { export class ReverseDrainAbAttr extends PostDefendAbAttr { applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean { - if (!!move.getMove().getAttrs(HitHealAttr).length || !!move.getMove().getAttrs(StrengthSapHealAttr).length ) { + if (move.getMove().hasAttr(HitHealAttr) || move.getMove().hasAttr(StrengthSapHealAttr) ) { pokemon.scene.queueMessage(getPokemonMessage(attacker, " sucked up the liquid ooze!")); return true; } @@ -2194,7 +2204,7 @@ function getAnticipationCondition(): AbAttrCondition { return true; } // move is a OHKO - if (move.getMove().findAttr(attr => attr instanceof OneHitKOAttr)) { + if (move.getMove().hasAttr(OneHitKOAttr)) { return true; } // edge case for hidden power, type is computed @@ -2248,7 +2258,7 @@ export class ForewarnAbAttr extends PostSummonAbAttr { for (const move of opponent.moveset) { if (move.getMove() instanceof StatusMove) { movePower = 1; - } else if (move.getMove().findAttr(attr => attr instanceof OneHitKOAttr)) { + } else if (move.getMove().hasAttr(OneHitKOAttr)) { movePower = 150; } else if (move.getMove().id === Moves.COUNTER || move.getMove().id === Moves.MIRROR_COAT || move.getMove().id === Moves.METAL_BURST) { movePower = 120; @@ -3325,7 +3335,7 @@ function applyAbAttrsInternal(attrType: { new(...args: any } const ability = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()); - const attrs = ability.getAttrs(attrType) as TAttr[]; + const attrs = ability.getAttrs(attrType); const clearSpliceQueueAndResolve = () => { pokemon.scene.clearPhaseQueueSplice(); @@ -3524,7 +3534,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.getMove().category !== MoveCategory.STATUS && !move.getMove().findAttr(attr => attr instanceof FlinchAttr)) ? 10 : 0, BattlerTagType.FLINCHED), + .attr(PostAttackApplyBattlerTagAbAttr, false, (user, target, move) => (move.getMove().category !== MoveCategory.STATUS && !move.getMove().hasAttr(FlinchAttr)) ? 10 : 0, BattlerTagType.FLINCHED), new Ability(Abilities.DRIZZLE, 3) .attr(PostSummonWeatherChangeAbAttr, WeatherType.RAIN) .attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.RAIN), diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index efda3ebcb0e..75c883ec1e7 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -468,7 +468,7 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise { } else { const loadedCheckTimer = setInterval(() => { if (moveAnims.get(move) !== null) { - const chargeAttr = allMoves[move].getAttrs(ChargeAttr).find(() => true) as ChargeAttr || allMoves[move].getAttrs(DelayedAttackAttr).find(() => true) as DelayedAttackAttr; + const chargeAttr = allMoves[move].getAttrs(ChargeAttr)[0] || allMoves[move].getAttrs(DelayedAttackAttr)[0]; if (chargeAttr && chargeAnims.get(chargeAttr.chargeAnim) === null) { return; } @@ -498,7 +498,7 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise { } else { populateMoveAnim(move, ba); } - const chargeAttr = allMoves[move].getAttrs(ChargeAttr).find(() => true) as ChargeAttr || allMoves[move].getAttrs(DelayedAttackAttr).find(() => true) as DelayedAttackAttr; + const chargeAttr = allMoves[move].getAttrs(ChargeAttr)[0] || allMoves[move].getAttrs(DelayedAttackAttr)[0]; if (chargeAttr) { initMoveChargeAnim(scene, chargeAttr.chargeAnim).then(() => resolve()); } else { @@ -569,7 +569,7 @@ export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLo return new Promise(resolve => { const moveAnimations = moveIds.map(m => moveAnims.get(m) as AnimConfig).flat(); for (const moveId of moveIds) { - const chargeAttr = allMoves[moveId].getAttrs(ChargeAttr).find(() => true) as ChargeAttr || allMoves[moveId].getAttrs(DelayedAttackAttr).find(() => true) as DelayedAttackAttr; + const chargeAttr = allMoves[moveId].getAttrs(ChargeAttr)[0] || allMoves[moveId].getAttrs(DelayedAttackAttr)[0]; if (chargeAttr) { const moveChargeAnims = chargeAnims.get(chargeAttr.chargeAnim); moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims[0]); diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 2cf1c6eff64..13c3954fd01 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -509,7 +509,7 @@ export class EncoreTag extends BattlerTag { return false; } - if (allMoves[repeatableMove.move].getAttrs(ChargeAttr).length && repeatableMove.result === MoveResult.OTHER) { + if (allMoves[repeatableMove.move].hasAttr(ChargeAttr) && repeatableMove.result === MoveResult.OTHER) { return false; } diff --git a/src/data/move.ts b/src/data/move.ts index 5594a45c195..5b7bd5f6c82 100755 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -148,8 +148,22 @@ export default class Move implements Localizable { this.effect = this.id ? `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}` : ""; } - getAttrs(attrType: { new(...args: any[]): MoveAttr }): MoveAttr[] { - return this.attrs.filter(a => a instanceof attrType); + /** + * Get all move attributes that match `attrType` + * @param attrType any attribute that extends {@linkcode MoveAttr} + * @returns Array of attributes that match `attrType`, Empty Array if none match. + */ + getAttrs(attrType: new(...args: any[]) => T): T[] { + return this.attrs.filter((a): a is T => a instanceof attrType); + } + + /** + * Check if a move has an attribute that matches `attrType` + * @param attrType any attribute that extends {@linkcode MoveAttr} + * @returns true if the move has attribute `attrType` + */ + hasAttr(attrType: new(...args: any[]) => T): boolean { + return this.attrs.some((attr) => attr instanceof attrType); } findAttr(attrPredicate: (attr: MoveAttr) => boolean): MoveAttr { @@ -3698,7 +3712,7 @@ export class ProtectAttr extends AddBattlerTagAttr { while (moveHistory.length) { turnMove = moveHistory.shift(); - if (!allMoves[turnMove.move].getAttrs(ProtectAttr).length || turnMove.result !== MoveResult.SUCCESS) { + if (!allMoves[turnMove.move].hasAttr(ProtectAttr) || turnMove.result !== MoveResult.SUCCESS) { break; } timesUsed++; @@ -4480,7 +4494,7 @@ const lastMoveCopiableCondition: MoveConditionFunc = (user, target, move) => { return false; } - if (allMoves[copiableMove].getAttrs(ChargeAttr).length) { + if (allMoves[copiableMove].hasAttr(ChargeAttr)) { return false; } @@ -4570,7 +4584,7 @@ const targetMoveCopiableCondition: MoveConditionFunc = (user, target, move) => { return false; } - if (allMoves[copiableMove.move].getAttrs(ChargeAttr).length && copiableMove.result === MoveResult.OTHER) { + if (allMoves[copiableMove.move].hasAttr(ChargeAttr) && copiableMove.result === MoveResult.OTHER) { return false; } @@ -4987,7 +5001,7 @@ export function getMoveTargets(user: Pokemon, move: Moves): MoveTargetSet { const variableTarget = new Utils.NumberHolder(0); user.getOpponents().forEach(p => applyMoveAttrs(VariableTargetAttr, user, p, allMoves[move], variableTarget)); - const moveTarget = allMoves[move].getAttrs(VariableTargetAttr).length ? variableTarget.value : move ? allMoves[move].moveTarget : move === undefined ? MoveTarget.NEAR_ENEMY : []; + const moveTarget = allMoves[move].hasAttr(VariableTargetAttr) ? variableTarget.value : move ? allMoves[move].moveTarget : move === undefined ? MoveTarget.NEAR_ENEMY : []; const opponents = user.getOpponents(); let set: Pokemon[] = []; diff --git a/src/data/terrain.ts b/src/data/terrain.ts index 6a77f876239..e396c693c4e 100644 --- a/src/data/terrain.ts +++ b/src/data/terrain.ts @@ -56,7 +56,7 @@ export class Terrain { isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move): boolean { switch (this.terrainType) { case TerrainType.PSYCHIC: - if (!move.getAttrs(ProtectAttr).length) { + if (!move.hasAttr(ProtectAttr)) { const priority = new Utils.IntegerHolder(move.priority); applyAbAttrs(IncrementMovePriorityAbAttr, user, null, move, priority); return priority.value > 0 && user.getOpponents().filter(o => targets.includes(o.getBattlerIndex())).length > 0; diff --git a/src/data/weather.ts b/src/data/weather.ts index 87500e69697..f2b4136f51c 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -114,9 +114,9 @@ export class Weather { const field = scene.getField(true); for (const pokemon of field) { - let suppressWeatherEffectAbAttr = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr; + let suppressWeatherEffectAbAttr = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr)[0]; if (!suppressWeatherEffectAbAttr) { - suppressWeatherEffectAbAttr = pokemon.hasPassive() ? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr : null; + suppressWeatherEffectAbAttr = pokemon.hasPassive() ? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr)[0] : null; } if (suppressWeatherEffectAbAttr && (!this.isImmutable() || suppressWeatherEffectAbAttr.affectsImmutable)) { return true; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 02565e799b7..8065b4633b7 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1058,7 +1058,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getAttackMoveEffectiveness(source: Pokemon, move: PokemonMove): TypeDamageMultiplier { - const typeless = !!move.getMove().getAttrs(TypelessAttr).length; + const typeless = move.getMove().hasAttr(TypelessAttr); const typeMultiplier = new Utils.NumberHolder(this.getAttackTypeEffectiveness(move.getMove().type, source)); const cancelled = new Utils.BooleanHolder(false); if (!typeless) { @@ -1424,19 +1424,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (this.isBoss()) { // Bosses never get self ko moves - movePool = movePool.filter(m => !allMoves[m[0]].getAttrs(SacrificialAttr).length); + movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttr)); } - movePool = movePool.filter(m => !allMoves[m[0]].getAttrs(SacrificialAttrOnHit).length); + movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttrOnHit)); if (this.hasTrainer()) { // Trainers never get OHKO moves - movePool = movePool.filter(m => !allMoves[m[0]].getAttrs(OneHitKOAttr).length); + movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(OneHitKOAttr)); // Half the weight of self KO moves - movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].getAttrs(SacrificialAttr).length ? 0.5 : 1)]); - movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].getAttrs(SacrificialAttrOnHit).length ? 0.5 : 1)]); + movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].hasAttr(SacrificialAttr) ? 0.5 : 1)]); + movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].hasAttr(SacrificialAttrOnHit) ? 0.5 : 1)]); // Trainers get a weight bump to stat buffing moves - movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].getAttrs(StatChangeAttr).some(a => (a as StatChangeAttr).levels > 1 && (a as StatChangeAttr).selfTarget) ? 1.25 : 1)]); + movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].getAttrs(StatChangeAttr).some(a => a.levels > 1 && a.selfTarget) ? 1.25 : 1)]); // Trainers get a weight decrease to multiturn moves - movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].getAttrs(ChargeAttr).length || !!allMoves[m[0]].getAttrs(RechargeAttr).length ? 0.7 : 1)]); + movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].hasAttr(ChargeAttr) || !!allMoves[m[0]].hasAttr(RechargeAttr) ? 0.7 : 1)]); } // Weight towards higher power moves, by reducing the power of moves below the highest power. @@ -1627,8 +1627,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const types = this.getTypes(true, true); const cancelled = new Utils.BooleanHolder(false); - const typeless = !!move.getAttrs(TypelessAttr).length; - const typeMultiplier = new Utils.NumberHolder(!typeless && (moveCategory !== MoveCategory.STATUS || move.getAttrs(StatusMoveTypeImmunityAttr).find(attr => types.includes((attr as StatusMoveTypeImmunityAttr).immuneType))) + const typeless = move.hasAttr(TypelessAttr); + const typeMultiplier = new Utils.NumberHolder(!typeless && (moveCategory !== MoveCategory.STATUS || move.getAttrs(StatusMoveTypeImmunityAttr).find(attr => types.includes(attr.immuneType))) ? this.getAttackTypeEffectiveness(type, source) : 1); applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier); @@ -1654,7 +1654,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const isPhysical = moveCategory === MoveCategory.PHYSICAL; const power = new Utils.NumberHolder(move.power); const sourceTeraType = source.getTeraType(); - if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === type && power.value < 60 && move.priority <= 0 && !move.getAttrs(MultiHitAttr).length && !this.scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) { + if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === type && power.value < 60 && move.priority <= 0 && !move.hasAttr(MultiHitAttr) && !this.scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) { power.value = 60; } applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, this, battlerMove, power); @@ -1756,7 +1756,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (!isTypeImmune) { damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier.value * screenMultiplier.value * ((this.scene.randBattleSeedInt(15) + 85) / 100) * criticalMultiplier.value); if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) { - if (!move.getAttrs(BypassBurnDamageReductionAttr).length) { + if (!move.hasAttr(BypassBurnDamageReductionAttr)) { const burnDamageReductionCancelled = new Utils.BooleanHolder(false); applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled); if (!burnDamageReductionCancelled.value) { @@ -1768,12 +1768,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, battlerMove, damage); /** - * For each {@link HitsTagAttr} the move has, doubles the damage of the move if: - * The target has a {@link BattlerTagType} that this move interacts with - * AND - * The move doubles damage when used against that tag - * */ - move.getAttrs(HitsTagAttr).map(hta => hta as HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => { + * For each {@link HitsTagAttr} the move has, doubles the damage of the move if: + * The target has a {@link BattlerTagType} that this move interacts with + * AND + * The move doubles damage when used against that tag + */ + move.getAttrs(HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => { if (this.getTag(hta.tagType)) { damage.value *= 2; } @@ -3454,7 +3454,7 @@ export class EnemyPokemon extends Pokemon { if (!sortedBenefitScores.length) { // Set target to BattlerIndex.ATTACKER when using a counter move // This is the same as when the player does so - if (!!move.findAttr(attr => attr instanceof CounterDamageAttr)) { + if (move.hasAttr(CounterDamageAttr)) { return [BattlerIndex.ATTACKER]; } diff --git a/src/phases.ts b/src/phases.ts index b23f848397d..8b5e8731faf 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -1553,7 +1553,7 @@ export class SwitchSummonPhase extends SummonPhase { const lastUsedMove = moveId ? allMoves[moveId] : undefined; const currentCommand = pokemon.scene.currentBattle.turnCommands[this.fieldIndex]?.command; - const lastPokemonIsForceSwitchedAndNotFainted = !!lastUsedMove?.findAttr(attr => attr instanceof ForceSwitchOutAttr) && !this.lastPokemon.isFainted(); + const lastPokemonIsForceSwitchedAndNotFainted = lastUsedMove?.hasAttr(ForceSwitchOutAttr) && !this.lastPokemon.isFainted(); // Compensate for turn spent summoning // Or compensate for force switch move if switched out pokemon is not fainted @@ -2459,9 +2459,9 @@ export class MovePhase extends BattlePhase { const oldTarget = moveTarget.value; this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, this.move.moveId, moveTarget)); //Check if this move is immune to being redirected, and restore its target to the intended target if it is. - if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) || this.move.getMove().getAttrs(BypassRedirectAttr).length)) { + if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) || this.move.getMove().hasAttr(BypassRedirectAttr))) { //If an ability prevented this move from being redirected, display its ability pop up. - if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) && !this.move.getMove().getAttrs(BypassRedirectAttr).length) && oldTarget !== moveTarget.value) { + if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) && !this.move.getMove().hasAttr(BypassRedirectAttr)) && oldTarget !== moveTarget.value) { this.scene.unshiftPhase(new ShowAbilityPhase(this.scene, this.pokemon.getBattlerIndex(), this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr))); } moveTarget.value = oldTarget; @@ -2551,7 +2551,7 @@ export class MovePhase extends BattlePhase { this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed)); } - if (!allMoves[this.move.moveId].getAttrs(CopyMoveAttr).length) { + if (!allMoves[this.move.moveId].hasAttr(CopyMoveAttr)) { this.scene.currentBattle.lastMove = this.move.moveId; } @@ -2635,7 +2635,7 @@ export class MovePhase extends BattlePhase { } showMoveText(): void { - if (this.move.getMove().getAttrs(ChargeAttr).length) { + if (this.move.getMove().hasAttr(ChargeAttr)) { const lastMove = this.pokemon.getLastXMoves() as TurnMove[]; if (!lastMove.length || lastMove[0].move !== this.move.getMove().id || lastMove[0].result !== MoveResult.OTHER) { this.scene.queueMessage(getPokemonMessage(this.pokemon, ` used\n${this.move.getName()}!`), 500); @@ -2706,7 +2706,7 @@ export class MoveEffectPhase extends PokemonPhase { const hitCount = new Utils.IntegerHolder(1); // Assume single target for multi hit applyMoveAttrs(MultiHitAttr, user, this.getTarget(), this.move.getMove(), hitCount); - if (this.move.getMove() instanceof AttackMove && !this.move.getMove().getAttrs(FixedDamageAttr).length) { + if (this.move.getMove() instanceof AttackMove && !this.move.getMove().hasAttr(FixedDamageAttr)) { this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0)); } user.turnData.hitsLeft = user.turnData.hitCount = hitCount.value; @@ -2717,7 +2717,7 @@ export class MoveEffectPhase extends PokemonPhase { const targetHitChecks = Object.fromEntries(targets.map(p => [ p.getBattlerIndex(), this.hitCheck(p) ])); const activeTargets = targets.map(t => t.isActive(true)); - if (!activeTargets.length || (!this.move.getMove().getAttrs(VariableTargetAttr).length && !this.move.getMove().isMultiTarget() && !targetHitChecks[this.targets[0]])) { + if (!activeTargets.length || (!this.move.getMove().hasAttr(VariableTargetAttr) && !this.move.getMove().isMultiTarget() && !targetHitChecks[this.targets[0]])) { user.turnData.hitCount = 1; user.turnData.hitsLeft = 1; if (activeTargets.length) { @@ -2761,7 +2761,7 @@ export class MoveEffectPhase extends PokemonPhase { applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit), user, target, this.move.getMove()).then(() => { if (hitResult !== HitResult.FAIL) { - const chargeEffect = !!this.move.getMove().getAttrs(ChargeAttr).find(ca => (ca as ChargeAttr).usedChargeEffect(user, this.getTarget(), this.move.getMove())); + const chargeEffect = !!this.move.getMove().getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget(), this.move.getMove())); // Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY && (attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit), user, target, this.move.getMove())).then(() => { @@ -2861,7 +2861,7 @@ export class MoveEffectPhase extends PokemonPhase { } const hiddenTag = target.getTag(HiddenTag); - if (hiddenTag && !this.move.getMove().getAttrs(HitsTagAttr).filter(hta => (hta as HitsTagAttr).tagType === hiddenTag.tagType).length) { + if (hiddenTag && !this.move.getMove().getAttrs(HitsTagAttr).some(hta => hta.tagType === hiddenTag.tagType)) { return false; } @@ -2873,7 +2873,7 @@ export class MoveEffectPhase extends PokemonPhase { return true; } - const isOhko = !!this.move.getMove().getAttrs(OneHitKOAccuracyAttr).length; + const isOhko = this.move.getMove().hasAttr(OneHitKOAccuracyAttr); if (!isOhko) { user.scene.applyModifiers(PokemonMoveAccuracyBoosterModifier, user.isPlayer(), user, moveAccuracy); @@ -3534,7 +3534,7 @@ export class FaintPhase extends PokemonPhase { if (defeatSource?.isOnField()) { applyPostVictoryAbAttrs(PostVictoryAbAttr, defeatSource); const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move]; - const pvattrs = pvmove.getAttrs(PostVictoryStatChangeAttr) as PostVictoryStatChangeAttr[]; + const pvattrs = pvmove.getAttrs(PostVictoryStatChangeAttr); if (pvattrs.length) { for (const pvattr of pvattrs) { pvattr.applyPostVictory(defeatSource, defeatSource, pvmove);