Spaces:
Running
Running
/** | |
* Gen 3 moves | |
*/ | |
export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = { | |
absorb: { | |
inherit: true, | |
pp: 20, | |
}, | |
acid: { | |
inherit: true, | |
secondary: { | |
chance: 10, | |
boosts: { | |
def: -1, | |
}, | |
}, | |
}, | |
ancientpower: { | |
inherit: true, | |
flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 }, | |
}, | |
assist: { | |
inherit: true, | |
flags: { metronome: 1, noassist: 1, nosleeptalk: 1 }, | |
}, | |
astonish: { | |
inherit: true, | |
basePowerCallback(pokemon, target) { | |
if (target.volatiles['minimize']) return 60; | |
return 30; | |
}, | |
}, | |
beatup: { | |
inherit: true, | |
onModifyMove(move, pokemon) { | |
pokemon.addVolatile('beatup'); | |
move.type = '???'; | |
move.category = 'Special'; | |
move.allies = pokemon.side.pokemon.filter(ally => !ally.fainted && !ally.status); | |
move.multihit = move.allies.length; | |
}, | |
condition: { | |
duration: 1, | |
onModifySpAPriority: -101, | |
onModifySpA(atk, pokemon, defender, move) { | |
// https://www.smogon.com/forums/posts/8992145/ | |
// this.add('-activate', pokemon, 'move: Beat Up', '[of] ' + move.allies![0].name); | |
this.event.modifier = 1; | |
return move.allies!.shift()!.species.baseStats.atk; | |
}, | |
onFoeModifySpDPriority: -101, | |
onFoeModifySpD(def, pokemon) { | |
this.event.modifier = 1; | |
return pokemon.species.baseStats.def; | |
}, | |
}, | |
}, | |
bide: { | |
inherit: true, | |
accuracy: 100, | |
priority: 0, | |
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; | |
}, | |
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' as ID, | |
name: "Bide", | |
accuracy: 100, | |
damage: this.effectState.totalDamage * 2, | |
category: "Physical", | |
priority: 0, | |
flags: { contact: 1, protect: 1 }, | |
effectType: 'Move', | |
type: 'Normal', | |
} as unknown as ActiveMove; | |
this.actions.tryMoveHit(target, pokemon, moveData); | |
pokemon.removeVolatile('bide'); | |
return false; | |
} | |
this.add('-activate', pokemon, 'move: Bide'); | |
}, | |
onMoveAborted(pokemon) { | |
pokemon.removeVolatile('bide'); | |
}, | |
onEnd(pokemon) { | |
this.add('-end', pokemon, 'move: Bide', '[silent]'); | |
}, | |
}, | |
}, | |
blizzard: { | |
inherit: true, | |
onModifyMove() { }, | |
}, | |
brickbreak: { | |
inherit: true, | |
onTryHit(target, source) { | |
// will shatter screens through sub, before you hit | |
const foe = source.side.foe; | |
foe.removeSideCondition('reflect'); | |
foe.removeSideCondition('lightscreen'); | |
}, | |
}, | |
charge: { | |
inherit: true, | |
boosts: null, | |
}, | |
conversion: { | |
inherit: true, | |
onHit(target) { | |
const possibleTypes = target.moveSlots.map(moveSlot => { | |
const move = this.dex.moves.get(moveSlot.id); | |
if (move.id !== 'curse' && !target.hasType(move.type)) { | |
return move.type; | |
} | |
return ''; | |
}).filter(type => type); | |
if (!possibleTypes.length) { | |
return false; | |
} | |
const type = this.sample(possibleTypes); | |
if (!target.setType(type)) return false; | |
this.add('-start', target, 'typechange', type); | |
}, | |
}, | |
counter: { | |
inherit: true, | |
condition: { | |
duration: 1, | |
noCopy: true, | |
onStart(target, source, move) { | |
this.effectState.slot = null; | |
this.effectState.damage = 0; | |
}, | |
onRedirectTargetPriority: -1, | |
onRedirectTarget(target, source, source2) { | |
if (source !== this.effectState.target || !this.effectState.slot) return; | |
return this.getAtSlot(this.effectState.slot); | |
}, | |
onDamagePriority: -101, | |
onDamage(damage, target, source, effect) { | |
if ( | |
effect.effectType === 'Move' && !source.isAlly(target) && | |
(effect.category === 'Physical' || effect.id === 'hiddenpower') | |
) { | |
this.effectState.slot = source.getSlot(); | |
this.effectState.damage = 2 * damage; | |
} | |
}, | |
}, | |
}, | |
covet: { | |
inherit: true, | |
flags: { protect: 1, mirror: 1, noassist: 1 }, | |
}, | |
crunch: { | |
inherit: true, | |
secondary: { | |
chance: 20, | |
boosts: { | |
spd: -1, | |
}, | |
}, | |
}, | |
dig: { | |
inherit: true, | |
basePower: 60, | |
}, | |
disable: { | |
inherit: true, | |
accuracy: 55, | |
flags: { protect: 1, mirror: 1, bypasssub: 1, metronome: 1 }, | |
volatileStatus: 'disable', | |
condition: { | |
durationCallback() { | |
return this.random(2, 6); | |
}, | |
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; | |
}, | |
onEnd(pokemon) { | |
this.add('-end', pokemon, 'move: Disable'); | |
}, | |
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); | |
} | |
} | |
}, | |
}, | |
}, | |
dive: { | |
inherit: true, | |
basePower: 60, | |
}, | |
doomdesire: { | |
inherit: true, | |
onTry(source, target) { | |
if (!target.side.addSlotCondition(target, 'futuremove')) return false; | |
const moveData = { | |
name: "Doom Desire", | |
basePower: 120, | |
category: "Physical", | |
flags: { metronome: 1, futuremove: 1 }, | |
willCrit: false, | |
type: '???', | |
} as unknown as ActiveMove; | |
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: "Physical", | |
flags: { metronome: 1, futuremove: 1 }, | |
effectType: 'Move', | |
type: '???', | |
}, | |
}); | |
this.add('-start', source, 'Doom Desire'); | |
return null; | |
}, | |
}, | |
encore: { | |
inherit: true, | |
volatileStatus: 'encore', | |
condition: { | |
durationCallback() { | |
return this.random(3, 7); | |
}, | |
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 | |
) { | |
// it failed | |
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 | |
) { | |
// early termination if you run out of PP | |
target.removeVolatile('encore'); | |
} | |
}, | |
onEnd(target) { | |
this.add('-end', target, 'Encore'); | |
}, | |
onDisableMove(pokemon) { | |
if (!this.effectState.move || !pokemon.hasMove(this.effectState.move)) { | |
return; | |
} | |
for (const moveSlot of pokemon.moveSlots) { | |
if (moveSlot.id !== this.effectState.move) { | |
pokemon.disableMove(moveSlot.id); | |
} | |
} | |
}, | |
}, | |
}, | |
extrasensory: { | |
inherit: true, | |
basePowerCallback(pokemon, target) { | |
if (target.volatiles['minimize']) return 160; | |
return 80; | |
}, | |
}, | |
fakeout: { | |
inherit: true, | |
flags: { protect: 1, mirror: 1, metronome: 1 }, | |
}, | |
feintattack: { | |
inherit: true, | |
flags: { protect: 1, mirror: 1, metronome: 1 }, | |
}, | |
flail: { | |
inherit: true, | |
basePowerCallback(pokemon) { | |
const ratio = Math.max(Math.floor(pokemon.hp * 48 / pokemon.maxhp), 1); | |
let bp; | |
if (ratio < 2) { | |
bp = 200; | |
} else if (ratio < 5) { | |
bp = 150; | |
} else if (ratio < 10) { | |
bp = 100; | |
} else if (ratio < 17) { | |
bp = 80; | |
} else if (ratio < 33) { | |
bp = 40; | |
} else { | |
bp = 20; | |
} | |
this.debug(`BP: ${bp}`); | |
return bp; | |
}, | |
}, | |
flash: { | |
inherit: true, | |
accuracy: 70, | |
}, | |
fly: { | |
inherit: true, | |
basePower: 70, | |
}, | |
followme: { | |
inherit: true, | |
volatileStatus: undefined, | |
slotCondition: 'followme', | |
condition: { | |
duration: 1, | |
onStart(target, source, effect) { | |
this.add('-singleturn', target, 'move: Follow Me'); | |
this.effectState.slot = target.getSlot(); | |
}, | |
onFoeRedirectTargetPriority: 1, | |
onFoeRedirectTarget(target, source, source2, move) { | |
const userSlot = this.getAtSlot(this.effectState.slot); | |
if (this.validTarget(userSlot, source, move.target)) { | |
return userSlot; | |
} | |
}, | |
}, | |
}, | |
foresight: { | |
inherit: true, | |
accuracy: 100, | |
}, | |
furycutter: { | |
inherit: true, | |
onHit(target, source) { | |
source.addVolatile('furycutter'); | |
}, | |
}, | |
gigadrain: { | |
inherit: true, | |
pp: 5, | |
}, | |
glare: { | |
inherit: true, | |
ignoreImmunity: false, | |
}, | |
hiddenpower: { | |
inherit: true, | |
category: "Physical", | |
onModifyMove(move, pokemon) { | |
move.type = pokemon.hpType || 'Dark'; | |
const specialTypes = ['Fire', 'Water', 'Grass', 'Ice', 'Electric', 'Dark', 'Psychic', 'Dragon']; | |
move.category = specialTypes.includes(move.type) ? 'Special' : 'Physical'; | |
}, | |
}, | |
highjumpkick: { | |
inherit: true, | |
basePower: 85, | |
onMoveFail(target, source, move) { | |
if (target.runImmunity('Fighting')) { | |
const damage = this.actions.getDamage(source, target, move, true); | |
if (typeof damage !== 'number') throw new Error("HJK recoil failed"); | |
this.damage(this.clampIntRange(damage / 2, 1, Math.floor(target.maxhp / 2)), source, source, move); | |
} | |
}, | |
}, | |
hypnosis: { | |
inherit: true, | |
accuracy: 60, | |
}, | |
jumpkick: { | |
inherit: true, | |
basePower: 70, | |
onMoveFail(target, source, move) { | |
if (target.runImmunity('Fighting')) { | |
const damage = this.actions.getDamage(source, target, move, true); | |
if (typeof damage !== 'number') throw new Error("Jump Kick didn't recoil"); | |
this.damage(this.clampIntRange(damage / 2, 1, Math.floor(target.maxhp / 2)), source, source, move); | |
} | |
}, | |
}, | |
leafblade: { | |
inherit: true, | |
basePower: 70, | |
}, | |
lockon: { | |
inherit: true, | |
accuracy: 100, | |
}, | |
megadrain: { | |
inherit: true, | |
pp: 10, | |
}, | |
memento: { | |
inherit: true, | |
accuracy: true, | |
}, | |
mindreader: { | |
inherit: true, | |
accuracy: 100, | |
}, | |
mimic: { | |
inherit: true, | |
flags: { protect: 1, bypasssub: 1, allyanim: 1, failencore: 1, noassist: 1, failmimic: 1 }, | |
}, | |
mirrorcoat: { | |
inherit: true, | |
condition: { | |
duration: 1, | |
noCopy: true, | |
onStart(target, source, move) { | |
this.effectState.slot = null; | |
this.effectState.damage = 0; | |
}, | |
onRedirectTargetPriority: -1, | |
onRedirectTarget(target, source, source2) { | |
if (source !== this.effectState.target || !this.effectState.slot) return; | |
return this.getAtSlot(this.effectState.slot); | |
}, | |
onDamagePriority: -101, | |
onDamage(damage, target, source, effect) { | |
if ( | |
effect.effectType === 'Move' && !source.isAlly(target) && | |
effect.category === 'Special' && effect.id !== 'hiddenpower' | |
) { | |
this.effectState.slot = source.getSlot(); | |
this.effectState.damage = 2 * damage; | |
} | |
}, | |
}, | |
}, | |
mirrormove: { | |
inherit: true, | |
flags: { metronome: 1, failencore: 1, nosleeptalk: 1, noassist: 1 }, | |
onTryHit() { }, | |
onHit(pokemon) { | |
const noMirror = [ | |
'assist', 'curse', 'doomdesire', 'focuspunch', 'futuresight', 'magiccoat', 'metronome', 'mimic', 'mirrormove', 'naturepower', 'psychup', 'roleplay', 'sketch', 'sleeptalk', 'spikes', 'spitup', 'taunt', 'teeterdance', 'transform', | |
]; | |
const lastAttackedBy = pokemon.getLastAttackedBy(); | |
if (!lastAttackedBy?.source.lastMove || !lastAttackedBy.move) { | |
return false; | |
} | |
if (noMirror.includes(lastAttackedBy.move) || !lastAttackedBy.source.hasMove(lastAttackedBy.move)) { | |
return false; | |
} | |
this.actions.useMove(lastAttackedBy.move, pokemon); | |
}, | |
target: "self", | |
}, | |
naturepower: { | |
inherit: true, | |
accuracy: 95, | |
onHit(target) { | |
this.actions.useMove('swift', target); | |
}, | |
}, | |
needlearm: { | |
inherit: true, | |
basePowerCallback(pokemon, target) { | |
if (target.volatiles['minimize']) return 120; | |
return 60; | |
}, | |
}, | |
nightmare: { | |
inherit: true, | |
accuracy: true, | |
}, | |
odorsleuth: { | |
inherit: true, | |
accuracy: 100, | |
}, | |
outrage: { | |
inherit: true, | |
basePower: 90, | |
}, | |
overheat: { | |
inherit: true, | |
flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 }, | |
}, | |
petaldance: { | |
inherit: true, | |
basePower: 70, | |
}, | |
recover: { | |
inherit: true, | |
pp: 20, | |
}, | |
reversal: { | |
inherit: true, | |
basePowerCallback(pokemon) { | |
const ratio = Math.max(Math.floor(pokemon.hp * 48 / pokemon.maxhp), 1); | |
let bp; | |
if (ratio < 2) { | |
bp = 200; | |
} else if (ratio < 5) { | |
bp = 150; | |
} else if (ratio < 10) { | |
bp = 100; | |
} else if (ratio < 17) { | |
bp = 80; | |
} else if (ratio < 33) { | |
bp = 40; | |
} else { | |
bp = 20; | |
} | |
this.debug(`BP: ${bp}`); | |
return bp; | |
}, | |
}, | |
rocksmash: { | |
inherit: true, | |
basePower: 20, | |
}, | |
sketch: { | |
inherit: true, | |
flags: { bypasssub: 1, failencore: 1, noassist: 1, failmimic: 1, nosketch: 1 }, | |
}, | |
sleeptalk: { | |
inherit: true, | |
onHit(pokemon) { | |
const moves = []; | |
for (const moveSlot of pokemon.moveSlots) { | |
const moveid = moveSlot.id; | |
const pp = moveSlot.pp; | |
const move = this.dex.moves.get(moveid); | |
if (moveid && !move.flags['nosleeptalk'] && !move.flags['charge']) { | |
moves.push({ move: moveid, pp }); | |
} | |
} | |
if (!moves.length) { | |
return false; | |
} | |
const randomMove = this.sample(moves); | |
if (!randomMove.pp) { | |
this.add('cant', pokemon, 'nopp', randomMove.move); | |
return; | |
} | |
this.actions.useMove(randomMove.move, pokemon); | |
}, | |
}, | |
spite: { | |
inherit: true, | |
onHit(target) { | |
const roll = this.random(2, 6); | |
if (target.lastMove && target.deductPP(target.lastMove.id, roll)) { | |
this.add("-activate", target, 'move: Spite', target.lastMove.id, roll); | |
return; | |
} | |
return false; | |
}, | |
}, | |
stockpile: { | |
inherit: true, | |
pp: 10, | |
condition: { | |
noCopy: true, | |
onStart(target) { | |
this.effectState.layers = 1; | |
this.add('-start', target, 'stockpile' + this.effectState.layers); | |
}, | |
onRestart(target) { | |
if (this.effectState.layers >= 3) return false; | |
this.effectState.layers++; | |
this.add('-start', target, 'stockpile' + this.effectState.layers); | |
}, | |
onEnd(target) { | |
this.effectState.layers = 0; | |
this.add('-end', target, 'Stockpile'); | |
}, | |
}, | |
}, | |
struggle: { | |
inherit: true, | |
flags: { contact: 1, protect: 1, noassist: 1, failencore: 1, failmimic: 1, nosketch: 1 }, | |
accuracy: 100, | |
recoil: [1, 4], | |
struggleRecoil: false, | |
}, | |
surf: { | |
inherit: true, | |
target: "allAdjacentFoes", | |
}, | |
taunt: { | |
inherit: true, | |
flags: { protect: 1, bypasssub: 1, metronome: 1 }, | |
condition: { | |
duration: 2, | |
onStart(target) { | |
this.add('-start', target, 'move: Taunt'); | |
}, | |
onResidualOrder: 10, | |
onResidualSubOrder: 15, | |
onEnd(target) { | |
this.add('-end', target, 'move: Taunt', '[silent]'); | |
}, | |
onDisableMove(pokemon) { | |
for (const moveSlot of pokemon.moveSlots) { | |
if (this.dex.moves.get(moveSlot.move).category === 'Status') { | |
pokemon.disableMove(moveSlot.id); | |
} | |
} | |
}, | |
onBeforeMove(attacker, defender, move) { | |
if (move.category === 'Status') { | |
this.add('cant', attacker, 'move: Taunt', move); | |
return false; | |
} | |
}, | |
}, | |
}, | |
teeterdance: { | |
inherit: true, | |
flags: { protect: 1, metronome: 1 }, | |
}, | |
tickle: { | |
inherit: true, | |
flags: { protect: 1, reflectable: 1, mirror: 1, bypasssub: 1, metronome: 1 }, | |
}, | |
uproar: { | |
inherit: true, | |
condition: { | |
onStart(target) { | |
this.add('-start', target, 'Uproar'); | |
// 2-5 turns | |
this.effectState.duration = this.random(2, 6); | |
}, | |
onResidual(target) { | |
if (target.volatiles['throatchop']) { | |
target.removeVolatile('uproar'); | |
return; | |
} | |
if (target.lastMove && target.lastMove.id === 'struggle') { | |
// don't lock | |
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; | |
} | |
}, | |
}, | |
}, | |
vinewhip: { | |
inherit: true, | |
pp: 10, | |
}, | |
volttackle: { | |
inherit: true, | |
secondary: null, | |
}, | |
waterfall: { | |
inherit: true, | |
secondary: null, | |
}, | |
weatherball: { | |
inherit: true, | |
onModifyMove(move) { | |
switch (this.field.effectiveWeather()) { | |
case 'sunnyday': | |
move.type = 'Fire'; | |
move.category = 'Special'; | |
break; | |
case 'raindance': | |
move.type = 'Water'; | |
move.category = 'Special'; | |
break; | |
case 'sandstorm': | |
move.type = 'Rock'; | |
break; | |
case 'hail': | |
move.type = 'Ice'; | |
move.category = 'Special'; | |
break; | |
} | |
if (this.field.effectiveWeather()) move.basePower *= 2; | |
}, | |
}, | |
zapcannon: { | |
inherit: true, | |
basePower: 100, | |
}, | |
}; | |