#!/usr/bin/env node 'use strict'; const fs = require("fs"); const path = require('path'); const child_process = require("child_process"); const rootDir = path.resolve(__dirname, '..'); process.chdir(rootDir); if (!fs.existsSync('caches/pokemon-showdown')) { child_process.execSync('git clone https://github.com/smogon/pokemon-showdown.git', { cwd: 'caches', }); } process.stdout.write("Syncing data from Git repository... "); child_process.execSync('git pull', { cwd: 'caches/pokemon-showdown' }); child_process.execSync('npm run build', { cwd: 'caches/pokemon-showdown' }); console.log("DONE"); const Dex = require('../caches/pokemon-showdown/dist/sim/dex').Dex; const CompoundWordNames = require('../caches/pokemon-showdown/dist/data/aliases').CompoundWordNames; const toID = Dex.toID; process.stdout.write("Loading gen 6 data... "); Dex.includeData(); console.log("DONE"); function es3stringify(obj) { const buf = JSON.stringify(obj); return buf.replace(/"([A-Za-z][A-Za-z0-9]*)":/g, (fullMatch, key) => ( ['return', 'new', 'delete'].includes(key) ? fullMatch : `${key}:` )); } function requireNoCache(pathSpec) { delete require.cache[require.resolve(pathSpec)]; return require(pathSpec); } /********************************************************* * Build search-index.js *********************************************************/ { process.stdout.write("Building `data/search-index.js`... "); let index = []; index = index.concat(Object.keys(Dex.data.Pokedex).map(x => x + ' pokemon')); index = index.concat(Object.keys(Dex.data.Moves).map(x => x + ' move')); index = index.concat(Object.keys(Dex.data.Items).map(x => x + ' item')); index = index.concat(Object.keys(Dex.data.Abilities).map(x => x + ' ability')); index = index.concat(Object.keys(Dex.data.TypeChart).map(x => toID(x) + ' type')); index = index.concat(['physical', 'special', 'status'].map(x => toID(x) + ' category')); index = index.concat([ 'monster', 'water1', 'bug', 'flying', 'field', 'fairy', 'grass', 'humanlike', 'water3', 'mineral', 'amorphous', 'water2', 'ditto', 'dragon', 'undiscovered', ].map(x => toID(x) + ' egggroup')); index = index.concat([ 'ou', 'uu', 'ru', 'nu', 'pu', 'zu', 'lc', 'nfe', 'uber', 'uubl', 'rubl', 'nubl', 'publ', 'zubl', 'cap', 'caplc', 'capnfe', ].map(x => toID(x) + ' tier')); const BattleArticleTitles = {}; try { for (const file of fs.readdirSync('../dex.pokemonshowdown.com/articles/')) { if (file.endsWith('.md')) { const id = file.slice(0, -3); const contents = '' + fs.readFileSync('../dex.pokemonshowdown.com/articles/' + file); if (contents.startsWith('# ')) { const title = contents.slice(2, contents.indexOf('\n')); if (title !== id.charAt(0).toUpperCase() + id.slice(1)) { BattleArticleTitles[id] = title; } } index.push('' + id + ' article'); } } } catch { console.log('\n(WARNING: NO ARTICLES)'); } index.push('pokemon article'); index.push('moves article'); const compoundTable = new Map(CompoundWordNames.map(word => [toID(word), word])); // generate aliases function generateAlias(id, name, type) { name = compoundTable.get(id) || name; if (type === 'pokemon' && !compoundTable.has(id)) { const species = Dex.species.get(id); const baseid = toID(species.baseSpecies); if (baseid !== id) { name = (compoundTable.get(baseid) || species.baseSpecies) + ' ' + species.forme; } } if (name.endsWith(' Mega-X') || name.endsWith(' Mega-Y')) { index.push('mega' + toID(name.slice(0, -7) + name.slice(-1)) + ' ' + type + ' ' + id + ' 0'); index.push('m' + toID(name.slice(0, -7) + name.slice(-1)) + ' ' + type + ' ' + id + ' 0'); index.push('mega' + toID(name.slice(-1)) + ' ' + type + ' ' + id + ' ' + toID(name.slice(0, -7)).length); } else if (name.endsWith(' Mega')) { index.push('mega' + toID(name.slice(0, -5)) + ' ' + type + ' ' + id + ' 0'); index.push('m' + toID(name.slice(0, -5)) + ' ' + type + ' ' + id + ' 0'); } else if (name.endsWith(' Alola')) { index.push('alolan' + toID(name.slice(0, -6)) + ' ' + type + ' ' + id + ' 0'); } else if (name.endsWith(' Galar')) { index.push('galarian' + toID(name.slice(0, -6)) + ' ' + type + ' ' + id + ' 0'); } else if (name.endsWith(' Hisui')) { index.push('hisuian' + toID(name.slice(0, -6)) + ' ' + type + ' ' + id + ' 0'); } const fullSplit = name.split(/ |-/).map(toID); if (fullSplit.length < 2) return; const fullAcronym = fullSplit.map(x => x.charAt(0)).join('') + fullSplit.at(-1).slice(1); index.push('' + fullAcronym + ' ' + type + ' ' + id + ' 0'); for (let i = 1; i < fullSplit.length; i++) { index.push('' + fullSplit.slice(i).join('') + ' ' + type + ' ' + id + ' ' + fullSplit.slice(0, i).join('').length); } const spaceSplit = name.split(' ').map(toID); if (spaceSplit.length !== fullSplit.length) { const spaceAcronym = spaceSplit.map(x => x.charAt(0)).join('') + spaceSplit.at(-1).slice(1); if (spaceAcronym !== fullAcronym) { index.push('' + spaceAcronym + ' ' + type + ' ' + id + ' 0'); } } } for (const id in Dex.data.Pokedex) { const name = Dex.data.Pokedex[id].name; if (Dex.data.Pokedex[id].isCosmeticForme) continue; generateAlias(id, name, 'pokemon'); } for (const id in Dex.data.Moves) { const name = Dex.data.Moves[id].name; generateAlias(id, name, 'move'); } for (const id in Dex.data.Items) { const name = Dex.data.Items[id].name; generateAlias(id, name, 'item'); } for (const id in Dex.data.Abilities) { const name = Dex.data.Abilities[id].name; generateAlias(id, name, 'ability'); } // hardcode ultra beasts const ultraBeasts = { ub01symbiont: "nihilego", ub02absorption: "buzzwole", ub02beauty: "pheromosa", ub03lightning: "xurkitree", ub04blade: "kartana", ub04blaster: "celesteela", ub05glutton: "guzzlord", ubburst: "blacephalon", ubassembly: "stakataka", ubadhesive: "poipole", ubstinger: "naganadel", }; for (const [ubCode, id] of Object.entries(ultraBeasts)) { index.push(`${ubCode} pokemon ${id} 0`); } index.sort(); // manually rearrange index[index.indexOf('grass type')] = 'grass egggroup'; index[index.indexOf('grass egggroup')] = 'grass type'; index[index.indexOf('fairy type')] = 'fairy egggroup'; index[index.indexOf('fairy egggroup')] = 'fairy type'; index[index.indexOf('flying type')] = 'flying egggroup'; index[index.indexOf('flying egggroup')] = 'flying type'; index[index.indexOf('dragon type')] = 'dragon egggroup'; index[index.indexOf('dragon egggroup')] = 'dragon type'; index[index.indexOf('bug type')] = 'bug egggroup'; index[index.indexOf('bug egggroup')] = 'bug type'; index[index.indexOf('psychic type')] = 'psychic move'; index[index.indexOf('psychic move')] = 'psychic type'; index[index.indexOf('ditto pokemon')] = 'ditto egggroup'; index[index.indexOf('ditto egggroup')] = 'ditto pokemon'; const BattleSearchIndex = index.map(x => { x = x.split(' '); if (x.length > 3) { x[3] = Number(x[3]); x[2] = index.indexOf(x[2] + ' ' + x[1]); } return x; }); const BattleSearchIndexOffset = BattleSearchIndex.map(entry => { const id = entry[0]; let name = ''; switch (entry[1]) { case 'pokemon': name = Dex.species.get(id).name; break; case 'move': name = Dex.moves.get(id).name; break; case 'item': name = Dex.items.get(id).name; break; case 'ability': name = Dex.abilities.get(id).name; break; case 'article': name = BattleArticleTitles[id] || ''; break; } let res = ''; let nonAlnum = 0; for (let i = 0, j = 0; i < id.length; i++, j++) { while (!/[a-zA-Z0-9]/.test(name[j])) { j++; nonAlnum++; } res += nonAlnum; } if (nonAlnum) return res; return ''; }); const BattleSearchCountIndex = {}; for (const type in Dex.data.TypeChart) { BattleSearchCountIndex[type + ' move'] = Object.keys(Dex.data.Moves) .filter(id => (Dex.data.Moves[id].type === type)).length; } for (const type in Dex.data.TypeChart) { BattleSearchCountIndex[type + ' pokemon'] = Object.keys(Dex.data.Pokedex) .filter(id => ( !Dex.data.Pokedex[id].isCosmeticForme && Dex.data.Pokedex[id].types.indexOf(type) >= 0 )).length; } let buf = '// DO NOT EDIT - automatically built with build-tools/build-indexes\n\n'; buf += 'exports.BattleSearchIndex = ' + JSON.stringify(BattleSearchIndex) + ';\n\n'; buf += 'exports.BattleSearchIndexOffset = ' + JSON.stringify(BattleSearchIndexOffset) + ';\n\n'; buf += 'exports.BattleSearchCountIndex = ' + JSON.stringify(BattleSearchCountIndex) + ';\n\n'; buf += 'exports.BattleArticleTitles = ' + JSON.stringify(BattleArticleTitles) + ';\n\n'; fs.writeFileSync('play.pokemonshowdown.com/data/search-index.js', buf); } console.log("DONE"); /********************************************************* * Build teambuilder-tables.js *********************************************************/ process.stdout.write("Building `data/teambuilder-tables.js`... "); { const BattleTeambuilderTable = {}; let buf = '// DO NOT EDIT - automatically built with build-tools/build-indexes\n\n'; const GENS = [9, 8, 7, 6, 5, 4, 3, 2, 1]; const DOUBLES = GENS.filter(x => x > 2).map(num => -num); const VGC = GENS.filter(x => x > 3).map(num => -num - 0.5); const NFE = GENS.map(num => num + 0.3); const LC = GENS.map(num => num + 0.7); const STADIUM = [2.04, 1.04]; const NATDEX = [9.1, 8.1]; const OTHER = [ 9.9, 9.6, 9.411, 9.41, 9.401, 9.4, 9.33, 9.2, -9.4, -9.401, 8.6, 8.4, 8.2, 8.1, -8.4, -8.6, 7.1, 5.1, 3.1, ]; // process.stdout.write("\n "); for (const genIdent of [...GENS, ...DOUBLES, ...VGC, ...NFE, ...STADIUM, ...OTHER, ...NATDEX, ...LC]) { const isLetsGo = (genIdent === 7.1); const isBDSP = (genIdent === 8.6 || genIdent === -8.6); const isMetBattle = ('' + genIdent).endsWith('.2'); const isNFE = ('' + genIdent).endsWith('.3'); const isLC = ('' + genIdent).endsWith('.7'); const isSSDLC1 = (genIdent === 8.4 || genIdent === -8.4); const isPreDLC = (genIdent === 9.4 || genIdent === 9.41 || genIdent === -9.4); const isSVDLC1 = (genIdent === 9.401 || genIdent === 9.411 || genIdent === -9.401); const isLegendsZA = genIdent === 9.33; const isNatDex = ('' + genIdent).endsWith('.1') && genIdent > 8; const isStadium = ('' + genIdent).endsWith('.04'); const isDoubles = (genIdent < 0); const isVGC = ('' + genIdent).endsWith('.5'); const isGen9BH = genIdent === 9.9; const isSSB = genIdent === 9.6; const genNum = Math.floor(isDoubles ? -genIdent : genIdent); const isBW1 = genIdent === 5.1; const isRS = genIdent === 3.1; const gen = (() => { let genStr = 'gen' + genNum; if (isSSDLC1) genStr += 'dlc1'; if (isLetsGo) genStr += 'letsgo'; if (isBDSP) genStr += 'bdsp'; if (isPreDLC) genStr += 'predlc'; if (isSVDLC1) genStr += 'dlc1'; if (isLegendsZA) genStr += 'legendsou'; if (isStadium) genStr += 'stadium' + (genNum > 1 ? genNum : ''); if (isSSB) genStr += 'ssb'; if (isBW1) genStr += 'bw1'; if (isRS) genStr += 'rs'; return genStr; })(); // process.stdout.write("" + gen + (isDoubles ? " doubles" : "") + "... "); const pokemon = Object.keys(Dex.data.Pokedex); pokemon.sort(); const tierTable = {}; const overrideTier = {}; const metagameBans = {}; const nonstandardMoves = []; const gen5zuBans = {}; const gen4puBans = {}; for (const id of pokemon) { const species = Dex.mod(gen).species.get(id); const baseSpecies = Dex.mod(gen).species.get(species.baseSpecies); if (species.gen > genNum) continue; const speciesTier = (() => { if (isMetBattle) { let tier = species.tier; if (species.isNonstandard) { if (species.isNonstandard === 'Past') { tier = Dex.mod('gen7').species.get(species.name).tier; } else { tier = 'OU'; } } if (species.isNonstandard === 'Gigantamax') tier = '(Uber)'; if (species.tier === 'CAP LC') tier = 'LC'; if (species.tier === 'CAP NFE') tier = 'NFE'; if (species.tier === 'CAP') tier = 'OU'; const format = Dex.formats.get(gen + 'metronomebattle'); let bst = 0; for (const stat of Object.values(species.baseStats)) { bst += stat; } if (bst > 625) tier = 'Illegal'; if (Dex.formats.getRuleTable(format).isBannedSpecies(species)) tier = 'Illegal'; if (species.types.includes('Steel')) tier = 'Illegal'; return tier; } if (isNFE) { let tier = species.tier; if (!species.nfe) tier = 'Illegal'; const format = Dex.formats.get(gen + 'nfe'); const banlist = Dex.formats.getRuleTable(format); if (banlist.isBannedSpecies(species)) { tier = 'Uber'; } return tier; } if (isLC) { let tier = species.tier; const lc = Dex.formats.get(gen + 'lc'); const lcBanlist = Dex.formats.getRuleTable(lc); const prevoSpecies = Dex.mod(gen).species.get(species.prevo); if (!species.nfe || (prevoSpecies.exists && prevoSpecies.gen <= genNum) || lcBanlist.isBannedSpecies(species)) { tier = 'Illegal'; } if (/^([OURNPZ]U(BL)?|Uber|AG)$/g.test(tier) && tier !== 'Illegal') { tier = 'LC'; } return tier; } if (isLetsGo) { const validNum = (baseSpecies.num <= 151 && species.num >= 1) || [808, 809].includes(baseSpecies.num); if (!validNum) return 'Illegal'; if (species.forme && !['Alola', 'Mega', 'Mega-X', 'Mega-Y', 'Starter'].includes(species.forme)) return 'Illegal'; if (species.name === 'Pikachu-Alola') return 'Illegal'; return species.tier; } if (isVGC) { if (species.isNonstandard && species.isNonstandard !== 'Gigantamax') return 'Illegal'; if (baseSpecies.tags.includes('Mythical')) return 'Mythical'; if (baseSpecies.tags.includes('Restricted Legendary')) return 'Restricted Legendary'; if (species.tier === 'NFE') return 'NFE'; if (species.tier === 'LC') return 'LC'; return 'Regular'; } if (isRS) { if (species.isNonstandard) { if (species.isNonstandard === 'Unobtainable') return 'Unreleased'; return 'Illegal'; } if (species.tier === 'Uber') return 'Uber'; if (species.nfe) { if (species.prevo.length === 0) return "LC"; return "NFE"; } return "Regular"; } if (isGen9BH) { if ((species.natDexTier === 'Illegal' || species.forme.includes('Totem')) && !['Floette-Eternal', 'Greninja-Ash', 'Xerneas-Neutral'].includes(species.name)) { return 'Illegal'; } if ((species.name === 'Xerneas' || species.battleOnly || species.forme === 'Eternamax') && !(species.isMega || species.isPrimal || ['Greninja-Ash', 'Necrozma-Ultra'].includes(species.name))) { return 'Illegal'; } if (species.isNonstandard && ['LGPE', 'CAP', 'Future'].includes(species.isNonstandard)) return 'Illegal'; return species.tags.includes('Mythical') ? 'Mythical' : species.tags.includes('Restricted Legendary') ? 'Restricted Legendary' : species.nfe ? (species.prevo ? 'NFE' : 'LC') : 'Regular'; } if (species.tier === 'CAP' || species.tier === 'CAP NFE' || species.tier === 'CAP LC') { return species.tier; } if (isDoubles && genNum > 4) { return species.doublesTier; } if (isNatDex || (isPreDLC && genNum === 9.41) || (isSVDLC1 && genNum === 9.411)) { return species.natDexTier; } return species.tier; })(); overrideTier[species.id] = speciesTier; if (species.forme) { if ( [ 'Aegislash', 'Castform', 'Cherrim', 'Cramorant', 'Eiscue', 'Meloetta', 'Mimikyu', 'Minior', 'Morpeko', 'Ramnarok', 'Wishiwashi', ].includes(species.baseSpecies) || species.forme.includes('Totem') || species.forme.includes('Zen') || (species.baseSpecies === 'Ogerpon' && species.forme.includes('Tera')) || species.isCosmeticForme ) { continue; } } if (!tierTable[speciesTier]) tierTable[speciesTier] = []; tierTable[speciesTier].push(id); if (genNum >= 6) { for (const meta of [ '1v1', '2v2doubles', 'lcuu', 'freeforall', 'ubersuu', 'almostanyability', 'balancedhackmons', 'godlygift', 'mixandmega', 'sharedpower', 'stabmons', '12switch', '350cup', 'alphabetcup', 'badnboosted', 'battlefields', 'biomechmons', 'camomons', 'categoryswap', 'convergence', 'crossevolution', 'ferventimpersonation', 'foresighters', 'formemons', 'fortemons', 'franticfusions', 'fullpotential', 'inheritance', 'inverse', 'natureswap', 'partnersincrime', 'passiveaggressive', 'pokebilities', 'pokemoves', 'relayrace', 'revelationmons', 'sharingiscaring', 'teradonation', 'teraoverride', 'thecardgame', 'thelosersgame', 'trademarked', 'triples', 'typesplit', 'voltturnmayhem', 'nationaldexubersuu', 'nationaldex1v1', 'nationaldexaaa', 'nationaldexbh', 'nationaldexgodlygift', 'nationaldexstabmons', 'tiershift', ]) { const format = Dex.formats.get(gen + meta); if (format.exists && Dex.formats.getRuleTable(format).isBannedSpecies(species)) { if (!metagameBans[meta]) metagameBans[meta] = {}; if (Dex.formats.getRuleTable(format).has('megarayquazaclause')) { metagameBans[meta]['megarayquazaclause'] = 1; } if (species.requiredItems && species.requiredItems.length) { for (const itemName of species.requiredItems) { const item = Dex.items.get(itemName); if (item.itemUser && item.itemUser.includes(species.name) && Dex.formats.getRuleTable(format).isBanned('item:' + item.id)) { metagameBans[meta][species.id] = 1; } if (item.megaStone && Object.values(item.megaStone).includes(species.name) && Dex.formats.getRuleTable(format).isBanned('item:' + item.id)) { metagameBans[meta][species.id] = 1; } } } if (species.requiredAbility) { const ability = Dex.items.get(species.requiredAbility); if (Dex.formats.getRuleTable(format).isBanned('ability:' + ability.id)) { metagameBans[meta][species.id] = 1; } } metagameBans[meta][species.id] = 1; } } const nd35Pokes = Dex.formats.get(gen + 'nationaldex35pokes'); if (nd35Pokes.exists && !Dex.formats.getRuleTable(nd35Pokes).isBannedSpecies(species)) { if (!metagameBans['nationaldex35pokes']) metagameBans['nationaldex35pokes'] = {}; metagameBans['nationaldex35pokes'][species.id] = 1; } } if (genNum >= 8) { for (const meta of ['nationaldexdoubles', 'nationaldexmonotype']) { const format = Dex.formats.get(gen + meta); if (format.exists && Dex.formats.getRuleTable(format).isBannedSpecies(species)) { if (!metagameBans[meta]) metagameBans[meta] = {}; metagameBans[meta][species.id] = 1; } } } if (genNum >= 5) { if (genNum === 5) { const gen5zu = Dex.formats.get(gen + 'zu'); if (gen5zu.exists && Dex.formats.getRuleTable(gen5zu).isBannedSpecies(species)) { gen5zuBans[species.id] = 1; } } const mono = Dex.formats.get(gen + 'monotype'); if (mono.exists && Dex.formats.getRuleTable(mono).isBannedSpecies(species)) { if (!metagameBans['monotype']) metagameBans['monotype'] = {}; metagameBans['monotype'][species.id] = 1; } } if (genNum === 4) { const gen4pu = Dex.formats.get(gen + 'pu'); if (gen4pu.exists && Dex.formats.getRuleTable(gen4pu).isBannedSpecies(species)) { gen4puBans[species.id] = 1; } } } nonstandardMoves.push(...Object.keys(Dex.data.Moves).filter(id => { const move = Dex.mod(isSSDLC1 ? 'gen8dlc1' : isPreDLC ? 'gen9predlc' : 'gen9dlc1').moves.get(id); const bMove = Dex.mod(isSSDLC1 ? 'gen8' : 'gen9').moves.get(id); return bMove.isNonstandard !== move.isNonstandard; })); const tiers = []; const items = []; const formatSlices = {}; if (isNatDex || (isPreDLC && genNum === 9.41) || (isSVDLC1 && genNum === 9.411)) { BattleTeambuilderTable['gen' + genNum + 'natdex'] = {}; BattleTeambuilderTable['gen' + genNum + 'natdex'].tiers = tiers; BattleTeambuilderTable['gen' + genNum + 'natdex'].overrideTier = overrideTier; BattleTeambuilderTable['gen' + genNum + 'natdex'].items = items; BattleTeambuilderTable['gen' + genNum + 'natdex'].metagameBans = metagameBans; BattleTeambuilderTable['gen' + genNum + 'natdex'].formatSlices = formatSlices; } else if (isMetBattle) { BattleTeambuilderTable[gen + 'metronome'] = {}; BattleTeambuilderTable[gen + 'metronome'].tiers = tiers; BattleTeambuilderTable[gen + 'metronome'].items = items; BattleTeambuilderTable[gen + 'metronome'].formatSlices = formatSlices; } else if (isNFE) { BattleTeambuilderTable[gen + 'nfe'] = {}; BattleTeambuilderTable[gen + 'nfe'].tiers = tiers; BattleTeambuilderTable[gen + 'nfe'].overrideTier = overrideTier; BattleTeambuilderTable[gen + 'nfe'].formatSlices = formatSlices; } else if (isLC) { BattleTeambuilderTable[gen + 'lc'] = {}; BattleTeambuilderTable[gen + 'lc'].tiers = tiers; BattleTeambuilderTable[gen + 'lc'].overrideTier = overrideTier; BattleTeambuilderTable[gen + 'lc'].metagameBans = metagameBans; BattleTeambuilderTable[gen + 'lc'].formatSlices = formatSlices; } else if (isLetsGo) { BattleTeambuilderTable['gen7letsgo'] = {}; BattleTeambuilderTable['gen7letsgo'].learnsets = {}; BattleTeambuilderTable['gen7letsgo'].tiers = tiers; BattleTeambuilderTable['gen7letsgo'].overrideTier = overrideTier; BattleTeambuilderTable['gen7letsgo'].formatSlices = formatSlices; } else if (isBDSP && !isDoubles) { BattleTeambuilderTable['gen8bdsp'] = {}; BattleTeambuilderTable['gen8bdsp'].learnsets = {}; BattleTeambuilderTable['gen8bdsp'].tiers = tiers; BattleTeambuilderTable['gen8bdsp'].items = items; BattleTeambuilderTable['gen8bdsp'].overrideTier = overrideTier; BattleTeambuilderTable['gen8bdsp'].metagameBans = metagameBans; BattleTeambuilderTable['gen8bdsp'].formatSlices = formatSlices; } else if (isVGC) { BattleTeambuilderTable[gen + 'vgc'] = {}; BattleTeambuilderTable[gen + 'vgc'].tiers = tiers; BattleTeambuilderTable[gen + 'vgc'].formatSlices = formatSlices; } else if (isDoubles) { BattleTeambuilderTable[gen + 'doubles'] = {}; BattleTeambuilderTable[gen + 'doubles'].tiers = tiers; BattleTeambuilderTable[gen + 'doubles'].items = items; BattleTeambuilderTable[gen + 'doubles'].overrideTier = overrideTier; BattleTeambuilderTable[gen + 'doubles'].metagameBans = metagameBans; BattleTeambuilderTable[gen + 'doubles'].formatSlices = formatSlices; } else if (isGen9BH) { BattleTeambuilderTable['bh'] = {}; BattleTeambuilderTable['bh'].tiers = tiers; BattleTeambuilderTable['bh'].overrideTier = overrideTier; BattleTeambuilderTable['bh'].metagameBans = metagameBans; BattleTeambuilderTable['bh'].formatSlices = formatSlices; } else if (isSSB) { BattleTeambuilderTable['gen9ssb'] = {}; BattleTeambuilderTable['gen9ssb'].tiers = tiers; BattleTeambuilderTable['gen9ssb'].overrideTier = overrideTier; BattleTeambuilderTable['gen9ssb'].formatSlices = formatSlices; } else if (isLegendsZA) { BattleTeambuilderTable['gen9legendsou'] = {}; BattleTeambuilderTable['gen9legendsou'].tiers = tiers; BattleTeambuilderTable['gen9legendsou'].overrideTier = overrideTier; BattleTeambuilderTable['gen9legendsou'].formatSlices = formatSlices; BattleTeambuilderTable['gen9legendsou'].items = items; BattleTeambuilderTable['gen9legendsou'].learnsets = {}; } else if (gen === 'gen9') { BattleTeambuilderTable.tiers = tiers; BattleTeambuilderTable.items = items; BattleTeambuilderTable.overrideTier = overrideTier; BattleTeambuilderTable.metagameBans = metagameBans; BattleTeambuilderTable.formatSlices = formatSlices; } else if (isBW1 || isRS) { BattleTeambuilderTable[gen] = {}; BattleTeambuilderTable[gen].overrideTier = overrideTier; BattleTeambuilderTable[gen].tiers = tiers; BattleTeambuilderTable[gen].items = items; BattleTeambuilderTable[gen].formatSlices = formatSlices; BattleTeambuilderTable[gen].nonstandardMoves = nonstandardMoves; BattleTeambuilderTable[gen].learnsets = {}; } else { BattleTeambuilderTable[gen] = {}; if (genNum === 5) { BattleTeambuilderTable[gen].gen5zuBans = gen5zuBans; } if (genNum === 4) { BattleTeambuilderTable[gen].gen4puBans = gen4puBans; } BattleTeambuilderTable[gen].overrideTier = overrideTier; BattleTeambuilderTable[gen].tiers = tiers; BattleTeambuilderTable[gen].items = items; BattleTeambuilderTable[gen].formatSlices = formatSlices; if (genNum >= 5) { BattleTeambuilderTable[gen].metagameBans = metagameBans; } if (isSSDLC1 || isPreDLC || isSVDLC1) { BattleTeambuilderTable[gen].nonstandardMoves = nonstandardMoves; BattleTeambuilderTable[gen].learnsets = {}; } } const tierOrder = (() => { if (isVGC || isGen9BH) { return ["Mythical", "Restricted Legendary", "Regular", "NFE", "LC"]; } if (isRS) { return ["Regular", "NFE", "LC", "Uber"]; } if (isDoubles && genNum > 4) { return ["DUber", "(DUber)", "DOU", "DBL", "(DOU)", "DUU", "(DUU)", "New", "NFE", "LC"]; } if (gen === 'gen4') { return ["CAP", "CAP NFE", "CAP LC", "AG", "Uber", "OU", "(OU)", "UUBL", "UU", "NUBL", "NU", "NFE", "LC"]; } return [ "CAP", "CAP NFE", "CAP LC", "AG", "Uber", "(Uber)", "OU", "(OU)", "UUBL", "UU", "RUBL", "RU", "NUBL", "NU", "PUBL", "PU", "ZUBL", "ZU", "New", "NFE", "LC", "Unreleased", ]; })(); for (const tier of tierOrder) { if (tier in { OU: 1, UUBL: 1, AG: 1, Uber: 1, UU: 1, RU: 1, NU: 1, PU: 1, ZU: 1, NFE: 1, LC: 1, DOU: 1, DUU: 1, "(DUU)": 1, New: 1, Legal: 1, Regular: 1, "Restricted Legendary": 1, "CAP LC": 1, }) { let usedTier = tier; if (usedTier === "(DUU)") usedTier = "DNU"; formatSlices[usedTier] = tiers.length; } if (!tierTable[tier]) continue; if (tier === "(PU)") { tiers.push(['header', "Below PU"]); } else if (tier === "(DUU)") { tiers.push(['header', "Below DUU"]); } else if (tier.charAt(0) === '(') { tiers.push(['header', tier.slice(1, -1) + " by technicality"]); } else if (tier === "NFE") { tiers.push(['header', "NFEs not in a higher tier"]); } else { tiers.push(['header', tier]); } tiers.push(...tierTable[tier]); } if (!isDoubles) { if (!formatSlices['OU']) formatSlices['OU'] = formatSlices['New']; if (!formatSlices['UU']) formatSlices['UU'] = formatSlices['New']; if (!formatSlices['RU']) formatSlices['RU'] = formatSlices['New']; } else { if (!formatSlices['DOU']) formatSlices['DOU'] = formatSlices['New']; if (!formatSlices['DUU']) formatSlices['DUU'] = formatSlices['New']; } const itemList = Object.keys(Dex.data.Items); itemList.sort(); const greatItems = [['header', "Popular items"]]; const goodItems = [['header', "Items"]]; const specificItems = [['header', "Pok\u00e9mon-specific items"]]; const poorItems = [['header', "Usually useless items"]]; const badItems = [['header', "Useless items"]]; const unreleasedItems = []; if (genNum === 6) unreleasedItems.push(['header', "Unreleased"]); for (const id of itemList) { const item = Dex.mod(gen).items.get(id); if (item.gen > genNum) { continue; } if (item.isNonstandard && !isMetBattle) { if (isNatDex) { let curItem = item; let curGen = genNum; while (curItem.isNonstandard && curGen >= 7) { curItem = Dex.forGen(curGen).items.get(item.id); curGen--; } if (curItem.isNonstandard) continue; } else if (genNum !== 2) { continue; } } if (isMetBattle) { const banlist = Dex.formats.getRuleTable(Dex.formats.get(gen + 'metronomebattle')); if (banlist.isBanned('item:' + item.id)) continue; } switch (id) { // mainstays case 'choiceband': case 'choicespecs': case 'eviolite': greatItems.push(id); break; // great everywhere but metronome case 'leftovers': case 'lifeorb': case 'choicescarf': case 'assaultvest': case 'focussash': case 'powerherb': case 'rockyhelmet': if (!isMetBattle) greatItems.push(id); else goodItems.push(id); break; // just singles case 'airballoon': case 'loadeddice': case 'heavydutyboots': case 'expertbelt': case 'salacberry': if (isDoubles || isMetBattle) goodItems.push(id); else greatItems.push(id); break; // just doubles case 'safetygoggles': case 'ejectbutton': case 'ejectpack': if (isDoubles) greatItems.push(id); else goodItems.push(id); break; // doubles + metronome case 'covertcloak': case 'clearamulet': case 'weaknesspolicy': if (isDoubles || isMetBattle) greatItems.push(id); else goodItems.push(id); break; // metronome only case 'mirrorherb': if (isMetBattle) greatItems.push(id); else goodItems.push(id); break; // generation specific case 'mentalherb': if (isMetBattle) goodItems.push(id); else if (genNum > 4) greatItems.push(id); else poorItems.push(id); break; case 'lumberry': if (genNum === 2 || genNum > 6) goodItems.push(id); else greatItems.push(id); break; case 'sitrusberry': if (genNum > 3 && (genNum < 7 || isDoubles)) greatItems.push(id); else if (genNum > 6) goodItems.push(id); else poorItems.push(id); break; case 'aguavberry': case 'figyberry': case 'iapapaberry': case 'magoberry': case 'wikiberry': if (genNum === 7) greatItems.push(id); else if (genNum >= 8) goodItems.push(id); else poorItems.push(id); break; case 'berryjuice': if (genNum === 2) poorItems.push(id); else goodItems.push(id); break; case 'dragonfang': if (genNum === 2) badItems.push(id); else goodItems.push(id); break; case 'dragonscale': if (genNum === 2) goodItems.push(id); else badItems.push(id); break; case 'mail': if (genNum >= 6) unreleasedItems.push(id); else goodItems.push(id); break; // Legendaries case 'adamantorb': case 'griseousorb': case 'lustrousorb': case 'blueorb': case 'redorb': case 'souldew': // falls through // Other case 'leek': case 'stick': case 'thickclub': case 'lightball': case 'luckypunch': case 'quickpowder': case 'metalpowder': case 'deepseascale': case 'deepseatooth': specificItems.push(id); break; // Fling-only case 'rarebone': case 'belueberry': case 'blukberry': case 'cornnberry': case 'durinberry': case 'hondewberry': case 'magostberry': case 'nanabberry': case 'nomelberry': case 'pamtreberry': case 'pinapberry': case 'pomegberry': case 'qualotberry': case 'rabutaberry': case 'razzberry': case 'spelonberry': case 'tamatoberry': case 'watmelberry': case 'wepearberry': case 'energypowder': case 'electirizer': case 'oldamber': case 'dawnstone': case 'dubiousdisc': case 'duskstone': case 'firestone': case 'icestone': case 'leafstone': case 'magmarizer': case 'moonstone': case 'ovalstone': case 'prismscale': case 'protector': case 'reapercloth': case 'sachet': case 'shinystone': case 'sunstone': case 'thunderstone': case 'upgrade': case 'waterstone': case 'whippeddream': case 'bottlecap': case 'goldbottlecap': case 'galaricacuff': case 'chippedpot': case 'crackedpot': case 'galaricawreath': case 'auspiciousarmor': case 'maliciousarmor': case 'masterpieceteacup': case 'metalalloy': case 'unremarkableteacup': case 'bignugget': badItems.push(id); break; // outclassed items case 'aspearberry': case 'bindingband': case 'cheriberry': case 'destinyknot': case 'enigmaberry': case 'floatstone': case 'ironball': case 'jabocaberry': case 'oranberry': case 'machobrace': case 'pechaberry': case 'persimberry': case 'poweranklet': case 'powerband': case 'powerbelt': case 'powerbracer': case 'powerlens': case 'powerweight': case 'rawstberry': case 'ringtarget': case 'rowapberry': case 'bigroot': case 'focusband': // gen 2 case 'psncureberry': case 'przcureberry': case 'burntberry': case 'bitterberry': case 'iceberry': case 'berry': poorItems.push(id); break; default: if ( item.name.endsWith(" Ball") || item.name.endsWith(" Fossil") || item.name.startsWith("Fossilized ") || item.name.endsWith(" Sweet") || item.name.endsWith(" Apple") ) { badItems.push(id); } else if (item.name.startsWith("TR")) { badItems.push(id); } else if (item.name.endsWith(" Gem") && item.name !== "Normal Gem") { if (genNum >= 6) { unreleasedItems.push(id); } else { goodItems.push(id); } } else if (item.name.endsWith(" Drive")) { specificItems.push(id); } else if (item.name.endsWith(" Memory")) { specificItems.push(id); } else if (item.name.startsWith("Rusted")) { specificItems.push(id); } else if (item.itemUser) { specificItems.push(id); } else if (item.megaStone) { specificItems.push(id); } else { goodItems.push(id); } } } items.push(...greatItems); items.push(...goodItems); items.push(...specificItems); items.push(...poorItems); items.push(...badItems); items.push(...unreleasedItems); } // // Learnset table // const gen3HMs = new Set(['cut', 'fly', 'surf', 'strength', 'flash', 'rocksmash', 'waterfall', 'dive']); const gen4HMs = new Set(['cut', 'fly', 'surf', 'strength', 'rocksmash', 'waterfall', 'rockclimb']); const learnsets = {}; BattleTeambuilderTable.learnsets = learnsets; for (const id in Dex.data.Learnsets) { const learnset = Dex.data.Learnsets[id].learnset; if (!learnset) continue; learnsets[id] = {}; for (const moveid in learnset) { const gens = learnset[moveid].map(x => Number(x[0])); const minGen = Math.min(...gens); if (minGen <= 4 && (gen3HMs.has(moveid) || gen4HMs.has(moveid))) { let legalGens = ''; let available = false; if (minGen === 3) { legalGens += '3'; available = true; } if (available) available = !gen3HMs.has(moveid); if (available || gens.includes(4)) { legalGens += '4'; available = true; } if (available) available = !gen4HMs.has(moveid); const minUpperGen = available ? 5 : Math.min( ...gens.filter(gen => gen > 4) ); legalGens += '0123456789'.slice(minUpperGen); learnsets[id][moveid] = legalGens; } else { learnsets[id][moveid] = '0123456789'.slice(minGen); } if (gens.indexOf(6) >= 0) learnsets[id][moveid] += 'p'; if (gens.indexOf(7) >= 0 && learnset[moveid].some(x => x[0] === '7' && x !== '7V')) { learnsets[id][moveid] += 'q'; } if (gens.indexOf(8) >= 0 && learnset[moveid].some(x => x[0] === '8' && x !== '8V')) { learnsets[id][moveid] += 'g'; } if (gens.indexOf(9) >= 0 && learnset[moveid].some(x => x[0] === '9' && x !== '9V')) { learnsets[id][moveid] += 'a'; } if (gens.indexOf(9) >= 0 && learnset[moveid].some(x => x === '9E')) learnsets[id][moveid] += 'e'; } } const G2Learnsets = Dex.mod('gen2').data.Learnsets; for (const id in G2Learnsets) { const learnset = G2Learnsets[id].learnset; for (const moveid in learnset) { const gens = learnset[moveid].map(x => Number(x[0])); const minGen = Math.min(...gens); if (!learnsets[id]) { throw new Error(`${id} has a Gen 2 learnset but not a modern learnset`); } if (!learnsets[id][moveid]) learnsets[id][moveid] = ''; if (minGen === 2) learnsets[id][moveid] = '2' + learnsets[id][moveid]; if (minGen === 1) learnsets[id][moveid] = '12' + learnsets[id][moveid]; } } const G3RSLearnsets = Dex.mod('gen3rs').data.Learnsets; for (const id in G3RSLearnsets) { const species = Dex.mod('gen3rs').species.get(id); if (species.isNonstandard && !['Unobtainable', 'CAP'].includes(species.isNonstandard)) continue; const learnset = G3RSLearnsets[id].learnset; BattleTeambuilderTable['gen3rs'].learnsets[id] = {}; for (const moveid in learnset) { BattleTeambuilderTable['gen3rs'].learnsets[id][moveid] = '3'; } } const G5BW1Learnsets = Dex.mod('gen5bw1').data.Learnsets; for (const id in G5BW1Learnsets) { const species = Dex.mod('gen5bw1').species.get(id); if (species.isNonstandard && !['Unobtainable', 'CAP'].includes(species.isNonstandard)) continue; const learnset = G5BW1Learnsets[id].learnset; BattleTeambuilderTable['gen5bw1'].learnsets[id] = {}; for (const moveid in learnset) { BattleTeambuilderTable['gen5bw1'].learnsets[id][moveid] = '5'; } } const LGLearnsets = Dex.mod('gen7letsgo').data.Learnsets; for (const id in LGLearnsets) { const species = Dex.mod('gen7letsgo').species.get(id); const baseSpecies = Dex.mod('gen7letsgo').species.get(species.baseSpecies); const validNum = (baseSpecies.num <= 151 && baseSpecies.num >= 1) || [808, 809].includes(baseSpecies.num); if (!validNum) continue; if (species.forme && !['Alola', 'Mega', 'Mega-X', 'Mega-Y', 'Starter'].includes(species.forme)) continue; const learnset = LGLearnsets[id].learnset; BattleTeambuilderTable['gen7letsgo'].learnsets[id] = {}; for (const moveid in learnset) { BattleTeambuilderTable['gen7letsgo'].learnsets[id][moveid] = '7'; } } const BDSPLearnsets = Dex.mod('gen8bdsp').data.Learnsets; for (const id in BDSPLearnsets) { const species = Dex.mod('gen8bdsp').species.get(id); if (species.isNonstandard && !['Unobtainable', 'CAP'].includes(species.isNonstandard)) continue; const learnset = BDSPLearnsets[id].learnset; BattleTeambuilderTable['gen8bdsp'].learnsets[id] = {}; for (const moveid in learnset) { BattleTeambuilderTable['gen8bdsp'].learnsets[id][moveid] = '8g'; } } const SSDLC1Learnsets = Dex.mod('gen8dlc1').data.Learnsets; for (const id in SSDLC1Learnsets) { const learnset = SSDLC1Learnsets[id].learnset; if (!learnset) continue; BattleTeambuilderTable['gen8dlc1'].learnsets[id] = {}; for (const moveid in learnset) { const gens = learnset[moveid].map(x => Number(x[0])); const minGen = Math.min(...gens); if (minGen <= 4 && (gen3HMs.has(moveid) || gen4HMs.has(moveid))) { let legalGens = ''; let available = false; if (minGen === 3) { legalGens += '3'; available = true; } if (available) available = !gen3HMs.has(moveid); if (available || gens.includes(4)) { legalGens += '4'; available = true; } if (available) available = !gen4HMs.has(moveid); const minUpperGen = available ? 5 : Math.min( ...gens.filter(gen => gen > 4) ); legalGens += '012345678'.slice(minUpperGen); BattleTeambuilderTable['gen8dlc1'].learnsets[id][moveid] = legalGens; } else { BattleTeambuilderTable['gen8dlc1'].learnsets[id][moveid] = '012345678'.slice(minGen); } if (gens.indexOf(6) >= 0) BattleTeambuilderTable['gen8dlc1'].learnsets[id][moveid] += 'p'; if (gens.indexOf(7) >= 0 && learnset[moveid].some(x => x[0] === '7' && x !== '7V')) { BattleTeambuilderTable['gen8dlc1'].learnsets[id][moveid] += 'q'; } if (gens.indexOf(8) >= 0 && learnset[moveid].some(x => x[0] === '8' && x !== '8V')) { BattleTeambuilderTable['gen8dlc1'].learnsets[id][moveid] += 'g'; } } } const PreDLCLearnsets = Dex.mod('gen9predlc').data.Learnsets; for (const id in PreDLCLearnsets) { const learnset = PreDLCLearnsets[id].learnset; if (!learnset) continue; BattleTeambuilderTable['gen9predlc'].learnsets[id] = {}; for (const moveid in learnset) { const gens = learnset[moveid].map(x => Number(x[0])); const minGen = Math.min(...gens); if (minGen <= 4 && (gen3HMs.has(moveid) || gen4HMs.has(moveid))) { let legalGens = ''; let available = false; if (minGen === 3) { legalGens += '3'; available = true; } if (available) available = !gen3HMs.has(moveid); if (available || gens.includes(4)) { legalGens += '4'; available = true; } if (available) available = !gen4HMs.has(moveid); const minUpperGen = available ? 5 : Math.min( ...gens.filter(gen => gen > 4) ); legalGens += '0123456789'.slice(minUpperGen); BattleTeambuilderTable['gen9predlc'].learnsets[id][moveid] = legalGens; } else { BattleTeambuilderTable['gen9predlc'].learnsets[id][moveid] = '0123456789'.slice(minGen); } if (gens.indexOf(6) >= 0) BattleTeambuilderTable['gen9predlc'].learnsets[id][moveid] += 'p'; if (gens.indexOf(7) >= 0 && learnset[moveid].some(x => x[0] === '7' && x !== '7V')) { BattleTeambuilderTable['gen9predlc'].learnsets[id][moveid] += 'q'; } if (gens.indexOf(8) >= 0 && learnset[moveid].some(x => x[0] === '8' && x !== '8V')) { BattleTeambuilderTable['gen9predlc'].learnsets[id][moveid] += 'g'; } if (gens.indexOf(9) >= 0 && learnset[moveid].some(x => x[0] === '9' && x !== '9V')) { BattleTeambuilderTable['gen9predlc'].learnsets[id][moveid] += 'a'; } } } const SVDLC1Learnsets = Dex.mod('gen9dlc1').data.Learnsets; for (const id in SVDLC1Learnsets) { const learnset = SVDLC1Learnsets[id].learnset; if (!learnset) continue; BattleTeambuilderTable['gen9dlc1'].learnsets[id] = {}; for (const moveid in learnset) { const gens = learnset[moveid].map(x => Number(x[0])); const minGen = Math.min(...gens); if (minGen <= 4 && (gen3HMs.has(moveid) || gen4HMs.has(moveid))) { let legalGens = ''; let available = false; if (minGen === 3) { legalGens += '3'; available = true; } if (available) available = !gen3HMs.has(moveid); if (available || gens.includes(4)) { legalGens += '4'; available = true; } if (available) available = !gen4HMs.has(moveid); const minUpperGen = available ? 5 : Math.min( ...gens.filter(gen => gen > 4) ); legalGens += '0123456789'.slice(minUpperGen); BattleTeambuilderTable['gen9dlc1'].learnsets[id][moveid] = legalGens; } else { BattleTeambuilderTable['gen9dlc1'].learnsets[id][moveid] = '0123456789'.slice(minGen); } if (gens.indexOf(6) >= 0) BattleTeambuilderTable['gen9dlc1'].learnsets[id][moveid] += 'p'; if (gens.indexOf(7) >= 0 && learnset[moveid].some(x => x[0] === '7' && x !== '7V')) { BattleTeambuilderTable['gen9dlc1'].learnsets[id][moveid] += 'q'; } if (gens.indexOf(8) >= 0 && learnset[moveid].some(x => x[0] === '8' && x !== '8V')) { BattleTeambuilderTable['gen9dlc1'].learnsets[id][moveid] += 'g'; } if (gens.indexOf(9) >= 0 && learnset[moveid].some(x => x[0] === '9' && x !== '9V')) { BattleTeambuilderTable['gen9dlc1'].learnsets[id][moveid] += 'a'; } } } const LegendsZALearnsets = Dex.mod('gen9legendsou').data.Learnsets; for (const id in LegendsZALearnsets) { const learnset = LegendsZALearnsets[id].learnset; if (!learnset) continue; BattleTeambuilderTable['gen9legendsou'].learnsets[id] = {}; for (const moveid in learnset) { const gens = learnset[moveid].map(x => Number(x[0])); const minGen = Math.min(...gens); if (minGen <= 4 && (gen3HMs.has(moveid) || gen4HMs.has(moveid))) { let legalGens = ''; let available = false; if (minGen === 3) { legalGens += '3'; available = true; } if (available) available = !gen3HMs.has(moveid); if (available || gens.includes(4)) { legalGens += '4'; available = true; } if (available) available = !gen4HMs.has(moveid); const minUpperGen = available ? 5 : Math.min( ...gens.filter(gen => gen > 4) ); legalGens += '0123456789'.slice(minUpperGen); BattleTeambuilderTable['gen9legendsou'].learnsets[id][moveid] = legalGens; } else { BattleTeambuilderTable['gen9legendsou'].learnsets[id][moveid] = '0123456789'.slice(minGen); } if (gens.indexOf(6) >= 0) BattleTeambuilderTable['gen9legendsou'].learnsets[id][moveid] += 'p'; if (gens.indexOf(7) >= 0 && learnset[moveid].some(x => x[0] === '7' && x !== '7V')) { BattleTeambuilderTable['gen9legendsou'].learnsets[id][moveid] += 'q'; } if (gens.indexOf(8) >= 0 && learnset[moveid].some(x => x[0] === '8' && x !== '8V')) { BattleTeambuilderTable['gen9legendsou'].learnsets[id][moveid] += 'g'; } if (gens.indexOf(9) >= 0 && learnset[moveid].some(x => x[0] === '9' && x !== '9V')) { BattleTeambuilderTable['gen9legendsou'].learnsets[id][moveid] += 'a'; } } } // Client relevant data that should be overridden by past gens and mods const overrideSpeciesKeys = [ 'abilities', 'baseStats', 'cosmeticFormes', 'isNonstandard', 'requiredItems', 'types', 'unreleasedHidden', ]; const overrideMoveKeys = [ 'accuracy', 'basePower', 'category', 'desc', 'flags', 'isNonstandard', 'pp', 'priority', 'shortDesc', 'target', 'type', ]; const overrideAbilityKeys = ['desc', 'flags', 'isNonstandard', 'rating', 'shortDesc']; const overrideItemKeys = ['desc', 'fling', 'isNonstandard', 'naturalGift', 'shortDesc']; // // Past gen table // for (const genNum of [8, 7, 6, 5, 4, 3, 2, 1]) { const gen = 'gen' + genNum; const nextGen = 'gen' + (genNum + 1); const genDex = Dex.mod(gen); const genData = genDex.data; const nextGenDex = Dex.mod(nextGen); const nextGenData = nextGenDex.data; const overrideSpeciesData = {}; BattleTeambuilderTable[gen].overrideSpeciesData = overrideSpeciesData; for (const id in genData.Pokedex) { const curEntry = genDex.species.get(id); if (curEntry.isCosmeticForme) continue; const nextEntry = nextGenDex.species.get(id); for (const key of overrideSpeciesKeys) { if (JSON.stringify(curEntry[key]) !== JSON.stringify(nextEntry[key])) { if (!overrideSpeciesData[id]) overrideSpeciesData[id] = {}; overrideSpeciesData[id][key] = curEntry[key]; } } } const overrideMoveData = {}; BattleTeambuilderTable[gen].overrideMoveData = overrideMoveData; for (const id in genData.Moves) { const curEntry = genDex.moves.get(id); const nextEntry = nextGenDex.moves.get(id); for (const key of overrideMoveKeys) { if (key === 'category' && genNum <= 3) continue; if (JSON.stringify(curEntry[key]) !== JSON.stringify(nextEntry[key])) { if (!overrideMoveData[id]) overrideMoveData[id] = {}; overrideMoveData[id][key] = curEntry[key]; } } } const overrideAbilityData = {}; BattleTeambuilderTable[gen].overrideAbilityData = overrideAbilityData; for (const id in genData.Abilities) { const curEntry = genDex.abilities.get(id); const nextEntry = nextGenDex.abilities.get(id); for (const key of overrideAbilityKeys) { if (JSON.stringify(curEntry[key]) !== JSON.stringify(nextEntry[key])) { if (!overrideAbilityData[id]) overrideAbilityData[id] = {}; overrideAbilityData[id][key] = curEntry[key]; } } } const overrideItemData = {}; BattleTeambuilderTable[gen].overrideItemData = overrideItemData; for (const id in genData.Items) { const curEntry = genDex.items.get(id); const nextEntry = nextGenDex.items.get(id); for (const key of overrideItemKeys) { if (JSON.stringify(curEntry[key]) !== JSON.stringify(nextEntry[key])) { if (!overrideItemData[id]) overrideItemData[id] = {}; overrideItemData[id][key] = curEntry[key]; } } } const overrideTypeChart = {}; BattleTeambuilderTable[gen].overrideTypeChart = overrideTypeChart; const removeType = {}; BattleTeambuilderTable[gen].removeType = removeType; for (const id in nextGenData.TypeChart) { const curEntry = genData.TypeChart[id]; const nextEntry = nextGenData.TypeChart[id]; if (curEntry.isNonstandard) { removeType[id] = true; continue; } if (JSON.stringify(nextEntry) !== JSON.stringify(curEntry)) { overrideTypeChart[id] = curEntry; } } } // // Mods // for (const mod of ['gen3rs', 'gen5bw1', 'gen7letsgo', 'gen8bdsp', 'gen9ssb', 'gen9legendsou']) { const modDex = Dex.mod(mod); const modData = modDex.data; const parentDex = Dex.forGen(modDex.gen); const overrideSpeciesData = {}; BattleTeambuilderTable[mod].overrideSpeciesData = overrideSpeciesData; for (const id in modData.Pokedex) { const modEntry = modDex.species.get(id); const parentEntry = parentDex.species.get(id); for (const key of overrideSpeciesKeys) { if (JSON.stringify(modEntry[key]) !== JSON.stringify(parentEntry[key])) { if (!overrideSpeciesData[id]) overrideSpeciesData[id] = {}; overrideSpeciesData[id][key] = modEntry[key]; } } } const overrideMoveData = {}; BattleTeambuilderTable[mod].overrideMoveData = overrideMoveData; for (const id in modData.Moves) { const modEntry = modDex.moves.get(id); const parentEntry = parentDex.moves.get(id); for (const key of overrideMoveKeys) { if (key === 'category' && modDex.gen <= 3) continue; if (JSON.stringify(modEntry[key]) !== JSON.stringify(parentEntry[key])) { if (!overrideMoveData[id]) overrideMoveData[id] = {}; overrideMoveData[id][key] = modEntry[key]; } } } const overrideAbilityData = {}; BattleTeambuilderTable[mod].overrideAbilityData = overrideAbilityData; for (const id in modData.Abilities) { const modEntry = modDex.abilities.get(id); const parentEntry = parentDex.abilities.get(id); for (const key of overrideAbilityKeys) { if (JSON.stringify(modEntry[key]) !== JSON.stringify(parentEntry[key])) { if (!overrideAbilityData[id]) overrideAbilityData[id] = {}; overrideAbilityData[id][key] = modEntry[key]; } } } const overrideItemData = {}; BattleTeambuilderTable[mod].overrideItemData = overrideItemData; for (const id in modData.Items) { const modEntry = modDex.items.get(id); const parentEntry = parentDex.items.get(id); for (const key of overrideItemKeys) { if (JSON.stringify(modEntry[key]) !== JSON.stringify(parentEntry[key])) { if (!overrideItemData[id]) overrideItemData[id] = {}; overrideItemData[id][key] = modEntry[key]; } } } } buf += `exports.BattleTeambuilderTable = JSON.parse('${JSON.stringify(BattleTeambuilderTable).replace(/['\\]/g, "\\$&")}');\n\n`; fs.writeFileSync('play.pokemonshowdown.com/data/teambuilder-tables.js', buf); } console.log("DONE"); /********************************************************* * Build pokedex.js *********************************************************/ process.stdout.write("Building `data/pokedex.js`... "); { const Pokedex = requireNoCache('../caches/pokemon-showdown/dist/data/pokedex.js').Pokedex; for (const id in Pokedex) { const entry = Pokedex[id]; if (Dex.data.FormatsData[id]) { // console.log('formatsentry:' + id); const formatsEntry = Dex.data.FormatsData[id]; if (formatsEntry.tier) entry.tier = formatsEntry.tier; if (formatsEntry.isNonstandard) entry.isNonstandard = formatsEntry.isNonstandard; if (formatsEntry.unreleasedHidden) entry.unreleasedHidden = formatsEntry.unreleasedHidden; } } const buf = 'exports.BattlePokedex = ' + es3stringify(Pokedex) + ';'; fs.writeFileSync('play.pokemonshowdown.com/data/pokedex.js', buf); fs.writeFileSync('play.pokemonshowdown.com/data/pokedex.json', JSON.stringify(Pokedex)); } console.log("DONE"); /********************************************************* * Build moves.js *********************************************************/ process.stdout.write("Building `data/moves,items,abilities,typechart,learnsets.js`..."); { const Moves = requireNoCache('../caches/pokemon-showdown/dist/data/moves.js').Moves; for (const id in Moves) { const move = Dex.moves.get(Moves[id].name); if (move.desc) Moves[id].desc = move.desc; if (move.shortDesc) Moves[id].shortDesc = move.shortDesc; if (move.basePowerCallback) Moves[id].basePowerCallback = true; } const buf = 'exports.BattleMovedex = ' + es3stringify(Moves) + ';'; fs.writeFileSync('play.pokemonshowdown.com/data/moves.js', buf); fs.writeFileSync('play.pokemonshowdown.com/data/moves.json', JSON.stringify(Moves)); } /********************************************************* * Build items.js *********************************************************/ { const Items = requireNoCache('../caches/pokemon-showdown/dist/data/items.js').Items; for (const id in Items) { const item = Dex.items.get(Items[id].name); if (item.desc) Items[id].desc = item.desc; if (item.shortDesc) Items[id].shortDesc = item.shortDesc; } const buf = 'exports.BattleItems = ' + es3stringify(Items) + ';'; fs.writeFileSync('play.pokemonshowdown.com/data/items.js', buf); } /********************************************************* * Build abilities.js *********************************************************/ { const Abilities = requireNoCache('../caches/pokemon-showdown/dist/data/abilities.js').Abilities; for (const id in Abilities) { const ability = Dex.abilities.get(Abilities[id].name); if (ability.desc) Abilities[id].desc = ability.desc; if (ability.shortDesc) Abilities[id].shortDesc = ability.shortDesc; } const buf = 'exports.BattleAbilities = ' + es3stringify(Abilities) + ';'; fs.writeFileSync('play.pokemonshowdown.com/data/abilities.js', buf); } /********************************************************* * Build typechart.js *********************************************************/ { const TypeChart = requireNoCache('../caches/pokemon-showdown/dist/data/typechart.js').TypeChart; const buf = 'exports.BattleTypeChart = ' + es3stringify(TypeChart) + ';'; fs.writeFileSync('play.pokemonshowdown.com/data/typechart.js', buf); } /********************************************************* * Build aliases.js *********************************************************/ { const Aliases = requireNoCache('../caches/pokemon-showdown/dist/data/aliases.js').Aliases; const buf = 'exports.BattleAliases = ' + es3stringify(Aliases) + ';'; fs.writeFileSync('play.pokemonshowdown.com/data/aliases.js', buf); } /********************************************************* * Build formats-data.js *********************************************************/ { const FormatsData = requireNoCache('../caches/pokemon-showdown/dist/data/formats-data.js').FormatsData; const buf = 'exports.BattleFormatsData = ' + es3stringify(FormatsData) + ';'; fs.writeFileSync('play.pokemonshowdown.com/data/formats-data.js', buf); } /********************************************************* * Build formats.js *********************************************************/ { const Formats = requireNoCache('../caches/pokemon-showdown/dist/config/formats.js').Formats; const buf = 'exports.Formats = ' + es3stringify(Formats) + ';'; fs.writeFileSync('play.pokemonshowdown.com/data/formats.js', buf); } /********************************************************* * Build learnsets.js *********************************************************/ { const Learnsets = requireNoCache('../caches/pokemon-showdown/dist/data/learnsets.js').Learnsets; const buf = 'exports.BattleLearnsets = ' + es3stringify(Learnsets) + ';'; fs.writeFileSync('play.pokemonshowdown.com/data/learnsets.js', buf); fs.writeFileSync('play.pokemonshowdown.com/data/learnsets.json', JSON.stringify(Learnsets)); } /********************************************************* * Build text.js *********************************************************/ { const textData = Dex.loadTextData(); const Text = textData.Default; function assignData(id, entry) { for (const key in entry) { if (['name', 'desc', 'shortDesc'].includes(key)) continue; const textEntry = Text[id] || (Text[id] = {}); if (key.startsWith('gen')) { for (const modKey in entry[key]) { if (['desc', 'shortDesc'].includes(key)) continue; textEntry[modKey + 'Gen' + key.charAt(3)] = entry[key][modKey]; } } else { textEntry[key] = entry[key]; } } } for (const id in textData.Moves) assignData(id, textData.Moves[id]); for (const id in textData.Abilities) assignData(id, textData.Abilities[id]); for (const id in textData.Items) assignData(id, textData.Items[id]); const buf = 'exports.BattleText = ' + es3stringify(Text) + ';'; fs.writeFileSync('play.pokemonshowdown.com/data/text.js', buf); } console.log("DONE");