module.exports = (function () { var dataTypes = ['Pokedex', 'Movedex', 'Statuses', 'TypeChart', 'Scripts', 'Items', 'Abilities', 'Formats', 'FormatsData', 'Learnsets', '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': 'formats.js', 'FormatsData': 'formats-data.js', 'Learnsets': 'learnsets.js', 'Aliases': 'aliases.js' }; function Tools(mod) { if (!mod) { mod = 'base'; this.isBase = true; } this.currentMod = mod; Data[mod] = { mod: mod }; if (mod === 'base') { dataTypes.forEach(function(dataType) { try { Data[mod][dataType] = require('./data/'+dataFiles[dataType])['Battle'+dataType]; } catch (e) {} if (!Data[mod][dataType]) Data[mod][dataType] = {}; }, this); } else { dataTypes.forEach(function(dataType) { try { Data[mod][dataType] = require('./mods/'+mod+'/'+dataFiles[dataType])['Battle'+dataType]; } catch (e) {} if (!Data[mod][dataType]) Data[mod][dataType] = {}; for (var i in Data.base[dataType]) { if (Data[mod][dataType][i] === null) { // null means don't inherit delete Data[mod][dataType][i]; } else if (typeof Data[mod][dataType][i] === 'undefined') { // If it doesn't exist is inherited from the base data Data[mod][dataType][i] = Object.clone(Data.base[dataType][i], true); } else if (Data[mod][dataType][i] && Data[mod][dataType][i].inherit) { // {inherit: true} can be used to modify only parts of the base data, // instead of overwriting entirely delete Data[mod][dataType][i].inherit; Object.merge(Data[mod][dataType][i], Data.base[dataType][i], true, false); } } }); } this.data = Data[mod]; // Scripts are Tools specific to a mod; they add to or overwrite base tools // Many Scripts are not meant to be run from Tools directly; rather, they're meant // to be copied into Battle.js using Tools.install and run from there for (var i in this.data.Scripts) { this[i] = this.data.Scripts[i]; } if (this.init) this.init(); } var moddedTools = {}; Tools.prototype.mod = function(mod) { if (!moddedTools[mod]) { mod = this.getFormat(mod).mod; } if (!mod) mod = 'base'; return moddedTools[mod]; }; Tools.prototype.effectToString = function() { return this.name; }; Tools.prototype.getImmunity = function(type, target) { for (var i=0; i= 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); move = {}; if (id.substr(0,12) === 'HiddenPower[') { var hptype = id.substr(12); hptype = hptype.substr(0,hptype.length-1); id = 'HiddenPower'+hptype; } if (id && this.data.Movedex[id]) { move = this.data.Movedex[id]; move.exists = true; } if (!move.id) move.id = id; if (!move.name) move.name = name; if (!move.fullname) move.fullname = '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 >= 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; } } 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; var 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 = 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); effect = {}; 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'; } if (!effect.id) effect.id = id; if (!effect.name) effect.name = name; if (!effect.fullname) effect.fullname = 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); item = {}; if (id && this.data.Items[id]) { item = this.data.Items[id]; item.exists = true; } if (!item.id) item.id = id; if (!item.name) item.name = name; if (!item.fullname) item.fullname = '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 }; } 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]; ability.exists = true; } if (!ability.id) ability.id = id; if (!ability.name) ability.name = name; if (!ability.fullname) ability.fullname = 'ability: '+name; ability.toString = this.effectToString; if (!ability.category) ability.category = 'Effect'; if (!ability.effectType) ability.effectType = 'Ability'; if (!ability.gen) { 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); type = {}; if (id && this.data.TypeChart[id]) { type = this.data.TypeChart[id]; 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; }; var BattleNatures = { Adamant: {plus:'atk', minus:'spa'}, Bashful: {}, Bold: {plus:'def', minus:'atk'}, Brave: {plus:'atk', minus:'spe'}, Calm: {plus:'spd', minus:'atk'}, Careful: {plus:'spd', minus:'spa'}, Docile: {}, Gentle: {plus:'spd', minus:'def'}, Hardy: {}, Hasty: {plus:'spe', minus:'def'}, Impish: {plus:'def', minus:'spa'}, Jolly: {plus:'spe', minus:'spa'}, Lax: {plus:'def', minus:'spd'}, Lonely: {plus:'atk', minus:'def'}, Mild: {plus:'spa', minus:'def'}, Modest: {plus:'spa', minus:'atk'}, Naive: {plus:'spe', minus:'spd'}, Naughty: {plus:'atk', minus:'spd'}, Quiet: {plus:'spa', minus:'spe'}, Quirky: {}, Rash: {plus:'spa', minus:'spd'}, Relaxed: {plus:'def', minus:'spe'}, Sassy: {plus:'spd', minus:'spe'}, Serious: {}, Timid: {plus:'spe', minus:'atk'}, }; Tools.prototype.getNature = function(nature) { if (typeof nature === 'string') nature = BattleNatures[nature]; if (!nature) nature = {}; return nature; }; Tools.prototype.natureModify = function(stats, nature) { if (typeof nature === 'string') nature = BattleNatures[nature]; if (!nature) return stats; if (nature.plus) stats[nature.plus] *= 1.1; if (nature.minus) stats[nature.minus] *= 0.9; return stats; }; Tools.prototype.checkLearnset = function(move, template, lsetData) { lsetData = lsetData || {set:{},format:{}}; var set = lsetData.set; var format = lsetData.format; var alreadyChecked = {}; var result = false; var isDW = (this.getAbility(set.ability).name === template.abilities.DW); var isMaleOnly = template.maleOnlyDreamWorld; var recheck = false; if (move.id) move = move.id; do { alreadyChecked[template.speciesid] = true; if (template.learnset) { if (template.learnset[move]) { var lset = template.learnset[move]; if (typeof lset === 'string') lset = [lset]; if (isDW) { result = null; // DW illegality if (isMaleOnly && !template.maleOnlyDreamWorld) { // the pokemon is released, but not its prevo(s) return result; } // the combination of DW ability and gen 3-4 exclusive move is illegal for (var i=0; i= 5) { result = 1; } else { return true; } } } } if (Object.keys(template.abilities).length === 1 && template.dreamWorldRelease && !recheck) { // Some Pokemon with no DW ability still have DW-exclusive moves (e.g. Gastly, Koffing, Chimecho) isDW = !isDW; recheck = true; alreadyChecked[template.speciesid] = null; continue; } else { recheck = false; } if (template.speciesid === 'shaymin') { template = this.getTemplate('shayminsky'); } else if (template.basespecies !== template.species && template.basespecies !== 'Kyurem') { template = this.getTemplate(template.basespecies); } else { template = this.getTemplate(template.prevo); } } while (template && template.species && !alreadyChecked[template.speciesid]); return result; }; 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 0) { var plusPlusPos = subformat.banlist[i].indexOf('++'); if (plusPlusPos && plusPlusPos > 0) { var complexList = subformat.banlist[i].split('++'); for (var j=0; j6) { return ["Your team has more than 6 pokemon."]; } var teamHas = {}; for (var i=0; i 510) { problems.push(name+" has more than 510 total EVs."); } if (ability.name !== template.abilities['0'] && ability.name !== template.abilities['1'] && ability.name !== template.abilities['DW']) { problems.push(name+" can't have "+set.ability+"."); } if (ability.name === template.abilities['DW']) { source = 'DW'; if (!template.dreamWorldRelease && banlistTable['Unreleased']) { problems.push(name+"'s Dream World ability is unreleased."); } else if (template.maleOnlyDreamWorld) { set.gender = 'M'; } } } var limit1 = 0; if (!set.moves || !set.moves.length) { problems.push(name+" has no moves."); } else { // A limit is imposed here to prevent too much engine strain or // too much layout deformation - to be exact, this is the Debug // Mode limitation. // The usual limit of 4 moves is handled elsewhere - currently // in the cartridge-compliant set validator: formats.js:pokemon set.moves = set.moves.slice(0,24); var lsetData = {set:set, format:format}; for (var i=0; i 1) { problems.push(name+" can't Sketch "+move.name+" - it's limited to 1 Sketch move."); } } } } } setHas[toId(template.tier)] = true; if (banlistTable[template.tier]) { problems.push(name+" is in "+template.tier+", which is banned."); } if (teamHas) { for (var i in setHas) { teamHas[i] = true; } } for (var i=0; i