Resource Other Metagames Code Megathread

dhelmise

banend doosre
is a Site Content Manageris a Battle Simulator Administratoris a Top Social Media Contributoris a Community Leaderis a Programmeris a Community Contributoris a Top Contributoris a Top Smogon Media Contributoris a Top Dedicated Tournament Hostis a Smogon Discord Contributor Alumnus
Social Media Head
approved by The Immortal; taken over from Spandan
Previous threads: ORAS | USUM


Hi, there!
This thread intends to collect the code required to play the approved Other Metagamess.

How to contribute?

You may provide a Github Gist, Pastebin (or a similar site), or a post containing either the code itself required to implement the OM or the diffs involved in its implementation (so a link to a dedicated commit or pull request would be cool). This is the way to go for OMs that dynamically change (e.g. Theorymon, OM Mashup, Pokémon Throwback, etc.) Past versions of periodical OMs can be published though.

For small, simple OMs (in the way of coding, at least), you may just post it fully, using the format showcased below.

If you want to request to have an OM coded, simply post to ask for the request; this includes past-gen OMs.

Gen 8 OMs
config/formats.js
JavaScript:
    {
        name: "[Gen 8] Inheritance",
        desc: `Pokémon may use the ability and moves of another, as long as they forfeit their own learnset.`,
        threads: [
            `&bullet; <a href="https://www.smogon.com/forums/threads/3656811/">Inheritance</a>`,
        ],

        mod: 'gen8',
        ruleset: ['[Gen 8] OU'],
        banlist: ['Shedinja', 'Assist', 'Shell Smash', 'Arena Trap', 'Huge Power', 'Imposter', 'Innards Out', 'Pure Power', 'Water Bubble'],
        restricted: ['Dracovish', 'Dracozolt'],
        // @ts-ignore
        getEvoFamily(speciesid) {
            let species = Dex.getSpecies(speciesid);
            while (species.prevo) {
                species = Dex.getSpecies(species.prevo);
            }
            return species.id;
        },
        validateSet(set, teamHas) {
            const bannedDonors = this.format.restricted || [];
            if (!teamHas.abilityMap) {
                teamHas.abilityMap = Object.create(null);
                for (const id in Dex.data.Pokedex) {
                    let pokemon = Dex.getSpecies(id);
                    if (pokemon.isNonstandard || pokemon.isUnreleased) continue;
                    if (pokemon.requiredAbility || pokemon.requiredItem || pokemon.requiredMove) continue;
                    if (pokemon.isGigantamax || bannedDonors.includes(pokemon.name)) continue;

                    for (const key of Object.values(pokemon.abilities)) {
                        let abilityId = toID(key);
                        if (abilityId in teamHas.abilityMap) {
                            teamHas.abilityMap[abilityId][pokemon.evos ? 'push' : 'unshift'](id);
                        } else {
                            teamHas.abilityMap[abilityId] = [id];
                        }
                    }
                }
            }

            const problem = this.validateForme(set);
            if (problem.length) return problem;

            let species = Dex.getSpecies(set.species);
            if (!species.exists || species.num < 1) return [`The Pok\u00e9mon "${set.species}" does not exist.`];
            if (species.isNonstandard || species.isUnreleased) return [`${species.name} is not obtainable in gen 8.`];
            if (toID(species.tier) === 'uber' || this.format.banlist.includes(species.name)) {
                return [`${species.name} is banned.`];
            }

            const name = set.name;

            let ability = Dex.getAbility(set.ability);
            if (!ability.exists || ability.isNonstandard) return [`${name} needs to have a valid ability.`];
            let pokemonWithAbility = teamHas.abilityMap[ability.id];
            if (!pokemonWithAbility) return [`"${set.ability}" is not available on a legal Pok\u00e9mon.`];

            // @ts-ignore
            this.format.debug = true;

            if (!teamHas.abilitySources) teamHas.abilitySources = Object.create(null);
            /** @type {string[]} */
            let validSources = teamHas.abilitySources[toID(set.species)] = []; // Evolution families

            let canonicalSource = ''; // Specific for the basic implementation of Donor Clause (see onValidateTeam).

            for (const donor of pokemonWithAbility) {
                let donorSpecies = Dex.getSpecies(donor);
                // @ts-ignore
                let evoFamily = this.format.getEvoFamily(donorSpecies);
                if (validSources.includes(evoFamily)) continue;

                set.species = donorSpecies.name;
                const problems = this.validateSet(set, teamHas) || [];
                if (!problems.length) {
                    validSources.push(evoFamily);
                    canonicalSource = donorSpecies.name;
                }
                // Specific for the basic implementation of Donor Clause (see onValidateTeam).
                if (validSources.length > 1) break;
            }
            // @ts-ignore
            this.format.debug = false;

            set.name = name;
            set.species = species.name;
            if (!validSources.length) {
                if (pokemonWithAbility.length > 1) return [`${name}'s set is illegal.`];
                return [`${name} has an illegal set with an ability from ${Dex.getSpecies(pokemonWithAbility[0]).name}.`];
            }

            // Protocol: Include the data of the donor species in the `ability` data slot.
            // Afterwards, we are going to reset the name to what the user intended.
            set.ability = `${set.ability}0${canonicalSource}`;
            return null;
        },
        onValidateTeam(team, format, teamHas) {
            // Donor Clause
            let evoFamilyLists = [];
            for (const set of team) {
                let abilitySources = (teamHas.abilitySources && teamHas.abilitySources[toID(set.species)]);
                if (!abilitySources) continue;
                // @ts-ignore
                evoFamilyLists.push(abilitySources.map(this.format.getEvoFamily));
            }

            // Checking actual full incompatibility would require expensive algebra.
            // Instead, we only check the trivial case of multiple Pokémon only legal for exactly one family. FIXME?
            let requiredFamilies = Object.create(null);
            for (const evoFamilies of evoFamilyLists) {
                if (evoFamilies.length !== 1) continue;
                let [familyId] = evoFamilies;
                if (!(familyId in requiredFamilies)) requiredFamilies[familyId] = 1;
                requiredFamilies[familyId]++;
                if (requiredFamilies[familyId] > 2) {
                    return [
                        `You are limited to up to two inheritances from each evolution family by the Donor Clause.`,
                        `(You inherit more than twice from ${this.dex.getSpecies(familyId).name}).`,
                    ];
                }
            }
        },
        onBegin() {
            for (const pokemon of this.getAllPokemon()) {
                if (pokemon.baseAbility.includes('0')) {
                    let donor = pokemon.baseAbility.split('0')[1];
                    pokemon.m.donor = toID(donor);
                    pokemon.baseAbility = toID(pokemon.baseAbility.split('0')[0]);
                    pokemon.ability = pokemon.baseAbility;
                }
            }
        },
        onSwitchIn(pokemon) {
            if (!pokemon.m.donor) return;
            let donorSpecies = this.dex.getSpecies(pokemon.m.donor);
            if (!donorSpecies.exists) return;
            // Place volatiles on the Pokémon to show the donor details.
            this.add('-start', pokemon, donorSpecies.name, '[silent]');
        },
    },
