From 647ebea024252d808cb8ab9e085e682bcf1f3c01 Mon Sep 17 00:00:00 2001 From: livid washed <115855253+livid-washed@users.noreply.github.com> Date: Wed, 3 Jan 2024 17:32:32 +1100 Subject: [PATCH] Revamp [Gen 3] Random Battle (#10056) * Set up framework for Gen 3 rands * Fixing code from other gens, tests, incompatibilities * Get Multiple Preferred types working * Berry Sweeper * STAB enforcement * Abilities * Abilities pt 2 * Items * more abilities * Removing teamgen limits (council decisions) * oops * simplify tests * Remove unnecessary PType * Fuse unnecessary set split * HP EVs finetuning * More HP EVs fiddling * role tinkering * Remove isDoubles from past gens code * Remove lingering code from gen 4 * improve tests * Psychic on Staller Lugia * Update data/mods/gen3/random-teams.ts --------- Co-authored-by: Kris Johnson <11083252+KrisXV@users.noreply.github.com> --- data/mods/gen2/random-teams.ts | 6 + data/mods/gen3/random-data.json | 882 ------- data/mods/gen3/random-sets.json | 2734 ++++++++++++++++++++ data/mods/gen3/random-teams.ts | 949 +++---- data/mods/gen4/random-teams.ts | 58 +- data/mods/gen5/random-teams.ts | 55 +- data/mods/gen6/random-teams.ts | 57 +- data/mods/gen7/random-teams.ts | 65 +- data/mods/gen8/random-teams.ts | 8 +- data/mods/gen8bdsp/random-teams.ts | 2 + server/chat-plugins/randombattles/index.ts | 6 +- sim/global-types.ts | 4 +- test/random-battles/all-gens.js | 2 +- test/random-battles/gen3.js | 102 +- test/random-battles/gen4.js | 11 +- test/random-battles/gen5.js | 11 +- test/random-battles/gen6.js | 4 +- test/random-battles/gen7.js | 4 +- 18 files changed, 3435 insertions(+), 1525 deletions(-) delete mode 100644 data/mods/gen3/random-data.json create mode 100644 data/mods/gen3/random-sets.json diff --git a/data/mods/gen2/random-teams.ts b/data/mods/gen2/random-teams.ts index 6e07dd9a3074..c5b28a378557 100644 --- a/data/mods/gen2/random-teams.ts +++ b/data/mods/gen2/random-teams.ts @@ -2,11 +2,17 @@ import RandomGen3Teams from '../gen3/random-teams'; import {PRNG, PRNGSeed} from '../../../sim/prng'; import type {MoveCounter, OldRandomBattleSpecies} from '../gen8/random-teams'; +// Moves that shouldn't be the only STAB moves: +const NO_STAB = [ + 'explosion', 'icywind', 'machpunch', 'pursuit', 'quickattack', 'reversal', 'selfdestruct', +]; + export class RandomGen2Teams extends RandomGen3Teams { randomData: {[species: string]: OldRandomBattleSpecies} = require('./random-data.json'); constructor(format: string | Format, prng: PRNG | PRNGSeed | null) { super(format, prng); + this.noStab = NO_STAB; this.moveEnforcementCheckers = { Electric: (movePool, moves, abilities, types, counter) => !counter.get('Electric'), Fire: (movePool, moves, abilities, types, counter) => !counter.get('Fire'), diff --git a/data/mods/gen3/random-data.json b/data/mods/gen3/random-data.json deleted file mode 100644 index 9f93de337801..000000000000 --- a/data/mods/gen3/random-data.json +++ /dev/null @@ -1,882 +0,0 @@ -{ - "venusaur": { - "level": 82, - "moves": ["curse", "earthquake", "hiddenpowerrock", "leechseed", "sleeppowder", "sludgebomb", "swordsdance", "synthesis"] - }, - "charizard": { - "level": 80, - "moves": ["bellydrum", "dragondance", "earthquake", "fireblast", "hiddenpowerflying", "substitute"] - }, - "blastoise": { - "level": 83, - "moves": ["earthquake", "icebeam", "mirrorcoat", "rest", "roar", "sleeptalk", "surf", "toxic"] - }, - "butterfree": { - "level": 95, - "moves": ["gigadrain", "hiddenpowerfire", "morningsun", "psychic", "sleeppowder", "stunspore", "toxic"] - }, - "beedrill": { - "level": 91, - "moves": ["brickbreak", "doubleedge", "endure", "hiddenpowerbug", "sludgebomb", "swordsdance"] - }, - "pidgeot": { - "level": 87, - "moves": ["aerialace", "hiddenpowerground", "quickattack", "return", "substitute", "toxic"] - }, - "raticate": { - "level": 88, - "moves": ["endeavor", "hiddenpowerground", "quickattack", "return", "reversal", "shadowball", "substitute"] - }, - "fearow": { - "level": 85, - "moves": ["agility", "batonpass", "drillpeck", "hiddenpowerground", "quickattack", "return", "substitute"] - }, - "arbok": { - "level": 87, - "moves": ["doubleedge", "earthquake", "hiddenpowerfire", "rest", "rockslide", "sleeptalk", "sludgebomb"] - }, - "pikachu": { - "level": 88, - "moves": ["hiddenpowerice", "substitute", "surf", "thunderbolt"] - }, - "raichu": { - "level": 84, - "moves": ["encore", "focuspunch", "hiddenpowergrass", "hiddenpowerice", "substitute", "surf", "thunderbolt", "thunderwave"] - }, - "sandslash": { - "level": 84, - "moves": ["earthquake", "hiddenpowerbug", "rapidspin", "rockslide", "swordsdance", "toxic"] - }, - "nidoqueen": { - "level": 82, - "moves": ["earthquake", "fireblast", "icebeam", "shadowball", "sludgebomb", "superpower"] - }, - "nidoking": { - "level": 81, - "moves": ["earthquake", "fireblast", "icebeam", "megahorn", "sludgebomb", "substitute", "thunderbolt"] - }, - "clefable": { - "level": 84, - "moves": ["calmmind", "counter", "icebeam", "return", "shadowball", "softboiled", "thunderbolt", "thunderwave"] - }, - "ninetales": { - "level": 82, - "moves": ["fireblast", "flamethrower", "hiddenpowergrass", "hypnosis", "substitute", "toxic", "willowisp"] - }, - "wigglytuff": { - "level": 90, - "moves": ["fireblast", "icebeam", "protect", "return", "thunderbolt", "toxic", "wish"] - }, - "vileplume": { - "level": 85, - "moves": ["aromatherapy", "hiddenpowerfire", "sleeppowder", "sludgebomb", "solarbeam", "sunnyday", "synthesis"] - }, - "parasect": { - "level": 93, - "moves": ["aromatherapy", "gigadrain", "hiddenpowerbug", "return", "spore", "stunspore", "swordsdance"] - }, - "venomoth": { - "level": 88, - "moves": ["batonpass", "hiddenpowerground", "signalbeam", "sleeppowder", "sludgebomb", "substitute"] - }, - "dugtrio": { - "level": 81, - "moves": ["aerialace", "earthquake", "hiddenpowerbug", "rockslide", "substitute"] - }, - "persian": { - "level": 88, - "moves": ["fakeout", "hiddenpowerground", "hypnosis", "irontail", "return", "shadowball", "substitute"] - }, - "golduck": { - "level": 81, - "moves": ["calmmind", "hiddenpowergrass", "hydropump", "hypnosis", "icebeam", "substitute", "surf"] - }, - "primeape": { - "level": 85, - "moves": ["bulkup", "crosschop", "earthquake", "hiddenpowerghost", "rockslide", "substitute"] - }, - "arcanine": { - "level": 81, - "moves": ["extremespeed", "fireblast", "flamethrower", "hiddenpowergrass", "rest", "sleeptalk", "toxic"] - }, - "poliwrath": { - "level": 84, - "moves": ["brickbreak", "bulkup", "hiddenpowerghost", "hydropump", "hypnosis", "icebeam", "substitute"] - }, - "alakazam": { - "level": 78, - "moves": ["calmmind", "encore", "firepunch", "icepunch", "psychic", "recover", "substitute"] - }, - "machamp": { - "level": 84, - "moves": ["bulkup", "crosschop", "earthquake", "hiddenpowerghost", "rest", "rockslide", "sleeptalk"] - }, - "victreebel": { - "level": 84, - "moves": ["hiddenpowerfire", "sleeppowder", "sludgebomb", "solarbeam", "sunnyday"] - }, - "tentacruel": { - "level": 82, - "moves": ["gigadrain", "haze", "hydropump", "icebeam", "rapidspin", "surf", "toxic"] - }, - "golem": { - "level": 84, - "moves": ["doubleedge", "earthquake", "explosion", "hiddenpowerbug", "rockslide", "toxic"] - }, - "rapidash": { - "level": 82, - "moves": ["fireblast", "hiddenpowergrass", "hiddenpowerrock", "substitute", "toxic"] - }, - "slowbro": { - "level": 82, - "moves": ["calmmind", "fireblast", "icebeam", "psychic", "rest", "sleeptalk", "surf", "thunderwave"] - }, - "magneton": { - "level": 84, - "moves": ["hiddenpowergrass", "hiddenpowerice", "rest", "sleeptalk", "thunderbolt", "toxic"] - }, - "farfetchd": { - "level": 97, - "moves": ["agility", "batonpass", "hiddenpowerflying", "return", "swordsdance"] - }, - "dodrio": { - "level": 81, - "moves": ["drillpeck", "flail", "hiddenpowerground", "quickattack", "return", "substitute"] - }, - "dewgong": { - "level": 88, - "moves": ["encore", "hiddenpowergrass", "icebeam", "rest", "sleeptalk", "surf", "toxic"] - }, - "muk": { - "level": 84, - "moves": ["brickbreak", "curse", "explosion", "fireblast", "hiddenpowerghost", "rest", "sludgebomb"] - }, - "cloyster": { - "level": 81, - "moves": ["explosion", "icebeam", "rapidspin", "spikes", "surf", "toxic"] - }, - "gengar": { - "level": 76, - "moves": ["destinybond", "explosion", "firepunch", "hypnosis", "icepunch", "substitute", "thunderbolt", "willowisp"] - }, - "hypno": { - "level": 86, - "moves": ["batonpass", "calmmind", "firepunch", "hypnosis", "protect", "psychic", "toxic", "wish"] - }, - "kingler": { - "level": 90, - "moves": ["doubleedge", "hiddenpowerghost", "hiddenpowerground", "surf", "swordsdance"] - }, - "electrode": { - "level": 84, - "moves": ["explosion", "hiddenpowergrass", "hiddenpowerice", "substitute", "thunderbolt", "thunderwave", "toxic"] - }, - "exeggutor": { - "level": 82, - "moves": ["explosion", "gigadrain", "hiddenpowerfire", "hiddenpowerice", "leechseed", "psychic", "sleeppowder", "solarbeam", "sunnyday"] - }, - "marowak": { - "level": 82, - "moves": ["bonemerang", "doubleedge", "earthquake", "rockslide", "swordsdance"] - }, - "hitmonlee": { - "level": 85, - "moves": ["bulkup", "earthquake", "hiddenpowerghost", "highjumpkick", "machpunch", "rockslide", "substitute"] - }, - "hitmonchan": { - "level": 87, - "moves": ["bulkup", "earthquake", "hiddenpowerghost", "machpunch", "rapidspin", "skyuppercut", "toxic"] - }, - "lickitung": { - "level": 91, - "moves": ["counter", "healbell", "protect", "return", "seismictoss", "toxic", "wish"] - }, - "weezing": { - "level": 82, - "moves": ["explosion", "fireblast", "haze", "painsplit", "sludgebomb", "toxic", "willowisp"] - }, - "rhydon": { - "level": 84, - "moves": ["doubleedge", "earthquake", "megahorn", "rockslide", "substitute", "swordsdance"] - }, - "tangela": { - "level": 92, - "moves": ["hiddenpowergrass", "leechseed", "morningsun", "sleeppowder", "stunspore"] - }, - "kangaskhan": { - "level": 79, - "moves": ["earthquake", "fakeout", "focuspunch", "rest", "return", "shadowball", "substitute", "toxic"] - }, - "seaking": { - "level": 90, - "moves": ["hiddenpowergrass", "hydropump", "icebeam", "megahorn", "raindance"] - }, - "starmie": { - "level": 75, - "moves": ["hydropump", "icebeam", "psychic", "recover", "surf", "thunderbolt"] - }, - "mrmime": { - "level": 84, - "moves": ["barrier", "batonpass", "calmmind", "encore", "firepunch", "hypnosis", "psychic", "substitute", "thunderbolt"] - }, - "scyther": { - "level": 81, - "moves": ["aerialace", "batonpass", "hiddenpowerground", "hiddenpowerrock", "quickattack", "silverwind", "swordsdance"] - }, - "jynx": { - "level": 81, - "moves": ["calmmind", "hiddenpowerfire", "icebeam", "lovelykiss", "psychic", "substitute"] - }, - "electabuzz": { - "level": 83, - "moves": ["crosschop", "firepunch", "focuspunch", "hiddenpowergrass", "icepunch", "substitute", "thunderbolt"] - }, - "magmar": { - "level": 84, - "moves": ["crosschop", "fireblast", "flamethrower", "hiddenpowergrass", "psychic", "substitute", "thunderpunch"] - }, - "pinsir": { - "level": 82, - "moves": ["earthquake", "hiddenpowerbug", "return", "rockslide", "swordsdance"] - }, - "tauros": { - "level": 77, - "moves": ["doubleedge", "earthquake", "hiddenpowerghost", "hiddenpowerrock", "return"] - }, - "gyarados": { - "level": 74, - "moves": ["doubleedge", "dragondance", "earthquake", "hiddenpowerflying", "hydropump"] - }, - "lapras": { - "level": 80, - "moves": ["healbell", "icebeam", "rest", "sleeptalk", "surf", "thunderbolt", "toxic"] - }, - "ditto": { - "level": 100, - "moves": ["transform"] - }, - "vaporeon": { - "level": 80, - "moves": ["icebeam", "protect", "surf", "toxic", "wish"] - }, - "jolteon": { - "level": 79, - "moves": ["batonpass", "hiddenpowerice", "substitute", "thunderbolt", "toxic", "wish"] - }, - "flareon": { - "level": 88, - "moves": ["doubleedge", "fireblast", "hiddenpowergrass", "protect", "shadowball", "toxic", "wish"] - }, - "omastar": { - "level": 84, - "moves": ["hiddenpowergrass", "hydropump", "icebeam", "raindance", "spikes", "surf"] - }, - "kabutops": { - "level": 84, - "moves": ["brickbreak", "doubleedge", "hiddenpowerground", "rockslide", "surf", "swordsdance"] - }, - "aerodactyl": { - "level": 76, - "moves": ["doubleedge", "earthquake", "hiddenpowerflying", "rockslide", "substitute"] - }, - "snorlax": { - "level": 73, - "moves": ["bodyslam", "curse", "earthquake", "rest", "return", "selfdestruct", "shadowball", "sleeptalk"] - }, - "articuno": { - "level": 80, - "moves": ["healbell", "hiddenpowerfire", "icebeam", "protect", "rest", "roar", "sleeptalk", "toxic"] - }, - "zapdos": { - "level": 76, - "moves": ["agility", "batonpass", "hiddenpowerice", "substitute", "thunderbolt", "thunderwave", "toxic"] - }, - "moltres": { - "level": 78, - "moves": ["fireblast", "flamethrower", "hiddenpowergrass", "morningsun", "substitute", "toxic", "willowisp"] - }, - "dragonite": { - "level": 78, - "moves": ["doubleedge", "dragondance", "earthquake", "flamethrower", "healbell", "hiddenpowerflying", "icebeam", "substitute"] - }, - "mewtwo": { - "level": 66, - "moves": ["calmmind", "flamethrower", "icebeam", "psychic", "recover", "substitute", "thunderbolt"] - }, - "mew": { - "level": 73, - "moves": ["calmmind", "explosion", "flamethrower", "icebeam", "psychic", "softboiled", "thunderbolt", "thunderwave", "transform"] - }, - "meganium": { - "level": 84, - "moves": ["bodyslam", "hiddenpowergrass", "leechseed", "synthesis", "toxic"] - }, - "typhlosion": { - "level": 79, - "moves": ["fireblast", "flamethrower", "focuspunch", "hiddenpowergrass", "hiddenpowerice", "substitute", "thunderpunch"] - }, - "feraligatr": { - "level": 83, - "moves": ["earthquake", "hiddenpowerflying", "hydropump", "rockslide", "swordsdance"] - }, - "furret": { - "level": 90, - "moves": ["doubleedge", "quickattack", "return", "reversal", "shadowball", "substitute", "trick"] - }, - "noctowl": { - "level": 92, - "moves": ["hiddenpowerfire", "hypnosis", "psychic", "reflect", "toxic", "whirlwind"] - }, - "ledian": { - "level": 100, - "moves": ["agility", "batonpass", "lightscreen", "reflect", "silverwind", "swordsdance", "toxic"] - }, - "ariados": { - "level": 95, - "moves": ["agility", "batonpass", "signalbeam", "sludgebomb", "spiderweb", "toxic"] - }, - "crobat": { - "level": 82, - "moves": ["aerialace", "haze", "hiddenpowerground", "shadowball", "sludgebomb", "taunt", "toxic"] - }, - "lanturn": { - "level": 82, - "moves": ["confuseray", "icebeam", "rest", "sleeptalk", "surf", "thunderbolt", "thunderwave", "toxic"] - }, - "togetic": { - "level": 92, - "moves": ["charm", "encore", "flamethrower", "seismictoss", "softboiled", "thunderwave", "toxic"] - }, - "xatu": { - "level": 84, - "moves": ["batonpass", "calmmind", "hiddenpowerfire", "psychic", "reflect", "wish"] - }, - "ampharos": { - "level": 82, - "moves": ["firepunch", "healbell", "hiddenpowergrass", "hiddenpowerice", "thunderbolt", "toxic"] - }, - "bellossom": { - "level": 88, - "moves": ["hiddenpowergrass", "leechseed", "moonlight", "sleeppowder", "sludgebomb", "stunspore"] - }, - "azumarill": { - "level": 87, - "moves": ["brickbreak", "encore", "hiddenpowerghost", "hydropump", "return"] - }, - "sudowoodo": { - "level": 92, - "moves": ["brickbreak", "doubleedge", "earthquake", "explosion", "rockslide", "toxic"] - }, - "politoed": { - "level": 84, - "moves": ["hiddenpowergrass", "hypnosis", "icebeam", "rest", "surf", "toxic"] - }, - "jumpluff": { - "level": 85, - "moves": ["encore", "hiddenpowerflying", "leechseed", "sleeppowder", "substitute", "toxic"] - }, - "aipom": { - "level": 91, - "moves": ["batonpass", "doubleedge", "focuspunch", "shadowball", "substitute", "thunderwave"] - }, - "sunflora": { - "level": 95, - "moves": ["hiddenpowerfire", "leechseed", "razorleaf", "synthesis", "toxic"] - }, - "yanma": { - "level": 90, - "moves": ["hiddenpowerflying", "hypnosis", "reversal", "shadowball", "substitute"] - }, - "quagsire": { - "level": 85, - "moves": ["counter", "curse", "earthquake", "hiddenpowerrock", "icebeam", "rest", "surf", "toxic"] - }, - "espeon": { - "level": 78, - "moves": ["batonpass", "calmmind", "hiddenpowerfire", "morningsun", "psychic", "reflect"] - }, - "umbreon": { - "level": 85, - "moves": ["batonpass", "hiddenpowerdark", "protect", "toxic", "wish"] - }, - "murkrow": { - "level": 90, - "moves": ["doubleedge", "drillpeck", "hiddenpowerfighting", "hiddenpowerground", "shadowball", "substitute"] - }, - "slowking": { - "level": 84, - "moves": ["calmmind", "flamethrower", "icebeam", "psychic", "rest", "sleeptalk", "surf", "thunderwave"] - }, - "misdreavus": { - "level": 85, - "moves": ["calmmind", "hiddenpowerice", "meanlook", "perishsong", "protect", "substitute", "thunderbolt", "toxic"] - }, - "unown": { - "level": 100, - "moves": ["hiddenpowerpsychic"] - }, - "wobbuffet": { - "level": 81, - "moves": ["counter", "destinybond", "encore", "mirrorcoat"] - }, - "girafarig": { - "level": 86, - "moves": ["agility", "batonpass", "calmmind", "psychic", "substitute", "thunderbolt", "thunderwave", "wish"] - }, - "forretress": { - "level": 81, - "moves": ["earthquake", "explosion", "hiddenpowerbug", "rapidspin", "spikes", "toxic"] - }, - "dunsparce": { - "level": 88, - "moves": ["bodyslam", "curse", "headbutt", "rest", "rockslide", "shadowball", "thunderwave"] - }, - "gligar": { - "level": 84, - "moves": ["earthquake", "hiddenpowerflying", "irontail", "quickattack", "rockslide", "substitute", "swordsdance"] - }, - "steelix": { - "level": 83, - "moves": ["doubleedge", "earthquake", "explosion", "hiddenpowerrock", "irontail", "rest", "roar", "toxic"] - }, - "granbull": { - "level": 83, - "moves": ["bulkup", "earthquake", "healbell", "overheat", "rest", "return", "shadowball", "thunderwave"] - }, - "qwilfish": { - "level": 85, - "moves": ["destinybond", "hydropump", "selfdestruct", "shadowball", "sludgebomb", "spikes", "swordsdance"] - }, - "scizor": { - "level": 82, - "moves": ["agility", "batonpass", "hiddenpowerground", "hiddenpowerrock", "morningsun", "silverwind", "steelwing", "swordsdance"] - }, - "shuckle": { - "level": 98, - "moves": ["encore", "rest", "toxic", "wrap"] - }, - "heracross": { - "level": 80, - "moves": ["brickbreak", "focuspunch", "megahorn", "rest", "rockslide", "sleeptalk", "substitute", "swordsdance"] - }, - "sneasel": { - "level": 87, - "moves": ["brickbreak", "doubleedge", "hiddenpowerflying", "shadowball", "substitute", "swordsdance"] - }, - "ursaring": { - "level": 81, - "moves": ["earthquake", "focuspunch", "hiddenpowerghost", "return", "swordsdance"] - }, - "magcargo": { - "level": 95, - "moves": ["fireblast", "hiddenpowergrass", "rest", "sleeptalk", "toxic", "yawn"] - }, - "piloswine": { - "level": 88, - "moves": ["doubleedge", "earthquake", "icebeam", "protect", "rockslide", "toxic"] - }, - "corsola": { - "level": 95, - "moves": ["calmmind", "icebeam", "recover", "surf", "toxic"] - }, - "octillery": { - "level": 87, - "moves": ["fireblast", "hiddenpowergrass", "icebeam", "rockblast", "surf", "thunderwave"] - }, - "delibird": { - "level": 95, - "moves": ["aerialace", "focuspunch", "hiddenpowerground", "icebeam", "quickattack"] - }, - "mantine": { - "level": 85, - "moves": ["haze", "hiddenpowergrass", "icebeam", "raindance", "rest", "sleeptalk", "surf", "toxic"] - }, - "skarmory": { - "level": 80, - "moves": ["drillpeck", "hiddenpowerground", "protect", "rest", "sleeptalk", "spikes", "toxic", "whirlwind"] - }, - "houndoom": { - "level": 81, - "moves": ["crunch", "fireblast", "flamethrower", "hiddenpowergrass", "pursuit", "willowisp"] - }, - "kingdra": { - "level": 81, - "moves": ["hiddenpowergrass", "hydropump", "icebeam", "raindance", "substitute", "surf"] - }, - "donphan": { - "level": 83, - "moves": ["earthquake", "rapidspin", "rest", "rockslide", "sleeptalk", "toxic"] - }, - "porygon2": { - "level": 81, - "moves": ["icebeam", "recover", "return", "thunderbolt", "thunderwave", "toxic"] - }, - "stantler": { - "level": 84, - "moves": ["earthquake", "hypnosis", "return", "shadowball", "thunderbolt"] - }, - "smeargle": { - "level": 87, - "moves": ["encore", "explosion", "spikes", "spore"] - }, - "hitmontop": { - "level": 85, - "moves": ["bulkup", "earthquake", "hiddenpowerghost", "highjumpkick", "machpunch", "rockslide", "toxic"] - }, - "miltank": { - "level": 78, - "moves": ["bodyslam", "curse", "earthquake", "healbell", "milkdrink", "toxic"] - }, - "blissey": { - "level": 78, - "moves": ["aromatherapy", "calmmind", "icebeam", "seismictoss", "softboiled", "thunderbolt", "thunderwave", "toxic"] - }, - "raikou": { - "level": 74, - "moves": ["calmmind", "crunch", "hiddenpowergrass", "hiddenpowerice", "rest", "sleeptalk", "substitute", "thunderbolt"] - }, - "entei": { - "level": 80, - "moves": ["bodyslam", "calmmind", "fireblast", "flamethrower", "hiddenpowergrass", "hiddenpowerice", "solarbeam", "substitute", "sunnyday"] - }, - "suicune": { - "level": 75, - "moves": ["calmmind", "icebeam", "rest", "sleeptalk", "substitute", "surf", "toxic"] - }, - "tyranitar": { - "level": 75, - "moves": ["dragondance", "earthquake", "fireblast", "focuspunch", "hiddenpowerbug", "icebeam", "pursuit", "rockslide", "substitute"] - }, - "lugia": { - "level": 70, - "moves": ["aeroblast", "calmmind", "earthquake", "icebeam", "recover", "substitute", "thunderbolt", "toxic"] - }, - "hooh": { - "level": 70, - "moves": ["calmmind", "earthquake", "recover", "sacredfire", "substitute", "thunderbolt", "toxic"] - }, - "celebi": { - "level": 75, - "moves": ["batonpass", "calmmind", "healbell", "hiddenpowergrass", "leechseed", "psychic", "recover"] - }, - "sceptile": { - "level": 82, - "moves": ["focuspunch", "hiddenpowerice", "leafblade", "leechseed", "substitute", "thunderpunch"] - }, - "blaziken": { - "level": 82, - "moves": ["endure", "fireblast", "hiddenpowerice", "reversal", "rockslide", "skyuppercut", "swordsdance", "thunderpunch"] - }, - "swampert": { - "level": 79, - "moves": ["earthquake", "hydropump", "icebeam", "protect", "rest", "rockslide", "sleeptalk", "surf", "toxic"] - }, - "mightyena": { - "level": 91, - "moves": ["crunch", "doubleedge", "healbell", "hiddenpowerfighting", "protect", "shadowball", "toxic"] - }, - "linoone": { - "level": 85, - "moves": ["bellydrum", "extremespeed", "flail", "hiddenpowerground", "shadowball", "substitute"] - }, - "beautifly": { - "level": 100, - "moves": ["hiddenpowerbug", "hiddenpowerflying", "morningsun", "stunspore", "substitute", "toxic"] - }, - "dustox": { - "level": 94, - "moves": ["hiddenpowerground", "moonlight", "sludgebomb", "toxic", "whirlwind"] - }, - "ludicolo": { - "level": 83, - "moves": ["hiddenpowergrass", "icebeam", "leechseed", "raindance", "substitute", "surf"] - }, - "shiftry": { - "level": 87, - "moves": ["brickbreak", "explosion", "shadowball", "swordsdance"] - }, - "swellow": { - "level": 81, - "moves": ["aerialace", "doubleedge", "hiddenpowerfighting", "hiddenpowerground", "quickattack", "return"] - }, - "pelipper": { - "level": 88, - "moves": ["icebeam", "protect", "rest", "sleeptalk", "surf", "toxic"] - }, - "gardevoir": { - "level": 79, - "moves": ["calmmind", "firepunch", "hypnosis", "psychic", "substitute", "thunderbolt", "willowisp"] - }, - "masquerain": { - "level": 94, - "moves": ["hydropump", "icebeam", "stunspore", "substitute", "toxic"] - }, - "breloom": { - "level": 85, - "moves": ["focuspunch", "hiddenpowerghost", "hiddenpowerrock", "leechseed", "machpunch", "skyuppercut", "spore", "substitute", "swordsdance"] - }, - "vigoroth": { - "level": 86, - "moves": ["brickbreak", "bulkup", "earthquake", "return", "shadowball", "slackoff"] - }, - "slaking": { - "level": 79, - "moves": ["doubleedge", "earthquake", "focuspunch", "return", "shadowball"] - }, - "ninjask": { - "level": 84, - "moves": ["aerialace", "batonpass", "protect", "silverwind", "substitute", "swordsdance"] - }, - "shedinja": { - "level": 95, - "moves": ["agility", "batonpass", "hiddenpowerground", "shadowball", "silverwind", "toxic"] - }, - "exploud": { - "level": 84, - "moves": ["earthquake", "flamethrower", "icebeam", "overheat", "return", "shadowball", "substitute"] - }, - "hariyama": { - "level": 83, - "moves": ["bulkup", "crosschop", "fakeout", "hiddenpowerghost", "rest", "rockslide", "sleeptalk"] - }, - "nosepass": { - "level": 96, - "moves": ["earthquake", "explosion", "rockslide", "thunderwave", "toxic"] - }, - "delcatty": { - "level": 93, - "moves": ["batonpass", "doubleedge", "healbell", "thunderwave", "wish"] - }, - "sableye": { - "level": 90, - "moves": ["knockoff", "recover", "seismictoss", "shadowball", "toxic"] - }, - "mawile": { - "level": 94, - "moves": ["batonpass", "brickbreak", "focuspunch", "hiddenpowersteel", "rockslide", "substitute", "swordsdance", "toxic"] - }, - "aggron": { - "level": 84, - "moves": ["doubleedge", "earthquake", "focuspunch", "irontail", "rockslide", "substitute", "thunderwave", "toxic"] - }, - "medicham": { - "level": 83, - "moves": ["brickbreak", "bulkup", "recover", "rockslide", "shadowball", "substitute"] - }, - "manectric": { - "level": 82, - "moves": ["crunch", "hiddenpowergrass", "hiddenpowerice", "substitute", "thunderbolt", "thunderwave"] - }, - "plusle": { - "level": 88, - "moves": ["agility", "batonpass", "encore", "hiddenpowergrass", "substitute", "thunderbolt", "toxic"] - }, - "minun": { - "level": 89, - "moves": ["batonpass", "encore", "hiddenpowerice", "lightscreen", "substitute", "thunderbolt", "wish"] - }, - "volbeat": { - "level": 93, - "moves": ["batonpass", "icepunch", "tailglow", "thunderbolt"] - }, - "illumise": { - "level": 95, - "moves": ["batonpass", "encore", "icepunch", "substitute", "thunderwave", "wish"] - }, - "roselia": { - "level": 93, - "moves": ["aromatherapy", "gigadrain", "hiddenpowerfire", "spikes", "stunspore", "synthesis"] - }, - "swalot": { - "level": 89, - "moves": ["encore", "explosion", "hiddenpowerground", "icebeam", "sludgebomb", "toxic", "yawn"] - }, - "sharpedo": { - "level": 85, - "moves": ["crunch", "earthquake", "endure", "hiddenpowerflying", "hydropump", "icebeam", "return"] - }, - "wailord": { - "level": 87, - "moves": ["hiddenpowergrass", "icebeam", "rest", "selfdestruct", "sleeptalk", "surf", "toxic"] - }, - "camerupt": { - "level": 85, - "moves": ["earthquake", "explosion", "fireblast", "rest", "rockslide", "sleeptalk", "toxic"] - }, - "torkoal": { - "level": 90, - "moves": ["explosion", "fireblast", "flamethrower", "hiddenpowergrass", "rest", "toxic", "yawn"] - }, - "grumpig": { - "level": 84, - "moves": ["calmmind", "firepunch", "icywind", "psychic", "substitute", "taunt"] - }, - "spinda": { - "level": 95, - "moves": ["bodyslam", "encore", "focuspunch", "shadowball", "substitute", "teeterdance", "toxic"] - }, - "flygon": { - "level": 79, - "moves": ["dragonclaw", "earthquake", "fireblast", "hiddenpowerbug", "rockslide", "substitute", "toxic"] - }, - "cacturne": { - "level": 92, - "moves": ["focuspunch", "hiddenpowerdark", "leechseed", "needlearm", "spikes", "substitute", "thunderpunch"] - }, - "altaria": { - "level": 85, - "moves": ["dragonclaw", "dragondance", "earthquake", "fireblast", "flamethrower", "haze", "hiddenpowerflying", "rest", "toxic"] - }, - "zangoose": { - "level": 80, - "moves": ["brickbreak", "quickattack", "return", "shadowball", "swordsdance"] - }, - "seviper": { - "level": 88, - "moves": ["crunch", "earthquake", "flamethrower", "hiddenpowergrass", "sludgebomb"] - }, - "lunatone": { - "level": 84, - "moves": ["batonpass", "calmmind", "explosion", "hypnosis", "icebeam", "psychic"] - }, - "solrock": { - "level": 85, - "moves": ["earthquake", "explosion", "overheat", "reflect", "rockslide", "shadowball"] - }, - "whiscash": { - "level": 86, - "moves": ["earthquake", "hiddenpowerbug", "icebeam", "rest", "rockslide", "sleeptalk", "spark", "surf", "toxic"] - }, - "crawdaunt": { - "level": 88, - "moves": ["brickbreak", "crunch", "doubleedge", "hiddenpowerghost", "icebeam", "surf"] - }, - "claydol": { - "level": 81, - "moves": ["earthquake", "explosion", "icebeam", "psychic", "rapidspin", "toxic"] - }, - "cradily": { - "level": 84, - "moves": ["barrier", "earthquake", "hiddenpowergrass", "mirrorcoat", "recover", "rockslide", "toxic"] - }, - "armaldo": { - "level": 82, - "moves": ["doubleedge", "earthquake", "hiddenpowerbug", "rockslide", "swordsdance"] - }, - "milotic": { - "level": 78, - "moves": ["icebeam", "mirrorcoat", "recover", "surf", "toxic"] - }, - "castform": { - "level": 90, - "moves": ["flamethrower", "icebeam", "return", "substitute", "thunderbolt", "thunderwave"] - }, - "kecleon": { - "level": 91, - "moves": ["brickbreak", "return", "shadowball", "thunderwave", "trick"] - }, - "banette": { - "level": 88, - "moves": ["destinybond", "endure", "hiddenpowerfighting", "knockoff", "shadowball", "willowisp"] - }, - "dusclops": { - "level": 86, - "moves": ["focuspunch", "icebeam", "painsplit", "rest", "shadowball", "sleeptalk", "substitute", "willowisp"] - }, - "tropius": { - "level": 94, - "moves": ["hiddenpowerfire", "solarbeam", "sunnyday", "synthesis"] - }, - "chimecho": { - "level": 89, - "moves": ["calmmind", "healbell", "hiddenpowerfire", "lightscreen", "psychic", "reflect", "toxic", "yawn"] - }, - "absol": { - "level": 88, - "moves": ["batonpass", "hiddenpowerfighting", "quickattack", "shadowball", "swordsdance"] - }, - "glalie": { - "level": 83, - "moves": ["earthquake", "explosion", "icebeam", "spikes", "toxic"] - }, - "walrein": { - "level": 81, - "moves": ["encore", "hiddenpowergrass", "icebeam", "rest", "sleeptalk", "surf", "toxic"] - }, - "huntail": { - "level": 88, - "moves": ["doubleedge", "hiddenpowergrass", "hydropump", "icebeam", "raindance", "surf"] - }, - "gorebyss": { - "level": 85, - "moves": ["hiddenpowerelectric", "hiddenpowergrass", "hydropump", "icebeam", "raindance", "surf"] - }, - "relicanth": { - "level": 88, - "moves": ["doubleedge", "earthquake", "hiddenpowerflying", "rest", "rockslide", "sleeptalk", "toxic"] - }, - "luvdisc": { - "level": 99, - "moves": ["icebeam", "protect", "substitute", "surf", "sweetkiss", "toxic"] - }, - "salamence": { - "level": 73, - "moves": ["brickbreak", "dragondance", "earthquake", "fireblast", "hiddenpowerflying", "rockslide"] - }, - "metagross": { - "level": 74, - "moves": ["agility", "earthquake", "explosion", "meteormash", "psychic", "rockslide"] - }, - "regirock": { - "level": 82, - "moves": ["curse", "earthquake", "explosion", "rest", "rockslide", "superpower", "thunderwave"] - }, - "regice": { - "level": 79, - "moves": ["explosion", "icebeam", "rest", "sleeptalk", "thunderbolt", "thunderwave", "toxic"] - }, - "registeel": { - "level": 78, - "moves": ["rest", "seismictoss", "sleeptalk", "toxic"] - }, - "latias": { - "level": 67, - "moves": ["calmmind", "dragonclaw", "hiddenpowerfire", "psychic", "recover"] - }, - "latios": { - "level": 66, - "moves": ["calmmind", "dragonclaw", "hiddenpowerfire", "psychic", "recover"] - }, - "kyogre": { - "level": 67, - "moves": ["calmmind", "icebeam", "rest", "sleeptalk", "surf", "thunder"] - }, - "groudon": { - "level": 70, - "moves": ["earthquake", "hiddenpowerbug", "overheat", "rockslide", "substitute", "swordsdance", "thunderwave"] - }, - "rayquaza": { - "level": 72, - "moves": ["dragondance", "earthquake", "extremespeed", "hiddenpowerflying", "rockslide"] - }, - "jirachi": { - "level": 74, - "moves": ["bodyslam", "calmmind", "firepunch", "icepunch", "protect", "psychic", "substitute", "thunderbolt", "wish"] - }, - "deoxys": { - "level": 74, - "moves": ["extremespeed", "firepunch", "icebeam", "psychoboost", "shadowball", "superpower"] - }, - "deoxysattack": { - "level": 73, - "moves": ["extremespeed", "firepunch", "psychoboost", "shadowball", "superpower"] - }, - "deoxysdefense": { - "level": 75, - "moves": ["recover", "seismictoss", "spikes", "taunt", "toxic"] - }, - "deoxysspeed": { - "level": 76, - "moves": ["calmmind", "icebeam", "psychic", "recover", "spikes", "taunt", "toxic"] - } -} diff --git a/data/mods/gen3/random-sets.json b/data/mods/gen3/random-sets.json new file mode 100644 index 000000000000..ba6b3ef8d20b --- /dev/null +++ b/data/mods/gen3/random-sets.json @@ -0,0 +1,2734 @@ +{ + "venusaur": { + "level": 82, + "sets": [ + { + "role": "Staller", + "movepool": ["hiddenpowergrass", "leechseed", "sleeppowder", "sludgebomb", "substitute"] + }, + { + "role": "Setup Sweeper", + "movepool": ["earthquake", "hiddenpowerghost", "sleeppowder", "sludgebomb", "swordsdance", "synthesis"], + "preferredTypes": ["Ground"] + } + ] + }, + "charizard": { + "level": 80, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["dragondance", "earthquake", "fireblast", "hiddenpowerflying", "rockslide"], + "preferredTypes": ["Ground"] + }, + { + "role": "Setup Sweeper", + "movepool": ["bellydrum", "earthquake", "hiddenpowerflying", "rockslide", "substitute"], + "preferredTypes": ["Ground"] + } + ] + }, + "blastoise": { + "level": 83, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["icebeam", "rest", "sleeptalk", "surf", "toxic"] + }, + { + "role": "Bulky Attacker", + "movepool": ["icebeam", "rapidspin", "refresh", "roar", "surf", "toxic"] + }, + { + "role": "Staller", + "movepool": ["icebeam", "protect", "refresh", "surf", "toxic"] + } + ] + }, + "butterfree": { + "level": 95, + "sets": [ + { + "role": "Generalist", + "movepool": ["hiddenpowerfire", "morningsun", "psychic", "sleeppowder", "stunspore", "toxic"], + "preferredTypes": ["Psychic"] + } + ] + }, + "beedrill": { + "level": 91, + "sets": [ + { + "role": "Berry Sweeper", + "movepool": ["brickbreak", "endure", "hiddenpowerbug", "sludgebomb", "swordsdance"], + "preferredTypes": ["Bug"] + }, + { + "role": "Fast Attacker", + "movepool": ["brickbreak", "doubleedge", "hiddenpowerbug", "sludgebomb", "swordsdance"] + } + ] + }, + "pidgeot": { + "level": 87, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["aerialace", "doubleedge", "hiddenpowerground", "quickattack", "return", "toxic"], + "preferredTypes": ["Ground"] + }, + { + "role": "Berry Sweeper", + "movepool": ["aerialace", "hiddenpowerground", "return", "substitute"] + } + ] + }, + "raticate": { + "level": 88, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "hiddenpowerground", "quickattack", "return", "shadowball"] + }, + { + "role": "Fast Attacker", + "movepool": ["doubleedge", "facade", "hiddenpowerground", "return", "shadowball"] + }, + { + "role": "Berry Sweeper", + "movepool": ["return", "reversal", "shadowball", "substitute"] + } + ] + }, + "fearow": { + "level": 85, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["batonpass", "doubleedge", "drillpeck", "hiddenpowerground", "quickattack", "return"], + "preferredTypes": ["Ground"] + } + ] + }, + "arbok": { + "level": 87, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["earthquake", "hiddenpowerghost", "rest", "rockslide", "sleeptalk", "sludgebomb"], + "preferredTypes": ["Ground"] + } + ] + }, + "pikachu": { + "level": 88, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["encore", "hiddenpowerice", "substitute", "surf", "thunderbolt"], + "preferredTypes": ["Ice", "Water"] + }, + { + "role": "Wallbreaker", + "movepool": ["hiddenpowerice", "surf", "thunderbolt", "volttackle"] + } + ] + }, + "raichu": { + "level": 84, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["encore", "hiddenpowerice", "surf", "thunderbolt", "thunderwave", "toxic"], + "preferredTypes": ["Ice"] + }, + { + "role": "Berry Sweeper", + "movepool": ["hiddenpowerice", "substitute", "surf", "thunderbolt"] + } + ] + }, + "sandslash": { + "level": 84, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["earthquake", "hiddenpowerbug", "rapidspin", "rockslide", "swordsdance", "toxic"], + "preferredTypes": ["Rock"] + } + ] + }, + "nidoqueen": { + "level": 82, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["earthquake", "fireblast", "icebeam", "shadowball", "sludgebomb", "substitute", "thunderbolt"] + } + ] + }, + "nidoking": { + "level": 81, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["earthquake", "fireblast", "icebeam", "megahorn", "shadowball", "sludgebomb", "substitute", "thunderbolt"] + } + ] + }, + "clefable": { + "level": 84, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["fireblast", "return", "shadowball", "softboiled", "thunderwave", "toxic"] + }, + { + "role": "Bulky Setup", + "movepool": ["calmmind", "icebeam", "softboiled", "thunderbolt"] + } + ] + }, + "ninetales": { + "level": 82, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["fireblast", "flamethrower", "hiddenpowergrass", "hypnosis", "substitute", "toxic", "willowisp"] + } + ] + }, + "wigglytuff": { + "level": 90, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["bodyslam", "fireblast", "protect", "wish"] + }, + { + "role": "Bulky Support", + "movepool": ["doubleedge", "protect", "thunderwave", "toxic", "wish"] + }, + { + "role": "Staller", + "movepool": ["protect", "seismictoss", "toxic", "wish"] + } + ] + }, + "vileplume": { + "level": 85, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["aromatherapy", "hiddenpowerfire", "hiddenpowergrass", "sleeppowder", "sludgebomb", "synthesis"] + } + ] + }, + "parasect": { + "level": 93, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["aromatherapy", "gigadrain", "hiddenpowerbug", "return", "spore", "stunspore"], + "preferredTypes": ["Normal"] + } + ] + }, + "venomoth": { + "level": 88, + "sets": [ + { + "role": "Generalist", + "movepool": ["batonpass", "hiddenpowerfire", "psychic", "signalbeam", "sleeppowder", "sludgebomb", "substitute"] + }, + { + "role": "Bulky Support", + "movepool": ["hiddenpowerfire", "psychic", "signalbeam", "sleeppowder", "sludgebomb"] + } + ] + }, + "dugtrio": { + "level": 81, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["earthquake", "hiddenpowerbug", "rockslide", "sludgebomb"] + } + ] + }, + "persian": { + "level": 88, + "sets": [ + { + "role": "Berry Sweeper", + "movepool": ["hiddenpowerground", "irontail", "return", "shadowball", "substitute"] + }, + { + "role": "Fast Attacker", + "movepool": ["hiddenpowerground", "hypnosis", "irontail", "return", "shadowball"] + } + ] + }, + "golduck": { + "level": 81, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["calmmind", "hiddenpowerelectric", "hiddenpowergrass", "hydropump", "hypnosis", "icebeam", "substitute", "surf"], + "preferredTypes": ["Ice"] + } + ] + }, + "primeape": { + "level": 85, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["bulkup", "crosschop", "earthquake", "hiddenpowerghost", "rockslide"] + }, + { + "role": "Setup Sweeper", + "movepool": ["bulkup", "crosschop", "hiddenpowerghost", "rockslide", "substitute"] + } + ] + }, + "arcanine": { + "level": 81, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["flamethrower", "hiddenpowergrass", "rest", "sleeptalk", "toxic"] + }, + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "extremespeed", "fireblast", "hiddenpowerrock", "irontail"], + "preferredTypes": ["Steel"] + }, + { + "role": "Staller", + "movepool": ["flamethrower", "hiddenpowergrass", "hiddenpowerrock", "protect", "toxic"] + } + ] + }, + "poliwrath": { + "level": 84, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["brickbreak", "bulkup", "earthquake", "hiddenpowerghost", "hydropump", "hypnosis", "substitute"] + }, + { + "role": "Bulky Attacker", + "movepool": ["brickbreak", "hiddenpowerghost", "hydropump", "hypnosis", "icebeam", "rest", "sleeptalk", "toxic"] + }, + { + "role": "Generalist", + "movepool": ["focuspunch", "hydropump", "icebeam", "substitute", "toxic"] + } + ] + }, + "alakazam": { + "level": 78, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["calmmind", "encore", "firepunch", "icepunch", "psychic", "recover", "substitute", "thunderpunch"], + "preferredTypes": ["Fire"] + } + ] + }, + "machamp": { + "level": 84, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["bulkup", "crosschop", "earthquake", "hiddenpowerghost", "rockslide"] + }, + { + "role": "Bulky Attacker", + "movepool": ["crosschop", "hiddenpowerghost", "rest", "rockslide", "sleeptalk"] + } + ] + }, + "victreebel": { + "level": 84, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["hiddenpowerfire", "sludgebomb", "solarbeam", "sunnyday"] + }, + { + "role": "Bulky Attacker", + "movepool": ["hiddenpowerground", "magicalleaf", "sleeppowder", "sludgebomb", "synthesis"], + "preferredTypes": ["Ground"] + }, + { + "role": "Setup Sweeper", + "movepool": ["hiddenpowerground", "sleeppowder", "sludgebomb", "swordsdance", "synthesis"] + } + ] + }, + "tentacruel": { + "level": 82, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["hydropump", "icebeam", "rapidspin", "sludgebomb", "surf", "toxic"] + } + ] + }, + "golem": { + "level": 84, + "sets": [ + { + "role": "Staller", + "movepool": ["earthquake", "protect", "rockslide", "toxic"] + }, + { + "role": "Bulky Attacker", + "movepool": ["doubleedge", "earthquake", "explosion", "hiddenpowerbug", "rockslide", "toxic"] + } + ] + }, + "rapidash": { + "level": 82, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["fireblast", "hiddenpowergrass", "hiddenpowerrock", "substitute", "toxic"] + } + ] + }, + "slowbro": { + "level": 82, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["fireblast", "icebeam", "psychic", "rest", "sleeptalk", "surf", "thunderwave", "toxic"] + }, + { + "role": "Setup Sweeper", + "movepool": ["calmmind", "psychic", "rest", "surf"] + }, + { + "role": "Bulky Setup", + "movepool": ["calmmind", "rest", "sleeptalk", "surf"] + } + ] + }, + "magneton": { + "level": 84, + "sets": [ + { + "role": "Staller", + "movepool": ["hiddenpowerice", "protect", "thunderbolt", "toxic"] + }, + { + "role": "Bulky Attacker", + "movepool": ["hiddenpowerice", "rest", "sleeptalk", "thunderbolt"] + } + ] + }, + "farfetchd": { + "level": 97, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["agility", "batonpass", "return", "swordsdance"] + } + ] + }, + "dodrio": { + "level": 81, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["batonpass", "doubleedge", "drillpeck", "hiddenpowerground", "quickattack", "return"], + "preferredTypes": ["Ground"] + }, + { + "role": "Berry Sweeper", + "movepool": ["drillpeck", "flail", "hiddenpowerground", "quickattack", "substitute"] + } + ] + }, + "dewgong": { + "level": 88, + "sets": [ + { + "role": "Staller", + "movepool": ["icebeam", "protect", "surf", "toxic"] + }, + { + "role": "Bulky Attacker", + "movepool": ["encore", "icebeam", "rest", "sleeptalk", "surf", "toxic"] + } + ] + }, + "muk": { + "level": 84, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["brickbreak", "explosion", "fireblast", "haze", "hiddenpowerghost", "rest", "sludgebomb", "toxic"] + }, + { + "role": "Setup Sweeper", + "movepool": ["brickbreak", "curse", "hiddenpowerghost", "rest", "sludgebomb"] + } + ] + }, + "cloyster": { + "level": 81, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["explosion", "icebeam", "rapidspin", "spikes", "surf", "toxic"] + } + ] + }, + "gengar": { + "level": 76, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["destinybond", "explosion", "firepunch", "icepunch", "substitute", "thunderbolt", "willowisp"], + "preferredTypes": ["Electric", "Ice"] + } + ] + }, + "hypno": { + "level": 86, + "sets": [ + { + "role": "Bulky Setup", + "movepool": ["batonpass", "calmmind", "firepunch", "protect", "psychic", "wish"] + }, + { + "role": "Bulky Support", + "movepool": ["batonpass", "firepunch", "protect", "psychic", "wish"] + }, + { + "role": "Staller", + "movepool": ["protect", "seismictoss", "toxic", "wish"] + } + ] + }, + "kingler": { + "level": 90, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["doubleedge", "hiddenpowerghost", "hiddenpowerground", "surf", "swordsdance"] + } + ] + }, + "electrode": { + "level": 84, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["explosion", "hiddenpowerice", "substitute", "thunderbolt", "toxic"] + } + ] + }, + "exeggutor": { + "level": 82, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["gigadrain", "hiddenpowerfire", "psychic", "sleeppowder", "stunspore", "synthesis"] + }, + { + "role": "Wallbreaker", + "movepool": ["explosion", "gigadrain", "hiddenpowerfire", "leechseed", "psychic", "sleeppowder", "stunspore", "substitute"] + }, + { + "role": "Setup Sweeper", + "movepool": ["hiddenpowerfire", "psychic", "solarbeam", "sunnyday"] + } + ] + }, + "marowak": { + "level": 82, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["doubleedge", "earthquake", "rockslide", "swordsdance"] + } + ] + }, + "hitmonlee": { + "level": 85, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["bulkup", "earthquake", "hiddenpowerghost", "highjumpkick", "machpunch", "rockslide"], + "preferredTypes": ["Ghost"] + }, + { + "role": "Berry Sweeper", + "movepool": ["earthquake", "hiddenpowerghost", "reversal", "rockslide", "substitute"], + "preferredTypes": ["Ghost"] + } + ] + }, + "hitmonchan": { + "level": 87, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["bulkup", "earthquake", "hiddenpowerghost", "machpunch", "rapidspin", "rockslide", "skyuppercut", "toxic"], + "preferredTypes": ["Ghost"] + } + ] + }, + "lickitung": { + "level": 91, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["bodyslam", "flamethrower", "protect", "wish"] + }, + { + "role": "Bulky Support", + "movepool": ["healbell", "knockoff", "protect", "seismictoss", "wish"] + }, + { + "role": "Staller", + "movepool": ["protect", "seismictoss", "toxic", "wish"] + } + ] + }, + "weezing": { + "level": 82, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["explosion", "fireblast", "haze", "painsplit", "sludgebomb", "toxic", "willowisp"] + } + ] + }, + "rhydon": { + "level": 84, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["earthquake", "megahorn", "rockslide", "substitute", "swordsdance"] + }, + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "earthquake", "megahorn", "rockslide"] + } + ] + }, + "tangela": { + "level": 92, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["hiddenpowergrass", "leechseed", "morningsun", "sleeppowder", "stunspore", "toxic"] + }, + { + "role": "Setup Sweeper", + "movepool": ["hiddenpowerfire", "morningsun", "sleeppowder", "solarbeam", "sunnyday"] + } + ] + }, + "kangaskhan": { + "level": 79, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["doubleedge", "earthquake", "rest", "return", "shadowball", "toxic"], + "preferredTypes": ["Ground"] + }, + { + "role": "Bulky Attacker", + "movepool": ["bodyslam", "earthquake", "protect", "return", "wish"] + } + ] + }, + "seaking": { + "level": 90, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["hiddenpowerelectric", "hiddenpowergrass", "hydropump", "icebeam", "megahorn", "raindance"] + } + ] + }, + "starmie": { + "level": 75, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["hydropump", "icebeam", "psychic", "recover", "surf", "thunderbolt"] + } + ] + }, + "mrmime": { + "level": 84, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["batonpass", "calmmind", "encore", "firepunch", "hypnosis", "psychic", "substitute", "thunderbolt"] + } + ] + }, + "scyther": { + "level": 81, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["aerialace", "batonpass", "hiddenpowerground", "silverwind", "swordsdance"], + "preferredTypes": ["Ground"] + } + ] + }, + "jynx": { + "level": 81, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["calmmind", "hiddenpowerfire", "icebeam", "lovelykiss", "psychic", "substitute"] + } + ] + }, + "electabuzz": { + "level": 83, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["crosschop", "firepunch", "focuspunch", "hiddenpowergrass", "icepunch", "substitute", "thunderbolt"], + "preferredTypes": ["Ice"] + }, + { + "role": "Berry Sweeper", + "movepool": ["firepunch", "hiddenpowergrass", "icepunch", "substitute", "thunderbolt"], + "preferredTypes": ["Ice"] + } + ] + }, + "magmar": { + "level": 84, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["crosschop", "fireblast", "flamethrower", "focuspunch", "hiddenpowergrass", "hiddenpowerice", "psychic", "substitute", "thunderpunch"], + "preferredTypes": ["Electric"] + }, + { + "role": "Berry Sweeper", + "movepool": ["fireblast", "flamethrower", "hiddenpowergrass", "hiddenpowerice", "psychic", "substitute", "thunderpunch"], + "preferredTypes": ["Electric"] + } + ] + }, + "pinsir": { + "level": 82, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["earthquake", "hiddenpowerbug", "rockslide", "swordsdance"] + }, + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "earthquake", "hiddenpowerbug", "rockslide"] + } + ] + }, + "tauros": { + "level": 77, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "earthquake", "hiddenpowerghost", "return"] + } + ] + }, + "gyarados": { + "level": 74, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "dragondance", "earthquake", "hiddenpowerflying", "hydropump"], + "preferredTypes": ["Ground"] + }, + { + "role": "Setup Sweeper", + "movepool": ["doubleedge", "dragondance", "earthquake", "hiddenpowerflying", "substitute"], + "preferredTypes": ["Ground"] + } + ] + }, + "lapras": { + "level": 80, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["healbell", "icebeam", "rest", "sleeptalk", "surf", "thunderbolt", "toxic"] + }, + { + "role": "Fast Attacker", + "movepool": ["healbell", "icebeam", "rest", "sleeptalk", "thunderbolt", "toxic"] + } + ] + }, + "ditto": { + "level": 100, + "sets": [ + { + "role": "Generalist", + "movepool": ["transform"] + } + ] + }, + "vaporeon": { + "level": 80, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["icebeam", "protect", "surf", "toxic", "wish"] + } + ] + }, + "jolteon": { + "level": 79, + "sets": [ + { + "role": "Staller", + "movepool": ["hiddenpowerice", "protect", "thunderbolt", "toxic"] + }, + { + "role": "Fast Attacker", + "movepool": ["batonpass", "hiddenpowerice", "substitute", "thunderbolt"] + } + ] + }, + "flareon": { + "level": 88, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["flamethrower", "hiddenpowergrass", "protect", "toxic", "wish"] + }, + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "fireblast", "hiddenpowergrass", "hiddenpowerrock", "irontail", "shadowball", "toxic"] + } + ] + }, + "omastar": { + "level": 84, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["hiddenpowergrass", "hydropump", "icebeam", "spikes", "surf"] + }, + { + "role": "Setup Sweeper", + "movepool": ["hiddenpowergrass", "hydropump", "icebeam", "raindance", "surf"] + } + ] + }, + "kabutops": { + "level": 84, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["brickbreak", "hiddenpowerflying", "rockslide", "surf", "swordsdance"] + } + ] + }, + "aerodactyl": { + "level": 76, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["doubleedge", "earthquake", "hiddenpowerflying", "rockslide"] + }, + { + "role": "Berry Sweeper", + "movepool": ["earthquake", "hiddenpowerflying", "rockslide", "substitute"] + } + ] + }, + "snorlax": { + "level": 73, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["bodyslam", "earthquake", "return", "selfdestruct", "shadowball"] + }, + { + "role": "Bulky Support", + "movepool": ["bodyslam", "curse", "rest", "sleeptalk"] + }, + { + "role": "Bulky Setup", + "movepool": ["bodyslam", "curse", "earthquake", "rest"] + } + ] + }, + "articuno": { + "level": 80, + "sets": [ + { + "role": "Staller", + "movepool": ["healbell", "hiddenpowerfire", "icebeam", "protect", "toxic"] + }, + { + "role": "Bulky Attacker", + "movepool": ["hiddenpowerfire", "icebeam", "rest", "sleeptalk"] + } + ] + }, + "zapdos": { + "level": 76, + "sets": [ + { + "role": "Staller", + "movepool": ["hiddenpowerice", "protect", "thunderbolt", "toxic"] + }, + { + "role": "Fast Attacker", + "movepool": ["batonpass", "hiddenpowerice", "substitute", "thunderbolt", "thunderwave", "toxic"] + }, + { + "role": "Bulky Attacker", + "movepool": ["hiddenpowerice", "rest", "sleeptalk", "thunderbolt"] + } + ] + }, + "moltres": { + "level": 78, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["fireblast", "flamethrower", "hiddenpowergrass", "morningsun", "substitute", "toxic", "willowisp"] + } + ] + }, + "dragonite": { + "level": 78, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["doubleedge", "dragondance", "earthquake", "healbell", "hiddenpowerflying", "rest", "substitute"], + "preferredTypes": ["Ground"] + }, + { + "role": "Wallbreaker", + "movepool": ["brickbreak", "doubleedge", "earthquake", "fireblast", "hiddenpowerflying"], + "preferredTypes": ["Ground"] + } + ] + }, + "mewtwo": { + "level": 66, + "sets": [ + { + "role": "Bulky Setup", + "movepool": ["calmmind", "flamethrower", "psychic", "recover"] + }, + { + "role": "Setup Sweeper", + "movepool": ["calmmind", "flamethrower", "icebeam", "psychic", "thunderbolt"], + "preferredTypes": ["Electric"] + } + ] + }, + "mew": { + "level": 73, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["explosion", "flamethrower", "psychic", "softboiled", "thunderwave", "transform"] + }, + { + "role": "Bulky Setup", + "movepool": ["calmmind", "flamethrower", "psychic", "softboiled", "thunderbolt"] + }, + { + "role": "Setup Sweeper", + "movepool": ["brickbreak", "earthquake", "explosion", "rockslide", "softboiled", "swordsdance"], + "preferredTypes": ["Ground", "Rock"] + } + ] + }, + "meganium": { + "level": 84, + "sets": [ + { + "role": "Staller", + "movepool": ["bodyslam", "earthquake", "hiddenpowergrass", "leechseed", "synthesis", "toxic"] + }, + { + "role": "Bulky Setup", + "movepool": ["bodyslam", "earthquake", "hiddenpowerrock", "swordsdance", "synthesis"], + "preferredTypes": ["Ground"] + } + ] + }, + "typhlosion": { + "level": 79, + "sets": [ + { + "role": "Berry Sweeper", + "movepool": ["fireblast", "flamethrower", "hiddenpowerice", "substitute", "thunderpunch"] + }, + { + "role": "Fast Attacker", + "movepool": ["earthquake", "fireblast", "flamethrower", "focuspunch", "hiddenpowerice", "substitute", "thunderpunch", "toxic"], + "preferredTypes": ["Electric"] + } + ] + }, + "feraligatr": { + "level": 83, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["earthquake", "hiddenpowerflying", "hydropump", "rockslide", "swordsdance"] + } + ] + }, + "furret": { + "level": 90, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["brickbreak", "doubleedge", "quickattack", "return", "shadowball"] + }, + { + "role": "Fast Attacker", + "movepool": ["brickbreak", "doubleedge", "return", "shadowball", "trick"] + }, + { + "role": "Berry Sweeper", + "movepool": ["return", "reversal", "shadowball", "substitute"] + } + ] + }, + "noctowl": { + "level": 92, + "sets": [ + { + "role": "Staller", + "movepool": ["hiddenpowerfire", "hypnosis", "return", "toxic", "whirlwind"] + } + ] + }, + "ledian": { + "level": 100, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["agility", "batonpass", "silverwind", "swordsdance"] + }, + { + "role": "Generalist", + "movepool": ["batonpass", "silverwind", "substitute", "swordsdance"] + } + ] + }, + "ariados": { + "level": 95, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["agility", "batonpass", "signalbeam", "sludgebomb"] + }, + { + "role": "Bulky Support", + "movepool": ["batonpass", "signalbeam", "sludgebomb", "spiderweb", "toxic"], + "preferredTypes": ["Bug"] + }, + { + "role": "Bulky Setup", + "movepool": ["agility", "batonpass", "sludgebomb", "spiderweb"] + } + ] + }, + "crobat": { + "level": 82, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["aerialace", "haze", "hiddenpowerground", "shadowball", "sludgebomb", "toxic"], + "preferredTypes": ["Ground"] + } + ] + }, + "lanturn": { + "level": 82, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["icebeam", "rest", "sleeptalk", "surf", "thunderbolt", "toxic"] + } + ] + }, + "togetic": { + "level": 92, + "sets": [ + { + "role": "Staller", + "movepool": ["charm", "encore", "flamethrower", "seismictoss", "softboiled", "thunderwave", "toxic"] + } + ] + }, + "xatu": { + "level": 84, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["batonpass", "calmmind", "gigadrain", "hiddenpowerfire", "psychic"], + "preferredTypes": ["Fire"] + }, + { + "role": "Bulky Attacker", + "movepool": ["hiddenpowerfire", "protect", "psychic", "wish"] + }, + { + "role": "Bulky Support", + "movepool": ["protect", "psychic", "thunderwave", "toxic", "wish"] + } + ] + }, + "ampharos": { + "level": 82, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["firepunch", "healbell", "hiddenpowerice", "thunderbolt", "toxic"], + "preferredTypes": ["Ice"] + } + ] + }, + "bellossom": { + "level": 88, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["hiddenpowerfire", "leechseed", "magicalleaf", "moonlight", "sleeppowder", "stunspore"] + } + ] + }, + "azumarill": { + "level": 87, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["brickbreak", "doubleedge", "hiddenpowerghost", "hydropump", "return"] + }, + { + "role": "Bulky Support", + "movepool": ["brickbreak", "encore", "hiddenpowerghost", "hydropump", "rest", "return", "sleeptalk"], + "preferredTypes": ["Normal"] + } + ] + }, + "sudowoodo": { + "level": 92, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["brickbreak", "doubleedge", "earthquake", "explosion", "rockslide", "toxic"], + "preferredTypes": ["Ground"] + } + ] + }, + "politoed": { + "level": 84, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["encore", "hiddenpowergrass", "hypnosis", "icebeam", "rest", "surf", "toxic"], + "preferredTypes": ["Ice"] + }, + { + "role": "Staller", + "movepool": ["encore", "icebeam", "protect", "surf", "toxic"] + } + ] + }, + "jumpluff": { + "level": 85, + "sets": [ + { + "role": "Generalist", + "movepool": ["encore", "hiddenpowerflying", "sleeppowder", "synthesis", "toxic"] + }, + { + "role": "Staller", + "movepool": ["hiddenpowerflying", "leechseed", "protect", "substitute"] + } + ] + }, + "aipom": { + "level": 91, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["brickbreak", "focuspunch", "return", "shadowball", "substitute", "thunderwave", "toxic"], + "preferredTypes": ["Ghost"] + }, + { + "role": "Generalist", + "movepool": ["batonpass", "brickbreak", "return", "shadowball", "substitute", "thunderwave", "toxic"] + }, + { + "role": "Wallbreaker", + "movepool": ["batonpass", "brickbreak", "doubleedge", "return", "shadowball"] + } + ] + }, + "sunflora": { + "level": 95, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["hiddenpowerfire", "leechseed", "razorleaf", "synthesis", "toxic"] + } + ] + }, + "yanma": { + "level": 90, + "sets": [ + { + "role": "Berry Sweeper", + "movepool": ["hiddenpowerflying", "hypnosis", "reversal", "shadowball", "substitute"] + }, + { + "role": "Fast Attacker", + "movepool": ["aerialace", "doubleedge", "hiddenpowerground", "hypnosis", "signalbeam", "toxic"], + "preferredTypes": ["Ground"] + } + ] + }, + "quagsire": { + "level": 85, + "sets": [ + { + "role": "Staller", + "movepool": ["earthquake", "icebeam", "protect", "toxic"] + }, + { + "role": "Bulky Attacker", + "movepool": ["earthquake", "icebeam", "rest", "sleeptalk", "surf", "toxic"] + } + ] + }, + "espeon": { + "level": 78, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["batonpass", "calmmind", "hiddenpowerfire", "morningsun", "psychic", "substitute"] + } + ] + }, + "umbreon": { + "level": 85, + "sets": [ + { + "role": "Staller", + "movepool": ["hiddenpowerfire", "hiddenpowerground", "protect", "toxic", "wish"] + }, + { + "role": "Bulky Support", + "movepool": ["batonpass", "protect", "toxic", "wish"] + }, + { + "role": "Generalist", + "movepool": ["batonpass", "meanlook", "moonlight", "toxic"] + } + ] + }, + "murkrow": { + "level": 90, + "sets": [ + { + "role": "Berry Sweeper", + "movepool": ["drillpeck", "hiddenpowerfighting", "hiddenpowerground", "shadowball", "substitute"] + }, + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "drillpeck", "hiddenpowerfighting", "hiddenpowerground", "shadowball"] + }, + { + "role": "Bulky Attacker", + "movepool": ["drillpeck", "hiddenpowerfighting", "hiddenpowerground", "shadowball", "thunderwave", "toxic"] + } + ] + }, + "slowking": { + "level": 84, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["fireblast", "icebeam", "psychic", "rest", "sleeptalk", "surf", "thunderwave", "toxic"] + }, + { + "role": "Setup Sweeper", + "movepool": ["calmmind", "psychic", "rest", "surf"] + }, + { + "role": "Bulky Setup", + "movepool": ["calmmind", "rest", "sleeptalk", "surf"] + } + ] + }, + "misdreavus": { + "level": 85, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["hiddenpowerice", "painsplit", "shadowball", "thunderbolt", "thunderwave", "toxic"] + }, + { + "role": "Staller", + "movepool": ["meanlook", "perishsong", "protect", "shadowball"] + }, + { + "role": "Setup Sweeper", + "movepool": ["calmmind", "hiddenpowerice", "substitute", "thunderbolt"] + } + ] + }, + "unown": { + "level": 100, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["hiddenpowerpsychic"] + }, + { + "role": "Wallbreaker", + "movepool": ["hiddenpowerbug", "hiddenpowerfighting"] + } + ] + }, + "wobbuffet": { + "level": 81, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["counter", "destinybond", "encore", "mirrorcoat"] + } + ] + }, + "girafarig": { + "level": 86, + "sets": [ + { + "role": "Bulky Setup", + "movepool": ["batonpass", "calmmind", "crunch", "protect", "psychic", "substitute", "thunderbolt", "wish"] + }, + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "earthquake", "protect", "psychic", "return", "shadowball", "thunderbolt", "thunderwave", "toxic", "wish"] + } + ] + }, + "forretress": { + "level": 81, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["earthquake", "explosion", "hiddenpowerbug", "hiddenpowersteel", "rapidspin", "spikes", "toxic"] + } + ] + }, + "dunsparce": { + "level": 88, + "sets": [ + { + "role": "Bulky Setup", + "movepool": ["bodyslam", "curse", "earthquake", "rest", "shadowball"] + }, + { + "role": "Bulky Attacker", + "movepool": ["earthquake", "headbutt", "shadowball", "thunderwave"] + } + ] + }, + "gligar": { + "level": 84, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["earthquake", "hiddenpowerflying", "quickattack", "rockslide", "substitute", "swordsdance"] + } + ] + }, + "steelix": { + "level": 83, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["doubleedge", "earthquake", "explosion", "hiddenpowerrock", "irontail", "rest", "roar", "toxic"] + }, + { + "role": "Bulky Support", + "movepool": ["doubleedge", "earthquake", "hiddenpowerrock", "rest", "sleeptalk"] + } + ] + }, + "granbull": { + "level": 83, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["doubleedge", "earthquake", "rest", "return", "sleeptalk"] + }, + { + "role": "Wallbreaker", + "movepool": ["bulkup", "doubleedge", "earthquake", "overheat", "shadowball"] + }, + { + "role": "Bulky Attacker", + "movepool": ["earthquake", "healbell", "return", "shadowball", "thunderwave"], + "preferredTypes": ["Ground"] + } + ] + }, + "qwilfish": { + "level": 85, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["hydropump", "selfdestruct", "shadowball", "sludgebomb", "swordsdance"] + }, + { + "role": "Fast Attacker", + "movepool": ["destinybond", "hydropump", "selfdestruct", "sludgebomb", "spikes"] + } + ] + }, + "scizor": { + "level": 82, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["batonpass", "hiddenpowerground", "morningsun", "silverwind", "steelwing", "swordsdance"] + }, + { + "role": "Generalist", + "movepool": ["agility", "batonpass", "hiddenpowerground", "silverwind", "steelwing"] + } + ] + }, + "shuckle": { + "level": 98, + "sets": [ + { + "role": "Staller", + "movepool": ["encore", "rest", "toxic", "wrap"] + } + ] + }, + "heracross": { + "level": 80, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["brickbreak", "earthquake", "hiddenpowerghost", "megahorn", "rockslide"], + "preferredTypes": ["Rock"] + }, + { + "role": "Setup Sweeper", + "movepool": ["brickbreak", "megahorn", "rockslide", "swordsdance"] + }, + { + "role": "Berry Sweeper", + "movepool": ["endure", "megahorn", "reversal", "rockslide", "substitute"] + } + ] + }, + "sneasel": { + "level": 87, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["brickbreak", "hiddenpowerflying", "shadowball", "substitute", "swordsdance"], + "preferredTypes": ["Fighting", "Ghost"] + }, + { + "role": "Wallbreaker", + "movepool": ["brickbreak", "doubleedge", "hiddenpowerflying", "shadowball", "swordsdance"], + "preferredTypes": ["Fighting", "Ghost"] + } + ] + }, + "ursaring": { + "level": 81, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["earthquake", "focuspunch", "hiddenpowerghost", "return"] + }, + { + "role": "Fast Attacker", + "movepool": ["earthquake", "facade", "hiddenpowerghost", "return"] + }, + { + "role": "Setup Sweeper", + "movepool": ["earthquake", "hiddenpowerghost", "return", "swordsdance"] + } + ] + }, + "magcargo": { + "level": 95, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["fireblast", "hiddenpowergrass", "rest", "sleeptalk", "toxic"] + } + ] + }, + "piloswine": { + "level": 88, + "sets": [ + { + "role": "Staller", + "movepool": ["earthquake", "icebeam", "protect", "toxic"] + }, + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "earthquake", "icebeam", "rest", "rockslide", "sleeptalk"] + } + ] + }, + "corsola": { + "level": 95, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["calmmind", "icebeam", "recover", "surf", "toxic"] + } + ] + }, + "octillery": { + "level": 87, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["fireblast", "hiddenpowerelectric", "hiddenpowergrass", "icebeam", "surf", "thunderwave"], + "preferredTypes": ["Ice"] + } + ] + }, + "delibird": { + "level": 95, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["aerialace", "doubleedge", "focuspunch", "hiddenpowerground", "icebeam", "quickattack"], + "preferredTypes": ["Ground"] + } + ] + }, + "mantine": { + "level": 85, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["hiddenpowergrass", "icebeam", "rest", "sleeptalk", "surf", "toxic"] + }, + { + "role": "Staller", + "movepool": ["haze", "icebeam", "protect", "surf", "toxic"] + }, + { + "role": "Setup Sweeper", + "movepool": ["hiddenpowergrass", "hydropump", "icebeam", "raindance", "surf"] + } + ] + }, + "skarmory": { + "level": 80, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["drillpeck", "protect", "rest", "spikes", "toxic"] + }, + { + "role": "Generalist", + "movepool": ["drillpeck", "spikes", "toxic", "whirlwind"] + }, + { + "role": "Staller", + "movepool": ["protect", "spikes", "toxic", "whirlwind"] + } + ] + }, + "houndoom": { + "level": 81, + "sets": [ + { + "role": "Berry Sweeper", + "movepool": ["crunch", "fireblast", "flamethrower", "hiddenpowergrass", "substitute"] + }, + { + "role": "Fast Attacker", + "movepool": ["crunch", "fireblast", "flamethrower", "hiddenpowergrass", "pursuit", "willowisp"] + } + ] + }, + "kingdra": { + "level": 81, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["hiddenpowerelectric", "hiddenpowergrass", "hydropump", "icebeam", "raindance", "substitute", "surf"], + "preferredTypes": ["Ice"] + } + ] + }, + "donphan": { + "level": 83, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["earthquake", "rapidspin", "rest", "rockslide", "sleeptalk", "toxic"] + } + ] + }, + "porygon2": { + "level": 81, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["icebeam", "recover", "return", "thunderbolt", "thunderwave", "toxic"] + } + ] + }, + "stantler": { + "level": 84, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["earthquake", "hypnosis", "return", "shadowball", "thunderbolt", "thunderwave"], + "preferredTypes": ["Ground"] + } + ] + }, + "smeargle": { + "level": 87, + "sets": [ + { + "role": "Generalist", + "movepool": ["encore", "explosion", "spikes", "spore"] + } + ] + }, + "hitmontop": { + "level": 85, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["bulkup", "earthquake", "hiddenpowerghost", "highjumpkick", "machpunch", "rapidspin", "rockslide", "toxic"], + "preferredTypes": ["Ghost"] + } + ] + }, + "miltank": { + "level": 78, + "sets": [ + { + "role": "Bulky Setup", + "movepool": ["bodyslam", "curse", "earthquake", "milkdrink"] + }, + { + "role": "Bulky Support", + "movepool": ["bodyslam", "earthquake", "healbell", "milkdrink", "toxic"] + } + ] + }, + "blissey": { + "level": 78, + "sets": [ + { + "role": "Staller", + "movepool": ["aromatherapy", "seismictoss", "softboiled", "thunderwave", "toxic"] + }, + { + "role": "Bulky Support", + "movepool": ["protect", "seismictoss", "toxic", "wish"] + }, + { + "role": "Bulky Setup", + "movepool": ["calmmind", "icebeam", "softboiled", "thunderbolt"] + } + ] + }, + "raikou": { + "level": 74, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["calmmind", "crunch", "hiddenpowerice", "substitute", "thunderbolt"], + "preferredTypes": ["Ice"] + }, + { + "role": "Bulky Attacker", + "movepool": ["hiddenpowerice", "rest", "sleeptalk", "thunderbolt"] + } + ] + }, + "entei": { + "level": 80, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["flamethrower", "rest", "sleeptalk", "toxic"] + }, + { + "role": "Staller", + "movepool": ["flamethrower", "protect", "substitute", "toxic"] + }, + { + "role": "Bulky Setup", + "movepool": ["calmmind", "flamethrower", "hiddenpowergrass", "hiddenpowerice", "substitute"] + } + ] + }, + "suicune": { + "level": 75, + "sets": [ + { + "role": "Bulky Setup", + "movepool": ["calmmind", "rest", "sleeptalk", "surf"] + }, + { + "role": "Staller", + "movepool": ["protect", "roar", "substitute", "surf", "toxic"] + }, + { + "role": "Bulky Attacker", + "movepool": ["calmmind", "icebeam", "rest", "substitute", "surf", "toxic"] + } + ] + }, + "tyranitar": { + "level": 75, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["dragondance", "earthquake", "fireblast", "hiddenpowerflying", "rockslide"], + "preferredTypes": ["Ground"] + }, + { + "role": "Bulky Attacker", + "movepool": ["crunch", "earthquake", "fireblast", "icebeam", "pursuit", "rockslide", "thunderwave"] + }, + { + "role": "Wallbreaker", + "movepool": ["earthquake", "fireblast", "hiddenpowerflying", "rest", "rockslide", "sleeptalk"], + "preferredTypes": ["Ground"] + } + ] + }, + "lugia": { + "level": 70, + "sets": [ + { + "role": "Staller", + "movepool": ["earthquake", "psychic", "recover", "substitute", "toxic"] + }, + { + "role": "Bulky Setup", + "movepool": ["calmmind", "icebeam", "recover", "thunderbolt"] + } + ] + }, + "hooh": { + "level": 70, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["earthquake", "recover", "sacredfire", "substitute", "thunderbolt", "toxic"] + }, + { + "role": "Bulky Setup", + "movepool": ["calmmind", "recover", "sacredfire", "thunderbolt"] + } + ] + }, + "celebi": { + "level": 75, + "sets": [ + { + "role": "Bulky Setup", + "movepool": ["batonpass", "calmmind", "hiddenpowerfire", "hiddenpowergrass", "psychic", "recover"] + }, + { + "role": "Bulky Support", + "movepool": ["healbell", "hiddenpowerfire", "hiddenpowergrass", "leechseed", "psychic", "recover", "toxic"] + } + ] + }, + "sceptile": { + "level": 82, + "sets": [ + { + "role": "Staller", + "movepool": ["hiddenpowerfire", "hiddenpowerice", "leafblade", "leechseed", "substitute"] + }, + { + "role": "Berry Sweeper", + "movepool": ["hiddenpowerice", "leafblade", "substitute", "thunderpunch"] + }, + { + "role": "Fast Attacker", + "movepool": ["earthquake", "hiddenpowerice", "leafblade", "thunderpunch", "toxic"], + "preferredTypes": ["Ground"] + } + ] + }, + "blaziken": { + "level": 82, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["earthquake", "fireblast", "hiddenpowerice", "rockslide", "skyuppercut", "thunderpunch"] + }, + { + "role": "Berry Sweeper", + "movepool": ["endure", "fireblast", "reversal", "swordsdance"] + }, + { + "role": "Wallbreaker", + "movepool": ["earthquake", "fireblast", "rockslide", "skyuppercut", "swordsdance"] + } + ] + }, + "swampert": { + "level": 79, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["earthquake", "hydropump", "protect", "surf", "toxic"] + }, + { + "role": "Bulky Support", + "movepool": ["earthquake", "hydropump", "rest", "sleeptalk", "surf"] + }, + { + "role": "Staller", + "movepool": ["earthquake", "hydropump", "icebeam", "refresh", "surf", "toxic"] + } + ] + }, + "mightyena": { + "level": 91, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["crunch", "doubleedge", "healbell", "hiddenpowerfighting", "shadowball", "toxic"], + "preferredTypes": ["Fighting"] + } + ] + }, + "linoone": { + "level": 85, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["bellydrum", "extremespeed", "hiddenpowerfighting", "shadowball"] + }, + { + "role": "Bulky Setup", + "movepool": ["bellydrum", "hiddenpowerground", "return", "shadowball", "substitute"] + } + ] + }, + "beautifly": { + "level": 100, + "sets": [ + { + "role": "Staller", + "movepool": ["hiddenpowerflying", "morningsun", "stunspore", "substitute", "toxic"] + } + ] + }, + "dustox": { + "level": 94, + "sets": [ + { + "role": "Staller", + "movepool": ["hiddenpowerground", "moonlight", "sludgebomb", "toxic", "whirlwind"] + } + ] + }, + "ludicolo": { + "level": 83, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["hiddenpowergrass", "hydropump", "icebeam", "raindance", "surf"] + } + ] + }, + "shiftry": { + "level": 87, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["brickbreak", "explosion", "shadowball", "swordsdance"] + }, + { + "role": "Staller", + "movepool": ["hiddenpowerdark", "leechseed", "substitute", "toxic"] + } + ] + }, + "swellow": { + "level": 81, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["aerialace", "doubleedge", "hiddenpowerground", "quickattack", "return"] + }, + { + "role": "Fast Attacker", + "movepool": ["aerialace", "doubleedge", "facade", "hiddenpowerground", "return"] + } + ] + }, + "pelipper": { + "level": 88, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["icebeam", "rest", "sleeptalk", "surf", "toxic"] + }, + { + "role": "Staller", + "movepool": ["icebeam", "protect", "surf", "toxic"] + } + ] + }, + "gardevoir": { + "level": 79, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["calmmind", "firepunch", "hypnosis", "icepunch", "psychic", "substitute", "thunderbolt"], + "preferredTypes": ["Fire"] + } + ] + }, + "masquerain": { + "level": 94, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["hydropump", "icebeam", "stunspore", "substitute", "toxic"] + } + ] + }, + "breloom": { + "level": 85, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["hiddenpowerghost", "hiddenpowerrock", "machpunch", "skyuppercut", "spore", "substitute", "swordsdance"] + }, + { + "role": "Generalist", + "movepool": ["focuspunch", "hiddenpowerghost", "hiddenpowerrock", "spore", "substitute"] + } + ] + }, + "vigoroth": { + "level": 86, + "sets": [ + { + "role": "Bulky Setup", + "movepool": ["bodyslam", "bulkup", "earthquake", "return", "shadowball", "slackoff"] + } + ] + }, + "slaking": { + "level": 79, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "earthquake", "focuspunch", "return", "shadowball"] + }, + { + "role": "Fast Attacker", + "movepool": ["doubleedge", "earthquake", "hyperbeam", "return", "shadowball"] + } + ] + }, + "ninjask": { + "level": 84, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["batonpass", "hiddenpowerflying", "substitute", "swordsdance"] + }, + { + "role": "Bulky Setup", + "movepool": ["batonpass", "hiddenpowerflying", "protect", "swordsdance"] + } + ] + }, + "shedinja": { + "level": 95, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["agility", "batonpass", "hiddenpowerfighting", "hiddenpowerground", "shadowball", "silverwind", "toxic"] + } + ] + }, + "exploud": { + "level": 84, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "earthquake", "overheat", "return", "shadowball"] + }, + { + "role": "Fast Attacker", + "movepool": ["earthquake", "flamethrower", "icebeam", "return", "shadowball", "substitute"], + "preferredTypes": ["Ground"] + } + ] + }, + "hariyama": { + "level": 83, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["bulkup", "crosschop", "earthquake", "hiddenpowerghost", "knockoff", "rockslide"] + }, + { + "role": "Bulky Attacker", + "movepool": ["crosschop", "hiddenpowerghost", "rest", "rockslide", "sleeptalk"] + } + ] + }, + "nosepass": { + "level": 96, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["earthquake", "explosion", "rockslide", "thunderwave", "toxic"] + } + ] + }, + "delcatty": { + "level": 93, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["doubleedge", "protect", "thunderwave", "toxic", "wish"] + }, + { + "role": "Generalist", + "movepool": ["batonpass", "bodyslam", "healbell", "protect", "wish"] + } + ] + }, + "sableye": { + "level": 90, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["knockoff", "recover", "seismictoss", "toxic"] + }, + { + "role": "Bulky Attacker", + "movepool": ["recover", "seismictoss", "shadowball", "toxic"] + } + ] + }, + "mawile": { + "level": 94, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["batonpass", "brickbreak", "hiddenpowersteel", "rockslide", "substitute", "swordsdance"], + "preferredTypes": ["Fighting"] + }, + { + "role": "Bulky Support", + "movepool": ["focuspunch", "hiddenpowersteel", "rockslide", "substitute", "toxic"] + } + ] + }, + "aggron": { + "level": 84, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "earthquake", "irontail", "rockslide", "thunderwave", "toxic"], + "preferredTypes": ["Ground"] + }, + { + "role": "Generalist", + "movepool": ["doubleedge", "earthquake", "focuspunch", "irontail", "rockslide", "substitute"] + } + ] + }, + "medicham": { + "level": 83, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["brickbreak", "bulkup", "recover", "rockslide", "shadowball", "substitute"], + "preferredTypes": ["Ghost"] + } + ] + }, + "manectric": { + "level": 82, + "sets": [ + { + "role": "Berry Sweeper", + "movepool": ["crunch", "hiddenpowerice", "substitute", "thunderbolt"] + } + ] + }, + "plusle": { + "level": 88, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["batonpass", "encore", "hiddenpowerice", "substitute", "thunderbolt", "toxic"] + }, + { + "role": "Staller", + "movepool": ["hiddenpowerice", "protect", "thunderbolt", "toxic"] + }, + { + "role": "Bulky Support", + "movepool": ["hiddenpowerice", "protect", "thunderbolt", "toxic", "wish"] + } + ] + }, + "minun": { + "level": 89, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["batonpass", "encore", "hiddenpowerice", "substitute", "thunderbolt", "toxic"] + }, + { + "role": "Staller", + "movepool": ["hiddenpowerice", "protect", "thunderbolt", "toxic"] + }, + { + "role": "Bulky Support", + "movepool": ["hiddenpowerice", "protect", "thunderbolt", "toxic", "wish"] + } + ] + }, + "volbeat": { + "level": 93, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["batonpass", "icepunch", "tailglow", "thunderbolt"] + } + ] + }, + "illumise": { + "level": 95, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["encore", "moonlight", "seismictoss", "thunderwave", "toxic"] + }, + { + "role": "Generalist", + "movepool": ["batonpass", "encore", "seismictoss", "substitute", "thunderwave", "toxic"] + } + ] + }, + "roselia": { + "level": 93, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["aromatherapy", "hiddenpowerfire", "magicalleaf", "spikes", "synthesis", "toxic"] + }, + { + "role": "Bulky Support", + "movepool": ["aromatherapy", "hiddenpowergrass", "spikes", "synthesis", "toxic"] + } + ] + }, + "swalot": { + "level": 89, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["encore", "explosion", "hiddenpowerground", "icebeam", "painsplit", "shadowball", "sludgebomb", "toxic", "yawn"] + } + ] + }, + "sharpedo": { + "level": 85, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "earthquake", "hiddenpowerflying", "hydropump"] + }, + { + "role": "Berry Sweeper", + "movepool": ["crunch", "hiddenpowerelectric", "hiddenpowergrass", "hydropump", "icebeam", "substitute"] + } + ] + }, + "wailord": { + "level": 87, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["icebeam", "rest", "sleeptalk", "surf", "toxic"] + }, + { + "role": "Bulky Attacker", + "movepool": ["hiddenpowergrass", "icebeam", "selfdestruct", "surf", "toxic"], + "preferredTypes": ["Ice"] + } + ] + }, + "camerupt": { + "level": 85, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["earthquake", "explosion", "fireblast", "rest", "rockslide", "sleeptalk", "toxic"] + } + ] + }, + "torkoal": { + "level": 90, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["explosion", "fireblast", "flamethrower", "hiddenpowergrass", "rest", "toxic", "yawn"] + } + ] + }, + "grumpig": { + "level": 84, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["calmmind", "firepunch", "psychic", "substitute", "thunderpunch"], + "preferredTypes": ["Fire"] + } + ] + }, + "spinda": { + "level": 95, + "sets": [ + { + "role": "Staller", + "movepool": ["encore", "protect", "seismictoss", "shadowball", "substitute", "toxic"] + }, + { + "role": "Bulky Attacker", + "movepool": ["bodyslam", "focuspunch", "shadowball", "substitute"] + }, + { + "role": "Bulky Support", + "movepool": ["protect", "seismictoss", "toxic", "wish"] + } + ] + }, + "flygon": { + "level": 79, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["dragonclaw", "earthquake", "fireblast", "hiddenpowerbug", "rockslide"], + "preferredTypes": ["Bug", "Rock"] + }, + { + "role": "Staller", + "movepool": ["dragonclaw", "earthquake", "fireblast", "protect", "toxic"] + }, + { + "role": "Bulky Attacker", + "movepool": ["dragonclaw", "earthquake", "fireblast", "rockslide", "substitute", "toxic"] + } + ] + }, + "cacturne": { + "level": 92, + "sets": [ + { + "role": "Staller", + "movepool": ["focuspunch", "hiddenpowerdark", "leechseed", "substitute"] + }, + { + "role": "Generalist", + "movepool": ["hiddenpowerdark", "needlearm", "spikes", "thunderpunch"] + } + ] + }, + "altaria": { + "level": 85, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["dragonclaw", "earthquake", "flamethrower", "haze", "healbell", "rest", "toxic"] + }, + { + "role": "Setup Sweeper", + "movepool": ["dragondance", "earthquake", "fireblast", "healbell", "hiddenpowerflying", "rest"], + "preferredTypes": ["Ground"] + } + ] + }, + "zangoose": { + "level": 80, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["brickbreak", "quickattack", "return", "shadowball", "swordsdance"], + "preferredTypes": ["Ghost"] + } + ] + }, + "seviper": { + "level": 88, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["crunch", "earthquake", "flamethrower", "hiddenpowergrass", "sludgebomb"], + "preferredTypes": ["Ground"] + } + ] + }, + "lunatone": { + "level": 84, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["batonpass", "calmmind", "hiddenpowerfire", "hypnosis", "icebeam", "psychic"] + }, + { + "role": "Bulky Attacker", + "movepool": ["explosion", "hypnosis", "icebeam", "psychic", "toxic"] + } + ] + }, + "solrock": { + "level": 85, + "sets": [ + { + "role": "Staller", + "movepool": ["earthquake", "protect", "rockslide", "toxic"] + }, + { + "role": "Wallbreaker", + "movepool": ["earthquake", "explosion", "overheat", "rockslide", "shadowball"], + "preferredTypes": ["Ground"] + } + ] + }, + "whiscash": { + "level": 86, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["earthquake", "icebeam", "rest", "sleeptalk", "surf", "toxic"] + }, + { + "role": "Staller", + "movepool": ["earthquake", "icebeam", "protect", "toxic"] + } + ] + }, + "crawdaunt": { + "level": 88, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["brickbreak", "crunch", "doubleedge", "hiddenpowerelectric", "hiddenpowergrass", "icebeam", "surf"] + }, + { + "role": "Wallbreaker", + "movepool": ["brickbreak", "doubleedge", "hiddenpowerflying", "surf", "swordsdance"], + "preferredTypes": ["Normal"] + } + ] + }, + "claydol": { + "level": 81, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["earthquake", "explosion", "icebeam", "psychic", "rapidspin", "toxic"] + } + ] + }, + "cradily": { + "level": 84, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["earthquake", "hiddenpowergrass", "recover", "rockslide", "toxic"], + "preferredTypes": ["Ground"] + } + ] + }, + "armaldo": { + "level": 82, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "earthquake", "hiddenpowerbug", "rapidspin", "rockslide", "swordsdance"], + "preferredTypes": ["Ground"] + } + ] + }, + "milotic": { + "level": 78, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["icebeam", "rest", "sleeptalk", "surf", "toxic"] + }, + { + "role": "Staller", + "movepool": ["icebeam", "recover", "refresh", "surf", "toxic"] + } + ] + }, + "castform": { + "level": 90, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["flamethrower", "icebeam", "return", "thunderbolt", "thunderwave"] + } + ] + }, + "kecleon": { + "level": 91, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["brickbreak", "return", "shadowball", "thunderwave", "trick"] + } + ] + }, + "banette": { + "level": 88, + "sets": [ + { + "role": "Berry Sweeper", + "movepool": ["destinybond", "endure", "hiddenpowerfighting", "shadowball"] + }, + { + "role": "Wallbreaker", + "movepool": ["doubleedge", "hiddenpowerfighting", "knockoff", "shadowball", "willowisp"], + "preferredTypes": ["Fighting"] + } + ] + }, + "dusclops": { + "level": 86, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["rest", "seismictoss", "sleeptalk", "willowisp"] + }, + { + "role": "Bulky Attacker", + "movepool": ["rest", "seismictoss", "shadowball", "sleeptalk"] + }, + { + "role": "Generalist", + "movepool": ["focuspunch", "icebeam", "painsplit", "shadowball", "substitute", "willowisp"] + } + ] + }, + "tropius": { + "level": 94, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["hiddenpowerfire", "solarbeam", "sunnyday", "synthesis"] + }, + { + "role": "Staller", + "movepool": ["earthquake", "hiddenpowerflying", "leechseed", "synthesis", "toxic"] + } + ] + }, + "chimecho": { + "level": 89, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["calmmind", "healbell", "hiddenpowerfire", "psychic", "toxic"] + } + ] + }, + "absol": { + "level": 88, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["batonpass", "hiddenpowerfighting", "quickattack", "shadowball", "swordsdance"] + } + ] + }, + "glalie": { + "level": 83, + "sets": [ + { + "role": "Generalist", + "movepool": ["earthquake", "explosion", "icebeam", "spikes", "toxic"] + } + ] + }, + "walrein": { + "level": 81, + "sets": [ + { + "role": "Staller", + "movepool": ["icebeam", "protect", "surf", "toxic"] + }, + { + "role": "Bulky Attacker", + "movepool": ["encore", "icebeam", "rest", "sleeptalk", "surf", "toxic"] + } + ] + }, + "huntail": { + "level": 88, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["doubleedge", "hiddenpowerelectric", "hiddenpowergrass", "hydropump", "icebeam", "raindance", "surf"], + "preferredTypes": ["Ice"] + } + ] + }, + "gorebyss": { + "level": 85, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["hiddenpowerelectric", "hiddenpowergrass", "hydropump", "icebeam", "raindance", "surf"] + } + ] + }, + "relicanth": { + "level": 88, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["doubleedge", "earthquake", "hiddenpowerflying", "rest", "rockslide", "sleeptalk", "toxic"], + "preferredTypes": ["Ground"] + } + ] + }, + "luvdisc": { + "level": 99, + "sets": [ + { + "role": "Staller", + "movepool": ["icebeam", "protect", "substitute", "surf", "toxic"] + } + ] + }, + "salamence": { + "level": 73, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["dragondance", "earthquake", "fireblast", "hiddenpowerflying", "rockslide"], + "preferredTypes": ["Ground"] + }, + { + "role": "Wallbreaker", + "movepool": ["brickbreak", "doubleedge", "earthquake", "fireblast", "hiddenpowerflying", "rockslide"], + "preferredTypes": ["Ground"] + } + ] + }, + "metagross": { + "level": 74, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["agility", "earthquake", "explosion", "meteormash", "psychic", "rockslide"], + "preferredTypes": ["Ground"] + } + ] + }, + "regirock": { + "level": 82, + "sets": [ + { + "role": "Fast Attacker", + "movepool": ["curse", "earthquake", "explosion", "rest", "rockslide", "superpower"], + "preferredTypes": ["Ground"] + }, + { + "role": "Bulky Attacker", + "movepool": ["earthquake", "explosion", "rest", "rockslide", "sleeptalk", "thunderwave", "toxic"] + } + ] + }, + "regice": { + "level": 79, + "sets": [ + { + "role": "Bulky Attacker", + "movepool": ["explosion", "icebeam", "rest", "sleeptalk", "thunderbolt", "thunderwave"] + }, + { + "role": "Staller", + "movepool": ["icebeam", "protect", "thunderbolt", "toxic"] + } + ] + }, + "registeel": { + "level": 78, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["rest", "seismictoss", "sleeptalk", "toxic"] + } + ] + }, + "latias": { + "level": 67, + "sets": [ + { + "role": "Bulky Setup", + "movepool": ["calmmind", "dragonclaw", "hiddenpowerfire", "psychic", "recover"] + }, + { + "role": "Setup Sweeper", + "movepool": ["calmmind", "dragonclaw", "recover", "refresh"] + } + ] + }, + "latios": { + "level": 66, + "sets": [ + { + "role": "Bulky Setup", + "movepool": ["calmmind", "dragonclaw", "hiddenpowerfire", "psychic", "recover"] + }, + { + "role": "Setup Sweeper", + "movepool": ["calmmind", "dragonclaw", "recover", "refresh"] + } + ] + }, + "kyogre": { + "level": 67, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["calmmind", "icebeam", "rest", "sleeptalk", "surf", "thunder"] + } + ] + }, + "groudon": { + "level": 70, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["earthquake", "hiddenpowerbug", "overheat", "rockslide", "substitute", "swordsdance", "thunderwave"], + "preferredTypes": ["Rock"] + } + ] + }, + "rayquaza": { + "level": 72, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["dragondance", "earthquake", "hiddenpowerflying", "overheat", "rockslide"], + "preferredTypes": ["Ground"] + }, + { + "role": "Bulky Setup", + "movepool": ["earthquake", "extremespeed", "hiddenpowerflying", "rockslide", "swordsdance"], + "preferredTypes": ["Ground"] + }, + { + "role": "Wallbreaker", + "movepool": ["earthquake", "extremespeed", "hiddenpowerflying", "overheat", "rockslide"], + "preferredTypes": ["Ground"] + } + ] + }, + "jirachi": { + "level": 74, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["bodyslam", "firepunch", "protect", "psychic", "toxic", "wish"] + }, + { + "role": "Setup Sweeper", + "movepool": ["calmmind", "firepunch", "icepunch", "psychic", "substitute", "thunderbolt"], + "preferredTypes": ["Fire"] + } + ] + }, + "deoxys": { + "level": 74, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["extremespeed", "firepunch", "icebeam", "psychoboost", "shadowball", "spikes", "superpower"], + "preferredTypes": ["Fighting", "Ghost"] + } + ] + }, + "deoxysattack": { + "level": 73, + "sets": [ + { + "role": "Wallbreaker", + "movepool": ["extremespeed", "firepunch", "icebeam", "psychoboost", "shadowball", "superpower"], + "preferredTypes": ["Fighting", "Ghost"] + } + ] + }, + "deoxysdefense": { + "level": 75, + "sets": [ + { + "role": "Bulky Support", + "movepool": ["recover", "seismictoss", "spikes", "toxic"] + } + ] + }, + "deoxysspeed": { + "level": 76, + "sets": [ + { + "role": "Setup Sweeper", + "movepool": ["calmmind", "firepunch", "icebeam", "psychic", "recover", "substitute"], + "preferredTypes": ["Fire"] + }, + { + "role": "Bulky Support", + "movepool": ["psychoboost", "recover", "spikes", "superpower", "toxic"] + } + ] + } +} diff --git a/data/mods/gen3/random-teams.ts b/data/mods/gen3/random-teams.ts index 7ea16f280283..c342b85dfba3 100644 --- a/data/mods/gen3/random-teams.ts +++ b/data/mods/gen3/random-teams.ts @@ -1,19 +1,37 @@ import RandomGen4Teams from '../gen4/random-teams'; import {Utils} from '../../../lib'; import {PRNG, PRNGSeed} from '../../../sim/prng'; -import type {MoveCounter, OldRandomBattleSpecies} from '../gen8/random-teams'; +import type {MoveCounter} from '../gen8/random-teams'; +// Moves that restore HP: +const RECOVERY_MOVES = [ + 'milkdrink', 'moonlight', 'morningsun', 'recover', 'slackoff', 'softboiled', 'synthesis', +]; +// Conglomerate for ease of access +const SETUP = [ + 'acidarmor', 'agility', 'bellydrum', 'bulkup', 'calmmind', 'curse', 'dragondance', 'growth', 'howl', 'irondefense', + 'meditate', 'raindance', 'sunnyday', 'swordsdance', 'tailglow', +]; // Moves that shouldn't be the only STAB moves: const NO_STAB = [ - 'bounce', 'eruption', 'explosion', 'fakeout', 'icywind', 'machpunch', - 'pursuit', 'quickattack', 'reversal', 'selfdestruct', 'waterspout', + 'eruption', 'explosion', 'fakeout', 'focuspunch', 'futuresight', 'icywind', 'knockoff', 'machpunch', 'pursuit', + 'quickattack', 'rapidspin', 'selfdestruct', 'skyattack', 'waterspout', +]; + +// Moves that should be paired together when possible +const MOVE_PAIRS = [ + ['sleeptalk', 'rest'], + ['protect', 'wish'], + ['leechseed', 'substitute'], + ['focuspunch', 'substitute'], + ['batonpass', 'spiderweb'], ]; export class RandomGen3Teams extends RandomGen4Teams { battleHasDitto: boolean; battleHasWobbuffet: boolean; - randomData: {[species: string]: OldRandomBattleSpecies} = require('./random-data.json'); + randomSets: {[species: string]: RandomTeamsTypes.RandomSpeciesData} = require('./random-sets.json'); constructor(format: string | Format, prng: PRNG | PRNGSeed | null) { super(format, prng); @@ -22,283 +40,344 @@ export class RandomGen3Teams extends RandomGen4Teams { this.battleHasWobbuffet = false; this.moveEnforcementCheckers = { Bug: (movePool, moves, abilities, types, counter, species) => ( - movePool.includes('megahorn') || (!species.types[1] && movePool.includes('hiddenpowerbug')) + !counter.get('Bug') && ['armaldo', 'heracross', 'parasect'].includes(species.id) ), + Dark: (movePool, moves, abilities, types, counter) => !counter.get('Dark'), Electric: (movePool, moves, abilities, types, counter) => !counter.get('Electric'), Fighting: (movePool, moves, abilities, types, counter) => !counter.get('Fighting'), Fire: (movePool, moves, abilities, types, counter) => !counter.get('Fire'), + Flying: (movePool, moves, abilities, types, counter, species) => (!counter.get('Flying') && species.id !== 'crobat'), + Ghost: (movePool, moves, abilities, types, counter) => !counter.get('Ghost'), Ground: (movePool, moves, abilities, types, counter) => !counter.get('Ground'), - Normal: (movePool, moves, abilities, types, counter, species) => { - if (species.id === 'blissey' && movePool.includes('softboiled')) return true; - return !counter.get('Normal') && counter.setupType === 'Physical'; - }, + Ice: (movePool, moves, abilities, types, counter) => !counter.get('Ice'), + Normal: (movePool, moves, abilities, types, counter, species) => !counter.get('Normal'), + Poison: (movePool, moves, abilities, types, counter) => !counter.get('Poison') && !counter.get('Bug'), Psychic: (movePool, moves, abilities, types, counter, species) => ( - types.has('Psychic') && - (movePool.includes('psychic') || movePool.includes('psychoboost')) && - species.baseStats.spa >= 100 + !counter.get('Psychic') && species.baseStats.spa >= 100 ), - Rock: (movePool, moves, abilities, types, counter, species) => !counter.get('Rock') && species.baseStats.atk >= 100, - Water: (movePool, moves, abilities, types, counter, species) => ( - !counter.get('Water') && counter.setupType !== 'Physical' && species.baseStats.spa >= 60 - ), - // If the Pokémon has this move, the other move will be forced - protect: movePool => movePool.includes('wish'), - sunnyday: movePool => movePool.includes('solarbeam'), - sleeptalk: movePool => movePool.includes('rest'), + Rock: (movePool, moves, abilities, types, counter, species) => !counter.get('Rock'), + Steel: (movePool, moves, abilities, types, counter, species) => (!counter.get('Steel') && species.id !== 'forretress'), + Water: (movePool, moves, abilities, types, counter, species) => !counter.get('Water'), }; } - shouldCullMove( - move: Move, - types: Set, + cullMovePool( + types: string[], moves: Set, abilities: Set, counter: MoveCounter, movePool: string[], teamDetails: RandomTeamsTypes.TeamDetails, species: Species, - ): {cull: boolean, isSetup?: boolean} { - const restTalk = moves.has('rest') && moves.has('sleeptalk'); - - switch (move.id) { - // Set up once and only if we have the moves for it - case 'bulkup': case 'curse': case 'dragondance': case 'swordsdance': - return { - cull: ( - (counter.setupType !== 'Physical' || counter.get('physicalsetup') > 1) || - (counter.get('Physical') + counter.get('physicalpool') < 2 && !moves.has('batonpass') && !restTalk) - ), - isSetup: true, - }; - case 'calmmind': - return { - cull: ( - counter.setupType !== 'Special' || - (counter.get('Special') + counter.get('specialpool') < 2 && !moves.has('batonpass') && - !moves.has('refresh') && !restTalk) || - !counter.get('Special') - ), - isSetup: true, - }; - case 'agility': - return { - cull: (counter.damagingMoves.size < 2 && !moves.has('batonpass')) || moves.has('substitute') || restTalk, - isSetup: !counter.setupType, - }; - - // Not very useful without their supporting moves - case 'amnesia': case 'sleeptalk': - if (moves.has('roar') || moves.has('whirlwind')) return {cull: true}; - if (!moves.has('rest')) return {cull: true}; - if (movePool.length > 1) { - const rest = movePool.indexOf('rest'); - if (rest >= 0) this.fastPop(movePool, rest); - } - break; - case 'barrier': - return {cull: !moves.has('calmmind') && !moves.has('batonpass') && !moves.has('mirrorcoat')}; - case 'batonpass': - return {cull: ( - (!counter.setupType && !counter.get('speedsetup')) && - ['meanlook', 'spiderweb', 'substitute', 'wish'].every(m => !moves.has(m)) - )}; - case 'endeavor': case 'flail': case 'reversal': - return {cull: restTalk || (!moves.has('endure') && !moves.has('substitute'))}; - case 'endure': - return {cull: movePool.includes('destinybond')}; - case 'extremespeed': case 'raindance': case 'sunnyday': - return {cull: counter.damagingMoves.size < 2 || moves.has('rest')}; - case 'focuspunch': - return {cull: ( - (counter.damagingMoves.size < 2 || moves.has('rest') || counter.setupType && !moves.has('spore')) || - (!moves.has('substitute') && (counter.get('Physical') < 4 || moves.has('fakeout'))) || - // Breloom likes to have coverage - (species.id === 'breloom' && (moves.has('machpunch') || moves.has('skyuppercut'))) - )}; - case 'moonlight': - return {cull: moves.has('wish') || moves.has('protect')}; - case 'perishsong': - return {cull: !moves.has('meanlook') && !moves.has('spiderweb')}; - case 'protect': - return {cull: !abilities.has('Speed Boost') && ['perishsong', 'toxic', 'wish'].every(m => !moves.has(m))}; - case 'refresh': - return {cull: !counter.setupType}; - case 'rest': - return {cull: ( - movePool.includes('sleeptalk') || - (!moves.has('sleeptalk') && (!!counter.get('recovery') || movePool.includes('curse'))) - )}; - case 'solarbeam': - if (movePool.length > 1) { - const sunnyday = movePool.indexOf('sunnyday'); - if (sunnyday >= 0) this.fastPop(movePool, sunnyday); - } - return {cull: !moves.has('sunnyday')}; - - // Bad after setup - case 'aromatherapy': case 'healbell': - return {cull: moves.has('rest') || !!teamDetails.statusCure}; - case 'confuseray': - return {cull: !!counter.setupType || restTalk}; - case 'counter': case 'mirrorcoat': - return {cull: !!counter.setupType || ['rest', 'substitute', 'toxic'].some(m => moves.has(m))}; - case 'destinybond': - return {cull: !!counter.setupType || moves.has('explosion') || moves.has('selfdestruct')}; - case 'doubleedge': case 'facade': case 'fakeout': case 'waterspout': - return {cull: ( - (!types.has(move.type) && counter.get('Status') >= 1) || - (move.id === 'doubleedge' && moves.has('return')) - )}; - case 'encore': case 'painsplit': case 'recover': case 'yawn': - return {cull: restTalk}; - case 'explosion': case 'machpunch': case 'selfdestruct': - // Snorlax doesn't want to roll selfdestruct as its only STAB move - const snorlaxCase = species.id === 'snorlax' && !moves.has('return') && !moves.has('bodyslam'); - return {cull: snorlaxCase || moves.has('rest') || moves.has('substitute') || !!counter.get('recovery')}; - case 'haze': - return {cull: !!counter.setupType || moves.has('raindance') || restTalk}; - case 'icywind': case 'pursuit': case 'superpower': case 'transform': - return {cull: !!counter.setupType || moves.has('rest')}; - case 'leechseed': - return {cull: !!counter.setupType || moves.has('explosion')}; - case 'stunspore': - return {cull: moves.has('sunnyday') || moves.has('toxic')}; - case 'lightscreen': - return {cull: !!counter.setupType || !!counter.get('speedsetup')}; - case 'meanlook': case 'spiderweb': - return {cull: !!counter.get('speedsetup') || (!moves.has('batonpass') && !moves.has('perishsong'))}; - case 'morningsun': - return {cull: counter.get('speedsetup') >= 1}; - case 'quickattack': - return {cull: ( - !!counter.get('speedsetup') || - moves.has('substitute') || - (!types.has('Normal') && !!counter.get('Status')) - )}; - case 'rapidspin': - return {cull: !!counter.setupType || moves.has('rest') || !!teamDetails.rapidSpin}; - case 'reflect': - return {cull: !!counter.setupType || !!counter.get('speedsetup')}; - case 'roar': case 'whirlwind': - return {cull: moves.has('sleeptalk') || moves.has('rest')}; - case 'seismictoss': - return {cull: !!counter.setupType || moves.has('thunderbolt')}; - case 'spikes': - return {cull: !!counter.setupType || moves.has('substitute') || restTalk || !!teamDetails.spikes}; - case 'substitute': - const restOrDD = moves.has('rest') || (moves.has('dragondance') && !moves.has('bellydrum')); - // This cull condition otherwise causes mono-solarbeam Entei - return {cull: restOrDD || (species.id !== 'entei' && !moves.has('batonpass') && movePool.includes('calmmind'))}; - case 'thunderwave': - return {cull: !!counter.setupType || moves.has('bodyslam') || - moves.has('substitute') && movePool.includes('toxic') || restTalk}; - case 'toxic': - return {cull: ( - !!counter.setupType || - !!counter.get('speedsetup') || - ['endure', 'focuspunch', 'raindance', 'yawn', 'hypnosis'].some(m => moves.has(m)) - )}; - case 'trick': - return {cull: counter.get('Status') > 1}; - case 'willowisp': - return {cull: !!counter.setupType || moves.has('hypnosis') || moves.has('toxic')}; - - // Bit redundant to have both - case 'bodyslam': - return {cull: moves.has('return') && !!counter.get('Status')}; - case 'headbutt': - return {cull: !moves.has('bodyslam') && !moves.has('thunderwave')}; - case 'return': - return {cull: ( - moves.has('endure') || - (moves.has('substitute') && moves.has('flail')) || - (moves.has('bodyslam') && !counter.get('Status')) - )}; - case 'fireblast': - return {cull: moves.has('flamethrower') && !!counter.get('Status')}; - case 'flamethrower': - return {cull: moves.has('fireblast') && !counter.get('Status')}; - case 'overheat': - return {cull: moves.has('flamethrower') || moves.has('substitute')}; - case 'hydropump': - return {cull: moves.has('surf') && !!counter.get('Status')}; - case 'surf': - return {cull: moves.has('hydropump') && !counter.get('Status')}; - case 'gigadrain': - return {cull: moves.has('morningsun') || moves.has('toxic')}; - case 'hiddenpower': - const stabCondition = types.has(move.type) && counter.get(move.type) > 1 && ( - (moves.has('substitute') && !counter.setupType && !moves.has('toxic')) || - // This otherwise causes STABless meganium - (species.id !== 'meganium' && moves.has('toxic') && !moves.has('substitute')) || - restTalk - ); - return {cull: stabCondition || (move.type === 'Grass' && moves.has('sunnyday') && moves.has('solarbeam'))}; - case 'brickbreak': case 'crosschop': case 'skyuppercut': - return {cull: moves.has('substitute') && (moves.has('focuspunch') || movePool.includes('focuspunch'))}; - case 'earthquake': - return {cull: moves.has('bonemerang')}; + isLead: boolean, + preferredType: string, + role: RandomTeamsTypes.Role, + ): void { + // Pokemon cannot have multiple Hidden Powers in any circumstance + let hasHiddenPower = false; + for (const move of moves) { + if (move.startsWith('hiddenpower')) hasHiddenPower = true; + } + if (hasHiddenPower) { + let movePoolHasHiddenPower = true; + while (movePoolHasHiddenPower) { + movePoolHasHiddenPower = false; + for (const moveid of movePool) { + if (moveid.startsWith('hiddenpower')) { + this.fastPop(movePool, movePool.indexOf(moveid)); + movePoolHasHiddenPower = true; + break; + } + } + } } - return {cull: false}; - } + if (moves.size + movePool.length <= this.maxMoveCount) return; + // If we have two unfilled moves and only one unpaired move, cull the unpaired move. + if (moves.size === this.maxMoveCount - 2) { + const unpairedMoves = [...movePool]; + for (const pair of MOVE_PAIRS) { + if (movePool.includes(pair[0]) && movePool.includes(pair[1])) { + this.fastPop(unpairedMoves, unpairedMoves.indexOf(pair[0])); + this.fastPop(unpairedMoves, unpairedMoves.indexOf(pair[1])); + } + } + if (unpairedMoves.length === 1) { + this.fastPop(movePool, movePool.indexOf(unpairedMoves[0])); + } + } + // These moves are paired, and shouldn't appear if there is not room for them both. + if (moves.size === this.maxMoveCount - 1) { + for (const pair of MOVE_PAIRS) { + if (movePool.includes(pair[0]) && movePool.includes(pair[1])) { + this.fastPop(movePool, movePool.indexOf(pair[0])); + this.fastPop(movePool, movePool.indexOf(pair[1])); + } + } + } - getItem( - ability: string, + // Team-based move culls + if (teamDetails.rapidSpin) { + if (movePool.includes('rapidspin')) this.fastPop(movePool, movePool.indexOf('rapidspin')); + if (moves.size + movePool.length <= this.maxMoveCount) return; + } + + // Develop additional move lists + const badWithSetup = ['knockoff', 'rapidspin', 'toxic']; + const statusMoves = this.dex.moves.all() + .filter(move => move.category === 'Status') + .map(move => move.id); + + // General incompatibilities + const incompatiblePairs = [ + // These moves don't mesh well with other aspects of the set + [statusMoves, 'trick'], + [SETUP, badWithSetup], + ['rest', ['protect', 'substitute']], + [['selfdestruct', 'explosion'], ['destinybond', 'painsplit', 'rest']], + + // These attacks are redundant with each other + ['surf', 'hydropump'], + [['bodyslam', 'return'], ['bodyslam', 'doubleedge']], + ['fireblast', 'flamethrower'], + + // Assorted hardcodes go here: + // Granbull + ['bulkup', 'overheat'], + // Heracross + ['endure', 'substitute'], + ]; + + for (const pair of incompatiblePairs) this.incompatibleMoves(moves, movePool, pair[0], pair[1]); + + const statusInflictingMoves = ['stunspore', 'thunderwave', 'toxic', 'willowisp', 'yawn']; + if (role !== 'Staller') { + this.incompatibleMoves(moves, movePool, statusInflictingMoves, statusInflictingMoves); + } + } + + // Generate random moveset for a given species, role, preferred type. + randomMoveset( types: string[], - moves: Set, - counter: MoveCounter, + abilities: Set, teamDetails: RandomTeamsTypes.TeamDetails, - species: Species - ) { - // First, the high-priority items - if (species.name === 'Ditto') return this.sample(['Metal Powder', 'Quick Claw']); - if (species.name === 'Farfetch\u2019d') return 'Stick'; - if (species.name === 'Latias' || species.name === 'Latios') return 'Soul Dew'; - if (species.name === 'Marowak') return 'Thick Club'; - if (species.name === 'Pikachu') return 'Light Ball'; - if (species.name === 'Shedinja') return 'Lum Berry'; - if (species.name === 'Unown') return 'Twisted Spoon'; + species: Species, + isLead: boolean, + movePool: string[], + preferredType: string, + role: RandomTeamsTypes.Role, + ): Set { + const preferredTypes = preferredType ? preferredType.split(',') : []; + const moves = new Set(); + let counter = this.newQueryMoves(moves, species, preferredType, abilities); + this.cullMovePool(types, moves, abilities, counter, movePool, teamDetails, species, isLead, + preferredType, role); + + // If there are only four moves, add all moves and return early + if (movePool.length <= this.maxMoveCount) { + // Still need to ensure that multiple Hidden Powers are not added (if maxMoveCount is increased) + while (movePool.length) { + const moveid = this.sample(movePool); + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } + return moves; + } - if (moves.has('trick')) return 'Choice Band'; - if (moves.has('rest') && !moves.has('sleeptalk') && !['Early Bird', 'Natural Cure', 'Shed Skin'].includes(ability)) { - return 'Chesto Berry'; + const runEnforcementChecker = (checkerName: string) => { + if (!this.moveEnforcementCheckers[checkerName]) return false; + return this.moveEnforcementCheckers[checkerName]( + movePool, moves, abilities, new Set(types), counter, species, teamDetails + ); + }; + + // Add required move (e.g. Relic Song for Meloetta-P) + if (species.requiredMove) { + const move = this.dex.moves.get(species.requiredMove).id; + counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); } - // Medium priority items - if (moves.has('dragondance') && ability !== 'Natural Cure') return 'Lum Berry'; - if ((moves.has('bellydrum') && counter.get('Physical') - counter.get('priority') > 1) || ( - ((moves.has('swordsdance') && counter.get('Status') < 2) || (moves.has('bulkup') && moves.has('substitute'))) && - !counter.get('priority') && - species.baseStats.spe >= 60 && species.baseStats.spe <= 95 - )) { - return 'Salac Berry'; - } - if (moves.has('endure') || ( - moves.has('substitute') && - ['bellydrum', 'endeavor', 'flail', 'reversal'].some(m => moves.has(m)) - )) { - return ( - species.baseStats.spe <= 100 && ability !== 'Speed Boost' && !counter.get('speedsetup') && !moves.has('focuspunch') - ) ? 'Salac Berry' : 'Liechi Berry'; - } - if (moves.has('substitute') && counter.get('Physical') >= 3 && species.baseStats.spe >= 120) return 'Liechi Berry'; - if ((moves.has('substitute') || moves.has('raindance')) && counter.get('Special') >= 3) return 'Petaya Berry'; - if (counter.get('Physical') >= 4 && !moves.has('fakeout')) return 'Choice Band'; - if (counter.get('Physical') >= 3 && !moves.has('rapidspin') && ( - ['fireblast', 'icebeam', 'overheat'].some(m => moves.has(m)) || - Array.from(moves).some(m => { - const moveData = this.dex.moves.get(m); - return moveData.category === 'Special' && types.includes(moveData.type); - }) - )) { - return 'Choice Band'; + // Add other moves you really want to have, e.g. STAB, recovery, setup. + + // Enforce Seismic Toss and Spore + for (const moveid of ['seismictoss', 'spore']) { + if (movePool.includes(moveid)) { + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } } - if (moves.has('psychoboost')) return 'White Herb'; - // Default to Leftovers - return 'Leftovers'; + // Enforce Substitute on non-Setup sets with Baton Pass + if (!role.includes('Setup')) { + if (movePool.includes('batonpass') && movePool.includes('substitute')) { + counter = this.addMove('substitute', moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } + } + + // Enforce moves of all Preferred Types + for (const type of preferredTypes) { + if (!counter.get(type)) { + const stabMoves = []; + for (const moveid of movePool) { + const move = this.dex.moves.get(moveid); + const moveType = this.getMoveType(move, species, abilities, preferredType); + if (!this.noStab.includes(moveid) && (move.basePower || move.basePowerCallback) && type === moveType) { + stabMoves.push(moveid); + } + } + if (stabMoves.length) { + const moveid = this.sample(stabMoves); + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } + } + } + + // Enforce STAB + for (const type of types) { + // Check if a STAB move of that type should be required + const stabMoves = []; + for (const moveid of movePool) { + const move = this.dex.moves.get(moveid); + const moveType = this.getMoveType(move, species, abilities, preferredType); + if (!this.noStab.includes(moveid) && (move.basePower || move.basePowerCallback) && type === moveType) { + stabMoves.push(moveid); + } + } + while (runEnforcementChecker(type)) { + if (!stabMoves.length) break; + const moveid = this.sampleNoReplace(stabMoves); + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } + } + + // If no STAB move was added, add a STAB move + if (!counter.get('stab')) { + const stabMoves = []; + for (const moveid of movePool) { + const move = this.dex.moves.get(moveid); + const moveType = this.getMoveType(move, species, abilities, preferredType); + if (!this.noStab.includes(moveid) && (move.basePower || move.basePowerCallback) && types.includes(moveType)) { + stabMoves.push(moveid); + } + } + if (stabMoves.length) { + const moveid = this.sample(stabMoves); + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } + } + + // Enforce recovery + if (['Bulky Support', 'Bulky Attacker', 'Bulky Setup', 'Staller'].includes(role)) { + const recoveryMoves = movePool.filter(moveid => RECOVERY_MOVES.includes(moveid)); + if (recoveryMoves.length) { + const moveid = this.sample(recoveryMoves); + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } + } + + // Enforce Staller moves + if (role === 'Staller') { + const enforcedMoves = ['protect', 'toxic', 'wish']; + for (const move of enforcedMoves) { + if (movePool.includes(move)) { + counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } + } + } + + // Enforce setup + if (role.includes('Setup') || role === 'Berry Sweeper') { + const setupMoves = movePool.filter(moveid => SETUP.includes(moveid)); + if (setupMoves.length) { + const moveid = this.sample(setupMoves); + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } + } + + // Enforce Berry Sweeper moves + if (role === 'Berry Sweeper') { + // Enforce Flail/Reversal + for (const move of ['flail', 'reversal']) { + if (movePool.includes(move)) { + counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } + } + // Enforce one of Endure and Substitute, but not both + const hpControlMoves = []; + for (const moveid of movePool) { + if (['endure', 'substitute'].includes(moveid)) hpControlMoves.push(moveid); + } + if (hpControlMoves.length) { + const moveid = this.sample(hpControlMoves); + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } + } + + // Enforce a move not on the noSTAB list + if (!counter.damagingMoves.size) { + // Choose an attacking move + const attackingMoves = []; + for (const moveid of movePool) { + const move = this.dex.moves.get(moveid); + if (!this.noStab.includes(moveid) && (move.category !== 'Status')) attackingMoves.push(moveid); + } + if (attackingMoves.length) { + const moveid = this.sample(attackingMoves); + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } + } + + // Enforce coverage move + if (['Fast Attacker', 'Setup Sweeper', 'Bulky Attacker', 'Wallbreaker', 'Berry Sweeper'].includes(role)) { + if (counter.damagingMoves.size === 1) { + // Find the type of the current attacking move + const currentAttackType = counter.damagingMoves.values().next().value.type; + // Choose an attacking move that is of different type to the current single attack + const coverageMoves = []; + for (const moveid of movePool) { + const move = this.dex.moves.get(moveid); + const moveType = this.getMoveType(move, species, abilities, preferredType); + if (!this.noStab.includes(moveid) && (move.basePower || move.basePowerCallback)) { + if (currentAttackType !== moveType) coverageMoves.push(moveid); + } + } + if (coverageMoves.length) { + const moveid = this.sample(coverageMoves); + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } + } + } + + // Choose remaining moves randomly from movepool and add them to moves list: + while (moves.size < this.maxMoveCount && movePool.length) { + const moveid = this.sample(movePool); + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + for (const pair of MOVE_PAIRS) { + if (moveid === pair[0] && movePool.includes(pair[1])) { + counter = this.addMove(pair[1], moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } + if (moveid === pair[1] && movePool.includes(pair[0])) { + counter = this.addMove(pair[0], moves, types, abilities, teamDetails, species, isLead, + movePool, preferredType, role); + } + } + } + return moves; } shouldCullAbility( @@ -310,31 +389,30 @@ export class RandomGen3Teams extends RandomGen4Teams { movePool: string[], teamDetails: RandomTeamsTypes.TeamDetails, species: Species, + preferredType: string, + role: RandomTeamsTypes.Role ) { switch (ability) { + case 'Rain Dish': case 'Sand Veil': case 'Soundproof': case 'Sticky Hold': + return true; case 'Chlorophyll': - return !moves.has('sunnyday') && !teamDetails['sun']; - case 'Compound Eyes': - return !counter.get('inaccurate'); + return !moves.has('sunnyday') && !teamDetails.sun; case 'Hustle': - return counter.get('Physical') < 2; - case 'Overgrow': - return !counter.get('Grass'); - case 'Rain Dish': case 'Swift Swim': - return !moves.has('raindance') && !teamDetails['rain']; + return !counter.get('Physical'); case 'Rock Head': return !counter.get('recoil'); - case 'Sand Veil': - return !teamDetails['sand']; - case 'Soundproof': - // Electrode prefers Static - return true; case 'Swarm': return !counter.get('Bug'); - case 'Torrent': - return !counter.get('Water'); + case 'Swift Swim': + return ( + // Relicanth always wants Swift Swim if it doesn't have Double-Edge + !moves.has('raindance') && !teamDetails.rain && !(species.id === 'relicanth' && !counter.get('recoil')) || + !moves.has('raindance') && ['Rock Head', 'Water Absorb'].some(abil => abilities.has(abil)) + ); + case 'Thick Fat': + return (species.id === 'snorlax' || (species.id === 'hariyama' && moves.has('sleeptalk'))); case 'Water Absorb': - return abilities.has('Swift Swim'); + return (species.id === 'mantine' && moves.has('raindance')); } return false; @@ -349,6 +427,8 @@ export class RandomGen3Teams extends RandomGen4Teams { movePool: string[], teamDetails: RandomTeamsTypes.TeamDetails, species: Species, + preferredType: string, + role: RandomTeamsTypes.Role, ): string { const abilityData = Array.from(abilities).map(a => this.dex.abilities.get(a)); Utils.sortBy(abilityData, abil => -abil.rating); @@ -356,14 +436,17 @@ export class RandomGen3Teams extends RandomGen4Teams { if (abilityData.length <= 1) return abilityData[0].name; // Hard-code abilities here - if (species.id === 'snorlax') return 'Immunity'; + if (species.id === 'yanma' && counter.get('inaccurate')) return 'Compound Eyes'; + if (species.id === 'arcanine') return 'Intimidate'; if (species.id === 'blissey') return 'Natural Cure'; + if (species.id === 'heracross' && role === 'Berry Sweeper') return 'Swarm'; + if (species.id === 'gardevoir') return 'Trace'; let abilityAllowed: Ability[] = []; // Obtain a list of abilities that are allowed (not culled) for (const ability of abilityData) { if (ability.rating >= 1 && !this.shouldCullAbility( - ability.name, types, moves, abilities, counter, movePool, teamDetails, species + ability.name, types, moves, abilities, counter, movePool, teamDetails, species, preferredType, role )) { abilityAllowed.push(ability); } @@ -389,162 +472,126 @@ export class RandomGen3Teams extends RandomGen4Teams { return abilityAllowed[0].name; } - randomSet(species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}): RandomTeamsTypes.RandomSet { - species = this.dex.species.get(species); - let forme = species.name; + getItem( + ability: string, + types: string[], + moves: Set, + counter: MoveCounter, + teamDetails: RandomTeamsTypes.TeamDetails, + species: Species, + isLead: boolean, + preferredType: string, + role: RandomTeamsTypes.Role, + ): string { + // First, the high-priority items + if (species.id === 'farfetchd') return 'Stick'; + if (species.id === 'latias' || species.id === 'latios') return 'Soul Dew'; + if (species.id === 'linoone' && role === 'Setup Sweeper') return 'Silk Scarf'; + if (species.id === 'marowak') return 'Thick Club'; + if (species.id === 'pikachu') return 'Light Ball'; + if (species.id === 'shedinja') return 'Lum Berry'; + if (species.id === 'unown') return counter.get('Physical') ? 'Choice Band' : 'Twisted Spoon'; + + if (moves.has('trick')) return 'Choice Band'; + if ( + moves.has('rest') && !moves.has('sleeptalk') && + // Altaria wants Chesto Berry on Dragon Dance + Rest + (moves.has('dragondance') || !['Early Bird', 'Natural Cure', 'Shed Skin'].includes(ability)) + ) return 'Chesto Berry'; + + // Medium priority items + if (counter.get('Physical') >= 4) return 'Choice Band'; + if (counter.get('Physical') >= 3 && (moves.has('batonpass') || (role === 'Wallbreaker' && counter.get('Special')))) { + return 'Choice Band'; + } - const data = this.randomData[species.id]; + if (moves.has('dragondance') && ability !== 'Natural Cure' && !moves.has('healbell')) return 'Lum Berry'; + if (moves.has('bellydrum')) return moves.has('substitute') ? 'Salac Berry' : 'Lum Berry'; - if (typeof species.battleOnly === 'string') forme = species.battleOnly; + if (moves.has('raindance') && counter.get('Special') >= 3) return 'Petaya Berry'; - const movePool: string[] = [...(data.moves || this.dex.species.getMovePool(species.id))]; - const rejectedPool = []; - const moves = new Set(); - let ability = ''; - const evs = {hp: 85, atk: 85, def: 85, spa: 85, spd: 85, spe: 85}; - const ivs = {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31}; - let availableHP = 0; - for (const setMoveid of movePool) { - if (setMoveid.startsWith('hiddenpower')) availableHP++; + if (role === 'Berry Sweeper') { + if (moves.has('endure')) return 'Salac Berry'; + if (moves.has('flail') || moves.has('reversal')) return (species.baseStats.spe >= 90) ? 'Liechi Berry' : 'Salac Berry'; + if (moves.has('substitute') && counter.get('Physical') >= 3) return 'Liechi Berry'; + if (moves.has('substitute') && counter.get('Special') >= 3) return 'Petaya Berry'; } - const types = new Set(species.types); + const salacReqs = species.baseStats.spe >= 60 && species.baseStats.spe <= 100 && !counter.get('priority'); - const abilities = new Set(Object.values(species.abilities)); + if (moves.has('bulkup') && moves.has('substitute') && counter.get('Status') === 2 && salacReqs) return 'Salac Berry'; - let counter: MoveCounter; - // We use a special variable to track Hidden Power - // so that we can check for all Hidden Powers at once - let hasHiddenPower = false; + if (moves.has('swordsdance') && moves.has('substitute') && counter.get('Status') === 2) { + if (salacReqs) return 'Salac Berry'; + if (species.baseStats.spe > 100 && counter.get('Physical') >= 2) return 'Liechi Berry'; + } - do { - // Choose next 4 moves from learnset/viable moves and add them to moves list: - while (moves.size < this.maxMoveCount && movePool.length) { - const moveid = this.sampleNoReplace(movePool); - if (moveid.startsWith('hiddenpower')) { - availableHP--; - if (hasHiddenPower) continue; - hasHiddenPower = true; - } - moves.add(moveid); + if (moves.has('swordsdance') && counter.get('Status') === 1) { + if (salacReqs) return 'Salac Berry'; + if (species.baseStats.spe > 100) { + return (counter.get('Physical') >= 3 && this.randomChance(1, 2)) ? 'Liechi Berry' : 'Lum Berry'; } + } - while (moves.size < this.maxMoveCount && rejectedPool.length) { - const moveid = this.sampleNoReplace(rejectedPool); - if (moveid.startsWith('hiddenpower')) { - if (hasHiddenPower) continue; - hasHiddenPower = true; - } - moves.add(moveid); - } + if (species.id === 'deoxys' || species.id === 'deoxysattack') return 'White Herb'; + + // Default to Leftovers + return 'Leftovers'; + } - counter = this.queryMoves(moves, species.types, abilities, movePool); + randomSet( + species: string | Species, + teamDetails: RandomTeamsTypes.TeamDetails = {}, + isLead = false + ): RandomTeamsTypes.RandomSet { + species = this.dex.species.get(species); + let forme = species.name; - // Iterate through the moves again, this time to cull them: - for (const moveid of moves) { - const move = this.dex.moves.get(moveid); + if (typeof species.battleOnly === 'string') { + // Only change the forme. The species has custom moves, and may have different typing and requirements. + forme = species.battleOnly; + } + if (species.cosmeticFormes) { + forme = this.sample([species.name].concat(species.cosmeticFormes)); + } + const sets = this.randomSets[species.id]["sets"]; - let {cull, isSetup} = this.shouldCullMove(move, types, moves, abilities, counter, movePool, teamDetails, species); + const set = this.sampleIfArray(sets); + const role = set.role; + const movePool: string[] = Array.from(set.movepool); + const preferredTypes = set.preferredTypes; + // In Gen 3, if a set has multiple preferred types, enforce all of them. + const preferredType = preferredTypes ? preferredTypes.join() : ''; - // This move doesn't satisfy our setup requirements: - if ( - (counter.setupType === 'Physical' && move.category === 'Special' && !types.has(move.type) && move.type !== 'Fire') || - (counter.setupType === 'Special' && move.category === 'Physical' && moveid !== 'superpower') - ) { - cull = true; - } - const moveIsRejectable = ( - !move.weather && - (move.category !== 'Status' || !move.flags.heal) && - (counter.setupType || !move.stallingMove) && - // These moves cannot be rejected in favor of a forced move - !['batonpass', 'sleeptalk', 'solarbeam', 'substitute', 'sunnyday'].includes(moveid) && - (move.category === 'Status' || !types.has(move.type) || (move.basePower && move.basePower < 40 && !move.multihit)) - ); - // Pokemon should usually have at least one STAB move - const requiresStab = ( - !counter.get('stab') && - !moves.has('seismictoss') && !moves.has('nightshade') && - species.id !== 'umbreon' && - // If a Flying-type has Psychic, it doesn't need STAB - !(moves.has('psychic') && types.has('Flying')) && - !(types.has('Ghost') && species.baseStats.spa > species.baseStats.atk) && - !( - // With Calm Mind, Lugia and pure Normal-types are fine without STAB - counter.setupType === 'Special' && ( - species.id === 'lugia' || - (types.has('Normal') && species.types.length < 2) - ) - ) && - !( - // With Swords Dance, Dark-types and pure Water-types are fine without STAB - counter.setupType === 'Physical' && - ((types.has('Water') && species.types.length < 2) || types.has('Dark')) - ) && - counter.get('physicalpool') + counter.get('specialpool') > 0 - ); - - const runEnforcementChecker = (checkerName: string) => { - if (!this.moveEnforcementCheckers[checkerName]) return false; - return this.moveEnforcementCheckers[checkerName]( - movePool, moves, abilities, types, counter, species as Species, teamDetails - ); - }; - - if (!cull && !isSetup && moveIsRejectable) { - // There may be more important moves that this Pokemon needs - if ( - requiresStab || - (counter.setupType && counter.get(counter.setupType) < 2 && !moves.has('refresh')) || - (moves.has('substitute') && movePool.includes('morningsun')) || - ['meteormash', 'spore', 'recover'].some(m => movePool.includes(m)) - ) { - cull = true; - } else { - // Pokemon should have moves that benefit their typing and their other moves - for (const type of types) { - if (runEnforcementChecker(type)) { - cull = true; - } - } - for (const m of moves) { - if (runEnforcementChecker(m)) cull = true; - } - } - } + let ability = ''; + let item = undefined; - // Sleep Talk shouldn't be selected without Rest - if (moveid === 'rest' && cull) { - const sleeptalk = movePool.indexOf('sleeptalk'); - if (sleeptalk >= 0) { - if (movePool.length < 2) { - cull = false; - } else { - this.fastPop(movePool, sleeptalk); - } - } - } + const evs = {hp: 85, atk: 85, def: 85, spa: 85, spd: 85, spe: 85}; + const ivs = {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31}; - // Remove rejected moves from the move list - const moveIsHP = moveid.startsWith('hiddenpower'); - if ( - cull && - (movePool.length - availableHP || availableHP && (moveIsHP || !hasHiddenPower)) - ) { - if (move.category !== 'Status' && !move.damage && (!moveIsHP || !availableHP)) { - rejectedPool.push(moveid); - } - if (moveIsHP) hasHiddenPower = false; - moves.delete(moveid); - break; - } - if (cull && rejectedPool.length) { - if (moveIsHP) hasHiddenPower = false; - moves.delete(moveid); - break; - } - } - } while (moves.size < this.maxMoveCount && (movePool.length || rejectedPool.length)); + const types = species.types; + const abilities = new Set(Object.values(species.abilities)); + + // Get moves + const moves = this.randomMoveset(types, abilities, teamDetails, species, isLead, movePool, + preferredType, role); + const counter = this.newQueryMoves(moves, species, preferredType, abilities); + + // Get ability + ability = this.getAbility(new Set(types), moves, abilities, counter, movePool, teamDetails, species, + preferredType, role); + + // Get items + item = this.getItem(ability, types, moves, counter, teamDetails, species, isLead, preferredType, role); + + const level = this.adjustLevel || this.randomSets[species.id]["level"] || (species.nfe ? 90 : 80); + + // We use a special variable to track Hidden Power + // so that we can check for all Hidden Powers at once + let hasHiddenPower = false; + for (const move of moves) { + if (move.startsWith('hiddenpower')) hasHiddenPower = true; + } if (hasHiddenPower) { let hpType; @@ -559,10 +606,29 @@ export class RandomGen3Teams extends RandomGen4Teams { } } - ability = this.getAbility(types, moves, abilities, counter, movePool, teamDetails, species); + // Prepare optimal HP + while (evs.hp > 1) { + const hp = Math.floor(Math.floor(2 * species.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10); + if (moves.has('substitute') && ['flail', 'reversal'].some(m => moves.has(m))) { + // Flail/Reversal users should be able to use four Substitutes + if (hp % 4 > 0) break; + } else if (moves.has('substitute') && (item === 'Salac Berry' || item === 'Petaya Berry' || item === 'Liechi Berry')) { + // Other pinch berry holders should have berries activate after three Substitutes + if (hp % 4 === 0) break; + } else if (moves.has('bellydrum')) { + // Belly Drum users should be able to use Belly Drum twice + if (hp % 2 > 0) break; + } else { + break; + } + evs.hp -= 4; + } - const item = this.getItem(ability, species.types, moves, counter, teamDetails, species); - const level = this.adjustLevel || data.level || (species.nfe ? 90 : 80); + // Minimize confusion damage + if (!counter.get('Physical') && !moves.has('transform')) { + evs.atk = 0; + ivs.atk = hasHiddenPower ? (ivs.atk || 31) - 28 : 0; + } // Prepare optimal HP let hp = Math.floor(Math.floor(2 * species.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10); @@ -577,23 +643,22 @@ export class RandomGen3Teams extends RandomGen4Teams { } } - // Minimize confusion damage - if (!counter.get('Physical') && !moves.has('transform')) { - evs.atk = 0; - ivs.atk = hasHiddenPower ? ivs.atk - 28 : 0; - } + // shuffle moves to add more randomness to camomons + const shuffledMoves = Array.from(moves); + this.prng.shuffle(shuffledMoves); return { name: species.baseSpecies, species: forme, gender: species.gender, - moves: Array.from(moves), - ability: ability, - evs: evs, - ivs: ivs, - item: item, - level, shiny: this.randomChance(1, 1024), + level, + moves: shuffledMoves, + ability, + evs, + ivs, + item, + role, }; } @@ -610,13 +675,12 @@ export class RandomGen3Teams extends RandomGen4Teams { const type = this.forceMonotype || this.sample(typePool); const baseFormes: {[k: string]: number} = {}; - const tierCount: {[k: string]: number} = {}; const typeCount: {[k: string]: number} = {}; - const typeComboCount: {[k: string]: number} = {}; const typeWeaknesses: {[k: string]: number} = {}; const teamDetails: RandomTeamsTypes.TeamDetails = {}; - const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, Object.keys(this.randomData)); + const pokemonList = (this.gen === 3) ? Object.keys(this.randomSets) : Object.keys(this.randomData); + const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { const baseSpecies = this.sampleNoReplace(baseSpeciesPool); const currentSpeciesPool: Species[] = []; @@ -635,17 +699,12 @@ export class RandomGen3Teams extends RandomGen4Teams { // Limit to one Ditto per battle in Gen 2 if (this.dex.gen < 3 && species.name === 'Ditto' && this.battleHasDitto) continue; - const tier = species.tier; const types = species.types; - const typeCombo = types.slice().sort().join(); if (!isMonotype && !this.forceMonotype) { // Dynamically scale limits for different team sizes. The default and minimum value is 1. const limitFactor = Math.round(this.maxTeamSize / 6) || 1; - // Limit two Pokemon per tier - if (tierCount[tier] >= 2 * limitFactor) continue; - // Limit two of any type let skip = false; for (const typeName of types) { @@ -668,25 +727,18 @@ export class RandomGen3Teams extends RandomGen4Teams { } } if (skip) continue; - - // Limit one of any type combination - if (!this.forceMonotype && typeComboCount[typeCombo] >= 1 * limitFactor) continue; } // Okay, the set passes, add it to our team const set = this.randomSet(species, teamDetails); pokemon.push(set); + // Don't bother tracking details for the last Pokemon + if (pokemon.length === this.maxTeamSize) break; + // Now that our Pokemon has passed all checks, we can increment our counters baseFormes[species.baseSpecies] = 1; - // Increment tier counter - if (tierCount[tier]) { - tierCount[tier]++; - } else { - tierCount[tier] = 1; - } - // Increment type counters for (const typeName of types) { if (typeName in typeCount) { @@ -695,11 +747,6 @@ export class RandomGen3Teams extends RandomGen4Teams { typeCount[typeName] = 1; } } - if (typeCombo in typeComboCount) { - typeComboCount[typeCombo]++; - } else { - typeComboCount[typeCombo] = 1; - } // Increment weakness counter for (const typeName of this.dex.types.names()) { @@ -711,14 +758,14 @@ export class RandomGen3Teams extends RandomGen4Teams { // Update team details if (set.ability === 'Drizzle' || set.moves.includes('raindance')) teamDetails.rain = 1; + if (set.ability === 'Drought' || set.moves.includes('sunnyday')) teamDetails.sun = 1; if (set.ability === 'Sand Stream') teamDetails.sand = 1; if (set.moves.includes('spikes')) teamDetails.spikes = 1; if (set.moves.includes('rapidspin')) teamDetails.rapidSpin = 1; - if (set.moves.includes('aromatherapy') || set.moves.includes('healbell')) teamDetails.statusCure = 1; // In Gen 3, Shadow Tag users can prevent each other from switching out, possibly causing and endless battle or at least causing a long stall war // To prevent this, we prevent more than one Wobbuffet in a single battle. - if (set.ability === 'Shadow Tag') this.battleHasWobbuffet = true; + if (species.id === 'wobbuffet') this.battleHasWobbuffet = true; if (species.id === 'ditto') this.battleHasDitto = true; } diff --git a/data/mods/gen4/random-teams.ts b/data/mods/gen4/random-teams.ts index 47cb75fb713f..4e3bde963fcc 100644 --- a/data/mods/gen4/random-teams.ts +++ b/data/mods/gen4/random-teams.ts @@ -88,7 +88,6 @@ export class RandomGen4Teams extends RandomGen5Teams { teamDetails: RandomTeamsTypes.TeamDetails, species: Species, isLead: boolean, - isDoubles: boolean, preferredType: string, role: RandomTeamsTypes.Role, ): void { @@ -161,7 +160,6 @@ export class RandomGen4Teams extends RandomGen5Teams { // Develop additional move lists const badWithSetup = ['healbell', 'pursuit', 'toxic']; - // Nature Power is Earthquake this gen const statusMoves = this.dex.moves.all() .filter(move => move.category === 'Status') .map(move => move.id); @@ -220,14 +218,13 @@ export class RandomGen4Teams extends RandomGen5Teams { teamDetails: RandomTeamsTypes.TeamDetails, species: Species, isLead: boolean, - isDoubles: boolean, movePool: string[], preferredType: string, role: RandomTeamsTypes.Role, ): Set { const moves = new Set(); let counter = this.newQueryMoves(moves, species, preferredType, abilities); - this.cullMovePool(types, moves, abilities, counter, movePool, teamDetails, species, isLead, isDoubles, + this.cullMovePool(types, moves, abilities, counter, movePool, teamDetails, species, isLead, preferredType, role); // If there are only four moves, add all moves and return early @@ -235,7 +232,7 @@ export class RandomGen4Teams extends RandomGen5Teams { // Still need to ensure that multiple Hidden Powers are not added (if maxMoveCount is increased) while (movePool.length) { const moveid = this.sample(movePool); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } return moves; @@ -251,7 +248,7 @@ export class RandomGen4Teams extends RandomGen5Teams { // Add required move (e.g. Relic Song for Meloetta-P) if (species.requiredMove) { const move = this.dex.moves.get(species.requiredMove).id; - counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } @@ -259,14 +256,14 @@ export class RandomGen4Teams extends RandomGen5Teams { // Enforce Facade if Guts is a possible ability if (movePool.includes('facade') && abilities.has('Guts')) { - counter = this.addMove('facade', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('facade', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } // Enforce Seismic Toss, Spore, and Volt Tackle for (const moveid of ['seismictoss', 'spore', 'volttackle']) { if (movePool.includes(moveid)) { - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -274,7 +271,7 @@ export class RandomGen4Teams extends RandomGen5Teams { // Enforce Substitute on non-Setup sets with Baton Pass if (!role.includes('Setup')) { if (movePool.includes('batonpass') && movePool.includes('substitute')) { - counter = this.addMove('substitute', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('substitute', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -282,7 +279,7 @@ export class RandomGen4Teams extends RandomGen5Teams { // Enforce hazard removal on Bulky Support and Spinner if the team doesn't already have it if (['Bulky Support', 'Spinner'].includes(role) && !teamDetails.rapidSpin) { if (movePool.includes('rapidspin')) { - counter = this.addMove('rapidspin', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('rapidspin', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -299,7 +296,7 @@ export class RandomGen4Teams extends RandomGen5Teams { } if (priorityMoves.length) { const moveid = this.sample(priorityMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -318,7 +315,7 @@ export class RandomGen4Teams extends RandomGen5Teams { while (runEnforcementChecker(type)) { if (!stabMoves.length) break; const moveid = this.sampleNoReplace(stabMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -335,7 +332,7 @@ export class RandomGen4Teams extends RandomGen5Teams { } if (stabMoves.length) { const moveid = this.sample(stabMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -352,12 +349,12 @@ export class RandomGen4Teams extends RandomGen5Teams { } if (stabMoves.length) { const moveid = this.sample(stabMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } else { // If they have no regular STAB move, enforce U-turn on Bug types. if (movePool.includes('uturn') && types.includes('Bug')) { - counter = this.addMove('uturn', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('uturn', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -368,7 +365,7 @@ export class RandomGen4Teams extends RandomGen5Teams { const recoveryMoves = movePool.filter(moveid => RECOVERY_MOVES.includes(moveid)); if (recoveryMoves.length) { const moveid = this.sample(recoveryMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -378,7 +375,7 @@ export class RandomGen4Teams extends RandomGen5Teams { const enforcedMoves = ['protect', 'toxic', 'wish']; for (const move of enforcedMoves) { if (movePool.includes(move)) { - counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -389,7 +386,7 @@ export class RandomGen4Teams extends RandomGen5Teams { const setupMoves = movePool.filter(moveid => SETUP.includes(moveid)); if (setupMoves.length) { const moveid = this.sample(setupMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -404,7 +401,7 @@ export class RandomGen4Teams extends RandomGen5Teams { } if (attackingMoves.length) { const moveid = this.sample(attackingMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -425,7 +422,7 @@ export class RandomGen4Teams extends RandomGen5Teams { } if (coverageMoves.length) { const moveid = this.sample(coverageMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -434,15 +431,15 @@ export class RandomGen4Teams extends RandomGen5Teams { // Choose remaining moves randomly from movepool and add them to moves list: while (moves.size < this.maxMoveCount && movePool.length) { const moveid = this.sample(movePool); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); for (const pair of MOVE_PAIRS) { if (moveid === pair[0] && movePool.includes(pair[1])) { - counter = this.addMove(pair[1], moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(pair[1], moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } if (moveid === pair[1] && movePool.includes(pair[0])) { - counter = this.addMove(pair[0], moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(pair[0], moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -459,7 +456,6 @@ export class RandomGen4Teams extends RandomGen5Teams { movePool: string[], teamDetails: RandomTeamsTypes.TeamDetails, species: Species, - isDoubles: boolean, preferredType: string, role: RandomTeamsTypes.Role ): boolean { @@ -498,7 +494,6 @@ export class RandomGen4Teams extends RandomGen5Teams { movePool: string[], teamDetails: RandomTeamsTypes.TeamDetails, species: Species, - isDoubles: boolean, preferredType: string, role: RandomTeamsTypes.Role, ): string { @@ -523,7 +518,7 @@ export class RandomGen4Teams extends RandomGen5Teams { // Obtain a list of abilities that are allowed (not culled) for (const ability of abilityData) { if (ability.rating >= 1 && !this.shouldCullAbility( - ability.name, types, moves, abilities, counter, movePool, teamDetails, species, isDoubles, preferredType, role + ability.name, types, moves, abilities, counter, movePool, teamDetails, species, preferredType, role )) { abilityAllowed.push(ability); } @@ -667,8 +662,7 @@ export class RandomGen4Teams extends RandomGen5Teams { randomSet( species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}, - isLead = false, - isDoubles = false + isLead = false ): RandomTeamsTypes.RandomSet { species = this.dex.species.get(species); let forme = species.name; @@ -711,13 +705,13 @@ export class RandomGen4Teams extends RandomGen5Teams { if (species.unreleasedHidden) abilities.delete(species.abilities.H); // Get moves - const moves = this.randomMoveset(types, abilities, teamDetails, species, isLead, isDoubles, movePool, + const moves = this.randomMoveset(types, abilities, teamDetails, species, isLead, movePool, preferredType, role); const counter = this.newQueryMoves(moves, species, preferredType, abilities); // Get ability ability = this.getAbility(new Set(types), moves, abilities, counter, movePool, teamDetails, species, - false, preferredType, role); + preferredType, role); // Get items item = this.getPriorityItem(ability, types, moves, counter, teamDetails, species, isLead, preferredType, role); @@ -754,9 +748,7 @@ export class RandomGen4Teams extends RandomGen5Teams { // Prepare optimal HP const srImmunity = ability === 'Magic Guard'; - let srWeakness = srImmunity ? 0 : this.dex.getEffectiveness('Rock', species); - // Crash damage move users want an odd HP to survive two misses - if (['highjumpkick', 'jumpkick'].some(m => moves.has(m))) srWeakness = 2; + const srWeakness = srImmunity ? 0 : this.dex.getEffectiveness('Rock', species); while (evs.hp > 1) { const hp = Math.floor(Math.floor(2 * species.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10); if (moves.has('substitute') && item === 'Sitrus Berry') { diff --git a/data/mods/gen5/random-teams.ts b/data/mods/gen5/random-teams.ts index 41339fb34d7d..dc90ddde59c3 100644 --- a/data/mods/gen5/random-teams.ts +++ b/data/mods/gen5/random-teams.ts @@ -103,7 +103,6 @@ export class RandomGen5Teams extends RandomGen6Teams { teamDetails: RandomTeamsTypes.TeamDetails, species: Species, isLead: boolean, - isDoubles: boolean, preferredType: string, role: RandomTeamsTypes.Role, ): void { @@ -240,14 +239,13 @@ export class RandomGen5Teams extends RandomGen6Teams { teamDetails: RandomTeamsTypes.TeamDetails, species: Species, isLead: boolean, - isDoubles: boolean, movePool: string[], preferredType: string, role: RandomTeamsTypes.Role, ): Set { const moves = new Set(); let counter = this.newQueryMoves(moves, species, preferredType, abilities); - this.cullMovePool(types, moves, abilities, counter, movePool, teamDetails, species, isLead, isDoubles, + this.cullMovePool(types, moves, abilities, counter, movePool, teamDetails, species, isLead, preferredType, role); // If there are only four moves, add all moves and return early @@ -255,7 +253,7 @@ export class RandomGen5Teams extends RandomGen6Teams { // Still need to ensure that multiple Hidden Powers are not added (if maxMoveCount is increased) while (movePool.length) { const moveid = this.sample(movePool); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } return moves; @@ -271,7 +269,7 @@ export class RandomGen5Teams extends RandomGen6Teams { // Add required move (e.g. Relic Song for Meloetta-P) if (species.requiredMove) { const move = this.dex.moves.get(species.requiredMove).id; - counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } @@ -279,28 +277,28 @@ export class RandomGen5Teams extends RandomGen6Teams { // Enforce Facade if Guts is a possible ability if (movePool.includes('facade') && abilities.has('Guts')) { - counter = this.addMove('facade', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('facade', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } // Enforce Seismic Toss and Spore for (const moveid of ['seismictoss', 'spore']) { if (movePool.includes(moveid)) { - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } // Enforce Thunder Wave on Prankster users if (movePool.includes('thunderwave') && abilities.has('Prankster')) { - counter = this.addMove('thunderwave', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('thunderwave', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } // Enforce hazard removal on Bulky Support and Spinner if the team doesn't already have it if (['Bulky Support', 'Spinner'].includes(role) && !teamDetails.rapidSpin) { if (movePool.includes('rapidspin')) { - counter = this.addMove('rapidspin', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('rapidspin', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -317,7 +315,7 @@ export class RandomGen5Teams extends RandomGen6Teams { } if (priorityMoves.length) { const moveid = this.sample(priorityMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -336,7 +334,7 @@ export class RandomGen5Teams extends RandomGen6Teams { while (runEnforcementChecker(type)) { if (!stabMoves.length) break; const moveid = this.sampleNoReplace(stabMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -353,7 +351,7 @@ export class RandomGen5Teams extends RandomGen6Teams { } if (stabMoves.length) { const moveid = this.sample(stabMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -370,12 +368,12 @@ export class RandomGen5Teams extends RandomGen6Teams { } if (stabMoves.length) { const moveid = this.sample(stabMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } else { // If they have no regular STAB move, enforce U-turn on Bug types. if (movePool.includes('uturn') && types.includes('Bug')) { - counter = this.addMove('uturn', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('uturn', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -386,7 +384,7 @@ export class RandomGen5Teams extends RandomGen6Teams { const recoveryMoves = movePool.filter(moveid => RECOVERY_MOVES.includes(moveid)); if (recoveryMoves.length) { const moveid = this.sample(recoveryMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -396,7 +394,7 @@ export class RandomGen5Teams extends RandomGen6Teams { const enforcedMoves = ['protect', 'toxic', 'wish']; for (const move of enforcedMoves) { if (movePool.includes(move)) { - counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -408,14 +406,14 @@ export class RandomGen5Teams extends RandomGen6Teams { const nonSpeedSetupMoves = movePool.filter(moveid => SETUP.includes(moveid) && !SPEED_SETUP.includes(moveid)); if (nonSpeedSetupMoves.length) { const moveid = this.sample(nonSpeedSetupMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } else { // No non-Speed setup moves, so add any (Speed) setup move const setupMoves = movePool.filter(moveid => SETUP.includes(moveid)); if (setupMoves.length) { const moveid = this.sample(setupMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -431,7 +429,7 @@ export class RandomGen5Teams extends RandomGen6Teams { } if (attackingMoves.length) { const moveid = this.sample(attackingMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -452,7 +450,7 @@ export class RandomGen5Teams extends RandomGen6Teams { } if (coverageMoves.length) { const moveid = this.sample(coverageMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -461,15 +459,15 @@ export class RandomGen5Teams extends RandomGen6Teams { // Choose remaining moves randomly from movepool and add them to moves list: while (moves.size < this.maxMoveCount && movePool.length) { const moveid = this.sample(movePool); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); for (const pair of MOVE_PAIRS) { if (moveid === pair[0] && movePool.includes(pair[1])) { - counter = this.addMove(pair[1], moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(pair[1], moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } if (moveid === pair[1] && movePool.includes(pair[0])) { - counter = this.addMove(pair[0], moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(pair[0], moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -486,7 +484,6 @@ export class RandomGen5Teams extends RandomGen6Teams { movePool: string[], teamDetails: RandomTeamsTypes.TeamDetails, species: Species, - isDoubles: boolean, preferredType: string, role: RandomTeamsTypes.Role ): boolean { @@ -582,7 +579,6 @@ export class RandomGen5Teams extends RandomGen6Teams { movePool: string[], teamDetails: RandomTeamsTypes.TeamDetails, species: Species, - isDoubles: boolean, preferredType: string, role: RandomTeamsTypes.Role, ): string { @@ -619,7 +615,7 @@ export class RandomGen5Teams extends RandomGen6Teams { // Obtain a list of abilities that are allowed (not culled) for (const ability of abilityData) { if (ability.rating >= 1 && !this.shouldCullAbility( - ability.name, types, moves, abilities, counter, movePool, teamDetails, species, isDoubles, preferredType, role + ability.name, types, moves, abilities, counter, movePool, teamDetails, species, preferredType, role )) { abilityAllowed.push(ability); } @@ -798,8 +794,7 @@ export class RandomGen5Teams extends RandomGen6Teams { randomSet( species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}, - isLead = false, - isDoubles = false + isLead = false ): RandomTeamsTypes.RandomSet { species = this.dex.species.get(species); let forme = species.name; @@ -842,13 +837,13 @@ export class RandomGen5Teams extends RandomGen6Teams { if (species.unreleasedHidden) abilities.delete(species.abilities.H); // Get moves - const moves = this.randomMoveset(types, abilities, teamDetails, species, isLead, isDoubles, movePool, + const moves = this.randomMoveset(types, abilities, teamDetails, species, isLead, movePool, preferredType, role); const counter = this.newQueryMoves(moves, species, preferredType, abilities); // Get ability ability = this.getAbility(new Set(types), moves, abilities, counter, movePool, teamDetails, species, - false, preferredType, role); + preferredType, role); // Get items item = this.getPriorityItem(ability, types, moves, counter, teamDetails, species, isLead, preferredType, role); diff --git a/data/mods/gen6/random-teams.ts b/data/mods/gen6/random-teams.ts index 1a08c48605d2..9ca81cac47e7 100644 --- a/data/mods/gen6/random-teams.ts +++ b/data/mods/gen6/random-teams.ts @@ -119,7 +119,6 @@ export class RandomGen6Teams extends RandomGen7Teams { teamDetails: RandomTeamsTypes.TeamDetails, species: Species, isLead: boolean, - isDoubles: boolean, preferredType: string, role: RandomTeamsTypes.Role, ): void { @@ -278,14 +277,13 @@ export class RandomGen6Teams extends RandomGen7Teams { teamDetails: RandomTeamsTypes.TeamDetails, species: Species, isLead: boolean, - isDoubles: boolean, movePool: string[], preferredType: string, role: RandomTeamsTypes.Role, ): Set { const moves = new Set(); let counter = this.newQueryMoves(moves, species, preferredType, abilities); - this.cullMovePool(types, moves, abilities, counter, movePool, teamDetails, species, isLead, isDoubles, + this.cullMovePool(types, moves, abilities, counter, movePool, teamDetails, species, isLead, preferredType, role); // If there are only four moves, add all moves and return early @@ -293,7 +291,7 @@ export class RandomGen6Teams extends RandomGen7Teams { // Still need to ensure that multiple Hidden Powers are not added (if maxMoveCount is increased) while (movePool.length) { const moveid = this.sample(movePool); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } return moves; @@ -309,7 +307,7 @@ export class RandomGen6Teams extends RandomGen7Teams { // Add required move (e.g. Relic Song for Meloetta-P) if (species.requiredMove) { const move = this.dex.moves.get(species.requiredMove).id; - counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } @@ -317,38 +315,38 @@ export class RandomGen6Teams extends RandomGen7Teams { // Enforce Facade if Guts is a possible ability if (movePool.includes('facade') && abilities.has('Guts')) { - counter = this.addMove('facade', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('facade', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } // Enforce Blizzard, Seismic Toss, Spore, and Sticky Web for (const moveid of ['blizzard', 'seismictoss', 'spore', 'stickyweb']) { if (movePool.includes(moveid)) { - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } // Enforce Thunder Wave on Prankster users if (movePool.includes('thunderwave') && abilities.has('Prankster')) { - counter = this.addMove('thunderwave', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('thunderwave', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } // Enforce Shadow Sneak on Kecleon if (movePool.includes('shadowsneak') && species.id === 'kecleon') { - counter = this.addMove('shadowsneak', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('shadowsneak', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } // Enforce hazard removal on Bulky Support if the team doesn't already have it if (role === 'Bulky Support' && !teamDetails.defog && !teamDetails.rapidSpin) { if (movePool.includes('rapidspin')) { - counter = this.addMove('rapidspin', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('rapidspin', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } if (movePool.includes('defog')) { - counter = this.addMove('defog', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('defog', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -365,7 +363,7 @@ export class RandomGen6Teams extends RandomGen7Teams { } if (priorityMoves.length) { const moveid = this.sample(priorityMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -384,7 +382,7 @@ export class RandomGen6Teams extends RandomGen7Teams { while (runEnforcementChecker(type)) { if (!stabMoves.length) break; const moveid = this.sampleNoReplace(stabMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -401,7 +399,7 @@ export class RandomGen6Teams extends RandomGen7Teams { } if (stabMoves.length) { const moveid = this.sample(stabMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -418,12 +416,12 @@ export class RandomGen6Teams extends RandomGen7Teams { } if (stabMoves.length) { const moveid = this.sample(stabMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } else { // If they have no regular STAB move, enforce U-turn on Bug types. if (movePool.includes('uturn') && types.includes('Bug')) { - counter = this.addMove('uturn', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('uturn', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -434,7 +432,7 @@ export class RandomGen6Teams extends RandomGen7Teams { const recoveryMoves = movePool.filter(moveid => RECOVERY_MOVES.includes(moveid)); if (recoveryMoves.length) { const moveid = this.sample(recoveryMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -444,7 +442,7 @@ export class RandomGen6Teams extends RandomGen7Teams { const enforcedMoves = [...PROTECT_MOVES, 'toxic', 'wish']; for (const move of enforcedMoves) { if (movePool.includes(move)) { - counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -455,7 +453,7 @@ export class RandomGen6Teams extends RandomGen7Teams { const setupMoves = movePool.filter(moveid => SETUP.includes(moveid)); if (setupMoves.length) { const moveid = this.sample(setupMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -470,7 +468,7 @@ export class RandomGen6Teams extends RandomGen7Teams { } if (attackingMoves.length) { const moveid = this.sample(attackingMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -491,7 +489,7 @@ export class RandomGen6Teams extends RandomGen7Teams { } if (coverageMoves.length) { const moveid = this.sample(coverageMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -500,15 +498,15 @@ export class RandomGen6Teams extends RandomGen7Teams { // Choose remaining moves randomly from movepool and add them to moves list: while (moves.size < this.maxMoveCount && movePool.length) { const moveid = this.sample(movePool); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); for (const pair of MOVE_PAIRS) { if (moveid === pair[0] && movePool.includes(pair[1])) { - counter = this.addMove(pair[1], moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(pair[1], moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } if (moveid === pair[1] && movePool.includes(pair[0])) { - counter = this.addMove(pair[0], moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(pair[0], moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -525,7 +523,6 @@ export class RandomGen6Teams extends RandomGen7Teams { movePool: string[], teamDetails: RandomTeamsTypes.TeamDetails, species: Species, - isDoubles: boolean, preferredType: string, role: RandomTeamsTypes.Role ): boolean { @@ -626,7 +623,6 @@ export class RandomGen6Teams extends RandomGen7Teams { movePool: string[], teamDetails: RandomTeamsTypes.TeamDetails, species: Species, - isDoubles: boolean, preferredType: string, role: RandomTeamsTypes.Role, ): string { @@ -672,7 +668,7 @@ export class RandomGen6Teams extends RandomGen7Teams { // Obtain a list of abilities that are allowed (not culled) for (const ability of abilityData) { if (ability.rating >= 1 && !this.shouldCullAbility( - ability.name, types, moves, abilities, counter, movePool, teamDetails, species, isDoubles, preferredType, role + ability.name, types, moves, abilities, counter, movePool, teamDetails, species, preferredType, role )) { abilityAllowed.push(ability); } @@ -860,8 +856,7 @@ export class RandomGen6Teams extends RandomGen7Teams { randomSet( species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}, - isLead = false, - isDoubles = false + isLead = false ): RandomTeamsTypes.RandomSet { species = this.dex.species.get(species); let forme = species.name; @@ -893,13 +888,13 @@ export class RandomGen6Teams extends RandomGen7Teams { if (species.unreleasedHidden) abilities.delete(species.abilities.H); // Get moves - const moves = this.randomMoveset(types, abilities, teamDetails, species, isLead, isDoubles, movePool, + const moves = this.randomMoveset(types, abilities, teamDetails, species, isLead, movePool, preferredType, role); const counter = this.newQueryMoves(moves, species, preferredType, abilities); // Get ability ability = this.getAbility(new Set(types), moves, abilities, counter, movePool, teamDetails, species, - false, preferredType, role); + preferredType, role); // Get items item = this.getPriorityItem(ability, types, moves, counter, teamDetails, species, isLead, preferredType, role); diff --git a/data/mods/gen7/random-teams.ts b/data/mods/gen7/random-teams.ts index eb29ecb7c499..e96e500a4aba 100644 --- a/data/mods/gen7/random-teams.ts +++ b/data/mods/gen7/random-teams.ts @@ -235,7 +235,6 @@ export class RandomGen7Teams extends RandomGen8Teams { teamDetails: RandomTeamsTypes.TeamDetails, species: Species, isLead: boolean, - isDoubles: boolean, preferredType: string, role: RandomTeamsTypes.Role, ): void { @@ -426,7 +425,6 @@ export class RandomGen7Teams extends RandomGen8Teams { teamDetails: RandomTeamsTypes.TeamDetails, species: Species, isLead: boolean, - isDoubles: boolean, movePool: string[], preferredType: string, role: RandomTeamsTypes.Role, @@ -434,7 +432,7 @@ export class RandomGen7Teams extends RandomGen8Teams { moves.add(move); this.fastPop(movePool, movePool.indexOf(move)); const counter = this.newQueryMoves(moves, species, preferredType, abilities); - this.cullMovePool(types, moves, abilities, counter, movePool, teamDetails, species, isLead, isDoubles, + this.cullMovePool(types, moves, abilities, counter, movePool, teamDetails, species, isLead, preferredType, role); return counter; } @@ -461,14 +459,13 @@ export class RandomGen7Teams extends RandomGen8Teams { teamDetails: RandomTeamsTypes.TeamDetails, species: Species, isLead: boolean, - isDoubles: boolean, movePool: string[], preferredType: string, role: RandomTeamsTypes.Role, ): Set { const moves = new Set(); let counter = this.newQueryMoves(moves, species, preferredType, abilities); - this.cullMovePool(types, moves, abilities, counter, movePool, teamDetails, species, isLead, isDoubles, + this.cullMovePool(types, moves, abilities, counter, movePool, teamDetails, species, isLead, preferredType, role); // If there are only four moves, add all moves and return early @@ -476,7 +473,7 @@ export class RandomGen7Teams extends RandomGen8Teams { // Still need to ensure that multiple Hidden Powers are not added (if maxMoveCount is increased) while (movePool.length) { const moveid = this.sample(movePool); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } return moves; @@ -492,7 +489,7 @@ export class RandomGen7Teams extends RandomGen8Teams { // Add required move (e.g. Relic Song for Meloetta-P) if (species.requiredMove) { const move = this.dex.moves.get(species.requiredMove).id; - counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } @@ -500,38 +497,38 @@ export class RandomGen7Teams extends RandomGen8Teams { // Enforce Facade if Guts is a possible ability if (movePool.includes('facade') && abilities.has('Guts')) { - counter = this.addMove('facade', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('facade', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } // Enforce Blizzard, Seismic Toss, Spore, and Sticky Web for (const moveid of ['blizzard', 'seismictoss', 'spore', 'stickyweb']) { if (movePool.includes(moveid)) { - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } // Enforce Thunder Wave on Prankster users if (movePool.includes('thunderwave') && abilities.has('Prankster')) { - counter = this.addMove('thunderwave', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('thunderwave', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } // Enforce Shadow Sneak on Kecleon if (movePool.includes('shadowsneak') && species.id === 'kecleon') { - counter = this.addMove('shadowsneak', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('shadowsneak', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } // Enforce hazard removal on Bulky Support if the team doesn't already have it if (role === 'Bulky Support' && !teamDetails.defog && !teamDetails.rapidSpin) { if (movePool.includes('rapidspin')) { - counter = this.addMove('rapidspin', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('rapidspin', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } if (movePool.includes('defog')) { - counter = this.addMove('defog', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('defog', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -548,7 +545,7 @@ export class RandomGen7Teams extends RandomGen8Teams { } if (priorityMoves.length) { const moveid = this.sample(priorityMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -567,7 +564,7 @@ export class RandomGen7Teams extends RandomGen8Teams { while (runEnforcementChecker(type)) { if (!stabMoves.length) break; const moveid = this.sampleNoReplace(stabMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -584,7 +581,7 @@ export class RandomGen7Teams extends RandomGen8Teams { } if (stabMoves.length) { const moveid = this.sample(stabMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -601,12 +598,12 @@ export class RandomGen7Teams extends RandomGen8Teams { } if (stabMoves.length) { const moveid = this.sample(stabMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } else { // If they have no regular STAB move, enforce U-turn on Bug types. if (movePool.includes('uturn') && types.includes('Bug')) { - counter = this.addMove('uturn', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('uturn', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -617,7 +614,7 @@ export class RandomGen7Teams extends RandomGen8Teams { const recoveryMoves = movePool.filter(moveid => RECOVERY_MOVES.includes(moveid)); if (recoveryMoves.length) { const moveid = this.sample(recoveryMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -627,7 +624,7 @@ export class RandomGen7Teams extends RandomGen8Teams { const enforcedMoves = [...PROTECT_MOVES, 'toxic', 'wish']; for (const move of enforcedMoves) { if (movePool.includes(move)) { - counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(move, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -639,11 +636,11 @@ export class RandomGen7Teams extends RandomGen8Teams { const setupMoves = movePool.filter(moveid => SETUP.includes(moveid) && moveid !== 'flamecharge'); if (setupMoves.length) { const moveid = this.sample(setupMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } else { if (movePool.includes('flamecharge')) { - counter = this.addMove('flamecharge', moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove('flamecharge', moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -659,7 +656,7 @@ export class RandomGen7Teams extends RandomGen8Teams { } if (attackingMoves.length) { const moveid = this.sample(attackingMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -680,7 +677,7 @@ export class RandomGen7Teams extends RandomGen8Teams { } if (coverageMoves.length) { const moveid = this.sample(coverageMoves); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -689,15 +686,15 @@ export class RandomGen7Teams extends RandomGen8Teams { // Choose remaining moves randomly from movepool and add them to moves list: while (moves.size < this.maxMoveCount && movePool.length) { const moveid = this.sample(movePool); - counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(moveid, moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); for (const pair of MOVE_PAIRS) { if (moveid === pair[0] && movePool.includes(pair[1])) { - counter = this.addMove(pair[1], moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(pair[1], moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } if (moveid === pair[1] && movePool.includes(pair[0])) { - counter = this.addMove(pair[0], moves, types, abilities, teamDetails, species, isLead, isDoubles, + counter = this.addMove(pair[0], moves, types, abilities, teamDetails, species, isLead, movePool, preferredType, role); } } @@ -714,7 +711,6 @@ export class RandomGen7Teams extends RandomGen8Teams { movePool: string[], teamDetails: RandomTeamsTypes.TeamDetails, species: Species, - isDoubles: boolean, preferredType: string, role: RandomTeamsTypes.Role ): boolean { @@ -832,7 +828,6 @@ export class RandomGen7Teams extends RandomGen8Teams { movePool: string[], teamDetails: RandomTeamsTypes.TeamDetails, species: Species, - isDoubles: boolean, preferredType: string, role: RandomTeamsTypes.Role, ): string { @@ -889,7 +884,7 @@ export class RandomGen7Teams extends RandomGen8Teams { // Obtain a list of abilities that are allowed (not culled) for (const ability of abilityData) { if (ability.rating >= 1 && !this.shouldCullAbility( - ability.name, types, moves, abilities, counter, movePool, teamDetails, species, isDoubles, preferredType, role + ability.name, types, moves, abilities, counter, movePool, teamDetails, species, preferredType, role )) { abilityAllowed.push(ability); } @@ -1107,8 +1102,7 @@ export class RandomGen7Teams extends RandomGen8Teams { randomSet( species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}, - isLead = false, - isDoubles = false + isLead = false ): RandomTeamsTypes.RandomSet { species = this.dex.species.get(species); let forme = species.name; @@ -1151,13 +1145,13 @@ export class RandomGen7Teams extends RandomGen8Teams { if (species.unreleasedHidden) abilities.delete(species.abilities.H); // Get moves - const moves = this.randomMoveset(types, abilities, teamDetails, species, isLead, isDoubles, movePool, + const moves = this.randomMoveset(types, abilities, teamDetails, species, isLead, movePool, preferredType, role); const counter = this.newQueryMoves(moves, species, preferredType, abilities); // Get ability ability = this.getAbility(new Set(types), moves, abilities, counter, movePool, teamDetails, species, - false, preferredType, role); + preferredType, role); // Get items item = this.getPriorityItem(ability, types, moves, counter, teamDetails, species, isLead, preferredType, role); @@ -1379,8 +1373,7 @@ export class RandomGen7Teams extends RandomGen8Teams { const set = this.randomSet( species, teamDetails, - pokemon.length === this.maxTeamSize - 1, - false + pokemon.length === this.maxTeamSize - 1 ); const item = this.dex.items.get(set.item); diff --git a/data/mods/gen8/random-teams.ts b/data/mods/gen8/random-teams.ts index 3e90a4dca090..2560f2f50b2f 100644 --- a/data/mods/gen8/random-teams.ts +++ b/data/mods/gen8/random-teams.ts @@ -1477,9 +1477,9 @@ export class RandomGen8Teams { movePool: string[], teamDetails: RandomTeamsTypes.TeamDetails, species: Species, - isDoubles: boolean, preferredType: string, role: RandomTeamsTypes.Role, + isDoubles: boolean, isNoDynamax: boolean ): boolean { if ([ @@ -1673,9 +1673,9 @@ export class RandomGen8Teams { movePool: string[], teamDetails: RandomTeamsTypes.TeamDetails, species: Species, - isDoubles: boolean, preferredType: string, role: RandomTeamsTypes.Role, + isDoubles: boolean, isNoDynamax: boolean ): string { const abilityData = Array.from(abilities).map(a => this.dex.abilities.get(a)); @@ -1720,7 +1720,7 @@ export class RandomGen8Teams { // Obtain a list of abilities that are allowed (not culled) for (const ability of abilityData) { if (ability.rating >= 1 && !this.shouldCullAbility( - ability.name, types, moves, abilities, counter, movePool, teamDetails, species, isDoubles, '', '', isNoDynamax + ability.name, types, moves, abilities, counter, movePool, teamDetails, species, '', '', isDoubles, isNoDynamax )) { abilityAllowed.push(ability); } @@ -2313,7 +2313,7 @@ export class RandomGen8Teams { } ability = this.getAbility(types, moves, abilities, counter, movePool, teamDetails, species, - isDoubles, '', '', isNoDynamax); + '', '', isDoubles, isNoDynamax); if (species.requiredItems) { item = this.sample(species.requiredItems); diff --git a/data/mods/gen8bdsp/random-teams.ts b/data/mods/gen8bdsp/random-teams.ts index 2cbcaf52cbb1..c391e6309ec6 100644 --- a/data/mods/gen8bdsp/random-teams.ts +++ b/data/mods/gen8bdsp/random-teams.ts @@ -533,6 +533,8 @@ export class RandomBDSPTeams extends RandomGen8Teams { movePool: string[], teamDetails: RandomTeamsTypes.TeamDetails, species: Species, + preferredType: string, + role: RandomTeamsTypes.Role, isDoubles: boolean, ): boolean { if ([ diff --git a/server/chat-plugins/randombattles/index.ts b/server/chat-plugins/randombattles/index.ts index 1c645d87c2ab..20d0cf3d973f 100644 --- a/server/chat-plugins/randombattles/index.ts +++ b/server/chat-plugins/randombattles/index.ts @@ -832,7 +832,7 @@ export const commands: Chat.ChatCommands = { const setsToCheck = [species]; if (dex.gen >= 8 && !isNoDMax) setsToCheck.push(dex.species.get(`${args[0]}gmax`)); if (species.otherFormes) setsToCheck.push(...species.otherFormes.map(pkmn => dex.species.get(pkmn))); - if ([4, 5, 6, 9].includes(dex.gen) || (dex.gen === 7 && !isDoubles)) { + if ([3, 4, 5, 6, 9].includes(dex.gen) || (dex.gen === 7 && !isDoubles)) { for (const pokemon of setsToCheck) { const data = getSets(pokemon, format.id); if (!data) continue; @@ -843,7 +843,7 @@ export const commands: Chat.ChatCommands = { buf += `
${set.role}`; if (dex.gen === 9) { buf += `Tera Type${Chat.plural(set.teraTypes)}: ${set.teraTypes.join(', ')}
`; - } else if (([4, 5, 6, 7].includes(dex.gen)) && set.preferredTypes) { + } else if (([3, 4, 5, 6, 7].includes(dex.gen)) && set.preferredTypes) { buf += `Preferred Type${Chat.plural(set.preferredTypes)}: ${set.preferredTypes.join(', ')}
`; } buf += `Moves: ${set.movepool.sort().map(formatMove).join(', ')}
`; @@ -1034,7 +1034,7 @@ export const commands: Chat.ChatCommands = { } let setExists: boolean; - if ([4, 5, 6, 9].includes(dex.gen) || dex.gen === 7 && format.gameType !== 'doubles') { + if ([3, 4, 5, 6, 9].includes(dex.gen) || dex.gen === 7 && format.gameType !== 'doubles') { setExists = !!getSets(species, format); } else if (dex.gen === 7 && format.gameType === 'doubles') { setExists = !!getData(species, format); diff --git a/sim/global-types.ts b/sim/global-types.ts index d1bb6b288c20..a25f45d45091 100644 --- a/sim/global-types.ts +++ b/sim/global-types.ts @@ -567,7 +567,7 @@ namespace RandomTeamsTypes { role: Role; movepool: string[]; teraTypes?: string[]; - preferredTypes?: string; + preferredTypes?: string[]; } export interface RandomSpeciesData { level?: number; @@ -577,5 +577,5 @@ namespace RandomTeamsTypes { 'Bulky Attacker' | 'Bulky Setup' | 'Fast Bulky Setup' | 'Bulky Support' | 'Fast Support' | 'AV Pivot' | 'Doubles Fast Attacker' | 'Doubles Setup Sweeper' | 'Doubles Wallbreaker' | 'Doubles Bulky Attacker' | 'Doubles Bulky Setup' | 'Offensive Protect' | 'Bulky Protect' | 'Doubles Support' | 'Choice Item user' | - 'Z-Move user' | 'Staller' | 'Spinner'; + 'Z-Move user' | 'Staller' | 'Spinner' | 'Generalist' | 'Berry Sweeper'; } diff --git a/test/random-battles/all-gens.js b/test/random-battles/all-gens.js index c99b9a5a166a..c6e3cefa6ae7 100644 --- a/test/random-battles/all-gens.js +++ b/test/random-battles/all-gens.js @@ -30,7 +30,7 @@ describe('value rule support (slow)', () => { if (gen === 1 && count !== 1) continue; const format = Dex.formats.get(`${formatID}@@@Max Move Count = ${count}`); // New set format - if ([4, 5, 6, 7, 9].includes(gen)) { + if ([3, 4, 5, 6, 7, 9].includes(gen)) { // Due to frontloading of moveset generation, formats with the new set format do not support // Max Move Counts less than 4 if (count < 4) continue; diff --git a/test/random-battles/gen3.js b/test/random-battles/gen3.js index 676175112ec5..57eca9f051f9 100644 --- a/test/random-battles/gen3.js +++ b/test/random-battles/gen3.js @@ -4,53 +4,83 @@ 'use strict'; const assert = require('../assert'); -const {testSet, testAlwaysHasMove, testHiddenPower, testNotBothMoves} = require('./tools'); +const {testTeam, validateLearnset} = require('./tools'); describe('[Gen 3] Random Battle (slow)', () => { const options = {format: 'gen3randombattle'}; + const setsJSON = require(`../../dist/data/mods/gen3/random-sets.json`); + const dex = Dex.forFormat(options.format); - it('should enforce STAB', () => { - testSet('beautifly', options, set => assert( - set.moves.some(m => m.startsWith('hiddenpower')), - `bad Beautifly set: ${JSON.stringify(set.moves)} has no STAB` - )); - - testSet('lickitung', options, set => assert( - set.moves.includes('return') || set.moves.includes('seismictoss'), - `bad Lickitung set: ${JSON.stringify(set.moves)} has no STAB` - )); - - testAlwaysHasMove('delcatty', options, 'doubleedge'); - }); - - it('should prevent double Hidden Power', () => { - testHiddenPower('kingler', options); - testHiddenPower('moltres', options); - }); - - it('should not give Magneton Sleep Talk without Rest', () => { - testSet('magneton', options, set => { - if (set.moves.includes('sleeptalk')) { - assert(set.moves.includes('rest'), `bad Magneton set: ${JSON.stringify(set.moves)}`); + describe("New set format", () => { + const filename = '../../data/mods/gen3/random-sets.json'; + it(`${filename} should have valid set data`, () => { + const setsJSON = require(filename); + const validRoles = [ + "Fast Attacker", "Setup Sweeper", "Wallbreaker", "Bulky Attacker", + "Bulky Setup", "Staller", "Bulky Support", "Generalist", "Berry Sweeper", + ]; + for (const [id, sets] of Object.entries(setsJSON)) { + const species = Dex.species.get(id); + assert(species.exists, `Misspelled species ID: ${id}`); + assert(Array.isArray(sets.sets)); + for (const set of sets.sets) { + assert(validRoles.includes(set.role), `Set for ${species.name} has invalid role: ${set.role}`); + for (const move of set.movepool) { + const dexMove = Dex.moves.get(move); + assert(dexMove.exists, `${species.name} has invalid move: ${move}`); + assert(move === dexMove.id || move.startsWith('hiddenpower'), `${species.name} has misformatted move: ${move}`); + assert(validateLearnset(dexMove, {species}, 'anythinggoes', 'gen3'), `${species.name} can't learn ${move}`); + } + for (let i = 0; i < set.movepool.length - 1; i++) { + assert(set.movepool[i + 1] > set.movepool[i], `${species} movepool should be sorted alphabetically`); + } + if (set.preferredTypes) { + for (const type of set.preferredTypes) { + const dexType = Dex.types.get(type); + assert(dexType.exists, `${species.name} has invalid Preferred Type: ${type}`); + assert.equal(type, dexType.name, `${species.name} has misformatted Preferred Type: ${type}`); + } + for (let i = 0; i < set.preferredTypes.length - 1; i++) { + assert(set.preferredTypes[i + 1] > set.preferredTypes[i], `${species} preferredTypes should be sorted alphabetically`); + } + } + } } }); }); - it('should give Blissey Soft-Boiled', () => { - testSet('blissey', options, set => { - assert(set.moves.includes('softboiled'), `bad Blissey set: ${JSON.stringify(set.moves)}`); + it('all Pokemon should have 4 moves, except for Ditto and Unown', function () { + // This test takes more than 2000ms + testTeam({...options, rounds: 100}, team => { + for (const pokemon of team) assert(pokemon.name === 'Ditto' || pokemon.name === 'Unown' || pokemon.moves.length === 4); }); }); - it('should not give Registeel Sleep Talk and Protect', () => { - testSet('registeel', options, set => { - if (set.moves.includes('sleeptalk')) { - assert(!set.moves.includes('protect'), `Registeel with Sleep Talk + Protect (${set.moves})`); + it('all moves on all sets should be obtainable', function () { + const generator = Teams.getGenerator(options.format); + const rounds = 100; + for (const pokemon of Object.keys(setsJSON)) { + const species = dex.species.get(pokemon); + const sets = setsJSON[pokemon]["sets"]; + const types = species.types; + const abilities = new Set(Object.values(species.abilities)); + if (species.unreleasedHidden) abilities.delete(species.abilities.H); + for (const set of sets) { + const role = set.role; + const moves = new Set(set.movepool.map(m => dex.moves.get(m).id)); + const preferredTypes = set.preferredTypes; + // Go through all possible teamDetails combinations, if necessary + for (let j = 0; j < rounds; j++) { + // Generate a moveset + // randomMoveset() deletes moves from the movepool, so recreate it every time + const preferredType = preferredTypes ? preferredTypes.join() : ''; + const movePool = set.movepool.map(m => dex.moves.get(m).id); + const moveSet = generator.randomMoveset(types, abilities, {}, species, false, movePool, preferredType, role); + for (const move of moveSet) moves.delete(move); + if (!moves.size) break; + } + assert(!moves.size, species); } - }); - }); - - it('should not give Skarmory Roar + Sleep Talk', () => { - testNotBothMoves('skarmory', options, 'roar', 'sleeptalk'); + } }); }); diff --git a/test/random-battles/gen4.js b/test/random-battles/gen4.js index a6a5833b5640..047afd065664 100644 --- a/test/random-battles/gen4.js +++ b/test/random-battles/gen4.js @@ -75,18 +75,17 @@ describe('[Gen 4] Random Battle (slow)', () => { // Generate a moveset as the lead, teamDetails is always empty for this const preferredType = preferredTypes ? preferredTypes[j % preferredTypes.length] : ''; const movePool = set.movepool.map(m => dex.moves.get(m).id); - const moveSet = generator.randomMoveset(types, abilities, {}, species, true, false, movePool, preferredType, role); + const moveSet = generator.randomMoveset(types, abilities, {}, species, true, movePool, preferredType, role); for (const move of moveSet) moves.delete(move); if (!moves.size) break; // Generate a moveset for each combination of relevant teamDetails - for (let i = 0; i < 8; i++) { - const defog = i % 2; + for (let i = 0; i < 4; i++) { + const rapidSpin = i % 2; const stealthRock = Math.floor(i / 2) % 2; - const stickyWeb = Math.floor(i / 4) % 2; - teamDetails = {defog, stealthRock, stickyWeb}; + teamDetails = {rapidSpin, stealthRock}; // randomMoveset() deletes moves from the movepool, so recreate it every time const movePool = set.movepool.map(m => dex.moves.get(m).id); - const moveSet = generator.randomMoveset(types, abilities, teamDetails, species, false, false, movePool, preferredType, role); + const moveSet = generator.randomMoveset(types, abilities, teamDetails, species, false, movePool, preferredType, role); for (const move of moveSet) moves.delete(move); if (!moves.size) break; } diff --git a/test/random-battles/gen5.js b/test/random-battles/gen5.js index ebfd13806366..5e44cf9f5dbb 100644 --- a/test/random-battles/gen5.js +++ b/test/random-battles/gen5.js @@ -75,18 +75,17 @@ describe('[Gen 5] Random Battle (slow)', () => { // Generate a moveset as the lead, teamDetails is always empty for this const preferredType = preferredTypes ? preferredTypes[j % preferredTypes.length] : ''; const movePool = set.movepool.map(m => dex.moves.get(m).id); - const moveSet = generator.randomMoveset(types, abilities, {}, species, true, false, movePool, preferredType, role); + const moveSet = generator.randomMoveset(types, abilities, {}, species, true, movePool, preferredType, role); for (const move of moveSet) moves.delete(move); if (!moves.size) break; // Generate a moveset for each combination of relevant teamDetails - for (let i = 0; i < 8; i++) { - const defog = i % 2; + for (let i = 0; i < 4; i++) { + const rapidSpin = i % 2; const stealthRock = Math.floor(i / 2) % 2; - const stickyWeb = Math.floor(i / 4) % 2; - teamDetails = {defog, stealthRock, stickyWeb}; + teamDetails = {rapidSpin, stealthRock}; // randomMoveset() deletes moves from the movepool, so recreate it every time const movePool = set.movepool.map(m => dex.moves.get(m).id); - const moveSet = generator.randomMoveset(types, abilities, teamDetails, species, false, false, movePool, preferredType, role); + const moveSet = generator.randomMoveset(types, abilities, teamDetails, species, false, movePool, preferredType, role); for (const move of moveSet) moves.delete(move); if (!moves.size) break; } diff --git a/test/random-battles/gen6.js b/test/random-battles/gen6.js index 25524913dba2..fa2c4b4935e4 100644 --- a/test/random-battles/gen6.js +++ b/test/random-battles/gen6.js @@ -75,7 +75,7 @@ describe('[Gen 6] Random Battle (slow)', () => { // Generate a moveset as the lead, teamDetails is always empty for this const preferredType = preferredTypes ? preferredTypes[j % preferredTypes.length] : ''; const movePool = set.movepool.map(m => dex.moves.get(m).id); - const moveSet = generator.randomMoveset(types, abilities, {}, species, true, false, movePool, preferredType, role); + const moveSet = generator.randomMoveset(types, abilities, {}, species, true, movePool, preferredType, role); for (const move of moveSet) moves.delete(move); if (!moves.size) break; // Generate a moveset for each combination of relevant teamDetails @@ -86,7 +86,7 @@ describe('[Gen 6] Random Battle (slow)', () => { teamDetails = {defog, stealthRock, stickyWeb}; // randomMoveset() deletes moves from the movepool, so recreate it every time const movePool = set.movepool.map(m => dex.moves.get(m).id); - const moveSet = generator.randomMoveset(types, abilities, teamDetails, species, false, false, movePool, preferredType, role); + const moveSet = generator.randomMoveset(types, abilities, teamDetails, species, false, movePool, preferredType, role); for (const move of moveSet) moves.delete(move); if (!moves.size) break; } diff --git a/test/random-battles/gen7.js b/test/random-battles/gen7.js index e88bb3aa51f6..c5bc0e688b0a 100644 --- a/test/random-battles/gen7.js +++ b/test/random-battles/gen7.js @@ -75,7 +75,7 @@ describe('[Gen 7] Random Battle (slow)', () => { // Generate a moveset as the lead, teamDetails is always empty for this const preferredType = preferredTypes ? preferredTypes[j % preferredTypes.length] : ''; const movePool = set.movepool.map(m => dex.moves.get(m).id); - const moveSet = generator.randomMoveset(types, abilities, {}, species, true, false, movePool, preferredType, role); + const moveSet = generator.randomMoveset(types, abilities, {}, species, true, movePool, preferredType, role); for (const move of moveSet) moves.delete(move); if (!moves.size) break; // Generate a moveset for each combination of relevant teamDetails @@ -86,7 +86,7 @@ describe('[Gen 7] Random Battle (slow)', () => { teamDetails = {defog, stealthRock, stickyWeb}; // randomMoveset() deletes moves from the movepool, so recreate it every time const movePool = set.movepool.map(m => dex.moves.get(m).id); - const moveSet = generator.randomMoveset(types, abilities, teamDetails, species, false, false, movePool, preferredType, role); + const moveSet = generator.randomMoveset(types, abilities, teamDetails, species, false, movePool, preferredType, role); for (const move of moveSet) moves.delete(move); if (!moves.size) break; }