/*Moves*/
'use strict';
/**@type {{[k: string]: ModdedMoveData}} */
let BattleMovedex = {
absorb: {
inherit: true,
desc: "The user recovers 1/2 the HP lost by the target, rounded down. If this move breaks the target's substitute, the user does not recover any HP.",
basepower: 30,
},
acid: {
inherit: true,
desc: "Has a 33% chance to lower the target's Defense by 1 stage.",
shortDesc: "33% chance to lower the target's Defense by 1.",
basepower: 50,
secondary: {
chance: 33,
boosts: {
def: -1,
},
},
target: "normal",
},
amnesia: {
inherit: true,
desc: "Raises the user's Special by 2 stages.",
shortDesc: "Raises the user's Special by 2.",
boosts: {
spd: 2,
spa: 2,
},
},
aurorabeam: {
inherit: true,
desc: "Has a 33% chance to lower the target's Attack by 1 stage.",
shortDesc: "33% chance to lower the target's Attack by 1.",
secondary: {
chance: 33,
boosts: {
atk: -1,
},
},
},
barrage: {
inherit: true,
desc: "Hits two to five times. Has a 3/8 chance to hit two or three times, and a 1/8 chance to hit four or five times. Damage is calculated once for the first hit and used for every hit. If one of the hits breaks the target's substitute, the move ends.",
},
bide: {
inherit: true,
desc: "The user spends two or three turns locked into this move and then, on the second or third turn after using this move, the user attacks the opponent, inflicting double the damage in HP it lost during those turns. This move ignores type immunity and cannot be avoided even if the target is using Dig or Fly. The user can choose to switch out during the effect. If the user switches out or is prevented from moving during this move's use, the effect ends. During the effect, if the opposing Pokemon switches out or uses Confuse Ray, Conversion, Focus Energy, Glare, Haze, Leech Seed, Light Screen, Mimic, Mist, Poison Gas, Poison Powder, Recover, Reflect, Rest, Soft-Boiled, Splash, Stun Spore, Substitute, Supersonic, Teleport, Thunder Wave, Toxic, or Transform, the previous damage dealt to the user will be added to the total.",
priority: 0,
accuracy: true,
ignoreEvasion: true,
effect: {
duration: 2,
durationCallback(target, source, effect) {
return this.random(3, 4);
},
onStart(pokemon) {
this.effectData.totalDamage = 0;
this.effectData.lastDamage = 0;
this.add('-start', pokemon, 'Bide');
},
onHit(target, source, move) {
if (source && source !== target && move.category !== 'Physical' && move.category !== 'Special') {
let damage = this.effectData.totalDamage;
this.effectData.totalDamage += damage;
this.effectData.lastDamage = damage;
this.effectData.sourcePosition = source.position;
this.effectData.sourceSide = source.side;
}
},
onDamage(damage, target, source, move) {
if (!source || source.side === target.side) return;
if (!move || move.effectType !== 'Move') return;
if (!damage && this.effectData.lastDamage > 0) {
damage = this.effectData.totalDamage;
}
this.effectData.totalDamage += damage;
this.effectData.lastDamage = damage;
this.effectData.sourcePosition = source.position;
this.effectData.sourceSide = source.side;
},
onAfterSetStatus(status, pokemon) {
// Sleep, freeze, and partial trap will just pause duration.
if (pokemon.volatiles['flinch']) {
this.effectData.duration++;
} else if (pokemon.volatiles['partiallytrapped']) {
this.effectData.duration++;
} else {
switch (status.id) {
case 'slp':
case 'frz':
this.effectData.duration++;
break;
}
}
},
onBeforeMove(pokemon, target, move) {
if (this.effectData.duration === 1) {
if (!this.effectData.totalDamage) {
this.add('-fail', pokemon);
return false;
}
this.add('-end', pokemon, 'Bide');
let target = this.effectData.sourceSide.active[this.effectData.sourcePosition];
this.moveHit(target, pokemon, move, /** @type {ActiveMove} */ ({damage: this.effectData.totalDamage * 2}));
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,
desc: "The user spends two to five turns using this move. Has a 3/8 chance to last two or three turns, and a 1/8 chance to last four or five turns. The damage calculated for the first turn is used for every other turn. The user cannot select a move and the target cannot execute a move during the effect, but both may switch out. If the user switches out, the target remains unable to execute a move during that turn. If the target switches out, the user uses this move again automatically, and if it had 0 PP at the time, it becomes 63. If the user or the target switch out, or the user is prevented from moving, the effect ends. This move can prevent the target from moving even if it has type immunity, but will not deal damage.",
shortDesc: "Prevents the target from moving for 2-5 turns.",
ignoreImmunity: true,
volatileStatus: 'partiallytrapped',
self: {
volatileStatus: 'partialtrappinglock',
},
// FIXME: onBeforeMove(pokemon, target) {target.removeVolatile('mustrecharge')}
onHit(target, source) {
/**
* The duration of the partially trapped must be always renewed to 2
* so target doesn't move on trapper switch out as happens in gen 1.
* However, this won't happen if there's no switch and the trapper is
* about to end its partial trapping.
**/
if (target.volatiles['partiallytrapped']) {
if (source.volatiles['partialtrappinglock'] && source.volatiles['partialtrappinglock'].duration > 1) {
target.volatiles['partiallytrapped'].duration = 2;
}
}
},
},
bite: {
inherit: true,
desc: "Has a 10% chance to flinch the target.",
shortDesc: "10% chance to flinch the target.",
secondary: {
chance: 10,
volatileStatus: 'flinch',
},
type: "Normal",
},
blizzard: {
inherit: true,
accuracy: 75,
target: "normal",
},
bonemerang: {
inherit: true,
desc: "Hits twice. If the first hit breaks the target's substitute, the move ends.",
},
bubble: {
inherit: true,
desc: "Has a 33% chance to lower the target's Speed by 1 stage.",
shortDesc: "33% chance to lower the target's Speed by 1.",
basePower: 30,
secondary: {
chance: 33,
boosts: {
spe: -1,
},
},
target: "normal",
},
bubblebeam: {
inherit: true,
desc: "Has a 33% chance to lower the target's Speed by 1 stage.",
shortDesc: "33% chance to lower the target's Speed by 1.",
secondary: {
chance: 33,
boosts: {
spe: -1,
},
},
},
clamp: {
inherit: true,
desc: "Prevents the target from switching out.",
accuracy: 100,
pp: 10,
onHit(target, source, move) {
return target.addVolatile('trapped', source, move, 'trapper');
},
secondary: null,
target: "normal",
},
cometpunch: {
inherit: true,
desc: "Hits two to five times. Has a 3/8 chance to hit two or three times, and a 1/8 chance to hit four or five times. Damage is calculated once for the first hit and used for every hit. If one of the hits breaks the target's substitute, the move ends.",
},
constrict: {
inherit: true,
accuracy: 100,
basePower: 0,
category: "Status",
desc: "Lowers the target's Speed by 2 stage.",
shortDesc: "Lowers the target's Speed by 2.",
boosts: {
spe: -2,
},
},
conversion: {
inherit: true,
desc: "The user's type changes to match a type that resists or is immune to the type of the last move used by the target, but not either of its current types. The determined type of the move is used rather than the original type. Fails if the target has not made a move, if the user cannot change its type, or if this move would only be able to select one of the user's current types.",
shortDesc: "Changes user's type to resist target's last move.",
volatileStatus: 'conversion',
accuracy: true,
target: "normal",
onHit(target, source) {
if (!target.lastMove) {
return false;
}
let possibleTypes = [];
let attackType = target.lastMove.type;
for (let type in this.data.TypeChart) {
if (source.hasType(type)) continue;
let typeCheck = this.data.TypeChart[type].damageTaken[attackType];
if (typeCheck === 2 || typeCheck === 3) {
possibleTypes.push(type);
}
}
if (!possibleTypes.length) {
return false;
}
let randomType = this.sample(possibleTypes);
if (!source.setType(randomType)) return false;
this.add('-start', source, 'typechange', randomType);
},
},
counter: {
inherit: true,
desc: "Deals damage to the opposing Pokemon equal to twice the damage dealt by the last move used in the battle. This move ignores type immunity. Fails if the user moves first, or if the opposing side's last move was Counter, had 0 power, or was not Normal or Fighting type. Fails if the last move used by either side did 0 damage and was not Confuse Ray, Conversion, Focus Energy, Glare, Haze, Leech Seed, Light Screen, Mimic, Mist, Poison Gas, Poison Powder, Recover, Reflect, Rest, Soft-Boiled, Splash, Stun Spore, Substitute, Supersonic, Teleport, Thunder Wave, Toxic, or Transform.",
ignoreImmunity: true,
willCrit: false,
damageCallback(pokemon, target) {
// Counter mechanics on gen 1 might be hard to understand.
// It will fail if the last move selected by the opponent has base power 0 or is not Normal or Fighting Type.
// If both are true, counter will deal twice the last damage dealt in battle, no matter what was the move.
// That means that, if opponent switches, counter will use last counter damage * 2.
let lastUsedMove = target.side.lastMove && this.getMove(target.side.lastMove.id);
if (lastUsedMove && lastUsedMove.basePower > 0 && ['Normal', 'Fighting'].includes(lastUsedMove.type) && this.lastDamage > 0 && !this.willMove(target)) {
return 2 * this.lastDamage;
}
this.add('-fail', pokemon);
return false;
},
},
crabhammer: {
inherit: true,
critRatio: 2,
},
defensecurl: {
inherit: true,
desc: "Raises the user's Defense by 1 stage.",
},
dig: {
inherit: true,
desc: "This attack charges on the first turn and executes on the second. On the first turn, the user avoids all attacks other than Bide, Swift, and Transform.",
basePower: 100,
},
dizzypunch: {
inherit: true,
desc: "No additional effect.",
shortDesc: "No additional effect.",
secondary: null,
},
doubleedge: {
inherit: true,
desc: "If the target lost HP, the user takes recoil damage equal to 1/4 the HP lost by the target, rounded down, but not less than 1 HP. If this move breaks the target's substitute, the user does not take any recoil damage.",
basePower: 120,
},
doublekick: {
inherit: true,
desc: "Hits twice. Damage is calculated once for the first hit and used for both hits. If the first hit breaks the target's substitute, the move ends.",
},
doubleslap: {
inherit: true,
desc: "Hits two to five times. Has a 3/8 chance to hit two or three times, and a 1/8 chance to hit four or five times. Damage is calculated once for the first hit and used for every hit. If one of the hits breaks the target's substitute, the move ends.",
},
dragonrage: {
inherit: true,
accuracy: 100,
category: "Physical",
damage: null,
basePower: 95,
},
dreameater: {
inherit: true,
desc: "The target is unaffected by this move unless it is asleep. The user recovers 1/2 the HP lost by the target, rounded down, but not less than 1 HP. If this move breaks the target's substitute, the user does not recover any HP.",
},
earthquake: {
inherit: true,
desc: "No additional effect.",
shortDesc: "No additional effect.",
},
fireblast: {
inherit: true,
accuracy: 80,
desc: "Has a 30% chance to burn the target.",
shortDesc: "30% chance to burn the target.",
flags: {defrost: 1},
secondary: {
chance: 30,
status: 'brn',
},
},
firepunch: {
inherit: true,
flags: {defrost: 1},
}
firespin: {
inherit: true,
desc: "Burns the target.",
shortDesc: "Burns the target.",
accuracy: 100,
basePower: 0,
category: "Status",
status: 'brn',
secondary: null,
},
fissure: {
inherit: true,
desc: "Deals 65535 damage to the target. Fails if the target's Speed is greater than the user's.",
shortDesc: "Deals 65535 damage. Fails if target is faster.",
},
flamethrower: {
inherit: true
flags: {defrost: 1},
}
furyattack: {
inherit: true,
desc: "Hits two to five times. Has a 3/8 chance to hit two or three times, and a 1/8 chance to hit four or five times. Damage is calculated once for the first hit and used for every hit. If one of the hits breaks the target's substitute, the move ends.",
},
furyswipes: {
inherit: true,
desc: "Hits two to five times. Has a 3/8 chance to hit two or three times, and a 1/8 chance to hit four or five times. Damage is calculated once for the first hit and used for every hit. If one of the hits breaks the target's substitute, the move ends.",
},
growth: {
inherit: true,
desc: "Raises the user's Attack and Special by 1 stage.",
shortDesc: "Raises the user's Attack and Special by 1.",
boosts: {
atk: 1,
spa: 1,
spd: 1,
},
},
guillotine: {
inherit: true,
desc: "Deals 65535 damage to the target. Fails if the target's Speed is greater than the user's.",
shortDesc: "Deals 65535 damage. Fails if target is faster.",
},
highjumpkick: {
inherit: true,
basepower: 100,
},
horndrill: {
inherit: true,
desc: "Deals 65535 damage to the target. Fails if the target's Speed is greater than the user's.",
shortDesc: "Deals 65535 damage. Fails if target is faster.",
},
hyperbeam: {
inherit: true,
desc: "If this move is successful, the user must recharge on the following turn and cannot select a move, unless the target or its substitute was knocked out by this move.",
shortDesc: "Can't move next turn if target or sub is not KOed.",
},
jumpkick: {
inherit: true,
basepower: 75,
},
karatechop: {
inherit: true,
critRatio: 2,
},
leechlife: {
inherit: true,
basepower: 70,
},
leechseed: {
inherit: true,
desc: "At the end of each of the target's turns, The Pokemon at the user's position steals 1/16 of the target's maximum HP, rounded down and multiplied by the target's current Toxic counter if it has one, even if the target currently has less than that amount of HP remaining. If the target switches out or any Pokemon uses Haze, this effect ends. Grass-type Pokemon are immune to this move.",
onHit() {},
effect: {
onStart(target) {
this.add('-start', target, 'move: Leech Seed');
},
onAfterMoveSelfPriority: 1,
onAfterMoveSelf(pokemon) {
let leecher = pokemon.side.foe.active[pokemon.volatiles['leechseed'].sourcePosition];
if (!leecher || leecher.fainted || leecher.hp <= 0) {
this.debug('Nothing to leech into');
return;
}
// We check if leeched Pokémon has Toxic to increase leeched damage.
let toxicCounter = 1;
let residualdmg = pokemon.volatiles['residualdmg'];
if (residualdmg) {
residualdmg.counter++;
toxicCounter = residualdmg.counter;
}
let toLeech = this.clampIntRange(Math.floor(pokemon.maxhp / 16), 1) * toxicCounter;
let damage = this.damage(toLeech, pokemon, leecher);
if (residualdmg) this.hint("In Gen 1, Leech Seed's damage is affected by Toxic's counter.", true);
if (!damage || toLeech > damage) {
this.hint("In Gen 1, Leech Seed recovery is not limited by the remaining HP of the seeded Pokemon.", true);
}
this.heal(toLeech, leecher, pokemon);
},
},
},
megadrain: {
inherit: true,
basepower: 70,
},
metronome: {
inherit: true,
desc: "A random move is selected for use, other than Metronome or Struggle.",
noMetronome: ['metronome', 'struggle'],
secondary: null,
target: "self",
type: "Normal",
},
mimic: {
inherit: true,
desc: "While the user remains active, this move is replaced by a random move known by the target, even if the user already knows that move. The copied move keeps the remaining PP for this move, regardless of the copied move's maximum PP. Whenever one PP is used for a copied move, one PP is used for this move.",
shortDesc: "Random move known by the target replaces this.",
onHit(target, source) {
let moveslot = source.moves.indexOf('mimic');
if (moveslot < 0) return false;
let moves = target.moves;
let moveid = this.sample(moves);
if (!moveid) return false;
let move = this.getMove(moveid);
source.moveSlots[moveslot] = {
move: move.name,
id: move.id,
pp: source.moveSlots[moveslot].pp,
maxpp: move.pp * 8 / 5,
target: move.target,
disabled: false,
used: false,
virtual: true,
};
this.add('-start', source, 'Mimic', move.name);
},
},
minimize: {
inherit: true,
desc: "Raises the user's evasiveness by 1 stage.",
},
mirrormove: {
inherit: true,
desc: "The user uses the last move used by the target. Fails if the target has not made a move, or if the last move used was Mirror Move.",
onHit(pokemon) {
let foe = pokemon.side.foe.active[0];
if (!foe || !foe.lastMove || foe.lastMove.id === 'mirrormove') {
return false;
}
this.useMove(foe.lastMove.id, pokemon);
},
},
mist: {
inherit: true,
desc: "For 5 turns, the user and its party members cannot have major status conditions or confusion inflicted on them by other Pokemon. It is removed from the user's side if the user or an ally is successfully hit by Defog. Fails if the effect is already active on the user's side.",
shortDesc: "For 5 turns, protects user's party from status.",
sideCondition: 'safeguard',
},
nightshade: {
inherit: true,
desc: "No additional effect.",
shortDesc: "No additional effect.",
basePower: 75,
},
petaldance: {
inherit: true,
desc: "Has a 33% chance to lower the target's Special by 1 stage.",
shortDesc: "33% chance to lower the target's Special by 1.",
basepower: 90,
secondary: {
chance: 33,
boosts: {
spd: -1,
spa: -1,
},
},
},
poisonsting: {
inherit: true,
desc: "Has a 20% chance to poison the target.",
shortDesc: "20% chance to poison the target.",
secondary: {
chance: 20,
status: 'psn',
},
},
psychic: {
inherit: true,
desc: "Has a 33% chance to lower the target's Special by 1 stage.",
shortDesc: "33% chance to lower the target's Special by 1.",
secondary: {
chance: 33,
boosts: {
spd: -1,
spa: -1,
},
},
},
psywave: {
inherit: true,
basePower: 1,
},
rage: {
inherit: true,
desc: "Once this move is successfully used, the user automatically uses this move every turn and can no longer switch out. During the effect, the user's Attack is raised by 1 stage every time it is hit by the opposing Pokemon, and this move's accuracy is overwritten every turn with the current calculated accuracy including stat stage changes, but not to less than 1/256 or more than 255/256.",
shortDesc: "Lasts forever. Raises user's Attack by 1 when hit.",
self: {
volatileStatus: 'rage',
},
effect: {
// Rage lock
duration: 255,
onStart(target, source, effect) {
this.effectData.move = 'rage';
},
onLockMove: 'rage',
onTryHit(target, source, move) {
if (target.boosts.atk < 6 && move.id === 'disable') {
this.boost({atk: 1});
}
},
onHit(target, source, move) {
if (target.boosts.atk < 6 && move.category !== 'Status') {
this.boost({atk: 1});
}
},
},
},
razorleaf: {
inherit: true,
critRatio: 2,
target: "normal",
},
razorwind: {
inherit: true,
desc: "No additional effect.",
shortDesc: "No additional effect.",
type: "Flying",
target: "normal",
secondary: null,
onTryMove(attacker, defender, move) {
this.attrLastMove('[still]');
this.addMove('-anim', attacker, move.name, defender);
return;
},
},
roar: {
inherit: true,
desc: "The target cannot avoid the user's moves.",
shortDesc: "User's next move will not miss the target.",
forceSwitch: false,
priority: 0,
boosts: {
accuracy: 6,
},
onHit(target) {
this.hint("+6 Accuracy is uses as a substitute for the X-Accuracy effect.");
}
},
},
rockslide: {
inherit: true,
desc: "No additional effect.",
shortDesc: "No additional effect.",
target: "normal",
},
seismictoss: {
inherit: true,
desc: "Deals damage to the target equal to the user's level. This move ignores type immunity.",
shortDesc: "Damage = user's level. Can hit Ghost types.",
ignoreImmunity: true,
basePower: 1,
},
selfdestruct: {
inherit: true,
desc: "The user faints after using this move, unless the target's substitute was broken by the damage. The target's Defense is halved during damage calculation.",
basePower: 130,
target: "normal",
},
skullbash: {
inherit: true,
desc: "This attack charges on the first turn and executes on the second.",
shortDesc: "Charges turn 1. Hits turn 2.",
onTryMove(attacker, defender, move) {
if (attacker.removeVolatile(move.id)) {
return;
}
this.add('-prepare', attacker, move.name, defender);
if (!this.runEvent('ChargeMove', attacker, defender, move)) {
return;
}
attacker.addVolatile('twoturnmove', defender);
return null;
},
},
skyattack: {
inherit: true,
desc: "This attack charges on the first turn and executes on the second.",
shortDesc: "Charges turn 1. Hits turn 2.",
basepower: 200,
},
slash: {
inherit: true,
critRatio: 2,
},
sludge: {
inherit: true,
desc: "Has a 40% chance to poison the target.",
shortDesc: "40% chance to poison the target.",
},
solarbeam: {
inherit: true,
desc: "This attack charges on the first turn and executes on the second.",
shortDesc: "Charges turn 1. Hits turn 2.",
basepower: 200,
},
sonicboom: {
inherit: true,
desc: "Deals 20 HP of damage to the target. This move ignores type immunity.",
},
spikecannon: {
inherit: true,
desc: "Hits two to five times. Has a 3/8 chance to hit two or three times, and a 1/8 chance to hit four or five times. Damage is calculated once for the first hit and used for every hit. If one of the hits breaks the target's substitute, the move ends.",
},
stomp: {
inherit: true,
desc: "Has a 30% chance to flinch the target.",
},
struggle: {
inherit: true,
desc: "Deals Normal-type damage. If this move was successful, the user takes damage equal to 1/2 the HP lost by the target, rounded down, but not less than 1 HP. This move is automatically used if none of the user's known moves can be selected.",
shortDesc: "User loses 1/2 the HP lost by the target.",
recoil: [1, 2],
onModifyMove() {},
},
submission: {
inherit: true,
desc: "If the target lost HP, the user takes recoil damage equal to 1/4 the HP lost by the target, rounded down, but not less than 1 HP. If this move breaks the target's substitute, the user does not take any recoil damage.",
},
substitute: {
num: 164,
accuracy: true,
basePower: 0,
category: "Status",
desc: "The user takes 1/4 of its maximum HP, rounded down, and puts it into a substitute to take its place in battle. The substitute has 1 HP plus the HP used to create it, and is removed once enough damage is inflicted on it or 255 damage is inflicted at once, or if the user switches out or faints. Until the substitute is broken, it receives damage from all attacks made by the opposing Pokemon and shields the user from status effects and stat stage changes caused by the opponent, unless the effect is Disable, Leech Seed, sleep, primary paralysis, or secondary confusion and the user's substitute did not break. The user still takes normal damage from status effects while behind its substitute, unless the effect is confusion damage, which is applied to the opposing Pokemon's substitute instead. If the substitute breaks during a multi-hit attack, the attack ends. Fails if the user does not have enough HP remaining to create a substitute, or if it already has a substitute. The user will create a substitute and then faint if its current HP is exactly 1/4 of its maximum HP.",
shortDesc: "User takes 1/4 its max HP to put in a Substitute.",
id: "substitute",
isViable: true,
name: "Substitute",
pp: 10,
priority: 0,
volatileStatus: 'substitute',
onTryHit(target) {
if (target.volatiles['substitute']) {
this.add('-fail', target, 'move: Substitute');
return null;
}
// We only prevent when hp is less than one quarter.
// If you use substitute at exactly one quarter, you faint.
if (target.hp === target.maxhp / 4) target.faint();
if (target.hp < target.maxhp / 4) {
this.add('-fail', target, 'move: Substitute', '[weak]');
return null;
}
},
onHit(target) {
// If max HP is 3 or less substitute makes no damage
if (target.maxhp > 3) {
this.directDamage(target.maxhp / 4, target, target);
}
},
effect: {
onStart(target) {
this.add('-start', target, 'Substitute');
this.effectData.hp = Math.floor(target.maxhp / 4) + 1;
delete target.volatiles['partiallytrapped'];
},
onTryHitPriority: -1,
onTryHit(target, source, move) {
if (move.category === 'Status') {
// In gen 1 it only blocks:
// poison, confusion, secondary effect confusion, stat reducing moves and Leech Seed.
let SubBlocked = ['lockon', 'meanlook', 'mindreader', 'nightmare'];
if (move.status === 'psn' || move.status === 'tox' || (move.boosts && target !== source) || move.volatileStatus === 'confusion' || SubBlocked.includes(move.id)) {
return false;
}
return;
}
if (move.volatileStatus && target === source) return;
// NOTE: In future generations the damage is capped to the remaining HP of the
// Substitute, here we deliberately use the uncapped damage when tracking lastDamage etc.
let uncappedDamage = this.getDamage(source, target, move);
if (!uncappedDamage) return null;
uncappedDamage = this.runEvent('SubDamage', target, source, move, uncappedDamage);
if (!uncappedDamage) return uncappedDamage;
source.lastDamage = uncappedDamage;
target.volatiles['substitute'].hp -= uncappedDamage > target.volatiles['substitute'].hp ?
/** @type {number} */(target.volatiles['substitute'].hp) : uncappedDamage;
if (target.volatiles['substitute'].hp <= 0) {
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(Math.round(uncappedDamage * move.recoil[0] / move.recoil[1]), source, target, 'recoil');
}
if (move.drain) {
this.heal(Math.ceil(uncappedDamage * move.drain[0] / move.drain[1]), source, target, 'drain');
}
}
this.runEvent('AfterSubDamage', target, source, move, uncappedDamage);
// Add here counter damage
let lastAttackedBy = target.getLastAttackedBy();
if (!lastAttackedBy) {
target.attackedBy.push({source: source, move: move.id, damage: uncappedDamage, thisTurn: true});
} else {
lastAttackedBy.move = move.id;
lastAttackedBy.damage = uncappedDamage;
}
return 0;
},
onEnd(target) {
this.add('-end', target, 'Substitute');
},
},
secondary: null,
target: "self",
type: "Normal",
},
superfang: {
inherit: true,
desc: "Deals damage to the target equal to half of its current HP, rounded down, but not less than 1 HP. This move ignores type immunity.",
shortDesc: "Damage = 1/2 target's current HP. Hits Ghosts.",
ignoreImmunity: true,
basePower: 1,
},
swift: {
inherit: true,
desc: "This move does not check accuracy and hits even if the target is using Dig or Fly.",
shortDesc: "Never misses, even against Dig and Fly.",
},
takedown: {
inherit: true,
desc: "If the target lost HP, the user takes recoil damage equal to 1/4 the HP lost by the target, rounded down, but not less than 1 HP. If this move breaks the target's substitute, the user does not take any recoil damage.",
},
thrash: {
inherit: true,
desc: "Whether or not this move is successful, the user spends three or four turns locked into this move and becomes confused immediately after its move on the last turn of the effect, even if it is already confused. If the user is prevented from moving, the effect ends without causing confusion. During the effect, this move's accuracy is overwritten every turn with the current calculated accuracy including stat stage changes, but not to less than 1/256 or more than 255/256.",
shortDesc: "Lasts 3-4 turns. Confuses the user afterwards.",
},
thunder: {
inherit: true,
desc: "Has a 10% chance to paralyze the target.",
shortDesc: "10% chance to paralyze the target.",
accuracy: 75,
secondary: {
chance: 10,
status: 'par',
},
},
thunderwave: {
inherit: true,
accuracy: 100,
onTryHit(target) {
if (target.hasType('Ground')) {
this.add('-immune', target);
return null;
}
},
},
transform: {
inherit: true,
desc: "The user transforms into the target. The target's current stats, stat stages, types, moves, DVs, species, and sprite are copied. The user's level and HP remain the same and each copied move receives only 5 PP. This move can hit a target using Dig or Fly.",
},
triattack: {
inherit: true,
desc: "No additional effect.",
shortDesc: "No additional effect.",
onHit() {},
secondary: null,
},
twineedle: {
inherit: true,
desc: "Hits twice, with the second hit having a 20% chance to poison the target. If the first hit breaks the target's substitute, the move ends.",
},
whirlwind: {
inherit: true,
accuracy: 100,
desc: "If this move is successful, the effects of Reflect, Light Screen, and Mist end for the target's side.",
shortDesc: "Clears user and target side's hazards.",
isViable: false,
forceSwitch: false,
onHit(target, source, move) {
let success = false;
if (!target.volatiles['substitute'] || move.infiltrates) success = !!this.boost({evasion: -1});
let removeTarget = ['reflect', 'lightscreen', 'safeguard', 'meanlook'];
for (const targetCondition of removeTarget) {
if (target.side.removeSideCondition(targetCondition)) {
if (!removeAll.includes(targetCondition)) continue;
this.add('-sideend', target.side, this.getEffect(targetCondition).name, '[from] move: Defog', '[of] ' + source);
success = true;
}
}
return success;
},
priority: 0,
},
wrap: {
inherit: true,
desc: "Prevents the target from switching out.",
accuracy: 100,
pp: 10,
onHit(target, source, move) {
return target.addVolatile('trapped', source, move, 'trapper');
},
secondary: null,
target: "normal",
},
};
exports.BattleMovedex = BattleMovedex;