From d9eb032d5f3185897624b6a63e907278a15d12e0 Mon Sep 17 00:00:00 2001 From: andrebastosdias Date: Sun, 22 Mar 2026 22:02:54 +0000 Subject: [PATCH 1/2] Fix interaction between Neutralizing Gas and Sheer Force --- data/abilities.ts | 10 +++++++++- data/items.ts | 3 +++ data/moves.ts | 14 ++++---------- sim/battle-actions.ts | 5 +++++ sim/dex-conditions.ts | 5 +++++ sim/dex-moves.ts | 7 +++++++ test/sim/abilities/sheerforce.js | 11 +++++++++++ test/sim/abilities/shielddust.js | 11 +++++++---- 8 files changed, 51 insertions(+), 15 deletions(-) diff --git a/data/abilities.ts b/data/abilities.ts index c54de73b98..58df376728 100644 --- a/data/abilities.ts +++ b/data/abilities.ts @@ -4114,7 +4114,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { num: 61, }, sheerforce: { - onModifyMove(move, pokemon) { + onModifyMove(move) { if (move.secondaries) { delete move.secondaries; // Technically not a secondary effect, but it is negated @@ -4128,6 +4128,10 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { onBasePower(basePower, pokemon, target, move) { if (move.hasSheerForce) return this.chainModify([5325, 4096]); }, + onModifyMovePhase2(move, source, target) { + ((this.effect as any).onModifyMove as (m: ActiveMove, p: Pokemon, p2: Pokemon) => void) + .call(this, move, source, target!); + }, flags: {}, name: "Sheer Force", rating: 3.5, @@ -4145,6 +4149,10 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { this.debug('Shield Dust prevent secondary'); return secondaries.filter(effect => !!effect.self); }, + onSourceModifyMovePhase2(move) { + this.debug('Shield Dust prevent secondary', move, move.numberTargets); + if (move.numberTargets === 1) move.dustproof = false; + }, flags: { breakable: 1 }, name: "Shield Dust", rating: 2, diff --git a/data/items.ts b/data/items.ts index b232f73d88..ac9ba79a82 100644 --- a/data/items.ts +++ b/data/items.ts @@ -1208,6 +1208,9 @@ export const Items: import('../sim/dex-items').ItemDataTable = { this.debug('Covert Cloak prevent secondary'); return secondaries.filter(effect => !!effect.self); }, + onSourceModifyMovePhase2(move) { + if (move.numberTargets === 1) move.dustproof = false; + }, num: 1885, gen: 9, }, diff --git a/data/moves.ts b/data/moves.ts index 1c23fa5522..278f382996 100644 --- a/data/moves.ts +++ b/data/moves.ts @@ -17998,21 +17998,15 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = { volatileStatus: 'sparklingaria', }, onAfterMove(source, target, move) { - if (source.fainted || !move.hitTargets || move.hasSheerForce) { - // make sure the volatiles are cleared - for (const pokemon of this.getAllActive()) delete pokemon.volatiles['sparklingaria']; - return; - } - const numberTargets = move.hitTargets.length; - for (const pokemon of move.hitTargets) { - // bypasses Shield Dust when hitting multiple targets - if (pokemon !== source && pokemon.isActive && (pokemon.removeVolatile('sparklingaria') || numberTargets > 1) && - pokemon.status === 'brn') { + if (source.fainted) return; + for (const pokemon of this.getAllActive()) { + if ((pokemon.removeVolatile('sparklingaria') || move.dustproof) && pokemon.status === 'brn') { pokemon.cureStatus(); } } }, target: "allAdjacent", + dustproof: true, type: "Water", contestType: "Tough", }, diff --git a/sim/battle-actions.ts b/sim/battle-actions.ts index 8c8456a66e..905cd72e93 100644 --- a/sim/battle-actions.ts +++ b/sim/battle-actions.ts @@ -804,6 +804,10 @@ export class BattleActions { return undefined; } afterMoveSecondaryEvent(targets: Pokemon[], pokemon: Pokemon, move: ActiveMove) { + for (const target of targets) { + this.battle.runEvent('ModifyMovePhase2', pokemon, target, move, move); + } + // console.log(`${targets}, ${pokemon}, ${move}`) if (!(move.hasSheerForce && pokemon.hasAbility('sheerforce'))) { this.battle.singleEvent('AfterMoveSecondary', move, null, targets[0], pokemon, move); @@ -1108,6 +1112,7 @@ export class BattleActions { for (const i of targets.keys()) { if (!damage[i] && damage[i] !== 0) targets[i] = false; } + move.numberTargets = damage.filter(val => typeof val === 'number').length; // steps 4 and 5 can mess with this.battle.activeTarget, which needs to be preserved for Dancer const activeTarget = this.battle.activeTarget; diff --git a/sim/dex-conditions.ts b/sim/dex-conditions.ts index f900795ad6..baeb2543d6 100644 --- a/sim/dex-conditions.ts +++ b/sim/dex-conditions.ts @@ -65,6 +65,7 @@ export interface EventMethods { onModifyDamage?: CommonHandlers['ModifierSourceMove']; onModifyDef?: CommonHandlers['ModifierMove']; onModifyMove?: MoveEventMethods['onModifyMove']; + onModifyMovePhase2?: MoveEventMethods['onModifyMove']; onModifyPriority?: CommonHandlers['ModifierSourceMove']; onModifySecondaries?: ( this: Battle, secondaries: SecondaryEffect[], target: Pokemon, source: Pokemon, move: ActiveMove @@ -169,6 +170,7 @@ export interface EventMethods { onFoeModifyDamage?: CommonHandlers['ModifierSourceMove']; onFoeModifyDef?: CommonHandlers['ModifierMove']; onFoeModifyMove?: MoveEventMethods['onModifyMove']; + onFoeModifyMovePhase2?: MoveEventMethods['onModifyMove']; onFoeModifyPriority?: CommonHandlers['ModifierSourceMove']; onFoeModifySecondaries?: ( this: Battle, secondaries: SecondaryEffect[], target: Pokemon, source: Pokemon, move: ActiveMove @@ -267,6 +269,7 @@ export interface EventMethods { onSourceModifyDamage?: CommonHandlers['ModifierSourceMove']; onSourceModifyDef?: CommonHandlers['ModifierMove']; onSourceModifyMove?: MoveEventMethods['onModifyMove']; + onSourceModifyMovePhase2?: MoveEventMethods['onModifyMove']; onSourceModifyPriority?: CommonHandlers['ModifierSourceMove']; onSourceModifySecondaries?: ( this: Battle, secondaries: SecondaryEffect[], target: Pokemon, source: Pokemon, move: ActiveMove @@ -369,6 +372,7 @@ export interface EventMethods { onAnyModifyDamage?: CommonHandlers['ModifierSourceMove']; onAnyModifyDef?: CommonHandlers['ModifierMove']; onAnyModifyMove?: MoveEventMethods['onModifyMove']; + onAnyModifyMovePhase2?: MoveEventMethods['onModifyMove']; onAnyModifyPriority?: CommonHandlers['ModifierSourceMove']; onAnyModifySecondaries?: ( this: Battle, secondaries: SecondaryEffect[], target: Pokemon, source: Pokemon, move: ActiveMove @@ -541,6 +545,7 @@ export interface PokemonEventMethods extends EventMethods { onAllyModifyDamage?: CommonHandlers['ModifierSourceMove']; onAllyModifyDef?: CommonHandlers['ModifierMove']; onAllyModifyMove?: MoveEventMethods['onModifyMove']; + onAllyModifyMovePhase2?: MoveEventMethods['onModifyMove']; onAllyModifyPriority?: CommonHandlers['ModifierSourceMove']; onAllyModifySecondaries?: ( this: Battle, secondaries: SecondaryEffect[], target: Pokemon, source: Pokemon, move: ActiveMove diff --git a/sim/dex-moves.ts b/sim/dex-moves.ts index 3fd3be5763..3e7488c47b 100644 --- a/sim/dex-moves.ts +++ b/sim/dex-moves.ts @@ -211,6 +211,7 @@ export interface MoveData extends EffectData, MoveEventMethods, HitEffect { secondaries?: SecondaryEffect[] | null; self?: SecondaryEffect | null; hasSheerForce?: boolean; + dustproof?: boolean; // Hit effect modifiers // -------------------- @@ -313,6 +314,7 @@ export interface ActiveMove extends MutableMove { status?: ID; hit: number; moveHitData?: MoveHitData; + numberTargets?: number; hitTargets?: Pokemon[]; ability?: Ability; allies?: Pokemon[]; @@ -388,6 +390,10 @@ export class DataMove extends BasicEffect implements Readonly { battle.makeChoices('move flamethrower', 'auto'); assert.equal(frozenMon.status, ''); }); + + it('should suppress Life Orb recoil after knocking out a Neutralizing Gas user', () => { + battle = common.createBattle([[ + { species: 'landorus', ability: 'sheerforce', item: 'lifeorb', moves: ['earthpower'] }, + ], [ + { species: 'weezing', ability: 'neutralizinggas', moves: ['sleeptalk'] }, + { species: 'wynaut', ability: 'neutralizinggas', moves: ['sleeptalk'] }, + ]]); + battle.makeChoices(); + assert.fullHP(battle.p1.active[0]); + }); }); diff --git a/test/sim/abilities/shielddust.js b/test/sim/abilities/shielddust.js index 61c27bf0c7..19d879f710 100644 --- a/test/sim/abilities/shielddust.js +++ b/test/sim/abilities/shielddust.js @@ -11,10 +11,13 @@ describe('Shield Dust', () => { }); it('should block secondary effects against the user', () => { - battle = common.createBattle({ gameType: 'doubles' }, [ - [{ species: 'Latios', ability: 'noguard', moves: ['snarl'] }, { species: 'Latias', ability: 'levitate', moves: ['roost'] }], - [{ species: 'Xerneas', ability: 'shielddust', moves: ['roost'] }, { species: 'Yveltal', ability: 'pressure', moves: ['roost'] }], - ]); + battle = common.createBattle({ gameType: 'doubles' }, [[ + { species: 'Latios', ability: 'noguard', moves: ['snarl'] }, + { species: 'Latias', ability: 'levitate', moves: ['roost'] }, + ], [ + { species: 'Xerneas', ability: 'shielddust', moves: ['roost'] }, + { species: 'Yveltal', ability: 'pressure', moves: ['roost'] }, + ]]); battle.makeChoices('move snarl, move roost', 'move roost, move roost'); assert.statStage(battle.p2.active[0], 'spa', 0); assert.statStage(battle.p2.active[1], 'spa', -1); From f6b7829b5ba24dd301c9eaea5f6799325f0fbe83 Mon Sep 17 00:00:00 2001 From: andrebastosdias Date: Sun, 22 Mar 2026 22:05:40 +0000 Subject: [PATCH 2/2] Remove debug --- data/abilities.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/data/abilities.ts b/data/abilities.ts index 58df376728..2061e2ddb2 100644 --- a/data/abilities.ts +++ b/data/abilities.ts @@ -4150,7 +4150,6 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { return secondaries.filter(effect => !!effect.self); }, onSourceModifyMovePhase2(move) { - this.debug('Shield Dust prevent secondary', move, move.numberTargets); if (move.numberTargets === 1) move.dustproof = false; }, flags: { breakable: 1 },