Jofthomas's picture
Upload 4781 files
5c2ed06 verified
/**
* Gen 2 moves
*/
export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
aeroblast: {
inherit: true,
critRatio: 3,
},
beatup: {
inherit: true,
onModifyMove(move, pokemon) {
move.type = '???';
move.category = 'Special';
move.allies = pokemon.side.pokemon.filter(ally => !ally.fainted && !ally.status);
move.multihit = move.allies.length;
},
},
bellydrum: {
inherit: true,
onHit(target) {
if (target.boosts.atk >= 6) {
return false;
}
if (target.hp <= target.maxhp / 2) {
this.boost({ atk: 2 }, null, null, this.dex.conditions.get('bellydrum2'));
return false;
}
this.directDamage(target.maxhp / 2);
const originalStage = target.boosts.atk;
let currentStage = originalStage;
let boosts = 0;
let loopStage = 0;
while (currentStage < 6) {
loopStage = currentStage;
currentStage++;
if (currentStage < 6) currentStage++;
target.boosts.atk = loopStage;
if (target.getStat('atk', false, true) < 999) {
target.boosts.atk = currentStage;
continue;
}
target.boosts.atk = currentStage - 1;
break;
}
boosts = target.boosts.atk - originalStage;
target.boosts.atk = originalStage;
this.boost({ atk: boosts });
},
},
bide: {
inherit: true,
condition: {
duration: 3,
durationCallback(target, source, effect) {
return this.random(3, 5);
},
onLockMove: 'bide',
onStart(pokemon) {
this.effectState.totalDamage = 0;
this.add('-start', pokemon, 'move: Bide');
},
onDamagePriority: -101,
onDamage(damage, target, source, move) {
if (!move || move.effectType !== 'Move' || !source) return;
this.effectState.totalDamage += damage;
this.effectState.lastDamageSource = source;
},
onBeforeMove(pokemon, target, move) {
if (this.effectState.duration === 1) {
this.add('-end', pokemon, 'move: Bide');
if (!this.effectState.totalDamage) {
this.add('-fail', pokemon);
return false;
}
target = this.effectState.lastDamageSource;
if (!target) {
this.add('-fail', pokemon);
return false;
}
if (!target.isActive) {
const possibleTarget = this.getRandomTarget(pokemon, this.dex.moves.get('pound'));
if (!possibleTarget) {
this.add('-miss', pokemon);
return false;
}
target = possibleTarget;
}
const moveData = {
id: 'bide',
name: "Bide",
accuracy: 100,
damage: this.effectState.totalDamage * 2,
category: "Physical",
priority: 0,
flags: { contact: 1, protect: 1 },
effectType: 'Move',
type: 'Normal',
} as unknown as ActiveMove;
this.actions.tryMoveHit(target, pokemon, moveData);
pokemon.removeVolatile('bide');
return false;
}
this.add('-activate', pokemon, 'move: Bide');
},
onMoveAborted(pokemon) {
pokemon.removeVolatile('bide');
},
onEnd(pokemon) {
this.add('-end', pokemon, 'move: Bide', '[silent]');
},
},
},
counter: {
inherit: true,
damageCallback(pokemon, target) {
const lastAttackedBy = pokemon.getLastAttackedBy();
if (!lastAttackedBy?.move || !lastAttackedBy.thisTurn) return false;
// Hidden Power counts as physical
if (this.getCategory(lastAttackedBy.move) === 'Physical' && target.lastMove?.id !== 'sleeptalk') {
return 2 * lastAttackedBy.damage;
}
return false;
},
beforeTurnCallback() {},
onTry() {},
condition: {},
priority: -1,
},
crabhammer: {
inherit: true,
critRatio: 3,
},
crosschop: {
inherit: true,
critRatio: 3,
},
curse: {
inherit: true,
condition: {
onStart(pokemon, source) {
this.add('-start', pokemon, 'Curse', `[of] ${source}`);
},
onAfterMoveSelf(pokemon) {
this.damage(pokemon.baseMaxhp / 4);
},
},
},
detect: {
inherit: true,
priority: 2,
},
dig: {
inherit: true,
onPrepareHit(target, source) {
return source.status !== 'slp';
},
condition: {
duration: 2,
onImmunity(type, pokemon) {
if (type === 'sandstorm') return false;
},
onInvulnerability(target, source, move) {
if (move.id === 'earthquake' || move.id === 'magnitude' || move.id === 'fissure') {
return;
}
if (['attract', 'curse', 'foresight', 'meanlook', 'mimic', 'nightmare', 'spiderweb', 'transform'].includes(move.id)) {
// Oversight in the interaction between these moves and the Lock-On effect
return false;
}
if (source.volatiles['lockon'] && target === source.volatiles['lockon'].source) return;
return false;
},
onSourceBasePower(basePower, target, source, move) {
if (move.id === 'earthquake' || move.id === 'magnitude') {
return this.chainModify(2);
}
},
},
},
doubleedge: {
inherit: true,
recoil: [25, 100],
},
encore: {
inherit: true,
condition: {
durationCallback() {
return this.random(3, 7);
},
onStart(target) {
const lockedMove = target.lastMoveEncore?.id || '';
const moveIndex = lockedMove ? target.moves.indexOf(lockedMove) : -1;
if (moveIndex < 0 || target.lastMoveEncore?.flags['failencore'] || target.moveSlots[moveIndex].pp <= 0) {
// it failed
return false;
}
this.effectState.move = lockedMove;
this.add('-start', target, 'Encore');
},
onOverrideAction(pokemon) {
return this.effectState.move;
},
onResidualOrder: 13,
onResidual(target) {
const lockedMoveIndex = target.moves.indexOf(this.effectState.move);
if (lockedMoveIndex >= 0 && target.moveSlots[lockedMoveIndex].pp <= 0) {
// early termination if you run out of PP
target.removeVolatile('encore');
}
},
onEnd(target) {
this.add('-end', target, 'Encore');
},
onDisableMove(pokemon) {
if (!this.effectState.move || !pokemon.hasMove(this.effectState.move)) {
return;
}
for (const moveSlot of pokemon.moveSlots) {
if (moveSlot.id !== this.effectState.move) {
pokemon.disableMove(moveSlot.id);
}
}
},
},
},
endure: {
inherit: true,
priority: 2,
},
explosion: {
inherit: true,
flags: { protect: 1, mirror: 1, metronome: 1, noparentalbond: 1, nosketch: 1 },
},
flail: {
inherit: true,
noDamageVariance: true,
willCrit: false,
},
fly: {
inherit: true,
onPrepareHit(target, source) {
return source.status !== 'slp';
},
condition: {
duration: 2,
onInvulnerability(target, source, move) {
if (move.id === 'gust' || move.id === 'twister' || move.id === 'thunder' || move.id === 'whirlwind') {
return;
}
if (move.id === 'earthquake' || move.id === 'magnitude' || move.id === 'fissure') {
// These moves miss even during the Lock-On effect
return false;
}
if (['attract', 'curse', 'foresight', 'meanlook', 'mimic', 'nightmare', 'spiderweb', 'transform'].includes(move.id)) {
// Oversight in the interaction between these moves and the Lock-On effect
return false;
}
if (source.volatiles['lockon'] && target === source.volatiles['lockon'].source) return;
return false;
},
onSourceBasePower(basePower, target, source, move) {
if (move.id === 'gust' || move.id === 'twister') {
return this.chainModify(2);
}
},
},
},
focusenergy: {
inherit: true,
condition: {
onStart(pokemon) {
this.add('-start', pokemon, 'move: Focus Energy');
},
onModifyCritRatio(critRatio) {
return critRatio + 1;
},
},
},
foresight: {
inherit: true,
onTryHit(target) {
if (target.volatiles['foresight']) return false;
},
condition: {
onStart(pokemon) {
this.add('-start', pokemon, 'Foresight');
},
onNegateImmunity(pokemon, type) {
if (pokemon.hasType('Ghost') && ['Normal', 'Fighting'].includes(type)) return false;
},
onModifyBoost(boosts) {
if (boosts.evasion && boosts.evasion > 0) {
boosts.evasion = 0;
}
},
},
},
frustration: {
inherit: true,
basePowerCallback(pokemon) {
return Math.floor(((255 - pokemon.happiness) * 10) / 25) || null;
},
},
healbell: {
inherit: true,
onHit(target, source) {
this.add('-cureteam', source, '[from] move: Heal Bell');
for (const pokemon of target.side.pokemon) {
pokemon.clearStatus();
}
},
},
highjumpkick: {
inherit: true,
onMoveFail(target, source, move) {
if (target.runImmunity('Fighting')) {
const damage = this.actions.getDamage(source, target, move, true);
if (typeof damage !== 'number') throw new Error("Couldn't get High Jump Kick recoil");
this.damage(this.clampIntRange(damage / 8, 1), source, source, move);
}
},
},
jumpkick: {
inherit: true,
onMoveFail(target, source, move) {
if (target.runImmunity('Fighting')) {
const damage = this.actions.getDamage(source, target, move, true);
if (typeof damage !== 'number') throw new Error("Couldn't get Jump Kick recoil");
this.damage(this.clampIntRange(damage / 8, 1), source, source, move);
}
},
},
karatechop: {
inherit: true,
critRatio: 3,
},
leechseed: {
inherit: true,
onHit() {},
condition: {
onStart(target) {
this.add('-start', target, 'move: Leech Seed');
},
onAfterMoveSelfPriority: 2,
onAfterMoveSelf(pokemon) {
if (!pokemon.hp) return;
const leecher = this.getAtSlot(pokemon.volatiles['leechseed'].sourceSlot);
if (!leecher || leecher.fainted || leecher.hp <= 0) {
return;
}
const toLeech = this.clampIntRange(pokemon.maxhp / 8, 1);
const damage = this.damage(toLeech, pokemon, leecher);
if (damage) {
this.heal(damage, leecher, pokemon);
}
},
},
},
lightscreen: {
inherit: true,
condition: {
duration: 5,
// Sp. Def boost applied directly in stat calculation
onSideStart(side) {
this.add('-sidestart', side, 'move: Light Screen');
},
onSideResidualOrder: 9,
onSideEnd(side) {
this.add('-sideend', side, 'move: Light Screen');
},
},
},
lockon: {
inherit: true,
onTryHit(target) {
if (target.volatiles['foresight'] || target.volatiles['lockon']) return false;
},
condition: {
duration: 2,
onSourceAccuracy(accuracy, target, source, move) {
if (move && source === this.effectState.target && target === this.effectState.source) return true;
},
},
},
lowkick: {
inherit: true,
accuracy: 90,
basePower: 50,
basePowerCallback() {
return 50;
},
secondary: {
chance: 30,
volatileStatus: 'flinch',
},
},
meanlook: {
inherit: true,
flags: { reflectable: 1, mirror: 1, metronome: 1 },
},
metronome: {
inherit: true,
flags: { failencore: 1, nosketch: 1 },
},
mimic: {
inherit: true,
accuracy: 100,
flags: { protect: 1, bypasssub: 1, allyanim: 1, failencore: 1, noassist: 1, nosketch: 1 },
},
mindreader: {
inherit: true,
onTryHit(target) {
if (target.volatiles['foresight'] || target.volatiles['lockon']) return false;
},
},
mirrorcoat: {
inherit: true,
damageCallback(pokemon, target) {
const lastAttackedBy = pokemon.getLastAttackedBy();
if (!lastAttackedBy?.move || !lastAttackedBy.thisTurn) return false;
// Hidden Power counts as physical
if (this.getCategory(lastAttackedBy.move) === 'Special' && target.lastMove?.id !== 'sleeptalk') {
return 2 * lastAttackedBy.damage;
}
return false;
},
beforeTurnCallback() {},
onTry() {},
condition: {},
priority: -1,
},
mirrormove: {
inherit: true,
flags: { metronome: 1, failencore: 1, nosketch: 1 },
onHit(pokemon) {
const noMirror = ['metronome', 'mimic', 'mirrormove', 'sketch', 'sleeptalk', 'transform'];
const target = pokemon.side.foe.active[0];
const lastMove = target?.lastMove && target?.lastMove.id;
if (!lastMove || (!pokemon.activeTurns && !target.moveThisTurn)) {
return false;
}
if (noMirror.includes(lastMove) || pokemon.moves.includes(lastMove)) {
return false;
}
this.actions.useMove(lastMove, pokemon);
},
},
mist: {
num: 54,
accuracy: true,
basePower: 0,
category: "Status",
name: "Mist",
pp: 30,
priority: 0,
flags: { metronome: 1 },
volatileStatus: 'mist',
condition: {
onStart(pokemon) {
this.add('-start', pokemon, 'Mist');
},
onTryBoost(boost, target, source, effect) {
if (source && target !== source) {
let showMsg = false;
let i: BoostID;
for (i in boost) {
if (boost[i]! < 0) {
delete boost[i];
showMsg = true;
}
}
if (showMsg && !(effect as ActiveMove).secondaries) {
this.add('-activate', target, 'move: Mist');
}
}
},
},
secondary: null,
target: "self",
type: "Ice",
},
moonlight: {
inherit: true,
onHit(pokemon) {
if (this.field.isWeather(['sunnyday', 'desolateland'])) {
this.heal(pokemon.maxhp);
} else if (this.field.isWeather(['raindance', 'primordialsea', 'sandstorm', 'hail'])) {
this.heal(pokemon.baseMaxhp / 4);
} else {
this.heal(pokemon.baseMaxhp / 2);
}
},
},
morningsun: {
inherit: true,
onHit(pokemon) {
if (this.field.isWeather(['sunnyday', 'desolateland'])) {
this.heal(pokemon.maxhp);
} else if (this.field.isWeather(['raindance', 'primordialsea', 'sandstorm', 'hail'])) {
this.heal(pokemon.baseMaxhp / 4);
} else {
this.heal(pokemon.baseMaxhp / 2);
}
},
},
nightmare: {
inherit: true,
condition: {
noCopy: true,
onStart(pokemon) {
if (pokemon.status !== 'slp') {
return false;
}
this.add('-start', pokemon, 'Nightmare');
},
onAfterMoveSelfPriority: 1,
onAfterMoveSelf(pokemon) {
if (pokemon.status === 'slp') this.damage(pokemon.baseMaxhp / 4);
},
},
},
outrage: {
inherit: true,
onMoveFail(target, source, move) {
source.addVolatile('lockedmove');
},
onAfterMove(pokemon) {
if (pokemon.volatiles['lockedmove'] && pokemon.volatiles['lockedmove'].duration === 1) {
pokemon.removeVolatile('lockedmove');
}
},
},
painsplit: {
inherit: true,
accuracy: 100,
},
perishsong: {
inherit: true,
condition: {
duration: 4,
onEnd(target) {
this.add('-start', target, 'perish0');
target.faint();
},
onResidualOrder: 4,
onResidual(pokemon) {
const duration = pokemon.volatiles['perishsong'].duration;
this.add('-start', pokemon, `perish${duration}`);
},
},
},
petaldance: {
inherit: true,
onMoveFail(target, source, move) {
source.addVolatile('lockedmove');
},
onAfterMove(pokemon) {
if (pokemon.volatiles['lockedmove'] && pokemon.volatiles['lockedmove'].duration === 1) {
pokemon.removeVolatile('lockedmove');
}
},
},
poisongas: {
inherit: true,
ignoreImmunity: false,
},
poisonpowder: {
inherit: true,
ignoreImmunity: false,
},
protect: {
inherit: true,
priority: 2,
},
psywave: {
inherit: true,
damageCallback(pokemon) {
return this.random(1, pokemon.level + Math.floor(pokemon.level / 2));
},
},
pursuit: {
inherit: true,
onModifyMove() {},
condition: {
duration: 1,
onBeforeSwitchOut(pokemon) {
this.debug('Pursuit start');
let alreadyAdded = false;
for (const source of this.effectState.sources) {
if (source.speed < pokemon.speed || (source.speed === pokemon.speed && this.randomChance(1, 2))) {
// Destiny Bond ends if the switch action "outspeeds" the attacker, regardless of host
pokemon.removeVolatile('destinybond');
}
if (!this.queue.cancelMove(source) || !source.hp) continue;
if (!alreadyAdded) {
this.add('-activate', pokemon, 'move: Pursuit');
alreadyAdded = true;
}
// Run through each action in queue to check if the Pursuit user is supposed to Mega Evolve this turn.
// If it is, then Mega Evolve before moving.
if (source.canMegaEvo || source.canUltraBurst) {
for (const [actionIndex, action] of this.queue.entries()) {
if (action.pokemon === source && action.choice === 'megaEvo') {
this.actions.runMegaEvo(source);
this.queue.list.splice(actionIndex, 1);
break;
}
}
}
this.actions.runMove('pursuit', source, source.getLocOf(pokemon));
}
},
},
},
razorleaf: {
inherit: true,
critRatio: 3,
},
razorwind: {
inherit: true,
accuracy: 75,
critRatio: 3,
onPrepareHit(target, source) {
return source.status !== 'slp';
},
},
reflect: {
inherit: true,
condition: {
duration: 5,
// Defense boost applied directly in stat calculation
onSideStart(side) {
this.add('-sidestart', side, 'Reflect');
},
onSideResidualOrder: 9,
onSideEnd(side) {
this.add('-sideend', side, 'Reflect');
},
},
},
rest: {
inherit: true,
onTry(pokemon) {
if (pokemon.hp < pokemon.maxhp) return;
this.add('-fail', pokemon);
return null;
},
onHit(target, source, move) {
if (target.status !== 'slp') {
if (!target.setStatus('slp', source, move)) return;
} else {
this.add('-status', target, 'slp', '[from] move: Rest');
}
target.statusState.time = 3;
target.statusState.startTime = 3;
target.statusState.source = target;
this.heal(target.maxhp);
},
secondary: null,
},
return: {
inherit: true,
basePowerCallback(pokemon) {
return Math.floor((pokemon.happiness * 10) / 25) || null;
},
},
reversal: {
inherit: true,
noDamageVariance: true,
willCrit: false,
},
roar: {
inherit: true,
onTryHit() {
for (const action of this.queue) {
// Roar only works if it is the last action in a turn, including when it's called by Sleep Talk
if (action.choice === 'move' || action.choice === 'switch') return false;
}
},
priority: -1,
},
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 === '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: 8,
onSideEnd(side) {
this.add('-sideend', side, 'Safeguard');
},
},
},
selfdestruct: {
inherit: true,
flags: { protect: 1, mirror: 1, metronome: 1, noparentalbond: 1, nosketch: 1 },
},
sketch: {
inherit: true,
flags: { bypasssub: 1, failencore: 1, noassist: 1, nosketch: 1 },
onHit() {
// Sketch always fails in Link Battles
this.add('-nothing');
},
},
skullbash: {
inherit: true,
onPrepareHit(target, source) {
return source.status !== 'slp';
},
},
skyattack: {
inherit: true,
critRatio: 1,
onPrepareHit(target, source) {
return source.status !== 'slp';
},
secondary: null,
},
slash: {
inherit: true,
critRatio: 3,
},
sleeptalk: {
inherit: true,
flags: { failencore: 1, nosleeptalk: 1, nosketch: 1 },
onHit(pokemon) {
const moves = [];
for (const moveSlot of pokemon.moveSlots) {
const moveid = moveSlot.id;
const move = this.dex.moves.get(moveid);
if (moveid && !move.flags['nosleeptalk'] && !move.flags['charge']) {
moves.push(moveid);
}
}
let randomMove = '';
if (moves.length) randomMove = this.sample(moves);
if (!randomMove) return false;
this.actions.useMove(randomMove, pokemon);
},
},
solarbeam: {
inherit: true,
onPrepareHit(target, source) {
return source.status !== 'slp';
},
// Rain weakening done directly in the damage formula
onBasePower() {},
},
spiderweb: {
inherit: true,
flags: { reflectable: 1, mirror: 1, metronome: 1 },
},
spikes: {
inherit: true,
condition: {
// this is a side condition
onSideStart(side) {
if (!this.effectState.layers || this.effectState.layers === 0) {
this.add('-sidestart', side, 'Spikes');
this.effectState.layers = 1;
} else {
return false;
}
},
onSwitchIn(pokemon) {
if (!pokemon.runImmunity('Ground')) return;
const damageAmounts = [0, 3];
this.damage(damageAmounts[this.effectState.layers] * pokemon.maxhp / 24);
},
},
},
substitute: {
inherit: true,
condition: {
onStart(target) {
this.add('-start', target, 'Substitute');
this.effectState.hp = Math.floor(target.maxhp / 4);
delete target.volatiles['partiallytrapped'];
},
onTryPrimaryHitPriority: -1,
onTryPrimaryHit(target, source, move) {
if (move.stallingMove) {
this.add('-fail', source);
return null;
}
if (target === source) {
this.debug('sub bypass: self hit');
return;
}
if (move.id === 'twineedle') {
move.secondaries = move.secondaries!.filter(p => !p.kingsrock);
}
if (move.drain) {
this.add('-miss', source);
this.hint("In Gen 2, draining moves always miss against Substitute.");
return null;
}
if (move.category === 'Status') {
const SubBlocked = ['leechseed', 'lockon', 'mindreader', 'nightmare', 'painsplit', 'sketch'];
if (move.id === 'swagger') {
// this is safe, move is a copy
delete move.volatileStatus;
}
if (
move.status || (move.boosts && move.id !== 'swagger') ||
move.volatileStatus === 'confusion' || SubBlocked.includes(move.id)
) {
this.add('-activate', target, 'Substitute', '[block] ' + move.name);
return null;
}
return;
}
let damage = this.actions.getDamage(source, target, move);
if (!damage) {
return null;
}
damage = this.runEvent('SubDamage', target, source, move, damage);
if (!damage) {
return damage;
}
if (damage > target.volatiles['substitute'].hp) {
damage = target.volatiles['substitute'].hp as number;
}
target.volatiles['substitute'].hp -= damage;
source.lastDamage = damage;
if (target.volatiles['substitute'].hp <= 0) {
target.removeVolatile('substitute');
} else {
this.add('-activate', target, 'Substitute', '[damage]');
}
if (move.recoil) {
this.damage(1, source, target, 'recoil');
}
this.runEvent('AfterSubDamage', target, source, move, damage);
return this.HIT_SUBSTITUTE;
},
onEnd(target) {
this.add('-end', target, 'Substitute');
},
},
},
swagger: {
inherit: true,
onTryHit(target, pokemon) {
if (target.boosts.atk >= 6 || target.getStat('atk', false, true) === 999) {
this.add('-miss', pokemon);
return null;
}
},
},
synthesis: {
inherit: true,
onHit(pokemon) {
if (this.field.isWeather(['sunnyday', 'desolateland'])) {
this.heal(pokemon.maxhp);
} else if (this.field.isWeather(['raindance', 'primordialsea', 'sandstorm', 'hail'])) {
this.heal(pokemon.baseMaxhp / 4);
} else {
this.heal(pokemon.baseMaxhp / 2);
}
},
},
thief: {
inherit: true,
onAfterHit() {},
secondary: {
chance: 100,
onHit(target, source) {
if (source.item || source.volatiles['gem']) {
return;
}
const yourItem = target.takeItem(source);
if (!yourItem) {
return;
}
if (!source.setItem(yourItem)) {
target.item = yourItem.id; // bypass setItem so we don't break choicelock or anything
return;
}
this.add('-item', source, yourItem, '[from] move: Thief', `[of] ${target}`);
},
},
},
thrash: {
inherit: true,
onMoveFail(target, source, move) {
source.addVolatile('lockedmove');
},
onAfterMove(pokemon) {
if (pokemon.volatiles['lockedmove'] && pokemon.volatiles['lockedmove'].duration === 1) {
pokemon.removeVolatile('lockedmove');
}
},
},
toxic: {
inherit: true,
ignoreImmunity: false,
},
transform: {
inherit: true,
flags: { bypasssub: 1, metronome: 1, failencore: 1, nosketch: 1 },
},
triattack: {
inherit: true,
onHit(target, source, move) {
move.statusRoll = ['par', 'frz', 'brn'][this.random(3)];
},
secondary: {
chance: 20,
onHit(target, source, move) {
if (move.statusRoll) {
target.trySetStatus(move.statusRoll, source);
}
},
},
},
triplekick: {
inherit: true,
multiaccuracy: false,
multihit: [1, 3],
},
whirlwind: {
inherit: true,
onTryHit() {
for (const action of this.queue) {
// Whirlwind only works if it is the last action in a turn, including when it's called by Sleep Talk
if (action.choice === 'move' || action.choice === 'switch') return false;
}
},
priority: -1,
},
};