pokemon-showdown/data/mods/sharedpower/moves.ts
Guangcong Luo f9fdc73133
Support per-pokemon Residual handlers in Side/Field conditions (#8222)
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
2021-04-25 10:55:54 -07:00

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