diff --git a/play.pokemonshowdown.com/src/battle-dex-data.ts b/play.pokemonshowdown.com/src/battle-dex-data.ts index 69c8f5e16..39d8eeebf 100644 --- a/play.pokemonshowdown.com/src/battle-dex-data.ts +++ b/play.pokemonshowdown.com/src/battle-dex-data.ts @@ -21,6 +21,89 @@ import { Dex, toID } from "./battle-dex"; */ export type ID = string & { __isID: true }; +export interface FormatData { + id: ID; + name: string; + team?: 'preset' | null; + section: string; + column: number; + searchShow?: boolean; + challengeShow?: boolean; + tournamentShow?: boolean; + bestOfDefault?: boolean; + teraPreviewDefault?: boolean; + itemClauseDefault?: boolean; + rated: boolean; + teambuilderLevel?: number | null; + partner?: boolean; + teambuilderFormat?: ID; + battleFormat?: string; + isTeambuilderFormat: boolean; + effectType: 'Format'; +} + +export class Format implements FormatData { + id: ID; + name: string; + readonly effectType = 'Format'; + gen: number; + mod: ID; + team: 'preset' | null = null; + section = ''; + column = 0; + searchShow?: boolean = undefined; + challengeShow?: boolean = undefined; + tournamentShow?: boolean = undefined; + bestOfDefault?: boolean = undefined; + teraPreviewDefault?: boolean = undefined; + itemClauseDefault?: boolean = undefined; + rated = false; + teambuilderLevel?: number | null = undefined; + partner?: boolean = undefined; + teambuilderFormat?: ID = undefined; + battleFormat?: string = undefined; + isTeambuilderFormat = false; + isLetsGo: boolean; + isNatDex: boolean; + isBDSP: boolean; + isChampions: boolean; + allowMultipleMegas: boolean; + formeLegality: 'normal' | 'hackmons' | 'custom' = 'normal'; + abilityLegality: 'normal' | 'hackmons' = 'normal'; + defaultLevel = 100; + + constructor(name: string, data?: FormatData) { + this.id = toID(name); + this.name = name; + const id = this.id; + this.gen = !id ? Dex.gen : id.startsWith('gen') ? parseInt(id.charAt(3)) || Dex.gen : 6; + this.isLetsGo = id.includes('letsgo'); + this.isNatDex = id.includes('nationaldex') || id.includes('natdex'); + this.isBDSP = id.includes('bdsp'); + this.isChampions = id.includes('champions'); + this.allowMultipleMegas = id.includes('mega'); + this.mod = `gen${this.gen}` as ID; + if (this.gen === 7 && this.isLetsGo) this.mod = 'gen7letsgo' as ID; + if (this.gen === 8 && this.isBDSP) this.mod = 'gen8bdsp' as ID; + if (this.gen === 9 && this.isChampions) this.mod = 'champions' as ID; + if (id.includes('almostanyability') || id.includes('aaa')) this.abilityLegality = 'hackmons'; + if (id.includes('hackmons') || id.includes('bh')) { + this.formeLegality = 'hackmons'; + this.abilityLegality = 'hackmons'; + } else if (id.includes('metronome') || id.includes('customgame')) { + this.formeLegality = 'custom'; + this.abilityLegality = 'hackmons'; + } + if ( + id.includes('vgc') || id.includes('bss') || id.includes('ultrasinnohclassic') || + id.includes('battlespot') || id.includes('battlestadium') || id.includes('battlefestival') || + this.isLetsGo || this.isChampions + ) this.defaultLevel = 50; + if (id.includes('lc')) this.defaultLevel = 5; + if (data) Object.assign(this, data); + } +} + export interface Nature { plus?: StatNameExceptHP; minus?: StatNameExceptHP; @@ -1700,4 +1783,5 @@ if (typeof require === 'function') { global.Ability = Ability; global.Item = Item; global.Move = Move; + global.Format = Format; } diff --git a/play.pokemonshowdown.com/src/battle-dex.ts b/play.pokemonshowdown.com/src/battle-dex.ts index abe1ff147..9f861546a 100644 --- a/play.pokemonshowdown.com/src/battle-dex.ts +++ b/play.pokemonshowdown.com/src/battle-dex.ts @@ -21,7 +21,7 @@ import { Pokemon, type ServerPokemon } from "./battle"; import { BattleAvatarNumbers, BattleBaseSpeciesChart, BattlePokemonIconIndexes, BattlePokemonIconIndexesLeft, - Ability, Item, Move, Species, PureEffect, type ID, type NatureEffect, type Type, + Ability, Item, Move, Species, PureEffect, Format, type FormatData, type ID, type NatureEffect, type Type, } from "./battle-dex-data"; import type * as DexData from "./battle-dex-data"; import type { Teams } from "./battle-teams"; @@ -34,6 +34,7 @@ export declare namespace Dex { export type Item = DexData.Item; export type Move = DexData.Move; export type Species = DexData.Species; + export type Format = DexData.Format; export type Type = DexData.Type; export type Nature = DexData.Nature; export type PureEffect = DexData.PureEffect; @@ -513,26 +514,31 @@ export const Dex = new class implements ModdedDex { if (!gen) return this; return this.mod(`gen${gen}` as ID); } + formats = { + cache: Object.create(null) as { [id: string]: Format }, + get(name: string): Format { + const id = toID(name); + this.cache[id] ||= new Format(name, window.BattleFormats?.[id]); + return this.cache[id]; + }, + load(data: { [id: string]: FormatData }): { [id: string]: Format } { + // Update in-place so retained references get updated too + for (const id in this.cache) { + Object.assign(this.cache[id], new Format(this.cache[id].name, data[id])); + } + const formats: { [id: string]: Format } = {}; + for (const id in data) { + const format = this.cache[id] ||= new Format(data[id].name, data[id]); + formats[id] = format; + } + return formats; + }, + }; formatGen(format: string) { - const formatid = toID(format); - if (!formatid) return Dex.gen; - if (!formatid.startsWith('gen')) return 6; - return parseInt(formatid.charAt(3)) || Dex.gen; + return this.formats.get(format).gen; } forFormat(format: string) { - let dex = Dex.forGen(Dex.formatGen(format)); - - const formatid = toID(format).slice(4); - if (dex.gen === 7 && formatid.includes('letsgo')) { - dex = Dex.mod('gen7letsgo' as ID); - } - if (dex.gen === 8 && formatid.includes('bdsp')) { - dex = Dex.mod('gen8bdsp' as ID); - } - if (dex.gen === 9 && formatid.includes('champions')) { - dex = Dex.mod('champions' as ID); - } - return dex; + return this.mod(this.formats.get(format).mod); } resolveAvatar(avatar: string): string { diff --git a/play.pokemonshowdown.com/src/battle-team-editor.tsx b/play.pokemonshowdown.com/src/battle-team-editor.tsx index b3d3300aa..da9d19480 100644 --- a/play.pokemonshowdown.com/src/battle-team-editor.tsx +++ b/play.pokemonshowdown.com/src/battle-team-editor.tsx @@ -61,18 +61,11 @@ export class TeamEditorState extends PSModel { index: number, } | null = null; search = new DexSearch(); - format: ID = `gen${this.gen}` as ID; + format = Dex.formats.get(`gen${this.gen}`); originalSpecies: string | null = null; narrow = false; narrowStats = false; innerFocus: InnerFocusState | null = null; - isLetsGo = false; - isNatDex = false; - isBDSP = false; - isChampions = false; - formeLegality: 'normal' | 'hackmons' | 'custom' = 'normal'; - abilityLegality: 'normal' | 'hackmons' = 'normal'; - defaultLevel = 100; readonly = false; fetching = false; handleParentKeyDown?: (ev: KeyboardEvent) => boolean | void; @@ -92,45 +85,12 @@ export class TeamEditorState extends PSModel { this.readonly = readonly; } setFormat(format: string) { - const team = this.team; - const formatid = toID(format); - this.format = formatid; - team.format = formatid; - this.dex = Dex.forFormat(formatid); + this.format = Dex.formats.get(format); + this.team.format = this.format.id; + this.dex = Dex.mod(this.format.mod); this.gen = this.dex.gen; - - format = toID(format).slice(4); - this.isLetsGo = formatid.includes('letsgo'); - this.isNatDex = formatid.includes('nationaldex') || formatid.includes('natdex'); - this.isBDSP = formatid.includes('bdsp'); - this.isChampions = formatid.includes('champions'); - if (formatid.includes('almostanyability') || formatid.includes('aaa')) { - this.abilityLegality = 'hackmons'; - } else { - this.abilityLegality = 'normal'; - } - if (formatid.includes('hackmons') || formatid.includes('bh')) { - this.formeLegality = 'hackmons'; - this.abilityLegality = 'hackmons'; - } else if (formatid.includes('metronome') || formatid.includes('customgame')) { - this.formeLegality = 'custom'; - this.abilityLegality = 'hackmons'; - } else { - this.formeLegality = 'normal'; - } - - this.defaultLevel = 100; - if ( - formatid.includes('vgc') || formatid.includes('bss') || formatid.includes('ultrasinnohclassic') || - formatid.includes('battlespot') || formatid.includes('battlestadium') || formatid.includes('battlefestival') || - formatid.includes('letsgo') || formatid.includes('champions') - ) { - this.defaultLevel = 50; - } - if (formatid.includes('lc')) { - this.defaultLevel = 5; - } } + stringifyFocus(focus: FocusState | null): string { if (!focus) return ''; return `set-${focus.setIndex}-${focus.type}${focus.typeIndex >= 0 ? `-${focus.typeIndex}` : ''}`; @@ -194,7 +154,7 @@ export class TeamEditorState extends PSModel { } setSearchType(type: SearchType, i: number, value?: string, typeIndex = -1) { const set = this.sets[i]; - this.search.setType(type, this.format, set); + this.search.setType(type, this.format.id, set); this.originalSpecies = null; this.search.prependResults = null; if (type === 'move') { @@ -467,10 +427,10 @@ export class TeamEditorState extends PSModel { return this.sets.length < 6 || this.team.isBox; } showItem(set: Dex.PokemonSet) { - return !!(this.gen > 1 && !this.isLetsGo || set.item); + return !!(this.gen > 1 && !this.format.isLetsGo || set.item); } showAbility(set: Dex.PokemonSet) { - return !!(this.gen > 2 && !this.isLetsGo || set.ability); + return !!(this.gen > 2 && !this.format.isLetsGo || set.ability); } getHPType(set: Dex.PokemonSet): Dex.TypeName { if (set.hpType) return set.hpType as Dex.TypeName; @@ -537,7 +497,7 @@ export class TeamEditorState extends PSModel { defaultIVs(set: Dex.PokemonSet, noGuess = !!set.ivs): Record { const useIVs = this.gen > 2; const defaultIVs = { hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31 }; - if (this.isChampions) return defaultIVs; + if (this.format.isChampions) return defaultIVs; if (!useIVs) { for (const stat of Dex.statNames) defaultIVs[stat] = 15; } @@ -595,11 +555,11 @@ export class TeamEditorState extends PSModel { if (set.species.startsWith('Terapagos')) minSpe = false; const preferMaxAtkFormats = ['1v1', 'categoryswap', 'noholdsbarred', 'partnersincrime', 'typesplit']; - if (preferMaxAtkFormats.some(f => this.format.includes(f))) { + if (preferMaxAtkFormats.some(f => this.format.id.includes(f))) { minAtk = false; return { minAtk, minSpe }; } - if (this.format === 'gen7hiddentype') return { minAtk, minSpe }; + if (this.format.id === 'gen7hiddentype') return { minAtk, minSpe }; // only available through an event with 31 Atk IVs if (set.ability === 'Battle Bond' || ['Koraidon', 'Miraidon', 'Gimmighoul-Roaming'].includes(set.species)) { @@ -633,10 +593,10 @@ export class TeamEditorState extends PSModel { return set.name || this.dex.species.get(set.species).baseSpecies || ''; } canHyperTrain(set: Dex.PokemonSet) { - let format: string = this.format; + let format: string = this.format.id; if (this.gen < 7 || format === 'gen7hiddentype') return false; - if ((set.level || this.defaultLevel) === 100) return true; - if ((set.level || this.defaultLevel) >= 50 && this.defaultLevel === 50) return true; + if ((set.level || this.format.defaultLevel) === 100) return true; + if ((set.level || this.format.defaultLevel) >= 50 && this.format.defaultLevel === 50) return true; return false; } getHPIVs(hpType: Dex.TypeName | null) { @@ -683,20 +643,20 @@ export class TeamEditorState extends PSModel { const species = this.dex.species.get(set.species); if (!species.exists) return 0; - const level = set.level || this.defaultLevel; + const level = set.level || this.format.defaultLevel; const baseStat = species.baseStats[stat]; const iv = ivOverride; let ev = evOverride ?? set.evs?.[stat] ?? (this.gen > 2 ? 0 : 252); - if (this.isChampions) ev *= 8; + if (this.format.isChampions) ev *= 8; if (stat === 'hp') { if (baseStat === 1) return 1; - if (this.isLetsGo) return Math.trunc(Math.trunc(2 * baseStat + iv + 100) * level / 100 + 10) + ev; + if (this.format.isLetsGo) return Math.trunc(Math.trunc(2 * baseStat + iv + 100) * level / 100 + 10) + ev; return Math.trunc(Math.trunc(2 * baseStat + iv + Math.trunc(ev / 4) + 100) * level / 100 + 10); } let val = Math.trunc(Math.trunc(2 * baseStat + iv + Math.trunc(ev / 4)) * level / 100 + 5); - if (this.isLetsGo) { + if (this.format.isLetsGo) { val = Math.trunc(Math.trunc(2 * baseStat + iv) * level / 100 + 5); } if (natureOverride) { @@ -706,7 +666,7 @@ export class TeamEditorState extends PSModel { } else if (BattleNatures[set.nature!]?.minus === stat) { val *= 0.9; } - if (this.isLetsGo) { + if (this.format.isLetsGo) { const friendshipValue = Math.trunc((70 / 255 / 10 + 1) * 100); val = Math.trunc(val) * friendshipValue / 100 + ev; } @@ -779,13 +739,13 @@ export class TeamEditorState extends PSModel { return counters; } getDefaultAbility(set: Dex.PokemonSet) { - if (this.gen < 3 || this.isLetsGo || this.formeLegality === 'custom') return set.ability; + if (this.gen < 3 || this.format.isLetsGo || this.format.formeLegality === 'custom') return set.ability; const species = this.dex.species.get(set.species); - if (this.formeLegality === 'hackmons') { + if (this.format.formeLegality === 'hackmons') { // TODO: support gen 9 hackmons forme legality more completely than this if (this.gen < 9 || species.baseSpecies !== 'Xerneas') return set.ability; // falls through to final return statement - } else if (this.abilityLegality === 'hackmons') { + } else if (this.format.abilityLegality === 'hackmons') { if (!species.battleOnly) return set.ability; if (species.requiredItems.length || species.baseSpecies === 'Meloetta') return set.ability; // battle only species only ever have one ability @@ -801,13 +761,13 @@ export class TeamEditorState extends PSModel { getDefaultItem(speciesName: string) { const species = this.dex.species.get(speciesName); let items = species.requiredItems; - if (this.gen !== 7 && !this.isNatDex) { + if (this.gen !== 7 && !this.format.isNatDex) { // Require plates on Arceus when Z crystals don't exist items = items.filter(i => !i.endsWith('ium Z')); } if (items.length === 1) { - if (this.formeLegality === 'normal' || - this.formeLegality === 'hackmons' && this.gen === 9 && species.battleOnly && + if (this.format.formeLegality === 'normal' || + this.format.formeLegality === 'hackmons' && this.gen === 9 && species.battleOnly && !species.isMega && !species.isPrimal && species.name !== 'Necrozma-Ultra') { return items[0]; } @@ -847,9 +807,9 @@ export class TeamEditorState extends PSModel { } /** returns null if sample sets aren't done loading */ getSampleSets(set: Dex.PokemonSet): string[] | null { - const d = TeamEditorState.sampleSets[this.format]; + const d = TeamEditorState.sampleSets[this.format.id]; if (d === undefined) { - this.fetchSampleSets(this.format); + this.fetchSampleSets(this.format.id); return null; } if (!d?.dex) return []; @@ -864,11 +824,11 @@ export class TeamEditorState extends PSModel { } /** returns null if no boxes exist, empty array if no sets for this species */ getUserSets(set: Dex.PokemonSet): { [setName: string]: Dex.PokemonSet } | null { - if (!this.userSetsCache[this.format]) { + if (!this.userSetsCache[this.format.id]) { const userSets: { [species: string]: { [setName: string]: Dex.PokemonSet } } = {}; for (const team of window.PS?.teams.list || []) { - if (team.format !== this.format || !team.isBox) continue; + if (team.format !== this.format.id || !team.isBox) continue; const setList = Teams.unpack(team.packedTeam); const duplicateNameIndices: Record = {}; @@ -885,10 +845,10 @@ export class TeamEditorState extends PSModel { } } - this.userSetsCache[this.format] = userSets; + this.userSetsCache[this.format.id] = userSets; } - const cachedSets = this.userSetsCache[this.format]; + const cachedSets = this.userSetsCache[this.format.id]; if (Object.keys(cachedSets).length === 0) return null; return cachedSets[set.species] || {}; } @@ -897,7 +857,7 @@ export class TeamEditorState extends PSModel { const set = this.sets[setIndex]; if (!set?.species) return false; - const data = TeamEditorState.sampleSets?.[this.format]; + const data = TeamEditorState.sampleSets?.[this.format.id]; const sid = toID(set.species); const setTemplate = data?.dex?.[set.species]?.[setName] ?? data?.dex?.[sid]?.[setName] ?? data?.stats?.[set.species]?.[setName] ?? data?.stats?.[sid]?.[setName]; @@ -1114,7 +1074,7 @@ export class TeamEditor extends preact.Component<{ const narrow = this.props.narrow ?? window.innerWidth < 500; editor.narrow = !useZoomedOutForms && narrow; editor.narrowStats = useZoomedOutForms || narrow; - if (this.props.team.format !== editor.format) { + if (this.props.team.format !== editor.format.id) { editor.setFormat(this.props.team.format); } const useSpaciousCSS = spacious && !mobileOptions || useZoomedOutForms; @@ -1806,12 +1766,12 @@ class TeamTextbox extends preact.Component<{ return diff --git a/play.pokemonshowdown.com/src/battle.ts b/play.pokemonshowdown.com/src/battle.ts index 4ea848bb5..c6fa32e4c 100644 --- a/play.pokemonshowdown.com/src/battle.ts +++ b/play.pokemonshowdown.com/src/battle.ts @@ -367,7 +367,7 @@ export class Pokemon implements PokemonDetails, PokemonHealth { if (ppUsed[1] < 0) ppUsed[1] = 0; const move = this.side.battle.dex.moves.get(entry[0]); let maxpp = (move.pp === 1 || move.noPPBoosts ? move.pp : move.pp * 8 / 5); - if (this.side.battle.tier.includes('Champions')) { + if (this.side.battle.format.isChampions) { maxpp = move.pp > 20 ? 20 : move.pp; maxpp = move.pp === 1 || move.noPPBoosts ? move.pp : (move.pp / 5 + 1) * 4; } @@ -957,7 +957,7 @@ export class Side { } pokemon.statusData.toxicTurns = 0; if (this.battle.gen === 5) pokemon.statusData.sleepTurns = 0; - if (this.battle.tier.includes('Champions')) { + if (this.battle.format.isChampions) { pokemon.timesAttacked = 0; } this.lastPokemon = pokemon; @@ -1151,6 +1151,7 @@ export class Battle { teamPreviewCount = 0; speciesClause = false; tier = ''; + format = Dex.formats.get(''); gameType: 'singles' | 'doubles' | 'triples' | 'multi' | 'freeforall' | 'rotation' = 'singles'; compatMode = true; rated: string | boolean = false; @@ -3527,6 +3528,7 @@ export class Battle { } case 'tier': { this.tier = args[1]; + this.format = Dex.formats.get(this.tier); if (this.tier.endsWith('Random Battle')) { this.speciesClause = true; } @@ -3534,13 +3536,13 @@ export class Battle { this.messageFadeTime = 40; this.isBlitz = true; } - if (this.tier.includes(`Let's Go`)) { + if (this.format.isLetsGo) { this.dex = Dex.mod('gen7letsgo' as ID); } if (this.tier.includes('Super Staff Bros')) { this.dex = Dex.mod('gen9ssb' as ID); } - if (this.tier.includes(`Champions`)) { + if (this.format.isChampions) { this.dex = Dex.mod('champions' as ID); } this.log(args); diff --git a/play.pokemonshowdown.com/src/globals.d.ts b/play.pokemonshowdown.com/src/globals.d.ts index 893046dda..3f33c173b 100644 --- a/play.pokemonshowdown.com/src/globals.d.ts +++ b/play.pokemonshowdown.com/src/globals.d.ts @@ -27,7 +27,7 @@ type BattleTextData = { declare const BattleText: { [lang: string]: BattleTextData }; type BattleUITextEntry = string | null | { [context: string]: string | null }; declare const BattleUIText: { [lang: string]: { [english: string]: BattleUITextEntry } }; -declare const BattleFormats: { [id: string]: import('./panel-teamdropdown').FormatData }; +declare const BattleFormats: { [id: string]: import('./battle-dex-data').FormatData }; declare const BattlePokedex: { [id: string]: AnyObject }; declare const BattleMovedex: { [id: string]: AnyObject }; declare const BattleAbilities: { [id: string]: AnyObject }; diff --git a/play.pokemonshowdown.com/src/oldclient/client.js b/play.pokemonshowdown.com/src/oldclient/client.js index ad6416769..65c513d1d 100644 --- a/play.pokemonshowdown.com/src/oldclient/client.js +++ b/play.pokemonshowdown.com/src/oldclient/client.js @@ -1480,6 +1480,7 @@ function toId() { } } } + window.BattleFormats = Dex.formats.load(BattleFormats); if (columnChanged) app.supports['formatColumns'] = true; this.trigger('init:formats'); }, diff --git a/play.pokemonshowdown.com/src/panel-battle.tsx b/play.pokemonshowdown.com/src/panel-battle.tsx index 467db30d8..200292cdc 100644 --- a/play.pokemonshowdown.com/src/panel-battle.tsx +++ b/play.pokemonshowdown.com/src/panel-battle.tsx @@ -761,9 +761,10 @@ class BattlePanel extends PSRoomPanel { const moveRequest = choices.currentMoveRequest()!; const canDynamax = moveRequest.canDynamax && !choices.alreadyMax; - const canMegaEvo = moveRequest.canMegaEvo && !choices.alreadyMega; - const canMegaEvoX = moveRequest.canMegaEvoX && !choices.alreadyMega; - const canMegaEvoY = moveRequest.canMegaEvoY && !choices.alreadyMega; + const alreadyMega = choices.alreadyMega && !this.props.room.battle.format.allowMultipleMegas; + const canMegaEvo = moveRequest.canMegaEvo && !alreadyMega; + const canMegaEvoX = moveRequest.canMegaEvoX && !alreadyMega; + const canMegaEvoY = moveRequest.canMegaEvoY && !alreadyMega; const canZMove = moveRequest.zMoves && !choices.alreadyZ; const canUltraBurst = moveRequest.canUltraBurst; const canTerastallize = moveRequest.canTerastallize; diff --git a/play.pokemonshowdown.com/src/panel-mainmenu.tsx b/play.pokemonshowdown.com/src/panel-mainmenu.tsx index 0acfcdbac..0477003dc 100644 --- a/play.pokemonshowdown.com/src/panel-mainmenu.tsx +++ b/play.pokemonshowdown.com/src/panel-mainmenu.tsx @@ -366,6 +366,7 @@ export class MainMenuRoom extends PSRoom { } } } + window.BattleFormats = Dex.formats.load(BattleFormats); PS.teams.update('format'); } handlePM(user1: string, user2: string, message?: string) { diff --git a/play.pokemonshowdown.com/src/panel-teamdropdown.tsx b/play.pokemonshowdown.com/src/panel-teamdropdown.tsx index a6aa3b71c..4efca4fcc 100644 --- a/play.pokemonshowdown.com/src/panel-teamdropdown.tsx +++ b/play.pokemonshowdown.com/src/panel-teamdropdown.tsx @@ -8,6 +8,7 @@ import { PS, type Team } from "./client-main"; import { PSIcon, PSPanelWrapper, PSRoomPanel } from "./panels"; import { Dex, TL, toID, type ID } from "./battle-dex"; +import { type FormatData } from "./battle-dex-data"; import { Teams } from "./battle-teams"; export class PSTeambuilder { @@ -358,29 +359,6 @@ class TeamDropdownPanel extends PSRoomPanel { } } -export interface FormatData { - id: ID; - name: string; - team?: 'preset' | null; - section: string; - column: number; - searchShow?: boolean; - challengeShow?: boolean; - tournamentShow?: boolean; - bestOfDefault?: boolean; - teraPreviewDefault?: boolean; - itemClauseDefault?: boolean; - rated: boolean; - teambuilderLevel?: number | null; - partner?: boolean; - teambuilderFormat?: ID; - battleFormat?: string; - isTeambuilderFormat: boolean; - effectType: 'Format'; -} - -declare const BattleFormats: { [id: string]: FormatData }; - export type SelectType = 'teambuilder' | 'challenge' | 'search' | 'tournament'; class FormatDropdownPanel extends PSRoomPanel { static readonly id = 'formatdropdown';