mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-05-17 10:46:53 -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
131 lines
4.3 KiB
TypeScript
131 lines
4.3 KiB
TypeScript
/**
|
|
* The japanese version of Blizzard in Gen 1 had a 30% chance to freeze
|
|
*/
|
|
|
|
export const Moves: {[k: string]: ModdedMoveData} = {
|
|
blizzard: {
|
|
inherit: true,
|
|
secondary: {
|
|
chance: 30,
|
|
status: 'frz',
|
|
},
|
|
},
|
|
dig: {
|
|
inherit: true,
|
|
condition: {
|
|
duration: 2,
|
|
onLockMove: 'dig',
|
|
onInvulnerability(target, source, move) {
|
|
if (move.id === 'swift' && target.volatiles['substitute']) return true;
|
|
this.add('-message', `The foe ${target.name} can't be hit underground!`);
|
|
return false;
|
|
},
|
|
onDamage(damage, target, source, move) {
|
|
if (!move || move.effectType !== 'Move') return;
|
|
if (!source) return;
|
|
if (move.id === 'earthquake') {
|
|
this.add('-message', `The foe ${target.name} can't be hit underground!`);
|
|
return null;
|
|
}
|
|
},
|
|
},
|
|
},
|
|
fly: {
|
|
inherit: true,
|
|
condition: {
|
|
duration: 2,
|
|
onLockMove: 'fly',
|
|
onInvulnerability(target, source, move) {
|
|
if (move.id === 'swift' && target.volatiles['substitute']) return true;
|
|
this.add('-message', `The foe ${target.name} can't be hit while flying!`);
|
|
return false;
|
|
},
|
|
onDamage(damage, target, source, move) {
|
|
if (!move || move.effectType !== 'Move') return;
|
|
if (!source || source.side === target.side) return;
|
|
if (move.id === 'gust' || move.id === 'thunder') {
|
|
this.add('-message', `The foe ${target.name} can't be hit while flying!`);
|
|
return null;
|
|
}
|
|
},
|
|
},
|
|
},
|
|
substitute: {
|
|
inherit: true,
|
|
condition: {
|
|
onStart(target) {
|
|
this.add('-start', target, 'Substitute');
|
|
this.effectState.hp = Math.floor(target.maxhp / 4) + 1;
|
|
delete target.volatiles['partiallytrapped'];
|
|
},
|
|
onTryHitPriority: -1,
|
|
onTryHit(target, source, move) {
|
|
if (move.drain) {
|
|
this.add('-miss', source);
|
|
this.hint("In the Japanese versions of Gen 1, draining moves always miss against substitutes.");
|
|
return null;
|
|
}
|
|
if (move.category === 'Status') {
|
|
// In gen 1 it only blocks:
|
|
// poison, confusion, secondary effect confusion, stat reducing moves and Leech Seed.
|
|
const subBlocked = ['lockon', 'meanlook', 'mindreader', 'nightmare'];
|
|
if ((move.status && ['psn', 'tox'].includes(move.status)) || (move.boosts && target !== source) ||
|
|
move.volatileStatus === 'confusion' || subBlocked.includes(move.id)) {
|
|
return false;
|
|
}
|
|
return;
|
|
}
|
|
if (move.volatileStatus && target === source) return;
|
|
// NOTE: In future generations the damage is capped to the remaining HP of the
|
|
// Substitute, here we deliberately use the uncapped damage when tracking lastDamage etc.
|
|
// Also, multi-hit moves must always deal the same damage as the first hit for any subsequent hits
|
|
let uncappedDamage = move.hit > 1 ? source.lastDamage : this.actions.getDamage(source, target, move);
|
|
if (!uncappedDamage) return null;
|
|
uncappedDamage = this.runEvent('SubDamage', target, source, move, uncappedDamage);
|
|
if (!uncappedDamage) return uncappedDamage;
|
|
source.lastDamage = uncappedDamage;
|
|
target.volatiles['substitute'].hp -= uncappedDamage > target.volatiles['substitute'].hp ?
|
|
target.volatiles['substitute'].hp : uncappedDamage;
|
|
if (target.volatiles['substitute'].hp <= 0) {
|
|
target.removeVolatile('substitute');
|
|
target.subFainted = true;
|
|
} else {
|
|
this.add('-activate', target, 'Substitute', '[damage]');
|
|
}
|
|
// Drain/recoil does not happen if the substitute breaks
|
|
if (target.volatiles['substitute']) {
|
|
if (move.recoil) {
|
|
this.damage(Math.round(uncappedDamage * move.recoil[0] / move.recoil[1]), source, target, 'recoil');
|
|
}
|
|
if (move.drain) {
|
|
this.heal(Math.ceil(uncappedDamage * move.drain[0] / move.drain[1]), source, target, 'drain');
|
|
}
|
|
}
|
|
this.runEvent('AfterSubDamage', target, source, move, uncappedDamage);
|
|
// Add here counter damage
|
|
const lastAttackedBy = target.getLastAttackedBy();
|
|
if (!lastAttackedBy) {
|
|
target.attackedBy.push({source: source, move: move.id, damage: uncappedDamage, thisTurn: true, slot: source.getSlot()});
|
|
} else {
|
|
lastAttackedBy.move = move.id;
|
|
lastAttackedBy.damage = uncappedDamage;
|
|
}
|
|
return 0;
|
|
},
|
|
onAccuracy(accuracy, target, source, move) {
|
|
if (move.id === 'swift') {
|
|
return true;
|
|
}
|
|
return accuracy;
|
|
},
|
|
onEnd(target) {
|
|
this.add('-end', target, 'Substitute');
|
|
},
|
|
},
|
|
},
|
|
swift: {
|
|
inherit: true,
|
|
accuracy: 100,
|
|
},
|
|
};
|