pokemon-showdown/mods/gen3/scripts.js
2018-10-28 05:04:30 -05:00

231 lines
7.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict';
/**@type {ModdedBattleScriptsData} */
let BattleScripts = {
inherit: 'gen4',
gen: 3,
init() {
for (let i in this.data.Pokedex) {
delete this.data.Pokedex[i].abilities['H'];
}
let specialTypes = ['Fire', 'Water', 'Grass', 'Ice', 'Electric', 'Dark', 'Psychic', 'Dragon'];
let newCategory = '';
for (let i in this.data.Movedex) {
if (!this.data.Movedex[i]) console.log(i);
if (this.data.Movedex[i].category === 'Status') continue;
newCategory = specialTypes.includes(this.data.Movedex[i].type) ? 'Special' : 'Physical';
if (newCategory !== this.data.Movedex[i].category) {
this.modData('Movedex', i).category = newCategory;
}
}
},
tryMoveHit(target, pokemon, move) {
this.setActiveMove(move, pokemon, target);
let naturalImmunity = false;
let accPass = true;
let hitResult = this.singleEvent('PrepareHit', move, {}, target, pokemon, move);
if (!hitResult) {
if (hitResult === false) this.add('-fail', target);
return false;
}
this.runEvent('PrepareHit', pokemon, target, move);
if (!this.singleEvent('Try', move, null, pokemon, target, move)) {
return false;
}
if (move.target === 'all' || move.target === 'foeSide' || move.target === 'allySide' || move.target === 'allyTeam') {
if (move.target === 'all') {
hitResult = this.runEvent('TryHitField', target, pokemon, move);
} else {
hitResult = this.runEvent('TryHitSide', target, pokemon, move);
}
if (!hitResult) {
if (hitResult === false) this.add('-fail', target);
return false;
}
return this.moveHit(target, pokemon, move);
}
hitResult = this.runEvent('TryImmunity', target, pokemon, move);
if (!hitResult) {
if (!move.spreadHit) this.attrLastMove('[miss]');
this.add('-miss', pokemon, target);
return false;
}
if (move.ignoreImmunity === undefined) {
move.ignoreImmunity = (move.category === 'Status');
}
if ((!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) && !target.runImmunity(move.type)) {
naturalImmunity = true;
}
let boostTable = [1, 4 / 3, 5 / 3, 2, 7 / 3, 8 / 3, 3];
// calculate true accuracy
/**@type {number | true} */
let accuracy = move.accuracy;
let boosts, boost;
if (accuracy !== true) {
if (!move.ignoreAccuracy) {
boosts = this.runEvent('ModifyBoost', pokemon, null, null, Object.assign({}, pokemon.boosts));
boost = this.clampIntRange(boosts['accuracy'], -6, 6);
if (boost > 0) {
accuracy *= boostTable[boost];
} else {
accuracy /= boostTable[-boost];
}
}
if (!move.ignoreEvasion) {
boosts = this.runEvent('ModifyBoost', target, null, null, Object.assign({}, target.boosts));
boost = this.clampIntRange(boosts['evasion'], -6, 6);
if (boost > 0) {
accuracy /= boostTable[boost];
} else if (boost < 0) {
accuracy *= boostTable[-boost];
}
}
}
if (move.ohko) { // bypasses accuracy modifiers
if (!target.isSemiInvulnerable()) {
accuracy = 30;
if (pokemon.level >= target.level && (move.ohko === true || !target.hasType(move.ohko))) {
accuracy += (pokemon.level - target.level);
} else {
this.add('-immune', target, '[ohko]');
return false;
}
}
} else {
accuracy = this.runEvent('ModifyAccuracy', target, pokemon, move, accuracy);
}
if (move.alwaysHit) {
accuracy = true; // bypasses ohko accuracy modifiers
} else {
accuracy = this.runEvent('Accuracy', target, pokemon, move, accuracy);
}
if (accuracy !== true && !this.randomChance(accuracy, 100)) {
accPass = false;
}
if (accPass) {
hitResult = this.runEvent('TryHit', target, pokemon, move);
if (!hitResult) {
if (hitResult === false) this.add('-fail', target);
return false;
} else if (naturalImmunity) {
this.add('-immune', target, '[msg]');
return false;
}
} else {
if (naturalImmunity) {
this.add('-immune', target, '[msg]');
} else {
if (!move.spreadHit) this.attrLastMove('[miss]');
this.add('-miss', pokemon, target);
}
return false;
}
move.totalDamage = 0;
/**@type {number | false} */
let damage = 0;
pokemon.lastDamage = 0;
if (move.multihit) {
let hits = move.multihit;
if (Array.isArray(hits)) {
// yes, it's hardcoded... meh
if (hits[0] === 2 && hits[1] === 5) {
hits = this.sample([2, 2, 2, 3, 3, 3, 4, 5]);
} else {
hits = this.random(hits[0], hits[1] + 1);
}
}
hits = Math.floor(hits);
let nullDamage = true;
/**@type {number | false} */
let moveDamage;
// There is no need to recursively check the ´sleepUsable´ flag as Sleep Talk can only be used while asleep.
let isSleepUsable = move.sleepUsable || this.getMove(move.sourceEffect).sleepUsable;
let i;
for (i = 0; i < hits && target.hp && pokemon.hp; i++) {
if (pokemon.status === 'slp' && !isSleepUsable) break;
move.hit = i + 1;
if (move.multiaccuracy && i > 0) {
accuracy = move.accuracy;
if (accuracy !== true) {
if (!move.ignoreAccuracy) {
boosts = this.runEvent('ModifyBoost', pokemon, null, null, Object.assign({}, pokemon.boosts));
boost = this.clampIntRange(boosts['accuracy'], -6, 6);
if (boost > 0) {
accuracy *= boostTable[boost];
} else {
accuracy /= boostTable[-boost];
}
}
if (!move.ignoreEvasion) {
boosts = this.runEvent('ModifyBoost', target, null, null, Object.assign({}, target.boosts));
boost = this.clampIntRange(boosts['evasion'], -6, 6);
if (boost > 0) {
accuracy /= boostTable[boost];
} else if (boost < 0) {
accuracy *= boostTable[-boost];
}
}
}
accuracy = this.runEvent('ModifyAccuracy', target, pokemon, move, accuracy);
if (!move.alwaysHit) {
accuracy = this.runEvent('Accuracy', target, pokemon, move, accuracy);
if (accuracy !== true && !this.randomChance(accuracy, 100)) break;
}
}
moveDamage = this.moveHit(target, pokemon, move);
if (moveDamage === false) break;
if (nullDamage && (moveDamage || moveDamage === 0 || moveDamage === undefined)) nullDamage = false;
// Damage from each hit is individually counted for the
// purposes of Counter, Metal Burst, and Mirror Coat.
damage = (moveDamage || 0);
move.totalDamage += damage;
this.eachEvent('Update');
}
if (i === 0) return false;
if (nullDamage) damage = false;
this.add('-hitcount', target, i);
} else {
damage = this.moveHit(target, pokemon, move);
move.totalDamage = damage;
}
if (move.recoil && move.totalDamage) {
this.damage(this.calcRecoilDamage(move.totalDamage, move), pokemon, target, 'recoil');
}
if (target && pokemon !== target) target.gotAttacked(move, damage, pokemon);
if (move.ohko) this.add('-ohko');
if (!damage && damage !== 0) return damage;
this.eachEvent('Update');
if (target && !move.negateSecondary) {
this.singleEvent('AfterMoveSecondary', move, null, target, pokemon, move);
this.runEvent('AfterMoveSecondary', target, pokemon, move);
}
return damage;
},
calcRecoilDamage(damageDealt, move) {
// @ts-ignore
return this.clampIntRange(Math.floor(damageDealt * move.recoil[0] / move.recoil[1]), 1);
},
};
exports.BattleScripts = BattleScripts;