diff --git a/data/rulesets.ts b/data/rulesets.ts index 7fe0954628..ddc298ee52 100644 --- a/data/rulesets.ts +++ b/data/rulesets.ts @@ -1798,7 +1798,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: { diff --git a/sim/team-validator.ts b/sim/team-validator.ts index 02cea0039b..5f7af374d5 100644 --- a/sim/team-validator.ts +++ b/sim/team-validator.ts @@ -335,6 +335,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) { @@ -348,6 +349,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; } @@ -835,7 +843,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 === 1) { let lowestEncounterLevel; for (const encounter of learnsetSpecies.encounters || []) { if (encounter.generation !== 1) continue; @@ -2103,7 +2111,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, 8) : dex.gen; if (!eventSpecies) eventSpecies = species; if (set.name && set.species !== set.name && species.baseSpecies !== set.name) name = `${set.name} (${set.species})`; @@ -2117,7 +2124,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}.`); } @@ -2261,7 +2268,10 @@ export class TeamValidator { } if (species.abilities['H']) { const isHidden = (set.ability === species.abilities['H']); - if (!isHidden && eventData.isHidden && dex.gen <= 8) { + 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; problems.push(`${name} must have its Hidden Ability${etc}.`); } @@ -2281,8 +2291,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; - return new PokemonSources(maxSourceGen, minSourceGen); + return new PokemonSources(this.maxSourceGen, minSourceGen); } validateMoves( @@ -2547,11 +2556,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 Tradeback) - */ - const noFutureGen = !ruleTable.has('allowtradeback'); /** * The format allows Sketch to copy moves in Gen 8 */ @@ -2629,7 +2633,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}.`; } diff --git a/test/sim/team-validator/basic.js b/test/sim/team-validator/basic.js index d0926240a7..4fd1005abf 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', () => { + // Machamp doesn't exist in Gen 9 + let team = [ + { species: 'machamp', ability: 'noguard', moves: ['fissure'], evs: { hp: 1 } }, + ]; + assert.false.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 } },