mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-05-21 06:37:09 -05:00
While a pokemon is dynamaxed, they can use any of their moves. Once it ends, they are locked back in to the move they were originally locked into.
805 lines
22 KiB
JavaScript
805 lines
22 KiB
JavaScript
'use strict';
|
|
|
|
/**@type {{[k: string]: PureEffectData}} */
|
|
let BattleStatuses = {
|
|
brn: {
|
|
name: 'brn',
|
|
id: 'brn',
|
|
num: 0,
|
|
effectType: 'Status',
|
|
onStart(target, source, sourceEffect) {
|
|
if (sourceEffect && sourceEffect.id === 'flameorb') {
|
|
this.add('-status', target, 'brn', '[from] item: Flame Orb');
|
|
} else if (sourceEffect && sourceEffect.effectType === 'Ability') {
|
|
this.add('-status', target, 'brn', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
|
|
} else {
|
|
this.add('-status', target, 'brn');
|
|
}
|
|
},
|
|
// Damage reduction is handled directly in the sim/battle.js damage function
|
|
onResidualOrder: 9,
|
|
onResidual(pokemon) {
|
|
this.damage(pokemon.maxhp / 16);
|
|
},
|
|
},
|
|
par: {
|
|
name: 'par',
|
|
id: 'par',
|
|
num: 0,
|
|
effectType: 'Status',
|
|
onStart(target, source, sourceEffect) {
|
|
if (sourceEffect && sourceEffect.effectType === 'Ability') {
|
|
this.add('-status', target, 'par', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
|
|
} else {
|
|
this.add('-status', target, 'par');
|
|
}
|
|
},
|
|
onModifySpe(spe, pokemon) {
|
|
if (!pokemon.hasAbility('quickfeet')) {
|
|
return this.chainModify(0.5);
|
|
}
|
|
},
|
|
onBeforeMovePriority: 1,
|
|
onBeforeMove(pokemon) {
|
|
if (this.randomChance(1, 4)) {
|
|
this.add('cant', pokemon, 'par');
|
|
return false;
|
|
}
|
|
},
|
|
},
|
|
slp: {
|
|
name: 'slp',
|
|
id: 'slp',
|
|
num: 0,
|
|
effectType: 'Status',
|
|
onStart(target, source, sourceEffect) {
|
|
if (sourceEffect && sourceEffect.effectType === 'Ability') {
|
|
this.add('-status', target, 'slp', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
|
|
} else if (sourceEffect && sourceEffect.effectType === 'Move') {
|
|
this.add('-status', target, 'slp', '[from] move: ' + sourceEffect.name);
|
|
} else {
|
|
this.add('-status', target, 'slp');
|
|
}
|
|
// 1-3 turns
|
|
this.effectData.startTime = this.random(2, 5);
|
|
this.effectData.time = this.effectData.startTime;
|
|
},
|
|
onBeforeMovePriority: 10,
|
|
onBeforeMove(pokemon, target, move) {
|
|
if (pokemon.hasAbility('earlybird')) {
|
|
pokemon.statusData.time--;
|
|
}
|
|
pokemon.statusData.time--;
|
|
if (pokemon.statusData.time <= 0) {
|
|
pokemon.cureStatus();
|
|
return;
|
|
}
|
|
this.add('cant', pokemon, 'slp');
|
|
if (move.sleepUsable) {
|
|
return;
|
|
}
|
|
return false;
|
|
},
|
|
},
|
|
frz: {
|
|
name: 'frz',
|
|
id: 'frz',
|
|
num: 0,
|
|
effectType: 'Status',
|
|
onStart(target, source, sourceEffect) {
|
|
if (sourceEffect && sourceEffect.effectType === 'Ability') {
|
|
this.add('-status', target, 'frz', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
|
|
} else {
|
|
this.add('-status', target, 'frz');
|
|
}
|
|
if (target.template.species === 'Shaymin-Sky' && target.baseTemplate.baseSpecies === 'Shaymin') {
|
|
target.formeChange('Shaymin', this.effect, true);
|
|
}
|
|
},
|
|
onBeforeMovePriority: 10,
|
|
onBeforeMove(pokemon, target, move) {
|
|
if (move.flags['defrost']) return;
|
|
if (this.randomChance(1, 5)) {
|
|
pokemon.cureStatus();
|
|
return;
|
|
}
|
|
this.add('cant', pokemon, 'frz');
|
|
return false;
|
|
},
|
|
onModifyMove(move, pokemon) {
|
|
if (move.flags['defrost']) {
|
|
this.add('-curestatus', pokemon, 'frz', '[from] move: ' + move);
|
|
pokemon.setStatus('');
|
|
}
|
|
},
|
|
onHit(target, source, move) {
|
|
if (move.thawsTarget || move.type === 'Fire' && move.category !== 'Status') {
|
|
target.cureStatus();
|
|
}
|
|
},
|
|
},
|
|
psn: {
|
|
name: 'psn',
|
|
id: 'psn',
|
|
num: 0,
|
|
effectType: 'Status',
|
|
onStart(target, source, sourceEffect) {
|
|
if (sourceEffect && sourceEffect.effectType === 'Ability') {
|
|
this.add('-status', target, 'psn', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
|
|
} else {
|
|
this.add('-status', target, 'psn');
|
|
}
|
|
},
|
|
onResidualOrder: 9,
|
|
onResidual(pokemon) {
|
|
this.damage(pokemon.maxhp / 8);
|
|
},
|
|
},
|
|
tox: {
|
|
name: 'tox',
|
|
id: 'tox',
|
|
num: 0,
|
|
effectType: 'Status',
|
|
onStart(target, source, sourceEffect) {
|
|
this.effectData.stage = 0;
|
|
if (sourceEffect && sourceEffect.id === 'toxicorb') {
|
|
this.add('-status', target, 'tox', '[from] item: Toxic Orb');
|
|
} else if (sourceEffect && sourceEffect.effectType === 'Ability') {
|
|
this.add('-status', target, 'tox', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
|
|
} else {
|
|
this.add('-status', target, 'tox');
|
|
}
|
|
},
|
|
onSwitchIn() {
|
|
this.effectData.stage = 0;
|
|
},
|
|
onResidualOrder: 9,
|
|
onResidual(pokemon) {
|
|
if (this.effectData.stage < 15) {
|
|
this.effectData.stage++;
|
|
}
|
|
this.damage(this.dex.clampIntRange(pokemon.maxhp / 16, 1) * this.effectData.stage);
|
|
},
|
|
},
|
|
confusion: {
|
|
name: 'confusion',
|
|
id: 'confusion',
|
|
num: 0,
|
|
// this is a volatile status
|
|
onStart(target, source, sourceEffect) {
|
|
if (sourceEffect && sourceEffect.id === 'lockedmove') {
|
|
this.add('-start', target, 'confusion', '[fatigue]');
|
|
} else {
|
|
this.add('-start', target, 'confusion');
|
|
}
|
|
this.effectData.time = this.random(2, 6);
|
|
},
|
|
onEnd(target) {
|
|
this.add('-end', target, 'confusion');
|
|
},
|
|
onBeforeMovePriority: 3,
|
|
onBeforeMove(pokemon) {
|
|
pokemon.volatiles.confusion.time--;
|
|
if (!pokemon.volatiles.confusion.time) {
|
|
pokemon.removeVolatile('confusion');
|
|
return;
|
|
}
|
|
this.add('-activate', pokemon, 'confusion');
|
|
if (!this.randomChance(1, 3)) {
|
|
return;
|
|
}
|
|
this.activeTarget = pokemon;
|
|
let damage = this.getDamage(pokemon, pokemon, 40);
|
|
if (typeof damage !== 'number') throw new Error("Confusion damage not dealt");
|
|
this.damage(damage, pokemon, pokemon, /** @type {ActiveMove} */ ({
|
|
id: 'confused',
|
|
effectType: 'Move',
|
|
type: '???',
|
|
}));
|
|
return false;
|
|
},
|
|
},
|
|
flinch: {
|
|
name: 'flinch',
|
|
id: 'flinch',
|
|
num: 0,
|
|
duration: 1,
|
|
onBeforeMovePriority: 8,
|
|
onBeforeMove(pokemon) {
|
|
if (!this.runEvent('Flinch', pokemon)) {
|
|
return;
|
|
}
|
|
this.add('cant', pokemon, 'flinch');
|
|
return false;
|
|
},
|
|
},
|
|
trapped: {
|
|
name: 'trapped',
|
|
id: 'trapped',
|
|
num: 0,
|
|
noCopy: true,
|
|
onTrapPokemon(pokemon) {
|
|
pokemon.tryTrap();
|
|
},
|
|
onStart(target) {
|
|
this.add('-activate', target, 'trapped');
|
|
},
|
|
},
|
|
trapper: {
|
|
name: 'trapper',
|
|
id: 'trapper',
|
|
num: 0,
|
|
noCopy: true,
|
|
},
|
|
partiallytrapped: {
|
|
name: 'partiallytrapped',
|
|
id: 'partiallytrapped',
|
|
num: 0,
|
|
duration: 5,
|
|
durationCallback(target, source) {
|
|
if (source && source.hasItem('gripclaw')) return 8;
|
|
return this.random(5, 7);
|
|
},
|
|
onStart(pokemon, source) {
|
|
this.add('-activate', pokemon, 'move: ' + this.effectData.sourceEffect, '[of] ' + source);
|
|
},
|
|
onResidualOrder: 11,
|
|
onResidual(pokemon) {
|
|
if (this.effectData.source && (!this.effectData.source.isActive || this.effectData.source.hp <= 0 || !this.effectData.source.activeTurns)) {
|
|
delete pokemon.volatiles['partiallytrapped'];
|
|
this.add('-end', pokemon, this.effectData.sourceEffect, '[partiallytrapped]', '[silent]');
|
|
return;
|
|
}
|
|
if (this.effectData.source.hasItem('bindingband')) {
|
|
this.damage(pokemon.maxhp / 6);
|
|
} else {
|
|
this.damage(pokemon.maxhp / 8);
|
|
}
|
|
},
|
|
onEnd(pokemon) {
|
|
this.add('-end', pokemon, this.effectData.sourceEffect, '[partiallytrapped]');
|
|
},
|
|
onTrapPokemon(pokemon) {
|
|
if (this.effectData.source && this.effectData.source.isActive) pokemon.tryTrap();
|
|
},
|
|
},
|
|
lockedmove: {
|
|
// Outrage, Thrash, Petal Dance...
|
|
name: 'lockedmove',
|
|
id: 'lockedmove',
|
|
num: 0,
|
|
duration: 2,
|
|
onResidual(target) {
|
|
if (target.status === 'slp') {
|
|
// don't lock, and bypass confusion for calming
|
|
delete target.volatiles['lockedmove'];
|
|
}
|
|
this.effectData.trueDuration--;
|
|
},
|
|
onStart(target, source, effect) {
|
|
this.effectData.trueDuration = this.random(2, 4);
|
|
this.effectData.move = effect.id;
|
|
},
|
|
onRestart() {
|
|
if (this.effectData.trueDuration >= 2) {
|
|
this.effectData.duration = 2;
|
|
}
|
|
},
|
|
onEnd(target) {
|
|
if (this.effectData.trueDuration > 1) return;
|
|
target.addVolatile('confusion');
|
|
},
|
|
onLockMove(pokemon) {
|
|
return this.effectData.move;
|
|
},
|
|
},
|
|
twoturnmove: {
|
|
// Skull Bash, SolarBeam, Sky Drop...
|
|
name: 'twoturnmove',
|
|
id: 'twoturnmove',
|
|
num: 0,
|
|
duration: 2,
|
|
onStart(target, source, effect) {
|
|
this.effectData.move = effect.id;
|
|
target.addVolatile(effect.id, source);
|
|
this.attrLastMove('[still]');
|
|
},
|
|
onEnd(target) {
|
|
target.removeVolatile(this.effectData.move);
|
|
},
|
|
onLockMove() {
|
|
return this.effectData.move;
|
|
},
|
|
onMoveAborted(pokemon) {
|
|
pokemon.removeVolatile('twoturnmove');
|
|
},
|
|
},
|
|
choicelock: {
|
|
name: 'choicelock',
|
|
id: 'choicelock',
|
|
num: 0,
|
|
noCopy: true,
|
|
onStart(pokemon) {
|
|
if (!this.activeMove) throw new Error("Battle.activeMove is null");
|
|
if (!this.activeMove.id || this.activeMove.hasBounced) return false;
|
|
this.effectData.move = this.activeMove.id;
|
|
},
|
|
onBeforeMove(pokemon, target, move) {
|
|
if (!pokemon.getItem().isChoice) {
|
|
pokemon.removeVolatile('choicelock');
|
|
return;
|
|
}
|
|
if (!pokemon.ignoringItem() && move.id !== this.effectData.move && move.id !== 'struggle') {
|
|
// Fails unless the Choice item is being ignored, and no PP is lost
|
|
this.addMove('move', pokemon, move.name);
|
|
this.attrLastMove('[still]');
|
|
this.add('-fail', pokemon);
|
|
return false;
|
|
}
|
|
},
|
|
onDisableMove(pokemon) {
|
|
if (!pokemon.getItem().isChoice || !pokemon.hasMove(this.effectData.move)) {
|
|
pokemon.removeVolatile('choicelock');
|
|
return;
|
|
}
|
|
if (pokemon.ignoringItem() || pokemon.volatiles['dynamax']) {
|
|
return;
|
|
}
|
|
for (const moveSlot of pokemon.moveSlots) {
|
|
if (moveSlot.id !== this.effectData.move) {
|
|
pokemon.disableMove(moveSlot.id, false, this.effectData.sourceEffect);
|
|
}
|
|
}
|
|
},
|
|
},
|
|
mustrecharge: {
|
|
name: 'mustrecharge',
|
|
id: 'mustrecharge',
|
|
num: 0,
|
|
duration: 2,
|
|
onBeforeMovePriority: 11,
|
|
onBeforeMove(pokemon) {
|
|
this.add('cant', pokemon, 'recharge');
|
|
pokemon.removeVolatile('mustrecharge');
|
|
pokemon.removeVolatile('truant');
|
|
return null;
|
|
},
|
|
onStart(pokemon) {
|
|
this.add('-mustrecharge', pokemon);
|
|
},
|
|
onLockMove: 'recharge',
|
|
},
|
|
futuremove: {
|
|
// this is a slot condition
|
|
name: 'futuremove',
|
|
id: 'futuremove',
|
|
num: 0,
|
|
duration: 3,
|
|
onResidualOrder: 3,
|
|
onEnd(target) {
|
|
const data = this.effectData;
|
|
// time's up; time to hit! :D
|
|
const move = this.dex.getMove(data.move);
|
|
if (target.fainted || target === data.source) {
|
|
this.hint(`${move.name} did not hit because the target is ${(data.fainted ? 'fainted' : 'the user')}.`);
|
|
return;
|
|
}
|
|
|
|
this.add('-end', target, 'move: ' + move.name);
|
|
target.removeVolatile('Protect');
|
|
target.removeVolatile('Endure');
|
|
|
|
if (data.source.hasAbility('infiltrator') && this.gen >= 6) {
|
|
data.moveData.infiltrates = true;
|
|
}
|
|
if (data.source.hasAbility('normalize') && this.gen >= 6) {
|
|
data.moveData.type = 'Normal';
|
|
}
|
|
if (data.source.hasAbility('adaptability') && this.gen >= 6) {
|
|
data.moveData.stab = 2;
|
|
}
|
|
const hitMove = new this.dex.Data.Move(data.moveData);
|
|
|
|
this.trySpreadMoveHit([target], data.source, /** @type {ActiveMove} */(/** @type {unknown} */(hitMove)));
|
|
},
|
|
},
|
|
healreplacement: {
|
|
// this is a slot condition
|
|
name: 'healreplacement',
|
|
id: 'healreplacement',
|
|
num: 0,
|
|
onStart(side, source, sourceEffect) {
|
|
this.effectData.sourceEffect = sourceEffect;
|
|
this.add('-activate', source, 'healreplacement');
|
|
},
|
|
onSwitchInPriority: 1,
|
|
onSwitchIn(target) {
|
|
if (!target.fainted) {
|
|
target.heal(target.maxhp);
|
|
this.add('-heal', target, target.getHealth, '[from] move: ' + this.effectData.sourceEffect, '[zeffect]');
|
|
target.side.removeSlotCondition(target, 'healreplacement');
|
|
}
|
|
},
|
|
},
|
|
stall: {
|
|
// Protect, Detect, Endure counter
|
|
name: 'stall',
|
|
id: 'stall',
|
|
num: 0,
|
|
duration: 2,
|
|
counterMax: 729,
|
|
onStart() {
|
|
this.effectData.counter = 3;
|
|
},
|
|
onStallMove(pokemon) {
|
|
// this.effectData.counter should never be undefined here.
|
|
// However, just in case, use 1 if it is undefined.
|
|
let counter = this.effectData.counter || 1;
|
|
this.debug("Success chance: " + Math.round(100 / counter) + "%");
|
|
let success = this.randomChance(1, counter);
|
|
if (!success) delete pokemon.volatiles['stall'];
|
|
return success;
|
|
},
|
|
onRestart() {
|
|
// @ts-ignore
|
|
if (this.effectData.counter < this.effect.counterMax) {
|
|
this.effectData.counter *= 3;
|
|
}
|
|
this.effectData.duration = 2;
|
|
},
|
|
},
|
|
gem: {
|
|
name: 'gem',
|
|
id: 'gem',
|
|
num: 0,
|
|
duration: 1,
|
|
affectsFainted: true,
|
|
onBasePower(basePower, user, target, move) {
|
|
this.debug('Gem Boost');
|
|
return this.chainModify([0x14CD, 0x1000]);
|
|
},
|
|
},
|
|
|
|
// weather is implemented here since it's so important to the game
|
|
|
|
raindance: {
|
|
name: 'RainDance',
|
|
id: 'raindance',
|
|
num: 0,
|
|
effectType: 'Weather',
|
|
duration: 5,
|
|
durationCallback(source, effect) {
|
|
if (source && source.hasItem('damprock')) {
|
|
return 8;
|
|
}
|
|
return 5;
|
|
},
|
|
onWeatherModifyDamage(damage, attacker, defender, move) {
|
|
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);
|
|
}
|
|
},
|
|
onStart(battle, source, effect) {
|
|
if (effect && effect.effectType === 'Ability') {
|
|
if (this.gen <= 5) this.effectData.duration = 0;
|
|
this.add('-weather', 'RainDance', '[from] ability: ' + effect, '[of] ' + source);
|
|
} else {
|
|
this.add('-weather', 'RainDance');
|
|
}
|
|
},
|
|
onResidualOrder: 1,
|
|
onResidual() {
|
|
this.add('-weather', 'RainDance', '[upkeep]');
|
|
this.eachEvent('Weather');
|
|
},
|
|
onEnd() {
|
|
this.add('-weather', 'none');
|
|
},
|
|
},
|
|
primordialsea: {
|
|
name: 'PrimordialSea',
|
|
id: 'primordialsea',
|
|
num: 0,
|
|
effectType: 'Weather',
|
|
duration: 0,
|
|
onTryMovePriority: 1,
|
|
onTryMove(attacker, defender, move) {
|
|
if (move.type === 'Fire' && move.category !== 'Status') {
|
|
this.debug('Primordial Sea fire suppress');
|
|
this.add('-fail', attacker, move, '[from] Primordial Sea');
|
|
this.attrLastMove('[still]');
|
|
return null;
|
|
}
|
|
},
|
|
onWeatherModifyDamage(damage, attacker, defender, move) {
|
|
if (move.type === 'Water') {
|
|
this.debug('Rain water boost');
|
|
return this.chainModify(1.5);
|
|
}
|
|
},
|
|
onStart(battle, source, effect) {
|
|
this.add('-weather', 'PrimordialSea', '[from] ability: ' + effect, '[of] ' + source);
|
|
},
|
|
onResidualOrder: 1,
|
|
onResidual() {
|
|
this.add('-weather', 'PrimordialSea', '[upkeep]');
|
|
this.eachEvent('Weather');
|
|
},
|
|
onEnd() {
|
|
this.add('-weather', 'none');
|
|
},
|
|
},
|
|
sunnyday: {
|
|
name: 'SunnyDay',
|
|
id: 'sunnyday',
|
|
num: 0,
|
|
effectType: 'Weather',
|
|
duration: 5,
|
|
durationCallback(source, effect) {
|
|
if (source && source.hasItem('heatrock')) {
|
|
return 8;
|
|
}
|
|
return 5;
|
|
},
|
|
onWeatherModifyDamage(damage, attacker, defender, move) {
|
|
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);
|
|
}
|
|
},
|
|
onStart(battle, source, effect) {
|
|
if (effect && effect.effectType === 'Ability') {
|
|
if (this.gen <= 5) this.effectData.duration = 0;
|
|
this.add('-weather', 'SunnyDay', '[from] ability: ' + effect, '[of] ' + source);
|
|
} else {
|
|
this.add('-weather', 'SunnyDay');
|
|
}
|
|
},
|
|
onImmunity(type) {
|
|
if (type === 'frz') return false;
|
|
},
|
|
onResidualOrder: 1,
|
|
onResidual() {
|
|
this.add('-weather', 'SunnyDay', '[upkeep]');
|
|
this.eachEvent('Weather');
|
|
},
|
|
onEnd() {
|
|
this.add('-weather', 'none');
|
|
},
|
|
},
|
|
desolateland: {
|
|
name: 'DesolateLand',
|
|
id: 'desolateland',
|
|
num: 0,
|
|
effectType: 'Weather',
|
|
duration: 0,
|
|
onTryMovePriority: 1,
|
|
onTryMove(attacker, defender, move) {
|
|
if (move.type === 'Water' && move.category !== 'Status') {
|
|
this.debug('Desolate Land water suppress');
|
|
this.add('-fail', attacker, move, '[from] Desolate Land');
|
|
this.attrLastMove('[still]');
|
|
return null;
|
|
}
|
|
},
|
|
onWeatherModifyDamage(damage, attacker, defender, move) {
|
|
if (move.type === 'Fire') {
|
|
this.debug('Sunny Day fire boost');
|
|
return this.chainModify(1.5);
|
|
}
|
|
},
|
|
onStart(battle, source, effect) {
|
|
this.add('-weather', 'DesolateLand', '[from] ability: ' + effect, '[of] ' + source);
|
|
},
|
|
onImmunity(type) {
|
|
if (type === 'frz') return false;
|
|
},
|
|
onResidualOrder: 1,
|
|
onResidual() {
|
|
this.add('-weather', 'DesolateLand', '[upkeep]');
|
|
this.eachEvent('Weather');
|
|
},
|
|
onEnd() {
|
|
this.add('-weather', 'none');
|
|
},
|
|
},
|
|
sandstorm: {
|
|
name: 'Sandstorm',
|
|
id: 'sandstorm',
|
|
num: 0,
|
|
effectType: 'Weather',
|
|
duration: 5,
|
|
durationCallback(source, effect) {
|
|
if (source && source.hasItem('smoothrock')) {
|
|
return 8;
|
|
}
|
|
return 5;
|
|
},
|
|
// This should be applied directly to the stat before any of the other modifiers are chained
|
|
// So we give it increased priority.
|
|
onModifySpDPriority: 10,
|
|
onModifySpD(spd, pokemon) {
|
|
if (pokemon.hasType('Rock') && this.field.isWeather('sandstorm')) {
|
|
return this.modify(spd, 1.5);
|
|
}
|
|
},
|
|
onStart(battle, source, effect) {
|
|
if (effect && effect.effectType === 'Ability') {
|
|
if (this.gen <= 5) this.effectData.duration = 0;
|
|
this.add('-weather', 'Sandstorm', '[from] ability: ' + effect, '[of] ' + source);
|
|
} else {
|
|
this.add('-weather', 'Sandstorm');
|
|
}
|
|
},
|
|
onResidualOrder: 1,
|
|
onResidual() {
|
|
this.add('-weather', 'Sandstorm', '[upkeep]');
|
|
if (this.field.isWeather('sandstorm')) this.eachEvent('Weather');
|
|
},
|
|
onWeather(target) {
|
|
this.damage(target.maxhp / 16);
|
|
},
|
|
onEnd() {
|
|
this.add('-weather', 'none');
|
|
},
|
|
},
|
|
hail: {
|
|
name: 'Hail',
|
|
id: 'hail',
|
|
num: 0,
|
|
effectType: 'Weather',
|
|
duration: 5,
|
|
durationCallback(source, effect) {
|
|
if (source && source.hasItem('icyrock')) {
|
|
return 8;
|
|
}
|
|
return 5;
|
|
},
|
|
onStart(battle, source, effect) {
|
|
if (effect && effect.effectType === 'Ability') {
|
|
if (this.gen <= 5) this.effectData.duration = 0;
|
|
this.add('-weather', 'Hail', '[from] ability: ' + effect, '[of] ' + source);
|
|
} else {
|
|
this.add('-weather', 'Hail');
|
|
}
|
|
},
|
|
onResidualOrder: 1,
|
|
onResidual() {
|
|
this.add('-weather', 'Hail', '[upkeep]');
|
|
if (this.field.isWeather('hail')) this.eachEvent('Weather');
|
|
},
|
|
onWeather(target) {
|
|
this.damage(target.maxhp / 16);
|
|
},
|
|
onEnd() {
|
|
this.add('-weather', 'none');
|
|
},
|
|
},
|
|
deltastream: {
|
|
name: 'DeltaStream',
|
|
id: 'deltastream',
|
|
num: 0,
|
|
effectType: 'Weather',
|
|
duration: 0,
|
|
onEffectiveness(typeMod, target, type, move) {
|
|
if (move && move.effectType === 'Move' && move.category !== 'Status' && type === 'Flying' && typeMod > 0) {
|
|
this.add('-activate', '', 'deltastream');
|
|
return 0;
|
|
}
|
|
},
|
|
onStart(battle, source, effect) {
|
|
this.add('-weather', 'DeltaStream', '[from] ability: ' + effect, '[of] ' + source);
|
|
},
|
|
onResidualOrder: 1,
|
|
onResidual() {
|
|
this.add('-weather', 'DeltaStream', '[upkeep]');
|
|
this.eachEvent('Weather');
|
|
},
|
|
onEnd() {
|
|
this.add('-weather', 'none');
|
|
},
|
|
},
|
|
|
|
dynamax: {
|
|
name: 'Dynamax',
|
|
id: 'dynamax',
|
|
num: 0,
|
|
duration: 3,
|
|
onStart(pokemon) {
|
|
//this.add('-dynamax', pokemon);
|
|
this.add('-message', `${pokemon.name || pokemon.species} Dynamaxed!`);
|
|
this.debug(`Dynamax Start: ${pokemon} (${pokemon.side.name})`);
|
|
if (pokemon.canGigantamax) pokemon.formeChange(pokemon.canGigantamax);
|
|
if (pokemon.species === 'Shedinja') return;
|
|
let ratio = (2 / 3); // Changes based on dynamax level, static (LVL 0) until we know the levels
|
|
pokemon.maxhp = Math.floor(pokemon.maxhp / ratio);
|
|
pokemon.hp = Math.floor(pokemon.hp / ratio);
|
|
// TODO work on display for healing
|
|
this.add('-heal', pokemon, pokemon.getHealth, '[from] Dynamax');
|
|
},
|
|
onFlinch: false,
|
|
onSwitchOut(pokemon) {
|
|
// Run the end event
|
|
// pokemon.volatiles.dynamax.onEnd(pokemon);
|
|
//this.add('-undynamax', pokemon);
|
|
this.add('-message', `${pokemon.name || pokemon.species}'s dynamax ended.`);
|
|
this.debug(`Dynamax End: ${pokemon} (${pokemon.side.name})`);
|
|
if (pokemon.canGigantamax) pokemon.formeChange(pokemon.baseTemplate.species);
|
|
if (pokemon.species === 'Shedinja') return;
|
|
let ratio = (2 / 3); // Changes based on dynamax level, static (LVL 0) until we know the levels
|
|
pokemon.maxhp = Math.floor(pokemon.maxhp * ratio); // TODO prevent maxhp loss
|
|
pokemon.hp = Math.floor(pokemon.hp * ratio);
|
|
// TODO work on display for healing
|
|
this.add('-heal', pokemon, pokemon.getHealth, '[from] Dynamax');
|
|
},
|
|
onEnd(pokemon) {
|
|
// Play animation
|
|
// Modify HP - Work with LVL 0 for now
|
|
//this.add('-undynamax', pokemon);
|
|
this.add('-message', `${pokemon.name || pokemon.species}'s dynamax ended.`);
|
|
this.debug(`Dynamax End: ${pokemon} (${pokemon.side.name})`);
|
|
if (pokemon.canGigantamax) pokemon.formeChange(pokemon.baseTemplate.species);
|
|
if (pokemon.species === 'Shedinja') return;
|
|
let ratio = (2 / 3); // Changes based on dynamax level, static (LVL 0) until we know the levels
|
|
pokemon.maxhp = Math.floor(pokemon.maxhp * ratio); // TODO prevent maxhp loss
|
|
pokemon.hp = Math.floor(pokemon.hp * ratio);
|
|
// TODO work on display for healing
|
|
this.add('-heal', pokemon, pokemon.getHealth, '[from] Dynamax');
|
|
},
|
|
},
|
|
|
|
// Arceus and Silvally's actual typing is implemented here.
|
|
// Their true typing for all their formes is Normal, and it's only
|
|
// Multitype and RKS System, respectively, that changes their type,
|
|
// but their formes are specified to be their corresponding type
|
|
// in the Pokedex, so that needs to be overridden.
|
|
// This is mainly relevant for Hackmons Cup and Balanced Hackmons.
|
|
arceus: {
|
|
name: 'Arceus',
|
|
id: 'arceus',
|
|
num: 493,
|
|
onTypePriority: 1,
|
|
onType(types, pokemon) {
|
|
if (pokemon.transformed) return types;
|
|
/** @type {string | undefined} */
|
|
let type = 'Normal';
|
|
if (pokemon.ability === 'multitype') {
|
|
type = pokemon.getItem().onPlate;
|
|
if (!type) {
|
|
type = 'Normal';
|
|
}
|
|
}
|
|
return [type];
|
|
},
|
|
},
|
|
silvally: {
|
|
name: 'Silvally',
|
|
id: 'silvally',
|
|
num: 773,
|
|
onTypePriority: 1,
|
|
onType(types, pokemon) {
|
|
if (pokemon.transformed) return types;
|
|
/** @type {string | undefined} */
|
|
let type = 'Normal';
|
|
if (pokemon.ability === 'rkssystem') {
|
|
type = pokemon.getItem().onMemory;
|
|
if (!type) {
|
|
type = 'Normal';
|
|
}
|
|
}
|
|
return [type];
|
|
},
|
|
},
|
|
};
|
|
|
|
exports.BattleStatuses = BattleStatuses;
|