JavaScript:
    {
        name: "[Gen 8] Max Berries",
        desc: `All berries have their effects maximized.`,
        threads: [
            `&bullet; <a href="https://www.smogon.com/forums/threads/3677136/">Max Berries</a>`,
        ],

        mod: 'maxberries',
        ruleset: ['Standard', 'Dynamax Clause'],
        banlist: [
            'Calyrex-Ice', 'Calyrex-Shadow', 'Darmanitan-Galar', 'Dialga', 'Dracovish', 'Eternatus', 'Genesect', 'Giratina',
            'Groudon', 'Ho-Oh', 'Kyogre', 'Kyurem-Black', 'Kyurem-White', 'Landorus-Base', 'Lugia', 'Lunala', 'Marshadow',
            'Mewtwo', 'Naganadel', 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palkia', 'Pheromosa', 'Rayquaza', 'Reshiram',
            'Solgaleo', 'Urshifu-Base', 'Xerneas', 'Yveltal', 'Zacian', 'Zamazenta', 'Zekrom', 'Zygarde-Base',
            'Arena Trap', 'Moody', 'Power Construct', 'Shadow Tag', 'Jaboca Berry', 'Rowap Berry', 'Starf Berry', 'Baton Pass',
            'Block', 'Mean Look', 'Substitute',
        ],
    },
