diff --git a/data/mods/gen7/moves.ts b/data/mods/gen7/moves.ts index 5a9fa8c69e..98408d94fc 100644 --- a/data/mods/gen7/moves.ts +++ b/data/mods/gen7/moves.ts @@ -300,22 +300,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = { inherit: true, isNonstandard: null, }, - healbell: { - inherit: true, - onHit(target, source) { - this.add('-activate', source, 'move: Heal Bell'); - let success = false; - const allies = [...target.side.pokemon, ...target.side.allySide?.pokemon || []]; - for (const ally of allies) { - if (ally.hasAbility('soundproof') && !this.suppressingAbility(ally)) { - this.add('-immune', ally, '[from] ability: Soundproof'); - continue; - } - if (ally.cureStatus()) success = true; - } - return success; - }, - }, healblock: { inherit: true, isNonstandard: null, diff --git a/data/mods/gen9ssb/scripts.ts b/data/mods/gen9ssb/scripts.ts index e3b535939d..0cc8cf59cf 100644 --- a/data/mods/gen9ssb/scripts.ts +++ b/data/mods/gen9ssb/scripts.ts @@ -1329,7 +1329,8 @@ export const Scripts: ModdedBattleScriptsData = { } let damage: number | false | undefined | '' = false; - if (move.target === 'all' || move.target === 'foeSide' || move.target === 'allySide' || move.target === 'allyTeam') { + if (move.target === 'all' || move.target === 'foeSide' || move.target === 'allySide' || + (move.target === 'allyTeam' && this.battle.gen <= 5)) { damage = this.tryMoveHit(targets, pokemon, move); if (damage === this.battle.NOT_FAIL) pokemon.moveThisTurnResult = null; if (damage || damage === 0 || damage === undefined) moveResult = true; @@ -1599,7 +1600,8 @@ export const Scripts: ModdedBattleScriptsData = { if (!moveData.flags) moveData.flags = {}; if (move.target === 'all' && !isSelf) { hitResult = this.battle.singleEvent('TryHitField', moveData, {}, target || null, pokemon, move); - } else if ((move.target === 'foeSide' || move.target === 'allySide' || move.target === 'allyTeam') && !isSelf) { + } else if ((move.target === 'foeSide' || move.target === 'allySide' || + (move.target === 'allyTeam' && this.battle.gen <= 5)) && !isSelf) { hitResult = this.battle.singleEvent('TryHitSide', moveData, {}, target || null, pokemon, move); } else if (target) { hitResult = this.battle.singleEvent('TryHit', moveData, {}, target, pokemon, move); @@ -1614,7 +1616,8 @@ export const Scripts: ModdedBattleScriptsData = { // 0. check for substitute if (!isSecondary && !isSelf) { - if (move.target !== 'all' && move.target !== 'allyTeam' && move.target !== 'allySide' && move.target !== 'foeSide') { + if (move.target !== 'all' && move.target !== 'allySide' && move.target !== 'foeSide' && + (move.target !== 'allyTeam' || this.battle.gen > 5)) { damage = this.tryPrimaryHitEvent(damage, targets, pokemon, move, moveData, isSecondary); } } diff --git a/data/moves.ts b/data/moves.ts index 67fc257f2d..5c6fd72ac7 100644 --- a/data/moves.ts +++ b/data/moves.ts @@ -563,25 +563,9 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = { pp: 5, priority: 0, flags: { snatch: 1, distance: 1, metronome: 1 }, - onHit(target, source, move) { - this.add('-activate', source, 'move: Aromatherapy'); - let success = false; - const allies = [...target.side.pokemon, ...target.side.allySide?.pokemon || []]; - for (const ally of allies) { - if (ally !== source && !this.suppressingAbility(ally)) { - if (ally.hasAbility('sapsipper')) { - this.add('-immune', ally, '[from] ability: Sap Sipper'); - continue; - } - if (ally.hasAbility('goodasgold')) { - this.add('-immune', ally, '[from] ability: Good as Gold'); - continue; - } - if (ally.volatiles['substitute'] && !move.infiltrates) continue; - } - if (ally.cureStatus()) success = true; - } - return success; + // activation message in sim/battle-actions.ts + onHit(target) { + target.cureStatus(); }, target: "allyTeam", type: "Grass", @@ -8248,24 +8232,9 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = { pp: 5, priority: 0, flags: { snatch: 1, sound: 1, distance: 1, bypasssub: 1, metronome: 1 }, - onHit(target, source) { - this.add('-activate', source, 'move: Heal Bell'); - let success = false; - const allies = [...target.side.pokemon, ...target.side.allySide?.pokemon || []]; - for (const ally of allies) { - if (ally !== source && !this.suppressingAbility(ally)) { - if (ally.hasAbility('soundproof')) { - this.add('-immune', ally, '[from] ability: Soundproof'); - continue; - } - if (ally.hasAbility('goodasgold')) { - this.add('-immune', ally, '[from] ability: Good as Gold'); - continue; - } - } - if (ally.cureStatus()) success = true; - } - return success; + // activation message in sim/battle-actions.ts + onHit(target, source, move) { + target.cureStatus(); }, target: "allyTeam", type: "Normal", diff --git a/sim/battle-actions.ts b/sim/battle-actions.ts index 66fd617e6d..48e3618399 100644 --- a/sim/battle-actions.ts +++ b/sim/battle-actions.ts @@ -503,7 +503,8 @@ export class BattleActions { } let damage: number | false | undefined | '' = false; - if (move.target === 'all' || move.target === 'foeSide' || move.target === 'allySide' || move.target === 'allyTeam') { + if (move.target === 'all' || move.target === 'foeSide' || move.target === 'allySide' || + (move.target === 'allyTeam' && this.battle.gen <= 5)) { damage = this.tryMoveHit(targets, pokemon, move); if (damage === this.battle.NOT_FAIL) pokemon.moveThisTurnResult = null; if (damage || damage === 0 || damage === undefined) moveResult = true; @@ -614,7 +615,12 @@ export class BattleActions { return moveResult; } hitStepInvulnerabilityEvent(targets: Pokemon[], pokemon: Pokemon, move: ActiveMove) { - if (move.id === 'helpinghand') return new Array(targets.length).fill(true); + if (['aromatherapy', 'healbell', 'helpinghand'].includes(move.id)) { + if (this.battle.gen > 5 && (move.id === 'aromatherapy' || move.id === 'healbell')) { + this.battle.add('-activate', pokemon, `move: ${move.name}`); + } + return new Array(targets.length).fill(true); + } const hitResults: boolean[] = []; for (const [i, target] of targets.entries()) { if (target.volatiles['commanding']) { @@ -1059,7 +1065,8 @@ export class BattleActions { if (!moveData.flags) moveData.flags = {}; if (move.target === 'all' && !isSelf) { hitResult = this.battle.singleEvent('TryHitField', moveData, {}, target || null, pokemon, move); - } else if ((move.target === 'foeSide' || move.target === 'allySide' || move.target === 'allyTeam') && !isSelf) { + } else if ((move.target === 'foeSide' || move.target === 'allySide' || + (move.target === 'allyTeam' && this.battle.gen <= 5)) && !isSelf) { hitResult = this.battle.singleEvent('TryHitSide', moveData, {}, target || null, pokemon, move); } else if (target) { hitResult = this.battle.singleEvent('TryHit', moveData, {}, target, pokemon, move); @@ -1074,7 +1081,8 @@ export class BattleActions { // 0. check for substitute if (!isSecondary && !isSelf) { - if (move.target !== 'all' && move.target !== 'allyTeam' && move.target !== 'allySide' && move.target !== 'foeSide') { + if (move.target !== 'all' && move.target !== 'allySide' && move.target !== 'foeSide' && + (move.target !== 'allyTeam' || this.battle.gen > 5)) { damage = this.tryPrimaryHitEvent(damage, targets, pokemon, move, moveData, isSecondary); } } diff --git a/sim/pokemon.ts b/sim/pokemon.ts index b9283feb3a..152aed3d8c 100644 --- a/sim/pokemon.ts +++ b/sim/pokemon.ts @@ -796,7 +796,6 @@ export class Pokemon { case 'all': case 'foeSide': case 'allySide': - case 'allyTeam': if (!move.target.startsWith('foe')) { targets.push(...this.alliesAndSelf()); } @@ -807,6 +806,12 @@ export class Pokemon { this.battle.retargetLastMove(targets[targets.length - 1]); } break; + case 'allyTeam': + targets.push(...this.side.pokemon.filter(ally => ally.hp)); + if (this.side.allySide) { + targets.push(...this.side.allySide.pokemon.filter(ally => ally.hp)); + } + break; case 'allAdjacent': targets.push(...this.adjacentAllies()); // falls through diff --git a/test/sim/moves/aromatherapy.js b/test/sim/moves/aromatherapy.js new file mode 100644 index 0000000000..4d8f5d654a --- /dev/null +++ b/test/sim/moves/aromatherapy.js @@ -0,0 +1,80 @@ +'use strict'; + +const assert = require('./../../assert'); +const common = require('./../../common'); + +let battle; + +describe('Aromatherapy', () => { + afterEach(() => { + battle.destroy(); + }); + + it(`should heal the major status conditions of the user's team`, () => { + battle = common.createBattle([[ + { species: 'Dunsparce', moves: ['sleeptalk'] }, + { species: 'Chansey', moves: ['aromatherapy'] }, + ], [ + { species: 'Nidoking', moves: ['toxic', 'glare'] }, + ]]); + battle.makeChoices('auto', 'move glare'); + battle.makeChoices('switch chansey', 'auto'); + battle.makeChoices(); + assert.equal(battle.p1.pokemon[0].status, ''); + assert.equal(battle.p1.pokemon[1].status, ''); + }); + + it(`should not heal the major status conditions of a Pokemon with Sap Sipper`, () => { + battle = common.createBattle({ gameType: 'doubles' }, [[ + { species: 'Azumarill', ability: 'sapsipper', moves: ['sleeptalk'] }, + { species: 'Chansey', moves: ['sleeptalk', 'aromatherapy'] }, + ], [ + { species: 'Nidoking', moves: ['sleeptalk', 'toxic'] }, + { species: 'Wynaut', moves: ['sleeptalk'] }, + ]]); + battle.makeChoices('auto', 'move toxic 1, move sleeptalk'); + battle.makeChoices('move sleeptalk, move aromatherapy', 'auto'); + assert.equal(battle.p1.pokemon[0].status, 'tox'); + }); + + it(`should not heal the major status conditions of a Pokemon behind a Substitute`, () => { + battle = common.createBattle({ gameType: 'doubles' }, [[ + { species: 'Azumarill', moves: ['sleeptalk', 'substitute'] }, + { species: 'Chansey', moves: ['sleeptalk', 'aromatherapy'] }, + ], [ + { species: 'Nidoking', moves: ['sleeptalk', 'toxic'] }, + { species: 'Wynaut', moves: ['sleeptalk'] }, + ]]); + battle.makeChoices('move substitute, move sleeptalk', 'move toxic 1, move sleeptalk'); + battle.makeChoices('move sleeptalk, move aromatherapy', 'move sleeptalk, move sleeptalk'); + assert.equal(battle.p1.pokemon[0].status, 'tox'); + }); + + describe('[Gen 5]', () => { + it(`should heal the major status conditions of a Pokemon with Sap Sipper`, () => { + battle = common.gen(5).createBattle({ gameType: 'doubles' }, [[ + { species: 'Azumarill', ability: 'sapsipper', moves: ['sleeptalk'] }, + { species: 'Chansey', moves: ['sleeptalk', 'aromatherapy'] }, + ], [ + { species: 'Nidoking', moves: ['sleeptalk', 'toxic'] }, + { species: 'Wynaut', moves: ['sleeptalk'] }, + ]]); + battle.makeChoices('auto', 'move toxic 1, move sleeptalk'); + battle.makeChoices('move sleeptalk, move aromatherapy', 'auto'); + assert.equal(battle.p1.pokemon[0].status, ''); + }); + + it(`should heal the major status conditions of a Pokemon behind a Substitute`, () => { + battle = common.gen(5).createBattle({ gameType: 'doubles' }, [[ + { species: 'Azumarill', moves: ['sleeptalk', 'substitute'] }, + { species: 'Chansey', moves: ['sleeptalk', 'aromatherapy'] }, + ], [ + { species: 'Nidoking', moves: ['sleeptalk', 'toxic'] }, + { species: 'Wynaut', moves: ['sleeptalk'] }, + ]]); + battle.makeChoices('move substitute, move sleeptalk', 'move toxic 1, move sleeptalk'); + battle.makeChoices('move sleeptalk, move aromatherapy', 'move sleeptalk, move sleeptalk'); + assert.equal(battle.p1.pokemon[0].status, ''); + }); + }); +}); diff --git a/test/sim/moves/healbell.js b/test/sim/moves/healbell.js index 0814907b4e..e573dbb33c 100644 --- a/test/sim/moves/healbell.js +++ b/test/sim/moves/healbell.js @@ -25,18 +25,29 @@ describe('Heal Bell', () => { assert.equal(battle.p1.pokemon[1].status, ''); }); - it(`should not heal the major status conditions of a Pokemon with Soundproof`, () => { - battle = common.createBattle({ gameType: 'doubles' }, [[ - { species: 'Kommo-o', ability: 'soundproof', moves: ['sleeptalk'] }, - { species: 'Chansey', moves: ['sleeptalk', 'healbell'] }, - ], [ - { species: 'Nidoking', moves: ['sleeptalk', 'toxic'] }, - { species: 'Wynaut', moves: ['sleeptalk'] }, - ]]); - battle.makeChoices('auto', 'move toxic 1, move sleeptalk'); - battle.makeChoices('move sleeptalk, move healbell', 'auto'); - assert.equal(battle.p1.pokemon[0].status, 'tox'); - }); + for (let gen = Dex.gen; gen >= 3; gen--) { + it(`should handle the interaction with Soundproof correctly in Gen ${gen}`, () => { + battle = common.gen(gen).createBattle({ gameType: 'doubles' }, [[ + { species: 'Meganium', ability: 'soundproof', moves: ['sleeptalk'] }, + { species: 'Typhlosion', ability: 'soundproof', moves: ['sleeptalk', 'healbell'] }, + { species: 'Feraligatr', ability: 'soundproof', moves: ['sleeptalk'] }, + ], [ + { species: 'Wynaut', moves: ['glare', 'sleeptalk'] }, + { species: 'Wynaut', moves: ['glare', 'sleeptalk'] }, + ]]); + battle.forceRandomChance = true; + battle.makeChoices('auto', 'move glare 1, move glare 2'); + battle.makeChoices('switch 3, move sleeptalk', 'move glare 1, move glare 2'); + battle.forceRandomChance = false; + battle.makeChoices('move sleeptalk, move healbell', 'move sleeptalk, move sleeptalk'); + const self = battle.p1.pokemon[1]; + const active = battle.p1.pokemon[0]; + const back = battle.p1.pokemon[2]; + assert.equal(self.status, gen >= 8 || gen === 5 ? '' : 'par'); + assert.equal(active.status, gen === 5 ? '' : 'par'); + assert.equal(back.status, gen > 4 ? '' : 'par'); + }); + } it(`with Mold Breaker should heal the major status conditions of a Pokemon with Soundproof`, () => { battle = common.createBattle({ gameType: 'doubles' }, [[ @@ -51,6 +62,20 @@ describe('Heal Bell', () => { assert.equal(battle.p1.pokemon[0].status, ''); }); + it(`with Electrify should trigger an ally's Volt Absorb`, () => { + battle = common.createBattle({ gameType: 'doubles' }, [[ + { species: 'Raichu', ability: 'voltabsorb', moves: ['electrify'] }, + { species: 'Chansey', moves: ['healbell'] }, + ], [ + { species: 'Regieleki', moves: ['tackle'] }, + { species: 'Deoxys', moves: ['toxic'] }, + ]]); + battle.makeChoices('move electrify -2, move healbell', 'move tackle 1, move toxic 1'); + const voltAbsorb = battle.p1.pokemon[0]; + assert.equal(voltAbsorb.status, 'tox'); + assert.equal(voltAbsorb.maxhp - voltAbsorb.hp, Math.floor(voltAbsorb.maxhp / 16)); + }); + it(`in a Multi Battle, should heal the major status conditions of the ally's team`, () => { battle = common.createBattle({ gameType: 'multi' }, [[ { species: 'Machamp', ability: 'noguard', moves: ['poisongas'] },