mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-04-24 15:00:11 -05:00
For side conditions, `onStart`/`onRestart`/`onResidual`/`onEnd` have been renamed `onSideStart`/`onSideRestart`/`onSideResidual`/`onSideEnd`, with the `onResidualOrder` properties renamed `onSideResidualOrder`. For field conditions, `onStart`/`onRestart`/`onResidual`/`onEnd` have been renamed `onFieldStart`/`onFieldRestart`/`onFieldResidual`/`onFieldEnd`, with the `onResidualOrder` properties renamed `onFieldResidualOrder`. (The `onField` and `onSide` part helps make it clear to the type system that the first argument is a Field or Side, not a Pokemon.) Side and field conditions can now use `onResidual` to tick separately on each pokemon in Speed order. `onResidualOrder` (the per-pokemon tick) can be timed separate from `onSideResidualOrder` (the per-condition tick), allowing conditions to end at a different priority than they tick per-pokemon. Relatedly, `onTeamPreview` and `onStart` in formats now need to be `onFieldTeamPreview` and `onFieldStart`. Unrelatedly, `effectData` has been renamed `effectState`, and the corresponding state containers (`pokemon.statusData`, `pokemon.speciesData`, `pokemon.itemData`, `pokemon.abilityData`, `field.weatherData`, `field.terrainData`) have been similarly renamed. I renamed the types a while ago, but I was holding off renaming the fields because it would be a breaking change. But this is a breaking change anyway, so we might as well do it now. Note: `onResidual` will tick even on `onSideEnd` turns, although `onSideResidual` won't. When refactoring weather, remember to check `this.state.duration` so you don't deal weather damage on the ending turn. Intended as a better fix for #8216
101 lines
3.3 KiB
TypeScript
101 lines
3.3 KiB
TypeScript
export const Moves: {[k: string]: ModdedMoveData} = {
|
|
attract: {
|
|
inherit: true,
|
|
condition: {
|
|
noCopy: true, // doesn't get copied by Baton Pass
|
|
onStart(pokemon, source, effect) {
|
|
if (!(pokemon.gender === 'M' && source.gender === 'F') && !(pokemon.gender === 'F' && source.gender === 'M')) {
|
|
this.debug('incompatible gender');
|
|
return false;
|
|
}
|
|
if (!this.runEvent('Attract', pokemon, source)) {
|
|
this.debug('Attract event failed');
|
|
return false;
|
|
}
|
|
|
|
if (effect.id === 'cutecharm' || effect.id === 'ability:cutecharm') {
|
|
this.add('-start', pokemon, 'Attract', '[from] ability: Cute Charm', '[of] ' + source);
|
|
} else if (effect.id === 'destinyknot') {
|
|
this.add('-start', pokemon, 'Attract', '[from] item: Destiny Knot', '[of] ' + source);
|
|
} else {
|
|
this.add('-start', pokemon, 'Attract');
|
|
}
|
|
},
|
|
onUpdate(pokemon) {
|
|
if (this.effectState.source && !this.effectState.source.isActive && pokemon.volatiles['attract']) {
|
|
this.debug('Removing Attract volatile on ' + pokemon);
|
|
pokemon.removeVolatile('attract');
|
|
}
|
|
},
|
|
onBeforeMovePriority: 2,
|
|
onBeforeMove(pokemon, target, move) {
|
|
this.add('-activate', pokemon, 'move: Attract', '[of] ' + this.effectState.source);
|
|
if (this.randomChance(1, 2)) {
|
|
this.add('cant', pokemon, 'Attract');
|
|
return false;
|
|
}
|
|
},
|
|
onEnd(pokemon) {
|
|
this.add('-end', pokemon, 'Attract', '[silent]');
|
|
},
|
|
},
|
|
},
|
|
gastroacid: {
|
|
inherit: true,
|
|
condition: {
|
|
// Ability suppression implemented in Pokemon.ignoringAbility() within sim/pokemon.js
|
|
onStart(pokemon) {
|
|
this.add('-endability', pokemon);
|
|
this.singleEvent('End', pokemon.getAbility(), pokemon.abilityState, pokemon, pokemon, 'gastroacid');
|
|
const keys = Object.keys(pokemon.volatiles).filter(x => x.startsWith("ability:"));
|
|
if (keys.length) {
|
|
for (const abil of keys) {
|
|
pokemon.removeVolatile(abil);
|
|
}
|
|
}
|
|
},
|
|
},
|
|
},
|
|
safeguard: {
|
|
inherit: true,
|
|
condition: {
|
|
duration: 5,
|
|
durationCallback(target, source, effect) {
|
|
if (source?.hasAbility('persistent')) {
|
|
this.add('-activate', source, 'ability: Persistent', effect);
|
|
return 7;
|
|
}
|
|
return 5;
|
|
},
|
|
onSetStatus(status, target, source, effect) {
|
|
if (!effect || !source) return;
|
|
if (effect.id === 'yawn') return;
|
|
if (effect.effectType === 'Move' && effect.infiltrates && !target.isAlly(source)) return;
|
|
if (target !== source) {
|
|
this.debug('interrupting setStatus');
|
|
if (effect.id.endsWith('synchronize') || (effect.effectType === 'Move' && !effect.secondaries)) {
|
|
this.add('-activate', target, 'move: Safeguard');
|
|
}
|
|
return null;
|
|
}
|
|
},
|
|
onTryAddVolatile(status, target, source, effect) {
|
|
if (!effect || !source) return;
|
|
if (effect.effectType === 'Move' && effect.infiltrates && !target.isAlly(source)) return;
|
|
if ((status.id === 'confusion' || status.id === 'yawn') && target !== source) {
|
|
if (effect.effectType === 'Move' && !effect.secondaries) this.add('-activate', target, 'move: Safeguard');
|
|
return null;
|
|
}
|
|
},
|
|
onSideStart(side) {
|
|
this.add('-sidestart', side, 'Safeguard');
|
|
},
|
|
onSideResidualOrder: 21,
|
|
onSideResidualSubOrder: 2,
|
|
onSideEnd(side) {
|
|
this.add('-sideend', side, 'Safeguard');
|
|
},
|
|
},
|
|
},
|
|
};
|