Spaces:
Running
Running
export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = { | |
bide: { | |
inherit: true, | |
priority: 0, | |
accuracy: true, | |
condition: { | |
durationCallback(target, source, effect) { | |
return this.random(3, 5); | |
}, | |
onStart(pokemon) { | |
this.effectState.totalDamage = 0; | |
this.effectState.lastDamage = 0; | |
this.add('-start', pokemon, 'Bide'); | |
}, | |
onHit(target, source, move) { | |
if (source && source !== target && move.category !== 'Physical' && move.category !== 'Special') { | |
const damage = this.effectState.totalDamage; | |
this.effectState.totalDamage += damage; | |
this.effectState.lastDamage = damage; | |
this.effectState.sourceSlot = source.getSlot(); | |
} | |
}, | |
onDamage(damage, target, source, move) { | |
if (!source || source.isAlly(target)) return; | |
if (!move || move.effectType !== 'Move') return; | |
if (!damage && this.effectState.lastDamage > 0) { | |
damage = this.effectState.totalDamage; | |
} | |
this.effectState.totalDamage += damage; | |
this.effectState.lastDamage = damage; | |
this.effectState.sourceSlot = source.getSlot(); | |
}, | |
onAfterSetStatus(status, pokemon) { | |
// Sleep, freeze, and partial trap will just pause duration. | |
if (pokemon.volatiles['flinch']) { | |
this.effectState.duration!++; | |
} else if (pokemon.volatiles['partiallytrapped']) { | |
this.effectState.duration!++; | |
} else { | |
switch (status.id) { | |
case 'slp': | |
case 'frz': | |
this.effectState.duration!++; | |
break; | |
} | |
} | |
}, | |
onBeforeMove(pokemon, t, move) { | |
if (this.effectState.duration === 1) { | |
this.add('-end', pokemon, 'Bide'); | |
if (!this.effectState.totalDamage) { | |
this.debug("Bide failed because no damage was taken"); | |
this.add('-fail', pokemon); | |
return false; | |
} | |
const target = this.getAtSlot(this.effectState.sourceSlot); | |
if (target.isSemiInvulnerable()) { | |
this.add('-message', 'The foe ' + target.name + ' can\'t be hit while flying!'); | |
pokemon.removeVolatile('bide'); | |
return false; | |
} | |
this.actions.moveHit(target, pokemon, move, { damage: this.effectState.totalDamage * 2 } as ActiveMove); | |
pokemon.removeVolatile('bide'); | |
return false; | |
} | |
this.add('-activate', pokemon, 'Bide'); | |
return false; | |
}, | |
onDisableMove(pokemon) { | |
if (!pokemon.hasMove('bide')) { | |
return; | |
} | |
for (const moveSlot of pokemon.moveSlots) { | |
if (moveSlot.id !== 'bide') { | |
pokemon.disableMove(moveSlot.id); | |
} | |
} | |
}, | |
}, | |
type: "???", // Will look as Normal but it's STAB-less | |
}, | |
bind: { | |
inherit: true, | |
// FIXME: onBeforeMove() {}, | |
}, | |
clamp: { | |
inherit: true, | |
// FIXME: onBeforeMove() {}, | |
}, | |
counter: { | |
inherit: true, | |
ignoreImmunity: true, | |
willCrit: false, | |
basePower: 1, | |
damageCallback(pokemon, target) { | |
// Counter mechanics in Stadium 1: | |
// - a move is Counterable if it is Normal or Fighting type, has nonzero Base Power, and is not Counter | |
// - Counter succeeds if the target used a Counterable move earlier this turn | |
const lastMoveThisTurn = target.side.lastMove && target.side.lastMove.id === target.side.lastSelectedMove && | |
!this.queue.willMove(target) && this.dex.moves.get(target.side.lastMove.id); | |
if (!lastMoveThisTurn) { | |
this.debug("Stadium 1 Counter: last move was not this turn"); | |
this.add('-fail', pokemon); | |
return false; | |
} | |
const lastMoveThisTurnIsCounterable = lastMoveThisTurn && lastMoveThisTurn.basePower > 0 && | |
['Normal', 'Fighting'].includes(lastMoveThisTurn.type) && lastMoveThisTurn.id !== 'counter'; | |
if (!lastMoveThisTurnIsCounterable) { | |
this.debug(`Stadium 1 Counter: last move ${lastMoveThisTurn.name} was not Counterable`); | |
this.add('-fail', pokemon); | |
return false; | |
} | |
if (this.lastDamage <= 0) { | |
this.debug("Stadium 1 Counter: no previous damage exists"); | |
this.add('-fail', pokemon); | |
return false; | |
} | |
return 2 * this.lastDamage; | |
}, | |
}, | |
firespin: { | |
inherit: true, | |
// FIXME: onBeforeMove() {}, | |
}, | |
haze: { | |
inherit: true, | |
onHit(target, source) { | |
this.add('-activate', target, 'move: Haze'); | |
this.add('-clearallboost', '[silent]'); | |
for (const pokemon of this.getAllActive()) { | |
pokemon.clearBoosts(); | |
pokemon.cureStatus(true); | |
for (const id of Object.keys(pokemon.volatiles)) { | |
pokemon.removeVolatile(id); | |
this.add('-end', pokemon, id, '[silent]'); | |
} | |
pokemon.recalculateStats!(); | |
} | |
}, | |
}, | |
hyperbeam: { | |
inherit: true, | |
onMoveFail(target, source, move) { | |
source.addVolatile('mustrecharge'); | |
}, | |
}, | |
psywave: { | |
inherit: true, | |
basePower: 1, | |
damageCallback(pokemon) { | |
return this.random(1, this.trunc(1.5 * pokemon.level)); | |
}, | |
}, | |
rage: { | |
inherit: true, | |
self: { | |
volatileStatus: 'rage', | |
}, | |
condition: { | |
// Rage lock | |
onStart(target, source, effect) { | |
this.effectState.move = 'rage'; | |
}, | |
onLockMove: 'rage', | |
onHit(target, source, move) { | |
if (target.boosts.atk < 6 && (move.category !== 'Status' || move.id === 'disable')) { | |
this.boost({ atk: 1 }); | |
} | |
}, | |
}, | |
}, | |
recover: { | |
inherit: true, | |
heal: null, | |
onHit(target) { | |
if (target.hp === target.maxhp) { | |
return false; | |
} | |
this.heal(Math.floor(target.maxhp / 2), target, target); | |
}, | |
}, | |
rest: { | |
inherit: true, | |
onHit(target, source, move) { | |
// Fails if the difference between | |
// max HP and current HP is 0, 255, or 511 | |
if (target.hp >= target.maxhp) return false; | |
if (!target.setStatus('slp', source, move)) return false; | |
target.statusState.time = 2; | |
target.statusState.startTime = 2; | |
target.recalculateStats!(); // Stadium Rest removes statdrops given by Major Status Conditions. | |
this.heal(target.maxhp); // Aesthetic only as the healing happens after you fall asleep in-game | |
}, | |
}, | |
softboiled: { | |
inherit: true, | |
heal: null, | |
onHit(target) { | |
// Fail when health is 255 or 511 less than max | |
if (target.hp === target.maxhp) { | |
return false; | |
} | |
this.heal(Math.floor(target.maxhp / 2), target, target); | |
}, | |
}, | |
substitute: { | |
inherit: true, | |
onTryHit(target) { | |
if (target.volatiles['substitute']) { | |
this.add('-fail', target, 'move: Substitute'); | |
return null; | |
} | |
// Stadium fixes the 25% = you die gag | |
if (target.hp <= target.maxhp / 4) { | |
this.add('-fail', target, 'move: Substitute', '[weak]'); | |
return null; | |
} | |
}, | |
condition: { | |
onStart(target) { | |
this.add('-start', target, 'Substitute'); | |
this.effectState.hp = Math.floor(target.maxhp / 4); | |
delete target.volatiles['partiallytrapped']; | |
}, | |
onTryHitPriority: -1, | |
onTryHit(target, source, move) { | |
if (target === source) { | |
this.debug('sub bypass: self hit'); | |
return; | |
} | |
if (move.drain) { | |
this.add('-miss', source); | |
return null; | |
} | |
if (move.category === 'Status') { | |
const SubBlocked = ['leechseed', 'lockon', 'mindreader', 'nightmare']; | |
if (move.status || move.boosts || move.volatileStatus === 'confusion' || SubBlocked.includes(move.id)) { | |
this.add('-activate', target, 'Substitute', '[block] ' + move.name); | |
return null; | |
} | |
return; | |
} | |
if (move.volatileStatus && target === source) return; | |
let damage = this.actions.getDamage(source, target, move); | |
if (damage && damage > target.volatiles['substitute'].hp) { | |
damage = target.volatiles['substitute'].hp; | |
} | |
if (!damage && damage !== 0) return null; | |
damage = this.runEvent('SubDamage', target, source, move, damage); | |
if (!damage && damage !== 0) return damage; | |
target.volatiles['substitute'].hp -= damage; | |
this.lastDamage = damage; | |
if (target.volatiles['substitute'].hp <= 0) { | |
this.debug('Substitute broke'); | |
target.removeVolatile('substitute'); | |
target.subFainted = true; | |
} else { | |
this.add('-activate', target, 'Substitute', '[damage]'); | |
} | |
// Drain/recoil does not happen if the substitute breaks | |
if (target.volatiles['substitute']) { | |
if (move.recoil) { | |
this.damage(this.clampIntRange(Math.floor(damage * move.recoil[0] / move.recoil[1]), 1), source, target, 'recoil'); | |
} | |
} | |
this.runEvent('AfterSubDamage', target, source, move, damage); | |
// Add here counter damage | |
const lastAttackedBy = target.getLastAttackedBy(); | |
if (!lastAttackedBy) { | |
target.attackedBy.push({ source, move: move.id, damage, slot: source.getSlot(), thisTurn: true }); | |
} else { | |
lastAttackedBy.move = move.id; | |
lastAttackedBy.damage = damage; | |
} | |
return 0; | |
}, | |
onEnd(target) { | |
this.add('-end', target, 'Substitute'); | |
}, | |
}, | |
secondary: null, | |
target: "self", | |
type: "Normal", | |
}, | |
struggle: { | |
inherit: true, | |
ignoreImmunity: { 'Normal': true }, | |
}, | |
wrap: { | |
inherit: true, | |
// FIXME: onBeforeMove() {}, | |
}, | |
}; | |