JavaScript:
const items: {[k: string]: ModdedItemData} = {
    chilanberry: {
        inherit: true,
        desc: "Blocks damage taken from a Normal-type attack. Single use.",
        onSourceModifyDamage(damage, source, target, move) {},
        onDamage(damage, target, source, effect) {
            const move = effect;
            if (!move || move.effectType !== 'Move') return;
            if (
                move.type === 'Normal' &&
                (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))
            ) {
                if (target.eatItem()) {
                    this.debug('-50% reduction');
                    this.add('-enditem', target, this.effect, '[weaken]');
                    return 0;
                }
            }
        },
    },
    enigmaberry: {
        inherit: true,
        desc: "Restores 100% max HP after holder is hit by a supereffective move. Single use.",
        onHit(target, source, move) {
            if (move && target.getMoveHitData(move).typeMod > 0) {
                if (target.eatItem()) {
                    this.heal(target.baseMaxhp);
                }
            }
        },
    },
    lansatberry: {
        inherit: true,
        desc: "Holder gains +4 Critical Hit ratio when at 1/4 max HP or less. Single use.",
        onEat(pokemon) {
            pokemon.addVolatile('lansatberry');
        },
        condition: {
            onStart(target, source, effect) {
                if (effect?.id === 'zpower') {
                    this.add('-start', target, 'move: Lansat Berry', '[zeffect]');
                } else if (effect && (['imposter', 'psychup', 'transform'].includes(effect.id))) {
                    this.add('-start', target, 'move: Lansat Berry', '[silent]');
                } else {
                    this.add('-start', target, 'move: Lansat Berry');
                }
            },
            onModifyCritRatio(critRatio) {
                return critRatio + 4;
            },
        },
    },
    leppaberry: {
        inherit: true,
        desc: "Restores the first of the holder's moves to reach 0 PP to maximum PP. Single use.",
        onEat(pokemon) {
            const moveSlot = pokemon.moveSlots.find(move => move.pp === 0) ||
                pokemon.moveSlots.find(move => move.pp < move.maxpp);
            if (!moveSlot) return;
            moveSlot.pp = moveSlot.maxpp;
            this.add('-activate', pokemon, 'item: Leppa Berry', moveSlot.move, '[consumed]');
        },
    },
    micleberry: {
        inherit: true,
        desc: "Holder's next move can't miss when at 1/4 max HP or less. Single use.",
        condition: {
            duration: 2,
            onSourceAccuracy(accuracy, target, source, move) {
                if (!move.ohko) {
                    this.add('-enditem', source, 'Micle Berry');
                    source.removeVolatile('micleberry');
                    if (typeof accuracy === 'number') {
                        return true;
                    }
                }
            },
        },
    },
    starfberry: {
        inherit: true,
        desc: "Raises a random stat by 12 when at 1/4 max HP or less (not acc/eva). Single use.",
        onEat(pokemon) {
            const stats: BoostName[] = [];
            let stat: BoostName;
            for (stat in pokemon.boosts) {
                if (stat !== 'accuracy' && stat !== 'evasion' && pokemon.boosts[stat] < 6) {
                    stats.push(stat);
                }
            }
            if (stats.length) {
                const randomStat = this.sample(stats);
                const boost: SparseBoostsTable = {};
                boost[randomStat] = 12;
                this.boost(boost);
            }
        },
    },
};

const berries = {
    heal: ['oran', 'sitrus'],
    supereffective: [
        ['occa', 'Fire'],
        ['passho', 'Water'],
        ['wacan', 'Electric'],
        ['rindo', 'Grass'],
        ['yache', 'Ice'],
        ['chople', 'Fighting'],
        ['kebia', 'Poison'],
        ['shuca', 'Ground'],
        ['coba', 'Flying'],
        ['payapa', 'Psychic'],
        ['tanga', 'Bug'],
        ['charti', 'Rock'],
        ['kasib', 'Ghost'],
        ['haban', 'Dragon'],
        ['colbur', 'Dark'],
        ['babiri', 'Steel'],
        ['roseli', 'Fairy'],
    ],
    pinchheal: [
        ['figy', 'Atk'],
        ['iapapa', 'Def'],
        ['wiki', 'SpA'],
        ['aguav', 'SpD'],
        ['Mago', 'Spe'],
    ],
    pinchstat: [
        ['liechi', 'atk', 'Attack'],
        ['ganlon', 'def', 'Defense'],
        ['petaya', 'spa', 'Sp. Atk'],
        ['apicot', 'spd', 'Sp. Def'],
        ['salac', 'spe', 'Speed'],
    ],
    keemaranga: [
        ['kee', 'def', 'Defense'],
        ['maranga', 'spd', 'Sp. Def'],
    ],
    jabocarowap: [
        ['jaboca', 'physical'],
        ['rowap', 'special'],
    ],
};

