mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-05-06 13:47:24 -05:00
I used a hack to make the multiple declaration errors go away, so all that remained in jsHint were actual errors, which I've now mostly fixed. All this linting ended up uncovering one actual bug: Uproar's message not showing. It's a very minor bug in a move no one uses, so I'm going to fold it into this commit.
931 lines
28 KiB
JavaScript
931 lines
28 KiB
JavaScript
/**
|
|
* Tools
|
|
* Pokemon Showdown - http://pokemonshowdown.com/
|
|
*
|
|
* Handles getting data about pokemon, items, etc.
|
|
*
|
|
* This file is used by the main process (to validate teams)
|
|
* as well as the individual simulator processes (to get
|
|
* information about pokemon, items, etc to simulate).
|
|
*
|
|
* @license MIT license
|
|
*/
|
|
|
|
var fs = require('fs');
|
|
|
|
module.exports = (function () {
|
|
var moddedTools = {};
|
|
|
|
var dataTypes = ['FormatsData', 'Learnsets', 'Pokedex', 'Movedex', 'Statuses', 'TypeChart', 'Scripts', 'Items', 'Abilities', 'Formats', 'Aliases'];
|
|
var dataFiles = {
|
|
'Pokedex': 'pokedex.js',
|
|
'Movedex': 'moves.js',
|
|
'Statuses': 'statuses.js',
|
|
'TypeChart': 'typechart.js',
|
|
'Scripts': 'scripts.js',
|
|
'Items': 'items.js',
|
|
'Abilities': 'abilities.js',
|
|
'Formats': 'rulesets.js',
|
|
'FormatsData': 'formats-data.js',
|
|
'Learnsets': 'learnsets.js',
|
|
'Aliases': 'aliases.js'
|
|
};
|
|
function Tools(mod, parentMod) {
|
|
if (!mod) {
|
|
mod = 'base';
|
|
this.isBase = true;
|
|
} else if (!parentMod) {
|
|
parentMod = 'base';
|
|
}
|
|
this.currentMod = mod;
|
|
|
|
var data = this.data = {
|
|
mod: mod
|
|
};
|
|
if (mod === 'base') {
|
|
dataTypes.forEach(function (dataType) {
|
|
try {
|
|
var path = './data/' + dataFiles[dataType];
|
|
if (fs.existsSync(path)) {
|
|
data[dataType] = require(path)['Battle' + dataType];
|
|
}
|
|
} catch (e) {
|
|
console.log('CRASH LOADING DATA: ' + e.stack);
|
|
}
|
|
if (!data[dataType]) data[dataType] = {};
|
|
}, this);
|
|
try {
|
|
var path = './config/formats.js';
|
|
if (fs.existsSync(path)) {
|
|
var configFormats = require(path).Formats;
|
|
for (var i = 0; i < configFormats.length; i++) {
|
|
var format = configFormats[i];
|
|
var id = toId(format.name);
|
|
format.effectType = 'Format';
|
|
if (format.challengeShow === undefined) format.challengeShow = true;
|
|
if (format.searchShow === undefined) format.searchShow = true;
|
|
data.Formats[id] = format;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.log('CRASH LOADING FORMATS: ' + e.stack);
|
|
}
|
|
} else {
|
|
var parentData = moddedTools[parentMod].data;
|
|
dataTypes.forEach(function (dataType) {
|
|
try {
|
|
var path = './mods/' + mod + '/' + dataFiles[dataType];
|
|
if (fs.existsSync(path)) {
|
|
data[dataType] = require(path)['Battle' + dataType];
|
|
}
|
|
} catch (e) {
|
|
console.log('CRASH LOADING MOD DATA: ' + e.stack);
|
|
}
|
|
if (!data[dataType]) data[dataType] = {};
|
|
for (var i in parentData[dataType]) {
|
|
if (data[dataType][i] === null) {
|
|
// null means don't inherit
|
|
delete data[dataType][i];
|
|
} else if (!(i in data[dataType])) {
|
|
// If it doesn't exist it's inherited from the parent data
|
|
if (dataType === 'Pokedex') {
|
|
// Pokedex entries can be modified too many different ways
|
|
data[dataType][i] = Object.clone(parentData[dataType][i], true);
|
|
} else {
|
|
data[dataType][i] = parentData[dataType][i];
|
|
}
|
|
} else if (data[dataType][i] && data[dataType][i].inherit) {
|
|
// {inherit: true} can be used to modify only parts of the parent data,
|
|
// instead of overwriting entirely
|
|
delete data[dataType][i].inherit;
|
|
Object.merge(data[dataType][i], parentData[dataType][i], true, false);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
data['Natures'] = {
|
|
adamant: {name:"Adamant", plus:'atk', minus:'spa'},
|
|
bashful: {name:"Bashful"},
|
|
bold: {name:"Bold", plus:'def', minus:'atk'},
|
|
brave: {name:"Brave", plus:'atk', minus:'spe'},
|
|
calm: {name:"Calm", plus:'spd', minus:'atk'},
|
|
careful: {name:"Careful", plus:'spd', minus:'spa'},
|
|
docile: {name:"Docile"},
|
|
gentle: {name:"Gentle", plus:'spd', minus:'def'},
|
|
hardy: {name:"Hardy"},
|
|
hasty: {name:"Hasty", plus:'spe', minus:'def'},
|
|
impish: {name:"Impish", plus:'def', minus:'spa'},
|
|
jolly: {name:"Jolly", plus:'spe', minus:'spa'},
|
|
lax: {name:"Lax", plus:'def', minus:'spd'},
|
|
lonely: {name:"Lonely", plus:'atk', minus:'def'},
|
|
mild: {name:"Mild", plus:'spa', minus:'def'},
|
|
modest: {name:"Modest", plus:'spa', minus:'atk'},
|
|
naive: {name:"Naive", plus:'spe', minus:'spd'},
|
|
naughty: {name:"Naughty", plus:'atk', minus:'spd'},
|
|
quiet: {name:"Quiet", plus:'spa', minus:'spe'},
|
|
quirky: {name:"Quirky"},
|
|
rash: {name:"Rash", plus:'spa', minus:'spd'},
|
|
relaxed: {name:"Relaxed", plus:'def', minus:'spe'},
|
|
sassy: {name:"Sassy", plus:'spd', minus:'spe'},
|
|
serious: {name:"Serious"},
|
|
timid: {name:"Timid", plus:'spe', minus:'atk'}
|
|
};
|
|
}
|
|
|
|
Tools.prototype.mod = function (mod) {
|
|
if (!moddedTools[mod]) {
|
|
mod = this.getFormat(mod).mod;
|
|
}
|
|
if (!mod) mod = 'base';
|
|
return moddedTools[mod];
|
|
};
|
|
Tools.prototype.modData = function (dataType, id) {
|
|
if (this.isBase) return this.data[dataType][id];
|
|
var parentMod = this.data.Scripts.inherit;
|
|
if (!parentMod) parentMod = 'base';
|
|
if (this.data[dataType][id] !== moddedTools[parentMod].data[dataType][id]) return this.data[dataType][id];
|
|
return (this.data[dataType][id] = Object.clone(this.data[dataType][id], true));
|
|
};
|
|
|
|
Tools.prototype.effectToString = function () {
|
|
return this.name;
|
|
};
|
|
Tools.prototype.getImmunity = function (type, target) {
|
|
var types = target.getTypes && target.getTypes() || target.types;
|
|
for (var i = 0; i < types.length; i++) {
|
|
if (this.data.TypeChart[types[i]] && this.data.TypeChart[types[i]].damageTaken && this.data.TypeChart[types[i]].damageTaken[type] === 3) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
Tools.prototype.getEffectiveness = function (source, target, pokemon) {
|
|
if (source.getEffectiveness) {
|
|
return source.getEffectiveness.call(this, source, target, pokemon);
|
|
}
|
|
var type = source.type || source;
|
|
var totalTypeMod = 0;
|
|
var targetTypes = target.getTypes && target.getTypes() || target.types;
|
|
for (var i = 0; i < targetTypes.length; i++) {
|
|
if (!this.data.TypeChart[targetTypes[i]]) continue;
|
|
var typeMod = this.data.TypeChart[targetTypes[i]].damageTaken[type];
|
|
if (typeMod === 1) { // super-effective
|
|
totalTypeMod++;
|
|
}
|
|
if (typeMod === 2) { // resist
|
|
totalTypeMod--;
|
|
}
|
|
// in case of weird situations like Gravity, immunity is
|
|
// handled elsewhere
|
|
}
|
|
return totalTypeMod;
|
|
};
|
|
Tools.prototype.getTemplate = function (template) {
|
|
if (!template || typeof template === 'string') {
|
|
var name = (template || '').trim();
|
|
var id = toId(name);
|
|
if (this.data.Aliases[id]) {
|
|
name = this.data.Aliases[id];
|
|
id = toId(name);
|
|
}
|
|
template = {};
|
|
if (id && this.data.Pokedex[id]) {
|
|
template = this.data.Pokedex[id];
|
|
if (template.cached) return template;
|
|
template.cached = true;
|
|
template.exists = true;
|
|
}
|
|
name = template.species || template.name || name;
|
|
if (this.data.FormatsData[id]) {
|
|
Object.merge(template, this.data.FormatsData[id]);
|
|
}
|
|
if (this.data.Learnsets[id]) {
|
|
Object.merge(template, this.data.Learnsets[id]);
|
|
}
|
|
if (!template.id) template.id = id;
|
|
if (!template.name) template.name = name;
|
|
if (!template.speciesid) template.speciesid = id;
|
|
if (!template.species) template.species = name;
|
|
if (!template.baseSpecies) template.baseSpecies = name;
|
|
if (!template.forme) template.forme = '';
|
|
if (!template.formeLetter) template.formeLetter = '';
|
|
if (!template.spriteid) template.spriteid = toId(template.baseSpecies) + (template.baseSpecies !== name ? '-' + toId(template.forme) : '');
|
|
if (!template.prevo) template.prevo = '';
|
|
if (!template.evos) template.evos = [];
|
|
if (!template.nfe) template.nfe = !!template.evos.length;
|
|
if (!template.gender) template.gender = '';
|
|
if (!template.genderRatio && template.gender === 'M') template.genderRatio = {M:1, F:0};
|
|
if (!template.genderRatio && template.gender === 'F') template.genderRatio = {M:0, F:1};
|
|
if (!template.genderRatio && template.gender === 'N') template.genderRatio = {M:0, F:0};
|
|
if (!template.genderRatio) template.genderRatio = {M:0.5, F:0.5};
|
|
if (!template.tier && template.baseSpecies !== template.species) template.tier = this.data.FormatsData[toId(template.baseSpecies)].tier;
|
|
if (!template.tier) template.tier = 'Illegal';
|
|
if (!template.gen) {
|
|
if (template.forme && template.forme in {'Mega':1, 'Mega-X':1, 'Mega-Y':1}) {
|
|
template.gen = 6;
|
|
template.isMega = true;
|
|
} else if (template.num >= 650) template.gen = 6;
|
|
else if (template.num >= 494) template.gen = 5;
|
|
else if (template.num >= 387) template.gen = 4;
|
|
else if (template.num >= 252) template.gen = 3;
|
|
else if (template.num >= 152) template.gen = 2;
|
|
else if (template.num >= 1) template.gen = 1;
|
|
else template.gen = 0;
|
|
}
|
|
}
|
|
return template;
|
|
};
|
|
Tools.prototype.getMove = function (move) {
|
|
if (!move || typeof move === 'string') {
|
|
var name = (move || '').trim();
|
|
var id = toId(name);
|
|
if (this.data.Aliases[id]) {
|
|
name = this.data.Aliases[id];
|
|
id = toId(name);
|
|
}
|
|
move = {};
|
|
if (id.substr(0, 11) === 'hiddenpower') {
|
|
var matches = /([a-z]*)([0-9]*)/.exec(id);
|
|
id = matches[1];
|
|
}
|
|
if (id && this.data.Movedex[id]) {
|
|
move = this.data.Movedex[id];
|
|
if (move.cached) return move;
|
|
move.cached = true;
|
|
move.exists = true;
|
|
}
|
|
if (!move.id) move.id = id;
|
|
if (!move.name) move.name = name;
|
|
if (!move.fullname) move.fullname = 'move: ' + move.name;
|
|
move.toString = this.effectToString;
|
|
if (!move.critRatio) move.critRatio = 1;
|
|
if (!move.baseType) move.baseType = move.type;
|
|
if (!move.effectType) move.effectType = 'Move';
|
|
if (!move.secondaries && move.secondary) move.secondaries = [move.secondary];
|
|
if (!move.gen) {
|
|
if (move.num >= 560) move.gen = 6;
|
|
else if (move.num >= 468) move.gen = 5;
|
|
else if (move.num >= 355) move.gen = 4;
|
|
else if (move.num >= 252) move.gen = 3;
|
|
else if (move.num >= 166) move.gen = 2;
|
|
else if (move.num >= 1) move.gen = 1;
|
|
else move.gen = 0;
|
|
}
|
|
if (!move.priority) move.priority = 0;
|
|
}
|
|
return move;
|
|
};
|
|
/**
|
|
* Ensure we're working on a copy of a move (and make a copy if we aren't)
|
|
*
|
|
* Remember: "ensure" - by default, it won't make a copy of a copy:
|
|
* moveCopy === Tools.getMoveCopy(moveCopy)
|
|
*
|
|
* If you really want to, use:
|
|
* moveCopyCopy = Tools.getMoveCopy(moveCopy.id)
|
|
*
|
|
* @param move Move ID, move object, or movecopy object describing move to copy
|
|
* @return movecopy object
|
|
*/
|
|
Tools.prototype.getMoveCopy = function (move) {
|
|
if (move && move.isCopy) return move;
|
|
move = this.getMove(move);
|
|
var moveCopy = Object.clone(move, true);
|
|
moveCopy.isCopy = true;
|
|
return moveCopy;
|
|
};
|
|
Tools.prototype.getEffect = function (effect) {
|
|
if (!effect || typeof effect === 'string') {
|
|
var name = (effect || '').trim();
|
|
var id = toId(name);
|
|
effect = {};
|
|
if (id && this.data.Statuses[id]) {
|
|
effect = this.data.Statuses[id];
|
|
effect.name = effect.name || this.data.Statuses[id].name;
|
|
} else if (id && this.data.Movedex[id] && this.data.Movedex[id].effect) {
|
|
effect = this.data.Movedex[id].effect;
|
|
effect.name = effect.name || this.data.Movedex[id].name;
|
|
} else if (id && this.data.Abilities[id] && this.data.Abilities[id].effect) {
|
|
effect = this.data.Abilities[id].effect;
|
|
effect.name = effect.name || this.data.Abilities[id].name;
|
|
} else if (id && this.data.Items[id] && this.data.Items[id].effect) {
|
|
effect = this.data.Items[id].effect;
|
|
effect.name = effect.name || this.data.Items[id].name;
|
|
} else if (id && this.data.Formats[id]) {
|
|
effect = this.data.Formats[id];
|
|
effect.name = effect.name || this.data.Formats[id].name;
|
|
if (!effect.mod) effect.mod = this.currentMod;
|
|
if (!effect.effectType) effect.effectType = 'Format';
|
|
} else if (id === 'recoil') {
|
|
effect = {
|
|
effectType: 'Recoil'
|
|
};
|
|
} else if (id === 'drain') {
|
|
effect = {
|
|
effectType: 'Drain'
|
|
};
|
|
}
|
|
if (!effect.id) effect.id = id;
|
|
if (!effect.name) effect.name = name;
|
|
if (!effect.fullname) effect.fullname = effect.name;
|
|
effect.toString = this.effectToString;
|
|
if (!effect.category) effect.category = 'Effect';
|
|
if (!effect.effectType) effect.effectType = 'Effect';
|
|
}
|
|
return effect;
|
|
};
|
|
Tools.prototype.getFormat = function (effect) {
|
|
if (!effect || typeof effect === 'string') {
|
|
var name = (effect || '').trim();
|
|
var id = toId(name);
|
|
if (this.data.Aliases[id]) {
|
|
name = this.data.Aliases[id];
|
|
id = toId(name);
|
|
}
|
|
effect = {};
|
|
if (id && this.data.Formats[id]) {
|
|
effect = this.data.Formats[id];
|
|
if (effect.cached) return effect;
|
|
effect.cached = true;
|
|
effect.name = effect.name || this.data.Formats[id].name;
|
|
if (!effect.mod) effect.mod = this.currentMod;
|
|
if (!effect.effectType) effect.effectType = 'Format';
|
|
}
|
|
if (!effect.id) effect.id = id;
|
|
if (!effect.name) effect.name = name;
|
|
if (!effect.fullname) effect.fullname = effect.name;
|
|
effect.toString = this.effectToString;
|
|
if (!effect.category) effect.category = 'Effect';
|
|
if (!effect.effectType) effect.effectType = 'Effect';
|
|
this.getBanlistTable(effect);
|
|
}
|
|
return effect;
|
|
};
|
|
Tools.prototype.getItem = function (item) {
|
|
if (!item || typeof item === 'string') {
|
|
var name = (item || '').trim();
|
|
var id = toId(name);
|
|
if (this.data.Aliases[id]) {
|
|
name = this.data.Aliases[id];
|
|
id = toId(name);
|
|
}
|
|
item = {};
|
|
if (id && this.data.Items[id]) {
|
|
item = this.data.Items[id];
|
|
if (item.cached) return item;
|
|
item.cached = true;
|
|
item.exists = true;
|
|
}
|
|
if (!item.id) item.id = id;
|
|
if (!item.name) item.name = name;
|
|
if (!item.fullname) item.fullname = 'item: ' + item.name;
|
|
item.toString = this.effectToString;
|
|
if (!item.category) item.category = 'Effect';
|
|
if (!item.effectType) item.effectType = 'Item';
|
|
if (item.isBerry) item.fling = { basePower: 10 };
|
|
if (!item.gen) {
|
|
if (item.num >= 577) item.gen = 6;
|
|
else if (item.num >= 537) item.gen = 5;
|
|
else if (item.num >= 377) item.gen = 4;
|
|
// Due to difference in storing items, gen 2 items must be specified specifically
|
|
else item.gen = 3;
|
|
}
|
|
}
|
|
return item;
|
|
};
|
|
Tools.prototype.getAbility = function (ability) {
|
|
if (!ability || typeof ability === 'string') {
|
|
var name = (ability || '').trim();
|
|
var id = toId(name);
|
|
ability = {};
|
|
if (id && this.data.Abilities[id]) {
|
|
ability = this.data.Abilities[id];
|
|
if (ability.cached) return ability;
|
|
ability.cached = true;
|
|
ability.exists = true;
|
|
}
|
|
if (!ability.id) ability.id = id;
|
|
if (!ability.name) ability.name = name;
|
|
if (!ability.fullname) ability.fullname = 'ability: ' + ability.name;
|
|
ability.toString = this.effectToString;
|
|
if (!ability.category) ability.category = 'Effect';
|
|
if (!ability.effectType) ability.effectType = 'Ability';
|
|
if (!ability.gen) {
|
|
if (ability.num >= 165) ability.gen = 6;
|
|
else if (ability.num >= 124) ability.gen = 5;
|
|
else if (ability.num >= 77) ability.gen = 4;
|
|
else if (ability.num >= 1) ability.gen = 3;
|
|
else ability.gen = 0;
|
|
}
|
|
}
|
|
return ability;
|
|
};
|
|
Tools.prototype.getType = function (type) {
|
|
if (!type || typeof type === 'string') {
|
|
var id = toId(type);
|
|
id = id.substr(0, 1).toUpperCase() + id.substr(1);
|
|
type = {};
|
|
if (id && this.data.TypeChart[id]) {
|
|
type = this.data.TypeChart[id];
|
|
if (type.cached) return type;
|
|
type.cached = true;
|
|
type.exists = true;
|
|
type.isType = true;
|
|
type.effectType = 'Type';
|
|
}
|
|
if (!type.id) type.id = id;
|
|
if (!type.effectType) {
|
|
// man, this is really meta
|
|
type.effectType = 'EffectType';
|
|
}
|
|
}
|
|
return type;
|
|
};
|
|
Tools.prototype.getNature = function (nature) {
|
|
if (!nature || typeof nature === 'string') {
|
|
var name = (nature || '').trim();
|
|
var id = toId(name);
|
|
nature = {};
|
|
if (id && this.data.Natures[id]) {
|
|
nature = this.data.Natures[id];
|
|
if (nature.cached) return nature;
|
|
nature.cached = true;
|
|
nature.exists = true;
|
|
}
|
|
if (!nature.id) nature.id = id;
|
|
if (!nature.name) nature.name = name;
|
|
nature.toString = this.effectToString;
|
|
if (!nature.effectType) nature.effectType = 'Nature';
|
|
}
|
|
return nature;
|
|
};
|
|
Tools.prototype.natureModify = function (stats, nature) {
|
|
nature = this.getNature(nature);
|
|
if (nature.plus) stats[nature.plus] *= 1.1;
|
|
if (nature.minus) stats[nature.minus] *= 0.9;
|
|
return stats;
|
|
};
|
|
|
|
Tools.prototype.getBanlistTable = function (format, subformat, depth) {
|
|
var banlistTable;
|
|
if (!depth) depth = 0;
|
|
if (depth > 8) return; // avoid infinite recursion
|
|
if (format.banlistTable && !subformat) {
|
|
banlistTable = format.banlistTable;
|
|
} else {
|
|
if (!format.banlistTable) format.banlistTable = {};
|
|
if (!format.setBanTable) format.setBanTable = [];
|
|
if (!format.teamBanTable) format.teamBanTable = [];
|
|
|
|
banlistTable = format.banlistTable;
|
|
if (!subformat) subformat = format;
|
|
if (subformat.banlist) {
|
|
for (var i = 0; i < subformat.banlist.length; i++) {
|
|
// don't revalidate what we already validate
|
|
if (banlistTable[toId(subformat.banlist[i])]) continue;
|
|
|
|
banlistTable[subformat.banlist[i]] = subformat.name || true;
|
|
banlistTable[toId(subformat.banlist[i])] = subformat.name || true;
|
|
|
|
var plusPos = subformat.banlist[i].indexOf('+');
|
|
var complexList;
|
|
if (plusPos && plusPos > 0) {
|
|
var plusPlusPos = subformat.banlist[i].indexOf('++');
|
|
if (plusPlusPos && plusPlusPos > 0) {
|
|
complexList = subformat.banlist[i].split('++');
|
|
for (var j = 0; j < complexList.length; j++) {
|
|
complexList[j] = toId(complexList[j]);
|
|
}
|
|
format.teamBanTable.push(complexList);
|
|
} else {
|
|
complexList = subformat.banlist[i].split('+');
|
|
for (var j = 0; j < complexList.length; j++) {
|
|
complexList[j] = toId(complexList[j]);
|
|
}
|
|
format.setBanTable.push(complexList);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (subformat.ruleset) {
|
|
for (var i = 0; i < subformat.ruleset.length; i++) {
|
|
// don't revalidate what we already validate
|
|
if (banlistTable['Rule:' + toId(subformat.ruleset[i])]) continue;
|
|
|
|
banlistTable['Rule:' + toId(subformat.ruleset[i])] = subformat.ruleset[i];
|
|
if (format.ruleset.indexOf(subformat.ruleset[i]) === -1) format.ruleset.push(subformat.ruleset[i]);
|
|
|
|
var subsubformat = this.getFormat(subformat.ruleset[i]);
|
|
if (subsubformat.ruleset || subsubformat.banlist) {
|
|
this.getBanlistTable(format, subsubformat, depth + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return banlistTable;
|
|
};
|
|
|
|
Tools.prototype.levenshtein = function (s, t, l) { // s = string 1, t = string 2, l = limit
|
|
// Original levenshtein distance function by James Westgate, turned out to be the fastest
|
|
var d = []; // 2d matrix
|
|
|
|
// Step 1
|
|
var n = s.length;
|
|
var m = t.length;
|
|
|
|
if (n === 0) return m;
|
|
if (m === 0) return n;
|
|
if (l && Math.abs(m - n) > l) return Math.abs(m - n);
|
|
|
|
// Create an array of arrays in javascript (a descending loop is quicker)
|
|
for (var i = n; i >= 0; i--) d[i] = [];
|
|
|
|
// Step 2
|
|
for (var i = n; i >= 0; i--) d[i][0] = i;
|
|
for (var j = m; j >= 0; j--) d[0][j] = j;
|
|
|
|
// Step 3
|
|
for (var i = 1; i <= n; i++) {
|
|
var s_i = s.charAt(i - 1);
|
|
|
|
// Step 4
|
|
for (var j = 1; j <= m; j++) {
|
|
// Check the jagged ld total so far
|
|
if (i === j && d[i][j] > 4) return n;
|
|
|
|
var t_j = t.charAt(j - 1);
|
|
var cost = (s_i === t_j) ? 0 : 1; // Step 5
|
|
|
|
// Calculate the minimum
|
|
var mi = d[i - 1][j] + 1;
|
|
var b = d[i][j - 1] + 1;
|
|
var c = d[i - 1][j - 1] + cost;
|
|
|
|
if (b < mi) mi = b;
|
|
if (c < mi) mi = c;
|
|
|
|
d[i][j] = mi; // Step 6
|
|
}
|
|
}
|
|
|
|
// Step 7
|
|
return d[n][m];
|
|
};
|
|
|
|
Tools.prototype.clampIntRange = function (num, min, max) {
|
|
if (typeof num !== 'number') num = 0;
|
|
num = Math.floor(num);
|
|
if (num < min) num = min;
|
|
if (max !== undefined && num > max) num = max;
|
|
return num;
|
|
};
|
|
|
|
Tools.prototype.escapeHTML = function (str) {
|
|
if (!str) return '';
|
|
return ('' + str).escapeHTML();
|
|
};
|
|
|
|
Tools.prototype.dataSearch = function (target, searchIn) {
|
|
if (!target) {
|
|
return false;
|
|
}
|
|
|
|
searchIn = searchIn || ['Pokedex', 'Movedex', 'Abilities', 'Items', 'Natures'];
|
|
|
|
var searchFunctions = {Pokedex: 'getTemplate', Movedex: 'getMove', Abilities: 'getAbility', Items: 'getItem', Natures: 'getNature'};
|
|
var searchTypes = {Pokedex: 'pokemon', Movedex: 'move', Abilities: 'ability', Items: 'item', Natures: 'nature'};
|
|
var searchResults = [];
|
|
for (var i = 0; i < searchIn.length; i++) {
|
|
var res = this[searchFunctions[searchIn[i]]](target);
|
|
if (res.exists) {
|
|
res.searchType = searchTypes[searchIn[i]];
|
|
searchResults.push(res);
|
|
}
|
|
}
|
|
if (searchResults.length) {
|
|
return searchResults;
|
|
}
|
|
|
|
var cmpTarget = target.toLowerCase();
|
|
var maxLd = 3;
|
|
if (cmpTarget.length <= 1) {
|
|
return false;
|
|
} else if (cmpTarget.length <= 4) {
|
|
maxLd = 1;
|
|
} else if (cmpTarget.length <= 6) {
|
|
maxLd = 2;
|
|
}
|
|
for (var i = 0; i < searchIn.length; i++) {
|
|
var searchObj = this.data[searchIn[i]];
|
|
if (!searchObj) {
|
|
continue;
|
|
}
|
|
|
|
for (var j in searchObj) {
|
|
var word = searchObj[j];
|
|
if (typeof word === "object") {
|
|
word = word.name || word.species;
|
|
}
|
|
if (!word) {
|
|
continue;
|
|
}
|
|
|
|
var ld = this.levenshtein(cmpTarget, word.toLowerCase(), maxLd);
|
|
if (ld <= maxLd) {
|
|
searchResults.push({ word: word, ld: ld });
|
|
}
|
|
}
|
|
}
|
|
|
|
if (searchResults.length) {
|
|
var newTarget = "";
|
|
var newLD = 10;
|
|
for (var i = 0, l = searchResults.length; i < l; i++) {
|
|
if (searchResults[i].ld < newLD) {
|
|
newTarget = searchResults[i];
|
|
newLD = searchResults[i].ld;
|
|
}
|
|
}
|
|
|
|
// To make sure we aren't in an infinite loop...
|
|
if (cmpTarget !== newTarget.word) {
|
|
return this.dataSearch(newTarget.word);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
Tools.prototype.packTeam = function (team) {
|
|
if (!team) return '';
|
|
|
|
var buf = '';
|
|
|
|
for (var i = 0; i < team.length; i++) {
|
|
var set = team[i];
|
|
if (buf) buf += ']';
|
|
|
|
// name
|
|
buf += (set.name || set.species);
|
|
|
|
// species
|
|
var id = toId(set.species || set.name);
|
|
buf += '|' + (toId(set.name || set.species) === id ? '' : id);
|
|
|
|
// item
|
|
buf += '|' + toId(set.item);
|
|
|
|
// ability
|
|
var template = moddedTools.base.getTemplate(set.species || set.name);
|
|
var abilities = template.abilities;
|
|
id = toId(set.ability);
|
|
if (abilities) {
|
|
if (id === toId(abilities['0'])) {
|
|
buf += '|';
|
|
} else if (id === toId(abilities['1'])) {
|
|
buf += '|1';
|
|
} else if (id === toId(abilities['H'])) {
|
|
buf += '|H';
|
|
} else {
|
|
buf += '|' + id;
|
|
}
|
|
} else {
|
|
buf += '|' + id;
|
|
}
|
|
|
|
// moves
|
|
buf += '|' + set.moves.map(toId).join(',');
|
|
|
|
// nature
|
|
buf += '|' + set.nature;
|
|
|
|
// evs
|
|
var evs = '|';
|
|
if (set.evs) {
|
|
evs = '|' + (set.evs['hp'] || '') + ',' + (set.evs['atk'] || '') + ',' + (set.evs['def'] || '') + ',' + (set.evs['spa'] || '') + ',' + (set.evs['spd'] || '') + ',' + (set.evs['spe'] || '');
|
|
}
|
|
if (evs === '|,,,,,') {
|
|
buf += '|';
|
|
} else {
|
|
buf += evs;
|
|
}
|
|
|
|
// gender
|
|
if (set.gender && set.gender !== template.gender) {
|
|
buf += '|' + set.gender;
|
|
} else {
|
|
buf += '|';
|
|
}
|
|
|
|
// ivs
|
|
var ivs = '|';
|
|
if (set.ivs) {
|
|
ivs = '|' + (set.ivs['hp'] === 31 || set.ivs['hp'] === undefined ? '' : set.ivs['hp']) + ',' + (set.ivs['atk'] === 31 || set.ivs['atk'] === undefined ? '' : set.ivs['atk']) + ',' + (set.ivs['def'] === 31 || set.ivs['def'] === undefined ? '' : set.ivs['def']) + ',' + (set.ivs['spa'] === 31 || set.ivs['spa'] === undefined ? '' : set.ivs['spa']) + ',' + (set.ivs['spd'] === 31 || set.ivs['spd'] === undefined ? '' : set.ivs['spd']) + ',' + (set.ivs['spe'] === 31 || set.ivs['spe'] === undefined ? '' : set.ivs['spe']);
|
|
}
|
|
if (ivs === '|,,,,,') {
|
|
buf += '|';
|
|
} else {
|
|
buf += ivs;
|
|
}
|
|
|
|
// shiny
|
|
if (set.shiny) {
|
|
buf += '|S';
|
|
} else {
|
|
buf += '|';
|
|
}
|
|
|
|
// level
|
|
if (set.level && set.level != 100) {
|
|
buf += '|' + set.level;
|
|
} else {
|
|
buf += '|';
|
|
}
|
|
|
|
// happiness
|
|
if (set.happiness !== undefined && set.happiness !== 255) {
|
|
buf += '|' + set.happiness;
|
|
} else {
|
|
buf += '|';
|
|
}
|
|
}
|
|
|
|
return buf;
|
|
};
|
|
|
|
Tools.prototype.fastUnpackTeam = function (buf) {
|
|
if (!buf) return null;
|
|
|
|
var team = [];
|
|
var i = 0, j = 0;
|
|
|
|
// limit to 24
|
|
for (var count = 0; count < 24; count++) {
|
|
var set = {};
|
|
team.push(set);
|
|
|
|
// name
|
|
j = buf.indexOf('|', i);
|
|
if (j < 0) return;
|
|
set.name = buf.substring(i, j);
|
|
i = j + 1;
|
|
|
|
// species
|
|
j = buf.indexOf('|', i);
|
|
if (j < 0) return;
|
|
set.species = buf.substring(i, j) || set.name;
|
|
i = j + 1;
|
|
|
|
// item
|
|
j = buf.indexOf('|', i);
|
|
if (j < 0) return;
|
|
set.item = buf.substring(i, j);
|
|
i = j + 1;
|
|
|
|
// ability
|
|
j = buf.indexOf('|', i);
|
|
if (j < 0) return;
|
|
var ability = buf.substring(i, j);
|
|
var template = moddedTools.base.getTemplate(set.species);
|
|
set.ability = (template.abilities && ability in {'':1, 0:1, 1:1, H:1} ? template.abilities[ability || '0'] : ability);
|
|
i = j + 1;
|
|
|
|
// moves
|
|
j = buf.indexOf('|', i);
|
|
if (j < 0) return;
|
|
set.moves = buf.substring(i, j).split(',');
|
|
i = j + 1;
|
|
|
|
// nature
|
|
j = buf.indexOf('|', i);
|
|
if (j < 0) return;
|
|
set.nature = buf.substring(i, j);
|
|
i = j + 1;
|
|
|
|
// evs
|
|
j = buf.indexOf('|', i);
|
|
if (j < 0) return;
|
|
if (j !== i) {
|
|
var evs = buf.substring(i, j).split(',');
|
|
set.evs = {
|
|
hp: Number(evs[0]) || 0,
|
|
atk: Number(evs[1]) || 0,
|
|
def: Number(evs[2]) || 0,
|
|
spa: Number(evs[3]) || 0,
|
|
spd: Number(evs[4]) || 0,
|
|
spe: Number(evs[5]) || 0
|
|
};
|
|
}
|
|
i = j + 1;
|
|
|
|
// gender
|
|
j = buf.indexOf('|', i);
|
|
if (j < 0) return;
|
|
if (i !== j) set.gender = buf.substring(i, j);
|
|
i = j + 1;
|
|
|
|
// ivs
|
|
j = buf.indexOf('|', i);
|
|
if (j < 0) return;
|
|
if (j !== i) {
|
|
var ivs = buf.substring(i, j).split(',');
|
|
set.ivs = {
|
|
hp: ivs[0] === '' ? 31 : Number(ivs[0]) || 0,
|
|
atk: ivs[1] === '' ? 31 : Number(ivs[1]) || 0,
|
|
def: ivs[2] === '' ? 31 : Number(ivs[2]) || 0,
|
|
spa: ivs[3] === '' ? 31 : Number(ivs[3]) || 0,
|
|
spd: ivs[4] === '' ? 31 : Number(ivs[4]) || 0,
|
|
spe: ivs[5] === '' ? 31 : Number(ivs[5]) || 0
|
|
};
|
|
}
|
|
i = j + 1;
|
|
|
|
// shiny
|
|
j = buf.indexOf('|', i);
|
|
if (j < 0) return;
|
|
if (i !== j) set.shiny = true;
|
|
i = j + 1;
|
|
|
|
// level
|
|
j = buf.indexOf('|', i);
|
|
if (j < 0) return;
|
|
if (i !== j) set.level = parseInt(buf.substring(i, j), 10);
|
|
i = j + 1;
|
|
|
|
// happiness
|
|
j = buf.indexOf(']', i);
|
|
if (j < 0) {
|
|
if (buf.substring(i)) {
|
|
set.happiness = Number(buf.substring(i));
|
|
}
|
|
break;
|
|
}
|
|
if (i !== j) set.happiness = Number(buf.substring(i, j));
|
|
i = j + 1;
|
|
}
|
|
|
|
return team;
|
|
};
|
|
|
|
/**
|
|
* Install our Tools functions into the battle object
|
|
*/
|
|
Tools.prototype.install = function (battle) {
|
|
for (var i in this.data.Scripts) {
|
|
battle[i] = this.data.Scripts[i];
|
|
}
|
|
};
|
|
|
|
Tools.construct = function (mod, parentMod) {
|
|
var tools = new Tools(mod, parentMod);
|
|
// Scripts override Tools.
|
|
var ret = Object.create(tools);
|
|
tools.install(ret);
|
|
if (ret.init) {
|
|
if (parentMod && ret.init === moddedTools[parentMod].data.Scripts.init) {
|
|
// don't inherit init
|
|
delete ret.init;
|
|
} else {
|
|
ret.init();
|
|
}
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
moddedTools.base = Tools.construct();
|
|
|
|
// "gen6" is an alias for the current base data
|
|
moddedTools.gen6 = moddedTools.base;
|
|
|
|
var parentMods = {};
|
|
|
|
try {
|
|
var mods = fs.readdirSync('./mods/');
|
|
|
|
mods.forEach(function (mod) {
|
|
if (fs.existsSync('./mods/' + mod + '/scripts.js')) {
|
|
parentMods[mod] = require('./mods/' + mod + '/scripts.js').BattleScripts.inherit || 'base';
|
|
} else {
|
|
parentMods[mod] = 'base';
|
|
}
|
|
});
|
|
|
|
var didSomething = false;
|
|
do {
|
|
didSomething = false;
|
|
for (var i in parentMods) {
|
|
if (!moddedTools[i] && moddedTools[parentMods[i]]) {
|
|
moddedTools[i] = Tools.construct(i, parentMods[i]);
|
|
didSomething = true;
|
|
}
|
|
}
|
|
} while (didSomething);
|
|
} catch (e) {
|
|
console.log("Error while loading mods: " + e);
|
|
}
|
|
|
|
Object.getPrototypeOf(moddedTools.base).moddedTools = moddedTools;
|
|
|
|
return moddedTools.base;
|
|
})();
|