Teambuilder: Update popular items for gen 9 & add doubles support (#2321)

* Teambuilder: Customize Metronome Battle's popular items

* Teambuilder: Update popular items for gen 9 & add doubles support

* Fixes

* Update play.pokemonshowdown.com/src/battle-dex-search.ts

* Apply suggestions from code review

* Update play.pokemonshowdown.com/src/battle-dex-search.ts

* Apply suggestions from code review

* Apply suggestions from code review

* Apply suggestions from code review

---------

Co-authored-by: Kris Johnson <11083252+KrisXV@users.noreply.github.com>
This commit is contained in:
pyuk-bot 2025-02-26 14:40:49 -06:00 committed by GitHub
parent 73823ab203
commit ec92955748
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 91 additions and 34 deletions

View File

@ -560,6 +560,7 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
} else if (isDoubles) {
BattleTeambuilderTable[gen + 'doubles'] = {};
BattleTeambuilderTable[gen + 'doubles'].tiers = tiers;
BattleTeambuilderTable[gen + 'doubles'].items = items;
BattleTeambuilderTable[gen + 'doubles'].overrideTier = overrideTier;
BattleTeambuilderTable[gen + 'doubles'].formatSlices = formatSlices;
} else if (isGen9BH) {
@ -685,23 +686,55 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
if (banlist.isBanned('item:' + item.id)) continue;
}
switch (id) {
case 'leftovers':
case 'lifeorb':
// mainstays
case 'choiceband':
case 'choicescarf':
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':
greatItems.push(id);
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 (genNum > 4) greatItems.push(id);
if (isMetBattle) goodItems.push(id);
else if (genNum > 4) greatItems.push(id);
else poorItems.push(id);
break;
case 'lumberry':
@ -709,8 +742,8 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
else greatItems.push(id);
break;
case 'sitrusberry':
if (genNum > 6) goodItems.push(id);
else if (genNum > 3 && genNum < 7) greatItems.push(id);
if (genNum > 3 && (genNum < 7 || isDoubles)) greatItems.push(id);
else if (genNum > 6) goodItems.push(id);
else poorItems.push(id);
break;
case 'aguavberry':
@ -718,7 +751,8 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
case 'iapapaberry':
case 'magoberry':
case 'wikiberry':
if (genNum >= 7) greatItems.push(id);
if (genNum === 7) greatItems.push(id);
else if (genNum >= 8) goodItems.push(id);
else poorItems.push(id);
break;
case 'berryjuice':
@ -744,8 +778,8 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
case 'blueorb':
case 'redorb':
case 'souldew':
// falls through
// Other
// fallsthrough
case 'stick':
case 'thickclub':
case 'lightball':

View File

@ -1496,6 +1496,7 @@ export class Species implements Effect {
readonly evoMove: string;
readonly evoItem: string;
readonly evoCondition: string;
readonly nfe: boolean;
readonly requiredItems: readonly string[];
readonly tier: string;
readonly isTotem: boolean;
@ -1550,6 +1551,7 @@ export class Species implements Effect {
this.evoMove = data.evoMove || '';
this.evoItem = data.evoItem || '';
this.evoCondition = data.evoCondition || '';
this.nfe = data.nfe || false;
this.requiredItems = data.requiredItems || (data.requiredItem ? [data.requiredItem] : []);
this.tier = data.tier || '';

View File

@ -729,6 +729,9 @@ abstract class BattleTypedSearch<T extends SearchType> {
results = [...this.baseResults];
illegalResults = null;
}
if (this.defaultFilter) {
results = this.defaultFilter(results);
}
if (sortCol) {
results = results.filter(([rowType]) => rowType === this.searchType);
@ -888,6 +891,7 @@ abstract class BattleTypedSearch<T extends SearchType> {
abstract getDefaultResults(): SearchRow[];
abstract getBaseResults(): SearchRow[];
abstract filter(input: SearchRow, filters: string[][]): boolean;
defaultFilter?(input: SearchRow[]): SearchRow[];
abstract sort(input: SearchRow[], sortCol: string, reverseSort?: boolean): SearchRow[];
}
@ -1279,6 +1283,8 @@ class BattleItemSearch extends BattleTypedSearch<'item'> {
table = table['gen5bw1'];
} else if (this.formatType === 'natdex') {
table = table[`gen${this.dex.gen}natdex`];
} else if (this.formatType?.endsWith('doubles')) { // no natdex/bdsp doubles support
table = table[`gen${this.dex.gen}doubles`];
} else if (this.formatType === 'metronome') {
table = table[`gen${this.dex.gen}metronome`];
} else if (this.dex.gen < 9) {
@ -1300,32 +1306,42 @@ class BattleItemSearch extends BattleTypedSearch<'item'> {
const speciesName = this.dex.species.get(this.species).name;
const results = this.getDefaultResults();
const speciesSpecific: SearchRow[] = [];
const abilitySpecific: SearchRow[] = [];
const abilityItem = {
protosynthesis: 'boosterenergy',
quarkdrive: 'boosterenegy',
// poisonheal: 'toxicorb',
// toxicboost: 'toxicorb',
// flareboost: 'flameorb',
}[toID(this.set?.ability) as string];
for (const row of results) {
if (row[0] !== 'item') continue;
if (this.dex.items.get(row[1]).itemUser?.includes(speciesName)) {
speciesSpecific.push(row);
}
const item = this.dex.items.get(row[1]);
if (item.itemUser?.includes(speciesName)) speciesSpecific.push(row);
if (abilityItem === item.id) abilitySpecific.push(row);
}
if (speciesSpecific.length) {
return [
results.unshift(
['header', "Specific to " + speciesName],
...speciesSpecific,
...results,
];
...speciesSpecific
);
}
if (abilitySpecific.length) {
results.unshift(
['header', `Specific to ${this.set!.ability!}`],
...abilitySpecific
);
}
return results;
}
override defaultFilter(results: SearchRow[]) {
if (this.species && !this.dex.species.get(this.species).nfe) {
results.splice(results.findIndex(row => row[1] === 'eviolite'), 1);
return results;
}
return results;
}
filter(row: SearchRow, filters: string[][]) {
if (!filters) return true;
if (row[0] !== 'ability') return true;
const ability = this.dex.abilities.get(row[1]);
for (const [filterType, value] of filters) {
switch (filterType) {
case 'pokemon':
if (!Dex.hasAbility(this.dex.species.get(value), ability.name)) return false;
break;
}
}
return true;
}
sort(results: SearchRow[], sortCol: string | null, reverseSort?: boolean): SearchRow[] {

View File

@ -471,6 +471,12 @@ export const Dex = new class implements ModdedDex {
if (!data.tier && data.baseSpecies && toID(data.baseSpecies) !== id) {
data.tier = this.species.get(data.baseSpecies).tier;
}
data.nfe = data.id === 'dipplin' || !!(data as Species).evos?.some(evo => {
const evoSpecies = this.species.get(evo);
return !evoSpecies.isNonstandard || evoSpecies.isNonstandard === data.isNonstandard ||
// Pokemon with Hisui evolutions
evoSpecies.isNonstandard === "Unobtainable";
});
species = new Species(id, name, data);
window.BattlePokedex[id] = species;
}
@ -1064,6 +1070,12 @@ export class ModdedDex {
data.tier = this.species.get(data.baseSpecies).tier;
}
if (data.gen > this.gen) data.tier = 'Illegal';
data.nfe = data.id === 'dipplin' || !!data.evos?.some(evo => {
const evoSpecies = this.species.get(evo);
return !evoSpecies.isNonstandard || evoSpecies.isNonstandard === data.isNonstandard ||
// Pokemon with Hisui evolutions
evoSpecies.isNonstandard === "Unobtainable";
});
const species = new Species(id, name, data);
this.cache.Species[id] = species;

View File

@ -1192,14 +1192,7 @@ export class BattleTooltips {
speedModifiers.push(1.5);
}
}
const isNFE = this.battle.dex.species.get(serverPokemon.speciesForme).evos?.some(evo => {
const evoSpecies = this.battle.dex.species.get(evo);
return !evoSpecies.isNonstandard ||
evoSpecies.isNonstandard === this.battle.dex.species.get(serverPokemon.speciesForme)?.isNonstandard ||
// Pokemon with Hisui evolutions
evoSpecies.isNonstandard === "Unobtainable";
});
if (item === 'eviolite' && (isNFE || this.battle.dex.species.get(serverPokemon.speciesForme).id === 'dipplin')) {
if (item === 'eviolite' && this.battle.dex.species.get(serverPokemon.speciesForme).nfe) {
stats.def = Math.floor(stats.def * 1.5);
stats.spd = Math.floor(stats.spd * 1.5);
}