diff --git a/client/package.json b/client/package.json index f79fe67b..e1ce50f7 100644 --- a/client/package.json +++ b/client/package.json @@ -15,11 +15,11 @@ "build" ], "dependencies": { - "@pkmn/data": "^0.7.59", + "@pkmn/data": "^0.8.0", "@pkmn/protocol": "^0.6.16" }, "devDependencies": { - "@pkmn/dex": "^0.7.59" + "@pkmn/dex": "^0.8.0" }, "scripts": { "lint": "eslint --cache src --ext ts", diff --git a/data/index.test.ts b/data/index.test.ts index aaf69cc0..570cbc45 100644 --- a/data/index.test.ts +++ b/data/index.test.ts @@ -386,7 +386,7 @@ for (const [pkg, Dex] of Object.entries(DATA)) { expect(Gen(3).species.get('Clefable')!.abilities).toEqual({'0': 'Cute Charm'}); expect(Gen(8).species.get('Clefable')!.abilities) .toEqual({'0': 'Cute Charm', '1': 'Magic Guard', H: 'Unaware'}); - expect(Gen(5).species.get('Milotic')!.abilities).toEqual({'0': 'Marvel Scale'}); + expect(Gen(4).species.get('Milotic')!.abilities).toEqual({'0': 'Marvel Scale'}); expect(Gen(6).species.get('Milotic')!.abilities) .toEqual({'0': 'Marvel Scale', '1': 'Competitive', H: 'Cute Charm'}); expect(Gen(2).species.get('Snorlax')!.tier).toBe('OU'); diff --git a/data/package.json b/data/package.json index 2b9ddde2..66ed73e1 100644 --- a/data/package.json +++ b/data/package.json @@ -1,6 +1,6 @@ { "name": "@pkmn/data", - "version": "0.7.59", + "version": "0.8.0", "description": "A forked implementation of the Pokémon Showdown client's data layer", "repository": "github:pkmn/ps", "license": "MIT", @@ -16,12 +16,12 @@ "build" ], "dependencies": { - "@pkmn/dex-types": "^0.7.59" + "@pkmn/dex-types": "^0.8.0" }, "devDependencies": { - "@pkmn/dex": "^0.7.59", - "@pkmn/mods": "^0.7.59", - "@pkmn/sim": "^0.7.59", + "@pkmn/dex": "^0.8.0", + "@pkmn/mods": "^0.8.0", + "@pkmn/sim": "^0.8.0", "@smogon/calc": "^0.8.1" }, "scripts": { diff --git a/dex/data/formats-data.json b/dex/data/formats-data.json index 685e08de..26b66dba 100644 --- a/dex/data/formats-data.json +++ b/dex/data/formats-data.json @@ -5345,7 +5345,7 @@ "cleffa": {"tier": "LC"}, "clefairy": {"tier": "NFE", "doublesTier": "DUU"}, "clefable": {"tier": "OU", "doublesTier": "(DUU)"}, - "vulpix": {"tier": "NUBL", "doublesTier": "NFE"}, + "vulpix": {"tier": "NFE"}, "vulpixalola": {"tier": "NFE"}, "ninetales": {"tier": "NU", "doublesTier": "DUU"}, "ninetalesalola": {"tier": "UU", "doublesTier": "DOU"}, @@ -5362,7 +5362,7 @@ "paras": {"isNonstandard": "Past", "tier": "Illegal"}, "parasect": {"isNonstandard": "Past", "tier": "Illegal"}, "venonat": {"tier": "LC"}, - "venomoth": {"tier": "NUBL", "doublesTier": "(DUU)"}, + "venomoth": {"tier": "NU", "doublesTier": "(DUU)"}, "diglett": {"tier": "NFE"}, "diglettalola": {"tier": "LC"}, "dugtrio": {"tier": "ZU", "doublesTier": "(DUU)"}, @@ -5373,7 +5373,7 @@ "meowthgmax": {"isNonstandard": "Past", "tier": "Illegal"}, "persian": {"tier": "ZU", "doublesTier": "(DUU)"}, "persianalola": {"tier": "PU", "doublesTier": "(DUU)"}, - "perrserker": {"tier": "ZU", "doublesTier": "(DUU)"}, + "perrserker": {"tier": "ZUBL", "doublesTier": "(DUU)"}, "psyduck": {"tier": "LC"}, "golduck": {"tier": "ZU", "doublesTier": "(DUU)"}, "mankey": {"tier": "LC"}, @@ -5447,7 +5447,7 @@ "kinglergmax": {"isNonstandard": "Past", "tier": "Illegal"}, "voltorb": {"tier": "LC"}, "voltorbhisui": {"tier": "LC"}, - "electrode": {"tier": "ZU", "doublesTier": "(DUU)"}, + "electrode": {"tier": "ZUBL", "doublesTier": "(DUU)"}, "electrodehisui": {"tier": "NU", "doublesTier": "(DUU)"}, "exeggcute": {"isNonstandard": "Past", "tier": "Illegal"}, "exeggutor": {"isNonstandard": "Past", "tier": "Illegal"}, @@ -5500,7 +5500,7 @@ "magmortar": {"isNonstandard": "Past", "tier": "Illegal"}, "pinsir": {"isNonstandard": "Past", "tier": "Illegal"}, "pinsirmega": {"isNonstandard": "Past", "tier": "Illegal"}, - "tauros": {"tier": "ZU", "doublesTier": "(DUU)"}, + "tauros": {"tier": "ZUBL", "doublesTier": "(DUU)"}, "taurospaldeacombat": {"tier": "PU", "doublesTier": "(DUU)"}, "taurospaldeablaze": {"tier": "NU", "doublesTier": "(DUU)"}, "taurospaldeaaqua": {"tier": "NU", "doublesTier": "(DUU)"}, @@ -5531,7 +5531,7 @@ "aerodactyl": {"isNonstandard": "Past", "tier": "Illegal"}, "aerodactylmega": {"isNonstandard": "Past", "tier": "Illegal"}, "munchlax": {"tier": "LC"}, - "snorlax": {"tier": "NUBL", "doublesTier": "(DUU)"}, + "snorlax": {"tier": "NU", "doublesTier": "(DUU)"}, "snorlaxgmax": {"isNonstandard": "Past", "tier": "Illegal"}, "articuno": {"tier": "PU", "doublesTier": "(DUU)"}, "articunogalar": {"tier": "NU", "doublesTier": "(DUU)"}, @@ -5620,7 +5620,7 @@ "weavile": {"tier": "UU", "doublesTier": "(DUU)"}, "sneasler": {"tier": "Uber", "doublesTier": "(DUU)"}, "teddiursa": {"tier": "LC"}, - "ursaring": {"tier": "ZU", "doublesTier": "NFE"}, + "ursaring": {"tier": "ZUBL", "doublesTier": "NFE"}, "ursaluna": {"tier": "UU", "doublesTier": "DOU"}, "ursalunabloodmoon": {"tier": "Uber", "doublesTier": "DOU"}, "slugma": {"tier": "LC"}, @@ -5643,7 +5643,7 @@ "phanpy": {"tier": "LC"}, "donphan": {"tier": "RU", "doublesTier": "(DUU)"}, "stantler": {"tier": "NFE"}, - "wyrdeer": {"tier": "ZU", "doublesTier": "(DUU)"}, + "wyrdeer": {"tier": "ZUBL", "doublesTier": "(DUU)"}, "smeargle": {"isNonstandard": "Past", "tier": "Illegal"}, "miltank": {"isNonstandard": "Past", "tier": "Illegal"}, "raikou": {"isNonstandard": "Past", "tier": "Illegal"}, @@ -5697,7 +5697,7 @@ "gallade": {"tier": "NU", "doublesTier": "(DUU)"}, "gallademega": {"isNonstandard": "Past", "tier": "Illegal"}, "surskit": {"tier": "LC"}, - "masquerain": {"tier": "ZU", "doublesTier": "(DUU)"}, + "masquerain": {"tier": "ZUBL", "doublesTier": "(DUU)"}, "shroomish": {"tier": "LC"}, "breloom": {"tier": "UU", "doublesTier": "(DUU)"}, "slakoth": {"tier": "LC"}, @@ -5901,11 +5901,11 @@ "rotom": {"tier": "PU", "doublesTier": "(DUU)"}, "rotomheat": {"tier": "RU", "doublesTier": "(DUU)"}, "rotomwash": {"tier": "UU", "doublesTier": "(DUU)"}, - "rotomfrost": {"tier": "ZU", "doublesTier": "(DUU)"}, - "rotomfan": {"tier": "ZU", "doublesTier": "(DUU)"}, + "rotomfrost": {"tier": "ZUBL", "doublesTier": "(DUU)"}, + "rotomfan": {"tier": "ZUBL", "doublesTier": "(DUU)"}, "rotommow": {"tier": "NU", "doublesTier": "(DUU)"}, "uxie": {"tier": "NU", "doublesTier": "(DUU)"}, - "mesprit": {"tier": "PU", "doublesTier": "(DUU)"}, + "mesprit": {"tier": "PUBL", "doublesTier": "(DUU)"}, "azelf": {"tier": "RU", "doublesTier": "(DUU)"}, "dialga": {"tier": "Uber", "doublesTier": "DUber"}, "dialgaorigin": {"tier": "Uber", "doublesTier": "DUber"}, @@ -6012,7 +6012,7 @@ "garbodorgmax": {"isNonstandard": "Past", "tier": "Illegal"}, "zorua": {"tier": "LC"}, "zoruahisui": {"tier": "LC"}, - "zoroark": {"tier": "NUBL", "doublesTier": "(DUU)"}, + "zoroark": {"tier": "NU", "doublesTier": "(DUU)"}, "zoroarkhisui": {"tier": "RU", "doublesTier": "(DUU)"}, "minccino": {"isNonstandard": "Past", "tier": "Illegal"}, "cinccino": {"isNonstandard": "Past", "tier": "Illegal"}, @@ -6085,7 +6085,7 @@ "cobalion": {"isNonstandard": "Past", "tier": "Illegal"}, "terrakion": {"isNonstandard": "Past", "tier": "Illegal"}, "virizion": {"isNonstandard": "Past", "tier": "Illegal"}, - "tornadus": {"tier": "NUBL", "doublesTier": "DOU"}, + "tornadus": {"tier": "NU", "doublesTier": "DOU"}, "tornadustherian": {"tier": "UU", "doublesTier": "(DUU)"}, "thundurus": {"tier": "RU", "doublesTier": "DUU"}, "thundurustherian": {"tier": "UU", "doublesTier": "(DUU)"}, @@ -6098,7 +6098,7 @@ "kyuremwhite": {"isNonstandard": "Past", "tier": "Illegal"}, "keldeo": {"isNonstandard": "Past", "tier": "Illegal"}, "keldeoresolute": {"isNonstandard": "Past"}, - "meloetta": {"tier": "NUBL", "doublesTier": "(DUU)"}, + "meloetta": {"tier": "NU", "doublesTier": "(DUU)"}, "genesect": {"isNonstandard": "Past", "tier": "Illegal"}, "genesectburn": {"isNonstandard": "Past", "tier": "Illegal"}, "genesectchill": {"isNonstandard": "Past", "tier": "Illegal"}, @@ -6109,7 +6109,7 @@ "chesnaught": {"tier": "RU", "doublesTier": "(DUU)"}, "fennekin": {"tier": "LC"}, "braixen": {"tier": "NFE"}, - "delphox": {"tier": "NUBL", "doublesTier": "(DUU)"}, + "delphox": {"tier": "NU", "doublesTier": "(DUU)"}, "froakie": {"tier": "LC"}, "frogadier": {"tier": "ZU", "doublesTier": "NFE"}, "greninja": {"tier": "OU", "doublesTier": "(DUU)"}, @@ -6215,10 +6215,10 @@ "vikavolttotem": {"isNonstandard": "Past", "tier": "Illegal"}, "crabrawler": {"tier": "LC"}, "crabominable": {"tier": "ZUBL", "doublesTier": "(DUU)"}, - "oricorio": {"tier": "NUBL", "doublesTier": "(DUU)"}, + "oricorio": {"tier": "NU", "doublesTier": "(DUU)"}, "oricoriopompom": {"tier": "NUBL", "doublesTier": "(DUU)"}, "oricoriopau": {"tier": "PUBL", "doublesTier": "(DUU)"}, - "oricoriosensu": {"tier": "NUBL", "doublesTier": "(DUU)"}, + "oricoriosensu": {"tier": "NU", "doublesTier": "(DUU)"}, "cutiefly": {"tier": "NFE"}, "ribombee": {"tier": "OU", "doublesTier": "(DUU)"}, "ribombeetotem": {"isNonstandard": "Past", "tier": "Illegal"}, @@ -6331,7 +6331,7 @@ "cinderacegmax": {"isNonstandard": "Past", "tier": "Illegal"}, "sobble": {"tier": "LC"}, "drizzile": {"tier": "NFE"}, - "inteleon": {"tier": "NUBL", "doublesTier": "(DUU)"}, + "inteleon": {"tier": "NU", "doublesTier": "(DUU)"}, "inteleongmax": {"isNonstandard": "Past", "tier": "Illegal"}, "skwovet": {"tier": "LC"}, "greedent": {"tier": "ZU", "doublesTier": "(DUU)"}, @@ -6371,7 +6371,7 @@ "arrokuda": {"tier": "LC"}, "barraskewda": {"tier": "UU", "doublesTier": "(DUU)"}, "toxel": {"tier": "LC"}, - "toxtricity": {"tier": "NUBL", "doublesTier": "(DUU)"}, + "toxtricity": {"tier": "NU", "doublesTier": "(DUU)"}, "toxtricitygmax": {"isNonstandard": "Past", "tier": "Illegal"}, "toxtricitylowkeygmax": {"isNonstandard": "Past", "tier": "Illegal"}, "sizzlipede": {"isNonstandard": "Past", "tier": "Illegal"}, @@ -6395,7 +6395,7 @@ "falinks": {"tier": "ZUBL", "doublesTier": "(DUU)"}, "pincurchin": {"tier": "ZU", "doublesTier": "(DUU)"}, "snom": {"tier": "LC"}, - "frosmoth": {"tier": "NUBL", "doublesTier": "(DUU)"}, + "frosmoth": {"tier": "NU", "doublesTier": "(DUU)"}, "stonjourner": {"tier": "ZU", "doublesTier": "(DUU)"}, "eiscue": {"tier": "ZU", "doublesTier": "(DUU)"}, "indeedee": {"tier": "PU", "doublesTier": "(DUU)"}, @@ -6488,7 +6488,7 @@ "kilowattrel": {"tier": "RU", "doublesTier": "(DUU)"}, "bombirdier": {"tier": "PU", "doublesTier": "(DUU)"}, "squawkabilly": {"tier": "ZU", "doublesTier": "(DUU)"}, - "flamigo": {"tier": "NUBL", "doublesTier": "(DUU)"}, + "flamigo": {"tier": "NU", "doublesTier": "(DUU)"}, "klawf": {"tier": "ZU", "doublesTier": "(DUU)"}, "nacli": {"tier": "LC"}, "naclstack": {"tier": "PU", "doublesTier": "NFE"}, @@ -6500,14 +6500,14 @@ "fidough": {"tier": "LC"}, "dachsbun": {"tier": "ZU", "doublesTier": "(DUU)"}, "maschiff": {"tier": "LC"}, - "mabosstiff": {"tier": "ZU", "doublesTier": "(DUU)"}, + "mabosstiff": {"tier": "ZUBL", "doublesTier": "(DUU)"}, "bramblin": {"tier": "LC"}, "brambleghast": {"tier": "RU", "doublesTier": "(DUU)"}, "gimmighoul": {"tier": "LC"}, "gimmighoulroaming": {"tier": "LC"}, "gholdengo": {"tier": "OU", "doublesTier": "DOU"}, "greattusk": {"tier": "OU", "doublesTier": "DUU"}, - "brutebonnet": {"tier": "NUBL", "doublesTier": "DUU"}, + "brutebonnet": {"tier": "NU", "doublesTier": "DUU"}, "sandyshocks": {"tier": "UU", "doublesTier": "(DUU)"}, "screamtail": {"tier": "NU", "doublesTier": "(DUU)"}, "fluttermane": {"tier": "Uber", "doublesTier": "DOU"}, @@ -6515,7 +6515,7 @@ "roaringmoon": {"tier": "Uber", "doublesTier": "DOU"}, "irontreads": {"tier": "UU", "doublesTier": "(DUU)"}, "ironmoth": {"tier": "OU", "doublesTier": "DUU"}, - "ironhands": {"tier": "UU", "doublesTier": "DOU"}, + "ironhands": {"tier": "UUBL", "doublesTier": "DOU"}, "ironjugulis": {"tier": "RU", "doublesTier": "(DUU)"}, "ironthorns": {"tier": "RU", "doublesTier": "(DUU)"}, "ironbundle": {"tier": "Uber", "doublesTier": "DOU"}, diff --git a/dex/data/learnsets.json b/dex/data/learnsets.json index 150dbd51..7814a4e2 100644 --- a/dex/data/learnsets.json +++ b/dex/data/learnsets.json @@ -149260,110 +149260,142 @@ }, "revenankh": { "learnset": { - "ancientpower": ["9E", "8E", "7E", "6E", "5E", "4T", "4E"], - "armthrust": ["9L5", "8L5", "7L13", "4L18"], - "attract": ["8M", "7M", "4M"], - "bide": ["7L1", "4L1"], - "bind": ["7T"], - "bodypress": ["9M", "8M"], + "ancientpower": ["9E", "8E", "7E", "6E", "5E", "4E"], + "armthrust": ["9L4", "8L4", "7L8", "6L8", "5L8", "4L8"], + "attract": ["8M", "7M", "6M", "5M", "4M"], + "bind": ["7T", "6T", "5T"], "bodyslam": ["9M", "8M"], - "brickbreak": ["9M", "8M", "7M", "4M"], - "brutalswing": ["8M", "7M"], - "bulkup": ["9M", "8M", "7M", "4M"], - "bulldoze": ["9M", "8M", "7M"], + "brickbreak": ["9M", "8M", "7M", "6M", "5M", "4M"], + "bulkup": ["9M", "8M", "7M", "6M", "5M", "4M"], "captivate": ["4M"], - "closecombat": ["9M"], + "closecombat": ["9M", "8M"], "coaching": ["8T"], - "confide": ["7M"], + "confide": ["7M", "6M"], "confuseray": ["9M"], - "counter": ["9E", "8E", "7E", "4T"], - "curse": ["9E", "8E", "7E", "4E"], - "destinybond": ["9E", "8E", "7E", "4E"], - "doubleteam": ["7M", "4M"], - "drainpunch": ["9M", "9L50", "8M", "8L55", "7M", "4M"], - "dreameater": ["7M", "4M"], - "dualchop": ["7T"], - "earthquake": ["9M", "8M", "7M", "4M"], - "embargo": ["7M", "4M"], + "counter": ["9E", "8E", "7E", "6E", "5E", "4E"], + "curse": ["9L36", "8L36", "7L28", "6L28", "5L28", "4L28"], + "darkestlariat": ["8M"], + "darkpulse": ["9M", "8M", "7M", "6M", "5T", "4M"], + "destinybond": ["9E", "8E", "7E", "6E", "5E", "4E"], + "detect": ["9L8", "8L8", "7L11", "6L11", "5L11", "4L11"], + "dig": ["9M", "8M", "6M", "5M", "4M"], + "doubleteam": ["8M", "7M", "6M", "5M", "4M"], + "drainpunch": ["9M", "8M", "7T", "6T", "5T", "4M"], + "dreameater": ["7M", "6M", "5M", "4M"], + "dualchop": ["7T", "6T", "5T"], + "earthquake": ["9M", "8M", "7M", "6M", "5M", "4M"], + "embargo": ["7M", "6M", "5M", "4M"], "endure": ["9M", "8M", "4M"], - "facade": ["9M", "8M", "7M", "4M"], - "fling": ["9M", "8M", "7M", "4M"], - "focusblast": ["9M", "8M", "7M", "4M"], - "focuspunch": ["9M", "9L70", "8L75", "7T", "6T", "4M"], - "forcepalm": ["9E", "8E", "7E", "4E"], - "frustration": ["7M", "4M"], - "gigaimpact": ["9M", "8M", "7M", "4M"], - "glare": ["9L35", "8L35", "7L26", "4L26"], - "hammerarm": ["9L60", "8L65", "7L57", "4L44"], - "headbutt": ["7T"], - "helpinghand": ["9M", "8M", "7T", "4T"], - "hex": ["9M", "8M", "7E"], - "hiddenpower": ["7M", "4M"], - "highhorsepower": ["8M"], - "hyperbeam": ["9M", "8M", "7M", "4M"], - "icepunch": ["9M", "8M", "7T", "4T"], + "facade": ["9M", "8M", "7M", "6M", "5M", "4M"], + "fling": ["9M", "8M", "7M", "6M", "5M", "4M"], + "focusblast": ["9M", "8M", "7M", "6M", "5M", "4M"], + "focuspunch": ["9M", "9L56", "8L60", "7T", "6T", "4M"], + "forcepalm": ["9L28", "8L28", "7L15", "6L15", "5L15", "4L15"], + "foulplay": ["9M", "8M", "7T", "6T", "5T"], + "frustration": ["7M", "6M", "5M", "4M"], + "gigaimpact": ["9M", "8M", "7M", "6M", "5M", "4M"], + "glare": ["9L24", "8L24", "7L21", "6L21", "5L21", "4L21"], + "grudge": ["8L52", "7L55", "6L55", "5L55", "4L49"], + "hammerarm": ["9L48", "8L48", "7L49", "6L49", "5L49", "4L44"], + "helpinghand": ["9M", "8M"], + "hex": ["9M", "9L32", "8M", "8L32", "7L44", "6L44", "5L44"], + "hiddenpower": ["7M", "6M", "5M", "4M"], + "hyperbeam": ["9M", "8M", "7M", "6M", "5M", "4M"], + "icepunch": ["9M", "8M", "7T", "6T", "5T", "4T"], "knockoff": ["9M", "9L40", "8L40", "7T", "6T", "5T", "4T"], - "machpunch": ["9E", "8E", "7E", "4E"], - "meanlook": ["9L25", "8L25", "7L18", "4L23"], + "laserfocus": ["7T"], + "lashout": ["9M", "8T"], + "leer": ["9L1", "8L1", "7L1", "6L1", "5L1", "4L1"], + "machpunch": ["9E", "8E", "7E", "6E", "5E", "4E"], + "meanlook": ["9L16", "8L16", "7L5", "6L5", "5L5", "4L5"], "megapunch": ["8M"], - "memento": ["9E", "8E", "7E", "4E"], - "metronome": ["9M", "8M", "7E", "4T"], - "mimic": ["7E", "4E"], - "moonlight": ["9L45", "8L45", "7L62", "4L62"], + "memento": ["9E", "8E", "7E", "6E", "5E", "4E"], + "metronome": ["9M", "8M"], + "moonlight": ["9L44", "8L44", "7L66", "6L66", "5L66", "4L60"], "mudslap": ["9M", "4T"], - "nastyplot": ["9M", "8M", "7E", "4E"], + "nastyplot": ["9M", "8M", "7E", "6E", "5E", "4E"], "naturalgift": ["4M"], - "ominouswind": ["7E", "4T", "4E"], - "painsplit": ["7T", "4T"], - "payback": ["9L20", "8M", "8L20", "7L38", "4M"], + "nightshade": ["9M"], + "ominouswind": ["7E", "6E", "5E", "4T", "4E"], + "painsplit": ["7T", "6T", "5T", "4T"], + "payback": ["8M", "7M", "6M", "5M", "4M"], + "phantomforce": ["9M"], + "poisonjab": ["9M", "8M", "7M", "6M", "5M", "4M"], "poltergeist": ["8T"], - "poweruppunch": ["7M"], - "powerwhip": ["9L65", "8M", "8L70", "7L52", "4L38"], - "protect": ["9M", "8M", "7M", "4M"], - "psychup": ["7M", "4M"], - "punishment": ["7L71", "4L52"], - "quickguard": ["9L15", "8L15", "7L48"], - "raindance": ["9M", "8M", "7M", "4M"], - "rest": ["9M", "8M", "7M", "4M"], - "retaliate": ["8M", "7M"], - "return": ["7M", "4M"], - "revenge": ["8M", "8L50", "7L33", "4L33"], + "poweruppunch": ["6M"], + "powerwhip": ["9L52", "8L56", "7L60", "6L60", "5L60", "4L55"], + "protect": ["9M", "8M", "7M", "6M", "5M", "4M"], + "psychup": ["7M", "6M", "5M", "4M"], + "punishment": ["7L33", "6L33", "5L33", "4L33"], + "quickguard": ["9E", "8E", "7E", "6E"], + "raindance": ["9M", "8M", "7M", "6M", "5M", "4M"], + "rest": ["9M", "8M", "7M", "6M", "5M", "4M"], + "retaliate": ["8M", "6M", "5M"], + "return": ["7M", "6M", "5M", "4M"], + "revenge": ["8M"], "reversal": ["9M", "8M"], - "rockslide": ["9M", "8M", "7M", "4M"], + "rockclimb": ["4M"], + "rockslide": ["9M", "8M", "7M", "6M", "5M", "4M"], "rocksmash": ["6M", "5M", "4M"], - "rocktomb": ["9M", "8M", "7M", "7L23", "4M", "4L13"], - "round": ["8M", "7M"], - "safeguard": ["8M", "7M", "4M"], - "sandstorm": ["9M", "8M", "7M", "4M"], - "sandtomb": ["9M", "9L1", "8M", "8L1", "7L5", "4L5"], - "secretpower": ["7M", "4M"], - "shadowball": ["9M", "8M", "7M", "4M"], - "shadowclaw": ["9M", "8M", "7M"], - "shadowpunch": ["9L30", "8L30", "7L29", "4L29"], - "shadowsneak": ["9L10", "8L10", "7E", "4E"], - "sleeptalk": ["9M", "8M", "7M", "4M"], - "smackdown": ["9M", "7M"], - "snore": ["8M", "7T"], + "rocktomb": [ + "9M", + "8M", + "7M", + "7L18", + "6M", + "6L18", + "5M", + "5L18", + "4M", + "4L18" + ], + "roleplay": ["7T", "6T", "5T", "4T"], + "round": ["8M", "7M", "6M", "5M"], + "safeguard": ["8M", "7M", "6M", "5M", "4M"], + "sandstorm": ["9M", "8M", "7M", "6M", "5M", "4M"], + "sandtomb": [ + "9M", + "9L12", + "8M", + "8L12", + "7L39", + "6L39", + "5L39", + "4L39" + ], + "scaryface": ["9M", "8M"], + "secretpower": ["6M", "4M"], + "shadowball": ["9M", "8M", "7M", "6M", "5M", "4M"], + "shadowclaw": ["9M", "8M", "7M", "6M", "5M", "4M"], + "shadowpunch": ["9L20", "8L20", "7L25", "6L25", "5L25", "4L25"], + "shadowsneak": ["9E", "8E", "7E", "6E", "5E", "4E"], + "sleeptalk": ["9M", "8M", "7M", "6M", "5T", "4M"], + "smackdown": ["9M", "7M", "6M", "5M"], + "snatch": ["7T", "6T", "5T", "4M"], + "snore": ["8M", "7T", "6T", "5T", "4T"], "spite": ["9M", "7T", "6T", "5T", "4T"], "strength": ["6M", "5M", "4M"], - "substitute": ["9M", "8M", "7M", "4M"], - "suckerpunch": ["9L55", "8L60", "4T"], - "sunnyday": ["9M", "8M", "7M", "4M"], + "substitute": ["9M", "8M", "7M", "6M", "5M", "4M"], + "suckerpunch": ["9E", "8E", "7E", "6E", "5E", "4T"], + "sunnyday": ["9M", "8M", "7M", "6M", "5M", "4M"], "superpower": ["8M", "7T", "6T", "5T", "4T"], - "swagger": ["7M", "4M"], + "swagger": ["7M", "6M", "5M", "4M"], "takedown": ["9M"], - "taunt": ["9M", "8M", "7M", "4M"], - "telekinesis": ["7M"], + "taunt": ["9M", "8M", "7M", "6M", "5M", "4M"], + "telekinesis": ["7T", "5M"], "terablast": ["9M"], - "thunderpunch": ["9M"], - "torment": ["7M", "4M"], - "toxic": ["7M", "4M"], - "trick": ["9M", "8M", "7T", "4T"], - "vacuumwave": ["4T"], + "thief": ["9M", "8M", "7M", "6M", "5M", "4M"], + "throatchop": ["8M", "7T"], + "thunderpunch": ["9M", "8M", "7T", "6T", "5T", "4T"], + "torment": ["7M", "6M", "5M", "4M"], + "toxic": ["7M", "6M", "5M", "4M"], + "trick": ["9M", "8M", "7T", "6T", "5T", "4T"], + "trickroom": ["9M", "8M", "7M", "6M", "5M", "4M"], + "vacuumwave": ["9M", "4T"], "willowisp": ["9M", "8M", "7M"], - "workup": ["8M", "7M"], - "wrap": ["9L1", "8L1", "7L1", "4L1"], - "wringout": ["7L66", "4L9"] + "wonderroom": ["8M", "7T", "6T", "5T"], + "workup": ["8M", "7M", "5M"], + "wrap": ["9L1", "8L1", "7L1", "6L1", "5L1", "4L1"] } }, "embirch": { diff --git a/dex/package.json b/dex/package.json index 98f7a117..4c16d990 100644 --- a/dex/package.json +++ b/dex/package.json @@ -1,6 +1,6 @@ { "name": "@pkmn/dex", - "version": "0.7.59", + "version": "0.8.0", "description": "A unification of Pokémon Showdown's client's and server's data layers", "repository": "github:pkmn/ps", "license": "MIT", @@ -16,7 +16,7 @@ "build" ], "dependencies": { - "@pkmn/dex-types": "^0.7.59" + "@pkmn/dex-types": "^0.8.0" }, "scripts": { "lint": "eslint --cache *.ts", diff --git a/dex/types/package.json b/dex/types/package.json index ba7a3af4..22981b40 100644 --- a/dex/types/package.json +++ b/dex/types/package.json @@ -1,6 +1,6 @@ { "name": "@pkmn/dex-types", - "version": "0.7.59", + "version": "0.8.0", "types": "index.d.ts", "description": "Common Pokémon Showdown Dex types shared by @pkmn/dex and @pkmn/sim", "repository": "github:pkmn/ps", diff --git a/import b/import index 535b22ca..21e53dea 100755 --- a/import +++ b/import @@ -340,9 +340,9 @@ const REWRITES = { '\tgetDetails = (): {side: SideID, secret: string, shared: string} => {', }, 'sim/dex-abilities.ts': { - '\t\tif (ability.exists) this.abilityCache.set(id, ability);': - `\t\t(ability as any).kind = 'Ability';\n` + - '\t\tif (ability.exists) this.abilityCache.set(id, ability);', + '\t\tif (ability.exists) this.abilityCache.set(id, this.dex.deepFreeze(ability));': + `\t\tif (!(ability as any).kind) (ability as any).kind = 'Ability';\n` + + '\t\tif (ability.exists) this.abilityCache.set(id, this.dex.deepFreeze(ability));', }, 'sim/dex-conditions.ts': { '\t\t} else if (this.dex.data.Rulesets.hasOwnProperty(id)) {': @@ -357,22 +357,22 @@ const REWRITES = { [KEY]: VALUE, }, 'sim/dex-items.ts': { - '\t\tif (item.exists) this.itemCache.set(id, item);': + '\t\tif (item.exists) this.itemCache.set(id, this.dex.deepFreeze(item));': `\t\t(item as any).kind = 'Item';\n` + - '\t\tif (item.exists) this.itemCache.set(id, item);', + '\t\tif (item.exists) this.itemCache.set(id, this.dex.deepFreeze(item));', }, 'sim/dex-moves.ts': { - '\t\tif (move.exists) this.moveCache.set(id, move);': + '\t\tif (move.exists) this.moveCache.set(id, this.dex.deepFreeze(move));': `\t\t(move as any).kind = 'Move';\n` + - '\t\tif (move.exists) this.moveCache.set(id, move);', + '\t\tif (move.exists) this.moveCache.set(id, this.dex.deepFreeze(move));', }, 'sim/dex-data.ts': { - '\t\tif (nature.exists) this.natureCache.set(id, nature);': + '\t\tif (nature.exists) this.natureCache.set(id, this.dex.deepFreeze(nature));': `\t\t(nature as any).kind = 'Nature';\n` + - '\t\tif (nature.exists) this.natureCache.set(id, nature);', - '\t\tif (type.exists) this.typeCache.set(id, type);': + '\t\tif (nature.exists) this.natureCache.set(id, this.dex.deepFreeze(nature));', + '\t\tif (type.exists) this.typeCache.set(id, this.dex.deepFreeze(type));': `\t\t(type as any).kind = 'Type';\n` + - '\t\tif (type.exists) this.typeCache.set(id, type);', + '\t\tif (type.exists) this.typeCache.set(id, this.dex.deepFreeze(type));', }, 'sim/battle.ts': { '\tgetCategory(move: string | Move) {': diff --git a/mods/package.json b/mods/package.json index f6b84495..704383bb 100644 --- a/mods/package.json +++ b/mods/package.json @@ -1,6 +1,6 @@ { "name": "@pkmn/mods", - "version": "0.7.59", + "version": "0.8.0", "description": "Support for non-standard mods to @pkmn/sim and @pkmn/dex", "repository": "github:pkmn/ps", "author": "Guangcong Luo (http://guangcongluo.com)", @@ -107,11 +107,11 @@ "build" ], "dependencies": { - "@pkmn/dex-types": "^0.7.59" + "@pkmn/dex-types": "^0.8.0" }, "devDependencies": { - "@pkmn/dex": "^0.7.59", - "@pkmn/sim": "^0.7.59" + "@pkmn/dex": "^0.8.0", + "@pkmn/sim": "^0.8.0" }, "scripts": { "lint": "eslint --cache src --ext ts", diff --git a/protocol/package.json b/protocol/package.json index e56f858f..087d26d9 100644 --- a/protocol/package.json +++ b/protocol/package.json @@ -40,8 +40,8 @@ "@pkmn/types": "^3.0.0" }, "devDependencies": { - "@pkmn/dex": "^0.7.59", - "@pkmn/data": "^0.7.59" + "@pkmn/dex": "^0.8.0", + "@pkmn/data": "^0.8.0" }, "scripts": { "lint": "eslint --cache src --ext ts", diff --git a/randoms/package.json b/randoms/package.json index 566ec9bb..99414421 100644 --- a/randoms/package.json +++ b/randoms/package.json @@ -1,6 +1,6 @@ { "name": "@pkmn/randoms", - "version": "0.7.59", + "version": "0.8.0", "description": "Random team generation logic for Pokémon Showdown's Random Battle formats", "repository": "github:pkmn/ps", "license": "MIT", @@ -15,7 +15,7 @@ "build" ], "dependencies": { - "@pkmn/sim": "^0.7.59" + "@pkmn/sim": "^0.8.0" }, "scripts": { "lint": "eslint --cache src --ext ts", diff --git a/randoms/src/gen1.ts b/randoms/src/gen1.ts index 3b247dea..35e7ef91 100644 --- a/randoms/src/gen1.ts +++ b/randoms/src/gen1.ts @@ -41,7 +41,6 @@ export class RandomGen1Teams extends RandomGen2Teams { for (const pokemon of randomN) { const species = this.dex.species.get(pokemon); - let learnset = this.dex.species.getLearnset(species.id); // Level balance: calculate directly from stats rather than using some silly lookup table. const mbstmin = 1307; @@ -96,26 +95,7 @@ export class RandomGen1Teams extends RandomGen2Teams { // Four random unique moves from movepool. don't worry about "attacking" or "viable". // Since Gens 1 and 2 learnsets are shared, we need to weed out Gen 2 moves. - const pool: string[] = []; - if (learnset) { - for (const move in learnset) { - if (this.dex.moves.get(move).gen !== 1) continue; - if (learnset[move].some(learned => learned.startsWith('1'))) { - pool.push(move); - } - } - } - let learnsetSpecies = species; - for (let i = 0; i < 2 && learnsetSpecies.prevo; i++) { - learnsetSpecies = this.dex.species.get(learnsetSpecies.prevo); - learnset = this.dex.species.getLearnset(learnsetSpecies.id); - for (const move in learnset) { - if (this.dex.moves.get(move).gen !== 1) continue; - if (learnset[move].some(learned => learned.startsWith('1'))) { - pool.push(move); - } - } - } + const pool = [...this.dex.species.getMovePool(species.id)]; team.push({ name: species.baseSpecies, diff --git a/randoms/src/gen2.ts b/randoms/src/gen2.ts index 4dd5c246..48d9fdf6 100644 --- a/randoms/src/gen2.ts +++ b/randoms/src/gen2.ts @@ -160,7 +160,7 @@ export class RandomGen2Teams extends RandomGen3Teams { species = this.dex.species.get(species); const data = this.randomData[species.id]; - const movePool = (data.moves || Object.keys(this.dex.species.getLearnset(species.id)!)).slice(); + const movePool: string[] = [...(data.moves || this.dex.species.getMovePool(species.id))]; const rejectedPool: string[] = []; const moves = new Set(); diff --git a/randoms/src/gen3.ts b/randoms/src/gen3.ts index 55371ad3..2bba456c 100644 --- a/randoms/src/gen3.ts +++ b/randoms/src/gen3.ts @@ -411,7 +411,7 @@ export class RandomGen3Teams extends RandomGen4Teams { if (typeof species.battleOnly === 'string') forme = species.battleOnly; - const movePool = (data.moves || Object.keys(this.dex.species.getLearnset(species.id)!)).slice(); + const movePool: string[] = [...(data.moves || this.dex.species.getMovePool(species.id))]; const rejectedPool = []; const moves = new Set(); let ability = ''; diff --git a/randoms/src/gen8.ts b/randoms/src/gen8.ts index 07ebc724..4eeac4a3 100644 --- a/randoms/src/gen8.ts +++ b/randoms/src/gen8.ts @@ -452,42 +452,11 @@ export class RandomGen8Teams { // Four random unique moves from the movepool let pool = ['struggle']; if (forme === 'Smeargle') { - pool = this.dex.moves - .all() + pool = this.dex.moves.all() .filter(move => !(move.isNonstandard || move.isZ || move.isMax || move.realMove)) .map(m => m.id); } else { - const formes = ['gastrodoneast', 'pumpkaboosuper', 'zygarde10']; - let learnset = this.dex.species.getLearnset(species.id); - let learnsetSpecies = species; - if (formes.includes(species.id) || !learnset) { - learnsetSpecies = this.dex.species.get(species.baseSpecies); - learnset = this.dex.species.getLearnset(learnsetSpecies.id); - } - if (learnset) { - pool = Object.keys(learnset).filter( - moveid => learnset![moveid].find(learned => learned.startsWith(String(this.gen))) - ); - } - if (learnset && learnsetSpecies === species && species.changesFrom) { - learnset = this.dex.species.getLearnset(toID(species.changesFrom)); - const basePool = Object.keys(learnset!).filter( - moveid => learnset![moveid].find(learned => learned.startsWith(String(this.gen))) - ); - pool = [...new Set(pool.concat(basePool))]; - } - const evoRegion = learnsetSpecies.evoRegion && learnsetSpecies.gen !== this.gen; - for (let i = 0; i < 2 && learnsetSpecies.prevo; i++) { - learnsetSpecies = this.dex.species.get(learnsetSpecies.prevo); - learnset = this.dex.species.getLearnset(learnsetSpecies.id); - for (const moveid in learnset) { - if (!pool.includes(moveid) && !evoRegion || learnset[moveid].some( - source => !source.startsWith(`${learnsetSpecies.gen}`) || source.charAt(1) === 'E' - )) { - pool.push(moveid); - } - } - } + pool = [...this.dex.species.getMovePool(species.id)]; } const moves = this.multipleSamplesNoReplace(pool, this.maxMoveCount); @@ -601,8 +570,7 @@ export class RandomGen8Teams { for (const species of speciesPool) { if (species.isNonstandard && species.isNonstandard !== 'Unobtainable') continue; if (requireMoves) { - const hasMovesInCurrentGen = Object.values(this.dex.species.getLearnset(species.id) || {}) - .some(sources => sources.some(source => source.startsWith('9'))); + const hasMovesInCurrentGen = this.dex.species.getMovePool(species.id).size; if (!hasMovesInCurrentGen) continue; } if (requiredType && !species.types.includes(requiredType)) continue; @@ -2208,7 +2176,7 @@ export class RandomGen8Teams { (isDoubles && data.doublesMoves) || (isNoDynamax && data.noDynamaxMoves) || data.moves; - const movePool = (randMoves || Object.keys(this.dex.species.getLearnset(species.id)!)).slice(); + const movePool: string[] = [...(randMoves || this.dex.species.getMovePool(species.id))]; if (this.format.gameType === 'multi' || this.format.gameType === 'freeforall') { // Random Multi Battle uses doubles move pools, but Ally Switch fails in multi battles // Random Free-For-All also uses doubles move pools, for now diff --git a/randoms/src/gen9.ts b/randoms/src/gen9.ts index 8e6e6132..4717ab1a 100644 --- a/randoms/src/gen9.ts +++ b/randoms/src/gen9.ts @@ -1936,43 +1936,11 @@ export class RandomTeams { // Four random unique moves from the movepool let pool = ['struggle']; if (forme === 'Smeargle') { - pool = this.dex.moves - .all() + pool = this.dex.moves.all() .filter(move => !(move.isNonstandard || move.isZ || move.isMax || move.realMove)) .map(m => m.id); } else { - const formes = ['gastrodoneast', 'pumpkaboosuper', 'zygarde10']; - let learnset = this.dex.species.getLearnset(species.id); - let learnsetSpecies = species; - if (formes.includes(species.id) || !learnset) { - learnsetSpecies = this.dex.species.get(species.baseSpecies); - learnset = this.dex.species.getLearnset(learnsetSpecies.id); - } - if (learnset) { - pool = Object.keys(learnset).filter( - moveid => learnset![moveid].find(learned => learned.startsWith(String(this.gen))) - ); - } - if (learnset && learnsetSpecies === species && species.changesFrom) { - learnset = this.dex.species.getLearnset(toID(species.changesFrom)); - for (const moveid in learnset) { - if (!pool.includes(moveid) && learnset[moveid].some(source => source.startsWith(String(this.gen)))) { - pool.push(moveid); - } - } - } - const evoRegion = learnsetSpecies.evoRegion && learnsetSpecies.gen !== this.gen; - for (let i = 0; i < 2 && learnsetSpecies.prevo; i++) { - learnsetSpecies = this.dex.species.get(learnsetSpecies.prevo); - learnset = this.dex.species.getLearnset(learnsetSpecies.id); - for (const moveid in learnset) { - if (!pool.includes(moveid) && learnset[moveid].some( - source => source.startsWith(String(this.gen)) && (!evoRegion || source.charAt(1) === 'E') - )) { - pool.push(moveid); - } - } - } + pool = [...this.dex.species.getMovePool(species.id)]; } const moves = this.multipleSamplesNoReplace(pool, this.maxMoveCount); @@ -2070,7 +2038,7 @@ export class RandomTeams { // Picks `n` random pokemon--no repeats, even among formes // Also need to either normalize for formes or select formes at random // Unreleased are okay but no CAP - const last = [0, 151, 251, 386, 493, 649, 721, 807, 898, 1010][this.gen]; + const last = [0, 151, 251, 386, 493, 649, 721, 807, 898, 1017][this.gen]; if (n <= 0 || n > last) throw new Error(`n must be a number between 1 and ${last} (got ${n})`); if (requiredType && !this.dex.types.get(requiredType).exists) { @@ -2086,8 +2054,7 @@ export class RandomTeams { for (const species of speciesPool) { if (species.isNonstandard && species.isNonstandard !== 'Unobtainable') continue; if (requireMoves) { - const hasMovesInCurrentGen = Object.values(this.dex.species.getLearnset(species.id) || {}) - .some(sources => sources.some(source => source.startsWith('9'))); + const hasMovesInCurrentGen = this.dex.species.getMovePool(species.id).size; if (!hasMovesInCurrentGen) continue; } if (requiredType && !species.types.includes(requiredType)) continue; @@ -2154,6 +2121,7 @@ export class RandomTeams { if (!(species.num in hasDexNumber)) continue; if (isNotCustom && (species.gen > this.gen || (species.isNonstandard && species.isNonstandard !== 'Unobtainable'))) continue; + if (requiredType && !species.types.includes(requiredType)) continue; if (!formes[hasDexNumber[species.num]]) formes[hasDexNumber[species.num]] = []; formes[hasDexNumber[species.num]].push(species.name); } diff --git a/sim/config/formats.ts b/sim/config/formats.ts index 72111211..bc0e6e0f 100644 --- a/sim/config/formats.ts +++ b/sim/config/formats.ts @@ -367,7 +367,7 @@ export const Formats: FormatList = [ banlist: [ 'Annihilape', 'Arceus-Base', 'Arceus-Fairy', 'Arceus-Ground', - 'Basculegion', 'Calyrex-Ice', + 'Basculegion-Base', 'Calyrex-Ice', 'Chien-Pao', 'Clodsire', 'Ditto', 'Eternatus', 'Flutter Mane', 'Glimmora', @@ -384,8 +384,8 @@ export const Formats: FormatList = [ 'Toxapex', 'Zacian-Crowned', 'Arceus-Electric', 'Arceus-Flying', 'Arceus-Ghost', 'Arceus-Steel', - 'Arceus-Water', 'Basculegion-F', - 'Shaymin-Sky' + 'Arceus-Water', 'Shaymin-Sky', + 'Last Respects' ] }, { name: '[Gen 9] ZU', mod: 'gen9', ruleset: [ '[Gen 9] PU' ], banlist: [ 'PU', 'ZUBL' ] }, @@ -436,9 +436,9 @@ export const Formats: FormatList = [ ] }, { - name: '[Gen 9] Monothreat Steel', + name: '[Gen 9] Monothreat Fire', mod: 'gen9', - ruleset: [ '[Gen 9] Monotype', 'Force Monotype = Steel' ] + ruleset: [ '[Gen 9] Monotype', 'Force Monotype = Fire' ] }, { name: '[Gen 9] Monotype CAP', @@ -451,30 +451,7 @@ export const Formats: FormatList = [ mod: 'gen9', ruleset: [ '[Gen 9] LC', 'Same Type Clause', 'Terastal Clause' ], banlist: [ 'Damp Rock', 'Focus Band', 'Heat Rock', 'Icy Rock', 'Quick Claw' ], - unbanlist: [ 'Diglett', 'Growlithe-Hisui', 'Vulpix', 'Vulpix-Alola' ] - }, - { - name: '[Gen 3] Hoenn Gaiden', - mod: 'gen3hoenngaiden', - ruleset: [ 'HG Standard', 'Freeze Clause Mod' ], - banlist: [ 'Uber' ], - unbanlist: [ 'Sand Veil' ] - }, - { - name: '[Gen 8] JolteMons Random Battle', - mod: 'gen8joltemons', - team: 'random', - ruleset: [ - 'Dynamax Clause', - 'Obtainable', - 'Species Clause', - 'HP Percentage Mod', - 'Cancel Mod', - 'Sleep Clause Mod', - 'Mega Data Mod', - 'Z-Move Clause', - 'Illusion Level Mod' - ] + unbanlist: [ 'Diglett-Base', 'Growlithe-Hisui', 'Vulpix', 'Vulpix-Alola', 'Sticky Web' ] }, { name: '[Gen 9] Balanced Hackmons', @@ -608,19 +585,19 @@ export const Formats: FormatList = [ 'Lunala', 'Magearna', 'Marshadow', 'Mawile-Mega', 'Medicham-Mega', 'Metagross-Mega', 'Mewtwo', 'Miraidon', 'Naganadel', - 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palafin', - 'Palkia', 'Pheromosa', 'Rayquaza', - 'Reshiram', 'Salamence-Mega', 'Shaymin-Sky', - 'Solgaleo', 'Spectrier', 'Urshifu-Base', - 'Xerneas', 'Yveltal', 'Zacian', - 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', - 'Zekrom', 'Zygarde-Base', 'Zygarde-Complete', - 'Moody', 'Shadow Tag', 'Power Construct', - 'Booster Energy', 'Damp Rock', 'Focus Band', - 'Icy Rock', "King's Rock", 'Leppa Berry', - 'Quick Claw', 'Razor Fang', 'Smooth Rock', - 'Terrain Extender', 'Acupressure', 'Baton Pass', - 'Last Respects' + 'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Ogerpon-Hearthflame', + 'Palafin', 'Palkia', 'Pheromosa', + 'Rayquaza', 'Reshiram', 'Salamence-Mega', + 'Shaymin-Sky', 'Solgaleo', 'Spectrier', + 'Urshifu-Base', 'Xerneas', 'Yveltal', + 'Zacian', 'Zacian-Crowned', 'Zamazenta', + 'Zamazenta-Crowned', 'Zekrom', 'Zygarde-Base', + 'Zygarde-Complete', 'Moody', 'Shadow Tag', + 'Power Construct', 'Booster Energy', 'Damp Rock', + 'Focus Band', 'Icy Rock', "King's Rock", + 'Leppa Berry', 'Quick Claw', 'Razor Fang', + 'Smooth Rock', 'Terrain Extender', 'Acupressure', + 'Baton Pass', 'Last Respects' ] }, { @@ -2081,12 +2058,12 @@ export const Formats: FormatList = [ '!Freeze Clause Mod' ], banlist: [ - 'Latias', 'Machamp', - 'Porygon-Z', 'Shaymin', - 'Snorlax', 'Togekiss', - 'Focus Sash', 'Destiny Bond', - 'Explosion', 'Perish Song', - 'Self-Destruct' + 'Jirachi', 'Latias', + 'Machamp', 'Porygon-Z', + 'Shaymin', 'Snorlax', + 'Togekiss', 'Focus Sash', + 'Destiny Bond', 'Explosion', + 'Perish Song', 'Self-Destruct' ], unbanlist: [ 'Wobbuffet', 'Wynaut', 'Sand Veil', 'Swagger' ] }, diff --git a/sim/data/abilities.ts b/sim/data/abilities.ts index ab19a378..d9497442 100644 --- a/sim/data/abilities.ts +++ b/sim/data/abilities.ts @@ -3305,7 +3305,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { condition: { noCopy: true, onStart(pokemon, source, effect) { - if (effect?.id === 'boosterenergy') { + if (effect?.name === 'Booster Energy') { this.effectState.fromBooster = true; this.add('-activate', pokemon, 'ability: Protosynthesis', '[fromitem]'); } else { @@ -3439,7 +3439,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { condition: { noCopy: true, onStart(pokemon, source, effect) { - if (effect?.id === 'boosterenergy') { + if (effect?.name === 'Booster Energy') { this.effectState.fromBooster = true; this.add('-activate', pokemon, 'ability: Quark Drive', '[fromitem]'); } else { diff --git a/sim/data/formats-data.ts b/sim/data/formats-data.ts index ec807eb8..3bd91281 100644 --- a/sim/data/formats-data.ts +++ b/sim/data/formats-data.ts @@ -328,9 +328,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { natDexTier: "UU", }, vulpix: { - tier: "NUBL", - doublesTier: "NFE", - natDexTier: "LC", + tier: "NFE", }, vulpixalola: { tier: "NFE", @@ -405,7 +403,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { tier: "LC", }, venomoth: { - tier: "NUBL", + tier: "NU", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -449,7 +447,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { natDexTier: "RU", }, perrserker: { - tier: "ZU", + tier: "ZUBL", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -772,7 +770,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { tier: "LC", }, electrode: { - tier: "ZU", + tier: "ZUBL", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -1033,7 +1031,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { natDexTier: "UUBL", }, tauros: { - tier: "ZU", + tier: "ZUBL", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -1179,7 +1177,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { tier: "LC", }, snorlax: { - tier: "NUBL", + tier: "NU", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -1570,13 +1568,13 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { sneasler: { tier: "Uber", doublesTier: "(DUU)", - natDexTier: "OU", + natDexTier: "Uber", }, teddiursa: { tier: "LC", }, ursaring: { - tier: "ZU", + tier: "ZUBL", doublesTier: "NFE", natDexTier: "NFE", }, @@ -1609,7 +1607,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { mamoswine: { tier: "UU", doublesTier: "(DUU)", - natDexTier: "RU", + natDexTier: "RUBL", }, corsola: { isNonstandard: "Past", @@ -1681,7 +1679,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { tier: "NFE", }, wyrdeer: { - tier: "ZU", + tier: "ZUBL", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -1929,7 +1927,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { tier: "LC", }, masquerain: { - tier: "ZU", + tier: "ZUBL", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -2853,12 +2851,12 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { natDexTier: "UU", }, rotomfrost: { - tier: "ZU", + tier: "ZUBL", doublesTier: "(DUU)", natDexTier: "RU", }, rotomfan: { - tier: "ZU", + tier: "ZUBL", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -2873,7 +2871,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { natDexTier: "RU", }, mesprit: { - tier: "PU", + tier: "PUBL", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -3383,7 +3381,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { tier: "LC", }, zoroark: { - tier: "NUBL", + tier: "NU", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -3710,7 +3708,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { natDexTier: "RU", }, tornadus: { - tier: "NUBL", + tier: "NU", doublesTier: "DOU", natDexTier: "RU", }, @@ -3773,7 +3771,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { isNonstandard: "Past", }, meloetta: { - tier: "NUBL", + tier: "NU", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -3820,7 +3818,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { tier: "NFE", }, delphox: { - tier: "NUBL", + tier: "NU", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -4287,7 +4285,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { natDexTier: "RU", }, oricorio: { - tier: "NUBL", + tier: "NU", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -4302,7 +4300,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { natDexTier: "RU", }, oricoriosensu: { - tier: "NUBL", + tier: "NU", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -4822,7 +4820,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { tier: "NFE", }, inteleon: { - tier: "NUBL", + tier: "NU", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -4994,7 +4992,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { tier: "LC", }, toxtricity: { - tier: "NUBL", + tier: "NU", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -5098,7 +5096,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { tier: "LC", }, frosmoth: { - tier: "NUBL", + tier: "NU", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -5500,7 +5498,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { natDexTier: "RU", }, flamigo: { - tier: "NUBL", + tier: "NU", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -5550,7 +5548,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { tier: "LC", }, mabosstiff: { - tier: "ZU", + tier: "ZUBL", doublesTier: "(DUU)", natDexTier: "RU", }, @@ -5579,7 +5577,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { natDexTier: "OU", }, brutebonnet: { - tier: "NUBL", + tier: "NU", doublesTier: "DUU", natDexTier: "RU", }, @@ -5619,7 +5617,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { natDexTier: "UU", }, ironhands: { - tier: "UU", + tier: "UUBL", doublesTier: "DOU", natDexTier: "UU", }, @@ -5773,7 +5771,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = { ogerponcornerstone: { tier: "OU", doublesTier: "DUU", - natDexTier: "UU", + natDexTier: "UUBL", }, missingno: { isNonstandard: "Custom", diff --git a/sim/data/learnsets.ts b/sim/data/learnsets.ts index 9b1d3e02..718823ca 100644 --- a/sim/data/learnsets.ts +++ b/sim/data/learnsets.ts @@ -137056,120 +137056,142 @@ export const Learnsets: {[k: string]: ModdedLearnsetData} = { }, revenankh: { learnset: { - ancientpower: [ - '9E', '8E', - '7E', '6E', - '5E', '4T', - '4E' - ], - armthrust: [ '9L5', '8L5', '7L13', '4L18' ], - attract: [ '8M', '7M', '4M' ], - bide: [ '7L1', '4L1' ], - bind: [ '7T' ], - bodypress: [ '9M', '8M' ], + ancientpower: [ '9E', '8E', '7E', '6E', '5E', '4E' ], + armthrust: [ '9L4', '8L4', '7L8', '6L8', '5L8', '4L8' ], + attract: [ '8M', '7M', '6M', '5M', '4M' ], + bind: [ '7T', '6T', '5T' ], bodyslam: [ '9M', '8M' ], - brickbreak: [ '9M', '8M', '7M', '4M' ], - brutalswing: [ '8M', '7M' ], - bulkup: [ '9M', '8M', '7M', '4M' ], - bulldoze: [ '9M', '8M', '7M' ], + brickbreak: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + bulkup: [ '9M', '8M', '7M', '6M', '5M', '4M' ], captivate: [ '4M' ], - closecombat: [ '9M' ], + closecombat: [ '9M', '8M' ], coaching: [ '8T' ], - confide: [ '7M' ], + confide: [ '7M', '6M' ], confuseray: [ '9M' ], - counter: [ '9E', '8E', '7E', '4T' ], - curse: [ '9E', '8E', '7E', '4E' ], - destinybond: [ '9E', '8E', '7E', '4E' ], - doubleteam: [ '7M', '4M' ], - drainpunch: [ '9M', '9L50', '8M', '8L55', '7M', '4M' ], - dreameater: [ '7M', '4M' ], - dualchop: [ '7T' ], - earthquake: [ '9M', '8M', '7M', '4M' ], - embargo: [ '7M', '4M' ], + counter: [ '9E', '8E', '7E', '6E', '5E', '4E' ], + curse: [ '9L36', '8L36', '7L28', '6L28', '5L28', '4L28' ], + darkestlariat: [ '8M' ], + darkpulse: [ '9M', '8M', '7M', '6M', '5T', '4M' ], + destinybond: [ '9E', '8E', '7E', '6E', '5E', '4E' ], + detect: [ '9L8', '8L8', '7L11', '6L11', '5L11', '4L11' ], + dig: [ '9M', '8M', '6M', '5M', '4M' ], + doubleteam: [ '8M', '7M', '6M', '5M', '4M' ], + drainpunch: [ '9M', '8M', '7T', '6T', '5T', '4M' ], + dreameater: [ '7M', '6M', '5M', '4M' ], + dualchop: [ '7T', '6T', '5T' ], + earthquake: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + embargo: [ '7M', '6M', '5M', '4M' ], endure: [ '9M', '8M', '4M' ], - facade: [ '9M', '8M', '7M', '4M' ], - fling: [ '9M', '8M', '7M', '4M' ], - focusblast: [ '9M', '8M', '7M', '4M' ], - focuspunch: [ '9M', '9L70', '8L75', '7T', '6T', '4M' ], - forcepalm: [ '9E', '8E', '7E', '4E' ], - frustration: [ '7M', '4M' ], - gigaimpact: [ '9M', '8M', '7M', '4M' ], - glare: [ '9L35', '8L35', '7L26', '4L26' ], - hammerarm: [ '9L60', '8L65', '7L57', '4L44' ], - headbutt: [ '7T' ], - helpinghand: [ '9M', '8M', '7T', '4T' ], - hex: [ '9M', '8M', '7E' ], - hiddenpower: [ '7M', '4M' ], - highhorsepower: [ '8M' ], - hyperbeam: [ '9M', '8M', '7M', '4M' ], - icepunch: [ '9M', '8M', '7T', '4T' ], + facade: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + fling: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + focusblast: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + focuspunch: [ '9M', '9L56', '8L60', '7T', '6T', '4M' ], + forcepalm: [ '9L28', '8L28', '7L15', '6L15', '5L15', '4L15' ], + foulplay: [ '9M', '8M', '7T', '6T', '5T' ], + frustration: [ '7M', '6M', '5M', '4M' ], + gigaimpact: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + glare: [ '9L24', '8L24', '7L21', '6L21', '5L21', '4L21' ], + grudge: [ '8L52', '7L55', '6L55', '5L55', '4L49' ], + hammerarm: [ '9L48', '8L48', '7L49', '6L49', '5L49', '4L44' ], + helpinghand: [ '9M', '8M' ], + hex: [ + '9M', '9L32', + '8M', '8L32', + '7L44', '6L44', + '5L44' + ], + hiddenpower: [ '7M', '6M', '5M', '4M' ], + hyperbeam: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + icepunch: [ '9M', '8M', '7T', '6T', '5T', '4T' ], knockoff: [ '9M', '9L40', '8L40', '7T', '6T', '5T', '4T' ], - machpunch: [ '9E', '8E', '7E', '4E' ], - meanlook: [ '9L25', '8L25', '7L18', '4L23' ], + laserfocus: [ '7T' ], + lashout: [ '9M', '8T' ], + leer: [ '9L1', '8L1', '7L1', '6L1', '5L1', '4L1' ], + machpunch: [ '9E', '8E', '7E', '6E', '5E', '4E' ], + meanlook: [ '9L16', '8L16', '7L5', '6L5', '5L5', '4L5' ], megapunch: [ '8M' ], - memento: [ '9E', '8E', '7E', '4E' ], - metronome: [ '9M', '8M', '7E', '4T' ], - mimic: [ '7E', '4E' ], - moonlight: [ '9L45', '8L45', '7L62', '4L62' ], + memento: [ '9E', '8E', '7E', '6E', '5E', '4E' ], + metronome: [ '9M', '8M' ], + moonlight: [ '9L44', '8L44', '7L66', '6L66', '5L66', '4L60' ], mudslap: [ '9M', '4T' ], - nastyplot: [ '9M', '8M', '7E', '4E' ], + nastyplot: [ '9M', '8M', '7E', '6E', '5E', '4E' ], naturalgift: [ '4M' ], - ominouswind: [ '7E', '4T', '4E' ], - painsplit: [ '7T', '4T' ], - payback: [ '9L20', '8M', '8L20', '7L38', '4M' ], + nightshade: [ '9M' ], + ominouswind: [ '7E', '6E', '5E', '4T', '4E' ], + painsplit: [ '7T', '6T', '5T', '4T' ], + payback: [ '8M', '7M', '6M', '5M', '4M' ], + phantomforce: [ '9M' ], + poisonjab: [ '9M', '8M', '7M', '6M', '5M', '4M' ], poltergeist: [ '8T' ], - poweruppunch: [ '7M' ], - powerwhip: [ '9L65', '8M', '8L70', '7L52', '4L38' ], - protect: [ '9M', '8M', '7M', '4M' ], - psychup: [ '7M', '4M' ], - punishment: [ '7L71', '4L52' ], - quickguard: [ '9L15', '8L15', '7L48' ], - raindance: [ '9M', '8M', '7M', '4M' ], - rest: [ '9M', '8M', '7M', '4M' ], - retaliate: [ '8M', '7M' ], - return: [ '7M', '4M' ], - revenge: [ '8M', '8L50', '7L33', '4L33' ], + poweruppunch: [ '6M' ], + powerwhip: [ '9L52', '8L56', '7L60', '6L60', '5L60', '4L55' ], + protect: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + psychup: [ '7M', '6M', '5M', '4M' ], + punishment: [ '7L33', '6L33', '5L33', '4L33' ], + quickguard: [ '9E', '8E', '7E', '6E' ], + raindance: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + rest: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + retaliate: [ '8M', '6M', '5M' ], + return: [ '7M', '6M', '5M', '4M' ], + revenge: [ '8M' ], reversal: [ '9M', '8M' ], - rockslide: [ '9M', '8M', '7M', '4M' ], + rockclimb: [ '4M' ], + rockslide: [ '9M', '8M', '7M', '6M', '5M', '4M' ], rocksmash: [ '6M', '5M', '4M' ], - rocktomb: [ '9M', '8M', '7M', '7L23', '4M', '4L13' ], - round: [ '8M', '7M' ], - safeguard: [ '8M', '7M', '4M' ], - sandstorm: [ '9M', '8M', '7M', '4M' ], - sandtomb: [ '9M', '9L1', '8M', '8L1', '7L5', '4L5' ], - secretpower: [ '7M', '4M' ], - shadowball: [ '9M', '8M', '7M', '4M' ], - shadowclaw: [ '9M', '8M', '7M' ], - shadowpunch: [ '9L30', '8L30', '7L29', '4L29' ], - shadowsneak: [ '9L10', '8L10', '7E', '4E' ], - sleeptalk: [ '9M', '8M', '7M', '4M' ], - smackdown: [ '9M', '7M' ], - snore: [ '8M', '7T' ], + rocktomb: [ + '9M', '8M', '7M', + '7L18', '6M', '6L18', + '5M', '5L18', '4M', + '4L18' + ], + roleplay: [ '7T', '6T', '5T', '4T' ], + round: [ '8M', '7M', '6M', '5M' ], + safeguard: [ '8M', '7M', '6M', '5M', '4M' ], + sandstorm: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + sandtomb: [ + '9M', '9L12', + '8M', '8L12', + '7L39', '6L39', + '5L39', '4L39' + ], + scaryface: [ '9M', '8M' ], + secretpower: [ '6M', '4M' ], + shadowball: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + shadowclaw: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + shadowpunch: [ '9L20', '8L20', '7L25', '6L25', '5L25', '4L25' ], + shadowsneak: [ '9E', '8E', '7E', '6E', '5E', '4E' ], + sleeptalk: [ '9M', '8M', '7M', '6M', '5T', '4M' ], + smackdown: [ '9M', '7M', '6M', '5M' ], + snatch: [ '7T', '6T', '5T', '4M' ], + snore: [ '8M', '7T', '6T', '5T', '4T' ], spite: [ '9M', '7T', '6T', '5T', '4T' ], strength: [ '6M', '5M', '4M' ], - substitute: [ '9M', '8M', '7M', '4M' ], - suckerpunch: [ '9L55', '8L60', '4T' ], - sunnyday: [ '9M', '8M', '7M', '4M' ], + substitute: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + suckerpunch: [ '9E', '8E', '7E', '6E', '5E', '4T' ], + sunnyday: [ '9M', '8M', '7M', '6M', '5M', '4M' ], superpower: [ '8M', '7T', '6T', '5T', '4T' ], - swagger: [ '7M', '4M' ], + swagger: [ '7M', '6M', '5M', '4M' ], takedown: [ '9M' ], - taunt: [ '9M', '8M', '7M', '4M' ], - telekinesis: [ '7M' ], + taunt: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + telekinesis: [ '7T', '5M' ], terablast: [ '9M' ], - thunderpunch: [ '9M' ], - torment: [ '7M', '4M' ], - toxic: [ '7M', '4M' ], - trick: [ '9M', '8M', '7T', '4T' ], - vacuumwave: [ '4T' ], + thief: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + throatchop: [ '8M', '7T' ], + thunderpunch: [ '9M', '8M', '7T', '6T', '5T', '4T' ], + torment: [ '7M', '6M', '5M', '4M' ], + toxic: [ '7M', '6M', '5M', '4M' ], + trick: [ '9M', '8M', '7T', '6T', '5T', '4T' ], + trickroom: [ '9M', '8M', '7M', '6M', '5M', '4M' ], + vacuumwave: [ '9M', '4T' ], willowisp: [ '9M', '8M', '7M' ], - workup: [ '8M', '7M' ], - wrap: [ '9L1', '8L1', '7L1', '4L1' ], - wringout: [ '7L66', '4L9' ] + wonderroom: [ '8M', '7T', '6T', '5T' ], + workup: [ '8M', '7M', '5M' ], + wrap: [ '9L1', '8L1', '7L1', '6L1', '5L1', '4L1' ] } }, embirch: { diff --git a/sim/data/mods/gen2/rulesets.ts b/sim/data/mods/gen2/rulesets.ts index 4acf130d..9c77b6bd 100644 --- a/sim/data/mods/gen2/rulesets.ts +++ b/sim/data/mods/gen2/rulesets.ts @@ -1,3 +1,5 @@ +import type {Learnset} from "../../../sim/dex-species"; + export const Rulesets: {[k: string]: ModdedFormatData} = { obtainablemoves: { inherit: true, @@ -74,34 +76,31 @@ export const Rulesets: {[k: string]: ModdedFormatData} = { magneton: {triattack: 'L'}, cloyster: {spikes: 'L'}, }; + + const moveSources: NonNullable = Object.fromEntries( + set.moves.map(move => [this.toID(move), []]) + ); + + const species = this.dex.species.get(set.species); + for (const {learnset} of this.dex.species.getFullLearnset(species.id)) { + for (const moveid in moveSources) { + moveSources[moveid].push(...(learnset[moveid] || [])); + } + } + const notUsableAsTM = ['icebeam', 'flamethrower', 'thunderbolt']; - const species = this.dex.species.get(set.species || set.name); - const learnsetData = {...(this.dex.data.Learnsets[species.id]?.learnset || {})}; const legalityList = illegalCombos[species.id]; const problems = []; - let prevo = species.prevo; - while (prevo) { - const prevoSpecies = this.dex.species.get(prevo); - const prevoLsetData = this.dex.data.Learnsets[prevoSpecies.id]?.learnset || {}; - for (const moveid in prevoLsetData) { - if (!(moveid in learnsetData)) { - learnsetData[moveid] = prevoLsetData[moveid]; - } else { - learnsetData[moveid].push(...prevoLsetData[moveid]); - } - } - prevo = prevoSpecies.prevo; - } for (const moveid of set.moves.map(this.toID)) { // Diglett Magnemite Shellder - if (!learnsetData[moveid]) continue; + if (!moveSources[moveid]) continue; if (legalityList) { - const list = learnsetData[moveid].filter(x => !x.includes(legalityList[moveid])); + const list = moveSources[moveid].filter(x => !x.includes(legalityList[moveid])); if (!list.length) { switch (legalityList[moveid]) { case 'L': // Converted to a set to remove duplicate entries - const levels = new Set(learnsetData[moveid].filter(x => x.includes(legalityList[moveid])).map(x => x.slice(2))); + const levels = new Set(moveSources[moveid].filter(x => x.includes(legalityList[moveid])).map(x => x.slice(2))); problems.push( `${species.name} can't learn ${this.dex.moves.get(moveid).name}.`, `(It learns ${this.dex.moves.get(moveid).name} in Pok\u00e9mon Crystal at the following levels: ${[...levels].join(', ')})` @@ -123,7 +122,7 @@ export const Rulesets: {[k: string]: ModdedFormatData} = { } } for (const id of notUsableAsTM) { - if (moveid === id && learnsetData[id] && !learnsetData[id].filter(x => !x.includes('2T')).length) { + if (moveid === id && moveSources[id] && !moveSources[id].filter(x => !x.includes('2T')).length) { problems.push(`${species.name} can't learn ${this.dex.moves.get(id).name}.`); } } diff --git a/sim/lib/utils.ts b/sim/lib/utils.ts index fbdc2986..36364950 100644 --- a/sim/lib/utils.ts +++ b/sim/lib/utils.ts @@ -335,6 +335,22 @@ export function deepClone(obj: any): any { return clone; } +export function deepFreeze(obj: T): T { + if (obj === null || typeof obj !== 'object') return obj; + // support objects with reference loops + if (Object.isFrozen(obj)) return obj; + + Object.freeze(obj); + if (Array.isArray(obj)) { + for (const elem of obj) deepFreeze(elem); + return obj; + } + for (const key of Object.keys(obj)) { + deepFreeze((obj as any)[key]); + } + return obj; +} + export function levenshtein(s: string, t: string, l: number): number { // Original levenshtein distance function by James Westgate, turned out to be the fastest const d: number[][] = []; diff --git a/sim/package.json b/sim/package.json index b7b4da17..4aba0951 100644 --- a/sim/package.json +++ b/sim/package.json @@ -1,6 +1,6 @@ { "name": "@pkmn/sim", - "version": "0.7.59", + "version": "0.8.0", "description": "An automatically generated extraction of just the simulator portion of Pokémon Showdown", "repository": "github:pkmn/ps", "homepage": "https://psim.us", diff --git a/sim/sim/dex-abilities.ts b/sim/sim/dex-abilities.ts index 005eb9ad..63e7bb66 100644 --- a/sim/sim/dex-abilities.ts +++ b/sim/sim/dex-abilities.ts @@ -108,8 +108,8 @@ export class DexAbilities { }); } - (ability as any).kind = 'Ability'; - if (ability.exists) this.abilityCache.set(id, ability); + if (!(ability as any).kind) (ability as any).kind = 'Ability'; + if (ability.exists) this.abilityCache.set(id, this.dex.deepFreeze(ability)); return ability; } diff --git a/sim/sim/dex-conditions.ts b/sim/sim/dex-conditions.ts index 5a436d15..16d11066 100644 --- a/sim/sim/dex-conditions.ts +++ b/sim/sim/dex-conditions.ts @@ -678,6 +678,9 @@ export class DexConditions { condition = move as any as Condition; } else if (this.dex.data.Rulesets.hasOwnProperty(id)) { condition = this.dex.formats.get(id) as any as Condition; + // formats can't be frozen if they don't have a ruleTable + this.conditionCache.set(id, condition); + return condition; } else if (this.dex.data.Conditions.hasOwnProperty(id)) { condition = new Condition({name: id, ...this.dex.data.Conditions[id]}); } else if ( @@ -694,7 +697,7 @@ export class DexConditions { condition = new Condition({name: id, exists: false}); } - this.conditionCache.set(id, condition); + this.conditionCache.set(id, this.dex.deepFreeze(condition)); return condition; } } diff --git a/sim/sim/dex-data.ts b/sim/sim/dex-data.ts index e0528d4e..8fda466e 100644 --- a/sim/sim/dex-data.ts +++ b/sim/sim/dex-data.ts @@ -193,7 +193,7 @@ export class DexNatures { } (nature as any).kind = 'Nature'; - if (nature.exists) this.natureCache.set(id, nature); + if (nature.exists) this.natureCache.set(id, this.dex.deepFreeze(nature)); return nature; } @@ -203,7 +203,7 @@ export class DexNatures { for (const id in this.dex.data.Natures) { natures.push(this.getByID(id as ID)); } - this.allCache = natures; + this.allCache = Object.freeze(natures); return this.allCache; } } @@ -294,7 +294,7 @@ export class DexTypes { } (type as any).kind = 'Type'; - if (type.exists) this.typeCache.set(id, type); + if (type.exists) this.typeCache.set(id, this.dex.deepFreeze(type)); return type; } @@ -318,7 +318,7 @@ export class DexTypes { for (const id in this.dex.data.TypeChart) { types.push(this.getByID(id as ID)); } - this.allCache = types; + this.allCache = Object.freeze(types); return this.allCache; } } diff --git a/sim/sim/dex-items.ts b/sim/sim/dex-items.ts index 88d24377..2eac80f3 100644 --- a/sim/sim/dex-items.ts +++ b/sim/sim/dex-items.ts @@ -214,7 +214,7 @@ export class DexItems { } (item as any).kind = 'Item'; - if (item.exists) this.itemCache.set(id, item); + if (item.exists) this.itemCache.set(id, this.dex.deepFreeze(item)); return item; } @@ -224,7 +224,7 @@ export class DexItems { for (const id in this.dex.data.Items) { items.push(this.getByID(id as ID)); } - this.allCache = items; + this.allCache = Object.freeze(items); return this.allCache; } } diff --git a/sim/sim/dex-moves.ts b/sim/sim/dex-moves.ts index 2bc33ecf..3d95eb36 100644 --- a/sim/sim/dex-moves.ts +++ b/sim/sim/dex-moves.ts @@ -671,7 +671,7 @@ export class DexMoves { }); } (move as any).kind = 'Move'; - if (move.exists) this.moveCache.set(id, move); + if (move.exists) this.moveCache.set(id, this.dex.deepFreeze(move)); return move; } @@ -681,7 +681,7 @@ export class DexMoves { for (const id in this.dex.data.Moves) { moves.push(this.getByID(id as ID)); } - this.allCache = moves; + this.allCache = Object.freeze(moves); return this.allCache; } } diff --git a/sim/sim/dex-species.ts b/sim/sim/dex-species.ts index 2c574ce2..1d2d7d71 100644 --- a/sim/sim/dex-species.ts +++ b/sim/sim/dex-species.ts @@ -358,14 +358,16 @@ export class Learnset { readonly eventData?: EventInfo[]; readonly encounters?: EventInfo[]; readonly exists: boolean; + readonly species: Species; - constructor(data: AnyObject) { + constructor(data: AnyObject, species: Species) { this.exists = data.exists === false ? data.exists : true; this.effectType = 'Learnset'; this.learnset = data.learnset || undefined; this.eventOnly = !!data.eventOnly; this.eventData = data.eventData || undefined; this.encounters = data.encounters || undefined; + this.species = species; } } @@ -424,7 +426,7 @@ export class DexSpecies { } } } - this.speciesCache.set(id, species); + this.speciesCache.set(id, this.dex.deepFreeze(species)); return species; } @@ -535,7 +537,10 @@ export class DexSpecies { species.canHatch = species.canHatch || (!['Ditto', 'Undiscovered'].includes(species.eggGroups[0]) && !species.prevo && species.name !== 'Manaphy'); if (this.dex.gen === 1) species.bst -= species.baseStats.spd; - if (this.dex.gen < 5) delete species.abilities['H']; + if (this.dex.gen < 5) { + species.abilities = this.dex.deepClone(species.abilities); + delete species.abilities['H']; + } if (this.dex.gen === 3 && this.dex.abilities.get(species.abilities['1']).gen === 4) delete species.abilities['1']; } else { species = new Species({ @@ -544,15 +549,114 @@ export class DexSpecies { }); } (species as any).kind = 'Species'; - if (species.exists) this.speciesCache.set(id, species); + if (species.exists) this.speciesCache.set(id, this.dex.deepFreeze(species)); return species; } + getMovePool(id: ID, isNatDex = false): Set { + let eggMovesOnly = false; + let maxGen = this.dex.gen; + const movePool = new Set(); + for (const {species, learnset} of this.getFullLearnset(id)) { + for (const moveid in learnset) { + if (eggMovesOnly) { + if (learnset[moveid].some(source => source.startsWith('9E'))) { + movePool.add(moveid as ID); + } + } else if (maxGen >= 9) { + // Pokemon Home now strips learnsets on withdrawal + if (isNatDex || learnset[moveid].some(source => source.startsWith('9'))) { + movePool.add(moveid as ID); + } + } else { + if (learnset[moveid].some(source => parseInt(source.charAt(0)) <= maxGen)) { + movePool.add(moveid as ID); + } + } + if (species.evoRegion) { + // species can only evolve in this gen, so prevo can't have any moves + // from after that gen + if (this.dex.gen >= 9) eggMovesOnly = true; + if (this.dex.gen === 8 && species.evoRegion === 'Alola') maxGen = 7; + } + } + } + return movePool; + } + + getFullLearnset(id: ID): (Learnset & {learnset: NonNullable})[] { + const originalSpecies = this.get(id); + let species: Species | null = originalSpecies; + const out: (Learnset & {learnset: NonNullable})[] = []; + const alreadyChecked: {[k: string]: boolean} = {}; + + while (species?.name && !alreadyChecked[species.id]) { + alreadyChecked[species.id] = true; + const learnset = this.getLearnsetData(species.id); + if (learnset.learnset) { + out.push(learnset as any); + species = this.learnsetParent(species); + continue; + } - getLearnset(id: ID): Learnset['learnset'] { - return this.getLearnsetData(id).learnset; + // no learnset + if ((species.changesFrom || species.baseSpecies) !== species.name) { + // forme without its own learnset + species = this.get(species.changesFrom || species.baseSpecies); + // warning: formes with their own learnset, like Wormadam, should NOT + // inherit from their base forme unless they're freely switchable + continue; + } + if (species.isNonstandard) { + // It's normal for a nonstandard species not to have learnset data + + // Formats should replace the `Obtainable Moves` rule if they want to + // allow pokemon without learnsets. + return out; + } + if (species.prevo && this.getLearnsetData(toID(species.prevo)).learnset) { + species = this.get(toID(species.prevo)); + continue; + } + + // should never happen + throw new Error(`Species with no learnset data: ${species.id}`); + } + + return out; } + learnsetParent(species: Species) { + // Own Tempo Rockruff and Battle Bond Greninja are special event formes + // that are visually indistinguishable from their base forme but have + // different learnsets. To prevent a leak, we make them show up as their + // base forme, but hardcode their learnsets into Rockruff-Dusk and + // Greninja-Ash + if (['Gastrodon', 'Pumpkaboo', 'Sinistea', 'Tatsugiri'].includes(species.baseSpecies) && species.forme) { + return this.get(species.baseSpecies); + } else if (species.name === 'Lycanroc-Dusk') { + return this.get('Rockruff-Dusk'); + } else if (species.name === 'Greninja-Bond') { + return null; + } else if (species.prevo) { + // there used to be a check for Hidden Ability here, but apparently it's unnecessary + // Shed Skin Pupitar can definitely evolve into Unnerve Tyranitar + species = this.get(species.prevo); + if (species.gen > Math.max(2, this.dex.gen)) return null; + return species; + } else if (species.changesFrom && species.baseSpecies !== 'Kyurem') { + // For Pokemon like Rotom and Necrozma whose movesets are extensions are their base formes + return this.get(species.changesFrom); + } + return null; + } + + /** + * Gets the raw learnset data for the species. + * + * In practice, if you're trying to figure out what moves a pokemon learns, + * you probably want to `getFullLearnset` or `getMovePool` instead. + */ getLearnsetData(id: ID): Learnset { return this.dex.learnsets.getInternal(id); } @@ -567,7 +671,7 @@ export class DexSpecies { for (const id in this.dex.data.Pokedex) { species.push(this.getByID(id as ID)); } - this.allCache = species; + this.allCache = Object.freeze(species); return this.allCache; } } @@ -592,12 +696,12 @@ export class DexLearnsets { let learnsetData = this.learnsetCache.get(id); if (learnsetData) return learnsetData; if (!this.dex.data.Learnsets.hasOwnProperty(id)) { - learnsetData = new Learnset({exists: false}); + learnsetData = new Learnset({exists: false}, this.dex.species.get(id)); } else { - learnsetData = new Learnset(this.dex.data.Learnsets[id]); + learnsetData = new Learnset(this.dex.data.Learnsets[id], this.dex.species.get(id)); } (learnsetData as any).kind = 'Learnset'; - if (learnsetData.exists) this.learnsetCache.set(id, learnsetData); + if (learnsetData.exists) this.learnsetCache.set(id, this.dex.deepFreeze(learnsetData)); return learnsetData; } } diff --git a/sim/sim/dex.ts b/sim/sim/dex.ts index b2a84b8a..9060ab05 100644 --- a/sim/sim/dex.ts +++ b/sim/sim/dex.ts @@ -1,4 +1,4 @@ -import {Utils} from '../lib/utils'; +import * as Utils from '../lib/utils'; import {Condition, DexConditions} from './dex-conditions'; import {DataMove, DexMoves} from './dex-moves'; import {Item, DexItems} from './dex-items'; @@ -185,6 +185,7 @@ export class ModdedDex { dataCache: DexTableData | null; deepClone = Utils.deepClone; + deepFreeze = Utils.deepFreeze; constructor(mod = 'base') { this.isBase = (mod === 'base'); diff --git a/sim/sim/team-validator.ts b/sim/sim/team-validator.ts index e887c291..4352632b 100644 --- a/sim/sim/team-validator.ts +++ b/sim/sim/team-validator.ts @@ -1407,7 +1407,7 @@ export class TeamValidator { // father must be male if (father.gender === 'N' || father.gender === 'F') continue; // can't inherit from dex entries with no learnsets - if (!dex.species.getLearnset(father.id)) continue; + if (!dex.species.getLearnsetData(father.id).learnset) continue; // something is clearly wrong if its only possible father is itself // (exceptions: ExtremeSpeed Dragonite, Self-destruct Snorlax) if (pokemonBlacklist.includes(father.id) && !['dragonite', 'snorlax'].includes(father.id)) continue; @@ -1444,8 +1444,7 @@ export class TeamValidator { */ fatherCanLearn(baseSpecies: Species, species: Species, moves: ID[], eggGen: number, pokemonBlacklist: ID[], noRecurse: boolean | undefined) { - let learnset = this.dex.species.getLearnset(species.id); - if (!learnset) return false; + if (!this.dex.species.getLearnsetData(species.id).learnset) return false; if (species.id === 'smeargle') return true; const canBreedWithSmeargle = species.eggGroups.includes('Field'); @@ -1453,12 +1452,10 @@ export class TeamValidator { const allEggSources = new PokemonSources(); allEggSources.sourcesBefore = eggGen; for (const move of moves) { - let curSpecies: Species | null = species; const eggSources = new PokemonSources(); - while (curSpecies) { + for (const {learnset, species: curSpecies} of this.dex.species.getFullLearnset(species.id)) { const eggPokemon = curSpecies.prevo ? curSpecies.id : ''; - learnset = this.dex.species.getLearnset(curSpecies.id); - if (learnset && learnset[move]) { + if (learnset[move]) { for (const moveSource of learnset[move]) { if (eggGen > 8 && parseInt(moveSource.charAt(0)) <= 8) continue; if (parseInt(moveSource.charAt(0)) > eggGen) continue; @@ -1477,7 +1474,6 @@ export class TeamValidator { } } if (eggSources.sourcesBefore === eggGen) break; - curSpecies = this.learnsetParent(curSpecies); } if (eggSources.sourcesBefore === eggGen) continue; @@ -2248,7 +2244,7 @@ export class TeamValidator { if (dex.gen < 8 || this.format.mod === 'gen8dlc1') return null; if (!pokemonGoData) { // Handles forms and evolutions not obtainable from Pokemon GO - const otherSpecies = this.learnsetParent(species); + const otherSpecies = this.dex.species.learnsetParent(species); // If a Pokemon is somehow not obtainable from Pokemon GO and it must be leveled up to be evolved, // validation for the game should stop because it's more optimal to get the Pokemon outside of the game if (otherSpecies && !species.evoLevel) { @@ -2355,8 +2351,8 @@ export class TeamValidator { /** Returns null if you can learn the move, or a string explaining why you can't learn it */ checkCanLearn( move: Move, - s: Species, - setSources = this.allSources(s), + originalSpecies: Species, + setSources = this.allSources(originalSpecies), set: Partial = {} ): string | null { const dex = this.dex; @@ -2364,12 +2360,10 @@ export class TeamValidator { move = dex.moves.get(move); const moveid = move.id; - const baseSpecies = dex.species.get(s); - let species: Species | null = baseSpecies; + const baseSpecies = dex.species.get(originalSpecies); const format = this.format; const ruleTable = dex.formats.getRuleTable(format); - const alreadyChecked: {[k: string]: boolean} = {}; const level = set.level || 100; let cantLearnReason = null; @@ -2401,40 +2395,25 @@ export class TeamValidator { const canSketchPostGen7Moves = ruleTable.has('sketchpostgen7moves') || this.dex.currentMod === 'gen8bdsp'; let tradebackEligible = false; - while (species?.name && !alreadyChecked[species.id]) { - alreadyChecked[species.id] = true; - if (dex.gen <= 2 && species.gen === 1) tradebackEligible = true; - let learnset = dex.species.getLearnset(species.id); - if (!learnset) { - if ((species.changesFrom || species.baseSpecies) !== species.name) { - // forme without its own learnset - species = dex.species.get(species.changesFrom || species.baseSpecies); - // warning: formes with their own learnset, like Wormadam, should NOT - // inherit from their base forme unless they're freely switchable - continue; - } - if (species.isNonstandard) { - // It's normal for a nonstandard species not to have learnset data + const fullLearnset = dex.species.getFullLearnset(originalSpecies.id); + if (!fullLearnset.length) { + // It's normal for a nonstandard species not to have learnset data - // Formats should replace the `Obtainable Moves` rule if they want to - // allow pokemon without learnsets. - return ` can't learn any moves at all.`; - } - if (species.prevo && dex.species.getLearnset(toID(species.prevo))) { - learnset = dex.species.getLearnset(toID(species.prevo)); - continue; - } - // should never happen - throw new Error(`Species with no learnset data: ${species.id}`); - } - const checkingPrevo = species.baseSpecies !== s.baseSpecies; + // Formats should replace the `Obtainable Moves` rule if they want to + // allow pokemon without learnsets. + return ` can't learn any moves at all.`; + } + + for (const {species, learnset} of fullLearnset) { + if (dex.gen <= 2 && species.gen === 1) tradebackEligible = true; + const checkingPrevo = species.baseSpecies !== originalSpecies.baseSpecies; if (checkingPrevo && !moveSources.size()) { if (!setSources.babyOnly || !species.prevo) { babyOnly = species.id; } } - let sources = learnset[moveid]; + let sources = learnset[moveid] || []; if (moveid === 'sketch') { sketch = true; } else if (learnset['sketch']) { @@ -2443,185 +2422,182 @@ export class TeamValidator { } else if (move.gen > 7 && !canSketchPostGen7Moves) { cantLearnReason = `can't be Sketched because it's a Gen ${move.gen} move and Sketch isn't available in Gen ${move.gen}.`; } else { - if (!sources || !moveSources.size()) sketch = true; - sources = learnset['sketch'].concat(sources || []); - } - } - - if (typeof sources === 'string') sources = [sources]; - if (sources) { - for (let learned of sources) { - // Every `learned` represents a single way a pokemon might - // learn a move. This can be handled one of several ways: - // `continue` - // means we can't learn it - // `return null` - // means we can learn it with no restrictions - // (there's a way to just teach any pokemon of this species - // the move in the current gen, like a TM.) - // `moveSources.add(source)` - // means we can learn it only if obtained that exact way described - // in source - // `moveSources.addGen(learnedGen)` - // means we can learn it only if obtained at or before learnedGen - // (i.e. get the pokemon however you want, transfer to that gen, - // teach it, and transfer it to the current gen.) - - const learnedGen = parseInt(learned.charAt(0)); - if (learnedGen < this.minSourceGen) { - if (!cantLearnReason) { - cantLearnReason = `can't be transferred from Gen ${learnedGen} to ${this.minSourceGen}.`; - } - continue; + if (!sources.length || !moveSources.size()) sketch = true; + sources = [...learnset['sketch'], ...sources]; + } + } + + for (let learned of sources) { + // Every `learned` represents a single way a pokemon might + // learn a move. This can be handled one of several ways: + // `continue` + // means we can't learn it + // `return null` + // means we can learn it with no restrictions + // (there's a way to just teach any pokemon of this species + // the move in the current gen, like a TM.) + // `moveSources.add(source)` + // means we can learn it only if obtained that exact way described + // in source + // `moveSources.addGen(learnedGen)` + // means we can learn it only if obtained at or before learnedGen + // (i.e. get the pokemon however you want, transfer to that gen, + // teach it, and transfer it to the current gen.) + + const learnedGen = parseInt(learned.charAt(0)); + if (learnedGen < this.minSourceGen) { + if (!cantLearnReason) { + cantLearnReason = `can't be transferred from Gen ${learnedGen} to ${this.minSourceGen}.`; } - if (noFutureGen && learnedGen > dex.gen) { - if (!cantLearnReason) { - cantLearnReason = `can't be transferred from Gen ${learnedGen} to ${dex.gen}.`; - } - continue; + continue; + } + if (noFutureGen && learnedGen > dex.gen) { + if (!cantLearnReason) { + cantLearnReason = `can't be transferred from Gen ${learnedGen} to ${dex.gen}.`; } + continue; + } + + // redundant + if (learnedGen <= moveSources.sourcesBefore) continue; + + if ( + baseSpecies.evoRegion === 'Alola' && checkingPrevo && learnedGen >= 8 && + (dex.gen < 9 || learned.charAt(1) !== 'E') + ) { + cantLearnReason = `is from a ${species.name} that can't be transferred to USUM to evolve into ${baseSpecies.name}.`; + continue; + } + + const canUseAbilityPatch = dex.gen >= 8 && format.mod !== 'gen8dlc1'; + if ( + learnedGen < 7 && setSources.isHidden && !canUseAbilityPatch && + !dex.mod('gen' + learnedGen).species.get(baseSpecies.name).abilities['H'] + ) { + cantLearnReason = `can only be learned in gens without Hidden Abilities.`; + continue; + } - // redundant - if (learnedGen <= moveSources.sourcesBefore) continue; + const ability = dex.abilities.get(set.ability); + if (dex.gen < 6 && ability.gen > learnedGen && !checkingPrevo) { + // You can evolve a transfered mon to reroll for its new Ability. + cantLearnReason = `is learned in gen ${learnedGen}, but the Ability ${ability.name} did not exist then.`; + continue; + } - if ( - baseSpecies.evoRegion === 'Alola' && checkingPrevo && learnedGen >= 8 && - (dex.gen < 9 || learned.charAt(1) !== 'E') - ) { - cantLearnReason = `is from a ${species.name} that can't be transferred to USUM to evolve into ${baseSpecies.name}.`; + if (!species.isNonstandard) { + // HMs can't be transferred + if (dex.gen >= 4 && learnedGen <= 3 && [ + 'cut', 'fly', 'surf', 'strength', 'flash', 'rocksmash', 'waterfall', 'dive', + ].includes(moveid)) { + cantLearnReason = `can't be transferred from Gen 3 to 4 because it's an HM move.`; continue; } - - const canUseAbilityPatch = dex.gen >= 8 && format.mod !== 'gen8dlc1'; - if ( - learnedGen < 7 && setSources.isHidden && !canUseAbilityPatch && - !dex.mod('gen' + learnedGen).species.get(baseSpecies.name).abilities['H'] - ) { - cantLearnReason = `can only be learned in gens without Hidden Abilities.`; + if (dex.gen >= 5 && learnedGen <= 4 && [ + 'cut', 'fly', 'surf', 'strength', 'rocksmash', 'waterfall', 'rockclimb', + ].includes(moveid)) { + cantLearnReason = `can't be transferred from Gen 4 to 5 because it's an HM move.`; continue; } - - const ability = dex.abilities.get(set.ability); - if (dex.gen < 6 && ability.gen > learnedGen && !checkingPrevo) { - // You can evolve a transfered mon to reroll for its new Ability. - cantLearnReason = `is learned in gen ${learnedGen}, but the Ability ${ability.name} did not exist then.`; + // Defog and Whirlpool can't be transferred together + if (dex.gen >= 5 && ['defog', 'whirlpool'].includes(moveid) && learnedGen <= 4) blockedHM = true; + } + + if (learned.charAt(1) === 'L') { + // special checking for level-up moves + if (level >= parseInt(learned.substr(2)) || learnedGen === 7) { + // we're past the required level to learn it + // (gen 7 level-up moves can be relearnered at any level) + // falls through to LMT check below + } else if (level >= 5 && learnedGen === 3 && species.canHatch) { + // Pomeg Glitch + learned = learnedGen + 'Epomeg'; + } else if ((!species.gender || species.gender === 'F') && + learnedGen >= 2 && species.canHatch && !setSources.isFromPokemonGo) { + // available as egg move + learned = learnedGen + 'Eany'; + // falls through to E check below + } else { + // this move is unavailable, skip it + cantLearnReason = `is learned at level ${parseInt(learned.substr(2))}.`; continue; } + } - if (!species.isNonstandard) { - // HMs can't be transferred - if (dex.gen >= 4 && learnedGen <= 3 && [ - 'cut', 'fly', 'surf', 'strength', 'flash', 'rocksmash', 'waterfall', 'dive', - ].includes(moveid)) { - cantLearnReason = `can't be transferred from Gen 3 to 4 because it's an HM move.`; - continue; - } - if (dex.gen >= 5 && learnedGen <= 4 && [ - 'cut', 'fly', 'surf', 'strength', 'rocksmash', 'waterfall', 'rockclimb', - ].includes(moveid)) { - cantLearnReason = `can't be transferred from Gen 4 to 5 because it's an HM move.`; - continue; + // Gen 8+ egg moves can be taught to any pokemon from any source + if (learnedGen >= 8 && learned.charAt(1) === 'E' && learned.slice(1) !== 'Eany' && + learned.slice(1) !== 'Epomeg' || 'LMTR'.includes(learned.charAt(1))) { + if (learnedGen === dex.gen && learned.charAt(1) !== 'R') { + // current-gen level-up, TM or tutor moves: + // always available + if (!(learnedGen >= 8 && learned.charAt(1) === 'E') && babyOnly) { + if (setSources.isFromPokemonGo && species.evoLevel) { + cantLearnReason = `is from a prevo, which is incompatible with its Pokemon GO origin.`; + continue; + } else { + setSources.babyOnly = babyOnly; + } } - // Defog and Whirlpool can't be transferred together - if (dex.gen >= 5 && ['defog', 'whirlpool'].includes(moveid) && learnedGen <= 4) blockedHM = true; + if (!moveSources.moveEvoCarryCount) return null; } - - if (learned.charAt(1) === 'L') { - // special checking for level-up moves - if (level >= parseInt(learned.substr(2)) || learnedGen === 7) { - // we're past the required level to learn it - // (gen 7 level-up moves can be relearnered at any level) - // falls through to LMT check below - } else if (level >= 5 && learnedGen === 3 && species.canHatch) { - // Pomeg Glitch - learned = learnedGen + 'Epomeg'; - } else if ((!species.gender || species.gender === 'F') && - learnedGen >= 2 && species.canHatch && !setSources.isFromPokemonGo) { - // available as egg move - learned = learnedGen + 'Eany'; - // falls through to E check below - } else { - // this move is unavailable, skip it - cantLearnReason = `is learned at level ${parseInt(learned.substr(2))}.`; - continue; - } + // past-gen level-up, TM, or tutor moves: + // available as long as the source gen was or was before this gen + if (learned.charAt(1) === 'R') { + moveSources.restrictedMove = moveid; } - - // Gen 8+ egg moves can be taught to any pokemon from any source - if (learnedGen >= 8 && learned.charAt(1) === 'E' && learned.slice(1) !== 'Eany' && - learned.slice(1) !== 'Epomeg' || 'LMTR'.includes(learned.charAt(1))) { - if (learnedGen === dex.gen && learned.charAt(1) !== 'R') { - // current-gen level-up, TM or tutor moves: - // always available - if (!(learnedGen >= 8 && learned.charAt(1) === 'E') && babyOnly) { - if (setSources.isFromPokemonGo && species.evoLevel) { - cantLearnReason = `is from a prevo, which is incompatible with its Pokemon GO origin.`; - continue; - } else { - setSources.babyOnly = babyOnly; - } - } - if (!moveSources.moveEvoCarryCount) return null; - } - // past-gen level-up, TM, or tutor moves: - // available as long as the source gen was or was before this gen - if (learned.charAt(1) === 'R') { - moveSources.restrictedMove = moveid; - } - limit1 = false; - moveSources.addGen(learnedGen); - } else if (learned.charAt(1) === 'E') { - // egg moves: - // only if hatched from an egg - let limitedEggMove: ID | null | undefined = undefined; - if (learned.slice(1) === 'Eany') { - if (species.gender === 'F') { - limitedEggMove = move.id; - moveSources.levelUpEggMoves = [move.id]; - } else { - limitedEggMove = null; - } - } else if (learned.slice(1) === 'Epomeg') { - // Pomeg glitched moves have to be from an egg but since they aren't true egg moves, - // there should be no breeding restrictions - moveSources.pomegEggMoves = [move.id]; - } else if (learnedGen < 6) { + limit1 = false; + moveSources.addGen(learnedGen); + } else if (learned.charAt(1) === 'E') { + // egg moves: + // only if hatched from an egg + let limitedEggMove: ID | null | undefined = undefined; + if (learned.slice(1) === 'Eany') { + if (species.gender === 'F') { limitedEggMove = move.id; + moveSources.levelUpEggMoves = [move.id]; + } else { + limitedEggMove = null; } - learned = learnedGen + 'E' + (species.prevo ? species.id : ''); - if (tradebackEligible && learnedGen === 2 && move.gen <= 1) { - // can tradeback - moveSources.add('1ET' + learned.slice(2), limitedEggMove); - } - moveSources.add(learned, limitedEggMove); - } else if (learned.charAt(1) === 'S') { - // event moves: - // only if that was the source - // Event Pokémon: - // Available as long as the past gen can get the Pokémon and then trade it back. - if (tradebackEligible && learnedGen === 2 && move.gen <= 1) { - // can tradeback - moveSources.add('1ST' + learned.slice(2) + ' ' + species.id); - } - moveSources.add(learned + ' ' + species.id); - const eventLearnset = dex.species.getLearnsetData(species.id); - if (eventLearnset.eventData?.[parseInt(learned.charAt(2))].emeraldEventEgg && learnedGen === 3) { - moveSources.pomegEventEgg = learned + ' ' + species.id; - } - } else if (learned.charAt(1) === 'D') { - // DW moves: - // only if that was the source - moveSources.add(learned + species.id); - moveSources.dreamWorldMoveCount++; - } else if (learned.charAt(1) === 'V' && this.minSourceGen < learnedGen) { - // Virtual Console or Let's Go transfer moves: - // only if that was the source - if (learned === '8V' && setSources.isFromPokemonGo && babyOnly && species.evoLevel) { - cantLearnReason = `is from a prevo, which is incompatible with its Pokemon GO origin.`; - continue; - } - moveSources.add(learned); + } else if (learned.slice(1) === 'Epomeg') { + // Pomeg glitched moves have to be from an egg but since they aren't true egg moves, + // there should be no breeding restrictions + moveSources.pomegEggMoves = [move.id]; + } else if (learnedGen < 6) { + limitedEggMove = move.id; + } + learned = learnedGen + 'E' + (species.prevo ? species.id : ''); + if (tradebackEligible && learnedGen === 2 && move.gen <= 1) { + // can tradeback + moveSources.add('1ET' + learned.slice(2), limitedEggMove); + } + moveSources.add(learned, limitedEggMove); + } else if (learned.charAt(1) === 'S') { + // event moves: + // only if that was the source + // Event Pokémon: + // Available as long as the past gen can get the Pokémon and then trade it back. + if (tradebackEligible && learnedGen === 2 && move.gen <= 1) { + // can tradeback + moveSources.add('1ST' + learned.slice(2) + ' ' + species.id); + } + moveSources.add(learned + ' ' + species.id); + const eventLearnset = dex.species.getLearnsetData(species.id); + if (eventLearnset.eventData?.[parseInt(learned.charAt(2))].emeraldEventEgg && learnedGen === 3) { + moveSources.pomegEventEgg = learned + ' ' + species.id; } + } else if (learned.charAt(1) === 'D') { + // DW moves: + // only if that was the source + moveSources.add(learned + species.id); + moveSources.dreamWorldMoveCount++; + } else if (learned.charAt(1) === 'V' && this.minSourceGen < learnedGen) { + // Virtual Console or Let's Go transfer moves: + // only if that was the source + if (learned === '8V' && setSources.isFromPokemonGo && babyOnly && species.evoLevel) { + cantLearnReason = `is from a prevo, which is incompatible with its Pokemon GO origin.`; + continue; + } + moveSources.add(learned); } } if (ruleTable.has('mimicglitch') && species.gen < 5) { @@ -2652,9 +2628,6 @@ export class TeamValidator { moveSources.moveEvoCarryCount = 1; } } - - // also check to see if the mon's prevo or freely switchable formes can learn this move - species = this.learnsetParent(species); } if (limit1 && sketch) { @@ -2676,7 +2649,7 @@ export class TeamValidator { } setSources.restrictiveMoves.push(move.name); - const checkedSpecies = babyOnly ? species : baseSpecies; + const checkedSpecies = babyOnly ? fullLearnset[fullLearnset.length - 1].species : baseSpecies; if (checkedSpecies && setSources.isFromPokemonGo && (setSources.pokemonGoSource === 'purified' || checkedSpecies.id === 'mew')) { // Pokemon that cannot be sent from Pokemon GO to Let's Go can only access Let's Go moves through HOME @@ -2734,31 +2707,6 @@ export class TeamValidator { return null; } - learnsetParent(species: Species) { - // Own Tempo Rockruff and Battle Bond Greninja are special event formes - // that are visually indistinguishable from their base forme but have - // different learnsets. To prevent a leak, we make them show up as their - // base forme, but hardcode their learnsets into Rockruff-Dusk and - // Greninja-Ash - if (['Gastrodon', 'Pumpkaboo', 'Sinistea', 'Tatsugiri'].includes(species.baseSpecies) && species.forme) { - return this.dex.species.get(species.baseSpecies); - } else if (species.name === 'Lycanroc-Dusk') { - return this.dex.species.get('Rockruff-Dusk'); - } else if (species.name === 'Greninja-Bond') { - return null; - } else if (species.prevo) { - // there used to be a check for Hidden Ability here, but apparently it's unnecessary - // Shed Skin Pupitar can definitely evolve into Unnerve Tyranitar - species = this.dex.species.get(species.prevo); - if (species.gen > Math.max(2, this.dex.gen)) return null; - return species; - } else if (species.changesFrom && species.baseSpecies !== 'Kyurem') { - // For Pokemon like Rotom and Necrozma whose movesets are extensions are their base formes - return this.dex.species.get(species.changesFrom); - } - return null; - } - static fillStats(stats: SparseStatsTable | null, fillNum = 0): StatsTable { const filledStats: StatsTable = {hp: fillNum, atk: fillNum, def: fillNum, spa: fillNum, spd: fillNum, spe: fillNum}; if (stats) { diff --git a/sim/test/sim/dex.js b/sim/test/sim/dex.js index adb606fb..3c1f3229 100644 --- a/sim/test/sim/dex.js +++ b/sim/test/sim/dex.js @@ -6,14 +6,14 @@ describe('Mod loader', function () { it('should work fine in any order', function () { { const Dex = require('./../../build/cjs/sim/dex').Dex; - assert.equal(Dex.mod('gen2').species.getLearnset('nidoking').bubblebeam.join(','), '1M'); + assert.equal(Dex.mod('gen2').species.getLearnsetData('nidoking').learnset.bubblebeam.join(','), '1M'); assert.equal(Dex.mod('gen2').moves.get('crunch').secondaries[0].boosts.def, undefined); } { const Dex = require('./../../build/cjs/sim/dex').Dex; - Dex.mod('gen2').species.getLearnset('nidoking'); + Dex.mod('gen2').species.getLearnsetData('nidoking'); Dex.mod('gen4').moves.get('crunch'); - assert.equal(Dex.mod('gen2').species.getLearnset('nidoking').bubblebeam.join(','), '1M'); + assert.equal(Dex.mod('gen2').species.getLearnsetData('nidoking').learnset.bubblebeam.join(','), '1M'); assert.equal(Dex.mod('gen2').moves.get('crunch').secondaries[0].boosts.def, undefined); } }); diff --git a/vendor/pokemon-showdown b/vendor/pokemon-showdown index 603e3cd8..58f2588b 160000 --- a/vendor/pokemon-showdown +++ b/vendor/pokemon-showdown @@ -1 +1 @@ -Subproject commit 603e3cd82fdfe84d212deced846c29c90e430d2b +Subproject commit 58f2588be13755943c301f9e718aa98e6e082850 diff --git a/vendor/pokemon-showdown-client b/vendor/pokemon-showdown-client index df11be96..b6a0f088 160000 --- a/vendor/pokemon-showdown-client +++ b/vendor/pokemon-showdown-client @@ -1 +1 @@ -Subproject commit df11be96d230f12ffc5af71fa3fd9680e6c60eb0 +Subproject commit b6a0f088f4c5414bd4b60d88dc91220407866802