This commit is contained in:
André Bastos Dias 2026-06-02 21:52:51 -05:00 committed by GitHub
commit 1f4866f9a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 147 additions and 73 deletions

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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",

View File

@ -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);
}
}

View File

@ -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

View File

@ -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, '');
});
});
});

View File

@ -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'] },