diff --git a/config/formats.ts b/config/formats.ts index e8357c7bd0..3320785d33 100644 --- a/config/formats.ts +++ b/config/formats.ts @@ -2683,7 +2683,7 @@ export const Formats: import('../sim/dex-formats').FormatList = [ } // weather modifier - baseDamage = this.battle.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); + baseDamage = this.battle.priorityEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); // crit - not a modifier const isCrit = target.getMoveHitData(move).crit; diff --git a/data/abilities.ts b/data/abilities.ts index f3b8742654..2bbe4b3de8 100644 --- a/data/abilities.ts +++ b/data/abilities.ts @@ -2512,11 +2512,11 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { }, megasol: { isNonstandard: "Future", + onWeatherModifyDamagePriority: 1, onWeatherModifyDamage(damage, attacker, defender, move) { - if (this.field.weather !== 'sunnyday') { - (this.dex.conditions.getByID('sunnyday' as ID) as any).onWeatherModifyDamage - .call(this, damage, attacker, defender, move); - } + (this.dex.conditions.getByID('sunnyday' as ID) as any).onWeatherModifyDamage + .call(this, damage, attacker, defender, move); + return damage; // fast exit from event }, flags: {}, name: "Mega Sol", @@ -4421,7 +4421,9 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { spicyspray: { isNonstandard: "Future", onDamagingHit(damage, target, source, move) { - source.trySetStatus('brn', target); + if (!source.trySetStatus('brn', target) && !source.status && source.hasType('Fire')) { + this.add('-immune', source); + } }, flags: {}, name: "Spicy Spray", diff --git a/data/conditions.ts b/data/conditions.ts index 9d6ee15ca2..b6c590395c 100644 --- a/data/conditions.ts +++ b/data/conditions.ts @@ -639,7 +639,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = { // So we give it increased priority. onModifySpDPriority: 10, onModifySpD(spd, pokemon) { - if (pokemon.hasType('Rock') && this.field.isWeather('sandstorm')) { + if (pokemon.hasType('Rock') && pokemon.effectiveWeather() === 'sandstorm') { return this.modify(spd, 1.5); } }, @@ -705,7 +705,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = { }, onModifyDefPriority: 10, onModifyDef(def, pokemon) { - if (pokemon.hasType('Ice') && this.field.isWeather('snowscape')) { + if (pokemon.hasType('Ice') && pokemon.effectiveWeather() === 'snowscape') { return this.modify(def, 1.5); } }, diff --git a/data/mods/afd/scripts.ts b/data/mods/afd/scripts.ts index 362e7c2185..e1f86878e8 100644 --- a/data/mods/afd/scripts.ts +++ b/data/mods/afd/scripts.ts @@ -374,7 +374,7 @@ export const Scripts: ModdedBattleScriptsData = { } // weather modifier - baseDamage = this.battle.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); + baseDamage = this.battle.priorityEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); // crit - not a modifier const isCrit = target.getMoveHitData(move).crit; diff --git a/data/mods/ccapm2025/scripts.ts b/data/mods/ccapm2025/scripts.ts index 907f2c2386..3b60b64d8f 100644 --- a/data/mods/ccapm2025/scripts.ts +++ b/data/mods/ccapm2025/scripts.ts @@ -120,7 +120,7 @@ export const Scripts: ModdedBattleScriptsData = { } // weather modifier - baseDamage = this.battle.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); + baseDamage = this.battle.priorityEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); // crit - not a modifier const isCrit = target.getMoveHitData(move).crit; diff --git a/data/mods/champions/abilities.ts b/data/mods/champions/abilities.ts index 43d6bca57f..4f6d151f65 100644 --- a/data/mods/champions/abilities.ts +++ b/data/mods/champions/abilities.ts @@ -77,12 +77,6 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa spicyspray: { inherit: true, isNonstandard: null, - onDamagingHit(damage, target, source, move) { - // this is only in the mod folder because it is weird like Dire Claw - if (!source.trySetStatus('brn', target) && !source.status && source.hasType('Fire')) { - this.add('-immune', source); - } - }, }, unseenfist: { onModifyMove: undefined, // no inherit diff --git a/data/mods/champions/conditions.ts b/data/mods/champions/conditions.ts index cb5f1ff2a9..bd03a55294 100644 --- a/data/mods/champions/conditions.ts +++ b/data/mods/champions/conditions.ts @@ -54,90 +54,4 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa return false; }, }, - - /** - * If Utility Umbrella continues to work as in previous gens and Mega Sol continues to bypass defensive - * weather boosts, the best implementation is: - * - run WeatherModifyDamage with `fastExit` - * - give WeatherModifyDamagePriority to Mega Sol - * - delete the weather conditions below - */ - raindance: { - inherit: true, - onWeatherModifyDamage(damage, attacker, defender, move) { - if (attacker.effectiveWeather() !== 'raindance') return; - if (move.type === 'Water') { - this.debug('rain water boost'); - return this.chainModify(1.5); - } - if (move.type === 'Fire') { - this.debug('rain fire suppress'); - return this.chainModify(0.5); - } - }, - }, - primordialsea: { - inherit: true, - onWeatherModifyDamage(damage, attacker, defender, move) { - if (attacker.effectiveWeather() !== 'primordialsea') return; - if (move.type === 'Water') { - this.debug('Rain water boost'); - return this.chainModify(1.5); - } - }, - }, - sunnyday: { - inherit: true, - onWeatherModifyDamage(damage, attacker, defender, move) { - if (attacker.effectiveWeather() !== 'sunnyday') return; - if (move.id === 'hydrosteam') { - this.debug('Sunny Day Hydro Steam boost'); - return this.chainModify(1.5); - } - if (move.type === 'Fire') { - this.debug('Sunny Day fire boost'); - return this.chainModify(1.5); - } - if (move.type === 'Water') { - this.debug('Sunny Day water suppress'); - return this.chainModify(0.5); - } - }, - }, - desolateland: { - inherit: true, - onWeatherModifyDamage(damage, attacker, defender, move) { - if (attacker.effectiveWeather() !== 'desolateland') return; - if (move.type === 'Fire') { - this.debug('Desolate Land fire boost'); - return this.chainModify(1.5); - } - }, - }, - sandstorm: { - inherit: true, - onModifySpD(spd, target, source) { - if (target.hasType('Rock') && source.effectiveWeather() === 'sandstorm') { - return this.modify(spd, 1.5); - } - }, - }, - snowscape: { - inherit: true, - onModifyDef(def, target, source) { - if (target.hasType('Ice') && source.effectiveWeather() === 'snowscape') { - return this.modify(def, 1.5); - } - }, - }, - // TODO: check Mega Sol's interaction with Deltastream - // deltastream: { - // inherit: true, - // onEffectiveness(typeMod, target, type, move) { - // if (move && move.effectType === 'Move' && move.category !== 'Status' && type === 'Flying' && typeMod > 0) { - // this.add('-fieldactivate', 'Delta Stream'); - // return 0; - // } - // }, - // }, }; diff --git a/data/mods/champions/moves.ts b/data/mods/champions/moves.ts index 5487f67846..cbc2f89c69 100644 --- a/data/mods/champions/moves.ts +++ b/data/mods/champions/moves.ts @@ -116,16 +116,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = { inherit: true, isNonstandard: null, }, - ceaselessedge: { - inherit: true, - onAfterHit(target, source, move) { - if (!move.hasSheerForce && source.hp) { - for (const side of source.side.foeSidesWithConditions()) { - side.addSideCondition('spikes'); - } - } - }, - }, celebrate: { inherit: true, isNonstandard: "Past", @@ -470,14 +460,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = { }, growth: { inherit: true, - onModifyMove(move, pokemon) { - if (pokemon.hasAbility('megasol') && this.field.weather !== 'sunnyday') { - // TODO: check in future patches - delete move.boosts; - } else if (['sunnyday', 'desolateland'].includes(pokemon.effectiveWeather())) { - move.boosts = { atk: 2, spa: 2 }; - } - }, type: "Grass", }, gust: { @@ -987,16 +969,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = { inherit: true, isNonstandard: "Past", }, - stoneaxe: { - inherit: true, - onAfterHit(target, source, move) { - if (!move.hasSheerForce && source.hp) { - for (const side of source.side.foeSidesWithConditions()) { - side.addSideCondition('stealthrock'); - } - } - }, - }, stormthrow: { inherit: true, isNonstandard: null, diff --git a/data/mods/champions/scripts.ts b/data/mods/champions/scripts.ts index 5927146d95..78b37ae99d 100644 --- a/data/mods/champions/scripts.ts +++ b/data/mods/champions/scripts.ts @@ -148,7 +148,7 @@ export const Scripts: ModdedBattleScriptsData = { } // weather modifier - baseDamage = this.battle.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); + baseDamage = this.battle.priorityEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); // crit - not a modifier const isCrit = target.getMoveHitData(move).crit; diff --git a/data/mods/gen3/scripts.ts b/data/mods/gen3/scripts.ts index de3150b0fd..75d9dd50ee 100644 --- a/data/mods/gen3/scripts.ts +++ b/data/mods/gen3/scripts.ts @@ -55,7 +55,7 @@ export const Scripts: ModdedBattleScriptsData = { } // Weather - baseDamage = this.battle.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); + baseDamage = this.battle.priorityEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); if (move.category === 'Physical' && !Math.floor(baseDamage)) { baseDamage = 1; diff --git a/data/mods/gen4/scripts.ts b/data/mods/gen4/scripts.ts index 2fc269a6c0..c28929177d 100644 --- a/data/mods/gen4/scripts.ts +++ b/data/mods/gen4/scripts.ts @@ -74,7 +74,7 @@ export const Scripts: ModdedBattleScriptsData = { } // Weather - baseDamage = this.battle.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); + baseDamage = this.battle.priorityEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); baseDamage += 2; diff --git a/data/mods/gen9ssb/scripts.ts b/data/mods/gen9ssb/scripts.ts index b146c1d0f3..6c93e1040c 100644 --- a/data/mods/gen9ssb/scripts.ts +++ b/data/mods/gen9ssb/scripts.ts @@ -688,7 +688,7 @@ export const Scripts: ModdedBattleScriptsData = { } // weather modifier - baseDamage = this.battle.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); + baseDamage = this.battle.priorityEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); // crit - not a modifier const isCrit = target.getMoveHitData(move).crit; diff --git a/data/moves.ts b/data/moves.ts index eba5db2bea..67fc257f2d 100644 --- a/data/moves.ts +++ b/data/moves.ts @@ -2227,7 +2227,7 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = { priority: 0, flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 }, onAfterHit(target, source, move) { - if (!move.hasSheerForce) { + if (!move.hasSheerForce && source.hp) { for (const side of source.side.foeSidesWithConditions()) { side.addSideCondition('spikes'); } @@ -7867,7 +7867,11 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = { priority: 0, flags: { snatch: 1, metronome: 1 }, onModifyMove(move, pokemon) { - if (['sunnyday', 'desolateland'].includes(pokemon.effectiveWeather())) move.boosts = { atk: 2, spa: 2 }; + if (pokemon.hasAbility('megasol') && !this.field.isWeather('sunnyday')) { + delete move.boosts; + } else if (['sunnyday', 'desolateland'].includes(pokemon.effectiveWeather())) { + move.boosts = { atk: 2, spa: 2 }; + } }, boosts: { atk: 1, @@ -12252,7 +12256,7 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = { flags: { snatch: 1, heal: 1, metronome: 1 }, onHit(pokemon) { let factor = 0.5; - switch (pokemon.effectiveWeather(true)) { + switch (pokemon.effectiveWeather(undefined, true)) { case 'sunnyday': case 'desolateland': factor = 0.667; @@ -12288,7 +12292,7 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = { flags: { snatch: 1, heal: 1, metronome: 1 }, onHit(pokemon) { let factor = 0.5; - switch (pokemon.effectiveWeather(true)) { + switch (pokemon.effectiveWeather(undefined, true)) { case 'sunnyday': case 'desolateland': factor = 0.667; @@ -17236,7 +17240,7 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = { return; } this.add('-prepare', attacker, move.name); - if (['sunnyday', 'desolateland'].includes(attacker.effectiveWeather(true))) { + if (['sunnyday', 'desolateland'].includes(attacker.effectiveWeather(undefined, true))) { this.attrLastMove('[still]'); this.addMove('-anim', attacker, move.name, defender); return; @@ -17272,7 +17276,7 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = { return; } this.add('-prepare', attacker, move.name); - if (['sunnyday', 'desolateland'].includes(attacker.effectiveWeather(true))) { + if (['sunnyday', 'desolateland'].includes(attacker.effectiveWeather(undefined, true))) { this.attrLastMove('[still]'); this.addMove('-anim', attacker, move.name, defender); return; @@ -18082,7 +18086,7 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = { priority: 0, flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 }, onAfterHit(target, source, move) { - if (!move.hasSheerForce) { + if (!move.hasSheerForce && source.hp) { for (const side of source.side.foeSidesWithConditions()) { side.addSideCondition('stealthrock'); } @@ -18739,7 +18743,7 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = { flags: { snatch: 1, heal: 1, metronome: 1 }, onHit(pokemon) { let factor = 0.5; - switch (pokemon.effectiveWeather(true)) { + switch (pokemon.effectiveWeather(undefined, true)) { case 'sunnyday': case 'desolateland': factor = 0.667; diff --git a/sim/battle-actions.ts b/sim/battle-actions.ts index 911033d665..66fd617e6d 100644 --- a/sim/battle-actions.ts +++ b/sim/battle-actions.ts @@ -1747,7 +1747,7 @@ export class BattleActions { } // weather modifier - baseDamage = this.battle.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); + baseDamage = this.battle.priorityEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); // crit - not a modifier const isCrit = target.getMoveHitData(move).crit; diff --git a/sim/dex-conditions.ts b/sim/dex-conditions.ts index 4fd867aaf0..9445d46083 100644 --- a/sim/dex-conditions.ts +++ b/sim/dex-conditions.ts @@ -495,6 +495,7 @@ export interface EventMethods { onTryMovePriority?: number; onTryPrimaryHitPriority?: number; onTypePriority?: number; + onWeatherModifyDamagePriority?: number; } export interface PokemonEventMethods extends EventMethods { diff --git a/sim/global-types.ts b/sim/global-types.ts index eb3a221177..671cc28889 100644 --- a/sim/global-types.ts +++ b/sim/global-types.ts @@ -279,7 +279,7 @@ interface ModdedBattlePokemon { this: Pokemon, move: string | Move, amount?: number | null, target?: Pokemon | null | false ) => number; eatItem?: (this: Pokemon, force?: boolean, source?: Pokemon, sourceEffect?: Effect) => boolean; - effectiveWeather?: (this: Pokemon, message?: string | boolean) => ID; + effectiveWeather?: (this: Pokemon, sourceEffect?: Effect, message?: string | boolean) => ID; formeChange?: ( this: Pokemon, speciesId: string | Species, source: Effect, isPermanent?: boolean, abilitySlot?: string, message?: string, diff --git a/sim/pokemon.ts b/sim/pokemon.ts index 2803eb6ef7..1a8969e6c3 100644 --- a/sim/pokemon.ts +++ b/sim/pokemon.ts @@ -2187,8 +2187,15 @@ export class Pokemon { * Like Field.effectiveWeather(), but ignores sun and rain if * the Utility Umbrella is active for the Pokemon. */ - effectiveWeather(message?: string | boolean) { + effectiveWeather(sourceEffect?: Effect, message?: string | boolean) { + if (!sourceEffect && this.battle.effect) sourceEffect = this.battle.effect; const weather = this.battle.field.effectiveWeather(); + if (this.battle.activePokemon?.hasAbility('megasol') && sourceEffect && + (sourceEffect.id === 'megasol' || sourceEffect.effectType === 'Move' || sourceEffect.effectType === 'Weather') && + sourceEffect.id !== 'electroshot') { + if (weather !== 'sunnyday' && message) this.battle.add('-activate', this, 'ability: Mega Sol'); + return 'sunnyday' as ID; + } switch (weather) { case 'sunnyday': case 'raindance': @@ -2196,11 +2203,6 @@ export class Pokemon { case 'primordialsea': if (this.hasItem('utilityumbrella')) return ''; } - // TODO: check interactions of Mega Sol with Utility Umbrella and Desolate Land - if (this.hasAbility('megasol') && this.battle.activePokemon === this && weather !== 'sunnyday') { - if (message) this.battle.add('-activate', this, 'ability: Mega Sol'); - return 'sunnyday' as ID; - } return weather; } diff --git a/test/sim/abilities/megasol.js b/test/sim/abilities/megasol.js new file mode 100644 index 0000000000..e2530c4c51 --- /dev/null +++ b/test/sim/abilities/megasol.js @@ -0,0 +1,36 @@ +'use strict'; + +const assert = require('./../../assert'); +const common = require('./../../common'); + +let battle; + +describe('Mega Sol', () => { + afterEach(() => { + battle.destroy(); + }); + + it('should apply Sunny Day damage boosts', () => { + battle = common.createBattle([[ + { species: "Meganium", item: 'meganiumite', moves: ['weatherball'] }, + ], [ + { species: "Pelipper", ability: 'drizzle', moves: ['sleeptalk'] }, + ]]); + + battle.makeChoices('move weatherball mega', 'move sleeptalk'); + const pelipper = battle.p2.active[0]; + assert.bounded(pelipper.maxhp - pelipper.hp, [98, 116]); + }); + + it('should bypass weather defensive boosts', () => { + battle = common.createBattle([[ + { species: "Meganium", item: 'meganiumite', moves: ['weatherball'] }, + ], [ + { species: "Tyranitar", item: 'tyranitarite', moves: ['sleeptalk'] }, + ]]); + + battle.makeChoices('move weatherball mega', 'move sleeptalk mega'); + const tyranitar = battle.p2.active[0]; + assert.bounded(tyranitar.maxhp - tyranitar.hp, [63, 75]); + }); +});