Minecraft-STC-Modpack/showdown/data/mods/gen4/moves.js
2023-08-14 21:45:09 -04:00

1969 lines
52 KiB
JavaScript

"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var moves_exports = {};
__export(moves_exports, {
Moves: () => Moves
});
module.exports = __toCommonJS(moves_exports);
const Moves = {
acupressure: {
inherit: true,
flags: { snatch: 1 },
onHit(target) {
if (target.volatiles["substitute"]) {
return false;
}
const stats = [];
let stat;
for (stat in target.boosts) {
if (target.boosts[stat] < 6) {
stats.push(stat);
}
}
if (stats.length) {
const randomStat = this.sample(stats);
const boost = {};
boost[randomStat] = 2;
this.boost(boost);
} else {
return false;
}
}
},
aromatherapy: {
inherit: true,
onHit(target, source) {
this.add("-cureteam", source, "[from] move: Aromatherapy");
const allies = [...target.side.pokemon, ...target.side.allySide?.pokemon || []];
for (const ally of allies) {
ally.clearStatus();
}
}
},
aquaring: {
inherit: true,
flags: {},
condition: {
onStart(pokemon) {
this.add("-start", pokemon, "Aqua Ring");
},
onResidualOrder: 10,
onResidualSubOrder: 2,
onResidual(pokemon) {
this.heal(pokemon.baseMaxhp / 16);
}
}
},
beatup: {
inherit: true,
basePower: 10,
basePowerCallback(pokemon, target, move) {
if (!move.allies?.length)
return null;
return 10;
},
onModifyMove(move, pokemon) {
pokemon.addVolatile("beatup");
move.type = "???";
move.category = "Physical";
move.allies = pokemon.side.pokemon.filter((ally) => !ally.fainted && !ally.status);
move.multihit = move.allies.length;
},
condition: {
duration: 1,
onModifyAtkPriority: -101,
onModifyAtk(atk, pokemon, defender, move) {
this.event.modifier = 1;
return move.allies.shift().species.baseStats.atk;
},
onFoeModifyDefPriority: -101,
onFoeModifyDef(def, pokemon) {
this.event.modifier = 1;
return pokemon.species.baseStats.def;
}
}
},
bide: {
inherit: true,
condition: {
duration: 3,
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;
},
onAfterSetStatus(status, pokemon) {
if (status.id === "slp" || status.id === "frz") {
pokemon.removeVolatile("bide");
}
},
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: true,
damage: this.effectState.totalDamage * 2,
category: "Physical",
priority: 1,
flags: { contact: 1, protect: 1 },
ignoreImmunity: true,
effectType: "Move",
type: "Normal"
};
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]");
}
}
},
bind: {
inherit: true,
accuracy: 75
},
bonerush: {
inherit: true,
accuracy: 80
},
bravebird: {
inherit: true,
recoil: [1, 3]
},
bulletseed: {
inherit: true,
basePower: 10
},
camouflage: {
inherit: true,
onHit(target) {
if (target.hasType("Normal") || !target.setType("Normal"))
return false;
this.add("-start", target, "typechange", "Normal");
}
},
chatter: {
inherit: true,
secondary: {
chance: 31,
volatileStatus: "confusion"
}
},
clamp: {
inherit: true,
accuracy: 75,
pp: 10
},
conversion: {
inherit: true,
flags: {},
onHit(target) {
const possibleTypes = target.moveSlots.map((moveSlot) => {
const move = this.dex.moves.get(moveSlot.id);
if (move.id !== "conversion" && move.id !== "curse" && !target.hasType(move.type)) {
return move.type;
}
return "";
}).filter((type2) => type2);
if (!possibleTypes.length) {
return false;
}
const type = this.sample(possibleTypes);
if (!target.setType(type))
return false;
this.add("-start", target, "typechange", type);
}
},
cottonspore: {
inherit: true,
accuracy: 85
},
covet: {
inherit: true,
basePower: 40
},
crabhammer: {
inherit: true,
accuracy: 85
},
crushgrip: {
inherit: true,
basePowerCallback(pokemon, target) {
const bp = Math.floor(target.hp * 120 / target.maxhp) + 1;
this.debug("BP for " + target.hp + "/" + target.maxhp + " HP: " + bp);
return bp;
}
},
curse: {
inherit: true,
flags: {},
onModifyMove(move, source, target) {
if (!source.hasType("Ghost")) {
delete move.volatileStatus;
delete move.onHit;
move.self = { boosts: { atk: 1, def: 1, spe: -1 } };
move.target = move.nonGhostTarget;
} else if (target?.volatiles["substitute"]) {
delete move.volatileStatus;
delete move.onHit;
}
},
condition: {
onStart(pokemon, source) {
this.add("-start", pokemon, "Curse", "[of] " + source);
},
onResidualOrder: 10,
onResidualSubOrder: 8,
onResidual(pokemon) {
this.damage(pokemon.baseMaxhp / 4);
}
},
type: "???"
},
defog: {
inherit: true,
flags: { protect: 1, mirror: 1, bypasssub: 1 }
},
detect: {
inherit: true,
priority: 3,
condition: {
duration: 1,
onStart(target) {
this.add("-singleturn", target, "Protect");
},
onTryHitPriority: 3,
onTryHit(target, source, move) {
if (!move.flags["protect"])
return;
this.add("-activate", target, "Protect");
const lockedmove = source.getVolatile("lockedmove");
if (lockedmove) {
if (source.volatiles["lockedmove"].trueDuration >= 2) {
source.volatiles["lockedmove"].duration = 2;
}
}
return null;
}
}
},
disable: {
inherit: true,
accuracy: 80,
flags: { protect: 1, mirror: 1, bypasssub: 1 },
volatileStatus: "disable",
condition: {
durationCallback() {
return this.random(4, 8);
},
noCopy: true,
onStart(pokemon) {
if (!this.queue.willMove(pokemon)) {
this.effectState.duration++;
}
if (!pokemon.lastMove) {
return false;
}
for (const moveSlot of pokemon.moveSlots) {
if (moveSlot.id === pokemon.lastMove.id) {
if (!moveSlot.pp) {
return false;
} else {
this.add("-start", pokemon, "Disable", moveSlot.move);
this.effectState.move = pokemon.lastMove.id;
return;
}
}
}
return false;
},
onResidualOrder: 10,
onResidualSubOrder: 13,
onEnd(pokemon) {
this.add("-end", pokemon, "move: Disable");
},
onBeforeMovePriority: 7,
onBeforeMove(attacker, defender, move) {
if (move.id === this.effectState.move) {
this.add("cant", attacker, "Disable", move);
return false;
}
},
onDisableMove(pokemon) {
for (const moveSlot of pokemon.moveSlots) {
if (moveSlot.id === this.effectState.move) {
pokemon.disableMove(moveSlot.id);
}
}
}
}
},
doomdesire: {
inherit: true,
accuracy: 85,
basePower: 120,
onTry(source, target) {
if (!target.side.addSlotCondition(target, "futuremove"))
return false;
const moveData = {
name: "Doom Desire",
basePower: 120,
category: "Special",
flags: { futuremove: 1 },
willCrit: false,
type: "???"
};
const damage = this.actions.getDamage(source, target, moveData, true);
Object.assign(target.side.slotConditions[target.position]["futuremove"], {
duration: 3,
move: "doomdesire",
source,
moveData: {
id: "doomdesire",
name: "Doom Desire",
accuracy: 85,
basePower: 0,
damage,
category: "Special",
flags: { futuremove: 1 },
effectType: "Move",
type: "???"
}
});
this.add("-start", source, "Doom Desire");
return null;
}
},
doubleedge: {
inherit: true,
recoil: [1, 3]
},
drainpunch: {
inherit: true,
basePower: 60,
pp: 5
},
dreameater: {
inherit: true,
onTryImmunity(target) {
return target.status === "slp" && !target.volatiles["substitute"];
}
},
embargo: {
inherit: true,
flags: { protect: 1, mirror: 1 },
onTryHit(pokemon) {
if (pokemon.ability === "multitype" || pokemon.item === "griseousorb") {
return false;
}
},
condition: {
duration: 5,
onStart(pokemon) {
this.add("-start", pokemon, "Embargo");
},
// Item suppression implemented in Pokemon.ignoringItem() within sim/pokemon.js
onResidualOrder: 10,
onResidualSubOrder: 18,
onEnd(pokemon) {
this.add("-end", pokemon, "Embargo");
}
}
},
encore: {
inherit: true,
flags: { protect: 1, mirror: 1, bypasssub: 1, failencore: 1 },
volatileStatus: "encore",
condition: {
durationCallback() {
return this.random(4, 9);
},
onStart(target, source) {
const moveIndex = target.lastMove ? target.moves.indexOf(target.lastMove.id) : -1;
if (!target.lastMove || target.lastMove.flags["failencore"] || !target.moveSlots[moveIndex] || target.moveSlots[moveIndex].pp <= 0) {
return false;
}
this.effectState.move = target.lastMove.id;
this.add("-start", target, "Encore");
},
onOverrideAction(pokemon) {
return this.effectState.move;
},
onResidualOrder: 10,
onResidualSubOrder: 14,
onResidual(target) {
if (target.moves.includes(this.effectState.move) && target.moveSlots[target.moves.indexOf(this.effectState.move)].pp <= 0) {
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);
}
}
}
}
},
endeavor: {
inherit: true,
onTry(pokemon, target) {
if (pokemon.hp >= target.hp) {
this.add("-fail", pokemon);
return null;
}
}
},
extremespeed: {
inherit: true,
priority: 1
},
fakeout: {
inherit: true,
priority: 1
},
feint: {
inherit: true,
basePower: 50,
onTry(source, target) {
if (!target.volatiles["protect"]) {
this.add("-fail", source);
return null;
}
}
},
firespin: {
inherit: true,
accuracy: 70,
basePower: 15
},
flail: {
inherit: true,
basePowerCallback(pokemon, target) {
const ratio = Math.max(Math.floor(pokemon.hp * 64 / pokemon.maxhp), 1);
let bp;
if (ratio < 2) {
bp = 200;
} else if (ratio < 6) {
bp = 150;
} else if (ratio < 13) {
bp = 100;
} else if (ratio < 22) {
bp = 80;
} else if (ratio < 43) {
bp = 40;
} else {
bp = 20;
}
this.debug("BP: " + bp);
return bp;
}
},
flareblitz: {
inherit: true,
recoil: [1, 3]
},
focuspunch: {
inherit: true,
priorityChargeCallback() {
},
beforeTurnCallback(pokemon) {
pokemon.addVolatile("focuspunch");
},
beforeMoveCallback() {
},
onTry(pokemon) {
if (pokemon.volatiles["focuspunch"]?.lostFocus) {
this.attrLastMove("[still]");
this.add("cant", pokemon, "Focus Punch", "Focus Punch");
return null;
}
}
},
foresight: {
inherit: true,
flags: { protect: 1, mirror: 1, bypasssub: 1 }
},
furycutter: {
inherit: true,
basePower: 10,
condition: {
duration: 2,
onStart() {
this.effectState.multiplier = 1;
},
onRestart() {
if (this.effectState.multiplier < 16) {
this.effectState.multiplier <<= 1;
}
this.effectState.duration = 2;
}
}
},
futuresight: {
inherit: true,
accuracy: 90,
basePower: 80,
pp: 15,
onTry(source, target) {
if (!target.side.addSlotCondition(target, "futuremove"))
return false;
const moveData = {
name: "Future Sight",
basePower: 80,
category: "Special",
flags: { futuremove: 1 },
willCrit: false,
type: "???"
};
const damage = this.actions.getDamage(source, target, moveData, true);
Object.assign(target.side.slotConditions[target.position]["futuremove"], {
duration: 3,
move: "futuresight",
source,
moveData: {
id: "futuresight",
name: "Future Sight",
accuracy: 90,
basePower: 0,
damage,
category: "Special",
flags: { futuremove: 1 },
effectType: "Move",
type: "???"
}
});
this.add("-start", source, "Future Sight");
return null;
}
},
gigadrain: {
inherit: true,
basePower: 60
},
glare: {
inherit: true,
accuracy: 75
},
gravity: {
inherit: true,
condition: {
duration: 5,
durationCallback(source, effect) {
if (source?.hasAbility("persistent")) {
this.add("-activate", source, "ability: Persistent", "[move] Gravity");
return 7;
}
return 5;
},
onFieldStart(target, source) {
if (source?.hasAbility("persistent")) {
this.add("-fieldstart", "move: Gravity", "[persistent]");
} else {
this.add("-fieldstart", "move: Gravity");
}
for (const pokemon of this.getAllActive()) {
let applies = false;
if (pokemon.removeVolatile("bounce") || pokemon.removeVolatile("fly")) {
applies = true;
this.queue.cancelMove(pokemon);
pokemon.removeVolatile("twoturnmove");
}
if (pokemon.volatiles["skydrop"]) {
applies = true;
this.queue.cancelMove(pokemon);
if (pokemon.volatiles["skydrop"].source) {
this.add("-end", pokemon.volatiles["twoturnmove"].source, "Sky Drop", "[interrupt]");
}
pokemon.removeVolatile("skydrop");
pokemon.removeVolatile("twoturnmove");
}
if (pokemon.volatiles["magnetrise"]) {
applies = true;
delete pokemon.volatiles["magnetrise"];
}
if (pokemon.volatiles["telekinesis"]) {
applies = true;
delete pokemon.volatiles["telekinesis"];
}
if (applies)
this.add("-activate", pokemon, "move: Gravity");
}
},
onModifyAccuracy(accuracy) {
if (typeof accuracy !== "number")
return;
return this.chainModify([6840, 4096]);
},
onDisableMove(pokemon) {
for (const moveSlot of pokemon.moveSlots) {
if (this.dex.moves.get(moveSlot.id).flags["gravity"]) {
pokemon.disableMove(moveSlot.id);
}
}
},
// groundedness implemented in battle.engine.js:BattlePokemon#isGrounded
onBeforeMovePriority: 6,
onBeforeMove(pokemon, target, move) {
if (move.flags["gravity"] && !move.isZ) {
this.add("cant", pokemon, "move: Gravity", move);
return false;
}
},
onModifyMove(move, pokemon, target) {
if (move.flags["gravity"] && !move.isZ) {
this.add("cant", pokemon, "move: Gravity", move);
return false;
}
},
onFieldResidualOrder: 9,
onFieldEnd() {
this.add("-fieldend", "move: Gravity");
}
}
},
growth: {
inherit: true,
onModifyMove() {
},
boosts: {
spa: 1
}
},
healbell: {
inherit: true,
onHit(target, source) {
this.add("-activate", source, "move: Heal Bell");
const allies = [...target.side.pokemon, ...target.side.allySide?.pokemon || []];
for (const ally of allies) {
if (!ally.hasAbility("soundproof"))
ally.cureStatus(true);
}
}
},
healblock: {
inherit: true,
flags: { protect: 1, mirror: 1 },
condition: {
duration: 5,
durationCallback(target, source, effect) {
if (source?.hasAbility("persistent")) {
this.add("-activate", source, "ability: Persistent", "[move] Heal Block");
return 7;
}
return 5;
},
onStart(pokemon) {
this.add("-start", pokemon, "move: Heal Block");
},
onDisableMove(pokemon) {
for (const moveSlot of pokemon.moveSlots) {
if (this.dex.moves.get(moveSlot.id).flags["heal"]) {
pokemon.disableMove(moveSlot.id);
}
}
},
onBeforeMovePriority: 6,
onBeforeMove(pokemon, target, move) {
if (move.flags["heal"]) {
this.add("cant", pokemon, "move: Heal Block", move);
return false;
}
},
onResidualOrder: 10,
onResidualSubOrder: 17,
onEnd(pokemon) {
this.add("-end", pokemon, "move: Heal Block");
},
onTryHeal(damage, pokemon, source, effect) {
if (effect && (effect.id === "drain" || effect.id === "leechseed" || effect.id === "wish")) {
return false;
}
}
}
},
healingwish: {
inherit: true,
flags: { heal: 1 },
onAfterMove(pokemon) {
pokemon.switchFlag = true;
},
condition: {
duration: 1,
onSwitchInPriority: -1,
onSwitchIn(target) {
if (target.hp > 0) {
target.heal(target.maxhp);
target.clearStatus();
this.add("-heal", target, target.getHealth, "[from] move: Healing Wish");
target.side.removeSlotCondition(target, "healingwish");
target.lastMove = this.lastMove;
} else {
target.switchFlag = true;
}
}
}
},
highjumpkick: {
inherit: true,
basePower: 100,
pp: 20,
onMoveFail(target, source, move) {
move.causedCrashDamage = true;
let damage = this.actions.getDamage(source, target, move, true);
if (!damage)
damage = target.maxhp;
this.damage(this.clampIntRange(damage / 2, 1, Math.floor(target.maxhp / 2)), source, source, move);
}
},
iciclespear: {
inherit: true,
basePower: 10
},
imprison: {
inherit: true,
flags: { bypasssub: 1 },
onTryHit(pokemon) {
for (const target of pokemon.foes()) {
for (const move of pokemon.moves) {
if (target.moves.includes(move))
return;
}
}
return false;
}
},
ingrain: {
inherit: true,
condition: {
onStart(pokemon) {
this.add("-start", pokemon, "move: Ingrain");
},
onResidualOrder: 10,
onResidualSubOrder: 1,
onResidual(pokemon) {
this.heal(pokemon.baseMaxhp / 16);
},
onTrapPokemon(pokemon) {
pokemon.tryTrap();
},
// groundedness implemented in battle.engine.js:BattlePokemon#isGrounded
onDragOut(pokemon) {
this.add("-activate", pokemon, "move: Ingrain");
return null;
}
}
},
jumpkick: {
inherit: true,
basePower: 85,
pp: 25,
onMoveFail(target, source, move) {
move.causedCrashDamage = true;
let damage = this.actions.getDamage(source, target, move, true);
if (!damage)
damage = target.maxhp;
this.damage(this.clampIntRange(damage / 2, 1, Math.floor(target.maxhp / 2)), source, source, move);
}
},
knockoff: {
inherit: true,
onAfterHit(target, source, move) {
if (!target.item || target.itemState.knockedOff)
return;
if (target.ability === "multitype")
return;
const item = target.getItem();
if (this.runEvent("TakeItem", target, source, move, item)) {
target.itemState.knockedOff = true;
this.add("-enditem", target, item.name, "[from] move: Knock Off");
this.hint("In Gens 3-4, Knock Off only makes the target's item unusable; it cannot obtain a new item.", true);
}
}
},
lastresort: {
inherit: true,
basePower: 130
},
leechseed: {
inherit: true,
condition: {
onStart(target) {
this.add("-start", target, "move: Leech Seed");
},
onResidualOrder: 10,
onResidualSubOrder: 5,
onResidual(pokemon) {
const target = this.getAtSlot(pokemon.volatiles["leechseed"].sourceSlot);
if (!target || target.fainted || target.hp <= 0) {
this.debug("Nothing to leech into");
return;
}
const damage = this.damage(pokemon.baseMaxhp / 8, pokemon, target);
if (damage) {
this.heal(damage, target, pokemon);
}
}
}
},
lightscreen: {
inherit: true,
condition: {
duration: 5,
durationCallback(target, source, effect) {
if (source?.hasItem("lightclay")) {
return 8;
}
return 5;
},
onAnyModifyDamagePhase1(damage, source, target, move) {
if (target !== source && this.effectState.target.hasAlly(target) && this.getCategory(move) === "Special") {
if (!target.getMoveHitData(move).crit && !move.infiltrates) {
this.debug("Light Screen weaken");
if (target.alliesAndSelf().length > 1)
return this.chainModify(2, 3);
return this.chainModify(0.5);
}
}
},
onSideStart(side) {
this.add("-sidestart", side, "Light Screen");
},
onSideResidualOrder: 2,
onSideEnd(side) {
this.add("-sideend", side, "Light Screen");
}
}
},
lockon: {
inherit: true,
condition: {
duration: 2,
onSourceInvulnerabilityPriority: 1,
onSourceInvulnerability(target, source, move) {
if (move && source === this.effectState.target && target === this.effectState.source)
return 0;
},
onSourceAccuracy(accuracy, target, source, move) {
if (move && source === this.effectState.target && target === this.effectState.source)
return true;
}
}
},
luckychant: {
inherit: true,
flags: {},
condition: {
duration: 5,
onSideStart(side) {
this.add("-sidestart", side, "move: Lucky Chant");
},
onCriticalHit: false,
onSideResidualOrder: 6,
onSideEnd(side) {
this.add("-sideend", side, "move: Lucky Chant");
}
}
},
lunardance: {
inherit: true,
flags: { heal: 1 },
onAfterMove(pokemon) {
pokemon.switchFlag = true;
},
condition: {
duration: 1,
onSideStart(side) {
this.debug("Lunar Dance started on " + side.name);
},
onSwitchInPriority: -1,
onSwitchIn(target) {
if (target.getSlot() !== this.effectState.sourceSlot) {
return;
}
if (target.hp > 0) {
target.heal(target.maxhp);
target.clearStatus();
for (const moveSlot of target.moveSlots) {
moveSlot.pp = moveSlot.maxpp;
}
this.add("-heal", target, target.getHealth, "[from] move: Lunar Dance");
target.side.removeSlotCondition(target, "lunardance");
target.lastMove = this.lastMove;
} else {
target.switchFlag = true;
}
}
}
},
magiccoat: {
inherit: true,
condition: {
duration: 1,
onTryHitPriority: 2,
onTryHit(target, source, move) {
if (target === source || move.hasBounced || !move.flags["reflectable"]) {
return;
}
target.removeVolatile("magiccoat");
const newMove = this.dex.getActiveMove(move.id);
newMove.hasBounced = true;
this.actions.useMove(newMove, target, source);
return null;
}
}
},
magmastorm: {
inherit: true,
accuracy: 70
},
magnetrise: {
inherit: true,
flags: { gravity: 1 },
volatileStatus: "magnetrise",
condition: {
duration: 5,
onStart(target) {
if (target.volatiles["ingrain"] || target.ability === "levitate")
return false;
this.add("-start", target, "Magnet Rise");
},
onImmunity(type) {
if (type === "Ground")
return false;
},
onResidualOrder: 10,
onResidualSubOrder: 16,
onEnd(target) {
this.add("-end", target, "Magnet Rise");
}
}
},
mefirst: {
inherit: true,
condition: {
duration: 1,
onModifyDamagePhase2(damage) {
return damage * 1.5;
}
}
},
metalburst: {
inherit: true,
flags: { protect: 1, mirror: 1 }
},
metronome: {
inherit: true,
flags: { noassist: 1, failcopycat: 1, nosleeptalk: 1, failmimic: 1 },
noMetronome: [
"Assist",
"Chatter",
"Copycat",
"Counter",
"Covet",
"Destiny Bond",
"Detect",
"Endure",
"Feint",
"Focus Punch",
"Follow Me",
"Helping Hand",
"Me First",
"Metronome",
"Mimic",
"Mirror Coat",
"Mirror Move",
"Protect",
"Sketch",
"Sleep Talk",
"Snatch",
"Struggle",
"Switcheroo",
"Thief",
"Trick"
]
},
mimic: {
inherit: true,
flags: {
protect: 1,
bypasssub: 1,
allyanim: 1,
noassist: 1,
failcopycat: 1,
failencore: 1,
failinstruct: 1,
failmimic: 1
},
onHit(target, source) {
if (source.transformed || !target.lastMove || target.volatiles["substitute"]) {
return false;
}
if (target.lastMove.flags["failmimic"] || source.moves.includes(target.lastMove.id)) {
return false;
}
const mimicIndex = source.moves.indexOf("mimic");
if (mimicIndex < 0)
return false;
const move = this.dex.moves.get(target.lastMove.id);
source.moveSlots[mimicIndex] = {
move: move.name,
id: move.id,
pp: 5,
maxpp: move.pp * 8 / 5,
disabled: false,
used: false,
virtual: true
};
this.add("-activate", source, "move: Mimic", move.name);
}
},
minimize: {
inherit: true,
boosts: {
evasion: 1
}
},
miracleeye: {
inherit: true,
flags: { protect: 1, mirror: 1, bypasssub: 1 }
},
mirrormove: {
inherit: true,
onTryHit() {
},
onHit(pokemon) {
const lastAttackedBy = pokemon.getLastAttackedBy();
if (!lastAttackedBy?.source.lastMove || !lastAttackedBy.move) {
return false;
}
const noMirror = [
"acupressure",
"aromatherapy",
"assist",
"chatter",
"copycat",
"counter",
"curse",
"doomdesire",
"feint",
"focuspunch",
"futuresight",
"gravity",
"hail",
"haze",
"healbell",
"helpinghand",
"lightscreen",
"luckychant",
"magiccoat",
"mefirst",
"metronome",
"mimic",
"mirrorcoat",
"mirrormove",
"mist",
"mudsport",
"naturepower",
"perishsong",
"psychup",
"raindance",
"reflect",
"roleplay",
"safeguard",
"sandstorm",
"sketch",
"sleeptalk",
"snatch",
"spikes",
"spitup",
"stealthrock",
"struggle",
"sunnyday",
"tailwind",
"toxicspikes",
"transform",
"watersport"
];
if (noMirror.includes(lastAttackedBy.move) || !lastAttackedBy.source.hasMove(lastAttackedBy.move)) {
return false;
}
this.actions.useMove(lastAttackedBy.move, pokemon);
},
target: "self"
},
mist: {
inherit: true,
condition: {
duration: 5,
onTryBoost(boost, target, source, effect) {
if (effect.effectType === "Move" && effect.infiltrates && !target.isAlly(source))
return;
if (source && target !== source) {
let showMsg = false;
let i;
for (i in boost) {
if (boost[i] < 0) {
delete boost[i];
showMsg = true;
}
}
if (showMsg && !effect.secondaries) {
this.add("-activate", target, "move: Mist");
}
}
},
onSideStart(side) {
this.add("-sidestart", side, "Mist");
},
onSideResidualOrder: 3,
onSideEnd(side) {
this.add("-sideend", side, "Mist");
}
}
},
moonlight: {
inherit: true,
onHit(pokemon) {
if (this.field.isWeather(["sunnyday", "desolateland"])) {
this.heal(pokemon.maxhp * 2 / 3);
} 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 * 2 / 3);
} else if (this.field.isWeather(["raindance", "primordialsea", "sandstorm", "hail"])) {
this.heal(pokemon.baseMaxhp / 4);
} else {
this.heal(pokemon.baseMaxhp / 2);
}
}
},
mudsport: {
inherit: true,
condition: {
noCopy: true,
onStart(pokemon) {
this.add("-start", pokemon, "move: Mud Sport");
},
onBasePowerPriority: 3,
onAnyBasePower(basePower, user, target, move) {
if (move.type === "Electric")
return this.chainModify(0.5);
}
}
},
naturepower: {
inherit: true,
flags: {},
onHit(pokemon) {
this.actions.useMove("triattack", pokemon);
}
},
nightmare: {
inherit: true,
condition: {
noCopy: true,
onStart(pokemon) {
if (pokemon.status !== "slp" && !pokemon.hasAbility("comatose")) {
return false;
}
this.add("-start", pokemon, "Nightmare");
},
onResidualOrder: 10,
onResidualSubOrder: 7,
onResidual(pokemon) {
this.damage(pokemon.baseMaxhp / 4);
}
}
},
odorsleuth: {
inherit: true,
flags: { protect: 1, mirror: 1, bypasssub: 1 }
},
outrage: {
inherit: true,
pp: 15,
onAfterMove() {
}
},
payback: {
inherit: true,
basePowerCallback(pokemon, target) {
if (this.queue.willMove(target)) {
return 50;
}
this.debug("BP doubled");
return 100;
}
},
payday: {
inherit: true,
onHit() {
this.add("-fieldactivate", "move: Pay Day");
}
},
perishsong: {
inherit: true,
condition: {
duration: 4,
onEnd(target) {
this.add("-start", target, "perish0");
target.faint();
},
onResidualOrder: 12,
onResidual(pokemon) {
const duration = pokemon.volatiles["perishsong"].duration;
this.add("-start", pokemon, "perish" + duration);
}
}
},
petaldance: {
inherit: true,
basePower: 90,
pp: 20,
onAfterMove() {
}
},
poisongas: {
inherit: true,
accuracy: 55,
target: "normal"
},
powertrick: {
inherit: true,
flags: {}
},
protect: {
inherit: true,
priority: 3,
condition: {
duration: 1,
onStart(target) {
this.add("-singleturn", target, "Protect");
},
onTryHitPriority: 3,
onTryHit(target, source, move) {
if (!move.flags["protect"])
return;
this.add("-activate", target, "Protect");
const lockedmove = source.getVolatile("lockedmove");
if (lockedmove) {
if (source.volatiles["lockedmove"].trueDuration >= 2) {
source.volatiles["lockedmove"].duration = 2;
}
}
return null;
}
}
},
psychup: {
inherit: true,
flags: { snatch: 1, bypasssub: 1 }
},
pursuit: {
inherit: true,
condition: {
duration: 1,
onBeforeSwitchOut(pokemon) {
this.debug("Pursuit start");
let alreadyAdded = false;
for (const source of this.effectState.sources) {
if (!this.queue.cancelMove(source) || !source.hp)
continue;
if (!alreadyAdded) {
this.add("-activate", pokemon, "move: Pursuit");
alreadyAdded = true;
}
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));
}
}
}
},
rapidspin: {
inherit: true,
self: {
onHit(pokemon) {
if (pokemon.removeVolatile("leechseed")) {
this.add("-end", pokemon, "Leech Seed", "[from] move: Rapid Spin", "[of] " + pokemon);
}
const sideConditions = ["spikes", "toxicspikes", "stealthrock", "stickyweb"];
for (const condition of sideConditions) {
if (pokemon.side.removeSideCondition(condition)) {
this.add("-sideend", pokemon.side, this.dex.conditions.get(condition).name, "[from] move: Rapid Spin", "[of] " + pokemon);
}
}
if (pokemon.volatiles["partiallytrapped"]) {
pokemon.removeVolatile("partiallytrapped");
}
}
}
},
recycle: {
inherit: true,
flags: {}
},
reflect: {
inherit: true,
condition: {
duration: 5,
durationCallback(target, source, effect) {
if (source?.hasItem("lightclay")) {
return 8;
}
return 5;
},
onAnyModifyDamagePhase1(damage, source, target, move) {
if (target !== source && this.effectState.target.hasAlly(target) && this.getCategory(move) === "Physical") {
if (!target.getMoveHitData(move).crit && !move.infiltrates) {
this.debug("Reflect weaken");
if (target.alliesAndSelf().length > 1)
return this.chainModify(2, 3);
return this.chainModify(0.5);
}
}
},
onSideStart(side) {
this.add("-sidestart", side, "Reflect");
},
onSideResidualOrder: 1,
onSideEnd(side) {
this.add("-sideend", side, "Reflect");
}
}
},
reversal: {
inherit: true,
basePowerCallback(pokemon, target) {
const ratio = Math.max(Math.floor(pokemon.hp * 64 / pokemon.maxhp), 1);
let bp;
if (ratio < 2) {
bp = 200;
} else if (ratio < 6) {
bp = 150;
} else if (ratio < 13) {
bp = 100;
} else if (ratio < 22) {
bp = 80;
} else if (ratio < 43) {
bp = 40;
} else {
bp = 20;
}
this.debug("BP: " + bp);
return bp;
}
},
roar: {
inherit: true,
flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1 }
},
rockblast: {
inherit: true,
accuracy: 80
},
roleplay: {
inherit: true,
onTryHit(target, source) {
if (target.ability === source.ability || source.hasItem("griseousorb"))
return false;
const bannedTargetAbilities = ["multitype", "wonderguard"];
if (bannedTargetAbilities.includes(target.ability) || source.ability === "multitype") {
return false;
}
}
},
safeguard: {
inherit: true,
condition: {
duration: 5,
durationCallback(target, source, effect) {
if (source?.hasAbility("persistent")) {
this.add("-activate", source, "ability: Persistent", "[move] Safeguard");
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, source) {
if (source?.hasAbility("persistent")) {
this.add("-sidestart", side, "Safeguard", "[persistent]");
} else {
this.add("-sidestart", side, "Safeguard");
}
},
onSideResidualOrder: 4,
onSideEnd(side) {
this.add("-sideend", side, "Safeguard");
}
}
},
sandtomb: {
inherit: true,
accuracy: 70,
basePower: 15
},
scaryface: {
inherit: true,
accuracy: 90
},
secretpower: {
inherit: true,
secondary: {
chance: 30,
status: "par"
}
},
sketch: {
inherit: true,
flags: { bypasssub: 1, allyanim: 1, failencore: 1, noassist: 1, failcopycat: 1, failinstruct: 1, failmimic: 1 },
onHit(target, source) {
const disallowedMoves = ["chatter", "sketch", "struggle"];
if (source.transformed || !target.lastMove || target.volatiles["substitute"]) {
return false;
}
if (disallowedMoves.includes(target.lastMove.id) || source.moves.includes(target.lastMove.id)) {
return false;
}
const sketchIndex = source.moves.indexOf("sketch");
if (sketchIndex < 0)
return false;
const move = this.dex.moves.get(target.lastMove.id);
const sketchedMove = {
move: move.name,
id: move.id,
pp: move.pp,
maxpp: move.pp,
disabled: false,
used: false
};
source.moveSlots[sketchIndex] = sketchedMove;
source.baseMoveSlots[sketchIndex] = sketchedMove;
this.add("-activate", source, "move: Mimic", move.name);
}
},
skillswap: {
inherit: true,
onHit(target, source) {
const targetAbility = target.ability;
const sourceAbility = source.ability;
if (targetAbility === sourceAbility || source.hasItem("griseousorb") || target.hasItem("griseousorb")) {
return false;
}
this.add("-activate", source, "move: Skill Swap");
source.setAbility(targetAbility);
target.setAbility(sourceAbility);
}
},
sleeptalk: {
inherit: true,
onTryHit(pokemon) {
return !pokemon.volatiles["choicelock"] && !pokemon.volatiles["encore"];
}
},
snatch: {
inherit: true,
flags: { bypasssub: 1, noassist: 1, failcopycat: 1 },
condition: {
duration: 1,
onStart(pokemon) {
this.add("-singleturn", pokemon, "Snatch");
},
onAnyPrepareHitPriority: -1,
onAnyPrepareHit(source, target, move) {
const snatchUser = this.effectState.source;
if (snatchUser.isSkyDropped())
return;
if (!move || move.isZ || move.isMax || !move.flags["snatch"]) {
return;
}
snatchUser.removeVolatile("snatch");
this.add("-activate", snatchUser, "move: Snatch", "[of] " + source);
this.actions.useMove(move.id, snatchUser);
return null;
}
}
},
spikes: {
inherit: true,
flags: {}
},
spite: {
inherit: true,
flags: { protect: 1, mirror: 1, bypasssub: 1 }
},
stealthrock: {
inherit: true,
flags: {}
},
struggle: {
inherit: true,
flags: {
contact: 1,
protect: 1,
failencore: 1,
failmefirst: 1,
noassist: 1,
failcopycat: 1,
failinstruct: 1,
failmimic: 1
},
onModifyMove(move) {
move.type = "???";
}
},
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 (target === source || move.flags["bypasssub"]) {
return;
}
let damage = this.actions.getDamage(source, target, move);
if (!damage && damage !== 0) {
this.add("-fail", source);
this.attrLastMove("[still]");
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;
}
target.volatiles["substitute"].hp -= damage;
source.lastDamage = damage;
if (target.volatiles["substitute"].hp <= 0) {
target.removeVolatile("substitute");
target.addVolatile("substitutebroken");
if (target.volatiles["substitutebroken"])
target.volatiles["substitutebroken"].move = move.id;
if (move.ohko)
this.add("-ohko");
} else {
this.add("-activate", target, "Substitute", "[damage]");
}
if (move.recoil && damage) {
this.damage(this.actions.calcRecoilDamage(damage, move), source, target, "recoil");
}
if (move.drain) {
this.heal(Math.ceil(damage * move.drain[0] / move.drain[1]), source, target, "drain");
}
this.runEvent("AfterSubDamage", target, source, move, damage);
return this.HIT_SUBSTITUTE;
},
onEnd(target) {
this.add("-end", target, "Substitute");
}
}
},
suckerpunch: {
inherit: true,
onTry(source, target) {
const action = this.queue.willMove(target);
if (!action || action.choice !== "move" || action.move.category === "Status" || target.volatiles["mustrecharge"]) {
this.add("-fail", source);
return null;
}
}
},
switcheroo: {
inherit: true,
onTryHit(target, source, move) {
if (target.hasAbility("multitype") || source.hasAbility("multitype"))
return false;
}
},
synthesis: {
inherit: true,
onHit(pokemon) {
if (this.field.isWeather(["sunnyday", "desolateland"])) {
this.heal(pokemon.maxhp * 2 / 3);
} else if (this.field.isWeather(["raindance", "primordialsea", "sandstorm", "hail"])) {
this.heal(pokemon.baseMaxhp / 4);
} else {
this.heal(pokemon.baseMaxhp / 2);
}
}
},
tackle: {
inherit: true,
accuracy: 95,
basePower: 35
},
tailglow: {
inherit: true,
boosts: {
spa: 2
}
},
tailwind: {
inherit: true,
condition: {
duration: 3,
durationCallback(target, source, effect) {
if (source?.hasAbility("persistent")) {
this.add("-activate", source, "ability: Persistent", "[move] Tailwind");
return 5;
}
return 3;
},
onSideStart(side, source) {
if (source?.hasAbility("persistent")) {
this.add("-sidestart", side, "move: Tailwind", "[persistent]");
} else {
this.add("-sidestart", side, "move: Tailwind");
}
},
onModifySpe(spe) {
return spe * 2;
},
onSideResidualOrder: 5,
onSideEnd(side) {
this.add("-sideend", side, "move: Tailwind");
}
}
},
taunt: {
inherit: true,
flags: { protect: 1, mirror: 1, bypasssub: 1 },
condition: {
durationCallback() {
return this.random(3, 6);
},
onStart(target) {
this.add("-start", target, "move: Taunt");
},
onResidualOrder: 10,
onResidualSubOrder: 15,
onEnd(target) {
this.add("-end", target, "move: Taunt");
},
onDisableMove(pokemon) {
for (const moveSlot of pokemon.moveSlots) {
if (this.dex.moves.get(moveSlot.id).category === "Status") {
pokemon.disableMove(moveSlot.id);
}
}
},
onBeforeMovePriority: 5,
onBeforeMove(attacker, defender, move) {
if (move.category === "Status") {
this.add("cant", attacker, "move: Taunt", move);
return false;
}
}
}
},
thrash: {
inherit: true,
basePower: 90,
pp: 20,
onAfterMove() {
}
},
torment: {
inherit: true,
flags: { protect: 1, mirror: 1, bypasssub: 1 }
},
toxic: {
inherit: true,
accuracy: 85
},
toxicspikes: {
inherit: true,
flags: {},
condition: {
// this is a side condition
onSideStart(side) {
this.add("-sidestart", side, "move: Toxic Spikes");
this.effectState.layers = 1;
},
onSideRestart(side) {
if (this.effectState.layers >= 2)
return false;
this.add("-sidestart", side, "move: Toxic Spikes");
this.effectState.layers++;
},
onEntryHazard(pokemon) {
if (!pokemon.isGrounded())
return;
if (pokemon.hasType("Poison")) {
this.add("-sideend", pokemon.side, "move: Toxic Spikes", "[of] " + pokemon);
pokemon.side.removeSideCondition("toxicspikes");
} else if (pokemon.volatiles["substitute"] || pokemon.hasType("Steel")) {
return;
} else if (this.effectState.layers >= 2) {
pokemon.trySetStatus("tox", pokemon.side.foe.active[0]);
} else {
pokemon.trySetStatus("psn", pokemon.side.foe.active[0]);
}
}
}
},
transform: {
inherit: true,
flags: { bypasssub: 1, failencore: 1 }
},
trick: {
inherit: true,
onTryHit(target, source, move) {
if (target.hasAbility("multitype") || source.hasAbility("multitype"))
return false;
}
},
trickroom: {
inherit: true,
condition: {
duration: 5,
durationCallback(source, effect) {
if (source?.hasAbility("persistent")) {
this.add("-activate", source, "ability: Persistent", "[move] Trick Room");
return 7;
}
return 5;
},
onFieldStart(target, source) {
if (source?.hasAbility("persistent")) {
this.add("-fieldstart", "move: Trick Room", "[of] " + source, "[persistent]");
} else {
this.add("-fieldstart", "move: Trick Room", "[of] " + source);
}
},
onFieldRestart(target, source) {
this.field.removePseudoWeather("trickroom");
},
// Speed modification is changed in Pokemon.getActionSpeed() in sim/pokemon.js
onFieldResidualOrder: 13,
onFieldEnd() {
this.add("-fieldend", "move: Trick Room");
}
}
},
uproar: {
inherit: true,
basePower: 50,
condition: {
onStart(target) {
this.add("-start", target, "Uproar");
this.effectState.duration = this.random(3, 7);
},
onResidual(target) {
if (target.volatiles["throatchop"]) {
target.removeVolatile("uproar");
return;
}
if (target.lastMove && target.lastMove.id === "struggle") {
delete target.volatiles["uproar"];
}
this.add("-start", target, "Uproar", "[upkeep]");
},
onResidualOrder: 10,
onResidualSubOrder: 11,
onEnd(target) {
this.add("-end", target, "Uproar");
},
onLockMove: "uproar",
onAnySetStatus(status, pokemon) {
if (status.id === "slp") {
if (pokemon === this.effectState.target) {
this.add("-fail", pokemon, "slp", "[from] Uproar", "[msg]");
} else {
this.add("-fail", pokemon, "slp", "[from] Uproar");
}
return null;
}
}
}
},
volttackle: {
inherit: true,
recoil: [1, 3]
},
watersport: {
inherit: true,
condition: {
noCopy: true,
onStart(pokemon) {
this.add("-start", pokemon, "move: Water Sport");
},
onBasePowerPriority: 3,
onAnyBasePower(basePower, user, target, move) {
if (move.type === "Fire")
return this.chainModify(0.5);
}
}
},
whirlpool: {
inherit: true,
accuracy: 70,
basePower: 15
},
whirlwind: {
inherit: true,
flags: { protect: 1, mirror: 1, bypasssub: 1 }
},
wish: {
inherit: true,
flags: { heal: 1 },
slotCondition: "Wish",
condition: {
duration: 2,
onResidualOrder: 7,
onEnd(target) {
if (!target.fainted) {
const source = this.effectState.source;
const damage = this.heal(target.baseMaxhp / 2, target, target);
if (damage)
this.add("-heal", target, target.getHealth, "[from] move: Wish", "[wisher] " + source.name);
}
}
}
},
woodhammer: {
inherit: true,
recoil: [1, 3]
},
worryseed: {
inherit: true,
onTryHit(pokemon) {
const bannedAbilities = ["multitype", "truant"];
if (bannedAbilities.includes(pokemon.ability) || pokemon.hasItem("griseousorb")) {
return false;
}
}
},
wrap: {
inherit: true,
accuracy: 85
},
wringout: {
inherit: true,
basePowerCallback(pokemon, target) {
const bp = Math.floor(target.hp * 120 / target.maxhp) + 1;
this.debug("BP for " + target.hp + "/" + target.maxhp + " HP: " + bp);
return bp;
}
},
yawn: {
inherit: true,
condition: {
noCopy: true,
// doesn't get copied by Baton Pass
duration: 2,
onStart(target, source) {
this.add("-start", target, "move: Yawn", "[of] " + source);
},
onResidualOrder: 10,
onResidualSubOrder: 19,
onEnd(target) {
this.add("-end", target, "move: Yawn", "[silent]");
target.trySetStatus("slp", this.effectState.source);
}
}
}
};
//# sourceMappingURL=moves.js.map