for (const berry of berries.heal) {
    items[`${berry}berry`] = {
        inherit: true,
        onEat(pokemon) {
            this.heal(pokemon.baseMaxhp);
        },
        desc: `Restores 100% max HP when at 1/2 max HP or less. Single use.`,
    };
}

for (const [berry, type] of berries.supereffective) {
    items[`${berry}berry`] = {
        inherit: true,
        desc: `Blocks damage taken from a supereffective ${type}-type attack. Single use.`,
        onSourceModifyDamage(damage, source, target, move) {},
        onDamage(damage, target, source, effect) {
            const move = effect;
            if (!move || move.effectType !== 'Move') return;
            if (move.type === type && target.getMoveHitData(move).typeMod > 0) {
                const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6);
                if (hitSub) return;

                if (target.eatItem()) {
                    this.debug('-100% reduction');
                    this.add('-enditem', target, this.effect, '[weaken]');
                    return 0;
                }
            }
        },
    };
}

for (const [berry, stat] of berries.pinchheal) {
    items[`${berry}berry`] = {
        inherit: true,
        desc: `Restores 100% max HP at 1/4 max HP or less; confuses if -${stat} Nature. Single use.`,
        onEat(pokemon) {
            this.heal(pokemon.baseMaxhp);
            if (pokemon.getNature().minus === this.toID(stat)) {
                pokemon.addVolatile('confusion');
            }
        },
    };
}

for (const [berry, stat, statName] of berries.pinchstat) {
    items[`${berry}berry`] = {
        inherit: true,
        desc: `Raises holder's ${statName} by 12 stages when at 1/4 max HP or less. Single use.`,
        onEat(pokemon) {
            this.boost({[stat]: 12});
        },
    };
}

for (const [berry, stat, statName] of berries.keemaranga) {
    items[`${berry}berry`] = {
        inherit: true,
        desc: `Raises holder's ${statName} by 12 stages after it is hit by a ${statName.startsWith('S') ? 'Special' : 'Physical'} attack. Single use.`,
        onEat(pokemon) {
            this.boost({[stat]: 12});
        },
    };
}

for (const [berry, category] of berries.jabocarowap) {
    items[`${berry}berry`] = {
        inherit: true,
        desc: `If holder is hit by a ${category} move, attacker loses 100% of its HP. Single use.`,
        onDamagingHit(damage, target, source, move) {
            if (move.category === category[0].toUpperCase() + category.slice(1)) {
                if (target.eatItem()) {
                    this.damage(source.baseMaxhp, source, target);
                }
            }
        },
    };
}

export const Items = items;
config/formats.js
JavaScript:
    {
        name: "[Gen 8] Pacifistmons",
        desc: `Pok&eacute;mon can only use status moves. Recovery moves are banned.`,
        threads: [
            `&bullet; <a href="https://www.smogon.com/forums/threads/3658719/">Pacifistmons</a>`,
        ],

        mod: 'gen8',
        ruleset: ['[Gen 8] Ubers'],
        banlist: [
            'Magic Bounce', 'Magic Guard', 'Neutralizing Gas', 'Regenerator', 'Assault Vest',
            'Ingrain', 'Life Dew', 'move:Metronome', 'Moonlight', 'Morning Sun', 'Nature Power', 'Purify', 'Recover',
            'Rest', 'Roost', 'Slack Off', 'Soft-Boiled', 'Strength Sap', 'Swallow', 'Synthesis', 'Taunt', 'Wish',
        ],
        onValidateSet(set) {
            if (set.moves) {
                for (const moveid of set.moves) {
                    const move = this.dex.getMove(moveid);
                    if (move.category !== "Status") {
                        return [`${set.species}'s move ${move.name} is banned.`, `(Non-Status moves are banned.)`];
                    }
                }
            }
            if (set.level !== 100) return [`${set.species} must be Level 100.`];
        },
    },
