This suggestion only affects custom metagames that wish to use Sketch in Gen VIII (CAP, anyone?). Currently, the ability for a Pokemon that learns Sketch to learn moves introduced in Gen VIII is tied to the tier name, requiring that it includes "National Dex". This is a horribly arbitrary way to implement this, because it prevents any other metagame from being able to do that without severely restricting its name and posing as an official metagame, which are obviously not good things.
I think it would be better to add a rule that can be added to any metagame's ruleset to allow this functionality. I even wrote up code for this already and confirmed it works in a private server:
rulesets.js:
team-validator.ts:
I think it would be better to add a rule that can be added to any metagame's ruleset to allow this functionality. I even wrote up code for this already and confirmed it works in a private server:
rulesets.js:
JavaScript:
standardnatdex: {
ruleset: [
'Obtainable', '+Unobtainable', '+Past', 'Gen 8 Sketch', 'Team Preview', 'Nickname Clause', 'HP Percentage Mod', 'Cancel Mod', 'Endless Battle Clause',
],
},
'gen8sketch': {
effectType: 'ValidatorRule',
name: 'Gen 8 Sketch',
desc: "Allows Sketch to be restored into Generation VIII formats.",
// Implemented in sim/team-validator.ts
},
Code:
checkLearnset(
move: Move,
s: Species,
setSources = this.allSources(s),
set: AnyObject = {}
): {type: string, [key: string]: any} | null {
const dex = this.dex;
if (!setSources.size()) throw new Error(`Bad sources passed to checkLearnset`);
move = dex.getMove(move);
const moveid = move.id;
const baseSpecies = dex.getSpecies(s);
let species: Species | null = baseSpecies;
const format = this.format;
const ruleTable = dex.getRuleTable(format);
const alreadyChecked: {[k: string]: boolean} = {};
const level = set.level || 100;
let incompatibleAbility = false;
let limit1 = true;
let sketch = false;
let blockedHM = false;
let sometimesPossible = false; // is this move in the learnset at all?
let babyOnly = '';
// This is a pretty complicated algorithm
// Abstractly, what it does is construct the union of sets of all
// possible ways this pokemon could be obtained, and then intersect
// it with a the pokemon's existing set of all possible ways it could
// be obtained. If this intersection is non-empty, the move is legal.
// 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
*/
const noSketchBlock = ruleTable.has('gen8sketch');
let tradebackEligible = false;
while (species?.name && !alreadyChecked[species.id]) {
alreadyChecked[species.id] = true;
if (dex.gen <= 2 && species.gen === 1) tradebackEligible = true;
const lsetData = dex.getLearnsetData(species.id);
if (!lsetData.learnset) {
if ((species.changesFrom || species.baseSpecies) !== species.name) {
// forme without its own learnset
species = dex.getSpecies(species.changesFrom || species.baseSpecies);
// warning: formes with their own learnset, like Wormadam, should NOT
// inherit from their base forme unless they're freely switchable
continue;
}
if (species.isNonstandard) {
// It's normal for a nonstandard species not to have learnset data
// Formats should replace the `Obtainable Moves` rule if they want to
// allow pokemon without learnsets.
return {type: 'invalid'};
}
// should never happen
throw new Error(`Species with no learnset data: ${species.id}`);
}
const checkingPrevo = species.baseSpecies !== s.baseSpecies;
if (checkingPrevo && !moveSources.size()) {
if (!setSources.babyOnly || !species.prevo) {
babyOnly = species.id;
}
}
if (lsetData.learnset[moveid] || lsetData.learnset['sketch']) {
sometimesPossible = true;
let lset = lsetData.learnset[moveid];
if (moveid === 'sketch' || !lset || species.id === 'smeargle') {
// The logic behind this comes from the idea that a Pokemon that learns Sketch
// should be able to Sketch any move before transferring into Generation 8.
if (move.noSketch || move.isZ || move.isMax || (move.gen > 7 && !noSketchBlock)) {
return {type: 'invalid'};
}
lset = lsetData.learnset['sketch'];
sketch = true;
}
if (typeof lset === 'string') lset = [lset];
for (let learned of lset) {
// Every `learned` represents a single way a pokemon might
// learn a move. This can be handled one of several ways:
// `continue`
// means we can't learn it
// `return false`
// means we can learn it with no restrictions
// (there's a way to just teach any pokemon of this species
// the move in the current gen, like a TM.)
// `moveSources.add(source)`
// means we can learn it only if obtained that exact way described
// in source
// `moveSources.addGen(learnedGen)`
// means we can learn it only if obtained at or before learnedGen
// (i.e. get the pokemon however you want, transfer to that gen,
// teach it, and transfer it to the current gen.)
const learnedGen = parseInt(learned.charAt(0));
if (learnedGen < this.minSourceGen) continue;
if (noFutureGen && learnedGen > dex.gen) continue;
// redundant
if (learnedGen <= moveSources.sourcesBefore) continue;
if (
learnedGen < 7 && setSources.isHidden && (dex.gen <= 7 || format.mod === 'gen8dlc1') &&
!dex.mod('gen' + learnedGen).getSpecies(baseSpecies.name).abilities['H']
) {
// check if the Pokemon's hidden ability was available
incompatibleAbility = true;
continue;
}
if (!species.isNonstandard) {
// HMs can't be transferred
if (dex.gen >= 4 && learnedGen <= 3 &&
['cut', 'fly', 'surf', 'strength', 'flash', 'rocksmash', 'waterfall', 'dive'].includes(moveid)) continue;
if (dex.gen >= 5 && learnedGen <= 4 &&
['cut', 'fly', 'surf', 'strength', 'rocksmash', 'waterfall', 'rockclimb'].includes(moveid)) continue;
// Defog and Whirlpool can't be transferred together
if (dex.gen >= 5 && ['defog', 'whirlpool'].includes(moveid) && learnedGen <= 4) blockedHM = true;
}
if (learned.charAt(1) === 'L') {
// special checking for level-up moves
if (level >= parseInt(learned.substr(2)) || learnedGen === 7) {
// we're past the required level to learn it
// (gen 7 level-up moves can be relearnered at any level)
// falls through to LMT check below
} else if (level >= 5 && learnedGen === 3 && species.canHatch) {
// Pomeg Glitch
} else if ((!species.gender || species.gender === 'F') && learnedGen >= 2 && species.canHatch) {
// available as egg move
learned = learnedGen + 'Eany';
// falls through to E check below
} else {
// this move is unavailable, skip it
continue;
}
}
// Gen 8 egg moves can be taught to any pokemon from any source
if (learned === '8E' || 'LMTR'.includes(learned.charAt(1))) {
if (learnedGen === dex.gen && learned.charAt(1) !== 'R') {
// current-gen level-up, TM or tutor moves:
// always available
if (learned !== '8E' && babyOnly) setSources.babyOnly = babyOnly;
if (!moveSources.moveEvoCarryCount) return null;
}
// past-gen level-up, TM, or tutor moves:
// available as long as the source gen was or was before this gen
if (learned.charAt(1) === 'R') {
moveSources.restrictedMove = moveid;
}
limit1 = false;
moveSources.addGen(learnedGen);
} else if (learned.charAt(1) === 'E') {
// egg moves:
// only if hatched from an egg
let limitedEggMove: ID | null | undefined = undefined;
if (learned.slice(1) === 'Eany') {
limitedEggMove = null;
} else if (learnedGen < 6) {
limitedEggMove = move.id;
}
learned = learnedGen + 'E' + (species.prevo ? species.id : '');
if (tradebackEligible && learnedGen === 2 && move.gen <= 1) {
// can tradeback
moveSources.add('1ET' + learned.slice(2));
}
moveSources.add(learned, limitedEggMove);
} else if (learned.charAt(1) === 'S') {
// event moves:
// only if that was the source
// Event Pokémon:
// Available as long as the past gen can get the Pokémon and then trade it back.
if (tradebackEligible && learnedGen === 2 && move.gen <= 1) {
// can tradeback
moveSources.add('1ST' + learned.slice(2) + ' ' + species.id);
}
moveSources.add(learned + ' ' + species.id);
} else if (learned.charAt(1) === 'D') {
// DW moves:
// only if that was the source
moveSources.add(learned + species.id);
} else if (learned.charAt(1) === 'V' && this.minSourceGen < learnedGen) {
// Virtual Console or Let's Go transfer moves:
// only if that was the source
moveSources.add(learned);
}
}
}
if (ruleTable.has('mimicglitch') && species.gen < 5) {
// include the Mimic Glitch when checking this mon's learnset
const glitchMoves = ['metronome', 'copycat', 'transform', 'mimic', 'assist'];
let getGlitch = false;
for (const i of glitchMoves) {
if (lsetData.learnset[i]) {
if (!(i === 'mimic' && dex.getAbility(set.ability).gen === 4 && !species.prevo)) {
getGlitch = true;
break;
}
}
}
if (getGlitch) {
moveSources.addGen(4);
if (move.gen < 5) {
limit1 = false;
}
}
}
if (!moveSources.size()) {
if (
(species.evoType === 'levelMove' && species.evoMove !== move.name) ||
(species.id === 'sylveon' && move.type !== 'Fairy')
) {
moveSources.moveEvoCarryCount = 1;
}
}
// also check to see if the mon's prevo or freely switchable formes can learn this move
species = this.learnsetParent(species);
}
if (limit1 && sketch) {
// limit 1 sketch move
if (setSources.sketchMove) {
return {type: 'oversketched', maxSketches: 1};
}
setSources.sketchMove = moveid;
}
if (blockedHM) {
// Limit one of Defog/Whirlpool to be transferred
if (setSources.hm) return {type: 'incompatible'};
setSources.hm = moveid;
}
if (!setSources.restrictiveMoves) {
setSources.restrictiveMoves = [];
}
setSources.restrictiveMoves.push(move.name);
// Now that we have our list of possible sources, intersect it with the current list
if (!moveSources.size()) {
if (this.minSourceGen > 1 && sometimesPossible) return {type: 'pastgen', gen: this.minSourceGen};
if (incompatibleAbility) return {type: 'incompatibleAbility'};
return {type: 'invalid'};
}
setSources.intersectWith(moveSources);
if (!setSources.size()) {
return {type: 'incompatible'};
}
if (babyOnly) setSources.babyOnly = babyOnly;
return null;
}