From 2e087d5f43c0fe934c5d760680809a74bfc701b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= Date: Mon, 26 Jan 2026 11:02:01 +0000 Subject: [PATCH 1/9] Implement Gen 8 Tradebacks --- config/formats.ts | 7 +++++++ sim/team-validator.ts | 14 +++++++------- test/sim/team-validator/basic.js | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/config/formats.ts b/config/formats.ts index b84a461ac2..816fc6e010 100644 --- a/config/formats.ts +++ b/config/formats.ts @@ -4306,6 +4306,13 @@ export const Formats: import('../sim/dex-formats').FormatList = [ ruleset: ['[Gen 8] PU'], banlist: ['PU', 'ZUBL', 'Damp Rock', 'Grassy Seed'], }, + { + name: "[Gen 8] Tradebacks OU", + desc: `SS OU with Virtual Console moves and non-Hidden Abilities.`, + mod: 'gen8', + searchShow: false, + ruleset: ['[Gen 8] OU', 'Allow Tradeback'], + }, { name: "[Gen 8] CAP", desc: "The Create-A-Pokémon project is a community dedicated to exploring and understanding the competitive Pokémon metagame by designing, creating, and playtesting new Pokémon concepts.", diff --git a/sim/team-validator.ts b/sim/team-validator.ts index e109837780..0d9f5d0501 100644 --- a/sim/team-validator.ts +++ b/sim/team-validator.ts @@ -817,10 +817,10 @@ export class TeamValidator { const learnsetSpecies = dex.species.getLearnsetData(outOfBattleSpecies.id); let isFromRBYEncounter = false; - if (this.gen === 1 && ruleTable.has('obtainablemisc') && !this.ruleTable.has('allowtradeback')) { + if (ruleTable.has('obtainablemisc') && !this.ruleTable.has('allowtradeback')) { let lowestEncounterLevel; for (const encounter of learnsetSpecies.encounters || []) { - if (encounter.generation !== 1) continue; + if (encounter.generation > this.gen) continue; if (!encounter.level) continue; if (lowestEncounterLevel && encounter.level > lowestEncounterLevel) continue; @@ -829,7 +829,7 @@ export class TeamValidator { if (lowestEncounterLevel) { if (set.level < lowestEncounterLevel) { - problems.push(`${name} is not obtainable at levels below ${lowestEncounterLevel} in Gen 1.`); + problems.push(`${name} is not obtainable at levels below ${lowestEncounterLevel} in Gen ${this.gen}.`); } isFromRBYEncounter = true; } @@ -2065,7 +2065,7 @@ export class TeamValidator { const dex = this.dex; let name = set.species; const species = dex.species.get(set.species); - const maxSourceGen = this.ruleTable.has('allowtradeback') ? Utils.clampIntRange(dex.gen + 1, 1, 8) : dex.gen; + const maxSourceGen = this.ruleTable.has('allowtradeback') ? Utils.clampIntRange(dex.gen + 1, 1, Dex.gen) : dex.gen; if (!eventSpecies) eventSpecies = species; if (set.name && set.species !== set.name && species.baseSpecies !== set.name) name = `${set.name} (${set.species})`; @@ -2204,7 +2204,7 @@ export class TeamValidator { } if (species.abilities['H']) { const isHidden = (set.ability === species.abilities['H']); - if (!isHidden && eventData.isHidden && dex.gen <= 8) { + if (!isHidden && eventData.isHidden && (dex.gen <= 7 || (this.gen === 8 && !this.ruleTable.has('allowtradeback')))) { if (fastReturn) return true; problems.push(`${name} must have its Hidden Ability${etc}.`); } @@ -2224,7 +2224,7 @@ export class TeamValidator { let minSourceGen = this.minSourceGen; if (this.dex.gen >= 3 && minSourceGen < 3) minSourceGen = 3; if (species) minSourceGen = Math.max(minSourceGen, species.gen); - const maxSourceGen = this.ruleTable.has('allowtradeback') ? Utils.clampIntRange(this.dex.gen + 1, 1, 8) : this.dex.gen; + const maxSourceGen = this.ruleTable.has('allowtradeback') ? Utils.clampIntRange(this.dex.gen + 1, 1, Dex.gen) : this.dex.gen; return new PokemonSources(maxSourceGen, minSourceGen); } @@ -2490,7 +2490,7 @@ export class TeamValidator { /** * The format doesn't allow Pokemon traded from the future - * (This is everything except in Gen 1 Tradeback) + * (This is everything except in Gen 1 and Gen 8 Tradeback) */ const noFutureGen = !ruleTable.has('allowtradeback'); /** diff --git a/test/sim/team-validator/basic.js b/test/sim/team-validator/basic.js index d0926240a7..efa263739b 100644 --- a/test/sim/team-validator/basic.js +++ b/test/sim/team-validator/basic.js @@ -244,6 +244,20 @@ describe('Team Validator', () => { assert.legalTeam(team, 'gen8ou'); }); + // https://www.smogon.com/forums/threads/tradebacks-in-gen-8-metagames.3735419/ + it('should allow VC moves with non-Hidden abilities in Gen 8 Tradebacks', () => { + let team = [ + { species: 'machamp', ability: 'noguard', moves: ['fissure'], evs: {hp: 1} }, + ]; + assert.false.legalTeam(team, 'gen8anythinggoes'); + assert.legalTeam(team, 'gen8anythinggoes@@@allowtradeback'); + team = [ + { species: 'clefable', ability: 'magicguard', moves: ['wish', 'teleport', 'knockoff'], evs: {hp: 1} }, + ]; + assert.false.legalTeam(team, 'gen8anythinggoes'); + assert.legalTeam(team, 'gen8anythinggoes@@@allowtradeback'); + }); + it(`should disallow past gen only moves in Gen 9`, () => { const team = [ { species: 'oricorio', ability: 'dancer', moves: ['roleplay'], evs: { hp: 1 } }, From a7a32ffb07edce088fb7dde8ede1fe751c4a6270 Mon Sep 17 00:00:00 2001 From: andrebastosdias Date: Mon, 26 Jan 2026 11:15:03 +0000 Subject: [PATCH 2/9] Fix lint --- sim/team-validator.ts | 3 ++- test/sim/team-validator/basic.js | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sim/team-validator.ts b/sim/team-validator.ts index 0d9f5d0501..2b76989cee 100644 --- a/sim/team-validator.ts +++ b/sim/team-validator.ts @@ -2224,7 +2224,8 @@ export class TeamValidator { let minSourceGen = this.minSourceGen; if (this.dex.gen >= 3 && minSourceGen < 3) minSourceGen = 3; if (species) minSourceGen = Math.max(minSourceGen, species.gen); - const maxSourceGen = this.ruleTable.has('allowtradeback') ? Utils.clampIntRange(this.dex.gen + 1, 1, Dex.gen) : this.dex.gen; + const maxSourceGen = this.ruleTable.has('allowtradeback') ? Utils.clampIntRange(this.dex.gen + 1, 1, Dex.gen) : + this.dex.gen; return new PokemonSources(maxSourceGen, minSourceGen); } diff --git a/test/sim/team-validator/basic.js b/test/sim/team-validator/basic.js index efa263739b..1930024360 100644 --- a/test/sim/team-validator/basic.js +++ b/test/sim/team-validator/basic.js @@ -247,12 +247,12 @@ describe('Team Validator', () => { // https://www.smogon.com/forums/threads/tradebacks-in-gen-8-metagames.3735419/ it('should allow VC moves with non-Hidden abilities in Gen 8 Tradebacks', () => { let team = [ - { species: 'machamp', ability: 'noguard', moves: ['fissure'], evs: {hp: 1} }, + { species: 'machamp', ability: 'noguard', moves: ['fissure'], evs: { hp: 1 } }, ]; assert.false.legalTeam(team, 'gen8anythinggoes'); assert.legalTeam(team, 'gen8anythinggoes@@@allowtradeback'); team = [ - { species: 'clefable', ability: 'magicguard', moves: ['wish', 'teleport', 'knockoff'], evs: {hp: 1} }, + { species: 'clefable', ability: 'magicguard', moves: ['wish', 'teleport', 'knockoff'], evs: { hp: 1 } }, ]; assert.false.legalTeam(team, 'gen8anythinggoes'); assert.legalTeam(team, 'gen8anythinggoes@@@allowtradeback'); From 5021f6141cea5c53e09da480cbead8f34bd38d56 Mon Sep 17 00:00:00 2001 From: andrebastosdias Date: Mon, 26 Jan 2026 11:21:08 +0000 Subject: [PATCH 3/9] Fix Gen 1 encounters --- sim/team-validator.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sim/team-validator.ts b/sim/team-validator.ts index 2b76989cee..51fd696363 100644 --- a/sim/team-validator.ts +++ b/sim/team-validator.ts @@ -817,10 +817,10 @@ export class TeamValidator { const learnsetSpecies = dex.species.getLearnsetData(outOfBattleSpecies.id); let isFromRBYEncounter = false; - if (ruleTable.has('obtainablemisc') && !this.ruleTable.has('allowtradeback')) { + if (this.gen === 1 && ruleTable.has('obtainablemisc') && !this.ruleTable.has('allowtradeback')) { let lowestEncounterLevel; for (const encounter of learnsetSpecies.encounters || []) { - if (encounter.generation > this.gen) continue; + if (encounter.generation !== 1) continue; if (!encounter.level) continue; if (lowestEncounterLevel && encounter.level > lowestEncounterLevel) continue; @@ -829,7 +829,7 @@ export class TeamValidator { if (lowestEncounterLevel) { if (set.level < lowestEncounterLevel) { - problems.push(`${name} is not obtainable at levels below ${lowestEncounterLevel} in Gen ${this.gen}.`); + problems.push(`${name} is not obtainable at levels below ${lowestEncounterLevel} in Gen 1.`); } isFromRBYEncounter = true; } From a9076013b0ba74fe92ed97dfa591c9109649b9a9 Mon Sep 17 00:00:00 2001 From: andrebastosdias Date: Mon, 26 Jan 2026 11:30:16 +0000 Subject: [PATCH 4/9] Update Allow Tradebacks description --- data/rulesets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/rulesets.ts b/data/rulesets.ts index d3d6f68b63..54954424ac 100644 --- a/data/rulesets.ts +++ b/data/rulesets.ts @@ -1805,7 +1805,7 @@ export const Rulesets: import('../sim/dex-formats').FormatDataTable = { allowtradeback: { effectType: 'ValidatorRule', name: 'Allow Tradeback', - desc: "Allows Gen 1 pokemon to have moves from their Gen 2 learnsets", + desc: "Allows moves and abilities only obtainable by trading from future generations.", // Implemented in team-validator.js }, lgpenormalrules: { From 940b12d1c87c98386cd6f2273fc3bc1672a9fa9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= Date: Mon, 26 Jan 2026 13:19:29 +0000 Subject: [PATCH 5/9] Refactor maxSourceGen --- sim/team-validator.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sim/team-validator.ts b/sim/team-validator.ts index 51fd696363..154757bade 100644 --- a/sim/team-validator.ts +++ b/sim/team-validator.ts @@ -320,6 +320,7 @@ export class TeamValidator { readonly gen: number; readonly ruleTable: RuleTable; readonly minSourceGen: number; + readonly maxSourceGen: number; readonly toID: (str: any) => ID; constructor(format: string | Format, dex = Dex) { @@ -333,6 +334,13 @@ export class TeamValidator { this.minSourceGen = this.ruleTable.minSourceGen; + let maxSourceGen = this.dex.gen; + if (this.ruleTable.has('allowtradeback')) { + if (this.dex.gen === 1) maxSourceGen = 2; + else if (this.dex.gen === 8) maxSourceGen = 9; + } + this.maxSourceGen = maxSourceGen; + this.toID = toID; } @@ -2065,7 +2073,6 @@ export class TeamValidator { const dex = this.dex; let name = set.species; const species = dex.species.get(set.species); - const maxSourceGen = this.ruleTable.has('allowtradeback') ? Utils.clampIntRange(dex.gen + 1, 1, Dex.gen) : dex.gen; if (!eventSpecies) eventSpecies = species; if (set.name && set.species !== set.name && species.baseSpecies !== set.name) name = `${set.name} (${set.species})`; @@ -2079,7 +2086,7 @@ export class TeamValidator { if (fastReturn) return true; problems.push(`This format requires Pokemon from gen ${this.minSourceGen} or later and ${name} is from gen ${eventData.generation}${etc}.`); } - if (maxSourceGen < eventData.generation) { + if (this.maxSourceGen < eventData.generation) { if (fastReturn) return true; problems.push(`This format is in gen ${dex.gen} and ${name} is from gen ${eventData.generation}${etc}.`); } @@ -2224,9 +2231,7 @@ export class TeamValidator { let minSourceGen = this.minSourceGen; if (this.dex.gen >= 3 && minSourceGen < 3) minSourceGen = 3; if (species) minSourceGen = Math.max(minSourceGen, species.gen); - const maxSourceGen = this.ruleTable.has('allowtradeback') ? Utils.clampIntRange(this.dex.gen + 1, 1, Dex.gen) : - this.dex.gen; - return new PokemonSources(maxSourceGen, minSourceGen); + return new PokemonSources(this.maxSourceGen, minSourceGen); } validateMoves( @@ -2489,11 +2494,6 @@ export class TeamValidator { // set of possible sources of a pokemon with this move const moveSources = new PokemonSources(); - /** - * The format doesn't allow Pokemon traded from the future - * (This is everything except in Gen 1 and Gen 8 Tradeback) - */ - const noFutureGen = !ruleTable.has('allowtradeback'); /** * The format allows Sketch to copy moves in Gen 8 */ @@ -2571,7 +2571,7 @@ export class TeamValidator { } continue; } - if (noFutureGen && learnedGen > dex.gen) { + if (learnedGen > this.maxSourceGen) { if (!cantLearnReason) { cantLearnReason = `can't be transferred from Gen ${learnedGen} to ${dex.gen}.`; } From 69309e3ef778f00178251943f907a5b9853e76fe Mon Sep 17 00:00:00 2001 From: andrebastosdias Date: Mon, 26 Jan 2026 15:58:25 +0000 Subject: [PATCH 6/9] =?UTF-8?q?Force=20HAs=20for=20Pok=C3=A9mon=20that=20d?= =?UTF-8?q?o=20not=20exist=20in=20SV?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/formats.ts | 7 ------- sim/team-validator.ts | 9 ++++++--- test/sim/team-validator/basic.js | 4 ++-- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/config/formats.ts b/config/formats.ts index 816fc6e010..b84a461ac2 100644 --- a/config/formats.ts +++ b/config/formats.ts @@ -4306,13 +4306,6 @@ export const Formats: import('../sim/dex-formats').FormatList = [ ruleset: ['[Gen 8] PU'], banlist: ['PU', 'ZUBL', 'Damp Rock', 'Grassy Seed'], }, - { - name: "[Gen 8] Tradebacks OU", - desc: `SS OU with Virtual Console moves and non-Hidden Abilities.`, - mod: 'gen8', - searchShow: false, - ruleset: ['[Gen 8] OU', 'Allow Tradeback'], - }, { name: "[Gen 8] CAP", desc: "The Create-A-Pokémon project is a community dedicated to exploring and understanding the competitive Pokémon metagame by designing, creating, and playtesting new Pokémon concepts.", diff --git a/sim/team-validator.ts b/sim/team-validator.ts index 154757bade..5a37725c86 100644 --- a/sim/team-validator.ts +++ b/sim/team-validator.ts @@ -2211,13 +2211,16 @@ export class TeamValidator { } if (species.abilities['H']) { const isHidden = (set.ability === species.abilities['H']); - if (!isHidden && eventData.isHidden && (dex.gen <= 7 || (this.gen === 8 && !this.ruleTable.has('allowtradeback')))) { + const canAbilityPatchRegular = dex.gen === 9 || ( + dex.gen === 8 && this.maxSourceGen === 9 && !Dex.mod('gen9').species.get(species.name).isNonstandard + ); + if (!isHidden && eventData.isHidden && !canAbilityPatchRegular) { if (fastReturn) return true; problems.push(`${name} must have its Hidden Ability${etc}.`); } - const canUseAbilityPatch = dex.gen >= 8 && this.format.mod !== 'gen8dlc1'; - if (isHidden && !eventData.isHidden && !canUseAbilityPatch) { + const canAbilityPatchHidden = dex.gen >= 8 && this.format.mod !== 'gen8dlc1'; + if (isHidden && !eventData.isHidden && !canAbilityPatchHidden) { if (fastReturn) return true; problems.push(`${name} must not have its Hidden Ability${etc}.`); } diff --git a/test/sim/team-validator/basic.js b/test/sim/team-validator/basic.js index 1930024360..4fd1005abf 100644 --- a/test/sim/team-validator/basic.js +++ b/test/sim/team-validator/basic.js @@ -246,11 +246,11 @@ describe('Team Validator', () => { // https://www.smogon.com/forums/threads/tradebacks-in-gen-8-metagames.3735419/ it('should allow VC moves with non-Hidden abilities in Gen 8 Tradebacks', () => { + // Machamp doesn't exist in Gen 9 let team = [ { species: 'machamp', ability: 'noguard', moves: ['fissure'], evs: { hp: 1 } }, ]; - assert.false.legalTeam(team, 'gen8anythinggoes'); - assert.legalTeam(team, 'gen8anythinggoes@@@allowtradeback'); + assert.false.legalTeam(team, 'gen8anythinggoes@@@allowtradeback'); team = [ { species: 'clefable', ability: 'magicguard', moves: ['wish', 'teleport', 'knockoff'], evs: { hp: 1 } }, ]; From 796a15e8fc2b3de8286fa76ac2df66eb8b087bf1 Mon Sep 17 00:00:00 2001 From: andrebastosdias Date: Mon, 26 Jan 2026 16:04:47 +0000 Subject: [PATCH 7/9] Revert variable name --- sim/team-validator.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sim/team-validator.ts b/sim/team-validator.ts index 5a37725c86..d23edb8269 100644 --- a/sim/team-validator.ts +++ b/sim/team-validator.ts @@ -2211,16 +2211,16 @@ export class TeamValidator { } if (species.abilities['H']) { const isHidden = (set.ability === species.abilities['H']); - const canAbilityPatchRegular = dex.gen === 9 || ( + const canUseAbilityPatchReverse = dex.gen === 9 || ( dex.gen === 8 && this.maxSourceGen === 9 && !Dex.mod('gen9').species.get(species.name).isNonstandard ); - if (!isHidden && eventData.isHidden && !canAbilityPatchRegular) { + if (!isHidden && eventData.isHidden && !canUseAbilityPatchReverse) { if (fastReturn) return true; problems.push(`${name} must have its Hidden Ability${etc}.`); } - const canAbilityPatchHidden = dex.gen >= 8 && this.format.mod !== 'gen8dlc1'; - if (isHidden && !eventData.isHidden && !canAbilityPatchHidden) { + const canUseAbilityPatch = dex.gen >= 8 && this.format.mod !== 'gen8dlc1'; + if (isHidden && !eventData.isHidden && !canUseAbilityPatch) { if (fastReturn) return true; problems.push(`${name} must not have its Hidden Ability${etc}.`); } From 658613cdde6bfdfccd6c649bf2f8c9c06d0059b1 Mon Sep 17 00:00:00 2001 From: andrebastosdias Date: Mon, 26 Jan 2026 16:33:36 +0000 Subject: [PATCH 8/9] Refactor inequalities --- sim/team-validator.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sim/team-validator.ts b/sim/team-validator.ts index d23edb8269..1dd4db5b18 100644 --- a/sim/team-validator.ts +++ b/sim/team-validator.ts @@ -825,7 +825,7 @@ export class TeamValidator { const learnsetSpecies = dex.species.getLearnsetData(outOfBattleSpecies.id); let isFromRBYEncounter = false; - if (this.gen === 1 && ruleTable.has('obtainablemisc') && !this.ruleTable.has('allowtradeback')) { + if (this.gen === 1 && ruleTable.has('obtainablemisc') && this.maxSourceGen >= 2) { let lowestEncounterLevel; for (const encounter of learnsetSpecies.encounters || []) { if (encounter.generation !== 1) continue; @@ -2211,8 +2211,8 @@ export class TeamValidator { } if (species.abilities['H']) { const isHidden = (set.ability === species.abilities['H']); - const canUseAbilityPatchReverse = dex.gen === 9 || ( - dex.gen === 8 && this.maxSourceGen === 9 && !Dex.mod('gen9').species.get(species.name).isNonstandard + const canUseAbilityPatchReverse = dex.gen >= 9 || ( + dex.gen === 8 && this.maxSourceGen >= 9 && !Dex.mod('gen9').species.get(species.name).isNonstandard ); if (!isHidden && eventData.isHidden && !canUseAbilityPatchReverse) { if (fastReturn) return true; From 17a69a81bb74b20b508b44357e9ab51495c7c052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= <80102738+andrebastosdias@users.noreply.github.com> Date: Mon, 26 Jan 2026 16:42:16 +0000 Subject: [PATCH 9/9] Fix condition --- sim/team-validator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim/team-validator.ts b/sim/team-validator.ts index 1dd4db5b18..6313c5d68f 100644 --- a/sim/team-validator.ts +++ b/sim/team-validator.ts @@ -825,7 +825,7 @@ export class TeamValidator { const learnsetSpecies = dex.species.getLearnsetData(outOfBattleSpecies.id); let isFromRBYEncounter = false; - if (this.gen === 1 && ruleTable.has('obtainablemisc') && this.maxSourceGen >= 2) { + if (this.gen === 1 && ruleTable.has('obtainablemisc') && this.maxSourceGen === 1) { let lowestEncounterLevel; for (const encounter of learnsetSpecies.encounters || []) { if (encounter.generation !== 1) continue;