From 1a18b3dd556e268c75aaf6ec218f741bdf43dcd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= <80102738+andrebastosdias@users.noreply.github.com> Date: Mon, 5 Jan 2026 17:03:25 +0000 Subject: [PATCH 01/66] Make `Battle#debug` more flexible (#11685) --- sim/battle.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sim/battle.ts b/sim/battle.ts index 8b787b026a..9b2c59cdc9 100644 --- a/sim/battle.ts +++ b/sim/battle.ts @@ -3144,9 +3144,9 @@ export class Battle { this.log[this.lastMoveLine] = parts.join('|'); } - debug(activity: string) { + debug(...activity: Part[]) { if (this.debugMode) { - this.add('debug', activity); + this.add('debug', ...activity); } } From acdbd55ee5be591add84ab2d0a6f96b4eafee230 Mon Sep 17 00:00:00 2001 From: Kris Johnson <11083252+KrisXV@users.noreply.github.com> Date: Mon, 5 Jan 2026 16:11:51 -0700 Subject: [PATCH 02/66] Bio Mech Mons: Fix duped item exploit --- config/formats.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/config/formats.ts b/config/formats.ts index 76a4178770..0b922f77c9 100644 --- a/config/formats.ts +++ b/config/formats.ts @@ -546,12 +546,12 @@ export const Formats: import('../sim/dex-formats').FormatList = [ // searchShow: false, ruleset: ['Standard OMs', 'Sleep Moves Clause'], banlist: [ - 'Annihilape​', 'Arceus​', 'Archaludon​', 'Baxcalibur​', 'Calyrex-Ice​', 'Calyrex-Shadow​', 'Chien-Pao​', 'Chi-Yu​', 'Deoxys-Normal​', 'Deoxys-Attack​', 'Dialga​', 'Dialga-Origin​', - 'Espathra​', 'Eternatus​', 'Flutter Mane​', 'Giratina​', 'Giratina-Origin​', 'Gouging Fire​', 'Groudon​', 'Ho-Oh​', 'Iron Bundle​', 'Koraidon​', 'Kyogre​', 'Kyurem-Black​', 'Kyurem-White​', - 'Landorus-Incarnate', 'Lugia​', 'Lunala​', 'Magearna​', 'Mewtwo​', 'Miraidon​', 'Necrozma-Dawn-Wings​', 'Necrozma-Dusk-Mane​', 'Ogerpon-Hearthflame​', 'Palafin​', 'Palkia​', 'Palkia-Origin​', - 'Rayquaza​', 'Regieleki​', 'Regigigas​', 'Reshiram​', 'Roaring Moon​', 'Slaking​', 'Shaymin-Sky​', 'Sneasler​', 'Solgaleo​', 'Spectrier​', 'Terapagos​', 'Ursaluna-Bloodmoon​', 'Urshifu', - 'Urshifu-Rapid-Strike​', 'Volcarona​', 'Zacian​', 'Zacian-Crowned​', 'Zamazenta-Crowned', 'Zekrom​', 'Arena Trap​', 'Moody​', 'Sand Veil​', 'Shadow Tag​', 'Snow Cloak​', '​Bright Powder​', - 'Choice Band​', 'Choice Specs​', 'King\'s Rock​', 'Razor Fang​', 'Baton Pass​', 'Last Respects​', 'Shed Tail​', + 'Annihilape', 'Arceus', 'Archaludon', 'Baxcalibur', 'Calyrex-Ice', 'Calyrex-Shadow', 'Chien-Pao', 'Chi-Yu', 'Deoxys-Normal', 'Deoxys-Attack', 'Dialga', 'Dialga-Origin', + 'Espathra', 'Eternatus', 'Flutter Mane', 'Giratina', 'Giratina-Origin', 'Gouging Fire', 'Groudon', 'Ho-Oh', 'Iron Bundle', 'Koraidon', 'Kyogre', 'Kyurem-Black', 'Kyurem-White', + 'Landorus-Incarnate', 'Lugia', 'Lunala', 'Magearna', 'Mewtwo', 'Miraidon', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Ogerpon-Hearthflame', 'Palafin', 'Palkia', 'Palkia-Origin', + 'Rayquaza', 'Regieleki', 'Regigigas', 'Reshiram', 'Roaring Moon', 'Slaking', 'Shaymin-Sky', 'Sneasler', 'Solgaleo', 'Spectrier', 'Terapagos', 'Ursaluna-Bloodmoon', 'Urshifu', + 'Urshifu-Rapid-Strike', 'Volcarona', 'Zacian', 'Zacian-Crowned', 'Zamazenta-Crowned', 'Zekrom', 'Arena Trap', 'Moody', 'Sand Veil', 'Shadow Tag', 'Snow Cloak', 'Bright Powder', + 'Choice Band', 'Choice Specs', 'King\'s Rock', 'Razor Fang', 'Baton Pass', 'Last Respects', 'Shed Tail', ], validateSet(set, teamHas) { const dex = this.dex; @@ -605,8 +605,11 @@ export const Formats: import('../sim/dex-formats').FormatList = [ } const setHas: { [k: string]: true } = {}; for (const thing of allThings) { - if (setHas[this.toID(thing)]) return [`${set.species} has multiple copies of ${thing}.`]; - setHas[this.toID(thing)] = true; + let sanitizedThing: Item | Move | Ability = this.dex.items.get(thing); + if (!sanitizedThing.exists) sanitizedThing = this.dex.abilities.get(thing); + if (!sanitizedThing.exists) sanitizedThing = this.dex.moves.get(thing); + if (setHas[sanitizedThing.id]) return [`${set.species} has multiple copies of ${sanitizedThing.name}.`]; + setHas[sanitizedThing.id] = true; } const normalAbility = set.ability; if (!abilities.length) { From c671f43d50b22bd91fa70b355fd1870e95292a4c Mon Sep 17 00:00:00 2001 From: Kris Johnson <11083252+KrisXV@users.noreply.github.com> Date: Mon, 5 Jan 2026 16:24:32 -0700 Subject: [PATCH 03/66] Add a /1v1factory command --- server/chat-plugins/randombattles/index.ts | 42 +++++++++++++++------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/server/chat-plugins/randombattles/index.ts b/server/chat-plugins/randombattles/index.ts index e9a767c76c..cf8c133ae8 100644 --- a/server/chat-plugins/randombattles/index.ts +++ b/server/chat-plugins/randombattles/index.ts @@ -257,21 +257,21 @@ function getLetsGoMoves(species: string | Species) { return data.moves.map(formatMove).sort().join(`, `); } -function battleFactorySets(species: string | Species, tier: string | null, gen = 'gen9', isBSS = false) { +function battleFactorySets(species: string | Species, tier: string | null, gen = 'gen9', isBSS = false, is1v1 = false) { species = Dex.species.get(species); if (typeof species.battleOnly === 'string') { species = Dex.species.get(species.battleOnly); } gen = toID(gen); const genNum = parseInt(gen[3]); - if (isNaN(genNum) || genNum < 6 || (isBSS && genNum < 7)) return null; + if (isNaN(genNum) || genNum < 6 || (isBSS && genNum < 7) || (is1v1 && genNum < 9)) return null; const statsFile = JSON.parse( - FS(`data/random-battles/gen${genNum}/${isBSS ? `bss-` : ``}factory-sets.json`).readIfExistsSync() || + FS(`data/random-battles/gen${genNum}/${isBSS ? `bss-` : is1v1 ? `1v1-` : ``}factory-sets.json`).readIfExistsSync() || "{}" ); if (!Object.keys(statsFile).length) return null; let buf = ``; - if (!isBSS) { + if (!isBSS && !is1v1) { if (!tier) throw new Chat.ErrorMessage(`Please provide a valid tier.`); if (!(toID(tier) in TIERS)) throw new Chat.ErrorMessage(`That tier isn't supported.`); if (!(TIERS[toID(tier)] in statsFile)) { @@ -330,19 +330,25 @@ function battleFactorySets(species: string | Species, tier: string | null, gen = buf += ``; } } else { - const format = Dex.formats.get(`${gen}bssfactory`); - if (!(species.id in statsFile)) throw new Chat.ErrorMessage(`${species.name} doesn't have any sets in ${format.name}.`); - const setObj = statsFile[species.id]; + const format = Dex.formats.get(`${gen}${is1v1 ? '1v1' : 'bss'}factory`); + if (!((is1v1 ? species.name : species.id) in statsFile)) throw new Chat.ErrorMessage(`${species.name} doesn't have any sets in ${format.name}.`); + const setObj = statsFile[is1v1 ? species.name : species.id]; if (genNum >= 9) { buf += `Species rarity: ${setObj.weight} (higher is more common, max 10)
`; buf += `Sets for ${species.name} in ${format.name}:
`; for (const [i, set] of setObj.sets.entries()) { buf += `
Set ${i + 1} (${set.weight}%)`; buf += `