'use strict';
exports.BattleScripts = {
init: function () {
let coolMoves = ['acrobatics', 'aerialace', 'aquajet', 'blazekick', 'bravebird', 'brickbreak', 'bulletseed',
'circlethrow', 'crosschop', 'crosspoison', 'crushclaw', 'cut', 'doublehit', 'doublekick',
'dragonclaw', 'drillpeck', 'dynamicpunch', 'extremespeed', 'falseswipe', 'fellstinger',
'firefang', 'flamecharge', 'flareblitz', 'forcepalm', 'furyattack', 'furycutter',
'fusionbolt', 'gyroball', 'highjumpkick', 'hornattack', 'hyperfang', 'icefang', 'irontail',
'jumpkick', 'leafblade', 'machpunch', 'magnetbomb', 'megakick', 'megahorn', 'metalclaw',
'meteormash', 'nightslash', 'outrage', 'peck', 'phantomforce', 'pinmissile', 'precipiceblades',
'psychocut', 'punishment', 'quickattack', 'rapidspin', 'razorleaf', 'razorshell', 'retaliate', 'reversal',
'rollingkick', 'sacredsword', 'shadowclaw', 'shadowforce', 'skyattack', 'skyuppercut', 'slash',
'spark', 'spikecannon', 'steelwing', 'stormthrow', 'submission', 'thunderfang', 'thunderpunch',
'triplekick', 'twineedle', 'vcreate', 'vinewhip', 'vitalthrow', 'volttackle', 'watershuriken',
'wingattack', 'xscissor', 'aeroblast', 'aircutter', 'airslash', 'darkpulse', 'dragonbreath',
'electroball', 'extrasensory', 'focusblast', 'hyperbeam', 'hypervoice', 'leaftornado',
'nightdaze', 'oblivionwing', 'psystrike', 'searingshot', 'shockwave', 'solarbeam', 'swift',
'technoblast', 'thunder', 'thunderbolt', 'vacuumwave', 'voltswitch', 'zapcannon',
//everything after this is nv's fault
'holdback', 'razorwind', 'thundershock', 'trumpcard', 'twister'];
let toughMoves = ['armthrust', 'bite', 'bodyslam', 'boneclub', 'bonerush', 'bonemerang', 'bulldoze',
'bulletpunch', 'chipaway', 'clamp', 'closecombat', 'crabhammer', 'crunch', 'dig', 'doubleedge',
'dragonrush', 'dragontail', 'drainpunch', 'drillrun', 'dualchop', 'earthquake', 'firepunch',
'flyingpress', 'focuspunch', 'gunkshot', 'hammerarm', 'headcharge', 'headsmash', 'headbutt',
'heatcrash', 'heavyslam', 'hornleech', 'hyperspacefury', 'ironhead', 'lowkick', 'poisonjab',
'powerwhip', 'poweruppunch', 'revenge', 'rockblast', 'rockclimb', 'rockslide', 'rockthrow',
'rockwrecker', 'seedbomb', 'skullbash', 'slam', 'smackdown', 'smellingsalts', 'steamroller',
'stomp', 'stoneedge', 'superpower', 'takedown', 'thief', 'wakeupslap', 'waterfall', 'wildcharge',
'woodhammer', 'ancientpower', 'belch', 'boomburst', 'hurricane', 'incinerate', 'lavaplume',
'magmastorm', 'muddywater', 'octazooka', 'scald', 'sludgebomb', 'sludgewave', 'snarl',
//everything after this is nv's fault
'bind', 'cometpunch', 'constrict', 'crushgrip', 'furyswipes', 'gigaimpact', 'karatechop',
'magnitude', 'megapunch', 'payback', 'pound', 'rage', 'rocksmash', 'scratch', 'skydrop', 'strength',
'tackle', 'thrash', 'vicegrip', 'wrap', 'brine', 'mudshot', 'sludge', 'smog', 'spitup', 'wringout'];
let beautifulMoves = ['aquatail', 'avalanche', 'boltstrike', 'diamondstorm', 'explosion', 'firespin',
'flamewheel', 'freezeshock', 'iceball', 'icepunch', 'iceshard', 'iciclecrash', 'iciclespear', 'landswrath',
'petalblizzard', 'sacredfire', 'acidspray', 'aurasphere', 'blizzard', 'blueflare', 'bugbuzz', 'dazzlinggleam',
'doomdesire', 'dracometeor', 'dragonpulse', 'earthpower', 'energyball', 'eruption', 'fierydance',
'fireblast', 'flamethrower', 'flashcannon', 'freezedry', 'glaciate', 'heatwave', 'hydropump', 'icebeam',
'icywind', 'inferno', 'leafstorm', 'moonblast', 'mysticalfire', 'naturepower', 'originpulse', 'overheat',
'petaldance', 'powergem', 'psyshock', 'secretsword', 'seedflare', 'signalbeam', 'surf', 'triattack', 'waterpulse',
'waterspout', 'weatherball',
//nv's fault again
'dive', 'dragonascent', 'selfdestruct', 'aurorabeam', 'blastburn', 'bubblebeam', 'chargebeam', 'clearsmog',
'discharge', 'echoedvoice', 'electroweb', 'fairywind', 'firepledge', 'flameburst', 'frostbreath',
'fusionflare', 'grasspledge', 'hydrocannon', 'iceburn', 'judgment', 'magicalleaf', 'mirrorshot',
'ominouswind', 'powdersnow', 'psybeam', 'relicsong', 'roaroftime', 'round', 'silverwind', 'spacialrend',
'venoshock', 'waterpledge', 'whirlpool'];
let cleverMoves = ['attackorder', 'feint', 'feintattack', 'foulplay', 'geargrind', 'knockoff', 'lowsweep', 'naturalgift',
'needlearm', 'poisonfang', 'poisontail', 'pursuit', 'rocktomb', 'shadowpunch', 'shadowsneak',
'suckerpunch', 'zenheadbutt', 'dreameater', 'futuresight', 'gigadrain', 'hex', 'hiddenpower',
'hyperspacehole', 'lusterpurge', 'mistball', 'paraboliccharge', 'psychic', 'psychoboost',
'shadowball', 'storedpower',
//NV SwiftRage
'assurance', 'beatup', 'fly', 'payday', 'poisonsting', 'sandtomb', 'secretpower', 'absorb', 'acid',
'confusion', 'gust', 'leechlife', 'megadrain', 'synchronoise'];
let cuteMoves = ['bounce', 'bugbite', 'facade', 'fakeout', 'frustration', 'heartstamp', 'nuzzle', 'playrough', 'return',
'rollout', 'tailslap', 'uturn', 'drainingkiss', 'grassknot', 'infestation', 'mudbomb', 'strugglebug', 'uproar',
//you know the drill
'astonish', 'barrage', 'covet', 'dizzypunch', 'doubleslap', 'eggbomb', 'flail', 'fling', 'lastresort', 'lick',
'pluck', 'present', 'bubble', 'chatter', 'disarmingvoice', 'ember', 'mudslap', 'snore', 'watergun'];
//applying coolness
for (let i = 0; i < coolMoves.length; i++) {
let applyTo = coolMoves[i];
this.modData('Movedex', applyTo).contestCondition = 'cool';
}
//toughness
for (let i = 0; i < toughMoves.length; i++) {
let applyTo = toughMoves[i];
this.modData('Movedex', applyTo).contestCondition = 'tough';
}
//beauty
for (let i = 0; i < beautifulMoves.length; i++) {
let applyTo = beautifulMoves[i];
this.modData('Movedex', applyTo).contestCondition = 'beautiful';
}
//cleverness
for (let i = 0; i < cleverMoves.length; i++) {
let applyTo = cleverMoves[i];
this.modData('Movedex', applyTo).contestCondition = 'clever';
}
//cuteness
for (let i = 0; i < cuteMoves.length; i++) {
let applyTo = cuteMoves[i];
this.modData('Movedex', applyTo).contestCondition = 'cute';
}
},
getDamage: function (pokemon, target, move, suppressMessages) {
if (typeof move === 'string') move = this.getMove(move);
if (typeof move === 'number') {
move = {
basePower: move,
type: '???',
category: 'Physical',
flags: {},
};
}
if (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) {
if (!target.runImmunity(move.type, !suppressMessages)) {
return false;
}
}
if (move.ohko) {
return target.maxhp;
}
if (move.damageCallback) {
return move.damageCallback.call(this, pokemon, target);
}
if (move.damage === 'level') {
return pokemon.level;
}
if (move.damage) {
return move.damage;
}
if (!move) {
move = {};
}
if (!move.type) move.type = '???';
let type = move.type;
// '???' is typeless damage: used for Struggle and Confusion etc
let category = this.getCategory(move);
let defensiveCategory = move.defensiveCategory || category;
let basePower = move.basePower;
if (move.basePowerCallback) {
basePower = move.basePowerCallback.call(this, pokemon, target, move);
}
if (!basePower) {
if (basePower === 0) return; // returning undefined means not dealing damage
return basePower;
}
basePower = this.clampIntRange(basePower, 1);
let critMult;
if (this.gen <= 5) {
move.critRatio = this.clampIntRange(move.critRatio, 0, 5);
critMult = [0, 16, 8, 4, 3, 2];
} else {
move.critRatio = this.clampIntRange(move.critRatio, 0, 4);
critMult = [0, 16, 8, 2, 1];
}
move.crit = move.willCrit || false;
if (move.willCrit === undefined) {
if (move.critRatio) {
move.crit = (this.random(critMult[move.critRatio]) === 0);
}
}
if (move.crit) {
move.crit = this.runEvent('CriticalHit', target, null, move);
}
// happens after crit calculation
basePower = this.runEvent('BasePower', pokemon, target, move, basePower, true);
if (!basePower) return 0;
basePower = this.clampIntRange(basePower, 1);
let level = pokemon.level;
let attacker = pokemon;
let defender = target;
let attackStat;
//tada!
if (move.contestCondition) {
if (move.contestCondition === 'cool') attackStat = 'atk';
else if (move.contestCondition === 'tough') attackStat = 'def';
else if (move.contestCondition === 'beautiful') attackStat = 'spa';
else if (move.contestCondition === 'clever') attackStat = 'spd';
else if (move.contestCondition === 'cute') attackStat = 'spe';
}
let defenseStat = defensiveCategory === 'Physical' ? 'def' : 'spd';
let statTable = {atk:'Atk', def:'Def', spa:'SpA', spd:'SpD', spe:'Spe'};
let attack;
let defense;
let atkBoosts = move.useTargetOffensive ? defender.boosts[attackStat] : attacker.boosts[attackStat];
let defBoosts = move.useSourceDefensive ? attacker.boosts[defenseStat] : defender.boosts[defenseStat];
let ignoreNegativeOffensive = !!move.ignoreNegativeOffensive;
let ignorePositiveDefensive = !!move.ignorePositiveDefensive;
if (move.crit) {
ignoreNegativeOffensive = true;
ignorePositiveDefensive = true;
}
let ignoreOffensive = !!(move.ignoreOffensive || (ignoreNegativeOffensive && atkBoosts < 0));
let ignoreDefensive = !!(move.ignoreDefensive || (ignorePositiveDefensive && defBoosts > 0));
if (ignoreOffensive) {
this.debug('Negating (sp)atk boost/penalty.');
atkBoosts = 0;
}
if (ignoreDefensive) {
this.debug('Negating (sp)def boost/penalty.');
defBoosts = 0;
}
if (move.useTargetOffensive) {
attack = defender.calculateStat(attackStat, atkBoosts);
} else {
attack = attacker.calculateStat(attackStat, atkBoosts);
}
if (move.useSourceDefensive) {
defense = attacker.calculateStat(defenseStat, defBoosts);
} else {
defense = defender.calculateStat(defenseStat, defBoosts);
}
// Apply Stat Modifiers
attack = this.runEvent('Modify' + statTable[attackStat], attacker, defender, move, attack);
defense = this.runEvent('Modify' + statTable[defenseStat], defender, attacker, move, defense);
//int(int(int(2 * L / 5 + 2) * A * P / D) / 50);
let baseDamage = Math.floor(Math.floor(Math.floor(2 * level / 5 + 2) * basePower * attack / defense) / 50) + 2;
// multi-target modifier (doubles only)
if (move.spreadHit) {
let spreadModifier = move.spreadModifier || 0.75;
this.debug('Spread modifier: ' + spreadModifier);
baseDamage = this.modify(baseDamage, spreadModifier);
}
// weather modifier
baseDamage = this.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage);
// crit
if (move.crit) {
baseDamage = this.modify(baseDamage, move.critModifier || (this.gen >= 6 ? 1.5 : 2));
}
// this is not a modifier
baseDamage = this.randomizer(baseDamage);
// STAB
if (move.hasSTAB || type !== '???' && pokemon.hasType(type)) {
// The "???" type never gets STAB
// Not even if you Roost in Gen 4 and somehow manage to use
// Struggle in the same turn.
// (On second thought, it might be easier to get a Missingno.)
baseDamage = this.modify(baseDamage, move.stab || 1.5);
}
// types
move.typeMod = target.runEffectiveness(move);
move.typeMod = this.clampIntRange(move.typeMod, -6, 6);
if (move.typeMod > 0) {
if (!suppressMessages) this.add('-supereffective', target);
for (let i = 0; i < move.typeMod; i++) {
baseDamage *= 2;
}
}
if (move.typeMod < 0) {
if (!suppressMessages) this.add('-resisted', target);
for (let i = 0; i > move.typeMod; i--) {
baseDamage = Math.floor(baseDamage / 2);
}
}
if (move.crit && !suppressMessages) this.add('-crit', target);
if (pokemon.status === 'brn' && basePower && move.category === 'Physical' && !pokemon.hasAbility('guts')) {
if (this.gen < 6 || move.id !== 'facade') {
baseDamage = this.modify(baseDamage, 0.5);
}
}
// Generation 5 sets damage to 1 before the final damage modifiers only
if (this.gen === 5 && basePower && !Math.floor(baseDamage)) {
baseDamage = 1;
}
// Final modifier. Modifiers that modify damage after min damage check, such as Life Orb.
baseDamage = this.runEvent('ModifyDamage', pokemon, target, move, baseDamage);
if (this.gen !== 5 && basePower && !Math.floor(baseDamage)) {
return 1;
}
return Math.floor(baseDamage);
},
};