diff --git a/data/abilities.ts b/data/abilities.ts index b8c631f1ff..475dd63b43 100644 --- a/data/abilities.ts +++ b/data/abilities.ts @@ -276,7 +276,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { if (effect?.effectType !== 'Move') { return; } - if (source.species.id === 'greninja' && source.hp && !source.transformed && source.side.foe.pokemonLeft) { + if (source.species.id === 'greninja' && source.hp && !source.transformed && source.side.foePokemonLeft()) { this.add('-activate', source, 'ability: Battle Bond'); source.formeChange('Greninja-Ash', this.effect, true); } @@ -3154,19 +3154,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onStart(pokemon) { let activated = false; for (const sideCondition of ['reflect', 'lightscreen', 'auroraveil']) { - if (pokemon.side.getSideCondition(sideCondition)) { - if (!activated) { - this.add('-activate', pokemon, 'ability: Screen Cleaner'); - activated = true; + for (const side of [pokemon.side, ...pokemon.side.foeSidesWithConditions()]) { + if (side.getSideCondition(sideCondition)) { + if (!activated) { + this.add('-activate', pokemon, 'ability: Screen Cleaner'); + activated = true; + } + side.removeSideCondition(sideCondition); } - pokemon.side.removeSideCondition(sideCondition); - } - if (pokemon.side.foe.getSideCondition(sideCondition)) { - if (!activated) { - this.add('-activate', pokemon, 'ability: Screen Cleaner'); - activated = true; - } - pokemon.side.foe.removeSideCondition(sideCondition); } } }, diff --git a/data/mods/gen3/abilities.ts b/data/mods/gen3/abilities.ts index 543d10fcbc..7ab80c99a3 100644 --- a/data/mods/gen3/abilities.ts +++ b/data/mods/gen3/abilities.ts @@ -152,7 +152,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { inherit: true, onUpdate(pokemon) { if (!pokemon.isStarted) return; - const target = pokemon.side.foe.randomAlly(); + const target = pokemon.side.randomFoe(); if (!target || target.fainted) return; const ability = target.getAbility(); const bannedAbilities = ['forecast', 'multitype', 'trace']; diff --git a/data/mods/gen4/abilities.ts b/data/mods/gen4/abilities.ts index b817dea0f1..1d09ed31ad 100644 --- a/data/mods/gen4/abilities.ts +++ b/data/mods/gen4/abilities.ts @@ -454,7 +454,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { inherit: true, onUpdate(pokemon) { if (!pokemon.isStarted) return; - const target = pokemon.side.foe.randomAlly(); + const target = pokemon.side.randomFoe(); if (!target || target.fainted) return; const ability = target.getAbility(); const bannedAbilities = ['forecast', 'multitype', 'trace']; diff --git a/data/mods/gen5/abilities.ts b/data/mods/gen5/abilities.ts index b642b2bba4..1185ec6133 100644 --- a/data/mods/gen5/abilities.ts +++ b/data/mods/gen5/abilities.ts @@ -19,7 +19,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { frisk: { inherit: true, onStart(pokemon) { - const target = pokemon.side.foe.randomAlly(); + const target = pokemon.side.randomFoe(); if (target?.item) { this.add('-item', target, target.getItem().name, '[from] ability: Frisk', '[of] ' + pokemon); } diff --git a/data/moves.ts b/data/moves.ts index 5ebe97f618..b92a4a0588 100644 --- a/data/moves.ts +++ b/data/moves.ts @@ -4877,20 +4877,16 @@ export const Moves: {[moveid: string]: MoveData} = { this.add('-sidestart', targetSide, 'Fire Pledge'); }, onEnd(targetSide) { - for (const pokemon of targetSide.activeTeam()) { - if (pokemon && !pokemon.hasType('Fire')) { - this.damage(pokemon.baseMaxhp / 8, pokemon); - } + for (const pokemon of targetSide.allies()) { + if (!pokemon.hasType('Fire')) this.damage(pokemon.baseMaxhp / 8, pokemon); } this.add('-sideend', targetSide, 'Fire Pledge'); }, onResidualOrder: 5, onResidualSubOrder: 1, onResidual(side) { - for (const pokemon of side.activeTeam()) { - if (pokemon && !pokemon.hasType('Fire')) { - this.damage(pokemon.baseMaxhp / 8, pokemon); - } + for (const pokemon of side.allies()) { + if (!pokemon.hasType('Fire')) this.damage(pokemon.baseMaxhp / 8, pokemon); } }, }, @@ -5944,15 +5940,10 @@ export const Moves: {[moveid: string]: MoveData} = { priority: 0, flags: {snatch: 1, authentic: 1}, onHitSide(side, source, move) { - const targets = []; - for (const pokemon of side.activeTeam()) { - if ( - pokemon.hasAbility(['plus', 'minus']) && - (!pokemon.volatiles['maxguard'] || this.runEvent('TryHit', pokemon, source, move)) - ) { - targets.push(pokemon); - } - } + const targets = side.allies().filter(target => ( + target.hasAbility(['plus', 'minus']) && + (!target.volatiles['maxguard'] || this.runEvent('TryHit', target, source, move)) + )); if (!targets.length) return false; let didSomething = false; for (const target of targets) { @@ -6187,13 +6178,13 @@ export const Moves: {[moveid: string]: MoveData} = { onResidualOrder: 5, onResidualSubOrder: 1.1, onResidual(targetSide) { - for (const pokemon of targetSide.activeTeam()) { - if (!pokemon.hasType('Water')) this.damage(pokemon.baseMaxhp / 6, pokemon); + for (const target of targetSide.allies()) { + if (!target.hasType('Water')) this.damage(target.baseMaxhp / 6, target); } }, onEnd(targetSide) { - for (const pokemon of targetSide.activeTeam()) { - if (!pokemon.hasType('Water')) this.damage(pokemon.baseMaxhp / 6, pokemon); + for (const target of targetSide.allies()) { + if (!target.hasType('Water')) this.damage(target.baseMaxhp / 6, target); } this.add('-sideend', targetSide, 'G-Max Cannonade'); }, @@ -6839,13 +6830,13 @@ export const Moves: {[moveid: string]: MoveData} = { onResidualOrder: 5, onResidualSubOrder: 1.1, onResidual(targetSide) { - for (const pokemon of targetSide.activeTeam()) { - if (!pokemon.hasType('Grass')) this.damage(pokemon.baseMaxhp / 6, pokemon); + for (const target of targetSide.allies()) { + if (!target.hasType('Grass')) this.damage(target.baseMaxhp / 6, target); } }, onEnd(targetSide) { - for (const pokemon of targetSide.activeTeam()) { - if (!pokemon.hasType('Grass')) this.damage(pokemon.baseMaxhp / 6, pokemon); + for (const target of targetSide.allies()) { + if (!target.hasType('Grass')) this.damage(target.baseMaxhp / 6, target); } this.add('-sideend', targetSide, 'G-Max Vine Lash'); }, @@ -6879,13 +6870,13 @@ export const Moves: {[moveid: string]: MoveData} = { onResidualOrder: 5, onResidualSubOrder: 1.1, onResidual(targetSide) { - for (const pokemon of targetSide.activeTeam()) { - if (!pokemon.hasType('Rock')) this.damage(pokemon.baseMaxhp / 6, pokemon); + for (const target of targetSide.allies()) { + if (!target.hasType('Rock')) this.damage(target.baseMaxhp / 6, target); } }, onEnd(targetSide) { - for (const pokemon of targetSide.activeTeam()) { - if (!pokemon.hasType('Rock')) this.damage(pokemon.baseMaxhp / 6, pokemon); + for (const target of targetSide.allies()) { + if (!target.hasType('Rock')) this.damage(target.baseMaxhp / 6, target); } this.add('-sideend', targetSide, 'G-Max Volcalith'); }, @@ -6942,13 +6933,13 @@ export const Moves: {[moveid: string]: MoveData} = { onResidualOrder: 5, onResidualSubOrder: 1.1, onResidual(targetSide) { - for (const pokemon of targetSide.activeTeam()) { - if (!pokemon.hasType('Fire')) this.damage(pokemon.baseMaxhp / 6, pokemon); + for (const target of targetSide.allies()) { + if (!target.hasType('Fire')) this.damage(target.baseMaxhp / 6, target); } }, onEnd(targetSide) { - for (const pokemon of targetSide.activeTeam()) { - if (!pokemon.hasType('Fire')) this.damage(pokemon.baseMaxhp / 6, pokemon); + for (const target of targetSide.allies()) { + if (!target.hasType('Fire')) this.damage(target.baseMaxhp / 6, target); } this.add('-sideend', targetSide, 'G-Max Wildfire'); }, @@ -10106,16 +10097,12 @@ export const Moves: {[moveid: string]: MoveData} = { priority: 0, flags: {snatch: 1, distance: 1, authentic: 1}, onHitSide(side, source, move) { - const targets = []; - for (const pokemon of side.activeTeam()) { - if ( - pokemon.hasAbility(['plus', 'minus']) && - (!pokemon.volatiles['maxguard'] || this.runEvent('TryHit', pokemon, source, move)) - ) { - targets.push(pokemon); - } - } + const targets = side.allies().filter(ally => ( + ally.hasAbility(['plus', 'minus']) && + (!ally.volatiles['maxguard'] || this.runEvent('TryHit', ally, source, move)) + )); if (!targets.length) return false; + let didSomething = false; for (const target of targets) { didSomething = this.boost({def: 1, spd: 1}, target, source, move, false, true) || didSomething; diff --git a/sim/battle.ts b/sim/battle.ts index 56389e37d6..ee75f5b3b3 100644 --- a/sim/battle.ts +++ b/sim/battle.ts @@ -1414,7 +1414,7 @@ export class Battle { if (this.maybeTriggerEndlessBattleClause(trappedBySide, stalenessBySide)) return; - if (this.gameType === 'triples' && !this.sides.filter(side => side.pokemonLeft > 1).length) { + if (this.gameType === 'triples' && this.sides.every(side => side.pokemonLeft === 1)) { // If both sides have one Pokemon left in triples and they are not adjacent, they are both moved to the center. const actives = this.getAllActive(); if (actives.length > 1 && !actives[0].isAdjacent(actives[1])) { @@ -1594,7 +1594,7 @@ export class Battle { } if (!target?.hp) return 0; if (!target.isActive) return false; - if (this.gen > 5 && !target.side.foe.pokemonLeft) return false; + if (this.gen > 5 && !target.side.foePokemonLeft()) return false; boost = this.runEvent('Boost', target, source, effect, {...boost}); let success = null; let boosted = isSecondary; @@ -2062,7 +2062,7 @@ export class Battle { return foeActives[frontPosition]; } } - return pokemon.side.foe.randomAlly() || pokemon.side.foe.active[0]; + return pokemon.side.randomFoe() || pokemon.side.foe.active[0]; } checkFainted() { diff --git a/sim/side.ts b/sim/side.ts index fc990cd2eb..7d3f74d30d 100644 --- a/sim/side.ts +++ b/sim/side.ts @@ -204,12 +204,27 @@ export class Side { return data; } - randomAlly() { - const actives = this.allies(); + randomFoe() { + const actives = this.foes(); if (!actives.length) return null; return this.battle.sample(actives); } + /** Intended as a way to iterate through all foe side conditions - do not use for anything else. */ + foeSidesWithConditions() { + if (this.battle.gameType === 'multi') return this.battle.sides.filter(side => side !== this); + + return [this.foe]; + } + foePokemonLeft() { + if (this.battle.gameType === 'multi') { + return this.battle.sides.filter(side => side !== this).map(side => side.pokemonLeft).reduce((a, b) => a + b); + } + + if (this.foe.allySide) return this.foe.pokemonLeft + this.foe.allySide.pokemonLeft; + + return this.foe.pokemonLeft; + } allies() { // called during the first switch-in, so `active` can still contain nulls at this point return this.activeTeam().filter(ally => ally && !ally.fainted); @@ -217,7 +232,7 @@ export class Side { foes() { if (this.battle.gameType === 'free-for-all') { return this.battle.sides.map(side => side.active[0]) - .filter(pokemon => pokemon.side !== this && pokemon && !pokemon.fainted); + .filter(pokemon => pokemon && pokemon.side !== this && !pokemon.fainted); } return this.foe.allies(); }