config/formats.js
JavaScript:
    {
        name: "[Gen 8] Trademarked",
        desc: `Sacrifice your Pok&eacute;mon's ability for a status move that activates on switch-in.`,
        threads: [
            `&bullet; <a href="https://www.smogon.com/forums/threads/3656980/">Trademarked</a>`,
        ],

        mod: 'gen8',
        ruleset: ['[Gen 8] OU'],
        banlist: [],
        restricted: [
            'Baneful Bunker', 'Block', 'Copycat', 'Detect', 'Destiny Bond', 'Ingrain', 'King\'s Shield', 'Mean Look', 'Metronome', 'Obstruct',
            'Octolock', 'Nature Power', 'Parting Shot', 'Protect', 'Roar', 'Skill Swap', 'Sleep Talk', 'Spiky Shield', 'Teleport', 'Whirlwind', 'Wish',
        ],
        onValidateTeam(team, format, teamHas) {
            for (const trademark in teamHas.trademarks) {
                if (teamHas.trademarks[trademark] > 1) return [`You are limited to 1 of each Trademark.`, `(You have ${teamHas.trademarks[trademark]} of ${trademark}).`];
            }
        },
        validateSet(set, teamHas) {
            const restrictedMoves = (this.format.restricted || []).concat('Yawn');
            const dex = this.dex;
            let ability = dex.getMove(set.ability);
            if (ability.category !== 'Status' || ability.status === 'slp' || restrictedMoves.includes(ability.name) || set.moves.map(toID).includes(ability.id)) return this.validateSet(set, teamHas);
            let customRules = this.format.customRules || [];
            if (!customRules.includes('!obtainableabilities')) customRules.push('!obtainableabilities');
            const TeamValidator = /** @type {new(format: string | Format) => TeamValidator} */ (this.constructor);
            const validator = new TeamValidator(dex.getFormat(`${this.format.id}@@@${customRules.join(',')}`));
            const moves = set.moves;
            set.moves = [ability.id];
            set.ability = dex.getTemplate(set.species).abilities['0'];
            let problems = validator.validateSet(set, {}) || [];
            if (problems.length) return problems;
            set.moves = moves;
            set.ability = dex.getTemplate(set.species).abilities['0'];
            problems = problems.concat(validator.validateSet(set, teamHas) || []);
            set.ability = ability.id;
            if (!teamHas.trademarks) teamHas.trademarks = {};
            teamHas.trademarks[ability.name] = (teamHas.trademarks[ability.name] || 0) + 1;
            return problems.length ? problems : null;
        },
        pokemon: {
            getAbility() {
                const move = this.battle.dex.getMove(toID(this.ability));
                if (!move.exists) return Object.getPrototypeOf(this).getAbility.call(this);
                return {
                    id: move.id,
                    name: move.name,
                    onStart(pokemon) {
                        this.add('-activate', pokemon, 'ability: ' + move.name);
                        this.useMove(move, pokemon);
                    },
                    toString() {
                        return "";
                    },
                };
            },
        },
    },
config/formats.js
JavaScript:
    {
        name: "[Gen 8] Scalemons",
        desc: `Every Pok&eacute;mon's stats, barring HP, are scaled to give them a BST as close to 600 as possible.`,
        threads: [
            `&bullet; <a href="https://www.smogon.com/forums/threads/3658482/">Scalemons</a>`,
        ],

        mod: 'gen8',
        ruleset: ['[Gen 8] Ubers', 'Dynamax Clause', 'Scalemons Mod'],
        banlist: ['Darmanitan-Galar', 'Gastly', 'Arena Trap', 'Drizzle', 'Drought', 'Huge Power', 'Shadow Tag', 'Baton Pass', 'Eviolite', 'Light Ball'],
    },

OMs that are available on Pokemon Showdown! via a ladder or challenge mode will not have their code displayed on this thread.

Also, useful references:

The code provided in this thread is written to work with the main Pokemon Showdown repository. If your code does not work, it is your responsibility to either adjust the code to work on your server. An alternative is just updating your server's code.
 
Last edited:

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

Top