Done Clause for allowing Sketching Gen VIII moves

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:
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
    },
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;
    }
 

Users Who Are Viewing This Thread (Users: 1, Guests: 0)

Top