Spaces:
Running
Running
; | |
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, metronome: 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: { metronome: 1 }, | |
condition: { | |
onStart(pokemon) { | |
this.add("-start", pokemon, "Aqua Ring"); | |
}, | |
onResidualOrder: 10, | |
onResidualSubOrder: 2, | |
onResidual(pokemon) { | |
this.heal(pokemon.baseMaxhp / 16); | |
} | |
} | |
}, | |
assist: { | |
inherit: true, | |
onHit(target) { | |
const moves = []; | |
for (const pokemon of target.side.pokemon) { | |
if (pokemon === target) | |
continue; | |
for (const moveSlot of pokemon.moveSlots) { | |
const moveid = moveSlot.id; | |
const move = this.dex.moves.get(moveid); | |
if (move.flags["noassist"] || this.field.pseudoWeather["gravity"] && move.flags["gravity"] || target.volatiles["healblock"] && move.flags["heal"]) { | |
continue; | |
} | |
moves.push(moveid); | |
} | |
} | |
let randomMove = ""; | |
if (moves.length) | |
randomMove = this.sample(moves); | |
if (!randomMove) { | |
return false; | |
} | |
this.actions.useMove(randomMove, target); | |
} | |
}, | |
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: { metronome: 1 }, | |
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); | |
} | |
}, | |
copycat: { | |
inherit: true, | |
onHit(pokemon) { | |
const move = this.lastMove; | |
if (!move) | |
return; | |
if (move.flags["failcopycat"] || this.field.pseudoWeather["gravity"] && move.flags["gravity"] || pokemon.volatiles["healblock"] && move.flags["heal"]) { | |
return false; | |
} | |
this.actions.useMove(move.id, pokemon); | |
} | |
}, | |
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: { metronome: 1 }, | |
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, metronome: 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, metronome: 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: { metronome: 1, 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: { metronome: 1, 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, metronome: 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, metronome: 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) { | |
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] | |
}, | |
fling: { | |
inherit: true, | |
onPrepareHit(target, source, move) { | |
if (source.ignoringItem()) | |
return false; | |
if (source.hasAbility("multitype")) | |
return false; | |
const item = source.getItem(); | |
if (!this.singleEvent("TakeItem", item, source.itemState, source, source, move, item)) | |
return false; | |
if (!item.fling) | |
return false; | |
move.basePower = item.fling.basePower; | |
this.debug(`BP: ${move.basePower}`); | |
if (item.isBerry) { | |
move.onHit = function(foe) { | |
if (this.singleEvent("Eat", item, null, foe, null, null)) { | |
this.runEvent("EatItem", foe, null, null, item); | |
if (item.id === "leppaberry") | |
foe.staleness = "external"; | |
} | |
if (item.onEat) | |
foe.ateBerry = true; | |
}; | |
} else if (item.fling.effect) { | |
move.onHit = item.fling.effect; | |
} else { | |
if (!move.secondaries) | |
move.secondaries = []; | |
if (item.fling.status) { | |
move.secondaries.push({ status: item.fling.status }); | |
} else if (item.fling.volatileStatus) { | |
move.secondaries.push({ volatileStatus: item.fling.volatileStatus }); | |
} | |
} | |
source.addVolatile("fling"); | |
} | |
}, | |
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, metronome: 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: { metronome: 1, 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: { metronome: 1, 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")) { | |
if (ally.isActive) | |
this.add("-immune", ally, "[from] ability: Soundproof"); | |
continue; | |
} | |
ally.cureStatus(true); | |
} | |
} | |
}, | |
healblock: { | |
inherit: true, | |
flags: { protect: 1, mirror: 1, metronome: 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, metronome: 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, metronome: 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", `[of] ${source}`); | |
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: { metronome: 1 }, | |
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, metronome: 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, { target: source }); | |
return null; | |
} | |
} | |
}, | |
magmastorm: { | |
inherit: true, | |
accuracy: 70 | |
}, | |
magnetrise: { | |
inherit: true, | |
flags: { gravity: 1, metronome: 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: 1 } | |
}, | |
metronome: { | |
inherit: true, | |
flags: { noassist: 1, failcopycat: 1, nosleeptalk: 1, failmimic: 1 }, | |
onHit(pokemon) { | |
const moves = this.dex.moves.all().filter((move) => (![2, 4].includes(this.gen) || !pokemon.moves.includes(move.id)) && (!move.isNonstandard || move.isNonstandard === "Unobtainable") && move.flags["metronome"] && !(this.field.pseudoWeather["gravity"] && move.flags["gravity"]) && !(pokemon.volatiles["healblock"] && move.flags["heal"])); | |
let randomMove = ""; | |
if (moves.length) { | |
moves.sort((a, b) => a.num - b.num); | |
randomMove = this.sample(moves).id; | |
} | |
if (!randomMove) | |
return false; | |
pokemon.side.lastSelectedMove = this.toID(randomMove); | |
this.actions.useMove(randomMove, pokemon); | |
} | |
}, | |
mimic: { | |
inherit: true, | |
flags: { | |
protect: 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, metronome: 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: { | |
onStart(pokemon) { | |
this.add("-start", pokemon, "move: Mud Sport"); | |
}, | |
onAnyBasePowerPriority: 3, | |
onAnyBasePower(basePower, user, target, move) { | |
if (move.type === "Electric") { | |
this.debug("Mud Sport weaken"); | |
return this.chainModify(0.5); | |
} | |
} | |
} | |
}, | |
naturepower: { | |
inherit: true, | |
flags: { metronome: 1 }, | |
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, metronome: 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: { metronome: 1 } | |
}, | |
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, metronome: 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: { metronome: 1 } | |
}, | |
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) { | |
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, metronome: 1 } | |
}, | |
rockblast: { | |
inherit: true, | |
accuracy: 80 | |
}, | |
roleplay: { | |
inherit: true, | |
onTryHit(target, source) { | |
if (target.ability === source.ability || source.hasItem("griseousorb")) | |
return false; | |
if (target.getAbility().flags["failroleplay"] || 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, | |
nosketch: 1 | |
}, | |
onHit(target, source) { | |
if (source.transformed || !target.lastMove || target.volatiles["substitute"]) { | |
return false; | |
} | |
if (target.lastMove.flags["nosketch"] || 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; | |
} | |
} | |
}, | |
snore: { | |
inherit: true, | |
flags: { protect: 1, mirror: 1, sound: 1, metronome: 1 } | |
}, | |
spikes: { | |
inherit: true, | |
flags: { metronome: 1, mustpressure: 1 }, | |
condition: { | |
// this is a side condition | |
onSideStart(side) { | |
this.add("-sidestart", side, "Spikes"); | |
this.effectState.layers = 1; | |
}, | |
onSideRestart(side) { | |
if (this.effectState.layers >= 3) | |
return false; | |
this.add("-sidestart", side, "Spikes"); | |
this.effectState.layers++; | |
}, | |
onEntryHazard(pokemon) { | |
if (!pokemon.isGrounded() || pokemon.hasItem("heavydutyboots")) | |
return; | |
const damageAmounts = [0, 3, 4, 6]; | |
this.damage(damageAmounts[this.effectState.layers] * pokemon.maxhp / 24); | |
} | |
} | |
}, | |
spite: { | |
inherit: true, | |
flags: { protect: 1, mirror: 1, bypasssub: 1, metronome: 1 } | |
}, | |
stealthrock: { | |
inherit: true, | |
flags: { metronome: 1, mustpressure: 1 }, | |
condition: { | |
// this is a side condition | |
onSideStart(side) { | |
this.add("-sidestart", side, "move: Stealth Rock"); | |
}, | |
onEntryHazard(pokemon) { | |
if (pokemon.hasItem("heavydutyboots")) | |
return; | |
const typeMod = this.clampIntRange(pokemon.runEffectiveness(this.dex.getActiveMove("stealthrock")), -6, 6); | |
this.damage(pokemon.maxhp * 2 ** typeMod / 8); | |
} | |
} | |
}, | |
struggle: { | |
inherit: true, | |
flags: { | |
contact: 1, | |
protect: 1, | |
failencore: 1, | |
failmefirst: 1, | |
noassist: 1, | |
failcopycat: 1, | |
failinstruct: 1, | |
failmimic: 1, | |
nosketch: 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), 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; | |
} | |
} | |
}, | |
swallow: { | |
inherit: true, | |
onTry(source) { | |
return !!source.volatiles["stockpile"]; | |
} | |
}, | |
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, metronome: 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, metronome: 1 } | |
}, | |
toxic: { | |
inherit: true, | |
accuracy: 85 | |
}, | |
toxicspikes: { | |
inherit: true, | |
flags: { metronome: 1, mustpressure: 1 }, | |
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")) { | |
} 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, metronome: 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: { | |
onStart(pokemon) { | |
this.add("-start", pokemon, "move: Water Sport"); | |
}, | |
onAnyBasePowerPriority: 3, | |
onAnyBasePower(basePower, user, target, move) { | |
if (move.type === "Fire") { | |
this.debug("Water Sport weaken"); | |
return this.chainModify(0.5); | |
} | |
} | |
} | |
}, | |
whirlpool: { | |
inherit: true, | |
accuracy: 70, | |
basePower: 15 | |
}, | |
whirlwind: { | |
inherit: true, | |
flags: { protect: 1, mirror: 1, bypasssub: 1, metronome: 1 } | |
}, | |
wish: { | |
inherit: true, | |
flags: { heal: 1, metronome: 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 | |