Implement Mega Sol and Spicy Spray in the base mod (#12017)
Some checks failed
Node.js CI / build (18.x) (push) Has been cancelled

* Implement Mega Sol and Spicy Spray in the base mod

* Remove empty line

* Refactor check in Growth

* Fix
This commit is contained in:
André Bastos Dias 2026-05-13 17:43:39 +01:00 committed by GitHub
parent 537ee47903
commit e1290bb1fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 75 additions and 150 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -495,6 +495,7 @@ export interface EventMethods {
onTryMovePriority?: number;
onTryPrimaryHitPriority?: number;
onTypePriority?: number;
onWeatherModifyDamagePriority?: number;
}
export interface PokemonEventMethods extends EventMethods {

View File

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

View File

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

View File

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