"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, 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