diff --git a/.eslintrc.js b/.eslintrc.js index 2f83eb3845..9c820e10ce 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,10 +19,10 @@ module.exports = { "BattleAbilities": false, "BattleAliases": false, "BattleBackdrops": false, "BattleBackdropsFour": false, "BattleBackdropsThree": false, "BattleEffects": false, "BattleFormats": false, "BattleFormatsData": false, "BattleLearnsets": false, "BattleItems": false, "BattleMoveAnims": false, "BattleMovedex": false, "BattleNatures": false, "BattleOtherAnims": false, "BattlePokedex": false,"BattlePokemonSprites": false, "BattlePokemonSpritesBW": false, "BattleSearchCountIndex": false, "BattleSearchIndex": false, - "BattleSearchIndexOffset": false, "BattleSearchIndexType": false, "BattleStatIDs": false, "BattleStatNames": false, "BattleStats": false, "BattleStatusAnims": false, "BattleStatuses": false, + "BattleSearchIndexOffset": false, "BattleSearchIndexType": false, "BattleStatIDs": false, "BattleStatNames": false, "BattleStats": false, "BattleStatusAnims": false, "BattleStatuses": false, "BattleTeambuilderTable": false, // Generic global variables - "Config": false, "Chart": false, "soundManager": false, "Storage": false, "Tools": false, + "Config": false, "BattleSearch": false, "soundManager": false, "Storage": false, "Tools": false, "app": false, "toId": false, "toRoomid": false, "toUserid": false, "toName": false, "hashColor": false, "MD5": false, "ChatHistory": false, "Topbar": false, "UserList": false, diff --git a/githooks/build-indexes b/githooks/build-indexes index c90318ced2..1f4bf185c0 100755 --- a/githooks/build-indexes +++ b/githooks/build-indexes @@ -103,3 +103,51 @@ const fs = require("fs"); } console.log("DONE"); + +/********************************************************* + * Build teambuilder-tables.js + *********************************************************/ + +process.stdout.write("Building `data/teambuilder-tables.js`... "); + +{ + const BattleTeambuilderTable = {}; + + let buf = '// automatically built with githooks/build-indexes\n\n'; + + const pokemon = Object.keys(Tools.data.Pokedex); + pokemon.sort(); + const tierTable = {}; + for (const id of pokemon) { + const tier = Tools.getTemplate(id).tier; + + if (!tierTable[tier]) tierTable[tier] = []; + tierTable[tier].push(id); + } + + const tiers = BattleTeambuilderTable.tiers = []; + const formatSlices = BattleTeambuilderTable.formatSlices = {}; + + const tierOrder = ["Uber", "OU", "BL", "(OU)", "UU", "BL2", "RU", "BL3", "NU", "BL4", "PU", "NFE", "LC Uber", "LC"]; + + for (const tier of tierOrder) { + if (tier === "OU" || tier === "Uber" || tier === "UU" || tier === "RU" || tier === "NU" || tier === "PU" || tier === "LC") { + formatSlices[tier] = tiers.length; + } + if (!tierTable[tier]) continue; + 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]); + } + + buf += 'exports.BattleTeambuilderTable = ' + JSON.stringify(BattleTeambuilderTable) + ';\n\n'; + + fs.writeFileSync('data/teambuilder-tables.js', buf); +} + +console.log("DONE"); diff --git a/index.template.html b/index.template.html index 5667b90968..da9269fc27 100644 --- a/index.template.html +++ b/index.template.html @@ -164,7 +164,9 @@

'); + this.el.innerHTML = '' + (this.renderingDone || qType ? '' : ''); return true; }; - Search.prototype.addFilter = function (name, result) { + Search.prototype.addFilter = function (node) { + if (!node.dataset.entry) return; + var entry = node.dataset.entry.split(':'); if (this.qType === 'pokemon') { - result = result.split('/'); - if (result[0] !== 'types' && result[0] !== 'moves' && result[0] !== 'abilities' && result[0] !== 'egggroups') return; - if (result[0] === 'types') result[1] = name; - if (result[0] === 'abilities') result[1] = name; - if (result[0] === 'egggroups') result[1] = name; + if (entry[0] !== 'type' && entry[0] !== 'move' && entry[0] !== 'ability' && entry[0] !== 'egggroup') return; + if (entry[0] === 'move') entry[1] = toId(entry[1]); if (!this.filters) this.filters = []; this.q = undefined; for (var i = 0; i < this.filters.length; i++) { - if (this.filters[i][0] === result[0] && this.filters[i][1] === result[1]) { + if (this.filters[i][0] === entry[0] && this.filters[i][1] === entry[1]) { return true; } } - this.filters.push(result); + this.filters.push(entry); return true; - } else if (this.qType === 'moves') { - result = result.split('/'); - if (result[0] !== 'types' && result[0] !== 'categories' && result[0] !== 'pokemon') return; - if (result[0] === 'types') result[1] = name; - if (result[0] === 'categories') result[1] = name; + } else if (this.qType === 'move') { + if (entry[0] !== 'type' && entry[0] !== 'category' && entry[0] !== 'pokemon') return; + if (entry[0] === 'pokemon') entry[1] = toId(entry[1]); if (!this.filters) this.filters = []; - this.filters.push(result); + this.filters.push(entry); this.q = undefined; return true; } @@ -300,34 +329,35 @@ this.updateScroll(); }; Search.prototype.getFilterText = function (q) { - var buf = 'Filters: '; + var buf = '

Filters: '; for (var i = 0; i < this.filters.length; i++) { - if (i) buf += ', '; var text = this.filters[i][1]; - if (this.filters[i][0] === 'moves') text = Tools.getMove(text).name; + if (this.filters[i][0] === 'move') text = Tools.getMove(text).name; if (this.filters[i][0] === 'pokemon') text = Tools.getTemplate(text).name; - buf += '' + text + ''; + buf += ' '; } - if (!q) buf += ' (backspace = delete filter)'; - return buf; + if (!q) buf += '(backspace = delete filter)'; + return buf + '

'; }; Search.prototype.filteredPokemon = function () { var resultSet = [['html', this.getFilterText(), 0]]; resultSet.push(['header', "Filtered results", 0]); + var illegalResultSet = []; var filters = this.filters; for (var id in BattlePokedex) { var template = BattlePokedex[id]; + if (template.exists === false) continue; for (var i = 0; i < filters.length; i++) { - if (filters[i][0] === 'types') { + if (filters[i][0] === 'type') { var type = filters[i][1]; if (template.types[0] !== type && template.types[1] !== type) break; - } else if (filters[i][0] === 'egggroups') { + } else if (filters[i][0] === 'egggroup') { var egggroup = filters[i][1]; if (template.eggGroups[0] !== egggroup && template.eggGroups[1] !== egggroup) break; - } else if (filters[i][0] === 'abilities') { + } else if (filters[i][0] === 'ability') { var ability = filters[i][1]; if (template.abilities['0'] !== ability && template.abilities['1'] !== ability && template.abilities['H'] !== ability) break; - } else if (filters[i][0] === 'moves') { + } else if (filters[i][0] === 'move') { var learned = false; var learnsetid = id; while (true) { @@ -345,9 +375,13 @@ } } if (i < filters.length) continue; - resultSet.push(['pokemon', id, 0]); + if (this.legalityFilter && !(id in this.legalityFilter)) { + illegalResultSet.push(['pokemon', id, -1]); + } else { + resultSet.push(['pokemon', id, 0]); + } } - this.resultSet = resultSet; + this.resultSet = resultSet.concat(illegalResultSet); this.renderedIndex = 0; this.renderingDone = false; this.updateScroll(); @@ -355,13 +389,15 @@ Search.prototype.filteredMoves = function () { var resultSet = [['html', this.getFilterText(), 0]]; resultSet.push(['header', "Filtered results", 0]); + var illegalResultSet = []; var filters = this.filters; for (var id in BattleMovedex) { var move = BattleMovedex[id]; + if (move.exists === false) continue; for (var i = 0; i < filters.length; i++) { - if (filters[i][0] === 'types') { + if (filters[i][0] === 'type') { if (move.type !== filters[i][1]) break; - } else if (filters[i][0] === 'categories') { + } else if (filters[i][0] === 'category') { if (move.category !== filters[i][1]) break; } else if (filters[i][0] === 'pokemon') { var learned = false; @@ -381,9 +417,13 @@ } } if (i < filters.length) continue; - resultSet.push(['move', id, 0]); + if (this.legalityFilter && !(id in this.legalityFilter)) { + illegalResultSet.push(['move', id, -1]); + } else { + resultSet.push(['move', id, 0]); + } } - this.resultSet = resultSet; + this.resultSet = resultSet.concat(illegalResultSet); this.renderedIndex = 0; this.renderingDone = false; this.updateScroll(); @@ -397,29 +437,142 @@ if (forceAdd && finalIndex < i + 20) finalIndex = i + 20; if (finalIndex <= i) return; + var resultSet = this.resultSet || this.defaultResultSet; var buf = ''; while (i < finalIndex) { - if (!this.resultSet[i]) { + if (!resultSet[i]) { this.renderingDone = true; break; } - var row = this.resultSet[i]; + var row = resultSet[i]; + + var errorMessage = ''; + if (row[2] < 0) { + errorMessage = '' + this.legalityLabel + ''; + row[2] = 0; + } - buf += Search.renderRow(row[1], row[0], 0, row[2]); + buf += Search.renderRow(row[1], row[0], 0, row[2], errorMessage, row[1] in this.cur ? ' class="cur"' : ''); i++; } if (!this.renderedIndex) { - this.el.innerHTML = ''; - // (this.renderingDone ? '' : ''); + this.el.innerHTML = ''; } else { $(this.el.firstChild).append(buf); - // if (this.renderingDone) { - // this.$el.find('li.more').parent().remove(); - // } } this.renderedIndex = i; }; + Search.prototype.setType = function (qType, format, set, cur) { + if (!format) format = ''; + if (this.qType !== qType) this.filters = null; + this.qType = qType; + this.q = undefined; + this.cur = cur || {}; + this.legalityFilter = {}; + this.legalityLabel = "Illegal"; + var gen = 6; + if (format.slice(0, 3) === 'gen') gen = (Number(format.charAt(3)) || 6); + var requirePentagon = (format === 'vgc2016'); + var template; + this.resultSet = null; + this.defaultResultSet = null; + + switch (qType) { + case 'pokemon': + if (!BattleTeambuilderTable.tierSet) { + BattleTeambuilderTable.tierSet = BattleTeambuilderTable.tiers.map(function (r) { + if (typeof r === 'string') return ['pokemon', r, 0]; + return [r[0], r[1], 0]; + }); + BattleTeambuilderTable.tiers = null; + } + var tierSet = BattleTeambuilderTable.tierSet; + if (format === 'ou') tierSet = tierSet.slice(BattleTeambuilderTable.formatSlices.OU); + if (format === 'uu') tierSet = tierSet.slice(BattleTeambuilderTable.formatSlices.UU); + if (format === 'ru') tierSet = tierSet.slice(BattleTeambuilderTable.formatSlices.RU); + if (format === 'nu') tierSet = tierSet.slice(BattleTeambuilderTable.formatSlices.NU); + if (format === 'lc') tierSet = tierSet.slice(BattleTeambuilderTable.formatSlices.LC); + this.defaultResultSet = tierSet; + this.legalityLabel = "Banned"; + break; + + case 'item': + var itemSet = [['header', "Items", 0]]; + for (var id in BattleItems) { + itemSet.push(['item', id, 0]); + } + this.defaultResultSet = itemSet; + this.legalityFilter = null; + break; + + case 'ability': + template = Tools.getTemplate(set.species); + var abilitySet = [['header', "Abilities", 0]]; + if (template.isMega) { + abilitySet.unshift(['html', '

Will be ' + Tools.escapeHTML(template.abilities['0']) + ' after Mega Evolving.

']); + template = Tools.getTemplate(template.baseSpecies); + } + abilitySet.push(['ability', toId(template.abilities['0']), 0]); + if (template.abilities['1']) { + abilitySet.push(['ability', toId(template.abilities['1']), 0]); + } + if (template.abilities['H']) { + abilitySet.push(['header', "Hidden Ability", 0]); + abilitySet.push(['ability', toId(template.abilities['H']), 0]); + } + this.defaultResultSet = abilitySet; + break; + + case 'move': + template = Tools.getTemplate(set.species); + var moves = []; + while (true) { + var learnsetTemplate = BattleLearnsets[toId(template.baseSpecies)]; + if (learnsetTemplate) { + for (var l in learnsetTemplate.learnset) { + if (requirePentagon && !learnsetTemplate.learnset[l].length) continue; + if (moves.indexOf(l) >= 0) continue; + moves.push(l); + if (l === 'hiddenpower') { + moves.push('hiddenpowerbug', 'hiddenpowerdark', 'hiddenpowerdragon', 'hiddenpowerelectric', 'hiddenpowerfighting', 'hiddenpowerfire', 'hiddenpowerflying', 'hiddenpowerghost', 'hiddenpowergrass', 'hiddenpowerground', 'hiddenpowerice', 'hiddenpowerpoison', 'hiddenpowerpsychic', 'hiddenpowerrock', 'hiddenpowersteel', 'hiddenpowerwater'); + } + } + } + if (template.baseSpecies && !learnsetTemplate) template = BattlePokedex[toId(template.baseSpecies)]; + else if (template.prevo) template = BattlePokedex[template.prevo]; + else break; + } + moves.sort(); + + var usableMoves = []; + var uselessMoves = []; + for (var i = 0; i < moves.length; i++) { + var id = moves[i]; + if (BattleMovedex[id] && BattleMovedex[id].isViable) { + if (!usableMoves.length) usableMoves.push(['header', "Moves", 0]); + usableMoves.push(['move', id, 0]); + } else { + if (!uselessMoves.length) uselessMoves.push(['header', "Usually useless moves", 0]); + uselessMoves.push(['move', id, 0]); + } + } + this.defaultResultSet = usableMoves.concat(uselessMoves); + break; + } + + if (this.legalityFilter) { + for (var i = 0; i < this.defaultResultSet.length; i++) { + if (this.defaultResultSet[i][0] !== 'header') { + this.legalityFilter[this.defaultResultSet[i][1]] = 1; + } + } + } + + this.renderedIndex = 0; + this.renderingDone = false; + this.find(''); + }; Search.getClosest = function (query) { // binary search through the index! @@ -439,13 +592,13 @@ return left; }; - // - // Rendering functions - // +/********************************************************* + * Rendering functions + *********************************************************/ + // These are all static! - // - Search.renderRow = function (id, type, matchStart, matchLength, errorMessage) { + Search.renderRow = function (id, type, matchStart, matchLength, errorMessage, attrs) { // errorMessage = '' + errorMessage + ''; switch (type) { case 'html': @@ -454,16 +607,16 @@ return '
  • ' + id + '

  • '; case 'pokemon': var pokemon = BattlePokedex[id]; - return Search.renderPokemonRow(pokemon, matchStart, matchLength, errorMessage); + return Search.renderPokemonRow(pokemon, matchStart, matchLength, errorMessage, attrs); case 'move': var move = BattleMovedex[id]; - return Search.renderMoveRow(move, matchStart, matchLength, errorMessage); + return Search.renderMoveRow(move, matchStart, matchLength, errorMessage, attrs); case 'item': var item = BattleItems[id]; - return Search.renderItemRow(item, matchStart, matchLength, errorMessage); + return Search.renderItemRow(item, matchStart, matchLength, errorMessage, attrs); case 'ability': var ability = BattleAbilities[id]; - return Search.renderAbilityRow(ability, matchStart, matchLength, errorMessage); + return Search.renderAbilityRow(ability, matchStart, matchLength, errorMessage, attrs); case 'type': var type = {name: id[0].toUpperCase() + id.substr(1)}; return Search.renderTypeRow(type, matchStart, matchLength, errorMessage); @@ -487,10 +640,11 @@ } return 'Error: not found'; }; - Search.renderPokemonRow = function (pokemon, matchStart, matchLength, errorMessage) { - var attrs = ''; - if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'pokemon/' + toId(pokemon.species) + '" data-target="push"'; - var buf = '
  • '; + Search.renderPokemonRow = function (pokemon, matchStart, matchLength, errorMessage, attrs) { + if (!attrs) attrs = ''; + var id = toId(pokemon.species); + if (Search.urlRoot) attrs += ' href="' + Search.urlRoot + 'pokemon/' + id + '" data-target="push"'; + var buf = '
  • '; // number buf += '' + (pokemon.num >= 0 ? pokemon.num : 'CAP') + ' '; @@ -545,7 +699,7 @@ if (!ability) continue; if (i === '1') buf += '
    '; - if (i === 'H') ability = '' + pokemon.abilities[i] + ''; + if (i === 'H') ability = '' + pokemon.abilities[i] + ''; buf += ability; } if (!pokemon.abilities['H']) buf += ''; @@ -572,7 +726,7 @@ Search.renderTaggedPokemonRowInner = function (pokemon, tag, errorMessage) { var attrs = ''; if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'pokemon/' + toId(pokemon.species) + '" data-target="push"'; - var buf = ''; + var buf = ''; // tag buf += '' + tag + ' '; @@ -638,10 +792,11 @@ return buf; }; - Search.renderItemRow = function (item, matchStart, matchLength, errorMessage) { - var attrs = ''; - if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'items/' + toId(item.name) + '" data-target="push"'; - var buf = '
  • '; + Search.renderItemRow = function (item, matchStart, matchLength, errorMessage, attrs) { + if (!attrs) attrs = ''; + var id = toId(item.name); + if (Search.urlRoot) attrs += ' href="' + Search.urlRoot + 'items/' + id + '" data-target="push"'; + var buf = '
  • '; // icon buf += ''; @@ -668,10 +823,11 @@ return buf; }; - Search.renderAbilityRow = function (ability, matchStart, matchLength, errorMessage) { - var attrs = ''; - if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'abilities/' + toId(ability.name) + '" data-target="push"'; - var buf = '
  • '; + Search.renderAbilityRow = function (ability, matchStart, matchLength, errorMessage, attrs) { + if (!attrs) attrs = ''; + var id = toId(ability.name); + if (Search.urlRoot) attrs += ' href="' + Search.urlRoot + 'abilities/' + id + '" data-target="push"'; + var buf = '
  • '; // name var name = ability.name; @@ -692,10 +848,11 @@ return buf; }; - Search.renderMoveRow = function (move, matchStart, matchLength, errorMessage) { - var attrs = ''; - if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'moves/' + toId(move.name) + '" data-target="push"'; - var buf = '
  • '; + Search.renderMoveRow = function (move, matchStart, matchLength, errorMessage, attrs) { + if (!attrs) attrs = ''; + var id = toId(move.name); + if (Search.urlRoot) attrs += ' href="' + Search.urlRoot + 'moves/' + id + '" data-target="push"'; + var buf = '
  • '; // name var name = move.name; @@ -744,7 +901,7 @@ Search.renderMoveRowInner = function (move, errorMessage) { var attrs = ''; if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'moves/' + toId(move.name) + '" data-target="push"'; - var buf = ''; + var buf = ''; // name var name = move.name; @@ -779,7 +936,7 @@ Search.renderTaggedMoveRow = function (move, tag, errorMessage) { var attrs = ''; if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'moves/' + toId(move.name) + '" data-target="push"'; - var buf = '
  • '; + var buf = '
  • '; // tag buf += '' + tag + ' '; @@ -817,7 +974,7 @@ Search.renderTypeRow = function (type, matchStart, matchLength, errorMessage) { var attrs = ''; if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'types/' + toId(type.name) + '" data-target="push"'; - var buf = '
  • '; + var buf = '
  • '; // name var name = type.name; @@ -844,7 +1001,7 @@ Search.renderCategoryRow = function (category, matchStart, matchLength, errorMessage) { var attrs = ''; if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'categories/' + toId(category.name) + '" data-target="push"'; - var buf = '
  • '; + var buf = '
  • '; // name var name = category.name; @@ -871,7 +1028,7 @@ Search.renderEggGroupRow = function (egggroup, matchStart, matchLength, errorMessage) { var attrs = ''; if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'egggroups/' + toId(egggroup.name) + '" data-target="push"'; - var buf = '
  • '; + var buf = '
  • '; // name var name = egggroup.name; diff --git a/style/client.css b/style/client.css index db722ecd68..265951e0cb 100644 --- a/style/client.css +++ b/style/client.css @@ -2726,12 +2726,13 @@ a.ilink.yours { border-radius: 4px; cursor: pointer; } -.teambuilder-results .result a:hover { +.teambuilder-results .result a:hover, +.teambuilder-results .result a.hover { border-color: #D8D8D8; background: #F8F8F8; } -.teambuilder-results .firstresult a, -.teambuilder-results .firstresult a:hover { +.teambuilder-results .result a.cur:hover, +.teambuilder-results .result a.cur.hover { border-color: #CCCCCC; background: #F2F2F2; } diff --git a/style/utilichart.css b/style/utilichart.css index a1a55ebb86..27cbfb92fc 100644 --- a/style/utilichart.css +++ b/style/utilichart.css @@ -15,12 +15,42 @@ margin: 0 5px 1px 5px; text-decoration: none; } +.utilichart li > a.cur { + border-color: #CCCCCC; + background: #F2F2F2; +} +.utilichart .filter { + padding:1px 3px; + border-radius: 3px; + border: 1px solid #777; + background: #EEE; + font-size: 9pt; + font-family: Verdana, sans-serif; + font-weight: bold; + cursor: pointer; +} +.utilichart .filter:hover { + color: #777777; + text-decoration: line-through; +} +.utilichart .filter i { + color: #999999; +} +.utilichart .filter:hover i { + color: #BB2222; +} .resultheader, .utilichart li.resultheader, .utilichart .result { height: 32px; padding: 1px 0 0 0; } +.result p, +.resultheader p, +.utilichart .result p { + padding: 7px 0 0 8px; + margin: 0; +} .utilichart h3, .dexentry h3, .resultheader h3 { @@ -185,6 +215,13 @@ width: 90px; text-align: center; } +.utilichart .hiddenabilitycol { + font-style: italic; +} +.utilichart .unreleasedhiddenabilitycol { + font-style: italic; + text-decoration: line-through; +} .utilichart b { color: #4488CC; text-decoration: underline; diff --git a/testclient.html b/testclient.html index e7b1628114..ec64db6bb9 100644 --- a/testclient.html +++ b/testclient.html @@ -116,7 +116,9 @@