From 28bf388b98cd5cc83a696a09befd7ece89a9e5e1 Mon Sep 17 00:00:00 2001 From: Kris Johnson <11083252+KrisXV@users.noreply.github.com> Date: Thu, 23 Apr 2020 10:16:09 -0600 Subject: [PATCH] Typescript data (#6553) --- .eslintrc-no-types.json | 17 +- .eslintrc-types.json | 4 +- .lib-dist/README.md | 3 - .server-dist/README.md | 3 - .sim-dist/README.md | 3 - build | 36 +- data/{abilities.js => abilities.ts} | 357 +- data/{aliases.js => aliases.ts} | 7 +- data/{formats-data.js => formats-data.ts} | 7 +- data/{items.js => items.ts} | 186 +- data/{learnsets.js => learnsets.ts} | 7 +- .../gen1/{formats-data.js => formats-data.ts} | 7 +- data/mods/gen1/{moves.js => moves.ts} | 55 +- data/mods/gen1/{pokedex.js => pokedex.ts} | 695 ++-- .../gen1/{random-teams.js => random-teams.ts} | 121 +- data/mods/gen1/{rulesets.js => rulesets.ts} | 15 +- data/mods/gen1/{scripts.js => scripts.ts} | 142 +- data/mods/gen1/{statuses.js => statuses.ts} | 17 +- data/mods/gen1/{typechart.js => typechart.ts} | 7 +- .../gen2/{formats-data.js => formats-data.ts} | 7 +- data/mods/gen2/{items.js => items.ts} | 11 +- data/mods/gen2/{learnsets.js => learnsets.ts} | 7 +- data/mods/gen2/{moves.js => moves.ts} | 86 +- .../gen2/{random-teams.js => random-teams.ts} | 122 +- data/mods/gen2/{rulesets.js => rulesets.ts} | 7 +- data/mods/gen2/{scripts.js => scripts.ts} | 120 +- data/mods/gen2/{statuses.js => statuses.ts} | 31 +- data/mods/gen2/{typechart.js => typechart.ts} | 7 +- data/mods/gen3/{abilities.js => abilities.ts} | 15 +- .../gen3/{formats-data.js => formats-data.ts} | 7 +- data/mods/gen3/{items.js => items.ts} | 11 +- data/mods/gen3/{moves.js => moves.ts} | 77 +- .../gen3/{random-teams.js => random-teams.ts} | 136 +- data/mods/gen3/{rulesets.js => rulesets.ts} | 8 +- data/mods/gen3/{scripts.js => scripts.ts} | 42 +- data/mods/gen3/{statuses.js => statuses.ts} | 12 +- data/mods/gen4/{abilities.js => abilities.ts} | 38 +- .../gen4/{formats-data.js => formats-data.ts} | 7 +- data/mods/gen4/{items.js => items.ts} | 29 +- data/mods/gen4/{moves.js => moves.ts} | 155 +- data/mods/gen4/{pokedex.js => pokedex.ts} | 7 +- .../gen4/{random-teams.js => random-teams.ts} | 90 +- data/mods/gen4/rulesets.js | 28 - data/mods/gen4/rulesets.ts | 28 + data/mods/gen4/{scripts.js => scripts.ts} | 16 +- data/mods/gen4/{statuses.js => statuses.ts} | 7 +- data/mods/gen5/{abilities.js => abilities.ts} | 16 +- .../gen5/{formats-data.js => formats-data.ts} | 7 +- data/mods/gen5/{items.js => items.ts} | 7 +- data/mods/gen5/{moves.js => moves.ts} | 51 +- data/mods/gen5/{pokedex.js => pokedex.ts} | 7 +- .../gen5/{random-teams.js => random-teams.ts} | 153 +- data/mods/gen5/rulesets.js | 19 - data/mods/gen5/rulesets.ts | 16 + data/mods/gen5/scripts.js | 9 - data/mods/gen5/scripts.ts | 4 + data/mods/gen5/{statuses.js => statuses.ts} | 14 +- data/mods/gen5/{typechart.js => typechart.ts} | 7 +- data/mods/gen6/{abilities.js => abilities.ts} | 12 +- .../gen6/{formats-data.js => formats-data.ts} | 7 +- data/mods/gen6/{items.js => items.ts} | 5 +- data/mods/gen6/{learnsets.js => learnsets.ts} | 7 +- data/mods/gen6/{moves.js => moves.ts} | 51 +- data/mods/gen6/{pokedex.js => pokedex.ts} | 7 +- .../gen6/{random-teams.js => random-teams.ts} | 253 +- data/mods/gen6/scripts.js | 9 - data/mods/gen6/scripts.ts | 4 + data/mods/gen6/{statuses.js => statuses.ts} | 13 +- data/mods/gen6/{typechart.js => typechart.ts} | 7 +- data/mods/gen7/{abilities.js => abilities.ts} | 27 +- .../gen7/{formats-data.js => formats-data.ts} | 7 +- data/mods/gen7/{items.js => items.ts} | 5 +- data/mods/gen7/{moves.js => moves.ts} | 29 +- data/mods/gen7/{pokedex.js => pokedex.ts} | 11 +- .../gen7/{random-teams.js => random-teams.ts} | 738 ++-- data/mods/gen7/{rulesets.js => rulesets.ts} | 7 +- data/mods/gen7/scripts.js | 8 - data/mods/gen7/scripts.ts | 3 + .../gennext/{abilities.js => abilities.ts} | 19 +- .../{formats-data.js => formats-data.ts} | 37 +- data/mods/gennext/{items.js => items.ts} | 23 +- data/mods/gennext/{moves.js => moves.ts} | 84 +- data/mods/gennext/{pokedex.js => pokedex.ts} | 7 +- data/mods/gennext/{scripts.js => scripts.ts} | 10 +- .../mods/gennext/{statuses.js => statuses.ts} | 104 +- .../{formats-data.js => formats-data.ts} | 7 +- .../letsgo/{learnsets.js => learnsets.ts} | 7 +- data/mods/letsgo/{moves.js => moves.ts} | 16 +- data/mods/letsgo/{pokedex.js => pokedex.ts} | 7 +- .../{random-teams.js => random-teams.ts} | 90 +- data/mods/letsgo/{rulesets.js => rulesets.ts} | 7 +- data/mods/letsgo/{scripts.js => scripts.ts} | 36 +- data/mods/mixandmega/{items.js => items.ts} | 7 +- .../mixandmega/{scripts.js => scripts.ts} | 54 +- .../{formats-data.js => formats-data.ts} | 7 +- data/mods/roulettemons/{items.js => items.ts} | 7 +- data/mods/roulettemons/learnsets.js | 2964 ---------------- data/mods/roulettemons/learnsets.ts | 2997 +++++++++++++++++ .../roulettemons/{pokedex.js => pokedex.ts} | 7 +- data/mods/ssb/{abilities.js => abilities.ts} | 111 +- data/mods/ssb/{items.js => items.ts} | 5 +- data/mods/ssb/{moves.js => moves.ts} | 361 +- data/mods/ssb/{pokedex.js => pokedex.ts} | 7 +- .../ssb/{random-teams.js => random-teams.ts} | 114 +- data/mods/ssb/{scripts.js => scripts.ts} | 104 +- data/mods/ssb/{statuses.js => statuses.ts} | 129 +- data/mods/stadium/{moves.js => moves.ts} | 17 +- .../mods/stadium/{rulesets.js => rulesets.ts} | 7 +- data/mods/stadium/{scripts.js => scripts.ts} | 111 +- .../mods/stadium/{statuses.js => statuses.ts} | 7 +- .../{formats-data.js => formats-data.ts} | 7 +- data/mods/vgc17/{items.js => items.ts} | 8 +- .../mods/vgc17/{learnsets.js => learnsets.ts} | 7 +- data/mods/vgc17/{rulesets.js => rulesets.ts} | 17 +- data/mods/vgc17/scripts.js | 8 - data/mods/vgc17/scripts.ts | 3 + data/{moves.js => moves.ts} | 618 ++-- data/{pokedex.js => pokedex.ts} | 7 +- data/{random-teams.js => random-teams.ts} | 464 ++- data/{rulesets.js => rulesets.ts} | 257 +- data/{scripts.js => scripts.ts} | 254 +- data/{statuses.js => statuses.ts} | 54 +- data/{typechart.js => typechart.ts} | 7 +- sim/battle-queue.ts | 4 +- sim/battle.ts | 2 + sim/dex.ts | 12 +- sim/global-types.ts | 4 +- sim/team-validator.ts | 2 +- test/sim/misc/random-teams.js | 4 +- 129 files changed, 6599 insertions(+), 6930 deletions(-) delete mode 100644 .lib-dist/README.md delete mode 100644 .server-dist/README.md delete mode 100644 .sim-dist/README.md rename data/{abilities.js => abilities.ts} (93%) rename data/{aliases.js => aliases.ts} (99%) rename data/{formats-data.js => formats-data.ts} (99%) rename data/{items.js => items.ts} (97%) rename data/{learnsets.js => learnsets.ts} (99%) mode change 100755 => 100644 rename data/mods/gen1/{formats-data.js => formats-data.ts} (99%) rename data/mods/gen1/{moves.js => moves.ts} (96%) rename data/mods/gen1/{pokedex.js => pokedex.ts} (82%) rename data/mods/gen1/{random-teams.js => random-teams.ts} (78%) rename data/mods/gen1/{rulesets.js => rulesets.ts} (72%) rename data/mods/gen1/{scripts.js => scripts.ts} (88%) rename data/mods/gen1/{statuses.js => statuses.ts} (92%) rename data/mods/gen1/{typechart.js => typechart.ts} (92%) rename data/mods/gen2/{formats-data.js => formats-data.ts} (99%) rename data/mods/gen2/{items.js => items.ts} (72%) rename data/mods/gen2/{learnsets.js => learnsets.ts} (99%) rename data/mods/gen2/{moves.js => moves.ts} (94%) rename data/mods/gen2/{random-teams.js => random-teams.ts} (75%) rename data/mods/gen2/{rulesets.js => rulesets.ts} (91%) rename data/mods/gen2/{scripts.js => scripts.ts} (87%) rename data/mods/gen2/{statuses.js => statuses.ts} (89%) rename data/mods/gen2/{typechart.js => typechart.ts} (87%) rename data/mods/gen3/{abilities.js => abilities.ts} (95%) rename data/mods/gen3/{formats-data.js => formats-data.ts} (99%) rename data/mods/gen3/{items.js => items.ts} (81%) rename data/mods/gen3/{moves.js => moves.ts} (95%) rename data/mods/gen3/{random-teams.js => random-teams.ts} (89%) rename data/mods/gen3/{rulesets.js => rulesets.ts} (75%) rename data/mods/gen3/{scripts.js => scripts.ts} (91%) rename data/mods/gen3/{statuses.js => statuses.ts} (80%) rename data/mods/gen4/{abilities.js => abilities.ts} (95%) rename data/mods/gen4/{formats-data.js => formats-data.ts} (99%) rename data/mods/gen4/{items.js => items.ts} (53%) rename data/mods/gen4/{moves.js => moves.ts} (95%) rename data/mods/gen4/{pokedex.js => pokedex.ts} (77%) rename data/mods/gen4/{random-teams.js => random-teams.ts} (92%) delete mode 100644 data/mods/gen4/rulesets.js create mode 100644 data/mods/gen4/rulesets.ts rename data/mods/gen4/{scripts.js => scripts.ts} (90%) rename data/mods/gen4/{statuses.js => statuses.ts} (92%) rename data/mods/gen5/{abilities.js => abilities.ts} (87%) rename data/mods/gen5/{formats-data.js => formats-data.ts} (99%) rename data/mods/gen5/{items.js => items.ts} (98%) rename data/mods/gen5/{moves.js => moves.ts} (97%) rename data/mods/gen5/{pokedex.js => pokedex.ts} (98%) rename data/mods/gen5/{random-teams.js => random-teams.ts} (88%) delete mode 100644 data/mods/gen5/rulesets.js create mode 100644 data/mods/gen5/rulesets.ts delete mode 100644 data/mods/gen5/scripts.js create mode 100644 data/mods/gen5/scripts.ts rename data/mods/gen5/{statuses.js => statuses.ts} (76%) rename data/mods/gen5/{typechart.js => typechart.ts} (89%) rename data/mods/gen6/{abilities.js => abilities.ts} (96%) rename data/mods/gen6/{formats-data.js => formats-data.ts} (99%) rename data/mods/gen6/{items.js => items.ts} (98%) rename data/mods/gen6/{learnsets.js => learnsets.ts} (98%) rename data/mods/gen6/{moves.js => moves.ts} (95%) rename data/mods/gen6/{pokedex.js => pokedex.ts} (98%) rename data/mods/gen6/{random-teams.js => random-teams.ts} (86%) delete mode 100644 data/mods/gen6/scripts.js create mode 100644 data/mods/gen6/scripts.ts rename data/mods/gen6/{statuses.js => statuses.ts} (74%) rename data/mods/gen6/{typechart.js => typechart.ts} (69%) rename data/mods/gen7/{abilities.js => abilities.ts} (89%) rename data/mods/gen7/{formats-data.js => formats-data.ts} (99%) rename data/mods/gen7/{items.js => items.ts} (99%) mode change 100755 => 100644 rename data/mods/gen7/{moves.js => moves.ts} (95%) rename data/mods/gen7/{pokedex.js => pokedex.ts} (89%) rename data/mods/gen7/{random-teams.js => random-teams.ts} (72%) rename data/mods/gen7/{rulesets.js => rulesets.ts} (90%) delete mode 100644 data/mods/gen7/scripts.js create mode 100644 data/mods/gen7/scripts.ts rename data/mods/gennext/{abilities.js => abilities.ts} (98%) rename data/mods/gennext/{formats-data.js => formats-data.ts} (60%) rename data/mods/gennext/{items.js => items.ts} (87%) rename data/mods/gennext/{moves.js => moves.ts} (97%) rename data/mods/gennext/{pokedex.js => pokedex.ts} (68%) rename data/mods/gennext/{scripts.js => scripts.ts} (97%) rename data/mods/gennext/{statuses.js => statuses.ts} (75%) rename data/mods/letsgo/{formats-data.js => formats-data.ts} (99%) rename data/mods/letsgo/{learnsets.js => learnsets.ts} (99%) rename data/mods/letsgo/{moves.js => moves.ts} (85%) rename data/mods/letsgo/{pokedex.js => pokedex.ts} (94%) rename data/mods/letsgo/{random-teams.js => random-teams.ts} (78%) rename data/mods/letsgo/{rulesets.js => rulesets.ts} (65%) rename data/mods/letsgo/{scripts.js => scripts.ts} (63%) rename data/mods/mixandmega/{items.js => items.ts} (96%) rename data/mods/mixandmega/{scripts.js => scripts.ts} (68%) rename data/mods/roulettemons/{formats-data.js => formats-data.ts} (94%) rename data/mods/roulettemons/{items.js => items.ts} (78%) delete mode 100644 data/mods/roulettemons/learnsets.js create mode 100644 data/mods/roulettemons/learnsets.ts rename data/mods/roulettemons/{pokedex.js => pokedex.ts} (98%) rename data/mods/ssb/{abilities.js => abilities.ts} (93%) rename data/mods/ssb/{items.js => items.ts} (98%) rename data/mods/ssb/{moves.js => moves.ts} (89%) rename data/mods/ssb/{pokedex.js => pokedex.ts} (96%) rename data/mods/ssb/{random-teams.js => random-teams.ts} (95%) rename data/mods/ssb/{scripts.js => scripts.ts} (82%) rename data/mods/ssb/{statuses.js => statuses.ts} (93%) rename data/mods/stadium/{moves.js => moves.ts} (90%) rename data/mods/stadium/{rulesets.js => rulesets.ts} (64%) rename data/mods/stadium/{scripts.js => scripts.ts} (88%) rename data/mods/stadium/{statuses.js => statuses.ts} (96%) rename data/mods/vgc17/{formats-data.js => formats-data.ts} (92%) rename data/mods/vgc17/{items.js => items.ts} (78%) rename data/mods/vgc17/{learnsets.js => learnsets.ts} (99%) rename data/mods/vgc17/{rulesets.js => rulesets.ts} (92%) delete mode 100644 data/mods/vgc17/scripts.js create mode 100644 data/mods/vgc17/scripts.ts rename data/{moves.js => moves.ts} (97%) rename data/{pokedex.js => pokedex.ts} (99%) rename data/{random-teams.js => random-teams.ts} (83%) rename data/{rulesets.js => rulesets.ts} (85%) rename data/{scripts.js => scripts.ts} (86%) rename data/{statuses.js => statuses.ts} (94%) rename data/{typechart.js => typechart.ts} (98%) diff --git a/.eslintrc-no-types.json b/.eslintrc-no-types.json index 249820e4b9..caaa5ffd73 100644 --- a/.eslintrc-no-types.json +++ b/.eslintrc-no-types.json @@ -188,7 +188,7 @@ }, "overrides": [ { - "files": ["./lib/*.ts", "./server/**/*.ts", "./sim/**/*.ts", "./tools/set-import/*.ts"], + "files": ["./data/**/*.ts", "./lib/*.ts", "./server/**/*.ts", "./sim/**/*.ts", "./tools/set-import/*.ts"], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": 9, @@ -217,7 +217,7 @@ "max-len": ["warn", { "code": 120, "tabWidth": 0, // see bottom of file for source - "ignorePattern": "^\\s*(?:\\/\\/ \\s*)?(?:(?:export )?(?:let |const )?[a-zA-Z0-9$.]+(?: \\+?=>? |: )|(?:return |throw )?(?:new )?(?:[a-zA-Z0-9$.]+\\()?)?(?:Chat\\.html|\\$\\()?['\"`/]" + "ignorePattern": "^\\s*(?:\\/\\/ \\s*)?(?:(?:export )?(?:let |const )?[a-zA-Z0-9$.]+(?: \\+?=>? )|[a-zA-Z0-9$]+: \\[?|(?:return |throw )?(?:new )?(?:[a-zA-Z0-9$.]+\\()?)?(?:Chat\\.html|\\$\\()?['\"`/]" }], "prefer-const": ["warn", {"destructuring": "all"}], @@ -301,14 +301,19 @@ } /* -REGEXFREE SOURCE FOR IGNOREPATTERN +REGEXFREE SOURCE FOR IGNOREPATTERN: https://zarel.github.io/regexfree/ # indentation - ^\s*(\/\/\ \s*)? + ^\s* + # possibly commented out + (\/\/\ \s*)? ( - # define a variable, append to a variable, or define a single-arg arrow function or property - (export\ )? (let\ |const\ )? [a-zA-Z0-9$.]+ (\ \+?=>?\ |:\ ) + # define a variable, append to a variable, or define a single-arg arrow function + (export\ )? (let\ |const\ )? [a-zA-Z0-9$.]+ (\ \+?=>?\ ) + | + # define a property (oversize arrays are only allowed in properties) + [a-zA-Z0-9$]+:\ \[? | # optionally return or throw (return\ |throw\ )? diff --git a/.eslintrc-types.json b/.eslintrc-types.json index b340cf4764..609a518f1b 100644 --- a/.eslintrc-types.json +++ b/.eslintrc-types.json @@ -7,7 +7,7 @@ }, "overrides": [ { - "files": ["./lib/*.ts", "./server/**/*.ts", "./sim/**/*.ts", "./tools/set-import/*.ts"], + "files": ["./data/**/*.ts", "./lib/*.ts", "./server/**/*.ts", "./sim/**/*.ts", "./tools/set-import/*.ts"], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": 9, @@ -30,7 +30,7 @@ "max-len": ["error", { "code": 120, "tabWidth": 0, // see bottom of .eslintrc-no-types.json for source - "ignorePattern": "^\\s*(?:\\/\\/ \\s*)?(?:(?:export )?(?:let |const )?[a-zA-Z0-9$.]+(?: \\+?=>? |: )|(?:return |throw )?(?:new )?(?:[a-zA-Z0-9$.]+\\()?)?(?:Chat\\.html|\\$\\()?['\"`/]" + "ignorePattern": "^\\s*(?:\\/\\/ \\s*)?(?:(?:export )?(?:let |const )?[a-zA-Z0-9$.]+(?: \\+?=>? )|[a-zA-Z0-9$]+: \\[?|(?:return |throw )?(?:new )?(?:[a-zA-Z0-9$.]+\\()?)?(?:Chat\\.html|\\$\\()?['\"`/]" }], "no-unused-vars": "off", "@typescript-eslint/no-unused-vars": ["error", {"args": "none"}], diff --git a/.lib-dist/README.md b/.lib-dist/README.md deleted file mode 100644 index edb4a4ecc7..0000000000 --- a/.lib-dist/README.md +++ /dev/null @@ -1,3 +0,0 @@ -**NOTE**: This folder contains the compiled output of the `lib/` directory. -You should be editing the `.ts` files there and then running `npm run build` or -`./pokemon-showdown` to force these `.js` files to be recreated. diff --git a/.server-dist/README.md b/.server-dist/README.md deleted file mode 100644 index 5b12746c2d..0000000000 --- a/.server-dist/README.md +++ /dev/null @@ -1,3 +0,0 @@ -**NOTE**: This folder contains the compiled output of the `server/` directory. -You should be editing the `.ts` files there and then running `npm run build` or -`./pokemon-showdown` to force these `.js` files to be recreated. diff --git a/.sim-dist/README.md b/.sim-dist/README.md deleted file mode 100644 index 9da980889f..0000000000 --- a/.sim-dist/README.md +++ /dev/null @@ -1,3 +0,0 @@ -**NOTE**: This folder contains the compiled output of the `sim/` directory. -You should be editing the `.ts` files there and then running `npm run build` or -`./pokemon-showdown` to force these `.js` files to be recreated. diff --git a/build b/build index fa8ff5e0a8..d5747232d7 100755 --- a/build +++ b/build @@ -47,6 +47,16 @@ function replace(file, replacements) { } }); } +function copyOverData(file) { + var source = './data/' + file; + var dest = './.data-dist/' + file; + fs.readFile(source, function (err, text) { + if (err) throw err; + fs.writeFile(dest, text, function (err) { + if (err) throw err; + }); + }); +} try { var sucraseVersion = require('sucrase').getVersion().split('.'); @@ -61,19 +71,39 @@ try { shell('npm install --production'); } +sucrase('./data', './.data-dist'); sucrase('./sim', './.sim-dist'); sucrase('./lib', './.lib-dist'); sucrase('./server', './.server-dist'); sucrase('./tools/set-import', './tools/set-import', '--exclude-dirs=sets'); + +if (!fs.existsSync('./.data-dist/README.md')) { + const text = '**NOTE**: This folder contains the compiled output of the `data/` directory.\n' + + 'You should be editing the `.ts` files there and then running `npm run build` or\n' + + '`./pokemon-showdown` to force these `.js` files to be recreated.\n'; + fs.writeFile('./.data-dist/README.md', text, function () {}); + fs.writeFile('./.sim-dist/README.md', text.replace('data/', 'sim/'), function () {}); + fs.writeFile('./.server-dist/README.md', text.replace('data/', 'server/'), function () {}); + fs.writeFile('./.lib-dist/README.md', text.replace('data/', 'lib/'), function () {}); +} + +// sucrase doesn't copy JSON over, so we'll have to do it ourselves +copyOverData('mods/gen7/factory-sets.json'); +copyOverData('mods/gen7/bss-factory-sets.json'); +copyOverData('mods/gen6/factory-sets.json'); + // NOTE: replace is asynchronous - add additional replacements for the same path in one call instead of making multiple calls. +replace(path.join(__dirname, '.data-dist'), [ + {regex: /(require\(.*?)(lib|sim)/g, replace: `$1.$2-dist`}, +]); replace(path.join(__dirname, '.sim-dist'), [ - {regex: new RegExp(`(require\\\(.*?)(lib)(.*?\\\))`, 'g'), replace: `$1.lib-dist$3`}, + {regex: /(require\(.*?)(lib)/g, replace: `$1.lib-dist`}, ]); replace(path.join(__dirname, '.server-dist'), [ - {regex: new RegExp(`(require\\\(.*?)(lib|sim)(.*?\\\))`, 'g'), replace: `$1.$2-dist$3`}, + {regex: /(require\(.*?)(lib|sim)/g, replace: `$1.$2-dist`}, ]); replace(path.join(__dirname, './tools/set-import/importer.js'), [ - {regex: new RegExp(`(require\\\(.*?)(lib|sim)(.*?\\\))`, 'g'), replace: `$1.$2-dist$3`}, + {regex: /(require\(.*?)(lib|sim)/g, replace: `$1.$2-dist`}, ]); // Make sure config.js exists. If not, copy it over synchronously from diff --git a/data/abilities.js b/data/abilities.ts similarity index 93% rename from data/abilities.js rename to data/abilities.ts index 2ec1dae6e0..5066104f71 100644 --- a/data/abilities.js +++ b/data/abilities.ts @@ -32,10 +32,7 @@ Ratings and how they work: */ -'use strict'; - -/**@type {{[k: string]: AbilityData}} */ -let BattleAbilities = { +export const BattleAbilities: {[abilityid: string]: AbilityData} = { noability: { shortDesc: "Does nothing.", id: "noability", @@ -60,7 +57,8 @@ let BattleAbilities = { shortDesc: "This Pokemon's Normal-type moves become Flying type and have 1.2x power.", onModifyTypePriority: -1, onModifyType(move, pokemon) { - if (move.type === 'Normal' && !['judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'weatherball'].includes(move.id) && !(move.isZ && move.category !== 'Status')) { + const noModifyType = ['judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'weatherball']; + if (move.type === 'Normal' && !noModifyType.includes(move.id) && !(move.isZ && move.category !== 'Status')) { move.type = 'Flying'; move.aerilateBoosted = true; } @@ -127,7 +125,7 @@ let BattleAbilities = { shortDesc: "If this Pokemon (not its substitute) takes a critical hit, its Attack is raised 12 stages.", onHit(target, source, move) { if (!target.hp) return; - if (move && move.effectType === 'Move' && target.getMoveHitData(move).crit) { + if (move?.effectType === 'Move' && target.getMoveHitData(move).crit) { target.setBoost({atk: 6}); this.add('-setboost', target, 'atk', 12, '[from] ability: Anger Point'); } @@ -145,8 +143,12 @@ let BattleAbilities = { if (!target || target.fainted) continue; for (const moveSlot of target.moveSlots) { const move = this.dex.getMove(moveSlot.move); + if (move.category === 'Status') continue; const moveType = move.id === 'hiddenpower' ? target.hpType : move.type; - if (move.category !== 'Status' && (this.dex.getImmunity(moveType, pokemon) && this.dex.getEffectiveness(moveType, pokemon) > 0 || move.ohko)) { + if ( + this.dex.getImmunity(moveType, pokemon) && this.dex.getEffectiveness(moveType, pokemon) > 0 || + move.ohko + ) { this.add('-ability', pokemon, 'Anticipation'); return; } @@ -263,7 +265,10 @@ let BattleAbilities = { desc: "If this Pokemon is a Greninja, it transforms into Ash-Greninja after knocking out a Pokemon. As Ash-Greninja, its Water Shuriken has 20 base power and always hits 3 times.", shortDesc: "After KOing a Pokemon: becomes Ash-Greninja, Water Shuriken: 20 power, hits 3x.", onSourceFaint(target, source, effect) { - if (effect && effect.effectType === 'Move' && source.species.id === 'greninja' && source.hp && !source.transformed && source.side.foe.pokemonLeft) { + if (effect?.effectType !== 'Move') { + return; + } + if (source.species.id === 'greninja' && source.hp && !source.transformed && source.side.foe.pokemonLeft) { this.add('-activate', source, 'ability: Battle Bond'); source.formeChange('Greninja-Ash', this.effect, true); } @@ -286,8 +291,7 @@ let BattleAbilities = { if (effect && effect.effectType === 'Move') { let statName = 'atk'; let bestStat = 0; - /** @type {StatNameExceptHP} */ - let s; + let s: StatNameExceptHP; for (s in source.storedStats) { if (source.storedStats[s] > bestStat) { statName = s; @@ -325,7 +329,7 @@ let BattleAbilities = { if (source && target === source) return; if (boost.def && boost.def < 0) { delete boost.def; - if (!(/** @type {ActiveMove} */(effect)).secondaries && effect.id !== 'octolock') { + if (!(effect as ActiveMove).secondaries && effect.id !== 'octolock') { this.add("-fail", target, "unboost", "Defense", "[from] ability: Big Pecks", "[of] " + target); } } @@ -400,15 +404,14 @@ let BattleAbilities = { onBoost(boost, target, source, effect) { if (source && target === source) return; let showMsg = false; - for (let i in boost) { - // @ts-ignore - if (boost[i] < 0) { - // @ts-ignore + let i: BoostName; + for (i in boost) { + if (boost[i]! < 0) { delete boost[i]; showMsg = true; } } - if (showMsg && !(/** @type {ActiveMove} */(effect)).secondaries && effect.id !== 'octolock') { + if (showMsg && !(effect as ActiveMove).secondaries && effect.id !== 'octolock') { this.add("-fail", target, "unboost", "[from] ability: Clear Body", "[of] " + target); } }, @@ -433,8 +436,11 @@ let BattleAbilities = { shortDesc: "This Pokemon's type changes to the type of a move it's hit by, unless it has the type.", onAfterMoveSecondary(target, source, move) { if (!target.hp) return; - let type = move.type; - if (target.isActive && move.effectType === 'Move' && move.category !== 'Status' && type !== '???' && !target.hasType(type)) { + const type = move.type; + if ( + target.isActive && move.effectType === 'Move' && move.category !== 'Status' && + type !== '???' && !target.hasType(type) + ) { if (!target.setType(type)) return false; this.add('-start', target, 'typechange', type, '[from] ability: Color Change'); @@ -481,9 +487,9 @@ let BattleAbilities = { return; } let statsLowered = false; - for (let i in boost) { - // @ts-ignore - if (boost[i] < 0) { + let i: BoostName; + for (i in boost) { + if (boost[i]! < 0) { statsLowered = true; } } @@ -514,9 +520,9 @@ let BattleAbilities = { shortDesc: "If this Pokemon has a stat stage raised it is lowered instead, and vice versa.", onBoost(boost, target, source, effect) { if (effect && effect.id === 'zpower') return; - for (let i in boost) { - // @ts-ignore - boost[i] *= -1; + let i: BoostName; + for (i in boost) { + boost[i]! *= -1; } }, id: "contrary", @@ -537,7 +543,7 @@ let BattleAbilities = { shortDesc: "If this Pokemon is hit, it lowers the Speed of all other Pokemon on the field 1 stage.", onDamagingHit(damage, target, source, move) { let activated = false; - for (let pokemon of this.getAllActive()) { + for (const pokemon of this.getAllActive()) { if (pokemon === target || pokemon.fainted) continue; if (!activated) { this.add('-ability', target, 'Cotton Down'); @@ -692,9 +698,9 @@ let BattleAbilities = { return; } let statsLowered = false; - for (let i in boost) { - // @ts-ignore - if (boost[i] < 0) { + let i: BoostName; + for (i in boost) { + if (boost[i]! < 0) { statsLowered = true; } } @@ -715,7 +721,8 @@ let BattleAbilities = { this.field.setWeather('deltastream'); }, onAnySetWeather(target, source, weather) { - if (this.field.getWeather().id === 'deltastream' && !['desolateland', 'primordialsea', 'deltastream'].includes(weather.id)) return false; + const strongWeathers = ['desolateland', 'primordialsea', 'deltastream']; + if (this.field.getWeather().id === 'deltastream' && !strongWeathers.includes(weather.id)) return false; }, onEnd(pokemon) { if (this.field.weatherData.source !== pokemon) return; @@ -740,7 +747,8 @@ let BattleAbilities = { this.field.setWeather('desolateland'); }, onAnySetWeather(target, source, weather) { - if (this.field.getWeather().id === 'desolateland' && !['desolateland', 'primordialsea', 'deltastream'].includes(weather.id)) return false; + const strongWeathers = ['desolateland', 'primordialsea', 'deltastream']; + if (this.field.getWeather().id === 'desolateland' && !strongWeathers.includes(weather.id)) return false; }, onEnd(pokemon) { if (this.field.weatherData.source !== pokemon) return; @@ -763,7 +771,10 @@ let BattleAbilities = { shortDesc: "(Mimikyu only) The first hit it takes is blocked, and it takes 1/8 HP damage instead.", onDamagePriority: 1, onDamage(damage, target, source, effect) { - if (effect && effect.effectType === 'Move' && ['mimikyu', 'mimikyutotem'].includes(target.species.id) && !target.transformed) { + if ( + effect && effect.effectType === 'Move' && + ['mimikyu', 'mimikyutotem'].includes(target.species.id) && !target.transformed + ) { this.add('-activate', target, 'ability: Disguise'); this.effectData.busted = true; return 0; @@ -771,13 +782,18 @@ let BattleAbilities = { }, onEffectiveness(typeMod, target, type, move) { if (!target) return; - if (!['mimikyu', 'mimikyutotem'].includes(target.species.id) || target.transformed || (target.volatiles['substitute'] && !(move.flags['authentic'] || move.infiltrates))) return; + if (!['mimikyu', 'mimikyutotem'].includes(target.species.id) || target.transformed) { + return; + } + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (!target.runImmunity(move.type)) return; return 0; }, onUpdate(pokemon) { if (['mimikyu', 'mimikyutotem'].includes(pokemon.species.id) && this.effectData.busted) { - let speciesid = pokemon.species.id === 'mimikyutotem' ? 'Mimikyu-Busted-Totem' : 'Mimikyu-Busted'; + const speciesid = pokemon.species.id === 'mimikyutotem' ? 'Mimikyu-Busted-Totem' : 'Mimikyu-Busted'; pokemon.formeChange(speciesid, this.effect, true); this.damage(pokemon.baseMaxhp / 8, pokemon, pokemon, this.dex.getSpecies(speciesid)); } @@ -881,7 +897,7 @@ let BattleAbilities = { shortDesc: "30% chance of poison/paralysis/sleep on others making contact with this Pokemon.", onDamagingHit(damage, target, source, move) { if (move.flags['contact'] && !source.status && source.runStatusImmunity('powder')) { - let r = this.random(100); + const r = this.random(100); if (r < 11) { source.setStatus('slp', target); } else if (r < 21) { @@ -1070,15 +1086,14 @@ let BattleAbilities = { onAllyBoost(boost, target, source, effect) { if ((source && target === source) || !target.hasType('Grass')) return; let showMsg = false; - for (let i in boost) { - // @ts-ignore - if (boost[i] < 0) { - // @ts-ignore + let i: BoostName; + for (i in boost) { + if (boost[i]! < 0) { delete boost[i]; showMsg = true; } } - if (showMsg && !(/** @type {ActiveMove} */(effect)).secondaries) { + if (showMsg && !(effect as ActiveMove).secondaries) { const effectHolder = this.effectData.target; this.add('-block', target, 'ability: Flower Veil', '[of] ' + effectHolder); } @@ -1155,13 +1170,12 @@ let BattleAbilities = { desc: "On switch-in, this Pokemon is alerted to the move with the highest power, at random, known by an opposing Pokemon.", shortDesc: "On switch-in, this Pokemon is alerted to the foes' move with the highest power.", onStart(pokemon) { - /**@type {(Move|Pokemon)[][]} */ - let warnMoves = []; + let warnMoves: (Move | Pokemon)[][] = []; let warnBp = 1; for (const target of pokemon.side.foe.active) { if (target.fainted) continue; for (const moveSlot of target.moveSlots) { - let move = this.dex.getMove(moveSlot.move); + const move = this.dex.getMove(moveSlot.move); let bp = move.basePower; if (move.ohko) bp = 150; if (move.id === 'counter' || move.id === 'metalburst' || move.id === 'mirrorcoat') bp = 120; @@ -1218,15 +1232,14 @@ let BattleAbilities = { onBoost(boost, target, source, effect) { if (source && target === source) return; let showMsg = false; - for (let i in boost) { - // @ts-ignore - if (boost[i] < 0) { - // @ts-ignore + let i: BoostName; + for (i in boost) { + if (boost[i]! < 0) { delete boost[i]; showMsg = true; } } - if (showMsg && !(/** @type {ActiveMove} */(effect)).secondaries && effect.id !== 'octolock') { + if (showMsg && !(effect as ActiveMove).secondaries && effect.id !== 'octolock') { this.add("-fail", target, "unboost", "[from] ability: Full Metal Body", "[of] " + target); } }, @@ -1250,7 +1263,7 @@ let BattleAbilities = { galewings: { shortDesc: "If this Pokemon is at full HP, its Flying-type moves have their priority increased by 1.", onModifyPriority(priority, pokemon, target, move) { - if (move && move.type === 'Flying' && pokemon.hp === pokemon.maxhp) return priority + 1; + if (move?.type === 'Flying' && pokemon.hp === pokemon.maxhp) return priority + 1; }, id: "galewings", name: "Gale Wings", @@ -1262,7 +1275,8 @@ let BattleAbilities = { shortDesc: "This Pokemon's Normal-type moves become Electric type and have 1.2x power.", onModifyTypePriority: -1, onModifyType(move, pokemon) { - if (move.type === 'Normal' && !['judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'weatherball'].includes(move.id) && !(move.isZ && move.category !== 'Status')) { + const noModifyType = ['judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'weatherball']; + if (move.type === 'Normal' && !noModifyType.includes(move.id) && !(move.isZ && move.category !== 'Status')) { move.type = 'Electric'; move.galvanizeBoosted = true; } @@ -1365,7 +1379,8 @@ let BattleAbilities = { desc: "If this Pokemon is a Cramorant, it changes forme when it hits a target with Surf or uses the first turn of Dive successfully. It becomes Gulping Form with an Arrokuda in its mouth if it has more than 1/2 of its maximum HP remaining, or Gorging Form with a Pikachu in its mouth if it has 1/2 or less of its maximum HP remaining. If Cramorant gets hit in Gulping or Gorging Form, it spits the Arrokuda or Pikachu at its attacker, even if it has no HP remaining. The projectile deals damage equal to 1/4 of the target's maximum HP, rounded down; this damage is blocked by the Magic Guard Ability but not by a substitute. An Arrokuda also lowers the target's Defense by 1 stage, and a Pikachu paralyzes the target. Cramorant will return to normal if it spits out a projectile, switches out, or Dynamaxes.", shortDesc: "When hit after Surf/Dive, attacker takes 1/4 max HP and -1 Defense or paralysis.", onDamagingHit(damage, target, source, move) { - if (move.effectType === 'Move' && ['cramorantgulping', 'cramorantgorging'].includes(target.species.id) && !target.transformed && !target.isSemiInvulnerable()) { + if (target.transformed || target.isSemiInvulnerable()) return; + if (['cramorantgulping', 'cramorantgorging'].includes(target.species.id)) { this.damage(source.baseMaxhp / 4, source, target); if (target.species.id === 'cramorantgulping') { this.boost({def: -1}, source, target, null, true); @@ -1377,13 +1392,19 @@ let BattleAbilities = { }, // The Dive part of this mechanic is implemented in Dive's `onTryMove` in moves.js onAnyDamage(damage, target, source, effect) { - if (effect && effect.id === 'surf' && source.hasAbility('gulpmissile') && source.species.name === 'Cramorant' && !source.transformed) { + if ( + effect && effect.id === 'surf' && source.hasAbility('gulpmissile') && + source.species.name === 'Cramorant' && !source.transformed + ) { const forme = source.hp <= source.maxhp / 2 ? 'cramorantgorging' : 'cramorantgulping'; source.formeChange(forme, effect); } }, onAnyAfterSubDamage(damage, target, source, effect) { - if (effect && effect.id === 'surf' && source.hasAbility('gulpmissile') && source.species.name === 'Cramorant' && !source.transformed) { + if ( + effect && effect.id === 'surf' && source.hasAbility('gulpmissile') && + source.species.name === 'Cramorant' && !source.transformed + ) { const forme = source.hp <= source.maxhp / 2 ? 'cramorantgorging' : 'cramorantgulping'; source.formeChange(forme, effect); } @@ -1438,7 +1459,10 @@ let BattleAbilities = { return; } for (const allyActive of pokemon.side.active) { - if (allyActive && allyActive.hp && this.isAdjacent(pokemon, allyActive) && allyActive.status && this.randomChance(3, 10)) { + if ( + allyActive && + (allyActive.hp && this.isAdjacent(pokemon, allyActive) && allyActive.status) && this.randomChance(3, 10) + ) { this.add('-activate', pokemon, 'ability: Healer'); allyActive.cureStatus(); } @@ -1499,7 +1523,7 @@ let BattleAbilities = { shortDesc: "If Morpeko, it changes between Full Belly and Hangry Mode at the end of each turn.", onResidual(pokemon) { if (pokemon.species.baseSpecies !== 'Morpeko' || pokemon.transformed) return; - let targetForme = pokemon.species.name === 'Morpeko' ? 'Morpeko-Hangry' : 'Morpeko'; + const targetForme = pokemon.species.name === 'Morpeko' ? 'Morpeko-Hangry' : 'Morpeko'; pokemon.formeChange(targetForme); }, id: "hungerswitch", @@ -1549,7 +1573,7 @@ let BattleAbilities = { if (source && target === source) return; if (boost.atk && boost.atk < 0) { delete boost.atk; - if (!(/** @type {ActiveMove} */(effect)).secondaries) { + if (!(effect as ActiveMove).secondaries) { this.add("-fail", target, "unboost", "Attack", "[from] ability: Hyper Cutter", "[of] " + target); } } @@ -1587,7 +1611,10 @@ let BattleAbilities = { } }, onDamage(damage, target, source, effect) { - if (effect && effect.effectType === 'Move' && effect.category === 'Physical' && target.species.id === 'eiscue' && !target.transformed) { + if ( + effect && effect.effectType === 'Move' && effect.category === 'Physical' && + target.species.id === 'eiscue' && !target.transformed + ) { this.add('-activate', target, 'ability: Ice Face'); this.effectData.busted = true; return 0; @@ -1595,7 +1622,8 @@ let BattleAbilities = { }, onEffectiveness(typeMod, target, type, move) { if (!target) return; - if (move.category !== 'Physical' || target.species.id !== 'eiscue' || target.transformed || (target.volatiles['substitute'] && !(move.flags['authentic'] || move.infiltrates))) return; + if (move.category !== 'Physical' || target.species.id !== 'eiscue' || target.transformed) return; + if (target.volatiles['substitute'] && !(move.flags['authentic'] || move.infiltrates)) return; if (!target.runImmunity(move.type)) return; return 0; }, @@ -1659,7 +1687,8 @@ let BattleAbilities = { if (pokemon.illusion) { this.debug('illusion cleared'); pokemon.illusion = null; - let details = pokemon.species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) + (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : ''); + const details = pokemon.species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) + + (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : ''); this.add('replace', pokemon, details); this.add('-end', pokemon, 'Illusion'); } @@ -1701,7 +1730,7 @@ let BattleAbilities = { onStart(pokemon) { // Imposter does not activate when Skill Swapped or when Neutralizing Gas leaves the field if (!this.effectData.switchingIn) return; - let target = pokemon.side.foe.active[pokemon.side.foe.active.length - 1 - pokemon.position]; + const target = pokemon.side.foe.active[pokemon.side.foe.active.length - 1 - pokemon.position]; if (target) { pokemon.transformInto(target, this.dex.getAbility('imposter')); } @@ -1849,7 +1878,7 @@ let BattleAbilities = { if (source && target === source) return; if (boost.accuracy && boost.accuracy < 0) { delete boost.accuracy; - if (!(/** @type {ActiveMove} */(effect)).secondaries) { + if (!(effect as ActiveMove).secondaries) { this.add("-fail", target, "unboost", "accuracy", "[from] ability: Keen Eye", "[of] " + target); } } @@ -1876,7 +1905,7 @@ let BattleAbilities = { shortDesc: "If Sunny Day is active, this Pokemon cannot be statused and Rest will fail for it.", onSetStatus(status, target, source, effect) { if (['sunnyday', 'desolateland'].includes(target.effectiveWeather())) { - if (effect && effect.status) this.add('-immune', target, '[from] ability: Leaf Guard'); + if (effect?.status) this.add('-immune', target, '[from] ability: Leaf Guard'); return false; } }, @@ -1905,7 +1934,7 @@ let BattleAbilities = { shortDesc: "This Pokemon's type changes to match the type of the move it is about to use.", onPrepareHit(source, target, move) { if (move.hasBounced) return; - let type = move.type; + const type = move.type; if (type && type !== '???' && source.getTypes().join() !== type) { if (!source.setType(type)) return; this.add('-start', source, 'typechange', type, '[from] ability: Libero'); @@ -1977,9 +2006,8 @@ let BattleAbilities = { id: "liquidooze", onSourceTryHeal(damage, target, source, effect) { this.debug("Heal is occurring: " + target + " <- " + source + " :: " + effect.id); - /**@type {{[k: string]: number}} */ - let canOoze = {drain: 1, leechseed: 1, strengthsap: 1}; - if (canOoze[effect.id]) { + const canOoze = ['drain', 'leechseed', 'strengthsap']; + if (canOoze.includes(effect.id)) { this.damage(damage); return 0; } @@ -2022,7 +2050,7 @@ let BattleAbilities = { if (target === source || move.hasBounced || !move.flags['reflectable']) { return; } - let newMove = this.dex.getActiveMove(move.id); + const newMove = this.dex.getActiveMove(move.id); newMove.hasBounced = true; newMove.pranksterBoosted = false; this.useMove(newMove, target, source); @@ -2032,7 +2060,7 @@ let BattleAbilities = { if (target.side === source.side || move.hasBounced || !move.flags['reflectable']) { return; } - let newMove = this.dex.getActiveMove(move.id); + const newMove = this.dex.getActiveMove(move.id); newMove.hasBounced = true; newMove.pranksterBoosted = false; this.useMove(newMove, this.effectData.target, source); @@ -2065,7 +2093,7 @@ let BattleAbilities = { if (!move || !target) return; if (target !== source && move.category !== 'Status') { if (source.item || source.volatiles['gem'] || move.id === 'fling') return; - let yourItem = target.takeItem(source); + const yourItem = target.takeItem(source); if (!yourItem) return; if (!source.setItem(yourItem)) { target.item = yourItem.id; // bypass setItem so we don't break choicelock or anything @@ -2159,14 +2187,14 @@ let BattleAbilities = { if (this.field.terrain) { pokemon.addVolatile('mimicry'); } else { - let types = pokemon.baseSpecies.types; + const types = pokemon.baseSpecies.types; if (pokemon.getTypes().join() === types.join() || !pokemon.setType(types)) return; this.add('-start', pokemon, 'typechange', types.join('/'), '[from] ability: Mimicry'); this.hint("Transform Mimicry changes you to your original un-transformed types."); } }, onAnyTerrainStart() { - let pokemon = this.effectData.target; + const pokemon = this.effectData.target; delete pokemon.volatiles['mimicry']; pokemon.addVolatile('mimicry'); }, @@ -2195,7 +2223,7 @@ let BattleAbilities = { }, onUpdate(pokemon) { if (!this.field.terrain) { - let types = pokemon.species.types; + const types = pokemon.species.types; if (pokemon.getTypes().join() === types.join() || !pokemon.setType(types)) return; this.add('-activate', pokemon, 'ability: Mimicry'); this.add('-end', pokemon, 'typechange', '[silent]'); @@ -2217,7 +2245,10 @@ let BattleAbilities = { return; } for (const allyActive of pokemon.side.active) { - if (allyActive && allyActive.position !== pokemon.position && !allyActive.fainted && allyActive.hasAbility(['minus', 'plus'])) { + if ( + allyActive && allyActive.position !== pokemon.position && + !allyActive.fainted && allyActive.hasAbility(['minus', 'plus']) + ) { return this.chainModify(1.5); } } @@ -2233,13 +2264,11 @@ let BattleAbilities = { onBoost(boost, target, source, effect) { // Don't bounce self stat changes, or boosts that have already bounced if (target === source || !boost || effect.id === 'mirrorarmor') return; - for (let b in boost) { - // @ts-ignore Index signature issue with for-in loops - if (boost[b] < 0) { - let negativeBoost = {}; - // @ts-ignore Index signature issue with for-in loops + let b: BoostName; + for (b in boost) { + if (boost[b]! < 0) { + const negativeBoost: SparseBoostsTable = {}; negativeBoost[b] = boost[b]; - // @ts-ignore Index signature issue with for-in loops delete boost[b]; this.add('-ability', target, 'Mirror Armor'); this.boost(negativeBoost, source, target, null, true); @@ -2280,29 +2309,27 @@ let BattleAbilities = { onResidualOrder: 26, onResidualSubOrder: 1, onResidual(pokemon) { - let stats = []; - let boost = {}; - for (let statPlus in pokemon.boosts) { + let stats: BoostName[] = []; + const boost: SparseBoostsTable = {}; + let statPlus: BoostName; + for (statPlus in pokemon.boosts) { if (statPlus === 'accuracy' || statPlus === 'evasion') continue; - // @ts-ignore if (pokemon.boosts[statPlus] < 6) { stats.push(statPlus); } } - let randomStat = stats.length ? this.sample(stats) : ""; - // @ts-ignore + let randomStat: BoostName | undefined = stats.length ? this.sample(stats) : undefined; if (randomStat) boost[randomStat] = 2; stats = []; - for (let statMinus in pokemon.boosts) { + let statMinus: BoostName; + for (statMinus in pokemon.boosts) { if (statMinus === 'accuracy' || statMinus === 'evasion') continue; - // @ts-ignore if (pokemon.boosts[statMinus] > -6 && statMinus !== randomStat) { stats.push(statMinus); } } - randomStat = stats.length ? this.sample(stats) : ""; - // @ts-ignore + randomStat = stats.length ? this.sample(stats) : undefined; if (randomStat) boost[randomStat] = -1; this.boost(boost); @@ -2369,7 +2396,7 @@ let BattleAbilities = { name: "Mummy", onDamagingHit(damage, target, source, move) { if (move.flags['contact'] && source.ability !== 'mummy') { - let oldAbility = source.setAbility('mummy', target); + const oldAbility = source.setAbility('mummy', target); if (oldAbility) { this.add('-activate', target, 'ability: Mummy', this.dex.getAbility(oldAbility).name, '[of] ' + source); } @@ -2392,7 +2419,7 @@ let BattleAbilities = { if (pokemon.side.active.length === 1) return; if (pokemon.showCure === true || pokemon.showCure === false) return; - let cureList = []; + const cureList = []; let noCureCount = 0; for (const curPoke of pokemon.side.active) { // pokemon not statused @@ -2404,9 +2431,9 @@ let BattleAbilities = { // this.add('-message', "" + curPoke + " skipped: Natural Cure already known"); continue; } - let species = this.dex.getSpecies(curPoke.forme); + const species = this.dex.getSpecies(curPoke.forme); // pokemon can't get Natural Cure - if (Object.values(species.abilities).indexOf('Natural Cure') < 0) { + if (!Object.values(species.abilities).includes('Natural Cure')) { // this.add('-message', "" + curPoke + " skipped: no Natural Cure"); continue; } @@ -2432,8 +2459,8 @@ let BattleAbilities = { if (!cureList.length || !noCureCount) { // It's possible to know what pokemon were cured - for (const pokemon of cureList) { - pokemon.showCure = true; + for (const pkmn of cureList) { + pkmn.showCure = true; } } else { // It's not possible to know what pokemon were cured @@ -2441,8 +2468,8 @@ let BattleAbilities = { // Unlike a -hint, this is real information that battlers need, so we use a -message this.add('-message', "(" + cureList.length + " of " + pokemon.side.name + "'s pokemon " + (cureList.length === 1 ? "was" : "were") + " cured by Natural Cure.)"); - for (const pokemon of cureList) { - pokemon.showCure = false; + for (const pkmn of cureList) { + pkmn.showCure = false; } } }, @@ -2528,7 +2555,10 @@ let BattleAbilities = { shortDesc: "This Pokemon's moves are changed to be Normal type and have 1.2x power.", onModifyTypePriority: 1, onModifyType(move, pokemon) { - if (!(move.isZ && move.category !== 'Status') && !['hiddenpower', 'judgment', 'multiattack', 'naturalgift', 'revelationdance', 'struggle', 'technoblast', 'weatherball'].includes(move.id)) { + const noModifyType = [ + 'hiddenpower', 'judgment', 'multiattack', 'naturalgift', 'revelationdance', 'struggle', 'technoblast', 'weatherball', + ]; + if (!(move.isZ && move.category !== 'Status') && !noModifyType.includes(move.id)) { move.type = 'Normal'; move.normalizeBoosted = true; } @@ -2623,7 +2653,7 @@ let BattleAbilities = { if (status.id === 'confusion') return null; }, onHit(target, source, move) { - if (move && move.volatileStatus === 'confusion') { + if (move?.volatileStatus === 'confusion') { this.add('-immune', target, 'confusion', '[from] ability: Own Tempo'); } }, @@ -2636,8 +2666,9 @@ let BattleAbilities = { desc: "This Pokemon's damaging moves become multi-hit moves that hit twice. The second hit has its damage quartered. Does not affect multi-hit moves or moves that have multiple targets.", shortDesc: "This Pokemon's damaging moves hit twice. The second hit has its damage quartered.", onPrepareHit(source, target, move) { + if (move.category === 'Status' || move.selfdestruct || move.multihit) return; if (['iceball', 'rollout'].includes(move.id)) return; - if (move.category !== 'Status' && !move.selfdestruct && !move.multihit && !move.flags['charge'] && !move.spreadHit && !move.isZ && !move.isMax) { + if (!move.flags['charge'] && !move.spreadHit && !move.isZ && !move.isMax) { move.multihit = 2; move.multihitType = 'parentalbond'; } @@ -2704,7 +2735,7 @@ let BattleAbilities = { if (!move.flags['contact']) return; let announced = false; - for (let pokemon of [target, source]) { + for (const pokemon of [target, source]) { if (pokemon.volatiles['perishsong']) continue; if (!announced) { this.add('-ability', target, 'Perish Body'); @@ -2722,11 +2753,11 @@ let BattleAbilities = { desc: "If this Pokemon has no item and is hit by a contact move, it steals the attacker's item. This effect applies after all hits from a multi-hit move; Sheer Force prevents it from activating if the move has a secondary effect.", shortDesc: "If this Pokemon has no item and is hit by a contact move, it steals the attacker's item.", onAfterMoveSecondary(target, source, move) { - if (source && source !== target && move && move.flags['contact']) { + if (source && source !== target && move?.flags['contact']) { if (target.item || target.switchFlag || target.forceSwitchFlag || source.switchFlag === true) { return; } - let yourItem = source.takeItem(target); + const yourItem = source.takeItem(target); if (!yourItem) { return; } @@ -2749,15 +2780,15 @@ let BattleAbilities = { onResidualSubOrder: 1, onResidual(pokemon) { if (pokemon.item) return; - let pickupTargets = []; + const pickupTargets = []; for (const target of this.getAllActive()) { if (target.lastItem && target.usedItemThisTurn && this.isAdjacent(pokemon, target)) { pickupTargets.push(target); } } if (!pickupTargets.length) return; - let randomTarget = this.sample(pickupTargets); - let item = randomTarget.lastItem; + const randomTarget = this.sample(pickupTargets); + const item = randomTarget.lastItem; randomTarget.lastItem = ''; this.add('-item', pokemon, this.dex.getItem(item), '[from] ability: Pickup'); pokemon.setItem(item); @@ -2772,7 +2803,8 @@ let BattleAbilities = { shortDesc: "This Pokemon's Normal-type moves become Fairy type and have 1.2x power.", onModifyTypePriority: -1, onModifyType(move, pokemon) { - if (move.type === 'Normal' && !['judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'weatherball'].includes(move.id) && !(move.isZ && move.category !== 'Status')) { + const noModifyType = ['judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'weatherball']; + if (move.type === 'Normal' && !noModifyType.includes(move.id) && !(move.isZ && move.category !== 'Status')) { move.type = 'Fairy'; move.pixilateBoosted = true; } @@ -2795,7 +2827,10 @@ let BattleAbilities = { return; } for (const allyActive of pokemon.side.active) { - if (allyActive && allyActive.position !== pokemon.position && !allyActive.fainted && allyActive.hasAbility(['minus', 'plus'])) { + if ( + allyActive && allyActive.position !== pokemon.position && + !allyActive.fainted && allyActive.hasAbility(['minus', 'plus']) + ) { return this.chainModify(1.5); } } @@ -2862,8 +2897,10 @@ let BattleAbilities = { if (pokemon.species.id === 'zygardecomplete' || pokemon.hp > pokemon.maxhp / 2) return; this.add('-activate', pokemon, 'ability: Power Construct'); pokemon.formeChange('Zygarde-Complete', this.effect, true); - pokemon.baseMaxhp = Math.floor(Math.floor(2 * pokemon.species.baseStats['hp'] + pokemon.set.ivs['hp'] + Math.floor(pokemon.set.evs['hp'] / 4) + 100) * pokemon.level / 100 + 10); - let newMaxHP = pokemon.volatiles['dynamax'] ? (2 * pokemon.baseMaxhp) : pokemon.baseMaxhp; + pokemon.baseMaxhp = Math.floor(Math.floor( + 2 * pokemon.species.baseStats['hp'] + pokemon.set.ivs['hp'] + Math.floor(pokemon.set.evs['hp'] / 4) + 100 + ) * pokemon.level / 100 + 10); + const newMaxHP = pokemon.volatiles['dynamax'] ? (2 * pokemon.baseMaxhp) : pokemon.baseMaxhp; pokemon.hp = newMaxHP - (pokemon.maxhp - pokemon.hp); pokemon.maxhp = newMaxHP; this.add('-heal', pokemon, pokemon.getHealth, '[silent]'); @@ -2878,8 +2915,10 @@ let BattleAbilities = { shortDesc: "This Pokemon copies the Ability of an ally that faints.", onAllyFaint(target) { if (!this.effectData.target.hp) return; - let ability = target.getAbility(); - let bannedAbilities = ['battlebond', 'comatose', 'disguise', 'flowergift', 'forecast', 'gulpmissile', 'hungerswitch', 'iceface', 'illusion', 'imposter', 'multitype', 'powerconstruct', 'powerofalchemy', 'receiver', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'trace', 'wonderguard', 'zenmode']; + const ability = target.getAbility(); + const bannedAbilities = [ + 'battlebond', 'comatose', 'disguise', 'flowergift', 'forecast', 'gulpmissile', 'hungerswitch', 'iceface', 'illusion', 'imposter', 'multitype', 'powerconstruct', 'powerofalchemy', 'receiver', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'trace', 'wonderguard', 'zenmode', + ]; if (bannedAbilities.includes(target.ability)) return; this.add('-ability', this.effectData.target, ability, '[from] ability: Power of Alchemy', '[of] ' + target); this.effectData.target.setAbility(ability); @@ -2906,7 +2945,7 @@ let BattleAbilities = { prankster: { shortDesc: "This Pokemon's Status moves have priority raised by 1, but Dark types are immune.", onModifyPriority(priority, pokemon, target, move) { - if (move && move.category === 'Status') { + if (move?.category === 'Status') { move.pranksterBoosted = true; return priority + 1; } @@ -2938,7 +2977,8 @@ let BattleAbilities = { this.field.setWeather('primordialsea'); }, onAnySetWeather(target, source, weather) { - if (this.field.getWeather().id === 'primordialsea' && !['desolateland', 'primordialsea', 'deltastream'].includes(weather.id)) return false; + const strongWeathers = ['desolateland', 'primordialsea', 'deltastream']; + if (this.field.getWeather().id === 'primordialsea' && !strongWeathers.includes(weather.id)) return false; }, onEnd(pokemon) { if (this.field.weatherData.source !== pokemon) return; @@ -2988,7 +3028,7 @@ let BattleAbilities = { shortDesc: "This Pokemon's type changes to match the type of the move it is about to use.", onPrepareHit(source, target, move) { if (move.hasBounced) return; - let type = move.type; + const type = move.type; if (type && type !== '???' && source.getTypes().join() !== type) { if (!source.setType(type)) return; this.add('-start', source, 'typechange', type, '[from] ability: Protean'); @@ -3112,8 +3152,10 @@ let BattleAbilities = { shortDesc: "This Pokemon copies the Ability of an ally that faints.", onAllyFaint(target) { if (!this.effectData.target.hp) return; - let ability = target.getAbility(); - let bannedAbilities = ['battlebond', 'comatose', 'disguise', 'flowergift', 'forecast', 'gulpmissile', 'hungerswitch', 'iceface', 'illusion', 'imposter', 'multitype', 'neutralizinggas', 'powerconstruct', 'powerofalchemy', 'receiver', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'trace', 'wonderguard', 'zenmode']; + const ability = target.getAbility(); + const bannedAbilities = [ + 'battlebond', 'comatose', 'disguise', 'flowergift', 'forecast', 'gulpmissile', 'hungerswitch', 'iceface', 'illusion', 'imposter', 'multitype', 'neutralizinggas', 'powerconstruct', 'powerofalchemy', 'receiver', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'trace', 'wonderguard', 'zenmode', + ]; if (bannedAbilities.includes(target.ability)) return; this.add('-ability', this.effectData.target, ability, '[from] ability: Receiver', '[of] ' + target); this.effectData.target.setAbility(ability); @@ -3143,7 +3185,8 @@ let BattleAbilities = { shortDesc: "This Pokemon's Normal-type moves become Ice type and have 1.2x power.", onModifyTypePriority: -1, onModifyType(move, pokemon) { - if (move.type === 'Normal' && !['judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'weatherball'].includes(move.id) && !(move.isZ && move.category !== 'Status')) { + const noModifyType = ['judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'weatherball']; + if (move.type === 'Normal' && !noModifyType.includes(move.id) && !(move.isZ && move.category !== 'Status')) { move.type = 'Ice'; move.refrigerateBoosted = true; } @@ -3173,16 +3216,18 @@ let BattleAbilities = { // Check if they are affected by ripen. shortDesc: "When this Pokemon eats a Berry, its effect is doubled.", onTryHeal(damage, target, source, effect) { - if (effect && /** @type {Item} */(effect).isBerry) { + if (effect && (effect as Item).isBerry) { this.debug(`Ripen doubled healing`); return this.chainModify(2); } }, onBoost(boost, target, source, effect) { - if (effect && /** @type {Item} */(effect).isBerry) { + if (effect && (effect as Item).isBerry) { this.debug(`Ripen doubled boost`); - // @ts-ignore Index signature issue with for-in loops - for (let b in boost) boost[b] *= 2; + let b: BoostName; + for (b in boost) { + boost[b]! *= 2; + } } }, onSourceModifyDamage(damage, source, target, move) { @@ -3195,7 +3240,9 @@ let BattleAbilities = { } }, onEatItem(item, pokemon) { - const weakenBerries = ['Babiri Berry', 'Charti Berry', 'Chilan Berry', 'Chople Berry', 'Coba Berry', 'Colbur Berry', 'Haban Berry', 'Kasib Berry', 'Kebia Berry', 'Occa Berry', 'Passho Berry', 'Payapa Berry', 'Rindo Berry', 'Roseli Berry', 'Shuca Berry', 'Tanga Berry', 'Wacan Berry', 'Yache Berry']; + const weakenBerries = [ + 'Babiri Berry', 'Charti Berry', 'Chilan Berry', 'Chople Berry', 'Coba Berry', 'Colbur Berry', 'Haban Berry', 'Kasib Berry', 'Kebia Berry', 'Occa Berry', 'Passho Berry', 'Payapa Berry', 'Rindo Berry', 'Roseli Berry', 'Shuca Berry', 'Tanga Berry', 'Wacan Berry', 'Yache Berry', + ]; if (weakenBerries.includes(item.name)) { // Record that the pokemon ate a berry to resist an attack pokemon.abilityData.berryWeaken = "true"; @@ -3386,7 +3433,10 @@ let BattleAbilities = { }, onResidualOrder: 27, onResidual(pokemon) { - if (pokemon.baseSpecies.baseSpecies !== 'Wishiwashi' || pokemon.level < 20 || pokemon.transformed || !pokemon.hp) return; + if ( + pokemon.baseSpecies.baseSpecies !== 'Wishiwashi' || pokemon.level < 20 || + pokemon.transformed || !pokemon.hp + ) return; if (pokemon.hp > pokemon.maxhp / 4) { if (pokemon.species.id === 'wishiwashi') { pokemon.formeChange('Wishiwashi-School'); @@ -3603,9 +3653,9 @@ let BattleAbilities = { shortDesc: "When this Pokemon's stat stages are raised or lowered, the effect is doubled instead.", onBoost(boost, target, source, effect) { if (effect && effect.id === 'zpower') return; - for (let i in boost) { - // @ts-ignore - boost[i] *= 2; + let i: BoostName; + for (i in boost) { + boost[i]! *= 2; } }, id: "simple", @@ -3849,7 +3899,7 @@ let BattleAbilities = { onBeforeMove(attacker, defender, move) { if (attacker.species.baseSpecies !== 'Aegislash' || attacker.transformed) return; if (move.category === 'Status' && move.id !== 'kingsshield') return; - let targetForme = (move.id === 'kingsshield' ? 'Aegislash' : 'Aegislash-Blade'); + const targetForme = (move.id === 'kingsshield' ? 'Aegislash' : 'Aegislash-Blade'); if (attacker.species.name !== targetForme) attacker.formeChange(targetForme); }, id: "stancechange", @@ -4122,11 +4172,13 @@ let BattleAbilities = { desc: "If an ally uses its item, this Pokemon gives its item to that ally immediately. Does not activate if the ally's item was stolen or knocked off.", shortDesc: "If an ally uses its item, this Pokemon gives its item to that ally immediately.", onAllyAfterUseItem(item, pokemon) { - let source = this.effectData.target; - let myItem = source.takeItem(); + const source = this.effectData.target; + const myItem = source.takeItem(); if (!myItem) return; - // @ts-ignore - if (!this.singleEvent('TakeItem', myItem, source.itemData, pokemon, source, this.effectData, myItem) || !pokemon.setItem(myItem)) { + if ( + !this.singleEvent('TakeItem', myItem, source.itemData, pokemon, source, this.effect, myItem) || + !pokemon.setItem(myItem) + ) { source.item = myItem.id; return; } @@ -4159,7 +4211,7 @@ let BattleAbilities = { shortDesc: "This Pokemon's evasiveness is doubled as long as it is confused.", onModifyAccuracy(accuracy, target) { if (typeof accuracy !== 'number') return; - if (target && target.volatiles['confusion']) { + if (target?.volatiles['confusion']) { this.debug('Tangled Feet - decreasing accuracy'); return accuracy * 0.5; } @@ -4187,7 +4239,7 @@ let BattleAbilities = { shortDesc: "This Pokemon's moves of 60 power or less have 1.5x power. Includes Struggle.", onBasePowerPriority: 30, onBasePower(basePower, attacker, defender, move) { - let basePowerAfterMultiplier = this.modify(basePower, this.event.modifier); + const basePowerAfterMultiplier = this.modify(basePower, this.event.modifier); this.debug('Base Power: ' + basePowerAfterMultiplier); if (basePowerAfterMultiplier <= 60) { this.debug('Technician boost'); @@ -4313,19 +4365,23 @@ let BattleAbilities = { desc: "On switch-in, or when this Pokemon acquires this ability, this Pokemon copies a random adjacent opposing Pokemon's Ability. However, if one or more adjacent Pokemon has the Ability \"No Ability\", Trace won't copy anything even if there is another valid Ability it could normally copy. Otherwise, if there is no Ability that can be copied at that time, this Ability will activate as soon as an Ability can be copied. Abilities that cannot be copied are the previously mentioned \"No Ability\", as well as Comatose, Disguise, Flower Gift, Forecast, Gulp Missile, Hunger Switch, Ice Face, Illusion, Imposter, Multitype, Schooling, Stance Change, Trace, and Zen Mode.", shortDesc: "On switch-in, or when it can, this Pokemon copies a random adjacent foe's Ability.", onStart(pokemon) { - if (pokemon.side.foe.active.some(foeActive => foeActive && this.isAdjacent(pokemon, foeActive) && foeActive.ability === 'noability')) { + if (pokemon.side.foe.active.some( + foeActive => foeActive && this.isAdjacent(pokemon, foeActive) && foeActive.ability === 'noability' + )) { this.effectData.gaveUp = true; } }, onUpdate(pokemon) { if (!pokemon.isStarted || this.effectData.gaveUp) return; - let possibleTargets = pokemon.side.foe.active.filter(foeActive => foeActive && this.isAdjacent(pokemon, foeActive)); + const possibleTargets = pokemon.side.foe.active.filter(foeActive => foeActive && this.isAdjacent(pokemon, foeActive)); while (possibleTargets.length) { let rand = 0; if (possibleTargets.length > 1) rand = this.random(possibleTargets.length); - let target = possibleTargets[rand]; - let ability = target.getAbility(); - let bannedAbilities = ['noability', 'battlebond', 'comatose', 'disguise', 'flowergift', 'forecast', 'gulpmissile', 'hungerswitch', 'iceface', 'illusion', 'imposter', 'multitype', 'powerconstruct', 'powerofalchemy', 'receiver', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'trace', 'zenmode']; + const target = possibleTargets[rand]; + const ability = target.getAbility(); + const bannedAbilities = [ + 'noability', 'battlebond', 'comatose', 'disguise', 'flowergift', 'forecast', 'gulpmissile', 'hungerswitch', 'iceface', 'illusion', 'imposter', 'multitype', 'powerconstruct', 'powerofalchemy', 'receiver', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'trace', 'zenmode', + ]; if (bannedAbilities.includes(target.ability)) { possibleTargets.splice(rand, 1); continue; @@ -4343,7 +4399,7 @@ let BattleAbilities = { triage: { shortDesc: "This Pokemon's healing moves have their priority increased by 3.", onModifyPriority(priority, pokemon, target, move) { - if (move && move.flags['heal']) return priority + 3; + if (move?.flags['heal']) return priority + 3; }, id: "triage", name: "Triage", @@ -4391,7 +4447,7 @@ let BattleAbilities = { id: "unaware", name: "Unaware", onAnyModifyBoost(boosts, pokemon) { - let unawareUser = this.effectData.target; + const unawareUser = this.effectData.target; if (unawareUser === pokemon) return; if (unawareUser === this.activePokemon && pokemon === this.activeTarget) { boosts['def'] = 0; @@ -4498,7 +4554,7 @@ let BattleAbilities = { if (target.volatiles['dynamax']) return; if (['illusion', 'neutralizinggas', 'wanderingspirit', 'wonderguard'].includes(source.ability)) return; if (move.flags['contact']) { - let sourceAbility = source.setAbility('wanderingspirit', target); + const sourceAbility = source.setAbility('wanderingspirit', target); if (!sourceAbility) return; if (target.side === source.side) { this.add('-activate', target, 'Skill Swap', '', '', '[of] ' + source); @@ -4620,15 +4676,14 @@ let BattleAbilities = { onBoost(boost, target, source, effect) { if (source && target === source) return; let showMsg = false; - for (let i in boost) { - // @ts-ignore - if (boost[i] < 0) { - // @ts-ignore + let i: BoostName; + for (i in boost) { + if (boost[i]! < 0) { delete boost[i]; showMsg = true; } } - if (showMsg && !(/** @type {ActiveMove} */(effect)).secondaries && effect.id !== 'octolock') { + if (showMsg && !(effect as ActiveMove).secondaries && effect.id !== 'octolock') { this.add("-fail", target, "unboost", "[from] ability: White Smoke", "[of] " + target); } }, @@ -4710,7 +4765,7 @@ let BattleAbilities = { pokemon.transformed = false; delete pokemon.volatiles['zenmode']; if (pokemon.species.baseSpecies === 'Darmanitan' && pokemon.species.battleOnly) { - pokemon.formeChange(/** @type {string} */ (pokemon.species.battleOnly), this.effect, false, '[silent]'); + pokemon.formeChange(pokemon.species.battleOnly as string, this.effect, false, '[silent]'); } }, effect: { @@ -4723,7 +4778,7 @@ let BattleAbilities = { }, onEnd(pokemon) { if (['Zen', 'Galar-Zen'].includes(pokemon.species.forme)) { - pokemon.formeChange(/** @type {string} */ (pokemon.species.battleOnly)); + pokemon.formeChange(pokemon.species.battleOnly as string); } }, }, @@ -4766,7 +4821,7 @@ let BattleAbilities = { if (target === source || move.hasBounced || !move.flags['reflectable']) { return; } - let newMove = this.dex.getActiveMove(move.id); + const newMove = this.dex.getActiveMove(move.id); newMove.hasBounced = true; this.useMove(newMove, target, source); return null; @@ -4777,7 +4832,7 @@ let BattleAbilities = { if (target.side === source.side || move.hasBounced || !move.flags['reflectable']) { return; } - let newMove = this.dex.getActiveMove(move.id); + const newMove = this.dex.getActiveMove(move.id); newMove.hasBounced = true; this.useMove(newMove, this.effectData.target, source); return null; @@ -4799,5 +4854,3 @@ let BattleAbilities = { num: -4, }, }; - -exports.BattleAbilities = BattleAbilities; diff --git a/data/aliases.js b/data/aliases.ts similarity index 99% rename from data/aliases.js rename to data/aliases.ts index 14cce201f5..b763c82e11 100644 --- a/data/aliases.js +++ b/data/aliases.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: string}} */ -let BattleAliases = { +export const BattleAliases: {[alias: string]: string} = { // formats randbats: "[Gen 8] Random Battle", uber: "[Gen 8] Ubers", @@ -1726,5 +1723,3 @@ let BattleAliases = { merutan: "Meltan", merumetaru: "Melmetal", }; - -exports.BattleAliases = BattleAliases; diff --git a/data/formats-data.js b/data/formats-data.ts similarity index 99% rename from data/formats-data.js rename to data/formats-data.ts index ff4622cd74..0bd3aa436d 100644 --- a/data/formats-data.js +++ b/data/formats-data.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: SpeciesFormatsData}} */ -let BattleFormatsData = { +export const BattleFormatsData: {[speciesid: string]: SpeciesFormatsData} = { bulbasaur: { tier: "LC", }, @@ -4904,5 +4901,3 @@ let BattleFormatsData = { tier: "Illegal", }, }; - -exports.BattleFormatsData = BattleFormatsData; diff --git a/data/items.js b/data/items.ts similarity index 97% rename from data/items.js rename to data/items.ts index 819d0c1dc3..19dd1fc7d5 100644 --- a/data/items.js +++ b/data/items.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ItemData}} */ -let BattleItems = { +export const BattleItems: {[itemid: string]: ItemData} = { abomasite: { id: "abomasite", name: "Abomasite", @@ -347,7 +344,10 @@ let BattleItems = { type: "Steel", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Steel' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Steel' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -454,9 +454,8 @@ let BattleItems = { }, onTryHealPriority: 1, onTryHeal(damage, target, source, effect) { - /**@type {{[k: string]: number}} */ - let heals = {drain: 1, leechseed: 1, ingrain: 1, aquaring: 1, strengthsap: 1}; - if (heals[effect.id]) { + const heals = ['drain', 'leechseed', 'ingrain', 'aquaring', 'strengthsap']; + if (heals.includes(effect.id)) { return this.chainModify([0x14CC, 0x1000]); } }, @@ -805,7 +804,10 @@ let BattleItems = { type: "Rock", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Rock' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Rock' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -883,7 +885,10 @@ let BattleItems = { type: "Normal", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Normal' && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if ( + move.type === 'Normal' && + (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6)) + ) { if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -1012,7 +1017,10 @@ let BattleItems = { type: "Fighting", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Fighting' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Fighting' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -1058,7 +1066,10 @@ let BattleItems = { type: "Flying", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Flying' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Flying' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -1081,7 +1092,10 @@ let BattleItems = { type: "Dark", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Dark' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Dark' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -1595,9 +1609,9 @@ let BattleItems = { }, onAfterBoost(boost, target, source, effect) { let eject = false; - for (let i in boost) { - // @ts-ignore - if (boost[i] < 0) { + let i: BoostName; + for (i in boost) { + if (boost[i]! < 0) { eject = true; } } @@ -1632,7 +1646,8 @@ let BattleItems = { spritenum: 120, isGem: true, onSourceTryPrimaryHit(target, source, move) { - if (target === source || move.category === 'Status' || ['firepledge', 'grasspledge', 'waterpledge'].includes(move.id)) return; + const pledges = ['firepledge', 'grasspledge', 'waterpledge']; + if (target === source || move.category === 'Status' || pledges.includes(move.id)) return; if (move.type === 'Electric' && source.useItem()) { source.addVolatile('gem'); } @@ -1911,7 +1926,8 @@ let BattleItems = { spritenum: 141, isGem: true, onSourceTryPrimaryHit(target, source, move) { - if (target === source || move.category === 'Status' || ['firepledge', 'grasspledge', 'waterpledge'].includes(move.id)) return; + const pledges = ['firepledge', 'grasspledge', 'waterpledge']; + if (target === source || move.category === 'Status' || pledges.includes(move.id)) return; if (move.type === 'Fire' && source.useItem()) { source.addVolatile('gem'); } @@ -2366,7 +2382,8 @@ let BattleItems = { spritenum: 172, isGem: true, onSourceTryPrimaryHit(target, source, move) { - if (target === source || move.category === 'Status' || ['firepledge', 'grasspledge', 'waterpledge'].includes(move.id)) return; + const pledges = ['firepledge', 'grasspledge', 'waterpledge']; + if (target === source || move.category === 'Status' || pledges.includes(move.id)) return; if (move.type === 'Grass' && source.useItem()) { source.addVolatile('gem'); } @@ -2566,7 +2583,10 @@ let BattleItems = { type: "Dragon", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Dragon' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Dragon' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -2938,7 +2958,10 @@ let BattleItems = { type: "Ghost", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Ghost' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Ghost' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -2961,7 +2984,10 @@ let BattleItems = { type: "Poison", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Poison' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Poison' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -3214,7 +3240,7 @@ let BattleItems = { } }, onEat(pokemon) { - let moveSlot = pokemon.moveSlots.find(move => move.pp === 0) || + const moveSlot = pokemon.moveSlots.find(move => move.pp === 0) || pokemon.moveSlots.find(move => move.pp < move.maxpp); if (!moveSlot) return; moveSlot.pp += 10; @@ -3707,7 +3733,7 @@ let BattleItems = { fling: { basePower: 10, effect(pokemon) { - let conditions = ['attract', 'taunt', 'encore', 'torment', 'disable', 'healblock']; + const conditions = ['attract', 'taunt', 'encore', 'torment', 'disable', 'healblock']; for (const firstCondition of conditions) { if (pokemon.volatiles[firstCondition]) { for (const secondCondition of conditions) { @@ -3722,7 +3748,7 @@ let BattleItems = { }, }, onUpdate(pokemon) { - let conditions = ['attract', 'taunt', 'encore', 'torment', 'disable', 'healblock']; + const conditions = ['attract', 'taunt', 'encore', 'torment', 'disable', 'healblock']; for (const firstCondition of conditions) { if (pokemon.volatiles[firstCondition]) { if (!pokemon.useItem()) return; @@ -3822,7 +3848,7 @@ let BattleItems = { }, onModifyDamage(damage, source, target, move) { const dmgMod = [0x1000, 0x1333, 0x1666, 0x1999, 0x1CCC, 0x2000]; - let numConsecutive = this.effectData.numConsecutive > 5 ? 5 : this.effectData.numConsecutive; + const numConsecutive = this.effectData.numConsecutive > 5 ? 5 : this.effectData.numConsecutive; return this.chainModify([dmgMod[numConsecutive], 0x1000]); }, }, @@ -4111,7 +4137,8 @@ let BattleItems = { spritenum: 307, isGem: true, onSourceTryPrimaryHit(target, source, move) { - if (target === source || move.category === 'Status' || ['firepledge', 'grasspledge', 'waterpledge'].includes(move.id)) return; + const pledges = ['firepledge', 'grasspledge', 'waterpledge']; + if (target === source || move.category === 'Status' || pledges.includes(move.id)) return; if (move.type === 'Normal' && source.useItem()) { source.addVolatile('gem'); } @@ -4142,7 +4169,10 @@ let BattleItems = { type: "Fire", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Fire' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Fire' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -4253,7 +4283,10 @@ let BattleItems = { type: "Water", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Water' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Water' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -4276,7 +4309,10 @@ let BattleItems = { type: "Psychic", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Psychic' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Psychic' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -4699,7 +4735,10 @@ let BattleItems = { }, onAttractPriority: -1, onAttract(target, source) { - if (target !== source && target === this.activePokemon && this.activeMove && this.activeMove.flags['contact']) return false; + if ( + target !== source && target === this.activePokemon && + this.activeMove && this.activeMove.flags['contact'] + ) return false; }, onBoostPriority: -1, onBoost(boost, target, source, effect) { @@ -4731,7 +4770,10 @@ let BattleItems = { } }, onSetStatus(status, target, source, effect) { - if (target !== source && target === this.activePokemon && this.activeMove && this.activeMove.flags['contact']) return false; + if ( + target !== source && target === this.activePokemon && + this.activeMove && this.activeMove.flags['contact'] + ) return false; }, num: 880, gen: 7, @@ -5004,8 +5046,11 @@ let BattleItems = { }, onAfterMoveSecondary(target, source, move) { if (source && source !== target && source.hp && target.hp && move && move.category !== 'Status') { - if (!source.isActive || !this.canSwitch(source.side) || source.forceSwitchFlag || target.forceSwitchFlag) return; - if (target.useItem(source)) { // This order is correct - the item is used up even against a pokemon with Ingrain or that otherwise can't be forced out + if (!source.isActive || !this.canSwitch(source.side) || source.forceSwitchFlag || target.forceSwitchFlag) { + return; + } + // The item is used up even against a pokemon with Ingrain or that otherwise can't be forced out + if (target.useItem(source)) { if (this.runEvent('DragOut', source, target, move)) { source.forceSwitchFlag = true; } @@ -5068,7 +5113,10 @@ let BattleItems = { type: "Grass", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Grass' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Grass' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -5232,7 +5280,10 @@ let BattleItems = { type: "Fairy", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Fairy' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Fairy' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -5572,7 +5623,10 @@ let BattleItems = { type: "Ground", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Ground' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Ground' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -5776,7 +5830,10 @@ let BattleItems = { }, onBasePowerPriority: 15, onBasePower(basePower, user, target, move) { - if (move && (user.baseSpecies.num === 380 || user.baseSpecies.num === 381) && (move.type === 'Psychic' || move.type === 'Dragon')) { + if ( + move && (user.baseSpecies.num === 380 || user.baseSpecies.num === 381) && + (move.type === 'Psychic' || move.type === 'Dragon') + ) { return this.chainModify([0x1333, 0x1000]); } }, @@ -5888,17 +5945,16 @@ let BattleItems = { } }, onEat(pokemon) { - let stats = []; - for (let stat in pokemon.boosts) { - // @ts-ignore + const stats: BoostName[] = []; + let stat: BoostName; + for (stat in pokemon.boosts) { if (stat !== 'accuracy' && stat !== 'evasion' && pokemon.boosts[stat] < 6) { stats.push(stat); } } if (stats.length) { - let randomStat = this.sample(stats); - /**@type {{[k: string]: number}} */ - let boost = {}; + const randomStat = this.sample(stats); + const boost: SparseBoostsTable = {}; boost[randomStat] = 2; this.boost(boost); } @@ -6013,7 +6069,7 @@ let BattleItems = { }, onHit(target, source, move) { if (source && source !== target && !source.item && move && move.flags['contact']) { - let barb = target.takeItem(); + const barb = target.takeItem(); if (!barb) return; // Gen 4 Multitype source.setItem(barb); // no message for Sticky Barb changing hands @@ -6120,7 +6176,10 @@ let BattleItems = { type: "Bug", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Bug' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Bug' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -7471,7 +7530,9 @@ let BattleItems = { type: "Electric", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Electric' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Electric' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -7490,7 +7551,8 @@ let BattleItems = { spritenum: 528, isGem: true, onSourceTryPrimaryHit(target, source, move) { - if (target === source || move.category === 'Status' || ['firepledge', 'grasspledge', 'waterpledge'].includes(move.id)) return; + const pledges = ['firepledge', 'grasspledge', 'waterpledge']; + if (target === source || move.category === 'Status' || pledges.includes(move.id)) return; if (move.type === 'Water' && source.useItem()) { source.addVolatile('gem'); } @@ -7584,7 +7646,10 @@ let BattleItems = { }, onHitPriority: 1, onHit(target, source, move) { - if (target.hp && move.category !== 'Status' && !move.damage && !move.damageCallback && target.getMoveHitData(move).typeMod > 0) { + if ( + target.hp && move.category !== 'Status' && !move.damage && + !move.damageCallback && target.getMoveHitData(move).typeMod > 0 + ) { target.useItem(); } }, @@ -7630,10 +7695,9 @@ let BattleItems = { basePower: 10, effect(pokemon) { let activate = false; - /**@type {{[k: string]: number}} */ - let boosts = {}; - for (let i in pokemon.boosts) { - // @ts-ignore + const boosts: SparseBoostsTable = {}; + let i: BoostName; + for (i in pokemon.boosts) { if (pokemon.boosts[i] < 0) { activate = true; boosts[i] = 0; @@ -7647,10 +7711,9 @@ let BattleItems = { }, onUpdate(pokemon) { let activate = false; - /**@type {{[k: string]: number}} */ - let boosts = {}; - for (let i in pokemon.boosts) { - // @ts-ignore + const boosts: SparseBoostsTable = {}; + let i: BoostName; + for (i in pokemon.boosts) { if (pokemon.boosts[i] < 0) { activate = true; boosts[i] = 0; @@ -7735,7 +7798,10 @@ let BattleItems = { type: "Ice", }, onSourceModifyDamage(damage, source, target, move) { - if (move.type === 'Ice' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'] || (move.infiltrates && this.gen >= 6))) { + if (move.type === 'Ice' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -7985,7 +8051,7 @@ let BattleItems = { }, onUpdate(pokemon) { if (!pokemon.hp) return; - let moveSlot = pokemon.lastMove && pokemon.getMoveData(pokemon.lastMove.id); + const moveSlot = pokemon.lastMove && pokemon.getMoveData(pokemon.lastMove.id); if (moveSlot && moveSlot.pp === 0) { pokemon.addVolatile('leppaberry'); pokemon.volatiles['leppaberry'].moveSlot = moveSlot; @@ -8111,5 +8177,3 @@ let BattleItems = { desc: "If held by a Crucibelle, this item allows it to Mega Evolve in battle.", }, }; - -exports.BattleItems = BattleItems; diff --git a/data/learnsets.js b/data/learnsets.ts old mode 100755 new mode 100644 similarity index 99% rename from data/learnsets.js rename to data/learnsets.ts index 0bef4fa46a..4df3bb3c18 --- a/data/learnsets.js +++ b/data/learnsets.ts @@ -1,7 +1,6 @@ -'use strict'; +/* eslint-disable max-len */ -/**@type {{[k: string]: LearnsetData}} */ -let BattleLearnsets = { +export const BattleLearnsets: {[speciesid: string]: LearnsetData} = { missingno: { learnset: { blizzard: ["3L1"], @@ -78413,5 +78412,3 @@ let BattleLearnsets = { ], }, }; - -exports.BattleLearnsets = BattleLearnsets; diff --git a/data/mods/gen1/formats-data.js b/data/mods/gen1/formats-data.ts similarity index 99% rename from data/mods/gen1/formats-data.js rename to data/mods/gen1/formats-data.ts index 85bd04a8d0..3462da0bbd 100644 --- a/data/mods/gen1/formats-data.js +++ b/data/mods/gen1/formats-data.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: SpeciesFormatsData}} */ -let BattleFormatsData = { +export const BattleFormatsData: {[k: string]: ModdedSpeciesFormatsData} = { bulbasaur: { randomBattleMoves: ["sleeppowder", "bodyslam"], essentialMove: "razorleaf", @@ -780,5 +777,3 @@ let BattleFormatsData = { tier: "Illegal", }, }; - -exports.BattleFormatsData = BattleFormatsData; diff --git a/data/mods/gen1/moves.js b/data/mods/gen1/moves.ts similarity index 96% rename from data/mods/gen1/moves.js rename to data/mods/gen1/moves.ts index bbb37f6965..3fb574cb24 100644 --- a/data/mods/gen1/moves.js +++ b/data/mods/gen1/moves.ts @@ -3,10 +3,7 @@ * Some moves have had major changes, such as Bite's typing. */ -'use strict'; - -/**@type {{[k: string]: ModdedMoveData}} */ -let BattleMovedex = { +export const BattleMovedex: {[k: string]: ModdedMoveData} = { absorb: { inherit: true, desc: "The user recovers 1/2 the HP lost by the target, rounded down. If this move breaks the target's substitute, the user does not recover any HP.", @@ -65,7 +62,7 @@ let BattleMovedex = { }, onHit(target, source, move) { if (source && source !== target && move.category !== 'Physical' && move.category !== 'Special') { - let damage = this.effectData.totalDamage; + const damage = this.effectData.totalDamage; this.effectData.totalDamage += damage; this.effectData.lastDamage = damage; this.effectData.sourcePosition = source.position; @@ -98,7 +95,7 @@ let BattleMovedex = { } } }, - onBeforeMove(pokemon, target, move) { + onBeforeMove(pokemon, t, move) { if (this.effectData.duration === 1) { if (!this.effectData.totalDamage) { this.debug("Bide failed due to 0 damage taken"); @@ -106,8 +103,8 @@ let BattleMovedex = { return false; } this.add('-end', pokemon, 'Bide'); - let target = this.effectData.sourceSide.active[this.effectData.sourcePosition]; - this.moveHit(target, pokemon, move, /** @type {ActiveMove} */ ({damage: this.effectData.totalDamage * 2})); + const target = this.effectData.sourceSide.active[this.effectData.sourcePosition]; + this.moveHit(target, pokemon, move, {damage: this.effectData.totalDamage * 2} as ActiveMove); return false; } this.add('-activate', pokemon, 'Bide'); @@ -254,8 +251,11 @@ let BattleMovedex = { // It will fail if the last move selected by the opponent has base power 0 or is not Normal or Fighting Type. // If both are true, counter will deal twice the last damage dealt in battle, no matter what was the move. // That means that, if opponent switches, counter will use last counter damage * 2. - let lastUsedMove = target.side.lastMove && this.dex.getMove(target.side.lastMove.id); - if (lastUsedMove && lastUsedMove.basePower > 0 && ['Normal', 'Fighting'].includes(lastUsedMove.type) && this.lastDamage > 0 && !this.queue.willMove(target)) { + const lastUsedMove = target.side.lastMove && this.dex.getMove(target.side.lastMove.id); + if ( + lastUsedMove && lastUsedMove.basePower > 0 && ['Normal', 'Fighting'].includes(lastUsedMove.type) && + this.lastDamage > 0 && !this.queue.willMove(target) + ) { return 2 * this.lastDamage; } this.debug("Gen 1 Counter failed due to conditions not met"); @@ -300,15 +300,15 @@ let BattleMovedex = { effect: { duration: 4, durationCallback(target, source, effect) { - let duration = this.random(1, 7); + const duration = this.random(1, 7); return duration; }, onStart(pokemon) { if (!this.queue.willMove(pokemon)) { this.effectData.duration++; } - let moves = pokemon.moves; - let move = this.dex.getMove(this.sample(moves)); + const moves = pokemon.moves; + const move = this.dex.getMove(this.sample(moves)); this.add('-start', pokemon, 'Disable', move.name); this.effectData.move = move.id; return; @@ -548,20 +548,20 @@ let BattleMovedex = { }, onAfterMoveSelfPriority: 1, onAfterMoveSelf(pokemon) { - let leecher = pokemon.side.foe.active[pokemon.volatiles['leechseed'].sourcePosition]; + const leecher = pokemon.side.foe.active[pokemon.volatiles['leechseed'].sourcePosition]; if (!leecher || leecher.fainted || leecher.hp <= 0) { this.debug('Nothing to leech into'); return; } // We check if leeched Pokémon has Toxic to increase leeched damage. let toxicCounter = 1; - let residualdmg = pokemon.volatiles['residualdmg']; + const residualdmg = pokemon.volatiles['residualdmg']; if (residualdmg) { residualdmg.counter++; toxicCounter = residualdmg.counter; } - let toLeech = this.dex.clampIntRange(Math.floor(pokemon.baseMaxhp / 16), 1) * toxicCounter; - let damage = this.damage(toLeech, pokemon, leecher); + const toLeech = this.dex.clampIntRange(Math.floor(pokemon.baseMaxhp / 16), 1) * toxicCounter; + const damage = this.damage(toLeech, pokemon, leecher); if (residualdmg) this.hint("In Gen 1, Leech Seed's damage is affected by Toxic's counter.", true); if (!damage || toLeech > damage) { this.hint("In Gen 1, Leech Seed recovery is not limited by the remaining HP of the seeded Pokemon.", true); @@ -610,12 +610,12 @@ let BattleMovedex = { desc: "While the user remains active, this move is replaced by a random move known by the target, even if the user already knows that move. The copied move keeps the remaining PP for this move, regardless of the copied move's maximum PP. Whenever one PP is used for a copied move, one PP is used for this move.", shortDesc: "Random move known by the target replaces this.", onHit(target, source) { - let moveslot = source.moves.indexOf('mimic'); + const moveslot = source.moves.indexOf('mimic'); if (moveslot < 0) return false; - let moves = target.moves; - let moveid = this.sample(moves); + const moves = target.moves; + const moveid = this.sample(moves); if (!moveid) return false; - let move = this.dex.getMove(moveid); + const move = this.dex.getMove(moveid); source.moveSlots[moveslot] = { move: move.name, id: move.id, @@ -637,7 +637,7 @@ let BattleMovedex = { inherit: true, desc: "The user uses the last move used by the target. Fails if the target has not made a move, or if the last move used was Mirror Move.", onHit(pokemon) { - let foe = pokemon.side.foe.active[0]; + const foe = pokemon.side.foe.active[0]; if (!foe || !foe.lastMove || foe.lastMove.id === 'mirrormove') { return false; } @@ -935,8 +935,11 @@ let BattleMovedex = { if (move.category === 'Status') { // In gen 1 it only blocks: // poison, confusion, secondary effect confusion, stat reducing moves and Leech Seed. - let SubBlocked = ['lockon', 'meanlook', 'mindreader', 'nightmare']; - if (move.status === 'psn' || move.status === 'tox' || (move.boosts && target !== source) || move.volatileStatus === 'confusion' || SubBlocked.includes(move.id)) { + const SubBlocked = ['lockon', 'meanlook', 'mindreader', 'nightmare']; + if ( + move.status === 'psn' || move.status === 'tox' || (move.boosts && target !== source) || + move.volatileStatus === 'confusion' || SubBlocked.includes(move.id) + ) { return false; } return; @@ -969,7 +972,7 @@ let BattleMovedex = { } this.runEvent('AfterSubDamage', target, source, move, uncappedDamage); // Add here counter damage - let lastAttackedBy = target.getLastAttackedBy(); + const lastAttackedBy = target.getLastAttackedBy(); if (!lastAttackedBy) { target.attackedBy.push({source: source, move: move.id, damage: uncappedDamage, thisTurn: true}); } else { @@ -1081,5 +1084,3 @@ let BattleMovedex = { }, }, }; - -exports.BattleMovedex = BattleMovedex; diff --git a/data/mods/gen1/pokedex.js b/data/mods/gen1/pokedex.ts similarity index 82% rename from data/mods/gen1/pokedex.js rename to data/mods/gen1/pokedex.ts index dc65e6370d..9f75ef73ae 100644 --- a/data/mods/gen1/pokedex.js +++ b/data/mods/gen1/pokedex.ts @@ -1,25 +1,19 @@ -'use strict'; - -exports.BattlePokedex = { +export const BattlePokedex: {[k: string]: ModdedSpeciesData} = { missingno: { - num: 0, + inherit: true, name: "MissingNo.", types: ["Bird", "Normal"], - gender: "N", baseStats: {hp: 33, atk: 136, def: 0, spa: 6, spd: 6, spe: 29}, - abilities: {}, heightm: 3, weightkg: 1590.8, color: "Gray", eggGroups: ["Undiscovered"], }, bulbasaur: { - num: 1, + inherit: true, name: "Bulbasaur", types: ["Grass", "Poison"], - gender: "N", baseStats: {hp: 45, atk: 49, def: 49, spa: 65, spd: 65, spe: 45}, - abilities: {}, heightm: 0.7, weightkg: 6.9, color: "Green", @@ -27,12 +21,10 @@ exports.BattlePokedex = { eggGroups: ["Monster", "Grass"], }, ivysaur: { - num: 2, + inherit: true, name: "Ivysaur", types: ["Grass", "Poison"], - gender: "N", baseStats: {hp: 60, atk: 62, def: 63, spa: 80, spd: 80, spe: 60}, - abilities: {}, heightm: 1, weightkg: 13, color: "Green", @@ -42,12 +34,10 @@ exports.BattlePokedex = { eggGroups: ["Monster", "Grass"], }, venusaur: { - num: 3, + inherit: true, name: "Venusaur", types: ["Grass", "Poison"], - gender: "N", baseStats: {hp: 80, atk: 82, def: 83, spa: 100, spd: 100, spe: 80}, - abilities: {}, heightm: 2, weightkg: 100, color: "Green", @@ -56,12 +46,10 @@ exports.BattlePokedex = { eggGroups: ["Monster", "Grass"], }, charmander: { - num: 4, + inherit: true, name: "Charmander", types: ["Fire"], - gender: "N", baseStats: {hp: 39, atk: 52, def: 43, spa: 50, spd: 50, spe: 65}, - abilities: {}, heightm: 0.6, weightkg: 8.5, color: "Red", @@ -69,12 +57,10 @@ exports.BattlePokedex = { eggGroups: ["Monster", "Dragon"], }, charmeleon: { - num: 5, + inherit: true, name: "Charmeleon", types: ["Fire"], - gender: "N", baseStats: {hp: 58, atk: 64, def: 58, spa: 65, spd: 65, spe: 80}, - abilities: {}, heightm: 1.1, weightkg: 19, color: "Red", @@ -84,12 +70,10 @@ exports.BattlePokedex = { eggGroups: ["Monster", "Dragon"], }, charizard: { - num: 6, + inherit: true, name: "Charizard", types: ["Fire", "Flying"], - gender: "N", baseStats: {hp: 78, atk: 84, def: 78, spa: 85, spd: 85, spe: 100}, - abilities: {}, heightm: 1.7, weightkg: 90.5, color: "Red", @@ -98,12 +82,10 @@ exports.BattlePokedex = { eggGroups: ["Monster", "Dragon"], }, squirtle: { - num: 7, + inherit: true, name: "Squirtle", types: ["Water"], - gender: "N", baseStats: {hp: 44, atk: 48, def: 65, spa: 50, spd: 50, spe: 43}, - abilities: {}, heightm: 0.5, weightkg: 9, color: "Blue", @@ -111,12 +93,10 @@ exports.BattlePokedex = { eggGroups: ["Monster", "Water 1"], }, wartortle: { - num: 8, + inherit: true, name: "Wartortle", types: ["Water"], - gender: "N", baseStats: {hp: 59, atk: 63, def: 80, spa: 65, spd: 65, spe: 58}, - abilities: {}, heightm: 1, weightkg: 22.5, color: "Blue", @@ -126,12 +106,10 @@ exports.BattlePokedex = { eggGroups: ["Monster", "Water 1"], }, blastoise: { - num: 9, + inherit: true, name: "Blastoise", types: ["Water"], - gender: "N", baseStats: {hp: 79, atk: 83, def: 100, spa: 85, spd: 85, spe: 78}, - abilities: {}, heightm: 1.6, weightkg: 85.5, color: "Blue", @@ -140,12 +118,10 @@ exports.BattlePokedex = { eggGroups: ["Monster", "Water 1"], }, caterpie: { - num: 10, + inherit: true, name: "Caterpie", types: ["Bug"], - gender: "N", baseStats: {hp: 45, atk: 30, def: 35, spa: 20, spd: 20, spe: 45}, - abilities: {}, heightm: 0.3, weightkg: 2.9, color: "Green", @@ -153,12 +129,10 @@ exports.BattlePokedex = { eggGroups: ["Bug"], }, metapod: { - num: 11, + inherit: true, name: "Metapod", types: ["Bug"], - gender: "N", baseStats: {hp: 50, atk: 20, def: 55, spa: 25, spd: 25, spe: 30}, - abilities: {}, heightm: 0.7, weightkg: 9.9, color: "Green", @@ -168,12 +142,10 @@ exports.BattlePokedex = { eggGroups: ["Bug"], }, butterfree: { - num: 12, + inherit: true, name: "Butterfree", types: ["Bug", "Flying"], - gender: "N", baseStats: {hp: 60, atk: 45, def: 50, spa: 80, spd: 80, spe: 70}, - abilities: {}, heightm: 1.1, weightkg: 32, color: "White", @@ -182,12 +154,10 @@ exports.BattlePokedex = { eggGroups: ["Bug"], }, weedle: { - num: 13, + inherit: true, name: "Weedle", types: ["Bug", "Poison"], - gender: "N", baseStats: {hp: 40, atk: 35, def: 30, spa: 20, spd: 20, spe: 50}, - abilities: {}, heightm: 0.3, weightkg: 3.2, color: "Brown", @@ -195,12 +165,10 @@ exports.BattlePokedex = { eggGroups: ["Bug"], }, kakuna: { - num: 14, + inherit: true, name: "Kakuna", types: ["Bug", "Poison"], - gender: "N", baseStats: {hp: 45, atk: 25, def: 50, spa: 25, spd: 25, spe: 35}, - abilities: {}, heightm: 0.6, weightkg: 10, color: "Yellow", @@ -210,12 +178,10 @@ exports.BattlePokedex = { eggGroups: ["Bug"], }, beedrill: { - num: 15, + inherit: true, name: "Beedrill", types: ["Bug", "Poison"], - gender: "N", baseStats: {hp: 65, atk: 80, def: 40, spa: 45, spd: 45, spe: 75}, - abilities: {}, heightm: 1, weightkg: 29.5, color: "Yellow", @@ -224,12 +190,10 @@ exports.BattlePokedex = { eggGroups: ["Bug"], }, pidgey: { - num: 16, + inherit: true, name: "Pidgey", types: ["Normal", "Flying"], - gender: "N", baseStats: {hp: 40, atk: 45, def: 40, spa: 35, spd: 35, spe: 56}, - abilities: {}, heightm: 0.3, weightkg: 1.8, color: "Brown", @@ -237,12 +201,10 @@ exports.BattlePokedex = { eggGroups: ["Flying"], }, pidgeotto: { - num: 17, + inherit: true, name: "Pidgeotto", types: ["Normal", "Flying"], - gender: "N", baseStats: {hp: 63, atk: 60, def: 55, spa: 50, spd: 50, spe: 71}, - abilities: {}, heightm: 1.1, weightkg: 30, color: "Brown", @@ -252,12 +214,10 @@ exports.BattlePokedex = { eggGroups: ["Flying"], }, pidgeot: { - num: 18, + inherit: true, name: "Pidgeot", types: ["Normal", "Flying"], - gender: "N", baseStats: {hp: 83, atk: 80, def: 75, spa: 70, spd: 70, spe: 91}, - abilities: {}, heightm: 1.5, weightkg: 39.5, color: "Brown", @@ -266,12 +226,10 @@ exports.BattlePokedex = { eggGroups: ["Flying"], }, rattata: { - num: 19, + inherit: true, name: "Rattata", types: ["Normal"], - gender: "N", baseStats: {hp: 30, atk: 56, def: 35, spa: 25, spd: 25, spe: 72}, - abilities: {}, heightm: 0.3, weightkg: 3.5, color: "Purple", @@ -279,12 +237,10 @@ exports.BattlePokedex = { eggGroups: ["Field"], }, raticate: { - num: 20, + inherit: true, name: "Raticate", types: ["Normal"], - gender: "N", baseStats: {hp: 55, atk: 81, def: 60, spa: 50, spd: 50, spe: 97}, - abilities: {}, heightm: 0.7, weightkg: 18.5, color: "Brown", @@ -293,12 +249,10 @@ exports.BattlePokedex = { eggGroups: ["Field"], }, spearow: { - num: 21, + inherit: true, name: "Spearow", types: ["Normal", "Flying"], - gender: "N", baseStats: {hp: 40, atk: 60, def: 30, spa: 31, spd: 31, spe: 70}, - abilities: {}, heightm: 0.3, weightkg: 2, color: "Brown", @@ -306,12 +260,10 @@ exports.BattlePokedex = { eggGroups: ["Flying"], }, fearow: { - num: 22, + inherit: true, name: "Fearow", types: ["Normal", "Flying"], - gender: "N", baseStats: {hp: 65, atk: 90, def: 65, spa: 61, spd: 61, spe: 100}, - abilities: {}, heightm: 1.2, weightkg: 38, color: "Brown", @@ -320,12 +272,10 @@ exports.BattlePokedex = { eggGroups: ["Flying"], }, ekans: { - num: 23, + inherit: true, name: "Ekans", types: ["Poison"], - gender: "N", baseStats: {hp: 35, atk: 60, def: 44, spa: 40, spd: 40, spe: 55}, - abilities: {}, heightm: 2, weightkg: 6.9, color: "Purple", @@ -333,12 +283,10 @@ exports.BattlePokedex = { eggGroups: ["Field", "Dragon"], }, arbok: { - num: 24, + inherit: true, name: "Arbok", types: ["Poison"], - gender: "N", baseStats: {hp: 60, atk: 85, def: 69, spa: 65, spd: 65, spe: 80}, - abilities: {}, heightm: 3.5, weightkg: 65, color: "Purple", @@ -347,40 +295,34 @@ exports.BattlePokedex = { eggGroups: ["Field", "Dragon"], }, pikachu: { - num: 25, + inherit: true, name: "Pikachu", types: ["Electric"], - gender: "N", baseStats: {hp: 35, atk: 55, def: 30, spa: 50, spd: 50, spe: 90}, - abilities: {}, heightm: 0.4, weightkg: 6, color: "Yellow", - prevo: "pichu", evos: ["raichu"], eggGroups: ["Field", "Fairy"], }, raichu: { - num: 26, + inherit: true, name: "Raichu", types: ["Electric"], - gender: "N", baseStats: {hp: 60, atk: 90, def: 55, spa: 90, spd: 90, spe: 100}, - abilities: {}, heightm: 0.8, weightkg: 30, color: "Yellow", prevo: "pikachu", - evoLevel: 1, + evoType: "useItem", + evoItem: "Thunder Stone", eggGroups: ["Field", "Fairy"], }, sandshrew: { - num: 27, + inherit: true, name: "Sandshrew", types: ["Ground"], - gender: "N", baseStats: {hp: 50, atk: 75, def: 85, spa: 30, spd: 30, spe: 40}, - abilities: {}, heightm: 0.6, weightkg: 12, color: "Yellow", @@ -388,12 +330,10 @@ exports.BattlePokedex = { eggGroups: ["Field"], }, sandslash: { - num: 28, + inherit: true, name: "Sandslash", types: ["Ground"], - gender: "N", baseStats: {hp: 75, atk: 100, def: 110, spa: 55, spd: 55, spe: 65}, - abilities: {}, heightm: 1, weightkg: 29.5, color: "Yellow", @@ -402,12 +342,10 @@ exports.BattlePokedex = { eggGroups: ["Field"], }, nidoranf: { - num: 29, + inherit: true, name: "Nidoran-F", types: ["Poison"], - gender: "N", baseStats: {hp: 55, atk: 47, def: 52, spa: 40, spd: 40, spe: 41}, - abilities: {}, heightm: 0.4, weightkg: 7, color: "Blue", @@ -415,12 +353,10 @@ exports.BattlePokedex = { eggGroups: ["Monster", "Field"], }, nidorina: { - num: 30, + inherit: true, name: "Nidorina", types: ["Poison"], - gender: "N", baseStats: {hp: 70, atk: 62, def: 67, spa: 55, spd: 55, spe: 56}, - abilities: {}, heightm: 0.8, weightkg: 20, color: "Blue", @@ -430,26 +366,23 @@ exports.BattlePokedex = { eggGroups: ["Undiscovered"], }, nidoqueen: { - num: 31, + inherit: true, name: "Nidoqueen", types: ["Poison", "Ground"], - gender: "N", baseStats: {hp: 90, atk: 82, def: 87, spa: 75, spd: 75, spe: 76}, - abilities: {}, heightm: 1.3, weightkg: 60, color: "Blue", prevo: "nidorina", - evoLevel: 16, + evoType: "useItem", + evoItem: "Moon Stone", eggGroups: ["Undiscovered"], }, nidoranm: { - num: 32, + inherit: true, name: "Nidoran-M", types: ["Poison"], - gender: "N", baseStats: {hp: 46, atk: 57, def: 40, spa: 40, spd: 40, spe: 50}, - abilities: {}, heightm: 0.5, weightkg: 9, color: "Purple", @@ -457,12 +390,10 @@ exports.BattlePokedex = { eggGroups: ["Monster", "Field"], }, nidorino: { - num: 33, + inherit: true, name: "Nidorino", types: ["Poison"], - gender: "N", baseStats: {hp: 61, atk: 72, def: 57, spa: 55, spd: 55, spe: 65}, - abilities: {}, heightm: 0.9, weightkg: 19.5, color: "Purple", @@ -472,26 +403,23 @@ exports.BattlePokedex = { eggGroups: ["Monster", "Field"], }, nidoking: { - num: 34, + inherit: true, name: "Nidoking", types: ["Poison", "Ground"], - gender: "N", baseStats: {hp: 81, atk: 92, def: 77, spa: 75, spd: 75, spe: 85}, - abilities: {}, heightm: 1.4, weightkg: 62, color: "Purple", prevo: "nidorino", - evoLevel: 16, + evoType: "useItem", + evoItem: "Moon Stone", eggGroups: ["Monster", "Field"], }, clefairy: { - num: 35, + inherit: true, name: "Clefairy", types: ["Normal"], - gender: "N", baseStats: {hp: 70, atk: 45, def: 48, spa: 60, spd: 60, spe: 35}, - abilities: {}, heightm: 0.6, weightkg: 7.5, color: "Pink", @@ -500,26 +428,23 @@ exports.BattlePokedex = { eggGroups: ["Fairy"], }, clefable: { - num: 36, + inherit: true, name: "Clefable", types: ["Normal"], - gender: "N", baseStats: {hp: 95, atk: 70, def: 73, spa: 85, spd: 85, spe: 60}, - abilities: {}, heightm: 1.3, weightkg: 40, color: "Pink", prevo: "clefairy", - evoLevel: 1, + evoType: "useItem", + evoItem: "Moon Stone", eggGroups: ["Fairy"], }, vulpix: { - num: 37, + inherit: true, name: "Vulpix", types: ["Fire"], - gender: "N", baseStats: {hp: 38, atk: 41, def: 40, spa: 65, spd: 65, spe: 65}, - abilities: {}, heightm: 0.6, weightkg: 9.9, color: "Brown", @@ -527,54 +452,47 @@ exports.BattlePokedex = { eggGroups: ["Field"], }, ninetales: { - num: 38, + inherit: true, name: "Ninetales", types: ["Fire"], - gender: "N", baseStats: {hp: 73, atk: 76, def: 75, spa: 100, spd: 100, spe: 100}, - abilities: {}, heightm: 1.1, weightkg: 19.9, color: "Yellow", prevo: "vulpix", - evoLevel: 1, + evoType: "useItem", + evoItem: "Fire Stone", eggGroups: ["Field"], }, jigglypuff: { - num: 39, + inherit: true, name: "Jigglypuff", types: ["Normal"], - gender: "N", baseStats: {hp: 115, atk: 45, def: 20, spa: 25, spd: 25, spe: 20}, - abilities: {}, heightm: 0.5, weightkg: 5.5, color: "Pink", - prevo: "igglybuff", evos: ["wigglytuff"], eggGroups: ["Fairy"], }, wigglytuff: { - num: 40, + inherit: true, name: "Wigglytuff", types: ["Normal"], - gender: "N", baseStats: {hp: 140, atk: 70, def: 45, spa: 50, spd: 50, spe: 45}, - abilities: {}, heightm: 1, weightkg: 12, color: "Pink", prevo: "jigglypuff", - evoLevel: 1, + evoType: "useItem", + evoItem: "Moon Stone", eggGroups: ["Fairy"], }, zubat: { - num: 41, + inherit: true, name: "Zubat", types: ["Poison", "Flying"], - gender: "N", baseStats: {hp: 40, atk: 45, def: 35, spa: 40, spd: 40, spe: 55}, - abilities: {}, heightm: 0.8, weightkg: 7.5, color: "Purple", @@ -582,27 +500,22 @@ exports.BattlePokedex = { eggGroups: ["Flying"], }, golbat: { - num: 42, + inherit: true, name: "Golbat", types: ["Poison", "Flying"], - gender: "N", baseStats: {hp: 75, atk: 80, def: 70, spa: 75, spd: 75, spe: 90}, - abilities: {}, heightm: 1.6, weightkg: 55, color: "Purple", prevo: "zubat", - evos: ["crobat"], evoLevel: 22, eggGroups: ["Flying"], }, oddish: { - num: 43, + inherit: true, name: "Oddish", types: ["Grass", "Poison"], - gender: "N", baseStats: {hp: 45, atk: 50, def: 55, spa: 75, spd: 75, spe: 30}, - abilities: {}, heightm: 0.5, weightkg: 5.4, color: "Blue", @@ -610,41 +523,36 @@ exports.BattlePokedex = { eggGroups: ["Grass"], }, gloom: { - num: 44, + inherit: true, name: "Gloom", types: ["Grass", "Poison"], - gender: "N", baseStats: {hp: 60, atk: 65, def: 70, spa: 85, spd: 85, spe: 40}, - abilities: {}, heightm: 0.8, weightkg: 8.6, color: "Blue", prevo: "oddish", - evos: ["vileplume", "bellossom"], + evos: ["vileplume"], evoLevel: 21, eggGroups: ["Grass"], }, vileplume: { - num: 45, + inherit: true, name: "Vileplume", types: ["Grass", "Poison"], - gender: "N", baseStats: {hp: 75, atk: 80, def: 85, spa: 100, spd: 100, spe: 50}, - abilities: {}, heightm: 1.2, weightkg: 18.6, color: "Red", prevo: "gloom", - evoLevel: 21, + evoType: "useItem", + evoItem: "Leaf Stone", eggGroups: ["Grass"], }, paras: { - num: 46, + inherit: true, name: "Paras", types: ["Bug", "Grass"], - gender: "N", baseStats: {hp: 35, atk: 70, def: 55, spa: 55, spd: 55, spe: 25}, - abilities: {}, heightm: 0.3, weightkg: 5.4, color: "Red", @@ -652,12 +560,10 @@ exports.BattlePokedex = { eggGroups: ["Bug", "Grass"], }, parasect: { - num: 47, + inherit: true, name: "Parasect", types: ["Bug", "Grass"], - gender: "N", baseStats: {hp: 60, atk: 95, def: 80, spa: 80, spd: 80, spe: 30}, - abilities: {}, heightm: 1, weightkg: 29.5, color: "Red", @@ -666,12 +572,10 @@ exports.BattlePokedex = { eggGroups: ["Bug", "Grass"], }, venonat: { - num: 48, + inherit: true, name: "Venonat", types: ["Bug", "Poison"], - gender: "N", baseStats: {hp: 60, atk: 55, def: 50, spa: 40, spd: 40, spe: 45}, - abilities: {}, heightm: 1, weightkg: 30, color: "Purple", @@ -679,12 +583,10 @@ exports.BattlePokedex = { eggGroups: ["Bug"], }, venomoth: { - num: 49, + inherit: true, name: "Venomoth", types: ["Bug", "Poison"], - gender: "N", baseStats: {hp: 70, atk: 65, def: 60, spa: 90, spd: 90, spe: 90}, - abilities: {}, heightm: 1.5, weightkg: 12.5, color: "Purple", @@ -693,12 +595,10 @@ exports.BattlePokedex = { eggGroups: ["Bug"], }, diglett: { - num: 50, + inherit: true, name: "Diglett", types: ["Ground"], - gender: "N", baseStats: {hp: 10, atk: 55, def: 25, spa: 45, spd: 45, spe: 95}, - abilities: {}, heightm: 0.2, weightkg: 0.8, color: "Brown", @@ -706,12 +606,10 @@ exports.BattlePokedex = { eggGroups: ["Field"], }, dugtrio: { - num: 51, + inherit: true, name: "Dugtrio", types: ["Ground"], - gender: "N", baseStats: {hp: 35, atk: 80, def: 50, spa: 70, spd: 70, spe: 120}, - abilities: {}, heightm: 0.7, weightkg: 33.3, color: "Brown", @@ -720,12 +618,10 @@ exports.BattlePokedex = { eggGroups: ["Field"], }, meowth: { - num: 52, + inherit: true, name: "Meowth", types: ["Normal"], - gender: "N", baseStats: {hp: 40, atk: 45, def: 35, spa: 40, spd: 40, spe: 90}, - abilities: {}, heightm: 0.4, weightkg: 4.2, color: "Yellow", @@ -733,12 +629,10 @@ exports.BattlePokedex = { eggGroups: ["Field"], }, persian: { - num: 53, + inherit: true, name: "Persian", types: ["Normal"], - gender: "N", baseStats: {hp: 65, atk: 70, def: 60, spa: 65, spd: 65, spe: 115}, - abilities: {}, heightm: 1, weightkg: 32, color: "Yellow", @@ -747,12 +641,10 @@ exports.BattlePokedex = { eggGroups: ["Field"], }, psyduck: { - num: 54, + inherit: true, name: "Psyduck", types: ["Water"], - gender: "N", baseStats: {hp: 50, atk: 52, def: 48, spa: 50, spd: 50, spe: 55}, - abilities: {}, heightm: 0.8, weightkg: 19.6, color: "Yellow", @@ -760,12 +652,10 @@ exports.BattlePokedex = { eggGroups: ["Water 1", "Field"], }, golduck: { - num: 55, + inherit: true, name: "Golduck", types: ["Water"], - gender: "N", baseStats: {hp: 80, atk: 82, def: 78, spa: 80, spd: 80, spe: 85}, - abilities: {}, heightm: 1.7, weightkg: 76.6, color: "Blue", @@ -774,12 +664,10 @@ exports.BattlePokedex = { eggGroups: ["Water 1", "Field"], }, mankey: { - num: 56, + inherit: true, name: "Mankey", types: ["Fighting"], - gender: "N", baseStats: {hp: 40, atk: 80, def: 35, spa: 35, spd: 35, spe: 70}, - abilities: {}, heightm: 0.5, weightkg: 28, color: "Brown", @@ -787,12 +675,10 @@ exports.BattlePokedex = { eggGroups: ["Field"], }, primeape: { - num: 57, + inherit: true, name: "Primeape", types: ["Fighting"], - gender: "N", baseStats: {hp: 65, atk: 105, def: 60, spa: 60, spd: 60, spe: 95}, - abilities: {}, heightm: 1, weightkg: 32, color: "Brown", @@ -801,12 +687,10 @@ exports.BattlePokedex = { eggGroups: ["Field"], }, growlithe: { - num: 58, + inherit: true, name: "Growlithe", types: ["Fire"], - gender: "N", baseStats: {hp: 55, atk: 70, def: 45, spa: 50, spd: 50, spe: 60}, - abilities: {}, heightm: 0.7, weightkg: 19, color: "Brown", @@ -814,26 +698,23 @@ exports.BattlePokedex = { eggGroups: ["Field"], }, arcanine: { - num: 59, + inherit: true, name: "Arcanine", types: ["Fire"], - gender: "N", baseStats: {hp: 90, atk: 110, def: 80, spa: 80, spd: 80, spe: 95}, - abilities: {}, heightm: 1.9, weightkg: 155, color: "Brown", prevo: "growlithe", - evoLevel: 1, + evoType: "useItem", + evoItem: "Fire Stone", eggGroups: ["Field"], }, poliwag: { - num: 60, + inherit: true, name: "Poliwag", types: ["Water"], - gender: "N", baseStats: {hp: 40, atk: 50, def: 40, spa: 40, spd: 40, spe: 90}, - abilities: {}, heightm: 0.6, weightkg: 12.4, color: "Blue", @@ -841,41 +722,36 @@ exports.BattlePokedex = { eggGroups: ["Water 1"], }, poliwhirl: { - num: 61, + inherit: true, name: "Poliwhirl", types: ["Water"], - gender: "N", baseStats: {hp: 65, atk: 65, def: 65, spa: 50, spd: 50, spe: 90}, - abilities: {}, heightm: 1, weightkg: 20, color: "Blue", prevo: "poliwag", - evos: ["poliwrath", "politoed"], + evos: ["poliwrath"], evoLevel: 25, eggGroups: ["Water 1"], }, poliwrath: { - num: 62, + inherit: true, name: "Poliwrath", types: ["Water", "Fighting"], - gender: "N", baseStats: {hp: 90, atk: 85, def: 95, spa: 70, spd: 70, spe: 70}, - abilities: {}, heightm: 1.3, weightkg: 54, color: "Blue", prevo: "poliwhirl", - evoLevel: 25, + evoType: "useItem", + evoItem: "Water Stone", eggGroups: ["Water 1"], }, abra: { - num: 63, + inherit: true, name: "Abra", types: ["Psychic"], - gender: "N", baseStats: {hp: 25, atk: 20, def: 15, spa: 105, spd: 105, spe: 90}, - abilities: {}, heightm: 0.9, weightkg: 19.5, color: "Brown", @@ -883,12 +759,10 @@ exports.BattlePokedex = { eggGroups: ["Human-Like"], }, kadabra: { - num: 64, + inherit: true, name: "Kadabra", types: ["Psychic"], - gender: "N", baseStats: {hp: 40, atk: 35, def: 30, spa: 120, spd: 120, spe: 105}, - abilities: {}, heightm: 1.3, weightkg: 56.5, color: "Brown", @@ -898,26 +772,22 @@ exports.BattlePokedex = { eggGroups: ["Human-Like"], }, alakazam: { - num: 65, + inherit: true, name: "Alakazam", types: ["Psychic"], - gender: "N", baseStats: {hp: 55, atk: 50, def: 45, spa: 135, spd: 135, spe: 120}, - abilities: {}, heightm: 1.5, weightkg: 48, color: "Brown", prevo: "kadabra", - evoLevel: 16, + evoType: "trade", eggGroups: ["Human-Like"], }, machop: { - num: 66, + inherit: true, name: "Machop", types: ["Fighting"], - gender: "N", baseStats: {hp: 70, atk: 80, def: 50, spa: 35, spd: 35, spe: 35}, - abilities: {}, heightm: 0.8, weightkg: 19.5, color: "Gray", @@ -925,12 +795,10 @@ exports.BattlePokedex = { eggGroups: ["Human-Like"], }, machoke: { - num: 67, + inherit: true, name: "Machoke", types: ["Fighting"], - gender: "N", baseStats: {hp: 80, atk: 100, def: 70, spa: 50, spd: 50, spe: 45}, - abilities: {}, heightm: 1.5, weightkg: 70.5, color: "Gray", @@ -940,26 +808,22 @@ exports.BattlePokedex = { eggGroups: ["Human-Like"], }, machamp: { - num: 68, + inherit: true, name: "Machamp", types: ["Fighting"], - gender: "N", baseStats: {hp: 90, atk: 130, def: 80, spa: 65, spd: 65, spe: 55}, - abilities: {}, heightm: 1.6, weightkg: 130, color: "Gray", prevo: "machoke", - evoLevel: 28, + evoType: "trade", eggGroups: ["Human-Like"], }, bellsprout: { - num: 69, + inherit: true, name: "Bellsprout", types: ["Grass", "Poison"], - gender: "N", baseStats: {hp: 50, atk: 75, def: 35, spa: 70, spd: 70, spe: 40}, - abilities: {}, heightm: 0.7, weightkg: 4, color: "Green", @@ -967,12 +831,10 @@ exports.BattlePokedex = { eggGroups: ["Grass"], }, weepinbell: { - num: 70, + inherit: true, name: "Weepinbell", types: ["Grass", "Poison"], - gender: "N", baseStats: {hp: 65, atk: 90, def: 50, spa: 85, spd: 85, spe: 55}, - abilities: {}, heightm: 1, weightkg: 6.4, color: "Green", @@ -982,26 +844,23 @@ exports.BattlePokedex = { eggGroups: ["Grass"], }, victreebel: { - num: 71, + inherit: true, name: "Victreebel", types: ["Grass", "Poison"], - gender: "N", baseStats: {hp: 80, atk: 105, def: 65, spa: 100, spd: 100, spe: 70}, - abilities: {}, heightm: 1.7, weightkg: 15.5, color: "Green", prevo: "weepinbell", - evoLevel: 21, + evoType: "useItem", + evoItem: "Leaf Stone", eggGroups: ["Grass"], }, tentacool: { - num: 72, + inherit: true, name: "Tentacool", types: ["Water", "Poison"], - gender: "N", baseStats: {hp: 40, atk: 40, def: 35, spa: 100, spd: 100, spe: 70}, - abilities: {}, heightm: 0.9, weightkg: 45.5, color: "Blue", @@ -1009,12 +868,10 @@ exports.BattlePokedex = { eggGroups: ["Water 3"], }, tentacruel: { - num: 73, + inherit: true, name: "Tentacruel", types: ["Water", "Poison"], - gender: "N", baseStats: {hp: 80, atk: 70, def: 65, spa: 120, spd: 120, spe: 100}, - abilities: {}, heightm: 1.6, weightkg: 55, color: "Blue", @@ -1023,12 +880,10 @@ exports.BattlePokedex = { eggGroups: ["Water 3"], }, geodude: { - num: 74, + inherit: true, name: "Geodude", types: ["Rock", "Ground"], - gender: "N", baseStats: {hp: 40, atk: 80, def: 100, spa: 30, spd: 30, spe: 20}, - abilities: {}, heightm: 0.4, weightkg: 20, color: "Brown", @@ -1036,12 +891,10 @@ exports.BattlePokedex = { eggGroups: ["Mineral"], }, graveler: { - num: 75, + inherit: true, name: "Graveler", types: ["Rock", "Ground"], - gender: "N", baseStats: {hp: 55, atk: 95, def: 115, spa: 45, spd: 45, spe: 35}, - abilities: {}, heightm: 1, weightkg: 105, color: "Brown", @@ -1051,26 +904,22 @@ exports.BattlePokedex = { eggGroups: ["Mineral"], }, golem: { - num: 76, + inherit: true, name: "Golem", types: ["Rock", "Ground"], - gender: "N", baseStats: {hp: 80, atk: 110, def: 130, spa: 55, spd: 55, spe: 45}, - abilities: {}, heightm: 1.4, weightkg: 300, color: "Brown", prevo: "graveler", - evoLevel: 25, + evoType: "trade", eggGroups: ["Mineral"], }, ponyta: { - num: 77, + inherit: true, name: "Ponyta", types: ["Fire"], - gender: "N", baseStats: {hp: 50, atk: 85, def: 55, spa: 65, spd: 65, spe: 90}, - abilities: {}, heightm: 1, weightkg: 30, color: "Yellow", @@ -1078,12 +927,10 @@ exports.BattlePokedex = { eggGroups: ["Field"], }, rapidash: { - num: 78, + inherit: true, name: "Rapidash", types: ["Fire"], - gender: "N", baseStats: {hp: 65, atk: 100, def: 70, spa: 80, spd: 80, spe: 105}, - abilities: {}, heightm: 1.7, weightkg: 95, color: "Yellow", @@ -1092,25 +939,21 @@ exports.BattlePokedex = { eggGroups: ["Field"], }, slowpoke: { - num: 79, + inherit: true, name: "Slowpoke", types: ["Water", "Psychic"], - gender: "N", baseStats: {hp: 90, atk: 65, def: 65, spa: 40, spd: 40, spe: 15}, - abilities: {}, heightm: 1.2, weightkg: 36, color: "Pink", - evos: ["slowbro", "slowking"], + evos: ["slowbro"], eggGroups: ["Monster", "Water 1"], }, slowbro: { - num: 80, + inherit: true, name: "Slowbro", types: ["Water", "Psychic"], - gender: "N", baseStats: {hp: 95, atk: 75, def: 110, spa: 80, spd: 80, spe: 30}, - abilities: {}, heightm: 1.6, weightkg: 78.5, color: "Pink", @@ -1119,12 +962,10 @@ exports.BattlePokedex = { eggGroups: ["Monster", "Water 1"], }, magnemite: { - num: 81, + inherit: true, name: "Magnemite", types: ["Electric"], - gender: "N", baseStats: {hp: 25, atk: 35, def: 70, spa: 95, spd: 95, spe: 45}, - abilities: {}, heightm: 0.3, weightkg: 6, color: "Gray", @@ -1132,39 +973,32 @@ exports.BattlePokedex = { eggGroups: ["Mineral"], }, magneton: { - num: 82, + inherit: true, name: "Magneton", types: ["Electric"], - gender: "N", baseStats: {hp: 50, atk: 60, def: 95, spa: 120, spd: 120, spe: 70}, - abilities: {}, heightm: 1, weightkg: 60, color: "Gray", prevo: "magnemite", - evos: ["magnezone"], evoLevel: 30, eggGroups: ["Mineral"], }, farfetchd: { - num: 83, + inherit: true, name: "Farfetch'd", types: ["Normal", "Flying"], - gender: "N", baseStats: {hp: 52, atk: 65, def: 55, spa: 58, spd: 58, spe: 60}, - abilities: {}, heightm: 0.8, weightkg: 15, color: "Brown", eggGroups: ["Flying", "Field"], }, doduo: { - num: 84, + inherit: true, name: "Doduo", types: ["Normal", "Flying"], - gender: "N", baseStats: {hp: 35, atk: 85, def: 45, spa: 35, spd: 35, spe: 75}, - abilities: {}, heightm: 1.4, weightkg: 39.2, color: "Brown", @@ -1172,12 +1006,10 @@ exports.BattlePokedex = { eggGroups: ["Flying"], }, dodrio: { - num: 85, + inherit: true, name: "Dodrio", types: ["Normal", "Flying"], - gender: "N", baseStats: {hp: 60, atk: 110, def: 70, spa: 60, spd: 60, spe: 100}, - abilities: {}, heightm: 1.8, weightkg: 85.2, color: "Brown", @@ -1186,12 +1018,10 @@ exports.BattlePokedex = { eggGroups: ["Flying"], }, seel: { - num: 86, + inherit: true, name: "Seel", types: ["Water"], - gender: "N", baseStats: {hp: 65, atk: 45, def: 55, spa: 70, spd: 70, spe: 45}, - abilities: {}, heightm: 1.1, weightkg: 90, color: "White", @@ -1199,12 +1029,10 @@ exports.BattlePokedex = { eggGroups: ["Water 1", "Field"], }, dewgong: { - num: 87, + inherit: true, name: "Dewgong", types: ["Water", "Ice"], - gender: "N", baseStats: {hp: 90, atk: 70, def: 80, spa: 95, spd: 95, spe: 70}, - abilities: {}, heightm: 1.7, weightkg: 120, color: "White", @@ -1213,12 +1041,10 @@ exports.BattlePokedex = { eggGroups: ["Water 1", "Field"], }, grimer: { - num: 88, + inherit: true, name: "Grimer", types: ["Poison"], - gender: "N", baseStats: {hp: 80, atk: 80, def: 50, spa: 40, spd: 40, spe: 25}, - abilities: {}, heightm: 0.9, weightkg: 30, color: "Purple", @@ -1226,12 +1052,10 @@ exports.BattlePokedex = { eggGroups: ["Amorphous"], }, muk: { - num: 89, + inherit: true, name: "Muk", types: ["Poison"], - gender: "N", baseStats: {hp: 105, atk: 105, def: 75, spa: 65, spd: 65, spe: 50}, - abilities: {}, heightm: 1.2, weightkg: 30, color: "Purple", @@ -1240,12 +1064,10 @@ exports.BattlePokedex = { eggGroups: ["Amorphous"], }, shellder: { - num: 90, + inherit: true, name: "Shellder", types: ["Water"], - gender: "N", baseStats: {hp: 30, atk: 65, def: 100, spa: 45, spd: 45, spe: 40}, - abilities: {}, heightm: 0.3, weightkg: 4, color: "Purple", @@ -1253,26 +1075,23 @@ exports.BattlePokedex = { eggGroups: ["Water 3"], }, cloyster: { - num: 91, + inherit: true, name: "Cloyster", types: ["Water", "Ice"], - gender: "N", baseStats: {hp: 50, atk: 95, def: 180, spa: 85, spd: 85, spe: 70}, - abilities: {}, heightm: 1.5, weightkg: 132.5, color: "Purple", prevo: "shellder", - evoLevel: 1, + evoType: "useItem", + evoItem: "Water Stone", eggGroups: ["Water 3"], }, gastly: { - num: 92, + inherit: true, name: "Gastly", types: ["Ghost", "Poison"], - gender: "N", baseStats: {hp: 30, atk: 35, def: 30, spa: 100, spd: 100, spe: 80}, - abilities: {}, heightm: 1.3, weightkg: 0.1, color: "Purple", @@ -1280,12 +1099,10 @@ exports.BattlePokedex = { eggGroups: ["Amorphous"], }, haunter: { - num: 93, + inherit: true, name: "Haunter", types: ["Ghost", "Poison"], - gender: "N", baseStats: {hp: 45, atk: 50, def: 45, spa: 115, spd: 115, spe: 95}, - abilities: {}, heightm: 1.6, weightkg: 0.1, color: "Purple", @@ -1295,39 +1112,32 @@ exports.BattlePokedex = { eggGroups: ["Amorphous"], }, gengar: { - num: 94, + inherit: true, name: "Gengar", types: ["Ghost", "Poison"], - gender: "N", baseStats: {hp: 60, atk: 65, def: 60, spa: 130, spd: 130, spe: 110}, - abilities: {}, heightm: 1.5, weightkg: 40.5, color: "Purple", prevo: "haunter", - evoLevel: 25, + evoType: "trade", eggGroups: ["Amorphous"], }, onix: { - num: 95, + inherit: true, name: "Onix", types: ["Rock", "Ground"], - gender: "N", baseStats: {hp: 35, atk: 45, def: 160, spa: 30, spd: 30, spe: 70}, - abilities: {}, heightm: 8.8, weightkg: 210, color: "Gray", - evos: ["steelix"], eggGroups: ["Mineral"], }, drowzee: { - num: 96, + inherit: true, name: "Drowzee", types: ["Psychic"], - gender: "N", baseStats: {hp: 60, atk: 48, def: 45, spa: 90, spd: 90, spe: 42}, - abilities: {}, heightm: 1, weightkg: 32.4, color: "Yellow", @@ -1335,12 +1145,10 @@ exports.BattlePokedex = { eggGroups: ["Human-Like"], }, hypno: { - num: 97, + inherit: true, name: "Hypno", types: ["Psychic"], - gender: "N", baseStats: {hp: 85, atk: 73, def: 70, spa: 115, spd: 115, spe: 67}, - abilities: {}, heightm: 1.6, weightkg: 75.6, color: "Yellow", @@ -1349,12 +1157,10 @@ exports.BattlePokedex = { eggGroups: ["Human-Like"], }, krabby: { - num: 98, + inherit: true, name: "Krabby", types: ["Water"], - gender: "N", baseStats: {hp: 30, atk: 105, def: 90, spa: 25, spd: 25, spe: 50}, - abilities: {}, heightm: 0.4, weightkg: 6.5, color: "Red", @@ -1362,12 +1168,10 @@ exports.BattlePokedex = { eggGroups: ["Water 3"], }, kingler: { - num: 99, + inherit: true, name: "Kingler", types: ["Water"], - gender: "N", baseStats: {hp: 55, atk: 130, def: 115, spa: 50, spd: 50, spe: 75}, - abilities: {}, heightm: 1.3, weightkg: 60, color: "Red", @@ -1376,12 +1180,10 @@ exports.BattlePokedex = { eggGroups: ["Water 3"], }, voltorb: { - num: 100, + inherit: true, name: "Voltorb", types: ["Electric"], - gender: "N", baseStats: {hp: 40, atk: 30, def: 50, spa: 55, spd: 55, spe: 100}, - abilities: {}, heightm: 0.5, weightkg: 10.4, color: "Red", @@ -1389,12 +1191,10 @@ exports.BattlePokedex = { eggGroups: ["Mineral"], }, electrode: { - num: 101, + inherit: true, name: "Electrode", types: ["Electric"], - gender: "N", baseStats: {hp: 60, atk: 50, def: 70, spa: 80, spd: 80, spe: 140}, - abilities: {}, heightm: 1.2, weightkg: 66.6, color: "Red", @@ -1403,12 +1203,10 @@ exports.BattlePokedex = { eggGroups: ["Mineral"], }, exeggcute: { - num: 102, + inherit: true, name: "Exeggcute", types: ["Grass", "Psychic"], - gender: "N", baseStats: {hp: 60, atk: 40, def: 80, spa: 60, spd: 60, spe: 40}, - abilities: {}, heightm: 0.4, weightkg: 2.5, color: "Pink", @@ -1416,26 +1214,23 @@ exports.BattlePokedex = { eggGroups: ["Grass"], }, exeggutor: { - num: 103, + inherit: true, name: "Exeggutor", types: ["Grass", "Psychic"], - gender: "N", baseStats: {hp: 95, atk: 95, def: 85, spa: 125, spd: 125, spe: 55}, - abilities: {}, heightm: 2, weightkg: 120, color: "Yellow", prevo: "exeggcute", - evoLevel: 1, + evoType: "useItem", + evoItem: "Leaf Stone", eggGroups: ["Grass"], }, cubone: { - num: 104, + inherit: true, name: "Cubone", types: ["Ground"], - gender: "N", baseStats: {hp: 50, atk: 50, def: 95, spa: 40, spd: 40, spe: 35}, - abilities: {}, heightm: 0.4, weightkg: 6.5, color: "Brown", @@ -1443,12 +1238,10 @@ exports.BattlePokedex = { eggGroups: ["Monster"], }, marowak: { - num: 105, + inherit: true, name: "Marowak", types: ["Ground"], - gender: "N", baseStats: {hp: 60, atk: 80, def: 110, spa: 50, spd: 50, spe: 45}, - abilities: {}, heightm: 1, weightkg: 45, color: "Brown", @@ -1457,51 +1250,40 @@ exports.BattlePokedex = { eggGroups: ["Monster"], }, hitmonlee: { - num: 106, + inherit: true, name: "Hitmonlee", types: ["Fighting"], - gender: "N", baseStats: {hp: 50, atk: 120, def: 53, spa: 35, spd: 35, spe: 87}, - abilities: {}, heightm: 1.5, weightkg: 49.8, color: "Brown", - prevo: "tyrogue", eggGroups: ["Human-Like"], }, hitmonchan: { - num: 107, + inherit: true, name: "Hitmonchan", types: ["Fighting"], - gender: "N", baseStats: {hp: 50, atk: 105, def: 79, spa: 35, spd: 35, spe: 76}, - abilities: {}, heightm: 1.4, weightkg: 50.2, color: "Brown", - prevo: "tyrogue", eggGroups: ["Human-Like"], }, lickitung: { - num: 108, + inherit: true, name: "Lickitung", types: ["Normal"], - gender: "N", baseStats: {hp: 90, atk: 55, def: 75, spa: 60, spd: 60, spe: 30}, - abilities: {}, heightm: 1.2, weightkg: 65.5, color: "Pink", - evos: ["lickilicky"], eggGroups: ["Monster"], }, koffing: { - num: 109, + inherit: true, name: "Koffing", types: ["Poison"], - gender: "N", baseStats: {hp: 40, atk: 65, def: 95, spa: 60, spd: 60, spe: 35}, - abilities: {}, heightm: 0.6, weightkg: 1, color: "Purple", @@ -1509,12 +1291,10 @@ exports.BattlePokedex = { eggGroups: ["Amorphous"], }, weezing: { - num: 110, + inherit: true, name: "Weezing", types: ["Poison"], - gender: "N", baseStats: {hp: 65, atk: 90, def: 120, spa: 85, spd: 85, spe: 60}, - abilities: {}, heightm: 1.2, weightkg: 9.5, color: "Purple", @@ -1523,12 +1303,10 @@ exports.BattlePokedex = { eggGroups: ["Amorphous"], }, rhyhorn: { - num: 111, + inherit: true, name: "Rhyhorn", types: ["Ground", "Rock"], - gender: "N", baseStats: {hp: 80, atk: 85, def: 95, spa: 30, spd: 30, spe: 25}, - abilities: {}, heightm: 1, weightkg: 115, color: "Gray", @@ -1536,65 +1314,52 @@ exports.BattlePokedex = { eggGroups: ["Monster", "Field"], }, rhydon: { - num: 112, + inherit: true, name: "Rhydon", types: ["Ground", "Rock"], - gender: "N", baseStats: {hp: 105, atk: 130, def: 120, spa: 45, spd: 45, spe: 40}, - abilities: {}, heightm: 1.9, weightkg: 120, color: "Gray", prevo: "rhyhorn", - evos: ["rhyperior"], evoLevel: 42, eggGroups: ["Monster", "Field"], }, chansey: { - num: 113, + inherit: true, name: "Chansey", types: ["Normal"], - gender: "N", baseStats: {hp: 250, atk: 5, def: 5, spa: 105, spd: 105, spe: 50}, - abilities: {}, heightm: 1.1, weightkg: 34.6, color: "Pink", - evos: ["blissey"], eggGroups: ["Fairy"], }, tangela: { - num: 114, + inherit: true, name: "Tangela", types: ["Grass"], - gender: "N", baseStats: {hp: 65, atk: 55, def: 115, spa: 100, spd: 100, spe: 60}, - abilities: {}, heightm: 1, weightkg: 35, color: "Blue", - evos: ["tangrowth"], eggGroups: ["Grass"], }, kangaskhan: { - num: 115, + inherit: true, name: "Kangaskhan", types: ["Normal"], - gender: "N", baseStats: {hp: 105, atk: 95, def: 80, spa: 40, spd: 40, spe: 90}, - abilities: {}, heightm: 2.2, weightkg: 80, color: "Brown", eggGroups: ["Monster"], }, horsea: { - num: 116, + inherit: true, name: "Horsea", types: ["Water"], - gender: "N", baseStats: {hp: 30, atk: 40, def: 70, spa: 70, spd: 70, spe: 60}, - abilities: {}, heightm: 0.4, weightkg: 8, color: "Blue", @@ -1602,27 +1367,22 @@ exports.BattlePokedex = { eggGroups: ["Water 1", "Dragon"], }, seadra: { - num: 117, + inherit: true, name: "Seadra", types: ["Water"], - gender: "N", baseStats: {hp: 55, atk: 65, def: 95, spa: 95, spd: 95, spe: 85}, - abilities: {}, heightm: 1.2, weightkg: 25, color: "Blue", prevo: "horsea", - evos: ["kingdra"], evoLevel: 32, eggGroups: ["Water 1", "Dragon"], }, goldeen: { - num: 118, + inherit: true, name: "Goldeen", types: ["Water"], - gender: "N", baseStats: {hp: 45, atk: 67, def: 60, spa: 50, spd: 50, spe: 63}, - abilities: {}, heightm: 0.6, weightkg: 15, color: "Red", @@ -1630,12 +1390,10 @@ exports.BattlePokedex = { eggGroups: ["Water 2"], }, seaking: { - num: 119, + inherit: true, name: "Seaking", types: ["Water"], - gender: "N", baseStats: {hp: 80, atk: 92, def: 65, spa: 80, spd: 80, spe: 68}, - abilities: {}, heightm: 1.3, weightkg: 39, color: "Red", @@ -1644,12 +1402,10 @@ exports.BattlePokedex = { eggGroups: ["Water 2"], }, staryu: { - num: 120, + inherit: true, name: "Staryu", types: ["Water"], - gender: "N", baseStats: {hp: 30, atk: 45, def: 55, spa: 70, spd: 70, spe: 85}, - abilities: {}, heightm: 0.8, weightkg: 34.5, color: "Brown", @@ -1657,116 +1413,93 @@ exports.BattlePokedex = { eggGroups: ["Water 3"], }, starmie: { - num: 121, + inherit: true, name: "Starmie", types: ["Water", "Psychic"], - gender: "N", baseStats: {hp: 60, atk: 75, def: 85, spa: 100, spd: 100, spe: 115}, - abilities: {}, heightm: 1.1, weightkg: 80, color: "Purple", prevo: "staryu", - evoLevel: 1, + evoType: "useItem", + evoItem: "Water Stone", eggGroups: ["Water 3"], }, mrmime: { - num: 122, + inherit: true, name: "Mr. Mime", types: ["Psychic"], - gender: "N", baseStats: {hp: 40, atk: 45, def: 65, spa: 100, spd: 100, spe: 90}, - abilities: {}, heightm: 1.3, weightkg: 54.5, color: "Pink", eggGroups: ["Human-Like"], }, scyther: { - num: 123, + inherit: true, name: "Scyther", types: ["Bug", "Flying"], - gender: "N", baseStats: {hp: 70, atk: 110, def: 80, spa: 55, spd: 55, spe: 105}, - abilities: {}, heightm: 1.5, weightkg: 56, color: "Green", - evos: ["scizor"], eggGroups: ["Bug"], }, jynx: { - num: 124, + inherit: true, name: "Jynx", types: ["Ice", "Psychic"], - gender: "N", baseStats: {hp: 65, atk: 50, def: 35, spa: 95, spd: 95, spe: 95}, - abilities: {}, heightm: 1.4, weightkg: 40.6, color: "Red", - prevo: "smoochum", eggGroups: ["Human-Like"], }, electabuzz: { - num: 125, + inherit: true, name: "Electabuzz", types: ["Electric"], - gender: "N", baseStats: {hp: 65, atk: 83, def: 57, spa: 85, spd: 85, spe: 105}, - abilities: {}, heightm: 1.1, weightkg: 30, color: "Yellow", - prevo: "elekid", - evos: ["electivire"], eggGroups: ["Human-Like"], }, magmar: { - num: 126, + inherit: true, name: "Magmar", types: ["Fire"], - gender: "N", baseStats: {hp: 65, atk: 95, def: 57, spa: 85, spd: 85, spe: 93}, - abilities: {}, heightm: 1.3, weightkg: 44.5, color: "Red", - prevo: "magby", - evos: ["magmortar"], eggGroups: ["Human-Like"], }, pinsir: { - num: 127, + inherit: true, name: "Pinsir", types: ["Bug"], - gender: "N", baseStats: {hp: 65, atk: 125, def: 100, spa: 55, spd: 55, spe: 85}, - abilities: {}, heightm: 1.5, weightkg: 55, color: "Brown", eggGroups: ["Bug"], }, tauros: { - num: 128, + inherit: true, name: "Tauros", types: ["Normal"], - gender: "N", baseStats: {hp: 75, atk: 100, def: 95, spa: 70, spd: 70, spe: 110}, - abilities: {}, heightm: 1.4, weightkg: 88.4, color: "Brown", eggGroups: ["Field"], }, magikarp: { - num: 129, + inherit: true, name: "Magikarp", types: ["Water"], - gender: "N", baseStats: {hp: 20, atk: 10, def: 55, spa: 20, spd: 20, spe: 80}, - abilities: {}, heightm: 0.9, weightkg: 10, color: "Red", @@ -1774,12 +1507,10 @@ exports.BattlePokedex = { eggGroups: ["Water 2", "Dragon"], }, gyarados: { - num: 130, + inherit: true, name: "Gyarados", types: ["Water", "Flying"], - gender: "N", baseStats: {hp: 95, atk: 125, def: 79, spa: 100, spd: 100, spe: 81}, - abilities: {}, heightm: 6.5, weightkg: 235, color: "Blue", @@ -1788,104 +1519,90 @@ exports.BattlePokedex = { eggGroups: ["Water 2", "Dragon"], }, lapras: { - num: 131, + inherit: true, name: "Lapras", types: ["Water", "Ice"], - gender: "N", baseStats: {hp: 130, atk: 85, def: 80, spa: 95, spd: 95, spe: 60}, - abilities: {}, heightm: 2.5, weightkg: 220, color: "Blue", eggGroups: ["Monster", "Water 1"], }, ditto: { - num: 132, + inherit: true, name: "Ditto", types: ["Normal"], - gender: "N", baseStats: {hp: 48, atk: 48, def: 48, spa: 48, spd: 48, spe: 48}, - abilities: {}, heightm: 0.3, weightkg: 4, color: "Purple", eggGroups: ["Ditto"], }, eevee: { - num: 133, + inherit: true, name: "Eevee", types: ["Normal"], - gender: "N", baseStats: {hp: 55, atk: 55, def: 50, spa: 65, spd: 65, spe: 55}, - abilities: {}, heightm: 0.3, weightkg: 6.5, color: "Brown", - evos: ["vaporeon", "jolteon", "flareon", "espeon", "umbreon", "leafeon", "glaceon", "sylveon"], + evos: ["vaporeon", "jolteon", "flareon"], eggGroups: ["Field"], }, vaporeon: { - num: 134, + inherit: true, name: "Vaporeon", types: ["Water"], - gender: "N", baseStats: {hp: 130, atk: 65, def: 60, spa: 110, spd: 110, spe: 65}, - abilities: {}, heightm: 1, weightkg: 29, color: "Blue", prevo: "eevee", - evoLevel: 1, + evoType: "useItem", + evoItem: "Water Stone", eggGroups: ["Field"], }, jolteon: { - num: 135, + inherit: true, name: "Jolteon", types: ["Electric"], - gender: "N", baseStats: {hp: 65, atk: 65, def: 60, spa: 110, spd: 110, spe: 130}, - abilities: {}, heightm: 0.8, weightkg: 24.5, color: "Yellow", prevo: "eevee", - evoLevel: 1, + evoType: "useItem", + evoItem: "Thunder Stone", eggGroups: ["Field"], }, flareon: { - num: 136, + inherit: true, name: "Flareon", types: ["Fire"], - gender: "N", baseStats: {hp: 65, atk: 130, def: 60, spa: 110, spd: 110, spe: 65}, - abilities: {}, heightm: 0.9, weightkg: 25, color: "Red", prevo: "eevee", - evoLevel: 1, + evoType: "useItem", + evoItem: "Fire Stone", eggGroups: ["Field"], }, porygon: { - num: 137, + inherit: true, name: "Porygon", types: ["Normal"], - gender: "N", baseStats: {hp: 65, atk: 60, def: 70, spa: 75, spd: 75, spe: 40}, - abilities: {}, heightm: 0.8, weightkg: 36.5, color: "Pink", - evos: ["porygon2"], eggGroups: ["Mineral"], }, omanyte: { - num: 138, + inherit: true, name: "Omanyte", types: ["Rock", "Water"], - gender: "N", baseStats: {hp: 35, atk: 40, def: 100, spa: 90, spd: 90, spe: 35}, - abilities: {}, heightm: 0.4, weightkg: 7.5, color: "Blue", @@ -1893,12 +1610,10 @@ exports.BattlePokedex = { eggGroups: ["Water 1", "Water 3"], }, omastar: { - num: 139, + inherit: true, name: "Omastar", types: ["Rock", "Water"], - gender: "N", baseStats: {hp: 70, atk: 60, def: 125, spa: 115, spd: 115, spe: 55}, - abilities: {}, heightm: 1, weightkg: 35, color: "Blue", @@ -1907,12 +1622,10 @@ exports.BattlePokedex = { eggGroups: ["Water 1", "Water 3"], }, kabuto: { - num: 140, + inherit: true, name: "Kabuto", types: ["Rock", "Water"], - gender: "N", baseStats: {hp: 30, atk: 80, def: 90, spa: 45, spd: 45, spe: 55}, - abilities: {}, heightm: 0.5, weightkg: 11.5, color: "Brown", @@ -1920,12 +1633,10 @@ exports.BattlePokedex = { eggGroups: ["Water 1", "Water 3"], }, kabutops: { - num: 141, + inherit: true, name: "Kabutops", types: ["Rock", "Water"], - gender: "N", baseStats: {hp: 60, atk: 115, def: 105, spa: 70, spd: 70, spe: 80}, - abilities: {}, heightm: 1.3, weightkg: 40.5, color: "Brown", @@ -1934,72 +1645,60 @@ exports.BattlePokedex = { eggGroups: ["Water 1", "Water 3"], }, aerodactyl: { - num: 142, + inherit: true, name: "Aerodactyl", types: ["Rock", "Flying"], - gender: "N", baseStats: {hp: 80, atk: 105, def: 65, spa: 60, spd: 60, spe: 130}, - abilities: {}, heightm: 1.8, weightkg: 59, color: "Purple", eggGroups: ["Flying"], }, snorlax: { - num: 143, + inherit: true, name: "Snorlax", types: ["Normal"], - gender: "N", baseStats: {hp: 160, atk: 110, def: 65, spa: 65, spd: 65, spe: 30}, - abilities: {}, heightm: 2.1, weightkg: 460, color: "Black", eggGroups: ["Monster"], }, articuno: { - num: 144, + inherit: true, name: "Articuno", types: ["Ice", "Flying"], - gender: "N", baseStats: {hp: 90, atk: 85, def: 100, spa: 125, spd: 125, spe: 85}, - abilities: {}, heightm: 1.7, weightkg: 55.4, color: "Blue", eggGroups: ["Undiscovered"], }, zapdos: { - num: 145, + inherit: true, name: "Zapdos", types: ["Electric", "Flying"], - gender: "N", baseStats: {hp: 90, atk: 90, def: 85, spa: 125, spd: 125, spe: 100}, - abilities: {}, heightm: 1.6, weightkg: 52.6, color: "Yellow", eggGroups: ["Undiscovered"], }, moltres: { - num: 146, + inherit: true, name: "Moltres", types: ["Fire", "Flying"], - gender: "N", baseStats: {hp: 90, atk: 100, def: 90, spa: 125, spd: 125, spe: 90}, - abilities: {}, heightm: 2, weightkg: 60, color: "Yellow", eggGroups: ["Undiscovered"], }, dratini: { - num: 147, + inherit: true, name: "Dratini", types: ["Dragon"], - gender: "N", baseStats: {hp: 41, atk: 64, def: 45, spa: 50, spd: 50, spe: 50}, - abilities: {}, heightm: 1.8, weightkg: 3.3, color: "Blue", @@ -2007,12 +1706,10 @@ exports.BattlePokedex = { eggGroups: ["Water 1", "Dragon"], }, dragonair: { - num: 148, + inherit: true, name: "Dragonair", types: ["Dragon"], - gender: "N", baseStats: {hp: 61, atk: 84, def: 65, spa: 70, spd: 70, spe: 70}, - abilities: {}, heightm: 4, weightkg: 16.5, color: "Blue", @@ -2022,12 +1719,10 @@ exports.BattlePokedex = { eggGroups: ["Water 1", "Dragon"], }, dragonite: { - num: 149, + inherit: true, name: "Dragonite", types: ["Dragon", "Flying"], - gender: "N", baseStats: {hp: 91, atk: 134, def: 95, spa: 100, spd: 100, spe: 80}, - abilities: {}, heightm: 2.2, weightkg: 210, color: "Brown", @@ -2036,24 +1731,20 @@ exports.BattlePokedex = { eggGroups: ["Water 1", "Dragon"], }, mewtwo: { - num: 150, + inherit: true, name: "Mewtwo", types: ["Psychic"], - gender: "N", baseStats: {hp: 106, atk: 110, def: 90, spa: 154, spd: 154, spe: 130}, - abilities: {}, heightm: 2, weightkg: 122, color: "Purple", eggGroups: ["Undiscovered"], }, mew: { - num: 151, + inherit: true, name: "Mew", types: ["Psychic"], - gender: "N", baseStats: {hp: 100, atk: 100, def: 100, spa: 100, spd: 100, spe: 100}, - abilities: {}, heightm: 0.4, weightkg: 4, color: "Pink", diff --git a/data/mods/gen1/random-teams.js b/data/mods/gen1/random-teams.ts similarity index 78% rename from data/mods/gen1/random-teams.js rename to data/mods/gen1/random-teams.ts index 03df1c2ac8..32e2834ec3 100644 --- a/data/mods/gen1/random-teams.js +++ b/data/mods/gen1/random-teams.ts @@ -1,19 +1,15 @@ -'use strict'; +import RandomGen2Teams from '../gen2/random-teams'; -const RandomGen2Teams = require('../gen2/random-teams'); - -class RandomGen1Teams extends RandomGen2Teams { +export class RandomGen1Teams extends RandomGen2Teams { // Challenge Cup or CC teams are basically fully random teams. randomCCTeam() { - let team = []; + const team = []; - /**@type {{[k: string]: number}} */ - let hasDexNumber = {}; - /**@type {string[][]} */ - let formes = [[], [], [], [], [], []]; + const hasDexNumber: {[k: string]: number} = {}; + const formes: string[][] = [[], [], [], [], [], []]; // Pick six random Pokémon, no repeats. - let num; + let num: number; for (let i = 0; i < 6; i++) { do { num = this.random(151) + 1; @@ -22,10 +18,10 @@ class RandomGen1Teams extends RandomGen2Teams { } let formeCounter = 0; - for (let id in this.dex.data.Pokedex) { + for (const id in this.dex.data.Pokedex) { if (!(this.dex.data.Pokedex[id].num in hasDexNumber)) continue; - let species = this.dex.getSpecies(id); - let lsetData = this.dex.getLearnsetData(/** @type {ID} */ (id)); + const species = this.dex.getSpecies(id); + const lsetData = this.dex.getLearnsetData(id as ID); if (!lsetData.learnset || species.forme) continue; formes[hasDexNumber[species.num]].push(species.name); if (++formeCounter >= 6) { @@ -36,13 +32,13 @@ class RandomGen1Teams extends RandomGen2Teams { for (let i = 0; i < 6; i++) { // Choose forme. - let poke = this.sample(formes[i]); - let species = this.dex.getSpecies(poke); - let lsetData = this.dex.getLearnsetData(species.id); + const poke = this.sample(formes[i]); + const species = this.dex.getSpecies(poke); + const lsetData = this.dex.getLearnsetData(species.id); // Level balance: calculate directly from stats rather than using some silly lookup table. - let mbstmin = 1307; - let stats = species.baseStats; + const mbstmin = 1307; + const stats = species.baseStats; // Modified base stat total assumes 15 DVs, 255 EVs in every stat let mbst = (stats["hp"] * 2 + 30 + 63 + 100) + 10; @@ -56,7 +52,8 @@ class RandomGen1Teams extends RandomGen2Teams { while (level < 100) { mbst = Math.floor((stats["hp"] * 2 + 30 + 63 + 100) * level / 100 + 10); - mbst += Math.floor(((stats["atk"] * 2 + 30 + 63 + 100) * level / 100 + 5) * level / 100); //since damage is roughly proportional to lvl + // Since damage is roughly proportional to lvl + mbst += Math.floor(((stats["atk"] * 2 + 30 + 63 + 100) * level / 100 + 5) * level / 100); mbst += Math.floor((stats["def"] * 2 + 30 + 63 + 100) * level / 100 + 5); mbst += Math.floor(((stats["spa"] * 2 + 30 + 63 + 100) * level / 100 + 5) * level / 100); mbst += Math.floor((stats["spd"] * 2 + 30 + 63 + 100) * level / 100 + 5); @@ -67,7 +64,7 @@ class RandomGen1Teams extends RandomGen2Teams { } // Random DVs. - let ivs = { + const ivs = { hp: 0, atk: this.random(16), def: this.random(16), @@ -83,15 +80,14 @@ class RandomGen1Teams extends RandomGen2Teams { ivs["spe"] = ivs["spe"] * 2; // Maxed EVs. - let evs = {hp: 255, atk: 255, def: 255, spa: 255, spd: 255, spe: 255}; + const evs = {hp: 255, atk: 255, def: 255, spa: 255, spd: 255, spe: 255}; // 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. let moves; - /**@type {string[]} */ - let pool = []; + const pool: string[] = []; if (lsetData.learnset) { - for (let move in lsetData.learnset) { + for (const move in lsetData.learnset) { if (this.dex.getMove(move).gen !== 1) continue; if (lsetData.learnset[move].some(learned => learned[0] === '1')) { pool.push(move); @@ -101,7 +97,10 @@ class RandomGen1Teams extends RandomGen2Teams { if (pool.length <= 4) { moves = pool; } else { - moves = [this.sampleNoReplace(pool), this.sampleNoReplace(pool), this.sampleNoReplace(pool), this.sampleNoReplace(pool)]; + moves = [ + this.sampleNoReplace(pool), this.sampleNoReplace(pool), + this.sampleNoReplace(pool), this.sampleNoReplace(pool), + ]; } team.push({ @@ -127,38 +126,36 @@ class RandomGen1Teams extends RandomGen2Teams { randomTeam() { // Get what we need ready. let pokemonLeft = 0; - let pokemon = []; + const pokemon = []; - let handicapMons = ['magikarp', 'weedle', 'kakuna', 'caterpie', 'metapod']; - let nuTiers = ['UU', 'UUBL', 'NFE', 'LC', 'NU']; - let uuTiers = ['NFE', 'UU', 'UUBL', 'NU']; + const handicapMons = ['magikarp', 'weedle', 'kakuna', 'caterpie', 'metapod']; + const nuTiers = ['UU', 'UUBL', 'NFE', 'LC', 'NU']; + const uuTiers = ['NFE', 'UU', 'UUBL', 'NU']; - let pokemonPool = []; - for (let id in this.dex.data.FormatsData) { - let species = this.dex.getSpecies(id); + const pokemonPool = []; + for (const id in this.dex.data.FormatsData) { + const species = this.dex.getSpecies(id); if (!species.isNonstandard && species.randomBattleMoves) { pokemonPool.push(id); } } // Now let's store what we are getting. - /**@type {{[k: string]: number}} */ - let typeCount = {}; - /**@type {{[k: string]: number}} */ - let weaknessCount = {Electric: 0, Psychic: 0, Water: 0, Ice: 0, Ground: 0}; + const typeCount: {[k: string]: number} = {}; + const weaknessCount: {[k: string]: number} = {Electric: 0, Psychic: 0, Water: 0, Ice: 0, Ground: 0}; let uberCount = 0; let nuCount = 0; let hasShitmon = false; while (pokemonPool.length && pokemonLeft < 6) { - let species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); + const species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); if (!species.exists) continue; // Bias the tiers so you get less shitmons and only one of the two Ubers. // If you have a shitmon, don't get another if (handicapMons.includes(species.id) && hasShitmon) continue; - let tier = species.tier; + const tier = species.tier; switch (tier) { case 'LC': case 'NFE': @@ -189,9 +186,9 @@ class RandomGen1Teams extends RandomGen2Teams { // We need a weakness count of spammable attacks to avoid being swept by those. // Spammable attacks are: Thunderbolt, Psychic, Surf, Blizzard, Earthquake. - let pokemonWeaknesses = []; - for (let type in weaknessCount) { - let increaseCount = this.dex.getImmunity(type, species) && this.dex.getEffectiveness(type, species) > 0; + const pokemonWeaknesses = []; + for (const type in weaknessCount) { + const increaseCount = this.dex.getImmunity(type, species) && this.dex.getEffectiveness(type, species) > 0; if (!increaseCount) continue; if (weaknessCount[type] >= 2) { skip = true; @@ -203,7 +200,7 @@ class RandomGen1Teams extends RandomGen2Teams { if (skip) continue; // The set passes the limitations. - let set = this.randomSet(species); + const set = this.randomSet(species); pokemon.push(set); // Now let's increase the counters. First, the Pokémon left. @@ -239,30 +236,24 @@ class RandomGen1Teams extends RandomGen2Teams { /** * Random set generation for Gen 1 Random Battles. - * @param {string | Species} species - * @return {RandomTeamsTypes.RandomSet} */ - randomSet(species) { + randomSet(species: string | Species): RandomTeamsTypes.RandomSet { species = this.dex.getSpecies(species); if (!species.exists) species = this.dex.getSpecies('pikachu'); // Because Gen 1. - let movePool = species.randomBattleMoves ? species.randomBattleMoves.slice() : []; - /**@type {string[]} */ - let moves = []; - /**@type {{[k: string]: true}} */ - let hasType = {}; + const movePool = species.randomBattleMoves ? species.randomBattleMoves.slice() : []; + let moves: string[] = []; + const hasType: {[k: string]: true} = {}; hasType[species.types[0]] = true; if (species.types[1]) hasType[species.types[1]] = true; - /**@type {{[k: string]: true}} */ - let hasMove = {}; - /**@type {{[k: string]: number}} */ - let counter = {}; - // let setupType = ''; + let hasMove: {[k: string]: true} = {}; + let counter: {[k: string]: number} = {}; + // const setupType = ''; // Moves that boost Attack: - let PhysicalSetup = ['swordsdance', 'sharpen']; + const PhysicalSetup = ['swordsdance', 'sharpen']; // Moves which boost Special Attack: - let SpecialSetup = ['amnesia', 'growth']; + const SpecialSetup = ['amnesia', 'growth']; // Either add all moves or add none if (species.comboMoves) { @@ -285,7 +276,7 @@ class RandomGen1Teams extends RandomGen2Teams { while (moves.length < 4 && movePool.length) { // Choose next 4 moves from learnset/viable moves and add them to moves list: while (moves.length < 4 && movePool.length) { - let moveid = this.sampleNoReplace(movePool); + const moveid = this.sampleNoReplace(movePool); moves.push(moveid); } @@ -294,8 +285,8 @@ class RandomGen1Teams extends RandomGen2Teams { hasMove = {}; counter = {Physical: 0, Special: 0, Status: 0, physicalsetup: 0, specialsetup: 0}; for (const setMoveid of moves) { - let move = this.dex.getMove(setMoveid); - let moveid = move.id; + const move = this.dex.getMove(setMoveid); + const moveid = move.id; hasMove[moveid] = true; if (!move.damage && !move.damageCallback) { counter[move.category]++; @@ -316,7 +307,7 @@ class RandomGen1Teams extends RandomGen2Teams { for (const [i, moveid] of moves.entries()) { if (moveid === species.essentialMove) continue; - let move = this.dex.getMove(moveid); + const move = this.dex.getMove(moveid); let rejected = false; if (!species.essentialMove || moveid !== species.essentialMove) { switch (moveid) { @@ -358,7 +349,7 @@ class RandomGen1Teams extends RandomGen2Teams { } // End of the check for more than 4 moves on moveset. } - let levelScale = { + const levelScale: {[k: string]: number} = { LC: 88, NFE: 80, UU: 74, @@ -366,14 +357,12 @@ class RandomGen1Teams extends RandomGen2Teams { Uber: 65, }; - let customScale = { + const customScale: {[k: string]: number} = { Mewtwo: 62, Caterpie: 99, Metapod: 99, Weedle: 99, Kakuna: 99, Magikarp: 99, Ditto: 88, }; - // @ts-ignore let level = levelScale[species.tier] || 80; - // @ts-ignore if (customScale[species.name]) level = customScale[species.name]; return { @@ -391,4 +380,4 @@ class RandomGen1Teams extends RandomGen2Teams { } } -module.exports = RandomGen1Teams; +export default RandomGen1Teams; diff --git a/data/mods/gen1/rulesets.js b/data/mods/gen1/rulesets.ts similarity index 72% rename from data/mods/gen1/rulesets.js rename to data/mods/gen1/rulesets.ts index 6f69be71ee..14519f9093 100644 --- a/data/mods/gen1/rulesets.js +++ b/data/mods/gen1/rulesets.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedFormatsData}} */ -let BattleFormats = { +export const BattleFormats: {[k: string]: ModdedFormatsData} = { standard: { effectType: 'ValidatorRule', name: 'Standard', @@ -18,11 +15,9 @@ let BattleFormats = { onModifySpecies(species, target, source) { const newSpecies = this.dex.deepClone(species); newSpecies.baseStats = this.dex.deepClone(newSpecies.baseStats); - /** @type {StatName[]} */ - let stats = ['atk', 'def', 'spa', 'spe']; - /** @type {number} */ - let pst = stats.map(stat => newSpecies.baseStats[stat]).reduce((x, y) => x + y); - let scale = 500 - newSpecies.baseStats['hp']; + const stats: StatName[] = ['atk', 'def', 'spa', 'spe']; + const pst: number = stats.map(stat => newSpecies.baseStats[stat]).reduce((x, y) => x + y); + const scale = 500 - newSpecies.baseStats['hp']; for (const stat of stats) { newSpecies.baseStats[stat] = this.dex.clampIntRange(newSpecies.baseStats[stat] * scale / pst, 1, 255); } @@ -30,5 +25,3 @@ let BattleFormats = { }, }, }; - -exports.BattleFormats = BattleFormats; diff --git a/data/mods/gen1/scripts.js b/data/mods/gen1/scripts.ts similarity index 88% rename from data/mods/gen1/scripts.js rename to data/mods/gen1/scripts.ts index 720ef7f220..44107ec888 100644 --- a/data/mods/gen1/scripts.js +++ b/data/mods/gen1/scripts.ts @@ -1,13 +1,10 @@ -'use strict'; - /** * Gen 1 mechanics are fairly different to those we know on current gen. * Therefor we need to make a lot of changes to the battle engine for this game simulation. * This generation inherits all the changes from older generations, that must be taken into account when editing code. */ -/**@type {ModdedBattleScriptsData} */ -let BattleScripts = { +export const BattleScripts: ModdedBattleScriptsData = { inherit: 'gen2', gen: 1, debug(activity) { @@ -28,57 +25,43 @@ let BattleScripts = { // BattlePokemon scripts. pokemon: { getStat(statName, unmodified) { - statName = /** @type {StatNameExceptHP} */(toID(statName)); // @ts-ignore - type checking prevents 'hp' from being passed, but we're paranoid if (statName === 'hp') throw new Error("Please read `maxhp` directly"); if (unmodified) return this.storedStats[statName]; - // @ts-ignore - return this.modifiedStats[statName]; + return this.modifiedStats![statName]; }, // Gen 1 function to apply a stat modification that is only active until the stat is recalculated or mon switched. modifyStat(statName, modifier) { if (!(statName in this.storedStats)) throw new Error("Invalid `statName` passed to `modifyStat`"); - // @ts-ignore - this.modifiedStats[statName] = this.battle.dex.clampIntRange(Math.floor(this.modifiedStats[statName] * modifier), 1, 999); + const modifiedStats = this.battle.dex.clampIntRange(Math.floor(this.modifiedStats![statName] * modifier), 1, 999); + this.modifiedStats![statName] = modifiedStats; }, // In generation 1, boosting function increases the stored modified stat and checks for opponent's status. boostBy(boost) { let changed = false; - for (let i in boost) { - // @ts-ignore - let delta = boost[i]; + let i: BoostName; + for (i in boost) { + const delta = boost[i]; if (delta === undefined) continue; - // @ts-ignore if (delta > 0 && this.boosts[i] >= 6) continue; - // @ts-ignore if (delta < 0 && this.boosts[i] <= -6) continue; - // @ts-ignore this.boosts[i] += delta; - // @ts-ignore if (this.boosts[i] > 6) { - // @ts-ignore this.boosts[i] = 6; } - // @ts-ignore if (this.boosts[i] < -6) { - // @ts-ignore this.boosts[i] = -6; } changed = true; // Recalculate the modified stat - // @ts-ignore + if (i === 'evasion' || i === 'accuracy') continue; let stat = this.species.baseStats[i]; - // @ts-ignore stat = Math.floor(Math.floor(2 * stat + this.set.ivs[i] + Math.floor(this.set.evs[i] / 4)) * this.level / 100 + 5); - // @ts-ignore - this.modifiedStats[i] = this.storedStats[i] = Math.floor(stat); - // @ts-ignore + this.modifiedStats![i] = this.storedStats[i] = Math.floor(stat); if (this.boosts[i] >= 0) { - // @ts-ignore - this.modifyStat(i, [1, 1.5, 2, 2.5, 3, 3.5, 4][this.boosts[i]]); + this.modifyStat!(i, [1, 1.5, 2, 2.5, 3, 3.5, 4][this.boosts[i]]); } else { - // @ts-ignore - this.modifyStat(i, [100, 66, 50, 40, 33, 28, 25][-this.boosts[i]] / 100); + this.modifyStat!(i, [100, 66, 50, 40, 33, 28, 25][-this.boosts[i]] / 100); } } return changed; @@ -90,9 +73,9 @@ let BattleScripts = { // This leads with partial trapping moves shennanigans after the move has been used. // It also deals with how PP reduction works on gen 1. runMove(moveOrMoveName, pokemon, targetLoc, sourceEffect) { - let target = this.getTarget(pokemon, moveOrMoveName, targetLoc); - let move = this.dex.getActiveMove(moveOrMoveName); - if (target && target.subFainted) target.subFainted = null; + const target = this.getTarget(pokemon, moveOrMoveName, targetLoc); + const move = this.dex.getActiveMove(moveOrMoveName); + if (target?.subFainted) target.subFainted = null; this.setActiveMove(move, pokemon, target); @@ -115,7 +98,10 @@ let BattleScripts = { pokemon.lastDamage = 0; let lockedMove = this.runEvent('LockMove', pokemon); if (lockedMove === true) lockedMove = false; - if (!lockedMove && (!pokemon.volatiles['partialtrappinglock'] || pokemon.volatiles['partialtrappinglock'].locked !== target)) { + if ( + !lockedMove && + (!pokemon.volatiles['partialtrappinglock'] || pokemon.volatiles['partialtrappinglock'].locked !== target) + ) { pokemon.deductPP(move, null, target); // On gen 1 moves are stored when they are chosen and a PP is deducted. pokemon.side.lastMove = move; @@ -124,7 +110,7 @@ let BattleScripts = { sourceEffect = move; } if (pokemon.volatiles['partialtrappinglock'] && target !== pokemon.volatiles['partialtrappinglock'].locked) { - const moveSlot = pokemon.moveSlots.find(moveSlot => moveSlot.id === move.id); + const moveSlot = pokemon.moveSlots.find(ms => ms.id === move.id); if (moveSlot && moveSlot.pp < 0) { moveSlot.pp = 63; this.hint("In Gen 1, if a player is forced to use a move with 0 PP, the move will underflow to have 63 PP."); @@ -171,10 +157,9 @@ let BattleScripts = { // useMove can be found on scripts.js // It is the function that actually uses the move, running ModifyMove events. // It uses the move and then deals with the effects after the move. - // @ts-ignore useMove(moveOrMoveName, pokemon, target, sourceEffect) { if (!sourceEffect && this.effect.id) sourceEffect = this.effect; - let baseMove = this.dex.getMove(moveOrMoveName); + const baseMove = this.dex.getMove(moveOrMoveName); let move = this.dex.getActiveMove(baseMove); if (target === undefined) target = this.getRandomTarget(pokemon, move); if (move.target === 'self') { @@ -220,8 +205,7 @@ let BattleScripts = { move.ignoreImmunity = (move.category === 'Status'); } - /** @type {number | undefined | false | ''} */ - let damage = false; + let damage: number | undefined | false | '' = false; if (!target || target.fainted) { this.attrLastMove('[notarget]'); this.add('-notarget'); @@ -231,8 +215,14 @@ let BattleScripts = { // Store 0 damage for last damage if move failed or dealt 0 damage. // This only happens on moves that don't deal damage but call GetDamageVarsForPlayerAttack (disassembly). - if (!damage && (move.category !== 'Status' || (move.status && move.category === 'Status' && !['psn', 'tox', 'par'].includes(move.status))) && - !['conversion', 'haze', 'mist', 'focusenergy', 'confuseray', 'supersonic', 'transform', 'lightscreen', 'reflect', 'substitute', 'mimic', 'leechseed', 'splash', 'softboiled', 'recover', 'rest'].includes(move.id)) { + const neverDamageMoves = [ + 'conversion', 'haze', 'mist', 'focusenergy', 'confuseray', 'supersonic', 'transform', 'lightscreen', 'reflect', 'substitute', 'mimic', 'leechseed', 'splash', 'softboiled', 'recover', 'rest', + ]; + if ( + !damage && + (move.category !== 'Status' || (move.status && !['psn', 'tox', 'par'].includes(move.status))) && + !neverDamageMoves.includes(move.id) + ) { this.lastDamage = 0; } @@ -252,8 +242,7 @@ let BattleScripts = { // This function attempts a move hit and returns the attempt result before the actual hit happens. // It deals with partial trapping weirdness and accuracy bugs as well. tryMoveHit(target, pokemon, move) { - /** @type {number | false | undefined} */ - let damage = 0; + let damage: number | false | undefined = 0; // First, check if the target is semi-invulnerable let hitResult = this.runEvent('Invulnerability', target, pokemon, move); @@ -264,7 +253,10 @@ let BattleScripts = { } // Then, check if the Pokémon is immune to this move. - if ((!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) && !target.runImmunity(move.type, true)) { + if ( + (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) && + !target.runImmunity(move.type, true) + ) { if (move.selfdestruct) { this.faint(pokemon, pokemon, move); } @@ -277,11 +269,10 @@ let BattleScripts = { } // Now, let's calculate the accuracy. - /** @type {number | true} */ let accuracy = move.accuracy; // Partial trapping moves: true accuracy while it lasts - if (move.volatileStatus === 'partiallytrapped' && pokemon.volatiles['partialtrappinglock'] && target === pokemon.volatiles['partialtrappinglock'].locked) { + if (move.volatileStatus === 'partiallytrapped' && target === pokemon.volatiles['partialtrappinglock']?.locked) { accuracy = true; } @@ -339,9 +330,8 @@ let BattleScripts = { } hits = Math.floor(hits); // In gen 1, all the hits have the same damage for multihits move - /** @type {number | undefined | false} */ - let moveDamage = 0; - let i; + let moveDamage: number | undefined | false = 0; + let i: number; for (i = 0; i < hits && target.hp && pokemon.hp; i++) { move.hit = i + 1; moveDamage = this.moveHit(target, pokemon, move); @@ -394,12 +384,10 @@ let BattleScripts = { // It deals with the actual move hit, as the name indicates, dealing damage and/or effects. // This function also deals with the Gen 1 Substitute behaviour on the hitting process. moveHit(target, pokemon, move, moveData, isSecondary, isSelf) { - /** @type {number | false | null | undefined} */ - let damage = 0; + let damage: number | false | null | undefined = 0; if (!isSecondary && !isSelf) this.setActiveMove(move, pokemon, target); - /**@type {number | boolean} */ - let hitResult = true; + let hitResult: number | boolean = true; if (!moveData) moveData = move; if (move.ignoreImmunity === undefined) { @@ -407,8 +395,8 @@ let BattleScripts = { } // We get the sub to the target to see if it existed - let targetSub = (target) ? target.volatiles['substitute'] : false; - let targetHadSub = (targetSub !== null && targetSub !== false && (typeof targetSub !== 'undefined')); + const targetSub = (target) ? target.volatiles['substitute'] : false; + const targetHadSub = (targetSub !== null && targetSub !== false && (typeof targetSub !== 'undefined')); if (target) { hitResult = this.singleEvent('TryHit', moveData, {}, target, pokemon, move); @@ -494,18 +482,16 @@ let BattleScripts = { if (pokemon.side.foe.active[0] && pokemon.side.foe.active[0].status) { // If it's paralysed, quarter its speed. if (pokemon.side.foe.active[0].status === 'par') { - // @ts-ignore - pokemon.side.foe.active[0].modifyStat('spe', 0.25); + pokemon.side.foe.active[0].modifyStat!('spe', 0.25); } // If it's burned, halve its attack. if (pokemon.side.foe.active[0].status === 'brn') { - // @ts-ignore - pokemon.side.foe.active[0].modifyStat('atk', 0.5); + pokemon.side.foe.active[0].modifyStat!('atk', 0.5); } } } if (moveData.heal && !target.fainted) { - let d = target.heal(Math.floor(target.maxhp * moveData.heal[0] / moveData.heal[1])); + const d = target.heal(Math.floor(target.maxhp * moveData.heal[0] / moveData.heal[1])); if (!d) { this.add('-fail', target); return false; @@ -530,10 +516,8 @@ let BattleScripts = { } else if (!target.status) { if (target.setStatus(moveData.status, pokemon, move)) { // Gen 1 mechanics: The burn attack drop and the paralyse speed drop are applied here directly on stat modifiers. - // @ts-ignore - if (moveData.status === 'brn') target.modifyStat('atk', 0.5); - // @ts-ignore - if (moveData.status === 'par') target.modifyStat('spe', 0.25); + if (moveData.status === 'brn') target.modifyStat!('atk', 0.5); + if (moveData.status === 'par') target.modifyStat!('spe', 0.25); } } else if (!isSecondary) { if (target.status === moveData.status) { @@ -578,10 +562,10 @@ let BattleScripts = { return false; } } - let targetHasSub = !!(target && target.volatiles['substitute']); + const targetHasSub = !!(target?.volatiles['substitute']); // Here's where self effects are applied. - let doSelf = (targetHadSub && targetHasSub) || !targetHadSub; + const doSelf = (targetHadSub && targetHasSub) || !targetHadSub; if (moveData.self && (doSelf || (moveData.self !== true && moveData.self.volatileStatus === 'partialtrappinglock'))) { this.moveHit(pokemon, pokemon, move, moveData.self, isSecondary, true); } @@ -623,17 +607,15 @@ let BattleScripts = { if (!target || !target.hp) return 0; let success = null; boost = this.runEvent('Boost', target, source, effect, Object.assign({}, boost)); - for (let i in boost) { + let i: BoostName; + for (i in boost) { /** @type {SparseBoostsTable} */ - let currentBoost = {}; - // @ts-ignore + const currentBoost: SparseBoostsTable = {}; currentBoost[i] = boost[i]; - // @ts-ignore if (boost[i] !== 0 && target.boostBy(currentBoost)) { success = true; let msg = '-boost'; - // @ts-ignore - if (boost[i] < 0) { + if (boost[i]! < 0) { msg = '-unboost'; // @ts-ignore boost[i] = -boost[i]; @@ -673,14 +655,13 @@ let BattleScripts = { if (typeof move === 'string') { move = this.dex.getActiveMove(move); } else if (typeof move === 'number') { - // @ts-ignore - move = /** @type {ActiveMove} */ ({ + move = { basePower: move, type: '???', category: 'Physical', willCrit: false, flags: {}, - }); + } as ActiveMove; } // Let's see if the target is immune to the move. @@ -725,11 +706,10 @@ let BattleScripts = { if (!move.defensiveCategory) move.defensiveCategory = move.category; // '???' is typeless damage: used for Struggle and Confusion etc if (!move.type) move.type = '???'; - let type = move.type; + const type = move.type; // We get the base power and apply basePowerCallback if necessary. - /** @type {number | false | null} */ - let basePower = move.basePower; + let basePower: number | false | null = move.basePower; if (move.basePowerCallback) { basePower = move.basePowerCallback.call(this, pokemon, target, move); } @@ -785,12 +765,10 @@ let BattleScripts = { // We now check attacker's and defender's stats. let level = pokemon.level; let attacker = pokemon; - let defender = target; + const defender = target; if (move.useTargetOffensive) attacker = target; - /** @type {StatNameExceptHP} */ - let atkType = (move.category === 'Physical') ? 'atk' : 'spa'; - /** @type {StatNameExceptHP} */ - let defType = (move.defensiveCategory === 'Physical') ? 'def' : 'spd'; + const atkType: StatNameExceptHP = (move.category === 'Physical') ? 'atk' : 'spa'; + const defType: StatNameExceptHP = (move.defensiveCategory === 'Physical') ? 'def' : 'spd'; let attack = attacker.getStat(move.useSourceDefensiveAsOffensive ? defType : atkType); let defense = defender.getStat(defType); // In gen 1, screen effect is applied here. @@ -850,7 +828,7 @@ let BattleScripts = { // Type effectiveness. // The order here is not correct, must change to check the move versus each type. - let totalTypeMod = target.runEffectiveness(move); + const totalTypeMod = target.runEffectiveness(move); // Super effective attack if (totalTypeMod > 0) { if (!suppressMessages) this.add('-supereffective', target); diff --git a/data/mods/gen1/statuses.js b/data/mods/gen1/statuses.ts similarity index 92% rename from data/mods/gen1/statuses.js rename to data/mods/gen1/statuses.ts index 2f7bf0b8f8..5a5a901b33 100644 --- a/data/mods/gen1/statuses.js +++ b/data/mods/gen1/statuses.ts @@ -8,10 +8,7 @@ * under certain conditions and re-applied under other conditions. */ -'use strict'; - -/**@type {{[k: string]: ModdedPureEffectData}} */ -let BattleStatuses = { +export const BattleStatuses: {[k: string]: ModdedPureEffectData} = { brn: { name: 'brn', id: 'brn', @@ -23,7 +20,7 @@ let BattleStatuses = { }, onAfterMoveSelfPriority: 2, onAfterMoveSelf(pokemon) { - let toxicCounter = pokemon.volatiles['residualdmg'] ? pokemon.volatiles['residualdmg'].counter : 1; + const toxicCounter = pokemon.volatiles['residualdmg'] ? pokemon.volatiles['residualdmg'].counter : 1; this.damage(this.dex.clampIntRange(Math.floor(pokemon.maxhp / 16), 1) * toxicCounter, pokemon); if (pokemon.volatiles['residualdmg']) { this.hint("In Gen 1, Toxic's counter is retained after Rest and applies to PSN/BRN.", true); @@ -121,7 +118,7 @@ let BattleStatuses = { }, onAfterMoveSelfPriority: 2, onAfterMoveSelf(pokemon) { - let toxicCounter = pokemon.volatiles['residualdmg'] ? pokemon.volatiles['residualdmg'].counter : 1; + const toxicCounter = pokemon.volatiles['residualdmg'] ? pokemon.volatiles['residualdmg'].counter : 1; this.damage(this.dex.clampIntRange(Math.floor(pokemon.maxhp / 16), 1) * toxicCounter, pokemon); if (pokemon.volatiles['residualdmg']) { this.hint("In Gen 1, Toxic's counter is retained after Rest and applies to PSN/BRN.", true); @@ -160,7 +157,9 @@ let BattleStatuses = { } this.add('-activate', pokemon, 'confusion'); if (!this.randomChance(128, 256)) { - let damage = Math.floor(Math.floor(((Math.floor(2 * pokemon.level / 5) + 2) * pokemon.getStat('atk') * 40) / pokemon.getStat('def', false)) / 50) + 2; + const damage = Math.floor(Math.floor(( + (Math.floor(2 * pokemon.level / 5) + 2) * pokemon.getStat('atk') * 40 + ) / pokemon.getStat('def', false)) / 50) + 2; this.directDamage(damage, pokemon, target); pokemon.removeVolatile('bide'); pokemon.removeVolatile('twoturnmove'); @@ -217,7 +216,7 @@ let BattleStatuses = { id: 'partialtrappinglock', num: 0, durationCallback() { - let duration = this.sample([2, 2, 2, 3, 3, 3, 4, 5]); + const duration = this.sample([2, 2, 2, 3, 3, 3, 4, 5]); return duration; }, onResidual(target) { @@ -263,7 +262,7 @@ let BattleStatuses = { onStallMove() { // this.effectData.counter should never be undefined here. // However, just in case, use 1 if it is undefined. - let counter = this.effectData.counter || 1; + const counter = this.effectData.counter || 1; if (counter >= 256) { // 2^32 - special-cased because Battle.random(n) can't handle n > 2^16 - 1 return (this.random() * 4294967296 < 1); diff --git a/data/mods/gen1/typechart.js b/data/mods/gen1/typechart.ts similarity index 92% rename from data/mods/gen1/typechart.js rename to data/mods/gen1/typechart.ts index 27c9f36ecc..9b6288d625 100644 --- a/data/mods/gen1/typechart.js +++ b/data/mods/gen1/typechart.ts @@ -6,10 +6,7 @@ * Psychic was immune to ghost */ -'use strict'; - -/**@type {{[k: string]: ModdedTypeData | null}} */ -let BattleTypeChart = { +export const BattleTypeChart: {[k: string]: ModdedTypeData | null} = { Bug: { damageTaken: { Bug: 0, @@ -132,5 +129,3 @@ let BattleTypeChart = { Dark: null, Steel: null, }; - -exports.BattleTypeChart = BattleTypeChart; diff --git a/data/mods/gen2/formats-data.js b/data/mods/gen2/formats-data.ts similarity index 99% rename from data/mods/gen2/formats-data.js rename to data/mods/gen2/formats-data.ts index edbcda5e31..2f139c86b9 100644 --- a/data/mods/gen2/formats-data.js +++ b/data/mods/gen2/formats-data.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesFormatsData}} */ -let BattleFormatsData = { +export const BattleFormatsData: {[k: string]: ModdedSpeciesFormatsData} = { bulbasaur: { tier: "LC", }, @@ -3558,5 +3555,3 @@ let BattleFormatsData = { tier: "Uber", }, }; - -exports.BattleFormatsData = BattleFormatsData; diff --git a/data/mods/gen2/items.js b/data/mods/gen2/items.ts similarity index 72% rename from data/mods/gen2/items.js rename to data/mods/gen2/items.ts index 6a13d9a9c0..2397c33d9d 100644 --- a/data/mods/gen2/items.js +++ b/data/mods/gen2/items.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedItemData}} */ -let BattleItems = { +export const BattleItems: {[k: string]: ModdedItemData} = { berryjuice: { inherit: true, isNonstandard: null, @@ -42,7 +39,9 @@ let BattleItems = { kingsrock: { inherit: true, onModifyMove(move) { - let affectedByKingsRock = ['absorb', 'aeroblast', 'barrage', 'beatup', 'bide', 'bonerush', 'bonemerang', 'cometpunch', 'counter', 'crabhammer', 'crosschop', 'cut', 'dig', 'doublekick', 'doubleslap', 'doubleedge', 'dragonrage', 'drillpeck', 'eggbomb', 'explosion', 'extremespeed', 'falseswipe', 'feintattack', 'flail', 'fly', 'frustration', 'furyattack', 'furycutter', 'furyswipes', 'gigadrain', 'hiddenpower', 'highjumpkick', 'hornattack', 'hydropump', 'jumpkick', 'karatechop', 'leechlife', 'machpunch', 'magnitude', 'megadrain', 'megakick', 'megapunch', 'megahorn', 'mirrorcoat', 'nightshade', 'outrage', 'payday', 'peck', 'petaldance', 'pinmissile', 'pound', 'present', 'pursuit', 'psywave', 'quickattack', 'rage', 'rapidspin', 'razorleaf', 'razorwind', 'return', 'reversal', 'rockthrow', 'rollout', 'scratch', 'seismictoss', 'selfdestruct', 'skullbash', 'skyattack', 'slam', 'slash', 'snore', 'solarbeam', 'sonicboom', 'spikecannon', 'strength', 'struggle', 'submission', 'superfang', 'surf', 'swift', 'tackle', 'takedown', 'thief', 'thrash', 'triplekick', 'twineedle', 'visegrip', 'vinewhip', 'vitalthrow', 'watergun', 'waterfall', 'wingattack']; + const affectedByKingsRock = [ + 'absorb', 'aeroblast', 'barrage', 'beatup', 'bide', 'bonerush', 'bonemerang', 'cometpunch', 'counter', 'crabhammer', 'crosschop', 'cut', 'dig', 'doublekick', 'doubleslap', 'doubleedge', 'dragonrage', 'drillpeck', 'eggbomb', 'explosion', 'extremespeed', 'falseswipe', 'feintattack', 'flail', 'fly', 'frustration', 'furyattack', 'furycutter', 'furyswipes', 'gigadrain', 'hiddenpower', 'highjumpkick', 'hornattack', 'hydropump', 'jumpkick', 'karatechop', 'leechlife', 'machpunch', 'magnitude', 'megadrain', 'megakick', 'megapunch', 'megahorn', 'mirrorcoat', 'nightshade', 'outrage', 'payday', 'peck', 'petaldance', 'pinmissile', 'pound', 'present', 'pursuit', 'psywave', 'quickattack', 'rage', 'rapidspin', 'razorleaf', 'razorwind', 'return', 'reversal', 'rockthrow', 'rollout', 'scratch', 'seismictoss', 'selfdestruct', 'skullbash', 'skyattack', 'slam', 'slash', 'snore', 'solarbeam', 'sonicboom', 'spikecannon', 'strength', 'struggle', 'submission', 'superfang', 'surf', 'swift', 'tackle', 'takedown', 'thief', 'thrash', 'triplekick', 'twineedle', 'visegrip', 'vinewhip', 'vitalthrow', 'watergun', 'waterfall', 'wingattack', + ]; if (affectedByKingsRock.includes(move.id)) { if (!move.secondaries) move.secondaries = []; // The kingsrock flag allows for differentiation from Snore, @@ -154,5 +153,3 @@ let BattleItems = { isNonstandard: null, }, }; - -exports.BattleItems = BattleItems; diff --git a/data/mods/gen2/learnsets.js b/data/mods/gen2/learnsets.ts similarity index 99% rename from data/mods/gen2/learnsets.js rename to data/mods/gen2/learnsets.ts index 4a198043a6..d42c0aa4dc 100644 --- a/data/mods/gen2/learnsets.js +++ b/data/mods/gen2/learnsets.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedLearnsetData}} */ -let BattleLearnsets = { +export const BattleLearnsets: {[k: string]: ModdedLearnsetData} = { missingno: { learnset: { blizzard: ["1M"], @@ -12156,5 +12153,3 @@ let BattleLearnsets = { eventOnly: true, }, }; - -exports.BattleLearnsets = BattleLearnsets; diff --git a/data/mods/gen2/moves.js b/data/mods/gen2/moves.ts similarity index 94% rename from data/mods/gen2/moves.js rename to data/mods/gen2/moves.ts index 88c04ca974..3607267b16 100644 --- a/data/mods/gen2/moves.js +++ b/data/mods/gen2/moves.ts @@ -2,10 +2,7 @@ * Gen 2 moves */ -'use strict'; - -/**@type {{[k: string]: ModdedMoveData}} */ -let BattleMovedex = { +export const BattleMovedex: {[k: string]: ModdedMoveData} = { absorb: { inherit: true, desc: "The user recovers 1/2 the HP lost by the target, rounded down. If the target has a substitute, this move misses.", @@ -43,7 +40,7 @@ let BattleMovedex = { return false; } this.directDamage(target.maxhp / 2); - let originalStage = target.boosts.atk; + const originalStage = target.boosts.atk; let currentStage = originalStage; let boosts = 0; let loopStage = 0; @@ -104,10 +101,8 @@ let BattleMovedex = { } target = possibleTarget; } - /** @type {ActiveMove} */ - // @ts-ignore - let moveData = { - id: /** @type {ID} */('bide'), + const moveData = { + id: 'bide', name: "Bide", accuracy: 100, damage: this.effectData.totalDamage * 2, @@ -116,7 +111,7 @@ let BattleMovedex = { flags: {contact: 1, protect: 1}, effectType: 'Move', type: 'Normal', - }; + } as unknown as ActiveMove; this.tryMoveHit(target, pokemon, moveData); return false; } @@ -147,8 +142,11 @@ let BattleMovedex = { inherit: true, desc: "Deals damage to the opposing Pokemon equal to twice the HP lost by the user from a physical attack this turn. This move considers Hidden Power as Normal type, and only the last hit of a multi-hit attack is counted. Fails if the user moves first, if the user was not hit by a physical attack this turn, or if the user did not lose HP from the attack. If the opposing Pokemon used Fissure or Horn Drill and missed, this move deals 65535 damage.", damageCallback(pokemon, target) { - let lastAttackedBy = pokemon.getLastAttackedBy(); - if (lastAttackedBy && lastAttackedBy.move && lastAttackedBy.thisTurn && (this.getCategory(lastAttackedBy.move) === 'Physical' || this.dex.getMove(lastAttackedBy.move).id === 'hiddenpower') && (!target.lastMove || target.lastMove.id !== 'sleeptalk')) { + const lastAttackedBy = pokemon.getLastAttackedBy(); + if (!lastAttackedBy || !lastAttackedBy.move || !lastAttackedBy.thisTurn) return false; + + // Hidden Power counts as physical + if (this.getCategory(lastAttackedBy.move) === 'Physical' && target.lastMove?.id !== 'sleeptalk') { return 2 * lastAttackedBy.damage; } return false; @@ -243,14 +241,15 @@ let BattleMovedex = { return this.random(3, 7); }, onStart(target) { - let noEncore = ['encore', 'metronome', 'mimic', 'mirrormove', 'sketch', 'sleeptalk', 'struggle', 'transform']; - let moveIndex = target.lastMove ? target.moves.indexOf(target.lastMove.id) : -1; - if (!target.lastMove || noEncore.includes(target.lastMove.id) || !target.moveSlots[moveIndex] || target.moveSlots[moveIndex].pp <= 0) { + const noEncore = ['encore', 'metronome', 'mimic', 'mirrormove', 'sketch', 'sleeptalk', 'struggle', 'transform']; + const lockedMove = target.lastMove?.id || ''; + const moveIndex = lockedMove ? target.moves.indexOf(lockedMove) : -1; + if (moveIndex < 0 || noEncore.includes(lockedMove) || target.moveSlots[moveIndex].pp <= 0) { // it failed this.add('-fail', target); return false; } - this.effectData.move = target.lastMove.id; + this.effectData.move = lockedMove; this.add('-start', target, 'Encore'); if (!this.queue.willMove(target)) { this.effectData.duration++; @@ -261,7 +260,8 @@ let BattleMovedex = { }, onResidualOrder: 13, onResidual(target) { - if (target.moves.includes(this.effectData.move) && target.moveSlots[target.moves.indexOf(this.effectData.move)].pp <= 0) { + const lockedMoveIndex = target.moves.indexOf(this.effectData.move); + if (lockedMoveIndex >= 0 && target.moveSlots[lockedMoveIndex].pp <= 0) { // early termination if you run out of PP target.removeVolatile('encore'); } @@ -383,7 +383,7 @@ let BattleMovedex = { shortDesc: "If miss, user takes 1/8 damage it would've dealt.", onMoveFail(target, source, move) { if (target.runImmunity('Fighting')) { - let damage = this.getDamage(source, target, move, true); + const damage = this.getDamage(source, target, move, true); if (typeof damage !== 'number') throw new Error("Couldn't get High Jump Kick recoil"); this.damage(this.dex.clampIntRange(damage / 8, 1), source, source, move); } @@ -403,7 +403,7 @@ let BattleMovedex = { shortDesc: "If miss, user takes 1/8 damage it would've dealt.", onMoveFail(target, source, move) { if (target.runImmunity('Fighting')) { - let damage = this.getDamage(source, target, move, true); + const damage = this.getDamage(source, target, move, true); if (typeof damage !== 'number') throw new Error("Couldn't get Jump Kick recoil"); this.damage(this.dex.clampIntRange(damage / 8, 1), source, source, move); } @@ -423,12 +423,12 @@ let BattleMovedex = { onAfterMoveSelfPriority: 2, onAfterMoveSelf(pokemon) { if (!pokemon.hp) return; - let leecher = pokemon.side.foe.active[pokemon.volatiles['leechseed'].sourcePosition]; + const leecher = pokemon.side.foe.active[pokemon.volatiles['leechseed'].sourcePosition]; if (!leecher || leecher.fainted || leecher.hp <= 0) { return; } - let toLeech = this.dex.clampIntRange(pokemon.maxhp / 8, 1); - let damage = this.damage(toLeech, pokemon, leecher); + const toLeech = this.dex.clampIntRange(pokemon.maxhp / 8, 1); + const damage = this.damage(toLeech, pokemon, leecher); if (damage) { this.heal(damage, leecher, pokemon); } @@ -504,8 +504,11 @@ let BattleMovedex = { inherit: true, desc: "Deals damage to the opposing Pokemon equal to twice the HP lost by the user from a special attack this turn. This move considers Hidden Power as Normal type, and only the last hit of a multi-hit attack is counted. Fails if the user moves first, if the user was not hit by a special attack this turn, or if the user did not lose HP from the attack.", damageCallback(pokemon, target) { - let lastAttackedBy = pokemon.getLastAttackedBy(); - if (lastAttackedBy && lastAttackedBy.move && lastAttackedBy.thisTurn && this.getCategory(lastAttackedBy.move) === 'Special' && this.dex.getMove(lastAttackedBy.move).id !== 'hiddenpower' && (!target.lastMove || target.lastMove.id !== 'sleeptalk')) { + const lastAttackedBy = pokemon.getLastAttackedBy(); + if (!lastAttackedBy || !lastAttackedBy.move || !lastAttackedBy.thisTurn) return false; + + // Hidden Power counts as physical + if (this.getCategory(lastAttackedBy.move) === 'Special' && target.lastMove?.id !== 'sleeptalk') { return 2 * lastAttackedBy.damage; } return false; @@ -519,10 +522,13 @@ let BattleMovedex = { inherit: true, desc: "The user uses the last move used by the target. Fails if the target has not made a move, or if the last move used was Metronome, Mimic, Mirror Move, Sketch, Sleep Talk, Transform, or any move the user knows.", onHit(pokemon) { - let noMirror = ['metronome', 'mimic', 'mirrormove', 'sketch', 'sleeptalk', 'transform']; + const noMirror = ['metronome', 'mimic', 'mirrormove', 'sketch', 'sleeptalk', 'transform']; const target = pokemon.side.foe.active[0]; - const lastMove = target && target.lastMove && target.lastMove.id; - if (!lastMove || (!pokemon.activeTurns && !target.moveThisTurn) || noMirror.includes(lastMove) || pokemon.moves.includes(lastMove)) { + const lastMove = target?.lastMove && target?.lastMove.id; + if (!lastMove || (!pokemon.activeTurns && !target.moveThisTurn)) { + return false; + } + if (noMirror.includes(lastMove) || pokemon.moves.includes(lastMove)) { return false; } this.useMove(lastMove, pokemon); @@ -769,10 +775,10 @@ let BattleMovedex = { inherit: true, desc: "One of the user's known moves, besides this move, is selected for use at random. Fails if the user is not asleep. The selected move does not have PP deducted from it, and can currently have 0 PP. This move cannot select Bide, Sleep Talk, or any two-turn move.", onHit(pokemon) { - let NoSleepTalk = ['bide', 'sleeptalk']; - let moves = []; + const NoSleepTalk = ['bide', 'sleeptalk']; + const moves = []; for (const moveSlot of pokemon.moveSlots) { - let move = moveSlot.id; + const move = moveSlot.id; if (move && !NoSleepTalk.includes(move) && !this.dex.getMove(move).flags['charge']) { moves.push(move); } @@ -809,7 +815,7 @@ let BattleMovedex = { }, onSwitchIn(pokemon) { if (!pokemon.runImmunity('Ground')) return; - let damageAmounts = [0, 3]; + const damageAmounts = [0, 3]; this.damage(damageAmounts[this.effectData.layers] * pokemon.maxhp / 24); }, }, @@ -857,8 +863,7 @@ let BattleMovedex = { return; } if (move.id === 'twineedle') { - // @ts-ignore: Twineedle has move.secondaries defined - move.secondaries = move.secondaries.filter(p => !p.kingsrock); + move.secondaries = move.secondaries!.filter(p => !p.kingsrock); } if (move.drain) { this.add('-miss', source); @@ -866,12 +871,15 @@ let BattleMovedex = { return null; } if (move.category === 'Status') { - let SubBlocked = ['leechseed', 'lockon', 'mindreader', 'nightmare', 'painsplit', 'sketch']; + const SubBlocked = ['leechseed', 'lockon', 'mindreader', 'nightmare', 'painsplit', 'sketch']; if (move.id === 'swagger') { // this is safe, move is a copy delete move.volatileStatus; } - if (move.status || (move.boosts && move.id !== 'swagger') || move.volatileStatus === 'confusion' || SubBlocked.includes(move.id)) { + if ( + move.status || (move.boosts && move.id !== 'swagger') || + move.volatileStatus === 'confusion' || SubBlocked.includes(move.id) + ) { this.add('-activate', target, 'Substitute', '[block] ' + move.name); return null; } @@ -886,7 +894,7 @@ let BattleMovedex = { return damage; } if (damage > target.volatiles['substitute'].hp) { - damage = /** @type {number} */ (target.volatiles['substitute'].hp); + damage = target.volatiles['substitute'].hp as number; } target.volatiles['substitute'].hp -= damage; source.lastDamage = damage; @@ -899,7 +907,7 @@ let BattleMovedex = { this.damage(1, source, target, 'recoil'); } this.runEvent('AfterSubDamage', target, source, move, damage); - return 0; // hit + return this.HIT_SUBSTITUTE; }, onEnd(target) { this.add('-end', target, 'Substitute'); @@ -960,7 +968,7 @@ let BattleMovedex = { if (source.item || source.volatiles['gem']) { return; } - let yourItem = target.takeItem(source); + const yourItem = target.takeItem(source); if (!yourItem) { return; } @@ -1042,5 +1050,3 @@ let BattleMovedex = { priority: -1, }, }; - -exports.BattleMovedex = BattleMovedex; diff --git a/data/mods/gen2/random-teams.js b/data/mods/gen2/random-teams.ts similarity index 75% rename from data/mods/gen2/random-teams.js rename to data/mods/gen2/random-teams.ts index a6419e0ac8..a3c7c7ef58 100644 --- a/data/mods/gen2/random-teams.js +++ b/data/mods/gen2/random-teams.ts @@ -1,51 +1,49 @@ -'use strict'; +import RandomGen3Teams from '../gen3/random-teams'; +import {PRNG, PRNGSeed} from '../../../sim/prng'; -const RandomGen3Teams = require('../gen3/random-teams'); +export class RandomGen2Teams extends RandomGen3Teams { + slot: number; + constructor(format: string | Format, prng: PRNG | PRNGSeed | null) { + super(format, prng); + this.slot = 0; + } -class RandomGen2Teams extends RandomGen3Teams { randomTeam() { let pokemonLeft = 6; - /** @type {RandomTeamsTypes.RandomSet[]} */ - let pokemon = []; + const pokemon: RandomTeamsTypes.RandomSet[] = []; - /** @type {string[]} */ - let pokemonPool = []; - for (let id in this.dex.data.FormatsData) { - let species = this.dex.getSpecies(id); + const pokemonPool: string[] = []; + for (const id in this.dex.data.FormatsData) { + const species = this.dex.getSpecies(id); if (!species.isNonstandard && this.dex.data.FormatsData[id].randomSets) { pokemonPool.push(id); } } // Setup storage. - /**@type {{[k: string]: number}} */ - let tierCount = {}; - /**@type {{[k: string]: number}} */ - let typeCount = {}; - /**@type {{[k: string]: number}} */ - let weaknessCount = { + const tierCount: {[k: string]: number} = {}; + const typeCount: {[k: string]: number} = {}; + const weaknessCount: {[k: string]: number} = { Normal: 0, Fighting: 0, Flying: 0, Poison: 0, Ground: 0, Rock: 0, Bug: 0, Ghost: 0, Steel: 0, Fire: 0, Water: 0, Grass: 0, Electric: 0, Psychic: 0, Ice: 0, Dragon: 0, Dark: 0, }; - /**@type {{[k: string]: number}} */ - let resistanceCount = { + const resistanceCount: {[k: string]: number} = { Normal: 0, Fighting: 0, Flying: 0, Poison: 0, Ground: 0, Rock: 0, Bug: 0, Ghost: 0, Steel: 0, Fire: 0, Water: 0, Grass: 0, Electric: 0, Psychic: 0, Ice: 0, Dragon: 0, Dark: 0, }; - /**@type {{[k: string]: number}} */ - let restrictMoves = { + let restrictMoves: {[k: string]: number} = { reflect: 1, lightscreen: 1, rapidspin: 1, spikes: 1, bellydrum: 1, haze: 1, healbell: 1, thief: 1, phazing: 1, sleeptalk: 2, sleeping: 2, }; while (pokemonPool.length && pokemonLeft > 0) { - let species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); + const species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); if (!species.exists) continue; let skip = false; // Ensure 1 Uber at most // Ensure 2 mons of same tier at most (this includes OU,UUBL,UU,NU; other tiers not supported yet) - let tier = species.tier; + const tier = species.tier; switch (tier) { case 'Uber': if (tierCount['Uber']) skip = true; @@ -57,7 +55,7 @@ class RandomGen2Teams extends RandomGen3Teams { // Ensure the same type not more than twice // 33% discard single-type mon if that type already exists // 66% discard double-type mon if both types already exist - let types = species.types; + const types = species.types; if (types.length === 1) { if (typeCount[types[0]] > 1) skip = true; if (typeCount[types[0]] && this.randomChance(1, 3)) skip = true; @@ -68,18 +66,18 @@ class RandomGen2Teams extends RandomGen3Teams { // Ensure the weakness-resistance balance is 2 points or lower for all types, // but ensure no more than 3 pokemon weak to the same regardless. - let weaknesses = []; - for (let type in weaknessCount) { - let weak = this.dex.getImmunity(type, species) && this.dex.getEffectiveness(type, species) > 0; + const weaknesses = []; + for (const type in weaknessCount) { + const weak = this.dex.getImmunity(type, species) && this.dex.getEffectiveness(type, species) > 0; if (!weak) continue; if (weaknessCount[type] > 2 || weaknessCount[type] - resistanceCount[type] > 1) { skip = true; } weaknesses.push(type); } - let resistances = []; - for (let type in resistanceCount) { - let resist = !this.dex.getImmunity(type, species) || this.dex.getEffectiveness(type, species) < 0; + const resistances = []; + for (const type in resistanceCount) { + const resist = !this.dex.getImmunity(type, species) || this.dex.getEffectiveness(type, species) < 0; if (resist) resistances.push(type); } @@ -87,7 +85,8 @@ class RandomGen2Teams extends RandomGen3Teams { if (skip && pokemonPool.length + 1 > pokemonLeft) continue; // The set passes the randomTeam limitations. - let set = this.randomSet(species, restrictMoves, pokemon.length); + const set = this.randomSet(species, restrictMoves); + this.slot = pokemon.length; if (set.other && set.other.discard && pokemonPool.length + 1 > pokemonLeft) continue; // The set also passes the randomSet limitations. @@ -136,37 +135,24 @@ class RandomGen2Teams extends RandomGen3Teams { return pokemon; } - /** - * @param {string | Species} species - * @param {{[k: string]: number}} restrictMoves - * @param {number} [slot] - * @return {RandomTeamsTypes.RandomSet} - */ - randomSet(species, restrictMoves, slot) { - if (slot === undefined) slot = 1; + randomSet(species: string | Species, restrictMoves: {[k: string]: number}): RandomTeamsTypes.RandomSet { species = this.dex.getSpecies(species); if (!species.exists) species = this.dex.getSpecies('unown'); if (!species.randomSets || !species.randomSets.length) species = this.dex.getSpecies('unown'); let randomSetNumber = 0; - /**@type {RandomTeamsTypes.Gen2RandomSet} */ - // @ts-ignore - let set = species.randomSets[0]; - /**@type {string[]} */ - let moves = []; - /**@type {{[k: string]: number}} */ - let hasMove = {}; + let set: RandomTeamsTypes.Gen2RandomSet = species.randomSets![0]; + let moves: string[] = []; + let hasMove: {[k: string]: number} = {}; let item = ''; - let ivs = {hp: 30, atk: 30, def: 30, spa: 30, spd: 30, spe: 30}; + const ivs = {hp: 30, atk: 30, def: 30, spa: 30, spd: 30, spe: 30}; let discard = false; let rerollsLeft = 3; - /**@param {string} move */ - let isPhazingMove = function (move) { + const isPhazingMove = (move: string) => { return (move === "roar" || move === "whirlwind"); }; - /**@param {string} move */ - let isSleepMove = function (move) { + const isSleepMove = (move: string) => { return (move === "sleeppowder" || move === "lovelykiss" || move === "sing" || move === "hypnosis" || move === "spore"); }; @@ -177,11 +163,9 @@ class RandomGen2Teams extends RandomGen3Teams { moves = []; hasMove = {}; - // @ts-ignore - if (species.randomSets.length > 1) { + if (species.randomSets!.length > 1) { randomSetNumber = 15; - // @ts-ignore - for (const s of species.randomSets) { + for (const s of species.randomSets!) { if (randomSetNumber < s.chance) { set = s; } @@ -223,13 +207,18 @@ class RandomGen2Teams extends RandomGen3Teams { // many restrictMoves are also rare and/or useful all around, so encourage adding them once to the team // Start accounting for this after the first half of the team has been added let discourage = false; - if (!discard && slot > 3) { + if (!discard && this.slot > 3) { discourage = true; for (const moveid of moves) { - if (moveid === "sleeptalk" && restrictMoves['sleeptalk'] > 1) { discourage = false; break; } - if (moveid !== "bellydrum" && moveid !== "haze" && moveid !== "thief" && restrictMoves[moveid] > 0) { discourage = false; break; } - if (isPhazingMove(moveid) && restrictMoves['phazing'] > 0) { discourage = false; break; } - if (isSleepMove(moveid) && restrictMoves['sleeping'] > 1) { discourage = false; break; } + if ( + (moveid === "sleeptalk" && restrictMoves['sleeptalk'] > 1) || + (moveid !== "bellydrum" && moveid !== "haze" && moveid !== "thief" && restrictMoves[moveid] > 0) || + (isPhazingMove(moveid) && restrictMoves['phazing'] > 0) || + (isSleepMove(moveid) && restrictMoves['sleeping'] > 1) + ) { + discourage = false; + break; + } } } if (discourage && this.randomChance(1, 2)) discard = true; @@ -241,7 +230,7 @@ class RandomGen2Teams extends RandomGen3Teams { // Adjust ivs for hiddenpower for (const setMoveid of moves) { if (!setMoveid.startsWith('hiddenpower')) continue; - let hpType = setMoveid.substr(11, setMoveid.length); + const hpType = setMoveid.substr(11, setMoveid.length); switch (hpType) { case 'dragon': ivs.def = 28; break; case 'ice': ivs.def = 26; break; @@ -263,8 +252,7 @@ class RandomGen2Teams extends RandomGen3Teams { if (ivs.def === 28 || ivs.def === 24) ivs.hp -= 8; } - /** @type {{[k: string]: number}} */ - let levelScale = { + const levelScale: {[k: string]: number} = { LC: 90, // unused NFE: 84, // unused NU: 80, @@ -274,8 +262,7 @@ class RandomGen2Teams extends RandomGen3Teams { OU: 68, Uber: 64, }; - /** @type {{[k: string]: number}} */ - let customScale = { + const customScale: {[k: string]: number} = { Caterpie: 99, Kakuna: 99, Magikarp: 99, Metapod: 99, Weedle: 99, // unused Unown: 98, Wobbuffet: 82, Ditto: 82, Snorlax: 66, Nidoqueen: 70, @@ -301,12 +288,7 @@ class RandomGen2Teams extends RandomGen3Teams { }; } - /** - * @param {string[]} moves - * @param {{[k: string]: number}} hasMove - * @param {string[]} fillerMoves - */ - randomMove(moves, hasMove, fillerMoves) { + randomMove(moves: string[], hasMove: {[k: string]: number}, fillerMoves: string[]) { let index = 0; let done = false; @@ -328,4 +310,4 @@ class RandomGen2Teams extends RandomGen3Teams { } } -module.exports = RandomGen2Teams; +export default RandomGen2Teams; diff --git a/data/mods/gen2/rulesets.js b/data/mods/gen2/rulesets.ts similarity index 91% rename from data/mods/gen2/rulesets.js rename to data/mods/gen2/rulesets.ts index fbb638ab34..a1ec38a94f 100644 --- a/data/mods/gen2/rulesets.js +++ b/data/mods/gen2/rulesets.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedFormatsData}} */ -let BattleFormats = { +export const BattleFormats: {[k: string]: ModdedFormatsData} = { obtainablemoves: { inherit: true, banlist: [ @@ -37,5 +34,3 @@ let BattleFormats = { ], }, }; - -exports.BattleFormats = BattleFormats; diff --git a/data/mods/gen2/scripts.js b/data/mods/gen2/scripts.ts similarity index 87% rename from data/mods/gen2/scripts.js rename to data/mods/gen2/scripts.ts index b00c25be1d..b66162bf75 100644 --- a/data/mods/gen2/scripts.js +++ b/data/mods/gen2/scripts.ts @@ -1,17 +1,13 @@ -'use strict'; - /** * Gen 2 scripts. */ -/**@type {ModdedBattleScriptsData} */ -let BattleScripts = { +export const BattleScripts: ModdedBattleScriptsData = { inherit: 'gen3', gen: 2, // BattlePokemon scripts. pokemon: { getStat(statName, unboosted, unmodified, fastReturn) { - statName = /** @type {StatNameExceptHP} */(toID(statName)); // @ts-ignore - type checking prevents 'hp' from being passed, but we're paranoid if (statName === 'hp') throw new Error("Please read `maxhp` directly"); @@ -20,15 +16,14 @@ let BattleScripts = { // Stat boosts. if (!unboosted) { - // @ts-ignore let boost = this.boosts[statName]; if (boost > 6) boost = 6; if (boost < -6) boost = -6; if (boost >= 0) { - let boostTable = [1, 1.5, 2, 2.5, 3, 3.5, 4]; + const boostTable = [1, 1.5, 2, 2.5, 3, 3.5, 4]; stat = Math.floor(stat * boostTable[boost]); } else { - let numerators = [100, 66, 50, 40, 33, 28, 25]; + const numerators = [100, 66, 50, 40, 33, 28, 25]; stat = Math.floor(stat * numerators[-boost] / 100); } } @@ -50,13 +45,19 @@ let BattleScripts = { // Screens if (!unboosted) { - if ((this.side.sideConditions['reflect'] && statName === 'def') || (this.side.sideConditions['lightscreen'] && statName === 'spd')) { + if ( + (statName === 'def' && this.side.sideConditions['reflect']) || + (statName === 'spd' && this.side.sideConditions['lightscreen']) + ) { stat *= 2; } } - // Treat here the items. - if ((['Cubone', 'Marowak'].includes(this.species.name) && this.item === 'thickclub' && statName === 'atk') || (this.species.name === 'Pikachu' && this.item === 'lightball' && statName === 'spa')) { + // Handle boosting items + if ( + (['Cubone', 'Marowak'].includes(this.species.name) && this.item === 'thickclub' && statName === 'atk') || + (this.species.name === 'Pikachu' && this.item === 'lightball' && statName === 'spa') + ) { stat *= 2; } else if (this.forme === 'Ditto' && this.item === 'metalpowder' && ['def', 'spd'].includes(statName)) { stat *= 1.5; @@ -66,28 +67,20 @@ let BattleScripts = { }, boostBy(boost) { let delta = 0; - for (let i in boost) { - // @ts-ignore - delta = boost[i]; - // @ts-ignore - if (delta > 0 && this.getStat(i, false, true, true) === 999) { + let i: BoostName; + for (i in boost) { + delta = boost[i]!; + if (delta > 0 && this.getStat(i as StatNameExceptHP, false, true) === 999) { delta = 0; continue; } - // @ts-ignore this.boosts[i] += delta; - // @ts-ignore if (this.boosts[i] > 6) { - // @ts-ignore delta -= this.boosts[i] - 6; - // @ts-ignore this.boosts[i] = 6; } - // @ts-ignore if (this.boosts[i] < -6) { - // @ts-ignore delta -= this.boosts[i] - (-6); - // @ts-ignore this.boosts[i] = -6; } } @@ -99,7 +92,7 @@ let BattleScripts = { let move = this.dex.getActiveMove(moveOrMoveName); let target = this.getTarget(pokemon, move, targetLoc); if (!sourceEffect && move.id !== 'struggle') { - let changedMove = this.runEvent('OverrideAction', pokemon, target, move); + const changedMove = this.runEvent('OverrideAction', pokemon, target, move); if (changedMove && changedMove !== true) { move = this.dex.getActiveMove(changedMove); target = this.getRandomTarget(pokemon, move); @@ -146,11 +139,10 @@ let BattleScripts = { if (!move.selfSwitch && target && target.hp > 0) this.runEvent('AfterMoveSelf', pokemon, target, move); }, tryMoveHit(target, pokemon, move) { - let positiveBoostTable = [1, 1.33, 1.66, 2, 2.33, 2.66, 3]; - let negativeBoostTable = [1, 0.75, 0.6, 0.5, 0.43, 0.36, 0.33]; - let doSelfDestruct = true; - /** @type {number | false | undefined} */ - let damage = 0; + const positiveBoostTable = [1, 1.33, 1.66, 2, 2.33, 2.66, 3]; + const negativeBoostTable = [1, 0.75, 0.6, 0.5, 0.43, 0.36, 0.33]; + const doSelfDestruct = true; + let damage: number | false | undefined = 0; if (move.selfdestruct && doSelfDestruct) { this.faint(pokemon, pokemon, move); @@ -178,7 +170,10 @@ let BattleScripts = { move.ignoreImmunity = (move.category === 'Status'); } - if ((!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) && !target.runImmunity(move.type, true)) { + if ( + (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) && + !target.runImmunity(move.type, true) + ) { return false; } @@ -194,7 +189,6 @@ let BattleScripts = { return false; } - /** @type {number | true} */ let accuracy = move.accuracy; if (move.alwaysHit) { accuracy = true; @@ -258,11 +252,10 @@ let BattleScripts = { } hits = Math.floor(hits); let nullDamage = true; - /**@type {number | undefined | false} */ - let moveDamage; + let moveDamage: number | undefined | false; - let isSleepUsable = move.sleepUsable || this.dex.getMove(move.sourceEffect).sleepUsable; - let i; + const isSleepUsable = move.sleepUsable || this.dex.getMove(move.sourceEffect).sleepUsable; + let i: number; for (i = 0; i < hits && target.hp && pokemon.hp; i++) { if (pokemon.status === 'slp' && !isSleepUsable) break; move.hit = i + 1; @@ -296,15 +289,12 @@ let BattleScripts = { } return damage; }, - /** @return {number | undefined | false} */ moveHit(target, pokemon, move, moveData, isSecondary, isSelf) { - /** @type {number | false | null | undefined} */ - let damage = undefined; + let damage: number | false | null | undefined = undefined; move = this.dex.getActiveMove(move); if (!moveData) moveData = move; - /**@type {?boolean | number} */ - let hitResult = true; + let hitResult: boolean | number | null = true; if (move.target === 'all' && !isSelf) { hitResult = this.singleEvent('TryHitField', moveData, {}, target, pokemon, move); @@ -334,8 +324,7 @@ let BattleScripts = { } if (target) { - /**@type {?boolean | number} */ - let didSomething = false; + let didSomething: boolean | number | null = false; damage = this.getDamage(pokemon, target, moveData); if ((damage || damage === 0) && !target.fainted) { @@ -358,7 +347,10 @@ let BattleScripts = { } if (moveData.boosts && !target.fainted) { - if (pokemon.volatiles['lockon'] && target === pokemon.volatiles['lockon'].source && target.isSemiInvulnerable() && !isSelf) { + if ( + pokemon.volatiles['lockon'] && target === pokemon.volatiles['lockon'].source && + target.isSemiInvulnerable() && !isSelf + ) { if (!isSecondary) this.add('-fail', target); return false; } @@ -366,7 +358,7 @@ let BattleScripts = { didSomething = didSomething || hitResult; } if (moveData.heal && !target.fainted) { - let d = target.heal(Math.round(target.maxhp * moveData.heal[0] / moveData.heal[1])); + const d = target.heal(Math.round(target.maxhp * moveData.heal[0] / moveData.heal[1])); if (!d && d !== 0) { this.add('-fail', target); this.debug('heal interrupted'); @@ -456,7 +448,7 @@ let BattleScripts = { } // Multi-hit moves only roll for status once if (!move.multihit || move.lastHit) { - let effectChance = Math.floor((secondary.chance || 100) * 255 / 100); + const effectChance = Math.floor((secondary.chance || 100) * 255 / 100); if (typeof secondary.chance === 'undefined' || this.randomChance(effectChance, 256)) { this.moveHit(target, pokemon, move, secondary, true, isSelf); } else if (effectChance === 255) { @@ -483,13 +475,13 @@ let BattleScripts = { if (typeof move === 'string') { move = this.dex.getActiveMove(move); } else if (typeof move === 'number') { - move = /** @type {ActiveMove} */ ({ + move = { basePower: move, type: '???', category: 'Physical', willCrit: false, flags: {}, - }); + } as unknown as ActiveMove; } // Let's test for immunities. @@ -524,11 +516,10 @@ let BattleScripts = { if (!move.defensiveCategory) move.defensiveCategory = move.category; // '???' is typeless damage: used for Struggle and Confusion etc if (!move.type) move.type = '???'; - let type = move.type; + const type = move.type; // We get the base power and apply basePowerCallback if necessary - /** @type {number | false | null | undefined} */ - let basePower = move.basePower; + let basePower: number | false | null | undefined = move.basePower; if (move.basePowerCallback) { basePower = move.basePowerCallback.call(this, pokemon, target, move); } @@ -543,7 +534,7 @@ let BattleScripts = { // Checking for the move's Critical Hit ratio let critRatio = this.runEvent('ModifyCritRatio', pokemon, target, move, move.critRatio || 0); critRatio = this.dex.clampIntRange(critRatio, 0, 5); - let critMult = [0, 16, 8, 4, 3, 2]; + const critMult = [0, 16, 8, 4, 3, 2]; let isCrit = move.willCrit || false; if (typeof move.willCrit === 'undefined') { if (critRatio) { @@ -559,8 +550,7 @@ let BattleScripts = { if (basePower) { // confusion damage if (move.isSelfHit) { - // @ts-ignore - move.type = move.baseMoveType; + move.type = move.baseMoveType!; basePower = this.runEvent('BasePower', pokemon, target, move, basePower, true); move.type = '???'; } else { @@ -583,12 +573,10 @@ let BattleScripts = { } let attacker = pokemon; - let defender = target; + const defender = target; if (move.useTargetOffensive) attacker = target; - /** @type {StatNameExceptHP} */ - let atkType = (move.category === 'Physical') ? 'atk' : 'spa'; - /** @type {StatNameExceptHP} */ - let defType = (move.defensiveCategory === 'Physical') ? 'def' : 'spd'; + let atkType: StatNameExceptHP = (move.category === 'Physical') ? 'atk' : 'spa'; + const defType: StatNameExceptHP = (move.defensiveCategory === 'Physical') ? 'def' : 'spd'; if (move.useSourceDefensiveAsOffensive) atkType = defType; let unboosted = false; let noburndrop = false; @@ -600,7 +588,6 @@ let BattleScripts = { if (!suppressMessages) this.add('-crit', target); // Stat level modifications are ignored if they are neutral to or favour the defender. // Reflect and Light Screen defensive boosts are only ignored if stat level modifications were also ignored as a result of that. - // @ts-ignore if (attacker.boosts[atkType] <= defender.boosts[defType]) { unboosted = true; noburndrop = true; @@ -629,8 +616,10 @@ let BattleScripts = { } if (move.id === 'present') { - /**@type {{[k: string]: number}} */ - const typeIndexes = {Normal: 0, Fighting: 1, Flying: 2, Poison: 3, Ground: 4, Rock: 5, Bug: 7, Ghost: 8, Steel: 9, Fire: 20, Water: 21, Grass: 22, Electric: 23, Psychic: 24, Ice: 25, Dragon: 26, Dark: 27}; + const typeIndexes: {[k: string]: number} = { + Normal: 0, Fighting: 1, Flying: 2, Poison: 3, Ground: 4, Rock: 5, Bug: 7, Ghost: 8, Steel: 9, + Fire: 20, Water: 21, Grass: 22, Electric: 23, Psychic: 24, Ice: 25, Dragon: 26, Dark: 27, + }; attack = 10; const attackerLastType = attacker.getTypes().slice(-1)[0]; @@ -673,7 +662,10 @@ let BattleScripts = { // Weather modifiers if ((this.field.isWeather('raindance') && type === 'Water') || (this.field.isWeather('sunnyday') && type === 'Fire')) { damage = Math.floor(damage * 1.5); - } else if ((this.field.isWeather('raindance') && (type === 'Fire' || move.id === 'solarbeam')) || (this.field.isWeather('sunnyday') && type === 'Water')) { + } else if ( + (this.field.isWeather('raindance') && (type === 'Fire' || move.id === 'solarbeam')) || + (this.field.isWeather('sunnyday') && type === 'Water') + ) { damage = Math.floor(damage / 2); } @@ -683,7 +675,7 @@ let BattleScripts = { } // Type effectiveness - let totalTypeMod = target.runEffectiveness(move); + const totalTypeMod = target.runEffectiveness(move); // Super effective attack if (totalTypeMod > 0) { if (!suppressMessages) this.add('-supereffective', target); @@ -716,5 +708,3 @@ let BattleScripts = { return damage; }, }; - -exports.BattleScripts = BattleScripts; diff --git a/data/mods/gen2/statuses.js b/data/mods/gen2/statuses.ts similarity index 89% rename from data/mods/gen2/statuses.js rename to data/mods/gen2/statuses.ts index b6c1cc20fd..d0aea902fd 100644 --- a/data/mods/gen2/statuses.js +++ b/data/mods/gen2/statuses.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedPureEffectData}} */ -let BattleStatuses = { +export const BattleStatuses: {[k: string]: ModdedPureEffectData} = { brn: { name: 'brn', id: 'brn', @@ -111,11 +108,12 @@ let BattleStatuses = { }, onAfterMoveSelfPriority: 3, onAfterMoveSelf(pokemon) { - this.damage(this.dex.clampIntRange(Math.floor(pokemon.maxhp / 16), 1) * pokemon.volatiles['residualdmg'].counter, pokemon, pokemon); + const damage = this.dex.clampIntRange(Math.floor(pokemon.maxhp / 16), 1) * pokemon.volatiles['residualdmg'].counter; + this.damage(damage, pokemon, pokemon); }, onSwitchIn(pokemon) { // Regular poison status and damage after a switchout -> switchin. - pokemon.status = /** @type {ID} */('psn'); + pokemon.status = 'psn' as ID; this.add('-status', pokemon, 'psn', '[silent]'); }, onAfterSwitchInSelf(pokemon) { @@ -146,7 +144,7 @@ let BattleStatuses = { if (this.randomChance(1, 2)) { return; } - move = /** @type {ActiveMove} */ ({ + move = { basePower: 40, type: '???', baseMoveType: move.type, @@ -156,8 +154,8 @@ let BattleStatuses = { noDamageVariance: true, flags: {}, selfdestruct: move.selfdestruct, - }); - let damage = this.getDamage(pokemon, pokemon, move); + } as unknown as ActiveMove; + const damage = this.getDamage(pokemon, pokemon, move); if (typeof damage !== 'number') throw new Error("Confusion damage not dealt"); this.directDamage(damage); return false; @@ -198,7 +196,7 @@ let BattleStatuses = { delete pokemon.volatiles['lockedmove']; }, onBeforeTurn(pokemon) { - let move = this.dex.getMove(this.effectData.move); + const move = this.dex.getMove(this.effectData.move); if (move.id) { this.debug('Forcing into ' + move.id); this.queue.changeAction(pokemon, {choice: 'move', moveid: move.id}); @@ -220,7 +218,7 @@ let BattleStatuses = { this.effectData.counter = 127; }, onStallMove() { - let counter = Math.floor(this.effectData.counter) || 127; + const counter = Math.floor(this.effectData.counter) || 127; this.debug("Success chance: " + Math.round(counter * 1000 / 255) / 10 + "% (" + counter + "/255)"); return this.randomChance(counter, 255); }, @@ -246,13 +244,12 @@ let BattleStatuses = { }, }; -/** - * @param {Battle} battle - * @param {Pokemon} pokemon - */ -function residualdmg(battle, pokemon) { +function residualdmg(battle: Battle, pokemon: Pokemon) { if (pokemon.volatiles['residualdmg']) { - battle.damage(battle.dex.clampIntRange(Math.floor(pokemon.maxhp / 16) * pokemon.volatiles['residualdmg'].counter, 1), pokemon); + const residualDmg = battle.dex.clampIntRange( + Math.floor(pokemon.maxhp / 16) * pokemon.volatiles['residualdmg'].counter, 1 + ); + battle.damage(residualDmg, pokemon); battle.hint("In Gen 2, Toxic's counter is retained through Baton Pass/Heal Bell and applies to PSN/BRN.", true); } else { battle.damage(battle.dex.clampIntRange(Math.floor(pokemon.maxhp / 8), 1), pokemon); diff --git a/data/mods/gen2/typechart.js b/data/mods/gen2/typechart.ts similarity index 87% rename from data/mods/gen2/typechart.js rename to data/mods/gen2/typechart.ts index f65e41d575..6c06b622e3 100644 --- a/data/mods/gen2/typechart.js +++ b/data/mods/gen2/typechart.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedTypeData}} */ -let BattleTypeChart = { +export const BattleTypeChart: {[k: string]: ModdedTypeData} = { Fire: { inherit: true, damageTaken: { @@ -71,5 +68,3 @@ let BattleTypeChart = { }, }, }; - -exports.BattleTypeChart = BattleTypeChart; diff --git a/data/mods/gen3/abilities.js b/data/mods/gen3/abilities.ts similarity index 95% rename from data/mods/gen3/abilities.js rename to data/mods/gen3/abilities.ts index a7129c8376..def284c670 100644 --- a/data/mods/gen3/abilities.js +++ b/data/mods/gen3/abilities.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedAbilityData}} */ -let BattleAbilities = { +export const BattleAbilities: {[k: string]: ModdedAbilityData} = { cutecharm: { inherit: true, desc: "There is a 1/3 chance a Pokemon making contact with this Pokemon will become infatuated if it is of the opposite gender.", @@ -20,7 +17,7 @@ let BattleAbilities = { shortDesc: "10% chance of poison/paralysis/sleep on others making contact with this Pokemon.", onDamagingHit(damage, target, source, move) { if (move.flags['contact'] && !source.status) { - let r = this.random(300); + const r = this.random(300); if (r < 10) { source.setStatus('slp', target); } else if (r < 20) { @@ -176,10 +173,10 @@ let BattleAbilities = { inherit: true, onUpdate(pokemon) { if (!pokemon.isStarted) return; - let target = pokemon.side.foe.randomActive(); + const target = pokemon.side.foe.randomActive(); if (!target || target.fainted) return; - let ability = target.getAbility(); - let bannedAbilities = ['forecast', 'multitype', 'trace']; + const ability = target.getAbility(); + const bannedAbilities = ['forecast', 'multitype', 'trace']; if (bannedAbilities.includes(target.ability)) { return; } @@ -223,5 +220,3 @@ let BattleAbilities = { shortDesc: "This Pokemon is only damaged by supereffective moves and indirect damage.", }, }; - -exports.BattleAbilities = BattleAbilities; diff --git a/data/mods/gen3/formats-data.js b/data/mods/gen3/formats-data.ts similarity index 99% rename from data/mods/gen3/formats-data.js rename to data/mods/gen3/formats-data.ts index f9ffcd8e64..0f5e1aef9e 100644 --- a/data/mods/gen3/formats-data.js +++ b/data/mods/gen3/formats-data.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesFormatsData}} */ -let BattleFormatsData = { +export const BattleFormatsData: {[k: string]: ModdedSpeciesFormatsData} = { bulbasaur: { tier: "LC", }, @@ -1398,5 +1395,3 @@ let BattleFormatsData = { tier: "Uber", }, }; - -exports.BattleFormatsData = BattleFormatsData; diff --git a/data/mods/gen3/items.js b/data/mods/gen3/items.ts similarity index 81% rename from data/mods/gen3/items.js rename to data/mods/gen3/items.ts index db38b385b7..6826474895 100644 --- a/data/mods/gen3/items.js +++ b/data/mods/gen3/items.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedItemData}} */ -let BattleItems = { +export const BattleItems: {[k: string]: ModdedItemData} = { aguavberry: { inherit: true, onUpdate() {}, @@ -123,7 +120,9 @@ let BattleItems = { kingsrock: { inherit: true, onModifyMove(move) { - let affectedByKingsRock = ['aerialace', 'aeroblast', 'aircutter', 'armthrust', 'barrage', 'beatup', 'bide', 'bind', 'blastburn', 'bonerush', 'bonemerang', 'bounce', 'brickbreak', 'bulletseed', 'clamp', 'cometpunch', 'crabhammer', 'crosschop', 'cut', 'dig', 'dive', 'doublekick', 'doubleslap', 'doubleedge', 'dragonbreath', 'dragonclaw', 'dragonrage', 'drillpeck', 'earthquake', 'eggbomb', 'endeavor', 'eruption', 'explosion', 'extremespeed', 'falseswipe', 'feintattack', 'firespin', 'flail', 'fly', 'frenzyplant', 'frustration', 'furyattack', 'furycutter', 'furyswipes', 'gust', 'hiddenpower', 'highjumpkick', 'hornattack', 'hydrocannon', 'hydropump', 'hyperbeam', 'iceball', 'iciclespear', 'jumpkick', 'karatechop', 'leafblade', 'lowkick', 'machpunch', 'magicalleaf', 'magnitude', 'megakick', 'megapunch', 'megahorn', 'meteormash', 'mudshot', 'muddywater', 'nightshade', 'outrage', 'overheat', 'payday', 'peck', 'petaldance', 'pinmissile', 'poisontail', 'pound', 'psychoboost', 'psywave', 'quickattack', 'rage', 'rapidspin', 'razorleaf', 'razorwind', 'return', 'revenge', 'reversal', 'rockblast', 'rockthrow', 'rollingkick', 'rollout', 'sandtomb', 'scratch', 'seismictoss', 'selfdestruct', 'shadowpunch', 'shockwave', 'signalbeam', 'silverwind', 'skullbash', 'skyattack', 'skyuppercut', 'slam', 'slash', 'snore', 'solarbeam', 'sonicboom', 'spikecannon', 'spitup', 'steelwing', 'strength', 'struggle', 'submission', 'surf', 'swift', 'tackle', 'takedown', 'thrash', 'tickle', 'triplekick', 'twister', 'uproar', 'visegrip', 'vinewhip', 'vitalthrow', 'volttackle', 'watergun', 'waterpulse', 'waterfall', 'weatherball', 'whirlpool', 'wingattack', 'wrap']; + const affectedByKingsRock = [ + 'aerialace', 'aeroblast', 'aircutter', 'armthrust', 'barrage', 'beatup', 'bide', 'bind', 'blastburn', 'bonerush', 'bonemerang', 'bounce', 'brickbreak', 'bulletseed', 'clamp', 'cometpunch', 'crabhammer', 'crosschop', 'cut', 'dig', 'dive', 'doublekick', 'doubleslap', 'doubleedge', 'dragonbreath', 'dragonclaw', 'dragonrage', 'drillpeck', 'earthquake', 'eggbomb', 'endeavor', 'eruption', 'explosion', 'extremespeed', 'falseswipe', 'feintattack', 'firespin', 'flail', 'fly', 'frenzyplant', 'frustration', 'furyattack', 'furycutter', 'furyswipes', 'gust', 'hiddenpower', 'highjumpkick', 'hornattack', 'hydrocannon', 'hydropump', 'hyperbeam', 'iceball', 'iciclespear', 'jumpkick', 'karatechop', 'leafblade', 'lowkick', 'machpunch', 'magicalleaf', 'magnitude', 'megakick', 'megapunch', 'megahorn', 'meteormash', 'mudshot', 'muddywater', 'nightshade', 'outrage', 'overheat', 'payday', 'peck', 'petaldance', 'pinmissile', 'poisontail', 'pound', 'psychoboost', 'psywave', 'quickattack', 'rage', 'rapidspin', 'razorleaf', 'razorwind', 'return', 'revenge', 'reversal', 'rockblast', 'rockthrow', 'rollingkick', 'rollout', 'sandtomb', 'scratch', 'seismictoss', 'selfdestruct', 'shadowpunch', 'shockwave', 'signalbeam', 'silverwind', 'skullbash', 'skyattack', 'skyuppercut', 'slam', 'slash', 'snore', 'solarbeam', 'sonicboom', 'spikecannon', 'spitup', 'steelwing', 'strength', 'struggle', 'submission', 'surf', 'swift', 'tackle', 'takedown', 'thrash', 'tickle', 'triplekick', 'twister', 'uproar', 'visegrip', 'vinewhip', 'vitalthrow', 'volttackle', 'watergun', 'waterpulse', 'waterfall', 'weatherball', 'whirlpool', 'wingattack', 'wrap', + ]; if (affectedByKingsRock.includes(move.id)) { if (!move.secondaries) move.secondaries = []; move.secondaries.push({ @@ -367,5 +366,3 @@ let BattleItems = { }, }, }; - -exports.BattleItems = BattleItems; diff --git a/data/mods/gen3/moves.js b/data/mods/gen3/moves.ts similarity index 95% rename from data/mods/gen3/moves.js rename to data/mods/gen3/moves.ts index 751a617b29..271e40976f 100644 --- a/data/mods/gen3/moves.js +++ b/data/mods/gen3/moves.ts @@ -2,10 +2,7 @@ * Gen 3 moves */ -'use strict'; - -/**@type {{[k: string]: ModdedMoveData}} */ -let BattleMovedex = { +export const BattleMovedex: {[k: string]: ModdedMoveData} = { absorb: { inherit: true, desc: "The user recovers 1/2 the HP lost by the target, rounded down.", @@ -88,9 +85,7 @@ let BattleMovedex = { } target = possibleTarget; } - /** @type {ActiveMove} */ - // @ts-ignore - let moveData = { + const moveData = { id: /** @type {ID} */('bide'), name: "Bide", accuracy: 100, @@ -100,7 +95,7 @@ let BattleMovedex = { flags: {contact: 1, protect: 1}, effectType: 'Move', type: 'Normal', - }; + } as unknown as ActiveMove; this.tryMoveHit(target, pokemon, moveData); return false; } @@ -166,8 +161,8 @@ let BattleMovedex = { inherit: true, desc: "The user's type changes to match the original type of one of its known moves besides Curse, at random, but not either of its current types. Fails if the user cannot change its type, or if this move would only be able to select one of the user's current types.", onHit(target) { - let possibleTypes = target.moveSlots.map(moveSlot => { - let move = this.dex.getMove(moveSlot.id); + const possibleTypes = target.moveSlots.map(moveSlot => { + const move = this.dex.getMove(moveSlot.id); if (move.id !== 'curse' && !target.hasType(move.type)) { return move.type; } @@ -176,7 +171,7 @@ let BattleMovedex = { if (!possibleTypes.length) { return false; } - let type = this.sample(possibleTypes); + const type = this.sample(possibleTypes); if (!target.setType(type)) return false; this.add('-start', target, 'typechange', type); @@ -190,9 +185,10 @@ let BattleMovedex = { inherit: true, desc: "Deals damage to the last opposing Pokemon to hit the user with a physical attack this turn equal to twice the HP lost by the user from that attack. If that opposing Pokemon's position is no longer in use and there is another opposing Pokemon on the field, the damage is done to it instead. This move considers Hidden Power as Normal type, and only the last hit of a multi-hit attack is counted. Fails if the user was not hit by an opposing Pokemon's physical attack this turn, or if the user did not lose HP from the attack.", damageCallback(pokemon) { - let lastAttackedBy = pokemon.getLastAttackedBy(); - if (lastAttackedBy && lastAttackedBy.move && lastAttackedBy.thisTurn && (this.getCategory(lastAttackedBy.move) === 'Physical' || this.dex.getMove(lastAttackedBy.move).id === 'hiddenpower')) { - // @ts-ignore + const lastAttackedBy = pokemon.getLastAttackedBy(); + if (!lastAttackedBy || !lastAttackedBy.move || !lastAttackedBy.thisTurn) return false; + + if (this.getCategory(lastAttackedBy.move) === 'Physical') { return 2 * lastAttackedBy.damage; } return false; @@ -211,7 +207,7 @@ let BattleMovedex = { }, onDamagePriority: -101, onDamage(damage, target, source, effect) { - if (effect && effect.effectType === 'Move' && source.side !== target.side && this.getCategory(effect.id) === 'Physical') { + if (effect.effectType === 'Move' && source.side !== target.side && this.getCategory(effect.id) === 'Physical') { this.effectData.position = source.position; this.effectData.damage = 2 * damage; } @@ -302,15 +298,15 @@ let BattleMovedex = { inherit: true, onTry(source, target) { if (!target.side.addSlotCondition(target, 'futuremove')) return false; - let moveData = /** @type {ActiveMove} */ ({ + const moveData = { name: "Doom Desire", basePower: 120, category: "Physical", flags: {}, willCrit: false, type: '???', - }); - let damage = this.getDamage(source, target, moveData, true); + } as unknown as ActiveMove; + const damage = this.getDamage(source, target, moveData, true); Object.assign(target.side.slotConditions[target.position]['futuremove'], { duration: 3, move: 'doomdesire', @@ -355,9 +351,12 @@ let BattleMovedex = { return this.random(3, 7); }, onStart(target, source) { - let noEncore = ['encore', 'mimic', 'mirrormove', 'sketch', 'struggle', 'transform']; - let moveIndex = target.lastMove ? target.moves.indexOf(target.lastMove.id) : -1; - if (!target.lastMove || noEncore.includes(target.lastMove.id) || !target.moveSlots[moveIndex] || target.moveSlots[moveIndex].pp <= 0) { + const noEncore = ['encore', 'mimic', 'mirrormove', 'sketch', 'struggle', 'transform']; + const moveIndex = target.lastMove ? target.moves.indexOf(target.lastMove.id) : -1; + if ( + !target.lastMove || noEncore.includes(target.lastMove.id) || + !target.moveSlots[moveIndex] || target.moveSlots[moveIndex].pp <= 0 + ) { // it failed this.add('-fail', source); this.attrLastMove('[still]'); @@ -374,7 +373,10 @@ let BattleMovedex = { }, onResidualOrder: 13, onResidual(target) { - if (target.moves.includes(this.effectData.move) && target.moveSlots[target.moves.indexOf(this.effectData.move)].pp <= 0) { + if ( + target.moves.includes(this.effectData.move) && + target.moveSlots[target.moves.indexOf(this.effectData.move)].pp <= 0 + ) { // early termination if you run out of PP target.removeVolatile('encore'); } @@ -474,7 +476,7 @@ let BattleMovedex = { category: "Physical", onModifyMove(move, pokemon) { move.type = pokemon.hpType || 'Dark'; - let specialTypes = ['Fire', 'Water', 'Grass', 'Ice', 'Electric', 'Dark', 'Psychic', 'Dragon']; + const specialTypes = ['Fire', 'Water', 'Grass', 'Ice', 'Electric', 'Dark', 'Psychic', 'Dragon']; move.category = specialTypes.includes(move.type) ? 'Special' : 'Physical'; }, }, @@ -485,7 +487,7 @@ let BattleMovedex = { shortDesc: "If miss, user takes 1/2 damage it would've dealt.", onMoveFail(target, source, move) { if (target.runImmunity('Fighting')) { - let damage = this.getDamage(source, target, move, true); + const damage = this.getDamage(source, target, move, true); if (typeof damage !== 'number') throw new Error("HJK recoil failed"); this.damage(this.dex.clampIntRange(damage / 2, 1, Math.floor(target.maxhp / 2)), source, source, move); } @@ -511,7 +513,7 @@ let BattleMovedex = { shortDesc: "If miss, user takes 1/2 damage it would've dealt.", onMoveFail(target, source, move) { if (target.runImmunity('Fighting')) { - let damage = this.getDamage(source, target, move, true); + const damage = this.getDamage(source, target, move, true); if (typeof damage !== 'number') throw new Error("Jump Kick didn't recoil"); this.damage(this.dex.clampIntRange(damage / 2, 1, Math.floor(target.maxhp / 2)), source, source, move); } @@ -583,7 +585,7 @@ let BattleMovedex = { }, onDamagePriority: -101, onDamage(damage, target, source, effect) { - if (effect && effect.effectType === 'Move' && source.side !== target.side && this.getCategory(effect.id) === 'Special') { + if (effect.effectType === 'Move' && source.side !== target.side && this.getCategory(effect.id) === 'Special') { this.effectData.position = source.position; this.effectData.damage = 2 * damage; } @@ -595,9 +597,14 @@ let BattleMovedex = { desc: "The user uses the last move that successfully targeted the user. The copied move is used with no specific target. Fails if no move has targeted the user, if the move missed, failed, or had no effect on the user, or if the move cannot be copied by this move.", onTryHit() { }, onHit(pokemon) { - let noMirror = ['assist', 'curse', 'doomdesire', 'focuspunch', 'futuresight', 'magiccoat', 'metronome', 'mimic', 'mirrormove', 'naturepower', 'psychup', 'roleplay', 'sketch', 'sleeptalk', 'spikes', 'spitup', 'taunt', 'teeterdance', 'transform']; - let lastAttackedBy = pokemon.getLastAttackedBy(); - if (!lastAttackedBy || !lastAttackedBy.source.lastMove || !lastAttackedBy.move || noMirror.includes(lastAttackedBy.move) || !lastAttackedBy.source.hasMove(lastAttackedBy.move)) { + const noMirror = [ + 'assist', 'curse', 'doomdesire', 'focuspunch', 'futuresight', 'magiccoat', 'metronome', 'mimic', 'mirrormove', 'naturepower', 'psychup', 'roleplay', 'sketch', 'sleeptalk', 'spikes', 'spitup', 'taunt', 'teeterdance', 'transform', + ]; + const lastAttackedBy = pokemon.getLastAttackedBy(); + if (!lastAttackedBy || !lastAttackedBy.source.lastMove || !lastAttackedBy.move) { + return false; + } + if (noMirror.includes(lastAttackedBy.move) || !lastAttackedBy.source.hasMove(lastAttackedBy.move)) { return false; } this.useMove(lastAttackedBy.move, pokemon); @@ -741,11 +748,11 @@ let BattleMovedex = { } }, onHit(pokemon) { - let moves = []; + const moves = []; for (const moveSlot of pokemon.moveSlots) { - let move = moveSlot.id; - let pp = moveSlot.pp; - let NoSleepTalk = ['assist', 'bide', 'focuspunch', 'metronome', 'mirrormove', 'sleeptalk', 'uproar']; + const move = moveSlot.id; + const pp = moveSlot.pp; + const NoSleepTalk = ['assist', 'bide', 'focuspunch', 'metronome', 'mirrormove', 'sleeptalk', 'uproar']; if (move && !(NoSleepTalk.includes(move) || this.dex.getMove(move).flags['charge'])) { moves.push({move: move, pp: pp}); } @@ -753,7 +760,7 @@ let BattleMovedex = { if (!moves.length) { return false; } - let randomMove = this.sample(moves); + const randomMove = this.sample(moves); if (!randomMove.pp) { this.add('cant', pokemon, 'nopp', randomMove.move); return; @@ -787,7 +794,7 @@ let BattleMovedex = { desc: "Causes the target's last move used to lose 2 to 5 PP, at random. Fails if the target has not made a move, if the move has 0 or 1 PP, or if it no longer knows the move.", shortDesc: "Lowers the PP of the target's last move by 2-5.", onHit(target) { - let roll = this.random(2, 6); + const roll = this.random(2, 6); if (target.lastMove && target.deductPP(target.lastMove.id, roll)) { this.add("-activate", target, 'move: Spite', target.lastMove.id, roll); return; diff --git a/data/mods/gen3/random-teams.js b/data/mods/gen3/random-teams.ts similarity index 89% rename from data/mods/gen3/random-teams.js rename to data/mods/gen3/random-teams.ts index 50e0476cd9..8116ebdf78 100644 --- a/data/mods/gen3/random-teams.js +++ b/data/mods/gen3/random-teams.ts @@ -1,26 +1,27 @@ -'use strict'; +/* eslint max-len: ["error", 240] */ -const RandomGen4Teams = require('../gen4/random-teams'); +import RandomGen4Teams from '../gen4/random-teams'; +import {PRNG, PRNGSeed} from '../../../sim/prng'; -class RandomGen3Teams extends RandomGen4Teams { - /** - * @param {string | Species} species - * @param {RandomTeamsTypes.TeamDetails} [teamDetails] - * @return {RandomTeamsTypes.RandomSet} - */ - randomSet(species, teamDetails = {}) { - let baseSpecies = (species = this.dex.getSpecies(species)); +export class RandomGen3Teams extends RandomGen4Teams { + hasWobbuffet: boolean; + + constructor(format: string | Format, prng: PRNG | PRNGSeed | null) { + super(format, prng); + this.hasWobbuffet = false; + } + + randomSet(species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}): RandomTeamsTypes.RandomSet { + const baseSpecies = (species = this.dex.getSpecies(species)); let forme = species.name; - if (species.battleOnly) forme = /** @type {string} */ (species.battleOnly); + if (species.battleOnly && typeof species.battleOnly === 'string') forme = species.battleOnly; - // @ts-ignore - let movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id].learnset)).slice(); - /**@type {string[]} */ - let moves = []; + const movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id]!.learnset!)).slice(); + const moves: string[] = []; let ability = ''; let item = ''; - let evs = { + const evs = { hp: 85, atk: 85, def: 85, @@ -36,14 +37,12 @@ class RandomGen3Teams extends RandomGen4Teams { spd: 31, spe: 31, }; - /**@type {{[k: string]: true}} */ - let hasType = {}; + const hasType: {[k: string]: true} = {}; hasType[species.types[0]] = true; if (species.types[1]) { hasType[species.types[1]] = true; } - /**@type {{[k: string]: true}} */ - let hasAbility = {}; + const hasAbility: {[k: string]: true} = {}; hasAbility[species.abilities[0]] = true; if (species.abilities[1]) { hasAbility[species.abilities[1]] = true; @@ -53,31 +52,30 @@ class RandomGen3Teams extends RandomGen4Teams { if (setMoveid.startsWith('hiddenpower')) availableHP++; } - let recoveryMoves = [ + const recoveryMoves = [ 'milkdrink', 'moonlight', 'morningsun', 'painsplit', 'recover', 'rest', 'slackoff', 'softboiled', 'synthesis', 'wish', ]; // these Pokemon always want recovery - let requiresRecovery = [ + const requiresRecovery = [ 'Clefable', 'Lickitung', 'Blissey', 'Umbreon', 'Porygon2', 'Meganium', 'Miltank', 'Lugia', 'Ho-Oh', 'Sableye', 'Cradily', 'Milotic', 'Dusclops', 'Latias', 'Deoxys-Defense', ]; // these Pokemon don't always want a STAB move - let noStab = [ + const noStab = [ 'Clefable', 'Gengar', 'Kingler', 'Blissey', 'Porygon2', 'Umbreon', 'Dragonite', 'Feraligatr', 'Noctowl', 'Azumarill', 'Misdreavus', 'Sneasel', 'Mightyena', 'Masquerain', 'Nosepass', 'Delcatty', 'Volbeat', 'Illumise', 'Castform', 'Absol', ]; // these Pokemon don't need any damaging attacks - let noAttacks = [ + const noAttacks = [ 'Smeargle', 'Shuckle', ]; - /**@type {{[k: string]: boolean}} */ - let hasMove = {}; + let hasMove: {[k: string]: boolean} = {}; let counter; do { @@ -93,7 +91,7 @@ class RandomGen3Teams extends RandomGen4Teams { // Choose next 4 moves from learnset/viable moves and add them to moves list: while (moves.length < 4 && movePool.length) { - let setMoveid = this.sampleNoReplace(movePool); + const setMoveid = this.sampleNoReplace(movePool); if (setMoveid.substr(0, 11) === 'hiddenpower') { availableHP--; if (hasMove['hiddenpower']) continue; @@ -108,8 +106,8 @@ class RandomGen3Teams extends RandomGen4Teams { // Iterate through the moves again, this time to cull them: for (const [i, setMoveid] of moves.entries()) { - let move = this.dex.getMove(setMoveid); - let moveid = move.id; + const move = this.dex.getMove(setMoveid); + const moveid = move.id; let rejected = false; let isSetup = false; @@ -144,7 +142,7 @@ class RandomGen3Teams extends RandomGen4Teams { if (hasMove['painsplit'] || hasMove['recover'] || hasMove['wish']) rejected = true; if (!hasMove['rest']) rejected = true; if (movePool.length > 1) { - let rest = movePool.indexOf('rest'); + const rest = movePool.indexOf('rest'); if (rest >= 0) this.fastPop(movePool, rest); } break; @@ -300,16 +298,15 @@ class RandomGen3Teams extends RandomGen4Teams { rejected = true; } - // @ts-ignore if (counter.setupType && !isSetup && move.category !== counter.setupType && counter[counter.setupType] < 2 && !hasMove['batonpass'] && moveid !== 'rest' && moveid !== 'sleeptalk') { // Mono-attacking with setup and RestTalk is allowed // Reject Status moves only if there is nothing else to reject - // @ts-ignore if (move.category !== 'Status' || counter[counter.setupType] + counter.Status > 3 && counter['physicalsetup'] + counter['specialsetup'] < 2) rejected = true; } // Pokemon should have moves that benefit their type - if ((hasType['Bug'] && !counter['Bug'] && movePool.includes('megahorn')) || + if ( + (hasType['Bug'] && !counter['Bug'] && movePool.includes('megahorn')) || (hasType['Fighting'] && !counter['Fighting']) || (hasType['Fire'] && !counter['Fire'] && counter.setupType !== 'Physical') || (hasType['Ground'] && !counter['Ground']) || @@ -317,10 +314,16 @@ class RandomGen3Teams extends RandomGen4Teams { (hasType['Steel'] && !counter['Steel'] && movePool.includes('meteormash')) || (hasType['Water'] && !counter['Water'] && (movePool.includes('surf') || movePool.includes('hydropump')) && counter.setupType !== 'Physical' && !hasAbility['Huge Power'] && (!hasType['Ice'] || !counter['Ice'])) || (movePool.includes('spore')) || - (movePool.includes('earthquake') && !counter['Ground'] && !counter['Fighting'] && counter.Physical > 1 && (hasType['Bug'] || hasType['Flying'] || hasType['Normal'] || hasType['Poison'] || hasType['Rock'] || hasType['Steel'])) || + (movePool.includes('earthquake') && !counter['Ground'] && !counter['Fighting'] && counter.Physical > 1 && ( + hasType['Bug'] || hasType['Flying'] || hasType['Normal'] || hasType['Poison'] || hasType['Rock'] || hasType['Steel'] + )) || (movePool.includes('rockslide') && !counter['Rock'] && counter.Physical > 1 && hasType['Ground']) || (movePool.includes('thunderbolt') && !counter['Electric'] && counter.Special > 1 && (hasType['Ice'] || hasType['Water'])) || - (requiresRecovery.includes(species.name) && !recoveryMoves.some(recoveryMove => hasMove[recoveryMove]) && recoveryMoves.some(recoveryMove => movePool.includes(recoveryMove)))) { + ( + requiresRecovery.includes(species.name) && !recoveryMoves.some(recoveryMove => hasMove[recoveryMove]) && + recoveryMoves.some(recoveryMove => movePool.includes(recoveryMove)) + ) + ) { // Reject Status or non-STAB if (!isSetup && !move.weather && !recoveryMoves.includes(moveid) && !['sleeptalk', 'substitute'].includes(moveid)) { if (move.category === 'Status' || !hasType[move.type] || (move.basePower && move.basePower < 40 && !move.multihit)) rejected = true; @@ -329,7 +332,7 @@ class RandomGen3Teams extends RandomGen4Teams { // Sleep Talk shouldn't be selected without Rest if (moveid === 'rest' && rejected) { - let sleeptalk = movePool.indexOf('sleeptalk'); + const sleeptalk = movePool.indexOf('sleeptalk'); if (sleeptalk >= 0) { if (movePool.length < 2) { rejected = false; @@ -357,10 +360,10 @@ class RandomGen3Teams extends RandomGen4Teams { } if (reqMove) { // reject a move - for (let [i, move] of moves.entries()) { + for (const [i, move] of moves.entries()) { if (move === 'weatherball' || this.dex.getMove(move).type in hasType) continue; moves[i] = reqMove; - let reqMoveIndex = movePool.indexOf(reqMove); + const reqMoveIndex = movePool.indexOf(reqMove); if (reqMoveIndex !== -1) this.fastPop(movePool, reqMoveIndex); break; } @@ -372,13 +375,13 @@ class RandomGen3Teams extends RandomGen4Teams { // A set shouldn't have zero attacking moves moves.splice(this.random(moves.length), 1); } else if (!counter.stab && !noStab.includes(species.name) && - !(hasType['Bug'] && moves.indexOf('hiddenpowerbug') > -1) && - !(hasType['Flying'] && moves.indexOf('hiddenpowerflying') > -1) && - !(hasType['Grass'] && moves.indexOf('hiddenpowergrass') > -1) && - !(hasType['Steel'] && moves.indexOf('hiddenpowersteel') > -1)) { + !(hasType['Bug'] && moves.includes('hiddenpowerbug')) && + !(hasType['Flying'] && moves.includes('hiddenpowerflying')) && + !(hasType['Grass'] && moves.includes('hiddenpowergrass')) && + !(hasType['Steel'] && moves.includes('hiddenpowersteel'))) { // In most cases, a set shouldn't have zero STABs if (counter.damagingMoves.length === 1) { - let damagingid = counter.damagingMoves[0].id; + const damagingid = counter.damagingMoves[0].id; if (movePool.length - availableHP || availableHP && (damagingid === 'hiddenpower' || !hasMove['hiddenpower'])) { if (!counter.damagingMoves[0].damage) { moves.splice(counter.damagingMoveIndex[damagingid], 1); @@ -386,8 +389,8 @@ class RandomGen3Teams extends RandomGen4Teams { } } else if (!counter.damagingMoves[0].damage && !counter.damagingMoves[1].damage) { // If you have three or more attacks, and none of them are STAB, reject one of them at random. - let rejectableMoves = []; - let baseDiff = movePool.length - availableHP; + const rejectableMoves = []; + const baseDiff = movePool.length - availableHP; for (const move of counter.damagingMoves) { if (baseDiff || availableHP && (!hasMove['hiddenpower'] || move.id === 'hiddenpower')) { rejectableMoves.push(counter.damagingMoveIndex[move.id]); @@ -430,7 +433,7 @@ class RandomGen3Teams extends RandomGen4Teams { ivs = {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31}; } - let abilities = Object.values(baseSpecies.abilities).filter(a => this.dex.getAbility(a).gen === 3); + const abilities = Object.values(baseSpecies.abilities).filter(a => this.dex.getAbility(a).gen === 3); abilities.sort((a, b) => this.dex.getAbility(b).rating - this.dex.getAbility(a).rating); let ability0 = this.dex.getAbility(abilities[0]); let ability1 = this.dex.getAbility(abilities[1]); @@ -442,7 +445,7 @@ class RandomGen3Teams extends RandomGen4Teams { } ability = ability0.name; - let rejectAbility; + let rejectAbility: boolean; do { rejectAbility = false; @@ -485,7 +488,6 @@ class RandomGen3Teams extends RandomGen4Teams { ability = ability1.name; } else { // Default to the highest rated ability if all are rejected - // @ts-ignore ability = abilities[0]; rejectAbility = false; } @@ -541,7 +543,7 @@ class RandomGen3Teams extends RandomGen4Teams { item = 'Leftovers'; } - let levelScale = { + const levelScale: {[k: string]: number} = { LC: 87, NFE: 85, NU: 83, @@ -551,13 +553,11 @@ class RandomGen3Teams extends RandomGen4Teams { OU: 75, Uber: 71, }; - let customScale = { + const customScale: {[k: string]: number} = { Ditto: 99, Unown: 99, }; - let tier = species.tier; - // @ts-ignore + const tier = species.tier; let level = levelScale[tier] || 75; - // @ts-ignore if (customScale[species.name]) level = customScale[species.name]; // Prepare optimal HP @@ -594,13 +594,13 @@ class RandomGen3Teams extends RandomGen4Teams { } randomTeam() { - let pokemon = []; + const pokemon = []; - let allowedNFE = ['Scyther', 'Vigoroth']; + const allowedNFE = ['Scyther', 'Vigoroth']; - let pokemonPool = []; - for (let id in this.dex.data.FormatsData) { - let species = this.dex.getSpecies(id); + const pokemonPool = []; + for (const id in this.dex.data.FormatsData) { + const species = this.dex.getSpecies(id); if (species.isNonstandard || !species.randomBattleMoves) continue; if (species.evos && !allowedNFE.includes(species.name)) { let invalid = false; @@ -615,19 +615,15 @@ class RandomGen3Teams extends RandomGen4Teams { pokemonPool.push(id); } - /**@type {{[k: string]: number}} */ - let typeCount = {}; - /**@type {{[k: string]: number}} */ - let typeComboCount = {}; - /**@type {{[k: string]: number}} */ - let baseFormes = {}; + const typeCount: {[k: string]: number} = {}; + const typeComboCount: {[k: string]: number} = {}; + const baseFormes: {[k: string]: number} = {}; let uberCount = 0; let nuCount = 0; - /**@type {RandomTeamsTypes.TeamDetails} */ - let teamDetails = {}; + const teamDetails: RandomTeamsTypes.TeamDetails = {}; while (pokemonPool.length && pokemon.length < 6) { - let species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); + const species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); if (!species.exists) continue; // Limit to one of each species (Species Clause) @@ -636,7 +632,7 @@ class RandomGen3Teams extends RandomGen4Teams { // Limit to one Wobbuffet per battle (not just per team) if (species.name === 'Wobbuffet' && this.hasWobbuffet) continue; - let tier = species.tier; + const tier = species.tier; switch (tier) { case 'Uber': // Ubers are limited to 2 but have a 20% chance of being added anyway. @@ -660,7 +656,7 @@ class RandomGen3Teams extends RandomGen4Teams { } if (skip) continue; - let set = this.randomSet(species, teamDetails); + const set = this.randomSet(species, teamDetails); // Limit 1 of any type combination let typeCombo = species.types.slice().sort().join(); @@ -715,4 +711,4 @@ class RandomGen3Teams extends RandomGen4Teams { } } -module.exports = RandomGen3Teams; +export default RandomGen3Teams; diff --git a/data/mods/gen3/rulesets.js b/data/mods/gen3/rulesets.ts similarity index 75% rename from data/mods/gen3/rulesets.js rename to data/mods/gen3/rulesets.ts index e5e6403969..94947d8b2a 100644 --- a/data/mods/gen3/rulesets.js +++ b/data/mods/gen3/rulesets.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedFormatsData}} */ -let BattleFormats = { +export const BattleFormats: {[k: string]: ModdedFormatsData} = { standard: { effectType: 'ValidatorRule', name: 'Standard', @@ -9,6 +6,3 @@ let BattleFormats = { ruleset: ['Obtainable', 'Sleep Clause Mod', 'Switch Priority Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Moody Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod'], }, }; - -exports.BattleFormats = BattleFormats; - diff --git a/data/mods/gen3/scripts.js b/data/mods/gen3/scripts.ts similarity index 91% rename from data/mods/gen3/scripts.js rename to data/mods/gen3/scripts.ts index fdbece1eb7..97e3e74512 100644 --- a/data/mods/gen3/scripts.js +++ b/data/mods/gen3/scripts.ts @@ -1,16 +1,13 @@ -'use strict'; - -/**@type {ModdedBattleScriptsData} */ -let BattleScripts = { +export const BattleScripts: ModdedBattleScriptsData = { inherit: 'gen4', gen: 3, init() { - for (let i in this.data.Pokedex) { + for (const i in this.data.Pokedex) { delete this.data.Pokedex[i].abilities['H']; } - let specialTypes = ['Fire', 'Water', 'Grass', 'Ice', 'Electric', 'Dark', 'Psychic', 'Dragon']; + const specialTypes = ['Fire', 'Water', 'Grass', 'Ice', 'Electric', 'Dark', 'Psychic', 'Dragon']; let newCategory = ''; - for (let i in this.data.Movedex) { + for (const i in this.data.Movedex) { if (!this.data.Movedex[i]) console.log(i); if (this.data.Movedex[i].category === 'Status') continue; newCategory = specialTypes.includes(this.data.Movedex[i].type) ? 'Special' : 'Physical'; @@ -101,8 +98,7 @@ let BattleScripts = { this.faint(pokemon, pokemon, move); } - /** @type {number | false | undefined | ''} */ - let damage = false; + let damage: number | false | undefined | '' = false; if (move.target === 'all' || move.target === 'foeSide' || move.target === 'allySide' || move.target === 'allyTeam') { damage = this.tryMoveHit(target, pokemon, move); if (damage === this.NOT_FAIL) pokemon.moveThisTurnResult = null; @@ -133,7 +129,7 @@ let BattleScripts = { target = targets[0]; let lacksTarget = !target || target.fainted; if (!lacksTarget) { - if (move.target === 'adjacentFoe' || move.target === 'adjacentAlly' || move.target === 'normal' || move.target === 'randomNormal') { + if (['adjacentFoe', 'adjacentAlly', 'normal', 'randomNormal'].includes(move.target)) { lacksTarget = !this.isAdjacent(target, pokemon); } } @@ -208,7 +204,10 @@ let BattleScripts = { move.ignoreImmunity = (move.category === 'Status'); } - if ((!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) && !target.runImmunity(move.type)) { + if ( + (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) && + !target.runImmunity(move.type) + ) { naturalImmunity = true; } else { hitResult = this.singleEvent('TryImmunity', move, {}, target, pokemon, move); @@ -217,12 +216,12 @@ let BattleScripts = { } } - let boostTable = [1, 4 / 3, 5 / 3, 2, 7 / 3, 8 / 3, 3]; + const boostTable = [1, 4 / 3, 5 / 3, 2, 7 / 3, 8 / 3, 3]; // calculate true accuracy - /**@type {number | true} */ let accuracy = move.accuracy; - let boosts, boost; + let boosts: SparseBoostsTable = {}; + let boost: number; if (accuracy !== true) { if (!move.ignoreAccuracy) { boosts = this.runEvent('ModifyBoost', pokemon, null, null, Object.assign({}, pokemon.boosts)); @@ -288,8 +287,7 @@ let BattleScripts = { } move.totalDamage = 0; - /** @type {number | undefined | false} */ - let damage = 0; + let damage: number | undefined | false = 0; pokemon.lastDamage = 0; if (move.multihit) { let hits = move.multihit; @@ -303,11 +301,10 @@ let BattleScripts = { } hits = Math.floor(hits); let nullDamage = true; - /** @type {number | undefined | false} */ - let moveDamage; + let moveDamage: number | undefined | false; // There is no need to recursively check the ´sleepUsable´ flag as Sleep Talk can only be used while asleep. - let isSleepUsable = move.sleepUsable || this.dex.getMove(move.sourceEffect).sleepUsable; - let i; + const isSleepUsable = move.sleepUsable || this.dex.getMove(move.sourceEffect).sleepUsable; + let i: number; for (i = 0; i < hits && target.hp && pokemon.hp; i++) { if (pokemon.status === 'slp' && !isSleepUsable) break; move.hit = i + 1; @@ -379,9 +376,6 @@ let BattleScripts = { }, calcRecoilDamage(damageDealt, move) { - // @ts-ignore - return this.dex.clampIntRange(Math.floor(damageDealt * move.recoil[0] / move.recoil[1]), 1); + return this.dex.clampIntRange(Math.floor(damageDealt * move.recoil![0] / move.recoil![1]), 1); }, }; - -exports.BattleScripts = BattleScripts; diff --git a/data/mods/gen3/statuses.js b/data/mods/gen3/statuses.ts similarity index 80% rename from data/mods/gen3/statuses.js rename to data/mods/gen3/statuses.ts index ad4dd25387..6dbde1866c 100644 --- a/data/mods/gen3/statuses.js +++ b/data/mods/gen3/statuses.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedPureEffectData}} */ -let BattleStatuses = { +export const BattleStatuses: {[k: string]: ModdedPureEffectData} = { slp: { name: 'slp', id: 'slp', @@ -44,15 +41,14 @@ let BattleStatuses = { frz: { inherit: true, onHit(target, source, move) { - if (move.thawsTarget || move.type === 'Fire' && move.category !== 'Status' && move.id !== 'hiddenpower' && move.id !== 'weatherball') { + // don't count Hidden Power or Weather Ball as Fire-type + if (move.thawsTarget || this.dex.getMove(move.id).type === 'Fire' && move.category !== 'Status') { target.cureStatus(); } }, }, sandstorm: { inherit: true, - onModifySpD() { }, + onModifySpD() {}, }, }; - -exports.BattleStatuses = BattleStatuses; diff --git a/data/mods/gen4/abilities.js b/data/mods/gen4/abilities.ts similarity index 95% rename from data/mods/gen4/abilities.js rename to data/mods/gen4/abilities.ts index f02da0e679..f10eb2474c 100644 --- a/data/mods/gen4/abilities.js +++ b/data/mods/gen4/abilities.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedAbilityData}} */ -let BattleAbilities = { +export const BattleAbilities: {[k: string]: ModdedAbilityData} = { angerpoint: { inherit: true, desc: "If this Pokemon, or its substitute, is struck by a critical hit, its Attack is raised by 12 stages.", @@ -35,7 +32,7 @@ let BattleAbilities = { desc: "This Pokemon's type changes to match the type of the last move that hit it, unless that type is already one of its types. This effect applies after each hit from a multi-hit move.", onDamagingHit(damage, target, source, move) { if (!target.hp) return; - let type = move.type; + const type = move.type; if (target.isActive && move.category !== 'Status' && type !== '???' && !target.hasType(type)) { if (!target.setType(type)) return false; this.add('-start', target, 'typechange', type, '[from] ability: Color Change'); @@ -47,7 +44,7 @@ let BattleAbilities = { inherit: true, onDamagingHit(damage, target, source, move) { if (move.flags['contact'] && !source.status) { - let r = this.random(100); + const r = this.random(100); if (r < 10) { source.setStatus('slp', target); } else if (r < 20) { @@ -105,13 +102,12 @@ let BattleAbilities = { forewarn: { inherit: true, onStart(pokemon) { - /**@type {Move[]} */ - let warnMoves = []; + let warnMoves: Move[] = []; let warnBp = 1; for (const target of pokemon.side.foe.active) { if (target.fainted) continue; for (const moveSlot of target.moveSlots) { - let move = this.dex.getMove(moveSlot.move); + const move = this.dex.getMove(moveSlot.move); let bp = move.basePower; if (move.ohko) bp = 160; if (move.id === 'counter' || move.id === 'metalburst' || move.id === 'mirrorcoat') bp = 120; @@ -125,7 +121,7 @@ let BattleAbilities = { } } if (!warnMoves.length) return; - let warnMove = this.sample(warnMoves); + const warnMove = this.sample(warnMoves); this.add('-activate', pokemon, 'ability: Forewarn', warnMove); }, }, @@ -206,7 +202,7 @@ let BattleAbilities = { desc: "If an active ally has the Plus Ability, this Pokemon's Special Attack is multiplied by 1.5.", shortDesc: "If an active ally has the Plus Ability, this Pokemon's Sp. Atk is 1.5x.", onModifySpA(spa, pokemon) { - let allyActive = pokemon.side.active; + const allyActive = pokemon.side.active; if (allyActive.length === 1) { return; } @@ -269,7 +265,7 @@ let BattleAbilities = { desc: "If an active ally has the Minus Ability, this Pokemon's Special Attack is multiplied by 1.5.", shortDesc: "If an active ally has the Minus Ability, this Pokemon's Sp. Atk is 1.5x.", onModifySpA(spa, pokemon) { - let allyActive = pokemon.side.active; + const allyActive = pokemon.side.active; if (allyActive.length === 1) { return; } @@ -313,9 +309,9 @@ let BattleAbilities = { simple: { shortDesc: "This Pokemon's stat stages are considered doubled during stat calculations.", onModifyBoost(boosts) { - for (let key in boosts) { - // @ts-ignore - boosts[key] *= 2; + let key: BoostName; + for (key in boosts) { + boosts[key]! *= 2; } }, id: "simple", @@ -380,9 +376,9 @@ let BattleAbilities = { onAfterSetStatus(status, target, source, effect) { if (!source || source === target) return; if (effect && effect.id === 'toxicspikes') return; - let id = status.id; + let id: string = status.id; if (id === 'slp' || id === 'frz') return; - if (id === 'tox') id = /** @type {ID} */('psn'); + if (id === 'tox') id = 'psn'; source.trySetStatus(id, target); }, }, @@ -418,10 +414,10 @@ let BattleAbilities = { inherit: true, onUpdate(pokemon) { if (!pokemon.isStarted) return; - let target = pokemon.side.foe.randomActive(); + const target = pokemon.side.foe.randomActive(); if (!target || target.fainted) return; - let ability = target.getAbility(); - let bannedAbilities = ['forecast', 'multitype', 'trace']; + const ability = target.getAbility(); + const bannedAbilities = ['forecast', 'multitype', 'trace']; if (bannedAbilities.includes(target.ability)) { return; } @@ -451,5 +447,3 @@ let BattleAbilities = { }, }, }; - -exports.BattleAbilities = BattleAbilities; diff --git a/data/mods/gen4/formats-data.js b/data/mods/gen4/formats-data.ts similarity index 99% rename from data/mods/gen4/formats-data.js rename to data/mods/gen4/formats-data.ts index 4f83d25ebc..5187dc0c14 100644 --- a/data/mods/gen4/formats-data.js +++ b/data/mods/gen4/formats-data.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesFormatsData}} */ -let BattleFormatsData = { +export const BattleFormatsData: {[k: string]: ModdedSpeciesFormatsData} = { bulbasaur: { tier: "LC", }, @@ -1854,5 +1851,3 @@ let BattleFormatsData = { randomBattleMoves: ["recover", "calmmind", "judgment", "icebeam", "thunderbolt", "willowisp", "refresh"], }, }; - -exports.BattleFormatsData = BattleFormatsData; diff --git a/data/mods/gen4/items.js b/data/mods/gen4/items.ts similarity index 53% rename from data/mods/gen4/items.js rename to data/mods/gen4/items.ts index d8f0ef8a1a..73d299ef6c 100644 --- a/data/mods/gen4/items.js +++ b/data/mods/gen4/items.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedItemData}} */ -let BattleItems = { +export const BattleItems: {[k: string]: ModdedItemData} = { adamantorb: { inherit: true, onBasePower(basePower, user, target, move) { @@ -13,9 +10,8 @@ let BattleItems = { bigroot: { inherit: true, onTryHeal(damage, target, source, effect) { - /**@type {{[k: string]: number}} */ - let heals = {drain: 1, leechseed: 1, ingrain: 1, aquaring: 1}; - if (heals[effect.id]) { + const heals = ['drain', 'leechseed', 'ingrain', 'aquaring']; + if (heals.includes(effect.id)) { return Math.floor(damage * 1.3); } }, @@ -36,7 +32,10 @@ let BattleItems = { inherit: true, onSourceModifyDamage(damage, source, target, move) { if (move.causedCrashDamage) return damage; - if (move.type === 'Fighting' && target.getMoveHitData(move).typeMod > 0 && (!target.volatiles['substitute'] || move.flags['authentic'])) { + if (move.type === 'Fighting' && target.getMoveHitData(move).typeMod > 0) { + const hitSub = target.volatiles['substitute'] && !move.flags['authentic']; + if (hitSub) return; + if (target.eatItem()) { this.debug('-50% reduction'); this.add('-enditem', target, this.effect, '[weaken]'); @@ -50,7 +49,7 @@ let BattleItems = { onFractionalPriority() {}, onBeforeTurn(pokemon) { if (pokemon.hp <= pokemon.maxhp / 4 || (pokemon.hp <= pokemon.maxhp / 2 && pokemon.ability === 'gluttony')) { - let action = this.queue.willMove(pokemon); + const action = this.queue.willMove(pokemon); if (!action) return; this.queue.insertChoice({ choice: 'event', @@ -63,7 +62,7 @@ let BattleItems = { } }, onCustap(pokemon) { - let action = this.queue.willMove(pokemon); + const action = this.queue.willMove(pokemon); this.debug('custap action: ' + action); if (action && pokemon.eatItem()) { this.queue.cancelAction(pokemon); @@ -128,7 +127,9 @@ let BattleItems = { kingsrock: { inherit: true, onModifyMove(move) { - let affectedByKingsRock = ['aerialace', 'aeroblast', 'aircutter', 'airslash', 'aquajet', 'aquatail', 'armthrust', 'assurance', 'attackorder', 'aurasphere', 'avalanche', 'barrage', 'beatup', 'bide', 'bind', 'blastburn', 'bonerush', 'bonemerang', 'bounce', 'bravebird', 'brickbreak', 'brine', 'bugbite', 'bulletpunch', 'bulletseed', 'chargebeam', 'clamp', 'closecombat', 'cometpunch', 'crabhammer', 'crosschop', 'crosspoison', 'crushgrip', 'cut', 'darkpulse', 'dig', 'discharge', 'dive', 'doublehit', 'doublekick', 'doubleslap', 'doubleedge', 'dracometeor', 'dragonbreath', 'dragonclaw', 'dragonpulse', 'dragonrage', 'dragonrush', 'drainpunch', 'drillpeck', 'earthpower', 'earthquake', 'eggbomb', 'endeavor', 'eruption', 'explosion', 'extremespeed', 'falseswipe', 'feintattack', 'firefang', 'firespin', 'flail', 'flashcannon', 'fly', 'forcepalm', 'frenzyplant', 'frustration', 'furyattack', 'furycutter', 'furyswipes', 'gigaimpact', 'grassknot', 'gunkshot', 'gust', 'gyroball', 'hammerarm', 'headsmash', 'hiddenpower', 'highjumpkick', 'hornattack', 'hydrocannon', 'hydropump', 'hyperbeam', 'iceball', 'icefang', 'iceshard', 'iciclespear', 'ironhead', 'judgment', 'jumpkick', 'karatechop', 'lastresort', 'lavaplume', 'leafblade', 'leafstorm', 'lowkick', 'machpunch', 'magicalleaf', 'magmastorm', 'magnetbomb', 'magnitude', 'megakick', 'megapunch', 'megahorn', 'meteormash', 'mirrorshot', 'mudbomb', 'mudshot', 'muddywater', 'nightshade', 'nightslash', 'ominouswind', 'outrage', 'overheat', 'payday', 'payback', 'peck', 'petaldance', 'pinmissile', 'pluck', 'poisonjab', 'poisontail', 'pound', 'powergem', 'powerwhip', 'psychoboost', 'psychocut', 'psywave', 'punishment', 'quickattack', 'rage', 'rapidspin', 'razorleaf', 'razorwind', 'return', 'revenge', 'reversal', 'roaroftime', 'rockblast', 'rockclimb', 'rockthrow', 'rockwrecker', 'rollingkick', 'rollout', 'sandtomb', 'scratch', 'seedbomb', 'seedflare', 'seismictoss', 'selfdestruct', 'shadowclaw', 'shadowforce', 'shadowpunch', 'shadowsneak', 'shockwave', 'signalbeam', 'silverwind', 'skullbash', 'skyattack', 'skyuppercut', 'slam', 'slash', 'snore', 'solarbeam', 'sonicboom', 'spacialrend', 'spikecannon', 'spitup', 'steelwing', 'stoneedge', 'strength', 'struggle', 'submission', 'suckerpunch', 'surf', 'swift', 'tackle', 'takedown', 'thrash', 'thunderfang', 'triplekick', 'trumpcard', 'twister', 'uturn', 'uproar', 'vacuumwave', 'visegrip', 'vinewhip', 'vitalthrow', 'volttackle', 'wakeupslap', 'watergun', 'waterpulse', 'waterfall', 'weatherball', 'whirlpool', 'wingattack', 'woodhammer', 'wrap', 'wringout', 'xscissor', 'zenheadbutt']; + const affectedByKingsRock = [ + 'aerialace', 'aeroblast', 'aircutter', 'airslash', 'aquajet', 'aquatail', 'armthrust', 'assurance', 'attackorder', 'aurasphere', 'avalanche', 'barrage', 'beatup', 'bide', 'bind', 'blastburn', 'bonerush', 'bonemerang', 'bounce', 'bravebird', 'brickbreak', 'brine', 'bugbite', 'bulletpunch', 'bulletseed', 'chargebeam', 'clamp', 'closecombat', 'cometpunch', 'crabhammer', 'crosschop', 'crosspoison', 'crushgrip', 'cut', 'darkpulse', 'dig', 'discharge', 'dive', 'doublehit', 'doublekick', 'doubleslap', 'doubleedge', 'dracometeor', 'dragonbreath', 'dragonclaw', 'dragonpulse', 'dragonrage', 'dragonrush', 'drainpunch', 'drillpeck', 'earthpower', 'earthquake', 'eggbomb', 'endeavor', 'eruption', 'explosion', 'extremespeed', 'falseswipe', 'feintattack', 'firefang', 'firespin', 'flail', 'flashcannon', 'fly', 'forcepalm', 'frenzyplant', 'frustration', 'furyattack', 'furycutter', 'furyswipes', 'gigaimpact', 'grassknot', 'gunkshot', 'gust', 'gyroball', 'hammerarm', 'headsmash', 'hiddenpower', 'highjumpkick', 'hornattack', 'hydrocannon', 'hydropump', 'hyperbeam', 'iceball', 'icefang', 'iceshard', 'iciclespear', 'ironhead', 'judgment', 'jumpkick', 'karatechop', 'lastresort', 'lavaplume', 'leafblade', 'leafstorm', 'lowkick', 'machpunch', 'magicalleaf', 'magmastorm', 'magnetbomb', 'magnitude', 'megakick', 'megapunch', 'megahorn', 'meteormash', 'mirrorshot', 'mudbomb', 'mudshot', 'muddywater', 'nightshade', 'nightslash', 'ominouswind', 'outrage', 'overheat', 'payday', 'payback', 'peck', 'petaldance', 'pinmissile', 'pluck', 'poisonjab', 'poisontail', 'pound', 'powergem', 'powerwhip', 'psychoboost', 'psychocut', 'psywave', 'punishment', 'quickattack', 'rage', 'rapidspin', 'razorleaf', 'razorwind', 'return', 'revenge', 'reversal', 'roaroftime', 'rockblast', 'rockclimb', 'rockthrow', 'rockwrecker', 'rollingkick', 'rollout', 'sandtomb', 'scratch', 'seedbomb', 'seedflare', 'seismictoss', 'selfdestruct', 'shadowclaw', 'shadowforce', 'shadowpunch', 'shadowsneak', 'shockwave', 'signalbeam', 'silverwind', 'skullbash', 'skyattack', 'skyuppercut', 'slam', 'slash', 'snore', 'solarbeam', 'sonicboom', 'spacialrend', 'spikecannon', 'spitup', 'steelwing', 'stoneedge', 'strength', 'struggle', 'submission', 'suckerpunch', 'surf', 'swift', 'tackle', 'takedown', 'thrash', 'thunderfang', 'triplekick', 'trumpcard', 'twister', 'uturn', 'uproar', 'vacuumwave', 'visegrip', 'vinewhip', 'vitalthrow', 'volttackle', 'wakeupslap', 'watergun', 'waterpulse', 'waterfall', 'weatherball', 'whirlpool', 'wingattack', 'woodhammer', 'wrap', 'wringout', 'xscissor', 'zenheadbutt', + ]; if (affectedByKingsRock.includes(move.id)) { if (!move.secondaries) move.secondaries = []; move.secondaries.push({ @@ -237,7 +238,9 @@ let BattleItems = { razorfang: { inherit: true, onModifyMove(move) { - let affectedByRazorFang = ['aerialace', 'aeroblast', 'aircutter', 'airslash', 'aquajet', 'aquatail', 'armthrust', 'assurance', 'attackorder', 'aurasphere', 'avalanche', 'barrage', 'beatup', 'bide', 'bind', 'blastburn', 'bonerush', 'bonemerang', 'bounce', 'bravebird', 'brickbreak', 'brine', 'bugbite', 'bulletpunch', 'bulletseed', 'chargebeam', 'clamp', 'closecombat', 'cometpunch', 'crabhammer', 'crosschop', 'crosspoison', 'crushgrip', 'cut', 'darkpulse', 'dig', 'discharge', 'dive', 'doublehit', 'doublekick', 'doubleslap', 'doubleedge', 'dracometeor', 'dragonbreath', 'dragonclaw', 'dragonpulse', 'dragonrage', 'dragonrush', 'drainpunch', 'drillpeck', 'earthpower', 'earthquake', 'eggbomb', 'endeavor', 'eruption', 'explosion', 'extremespeed', 'falseswipe', 'feintattack', 'firefang', 'firespin', 'flail', 'flashcannon', 'fly', 'forcepalm', 'frenzyplant', 'frustration', 'furyattack', 'furycutter', 'furyswipes', 'gigaimpact', 'grassknot', 'gunkshot', 'gust', 'gyroball', 'hammerarm', 'headsmash', 'hiddenpower', 'highjumpkick', 'hornattack', 'hydrocannon', 'hydropump', 'hyperbeam', 'iceball', 'icefang', 'iceshard', 'iciclespear', 'ironhead', 'judgment', 'jumpkick', 'karatechop', 'lastresort', 'lavaplume', 'leafblade', 'leafstorm', 'lowkick', 'machpunch', 'magicalleaf', 'magmastorm', 'magnetbomb', 'magnitude', 'megakick', 'megapunch', 'megahorn', 'meteormash', 'mirrorshot', 'mudbomb', 'mudshot', 'muddywater', 'nightshade', 'nightslash', 'ominouswind', 'outrage', 'overheat', 'payday', 'payback', 'peck', 'petaldance', 'pinmissile', 'pluck', 'poisonjab', 'poisontail', 'pound', 'powergem', 'powerwhip', 'psychoboost', 'psychocut', 'psywave', 'punishment', 'quickattack', 'rage', 'rapidspin', 'razorleaf', 'razorwind', 'return', 'revenge', 'reversal', 'roaroftime', 'rockblast', 'rockclimb', 'rockthrow', 'rockwrecker', 'rollingkick', 'rollout', 'sandtomb', 'scratch', 'seedbomb', 'seedflare', 'seismictoss', 'selfdestruct', 'shadowclaw', 'shadowforce', 'shadowpunch', 'shadowsneak', 'shockwave', 'signalbeam', 'silverwind', 'skullbash', 'skyattack', 'skyuppercut', 'slam', 'slash', 'snore', 'solarbeam', 'sonicboom', 'spacialrend', 'spikecannon', 'spitup', 'steelwing', 'stoneedge', 'strength', 'struggle', 'submission', 'suckerpunch', 'surf', 'swift', 'tackle', 'takedown', 'thrash', 'thunderfang', 'triplekick', 'trumpcard', 'twister', 'uturn', 'uproar', 'vacuumwave', 'visegrip', 'vinewhip', 'vitalthrow', 'volttackle', 'wakeupslap', 'watergun', 'waterpulse', 'waterfall', 'weatherball', 'whirlpool', 'wingattack', 'woodhammer', 'wrap', 'wringout', 'xscissor', 'zenheadbutt']; + const affectedByRazorFang = [ + 'aerialace', 'aeroblast', 'aircutter', 'airslash', 'aquajet', 'aquatail', 'armthrust', 'assurance', 'attackorder', 'aurasphere', 'avalanche', 'barrage', 'beatup', 'bide', 'bind', 'blastburn', 'bonerush', 'bonemerang', 'bounce', 'bravebird', 'brickbreak', 'brine', 'bugbite', 'bulletpunch', 'bulletseed', 'chargebeam', 'clamp', 'closecombat', 'cometpunch', 'crabhammer', 'crosschop', 'crosspoison', 'crushgrip', 'cut', 'darkpulse', 'dig', 'discharge', 'dive', 'doublehit', 'doublekick', 'doubleslap', 'doubleedge', 'dracometeor', 'dragonbreath', 'dragonclaw', 'dragonpulse', 'dragonrage', 'dragonrush', 'drainpunch', 'drillpeck', 'earthpower', 'earthquake', 'eggbomb', 'endeavor', 'eruption', 'explosion', 'extremespeed', 'falseswipe', 'feintattack', 'firefang', 'firespin', 'flail', 'flashcannon', 'fly', 'forcepalm', 'frenzyplant', 'frustration', 'furyattack', 'furycutter', 'furyswipes', 'gigaimpact', 'grassknot', 'gunkshot', 'gust', 'gyroball', 'hammerarm', 'headsmash', 'hiddenpower', 'highjumpkick', 'hornattack', 'hydrocannon', 'hydropump', 'hyperbeam', 'iceball', 'icefang', 'iceshard', 'iciclespear', 'ironhead', 'judgment', 'jumpkick', 'karatechop', 'lastresort', 'lavaplume', 'leafblade', 'leafstorm', 'lowkick', 'machpunch', 'magicalleaf', 'magmastorm', 'magnetbomb', 'magnitude', 'megakick', 'megapunch', 'megahorn', 'meteormash', 'mirrorshot', 'mudbomb', 'mudshot', 'muddywater', 'nightshade', 'nightslash', 'ominouswind', 'outrage', 'overheat', 'payday', 'payback', 'peck', 'petaldance', 'pinmissile', 'pluck', 'poisonjab', 'poisontail', 'pound', 'powergem', 'powerwhip', 'psychoboost', 'psychocut', 'psywave', 'punishment', 'quickattack', 'rage', 'rapidspin', 'razorleaf', 'razorwind', 'return', 'revenge', 'reversal', 'roaroftime', 'rockblast', 'rockclimb', 'rockthrow', 'rockwrecker', 'rollingkick', 'rollout', 'sandtomb', 'scratch', 'seedbomb', 'seedflare', 'seismictoss', 'selfdestruct', 'shadowclaw', 'shadowforce', 'shadowpunch', 'shadowsneak', 'shockwave', 'signalbeam', 'silverwind', 'skullbash', 'skyattack', 'skyuppercut', 'slam', 'slash', 'snore', 'solarbeam', 'sonicboom', 'spacialrend', 'spikecannon', 'spitup', 'steelwing', 'stoneedge', 'strength', 'struggle', 'submission', 'suckerpunch', 'surf', 'swift', 'tackle', 'takedown', 'thrash', 'thunderfang', 'triplekick', 'trumpcard', 'twister', 'uturn', 'uproar', 'vacuumwave', 'visegrip', 'vinewhip', 'vitalthrow', 'volttackle', 'wakeupslap', 'watergun', 'waterpulse', 'waterfall', 'weatherball', 'whirlpool', 'wingattack', 'woodhammer', 'wrap', 'wringout', 'xscissor', 'zenheadbutt', + ]; if (affectedByRazorFang.includes(move.id)) { if (!move.secondaries) move.secondaries = []; move.secondaries.push({ @@ -264,5 +267,3 @@ let BattleItems = { }, }, }; - -exports.BattleItems = BattleItems; diff --git a/data/mods/gen4/moves.js b/data/mods/gen4/moves.ts similarity index 95% rename from data/mods/gen4/moves.js rename to data/mods/gen4/moves.ts index 0f7ff32252..00206b252a 100644 --- a/data/mods/gen4/moves.js +++ b/data/mods/gen4/moves.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedMoveData}} */ -let BattleMovedex = { +export const BattleMovedex: {[k: string]: ModdedMoveData} = { absorb: { inherit: true, desc: "The user recovers 1/2 the HP lost by the target, rounded down. If Big Root is held by the user, the HP recovered is 1.3x normal, rounded down.", @@ -14,17 +11,16 @@ let BattleMovedex = { if (target.volatiles['substitute']) { return false; } - let stats = []; - for (let stat in target.boosts) { - // @ts-ignore + const stats: BoostName[] = []; + let stat: BoostName; + for (stat in target.boosts) { if (target.boosts[stat] < 6) { stats.push(stat); } } if (stats.length) { - let randomStat = this.sample(stats); - /**@type {{[k: string]: number}} */ - let boost = {}; + const randomStat = this.sample(stats); + const boost: SparseBoostsTable = {}; boost[randomStat] = 2; this.boost(boost); } else { @@ -53,11 +49,11 @@ let BattleMovedex = { inherit: true, desc: "A random move among those known by the user's party members is selected for use. Does not select Assist, Chatter, Copycat, Counter, Covet, Destiny Bond, Detect, Endure, Feint, Focus Punch, Follow Me, Helping Hand, Me First, Metronome, Mimic, Mirror Coat, Mirror Move, Protect, Sketch, Sleep Talk, Snatch, Struggle, Switcheroo, Thief, or Trick.", onHit(target) { - let moves = []; + const moves = []; for (const pokemon of target.side.pokemon) { if (pokemon === target) continue; for (const move of pokemon.moves) { - let noAssist = [ + const noAssist = [ 'assist', 'chatter', 'copycat', 'counter', 'covet', 'destinybond', 'detect', 'endure', 'feint', 'focuspunch', 'followme', 'helpinghand', 'mefirst', 'metronome', 'mimic', 'mirrorcoat', 'mirrormove', 'protect', 'sketch', 'sleeptalk', 'snatch', 'struggle', 'switcheroo', 'thief', 'trick', ]; if (move && !noAssist.includes(move)) { @@ -89,8 +85,7 @@ let BattleMovedex = { inherit: true, basePower: 10, basePowerCallback(pokemon, target, move) { - // @ts-ignore - if (!move.allies.length) return null; + if (!move.allies?.length) return null; return 10; }, desc: "Deals typeless damage. Hits one time for the user and one time for each unfainted Pokemon without a major status condition in the user's party. For each hit, the damage formula uses the participating Pokemon's base Attack as the Attack stat, the target's base Defense as the Defense stat, and ignores stat stages and other effects that modify Attack or Defense; each hit is considered to come from the user.", @@ -105,11 +100,9 @@ let BattleMovedex = { duration: 1, onModifyAtkPriority: -101, onModifyAtk(atk, pokemon, defender, move) { - // @ts-ignore - this.add('-activate', pokemon, 'move: Beat Up', '[of] ' + move.allies[0].name); + this.add('-activate', pokemon, 'move: Beat Up', '[of] ' + move.allies![0].name); this.event.modifier = 1; - // @ts-ignore - return move.allies.shift().species.baseStats.atk; + return move.allies!.shift()!.species.baseStats.atk; }, onFoeModifyDefPriority: -101, onFoeModifyDef(def, pokemon) { @@ -159,9 +152,7 @@ let BattleMovedex = { } target = possibleTarget; } - /**@type {Move} */ - // @ts-ignore - let moveData = /** @type {ActiveMove} */ ({ + const moveData = { id: 'bide', name: "Bide", accuracy: true, @@ -172,7 +163,7 @@ let BattleMovedex = { ignoreImmunity: true, effectType: 'Move', type: 'Normal', - }); + } as unknown as ActiveMove; this.tryMoveHit(target, pokemon, moveData); return false; } @@ -263,8 +254,8 @@ let BattleMovedex = { desc: "The user's type changes to match the original type of one of its known moves besides this move and Curse, at random, but not either of its current types. Fails if the user cannot change its type, or if this move would only be able to select one of the user's current types.", flags: {}, onHit(target) { - let possibleTypes = target.moveSlots.map(moveSlot => { - let move = this.dex.getMove(moveSlot.id); + const possibleTypes = target.moveSlots.map(moveSlot => { + const move = this.dex.getMove(moveSlot.id); if (move.id !== 'conversion' && move.id !== 'curse' && !target.hasType(move.type)) { return move.type; } @@ -273,7 +264,7 @@ let BattleMovedex = { if (!possibleTypes.length) { return false; } - let type = this.sample(possibleTypes); + const type = this.sample(possibleTypes); if (!target.setType(type)) return false; this.add('-start', target, 'typechange', type); @@ -288,7 +279,9 @@ let BattleMovedex = { inherit: true, desc: "The user uses the last move used by any Pokemon, including itself. Fails if no move has been used, or if the last move used was Assist, Chatter, Copycat, Counter, Covet, Destiny Bond, Detect, Endure, Feint, Focus Punch, Follow Me, Helping Hand, Me First, Metronome, Mimic, Mirror Coat, Mirror Move, Protect, Sketch, Sleep Talk, Snatch, Struggle, Switcheroo, Thief, or Trick.", onHit(pokemon) { - let noCopycat = ['assist', 'chatter', 'copycat', 'counter', 'covet', 'destinybond', 'detect', 'endure', 'feint', 'focuspunch', 'followme', 'helpinghand', 'mefirst', 'metronome', 'mimic', 'mirrorcoat', 'mirrormove', 'protect', 'sketch', 'sleeptalk', 'snatch', 'struggle', 'switcheroo', 'thief', 'trick']; + const noCopycat = [ + 'assist', 'chatter', 'copycat', 'counter', 'covet', 'destinybond', 'detect', 'endure', 'feint', 'focuspunch', 'followme', 'helpinghand', 'mefirst', 'metronome', 'mimic', 'mirrorcoat', 'mirrormove', 'protect', 'sketch', 'sleeptalk', 'snatch', 'struggle', 'switcheroo', 'thief', 'trick', + ]; if (!this.lastMove || noCopycat.includes(this.lastMove.id)) { return false; } @@ -328,8 +321,7 @@ let BattleMovedex = { delete move.volatileStatus; delete move.onHit; move.self = {boosts: {atk: 1, def: 1, spe: -1}}; - // @ts-ignore - move.target = move.nonGhostTarget; + move.target = move.nonGhostTarget as MoveTarget; } else if (target.volatiles['substitute']) { delete move.volatileStatus; delete move.onHit; @@ -354,7 +346,7 @@ let BattleMovedex = { onTryHit(target, source, move) { if (!move.flags['protect']) return; this.add('-activate', target, 'Protect'); - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is NOT reset if (source.volatiles['lockedmove'].trueDuration >= 2) { @@ -431,15 +423,15 @@ let BattleMovedex = { desc: "Deals typeless damage that cannot be a critical hit two turns after this move is used. Damage is calculated against the target on use, and at the end of the final turn that damage is dealt to the Pokemon at the position the original target had at the time. Fails if this move or Future Sight is already in effect for the target's position.", onTry(source, target) { if (!target.side.addSlotCondition(target, 'futuremove')) return false; - let moveData = /** @type {ActiveMove} */ ({ + const moveData = { name: "Doom Desire", basePower: 120, category: "Special", flags: {}, willCrit: false, type: '???', - }); - let damage = this.getDamage(source, target, moveData, true); + } as unknown as ActiveMove; + const damage = this.getDamage(source, target, moveData, true); Object.assign(target.side.slotConditions[target.position]['futuremove'], { duration: 3, move: 'doomdesire', @@ -517,9 +509,12 @@ let BattleMovedex = { return this.random(4, 9); }, onStart(target, source) { - let noEncore = ['encore', 'mimic', 'mirrormove', 'sketch', 'struggle', 'transform']; - let moveIndex = target.lastMove ? target.moves.indexOf(target.lastMove.id) : -1; - if (!target.lastMove || noEncore.includes(target.lastMove.id) || !target.moveSlots[moveIndex] || target.moveSlots[moveIndex].pp <= 0) { + const noEncore = ['encore', 'mimic', 'mirrormove', 'sketch', 'struggle', 'transform']; + const moveIndex = target.lastMove ? target.moves.indexOf(target.lastMove.id) : -1; + if ( + !target.lastMove || noEncore.includes(target.lastMove.id) || + !target.moveSlots[moveIndex] || target.moveSlots[moveIndex].pp <= 0 + ) { // it failed this.add('-fail', source); this.attrLastMove('[still]'); @@ -536,7 +531,10 @@ let BattleMovedex = { }, onResidualOrder: 13, onResidual(target) { - if (target.moves.includes(this.effectData.move) && target.moveSlots[target.moves.indexOf(this.effectData.move)].pp <= 0) { + if ( + target.moves.includes(this.effectData.move) && + target.moveSlots[target.moves.indexOf(this.effectData.move)].pp <= 0 + ) { // early termination if you run out of PP target.removeVolatile('encore'); } @@ -610,7 +608,7 @@ let BattleMovedex = { inherit: true, desc: "The power of this move is 20 if X is 43 to 48, 40 if X is 22 to 42, 80 if X is 13 to 21, 100 if X is 6 to 12, 150 if X is 2 to 5, and 200 if X is 0 or 1, where X is equal to (user's current HP * 64 / user's maximum HP), rounded down.", basePowerCallback(pokemon, target) { - let ratio = pokemon.hp * 64 / pokemon.maxhp; + const ratio = pokemon.hp * 64 / pokemon.maxhp; if (ratio < 2) { return 200; } @@ -684,15 +682,15 @@ let BattleMovedex = { pp: 15, onTry(source, target) { if (!target.side.addSlotCondition(target, 'futuremove')) return false; - let moveData = /** @type {ActiveMove} */ ({ + const moveData = { name: "Future Sight", basePower: 80, category: "Special", flags: {}, willCrit: false, type: '???', - }); - let damage = this.getDamage(source, target, moveData, true); + } as unknown as ActiveMove; + const damage = this.getDamage(source, target, moveData, true); Object.assign(target.side.slotConditions[target.position]['futuremove'], { duration: 3, move: 'futuresight', @@ -766,7 +764,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(target, source, effect) { - if (source && source.hasAbility('persistent')) { + if (source?.hasAbility('persistent')) { this.add('-activate', source, 'ability: Persistent', effect); return 7; } @@ -881,7 +879,7 @@ let BattleMovedex = { desc: "The target's held item is lost for the rest of the battle, unless the item is a Griseous Orb or the target has the Multitype or Sticky Hold Abilities. During the effect, the target cannot obtain a new item by any means.", shortDesc: "Target's item is lost and it cannot obtain another.", onAfterHit(target, source) { - let item = target.takeItem(); + const item = target.takeItem(); if (item) { this.add('-enditem', target, item.name, '[from] move: Knock Off', '[of] ' + source); } @@ -901,7 +899,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(target, source, effect) { - if (source && source.hasItem('lightclay')) { + if (source?.hasItem('lightclay')) { return 8; } return 5; @@ -985,7 +983,7 @@ let BattleMovedex = { return; } target.removeVolatile('magiccoat'); - let newMove = this.dex.getActiveMove(move.id); + const newMove = this.dex.getActiveMove(move.id); newMove.hasBounced = true; this.useMove(newMove, target, source); return null; @@ -1062,11 +1060,16 @@ let BattleMovedex = { inherit: true, desc: "While the user remains active, this move is replaced by the last move used by the target. The copied move has 5 PP. Fails if the target has not made a move, if the user has Transformed, if the user already knows the move, or if the move is Chatter, Metronome, Mimic, Sketch, or Struggle.", onHit(target, source) { - let disallowedMoves = ['chatter', 'metronome', 'mimic', 'sketch', 'struggle', 'transform']; - if (source.transformed || !target.lastMove || disallowedMoves.includes(target.lastMove.id) || source.moves.indexOf(target.lastMove.id) !== -1 || target.volatiles['substitute']) return false; - let mimicIndex = source.moves.indexOf('mimic'); + const disallowedMoves = ['chatter', 'metronome', 'mimic', 'sketch', 'struggle', 'transform']; + if (source.transformed || !target.lastMove || target.volatiles['substitute']) { + return false; + } + if (disallowedMoves.includes(target.lastMove.id) || source.moves.includes(target.lastMove.id)) { + return false; + } + const mimicIndex = source.moves.indexOf('mimic'); if (mimicIndex < 0) return false; - let move = this.dex.getMove(target.lastMove.id); + const move = this.dex.getMove(target.lastMove.id); source.moveSlots[mimicIndex] = { move: move.name, id: move.id, @@ -1103,13 +1106,18 @@ let BattleMovedex = { mirrormove: { inherit: true, desc: "The user uses the last move that successfully targeted the user. The copied move is used with no specific target. Fails if no move has targeted the user, if the move was called by another move, if the move is Encore, or if the move cannot be copied by this move.", - onTryHit() { }, + onTryHit() {}, onHit(pokemon) { - let noMirror = ['acupressure', 'aromatherapy', 'assist', 'chatter', 'copycat', 'counter', 'curse', 'doomdesire', 'feint', 'focuspunch', 'futuresight', 'gravity', 'hail', 'haze', 'healbell', 'helpinghand', 'lightscreen', 'luckychant', 'magiccoat', 'mefirst', 'metronome', 'mimic', 'mirrorcoat', 'mirrormove', 'mist', 'mudsport', 'naturepower', 'perishsong', 'psychup', 'raindance', 'reflect', 'roleplay', 'safeguard', 'sandstorm', 'sketch', 'sleeptalk', 'snatch', 'spikes', 'spitup', 'stealthrock', 'struggle', 'sunnyday', 'tailwind', 'toxicspikes', 'transform', 'watersport']; - let lastAttackedBy = pokemon.getLastAttackedBy(); - if (!lastAttackedBy || !lastAttackedBy.source.lastMove || !lastAttackedBy.move || noMirror.includes(lastAttackedBy.move) || !lastAttackedBy.source.hasMove(lastAttackedBy.move)) { + const lastAttackedBy = pokemon.getLastAttackedBy(); + if (!lastAttackedBy || !lastAttackedBy.source.lastMove || !lastAttackedBy.move) { return false; } + const noMirror = [ + 'acupressure', 'aromatherapy', 'assist', 'chatter', 'copycat', 'counter', 'curse', 'doomdesire', 'feint', 'focuspunch', 'futuresight', 'gravity', 'hail', 'haze', 'healbell', 'helpinghand', 'lightscreen', 'luckychant', 'magiccoat', 'mefirst', 'metronome', 'mimic', 'mirrorcoat', 'mirrormove', 'mist', 'mudsport', 'naturepower', 'perishsong', 'psychup', 'raindance', 'reflect', 'roleplay', 'safeguard', 'sandstorm', 'sketch', 'sleeptalk', 'snatch', 'spikes', 'spitup', 'stealthrock', 'struggle', 'sunnyday', 'tailwind', 'toxicspikes', 'transform', 'watersport', + ]; + if (noMirror.includes(lastAttackedBy.move) || !lastAttackedBy.source.hasMove(lastAttackedBy.move)) { + return false; + } this.useMove(lastAttackedBy.move, pokemon); }, target: "self", @@ -1225,7 +1233,7 @@ let BattleMovedex = { onTryHit(target, source, move) { if (!move.flags['protect']) return; this.add('-activate', target, 'Protect'); - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is NOT reset if (source.volatiles['lockedmove'].trueDuration >= 2) { @@ -1256,7 +1264,7 @@ let BattleMovedex = { if (pokemon.removeVolatile('leechseed')) { this.add('-end', pokemon, 'Leech Seed', '[from] move: Rapid Spin', '[of] ' + pokemon); } - let sideConditions = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; + const sideConditions = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; for (const condition of sideConditions) { if (pokemon.side.removeSideCondition(condition)) { this.add('-sideend', pokemon.side, this.dex.getEffect(condition).name, '[from] move: Rapid Spin', '[of] ' + pokemon); @@ -1287,7 +1295,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(target, source, effect) { - if (source && source.hasItem('lightclay')) { + if (source?.hasItem('lightclay')) { return 8; } return 5; @@ -1318,7 +1326,7 @@ let BattleMovedex = { inherit: true, desc: "The power of this move is 20 if X is 43 to 48, 40 if X is 22 to 42, 80 if X is 13 to 21, 100 if X is 6 to 12, 150 if X is 2 to 5, and 200 if X is 0 or 1, where X is equal to (user's current HP * 64 / user's maximum HP), rounded down.", basePowerCallback(pokemon, target) { - let ratio = pokemon.hp * 64 / pokemon.maxhp; + const ratio = pokemon.hp * 64 / pokemon.maxhp; if (ratio < 2) { return 200; } @@ -1383,12 +1391,17 @@ let BattleMovedex = { sketch: { inherit: true, onHit(target, source) { - let disallowedMoves = ['chatter', 'sketch', 'struggle']; - if (source.transformed || !target.lastMove || disallowedMoves.includes(target.lastMove.id) || source.moves.includes(target.lastMove.id) || target.volatiles['substitute']) return false; - let sketchIndex = source.moves.indexOf('sketch'); + const disallowedMoves = ['chatter', 'sketch', 'struggle']; + if (source.transformed || !target.lastMove || target.volatiles['substitute']) { + return false; + } + if (disallowedMoves.includes(target.lastMove.id) || source.moves.includes(target.lastMove.id)) { + return false; + } + const sketchIndex = source.moves.indexOf('sketch'); if (sketchIndex < 0) return false; - let move = this.dex.getMove(target.lastMove.id); - let sketchedMove = { + const move = this.dex.getMove(target.lastMove.id); + const sketchedMove = { move: move.name, id: move.id, pp: move.pp, @@ -1405,8 +1418,8 @@ let BattleMovedex = { inherit: true, desc: "The user swaps its Ability with the target's Ability. Fails if either the user or the target's Ability is Multitype or Wonder Guard, or if both have the same Ability.", onHit(target, source) { - let targetAbility = target.ability; - let sourceAbility = source.ability; + const targetAbility = target.ability; + const sourceAbility = source.ability; if (targetAbility === sourceAbility) { return false; } @@ -1514,7 +1527,7 @@ let BattleMovedex = { return damage; } if (damage > target.volatiles['substitute'].hp) { - damage = /** @type {number} */ (target.volatiles['substitute'].hp); + damage = target.volatiles['substitute'].hp as number; } target.volatiles['substitute'].hp -= damage; source.lastDamage = damage; @@ -1532,7 +1545,7 @@ let BattleMovedex = { this.heal(Math.ceil(damage * move.drain[0] / move.drain[1]), source, target, 'drain'); } this.runEvent('AfterSubDamage', target, source, move, damage); - return 0; // hit + return this.HIT_SUBSTITUTE; }, onEnd(target) { this.add('-end', target, 'Substitute'); @@ -1543,7 +1556,7 @@ let BattleMovedex = { inherit: true, desc: "Fails if the target did not select a physical or special attack for use this turn, or if the target moves before the user.", onTry(source, target) { - let action = this.queue.willMove(target); + const action = this.queue.willMove(target); if (!action || action.choice !== 'move' || action.move.category === 'Status' || target.volatiles.mustrecharge) { this.add('-fail', source); return null; @@ -1596,7 +1609,7 @@ let BattleMovedex = { effect: { duration: 3, durationCallback(target, source, effect) { - if (source && source.hasAbility('persistent')) { + if (source?.hasAbility('persistent')) { this.add('-activate', source, 'ability: Persistent', effect); return 5; } @@ -1787,8 +1800,8 @@ let BattleMovedex = { onResidualOrder: 0.5, onEnd(target) { if (!target.fainted) { - let source = this.effectData.source; - let damage = this.heal(target.baseMaxhp / 2, target, target); + const source = this.effectData.source; + const damage = this.heal(target.baseMaxhp / 2, target, target); if (damage) this.add('-heal', target, target.getHealth, '[from] move: Wish', '[wisher] ' + source.name); } }, @@ -1804,7 +1817,7 @@ let BattleMovedex = { inherit: true, desc: "Causes the target's Ability to become Insomnia. Fails if the target's Ability is Multitype or Truant.", onTryHit(pokemon) { - let bannedAbilities = ['multitype', 'truant']; + const bannedAbilities = ['multitype', 'truant']; if (bannedAbilities.includes(pokemon.ability)) { return false; } @@ -1824,5 +1837,3 @@ let BattleMovedex = { }, }, }; - -exports.BattleMovedex = BattleMovedex; diff --git a/data/mods/gen4/pokedex.js b/data/mods/gen4/pokedex.ts similarity index 77% rename from data/mods/gen4/pokedex.js rename to data/mods/gen4/pokedex.ts index 505f15f040..3a50cad726 100644 --- a/data/mods/gen4/pokedex.js +++ b/data/mods/gen4/pokedex.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesData}} */ -let BattlePokedex = { +export const BattlePokedex: {[k: string]: ModdedSpeciesData} = { milotic: { inherit: true, evoType: 'levelExtra', @@ -28,5 +25,3 @@ let BattlePokedex = { types: ["Electric", "Ghost"], }, }; - -exports.BattlePokedex = BattlePokedex; diff --git a/data/mods/gen4/random-teams.js b/data/mods/gen4/random-teams.ts similarity index 92% rename from data/mods/gen4/random-teams.js rename to data/mods/gen4/random-teams.ts index aa1e532056..c0bfade2eb 100644 --- a/data/mods/gen4/random-teams.js +++ b/data/mods/gen4/random-teams.ts @@ -1,29 +1,20 @@ -'use strict'; +/* eslint max-len: ["error", 240] */ -const RandomGen5Teams = require('../gen5/random-teams'); +import RandomGen5Teams from '../gen5/random-teams'; -class RandomGen4Teams extends RandomGen5Teams { - /** - * @param {string | Species} species - * @param {RandomTeamsTypes.TeamDetails} [teamDetails] - * @param {boolean} [isLead] - * @return {RandomTeamsTypes.RandomSet} - */ - randomSet(species, teamDetails = {}, isLead = false) { - let baseSpecies = (species = this.dex.getSpecies(species)); +export class RandomGen4Teams extends RandomGen5Teams { + randomSet(species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}, isLead = false): RandomTeamsTypes.RandomSet { + const baseSpecies = (species = this.dex.getSpecies(species)); let forme = species.name; - if (species.battleOnly) forme = /** @type {string} */ (species.battleOnly); + if (species.battleOnly && species.battleOnly === 'string') forme = species.battleOnly; - // @ts-ignore - let movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id].learnset)).slice(); - /** @type {string[]} */ - let rejectedPool = []; - /**@type {string[]} */ - let moves = []; + const movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id].learnset!)).slice(); + const rejectedPool: string[] = []; + const moves: string[] = []; let ability = ''; let item = ''; - let evs = { + const evs = { hp: 85, atk: 85, def: 85, @@ -39,14 +30,12 @@ class RandomGen4Teams extends RandomGen5Teams { spd: 31, spe: 31, }; - /**@type {{[k: string]: true}} */ - let hasType = {}; + const hasType: {[k: string]: true} = {}; hasType[species.types[0]] = true; if (species.types[1]) { hasType[species.types[1]] = true; } - /**@type {{[k: string]: true}} */ - let hasAbility = {}; + const hasAbility: {[k: string]: true} = {}; hasAbility[species.abilities[0]] = true; if (species.abilities[1]) { hasAbility[species.abilities[1]] = true; @@ -57,15 +46,14 @@ class RandomGen4Teams extends RandomGen5Teams { } // These moves can be used even if we aren't setting up to use them: - let SetupException = ['suckerpunch', 'dracometeor', 'overheat']; - let counterAbilities = ['Adaptability', 'Hustle', 'Iron Fist', 'Skill Link']; + const SetupException = ['suckerpunch', 'dracometeor', 'overheat']; + const counterAbilities = ['Adaptability', 'Hustle', 'Iron Fist', 'Skill Link']; // Give recovery moves priority over certain other defensive status moves - let recoveryMoves = ['healorder', 'milkdrink', 'moonlight', 'morningsun', 'painsplit', 'recover', 'rest', 'roost', 'slackoff', 'softboiled', 'synthesis', 'wish']; - let defensiveStatusMoves = ['aromatherapy', 'haze', 'healbell', 'roar', 'whirlwind', 'willowisp', 'yawn']; + const recoveryMoves = ['healorder', 'milkdrink', 'moonlight', 'morningsun', 'painsplit', 'recover', 'rest', 'roost', 'slackoff', 'softboiled', 'synthesis', 'wish']; + const defensiveStatusMoves = ['aromatherapy', 'haze', 'healbell', 'roar', 'whirlwind', 'willowisp', 'yawn']; - /**@type {{[k: string]: boolean}} */ - let hasMove = {}; + let hasMove: {[k: string]: boolean} = {}; let counter; do { @@ -81,7 +69,7 @@ class RandomGen4Teams extends RandomGen5Teams { // Choose next 4 moves from learnset/viable moves and add them to moves list: while (moves.length < 4 && movePool.length) { - let moveid = this.sampleNoReplace(movePool); + const moveid = this.sampleNoReplace(movePool); if (moveid.substr(0, 11) === 'hiddenpower') { availableHP--; if (hasMove['hiddenpower']) continue; @@ -92,7 +80,7 @@ class RandomGen4Teams extends RandomGen5Teams { moves.push(moveid); } while (moves.length < 4 && rejectedPool.length) { - let moveid = this.sampleNoReplace(rejectedPool); + const moveid = this.sampleNoReplace(rejectedPool); hasMove[moveid] = true; moves.push(moveid); } @@ -104,8 +92,8 @@ class RandomGen4Teams extends RandomGen5Teams { // Iterate through the moves again, this time to cull them: for (const [i, setMoveid] of moves.entries()) { - let move = this.dex.getMove(setMoveid); - let moveid = move.id; + const move = this.dex.getMove(setMoveid); + const moveid = move.id; let rejected = false; let isSetup = false; @@ -134,7 +122,7 @@ class RandomGen4Teams extends RandomGen5Teams { case 'sleeptalk': if (!hasMove['rest']) rejected = true; if (movePool.length > 1) { - let rest = movePool.indexOf('rest'); + const rest = movePool.indexOf('rest'); if (rest >= 0) this.fastPop(movePool, rest); } break; @@ -366,14 +354,12 @@ class RandomGen4Teams extends RandomGen5Teams { // Reject STABs last in case the setup type changes later on if (!SetupException.includes(moveid) && (!hasType[move.type] || counter.stab > 1 || counter[move.category] < 2)) rejected = true; } - // @ts-ignore if (counter.setupType && !isSetup && move.category !== counter.setupType && counter[counter.setupType] < 2 && !hasMove['batonpass']) { // Mono-attacking with setup and RestTalk or recovery + status healing is allowed if (moveid !== 'rest' && moveid !== 'sleeptalk' && !(recoveryMoves.includes(moveid) && (hasMove['healbell'] || hasMove['refresh'])) && !((moveid === 'healbell' || moveid === 'refresh') && moves.some(id => !!recoveryMoves.includes(id)))) { // Reject Status moves only if there is nothing else to reject - // @ts-ignore if (move.category !== 'Status' || counter[counter.setupType] + counter.Status > 3 && counter['physicalsetup'] + counter['specialsetup'] < 2) rejected = true; } } @@ -385,7 +371,6 @@ class RandomGen4Teams extends RandomGen5Teams { // Reject defensive status moves if a reliable recovery move is available but not selected. // Toxic is only defensive if used with another status move other than Protect (Toxic + 3 attacks and Toxic + Protect are ok). if ((defensiveStatusMoves.includes(moveid) || moveid === 'toxic' && ((counter.Status > 1 && !hasMove['protect']) || counter.Status > 2)) && - // @ts-ignore !moves.some(id => recoveryMoves.includes(id)) && movePool.some(id => recoveryMoves.includes(id))) { rejected = true; } @@ -404,15 +389,12 @@ class RandomGen4Teams extends RandomGen5Teams { (hasType['Rock'] && !counter['Rock'] && (movePool.includes('headsmash') || movePool.includes('stoneedge'))) || (hasType['Steel'] && !counter['Steel'] && movePool.includes('meteormash')) || (hasType['Water'] && !counter['Water'] && (hasMove['raindance'] || !hasType['Ice'] || !counter['Ice'])) || - // @ts-ignore ((hasAbility['Adaptability'] && !counter.setupType && species.types.length > 1 && (!counter[species.types[0]] || !counter[species.types[1]])) || (hasAbility['Guts'] && hasType['Normal'] && movePool.includes('facade')) || (hasAbility['Slow Start'] && movePool.includes('substitute')) || - // @ts-ignore (counter['defensesetup'] && !counter.recovery && !hasMove['rest']) || (movePool.includes('spore') || (!moves.some(id => recoveryMoves.includes(id)) && (movePool.includes('softboiled') || (species.baseSpecies === 'Arceus' && movePool.includes('recover'))))) || (species.requiredMove && movePool.includes(toID(species.requiredMove)))) && - // @ts-ignore (counter['physicalsetup'] + counter['specialsetup'] < 2 && (!counter.setupType || (move.category !== counter.setupType && move.category !== 'Status') || counter[counter.setupType] + counter.Status > 3))) { // Reject Status or non-STAB if (!isSetup && !move.weather && moveid !== 'judgment' && !recoveryMoves.includes(moveid) && moveid !== 'sleeptalk') { @@ -422,7 +404,7 @@ class RandomGen4Teams extends RandomGen5Teams { // Sleep Talk shouldn't be selected without Rest if (moveid === 'rest' && rejected) { - let sleeptalk = movePool.indexOf('sleeptalk'); + const sleeptalk = movePool.indexOf('sleeptalk'); if (sleeptalk >= 0) { if (movePool.length < 2) { rejected = false; @@ -450,7 +432,7 @@ class RandomGen4Teams extends RandomGen5Teams { moves.splice(this.random(moves.length), 1); } else if (counter.damagingMoves.length === 1) { // In most cases, a set shouldn't have no STAB - let damagingid = counter.damagingMoves[0].id; + const damagingid = counter.damagingMoves[0].id; if (movePool.length - availableHP || availableHP && (damagingid === 'hiddenpower' || !hasMove['hiddenpower'])) { let replace = false; if (!counter.damagingMoves[0].damage && species.name !== 'Blissey' && species.name !== 'Porygon2') { @@ -460,8 +442,8 @@ class RandomGen4Teams extends RandomGen5Teams { } } else if (!counter.damagingMoves[0].damage && !counter.damagingMoves[1].damage && species.name !== 'Blissey' && species.name !== 'Clefable' && species.name !== 'Porygon2') { // If you have three or more attacks, and none of them are STAB, reject one of them at random. - let rejectableMoves = []; - let baseDiff = movePool.length - availableHP; + const rejectableMoves = []; + const baseDiff = movePool.length - availableHP; for (const move of counter.damagingMoves) { if (baseDiff || availableHP && (!hasMove['hiddenpower'] || move.id === 'hiddenpower')) { rejectableMoves.push(counter.damagingMoveIndex[move.id]); @@ -479,7 +461,7 @@ class RandomGen4Teams extends RandomGen5Teams { ivs = {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31}; } - let abilities = Object.values(baseSpecies.abilities); + const abilities = Object.values(baseSpecies.abilities); abilities.sort((a, b) => this.dex.getAbility(b).rating - this.dex.getAbility(a).rating); let ability0 = this.dex.getAbility(abilities[0]); let ability1 = this.dex.getAbility(abilities[1]); @@ -491,12 +473,11 @@ class RandomGen4Teams extends RandomGen5Teams { } ability = ability0.name; - let rejectAbility; + let rejectAbility: boolean; do { rejectAbility = false; if (counterAbilities.includes(ability)) { // Adaptability, Hustle, Iron Fist, Skill Link - // @ts-ignore rejectAbility = !counter[toID(ability)]; } else if (ability === 'Blaze') { rejectAbility = !counter['Fire']; @@ -545,7 +526,6 @@ class RandomGen4Teams extends RandomGen5Teams { ability = ability1.name; } else { // Default to the highest rated ability if all are rejected - // @ts-ignore ability = abilities[0]; rejectAbility = false; } @@ -626,7 +606,7 @@ class RandomGen4Teams extends RandomGen5Teams { } else if (counter.damagingMoves.length >= 4) { item = (!!counter['Normal'] || counter['Dragon'] > 1 || hasMove['chargebeam'] || hasMove['suckerpunch']) ? 'Life Orb' : 'Expert Belt'; } else if (counter.damagingMoves.length >= 3 && !hasMove['superfang'] && !hasMove['metalburst']) { - let totalBulk = species.baseStats.hp + species.baseStats.def + species.baseStats.spd; + const totalBulk = species.baseStats.hp + species.baseStats.def + species.baseStats.spd; item = (!!counter['speedsetup'] || !!counter['priority'] || hasMove['dragondance'] || hasMove['trickroom'] || totalBulk < 235 || (species.baseStats.spe >= 70 && (totalBulk < 260 || (!!counter['recovery'] && totalBulk < 285)))) ? 'Life Orb' : 'Leftovers'; @@ -646,7 +626,7 @@ class RandomGen4Teams extends RandomGen5Teams { item = 'Black Sludge'; } - let levelScale = { + const levelScale: {[k: string]: number} = { LC: 87, NFE: 85, NU: 83, @@ -656,13 +636,11 @@ class RandomGen4Teams extends RandomGen5Teams { OU: 75, Uber: 71, }; - let customScale = { + const customScale: {[k: string]: number} = { Delibird: 99, Ditto: 99, 'Farfetch\'d': 99, Unown: 99, }; - let tier = species.tier; - // @ts-ignore + const tier = species.tier; let level = levelScale[tier] || 75; - // @ts-ignore if (customScale[species.name]) level = customScale[species.name]; // Prepare optimal HP @@ -681,7 +659,7 @@ class RandomGen4Teams extends RandomGen5Teams { if (hp % 4 === 0) evs.hp -= 4; } else { // Maximize number of Stealth Rock switch-ins - let srWeakness = this.dex.getEffectiveness('Rock', species); + const srWeakness = this.dex.getEffectiveness('Rock', species); if (srWeakness > 0 && hp % (4 / srWeakness) === 0) evs.hp -= 4; } @@ -711,4 +689,4 @@ class RandomGen4Teams extends RandomGen5Teams { } } -module.exports = RandomGen4Teams; +export default RandomGen4Teams; diff --git a/data/mods/gen4/rulesets.js b/data/mods/gen4/rulesets.js deleted file mode 100644 index 9fbf1801fe..0000000000 --- a/data/mods/gen4/rulesets.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -/**@type {{[k: string]: ModdedFormatsData}} */ -let BattleFormats = { - standard: { - inherit: true, - ruleset: ['Obtainable', 'Sleep Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod'], - }, - validatestats: { - inherit: true, - onValidateSet(set) { - let species = this.dex.getSpecies(set.species); - let item = this.dex.getItem(set.item); - if (item && item.id === 'griseousorb' && species.num !== 487) { - return ['Griseous Orb can only be held by Giratina in Generation 4.']; - } - if (species.num === 493 && set.evs && (set.moves.includes('roaroftime') || set.moves.includes('shadowforce') || set.moves.includes('spacialrend'))) { - for (let stat in set.evs) { - // @ts-ignore - if (set.evs[stat] > 100) return ["Event Arceus may not have more than 100 of any EVs in Generation 4."]; - } - } - }, - }, -}; - -exports.BattleFormats = BattleFormats; - diff --git a/data/mods/gen4/rulesets.ts b/data/mods/gen4/rulesets.ts new file mode 100644 index 0000000000..70443f073e --- /dev/null +++ b/data/mods/gen4/rulesets.ts @@ -0,0 +1,28 @@ +export const BattleFormats: {[k: string]: ModdedFormatsData} = { + standard: { + inherit: true, + ruleset: ['Obtainable', 'Sleep Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod'], + }, + validatestats: { + inherit: true, + onValidateSet(set) { + const species = this.dex.getSpecies(set.species); + const item = this.dex.getItem(set.item); + if (item && item.id === 'griseousorb' && species.num !== 487) { + return ['Griseous Orb can only be held by Giratina in Generation 4.']; + } + if (species.num === 493 && set.evs) { + const isEventArceus = set.moves.includes('roaroftime') || set.moves.includes('shadowforce') || + set.moves.includes('spacialrend'); + if (isEventArceus) { + let stat: StatName; + for (stat in set.evs) { + if (set.evs[stat] > 100) { + return ["Event Arceus may not have more than 100 of any EVs in Generation 4."]; + } + } + } + } + }, + }, +}; diff --git a/data/mods/gen4/scripts.js b/data/mods/gen4/scripts.ts similarity index 90% rename from data/mods/gen4/scripts.js rename to data/mods/gen4/scripts.ts index 03662edc20..efeb34f1b9 100644 --- a/data/mods/gen4/scripts.js +++ b/data/mods/gen4/scripts.ts @@ -1,11 +1,8 @@ -'use strict'; - -/**@type {ModdedBattleScriptsData} */ -let BattleScripts = { +export const BattleScripts: ModdedBattleScriptsData = { inherit: 'gen5', gen: 4, init() { - for (let i in this.data.Pokedex) { + for (const i in this.data.Pokedex) { delete this.data.Pokedex[i].abilities['H']; } }, @@ -15,7 +12,7 @@ let BattleScripts = { // The modifiers run earlier than other generations are called with ModifyDamagePhase1 and ModifyDamagePhase2 if (!move.type) move.type = '???'; - let type = move.type; + const type = move.type; // Burn if (pokemon.status === 'brn' && baseDamage && move.category === 'Physical' && !pokemon.hasAbility('guts')) { @@ -27,7 +24,7 @@ let BattleScripts = { // Double battle multi-hit if (move.spreadHit) { - let spreadModifier = move.spreadModifier || (this.gameType === 'free-for-all' ? 0.5 : 0.75); + const spreadModifier = move.spreadModifier || (this.gameType === 'free-for-all' ? 0.5 : 0.75); this.debug('Spread modifier: ' + spreadModifier); baseDamage = this.modify(baseDamage, spreadModifier); } @@ -102,9 +99,6 @@ let BattleScripts = { }, calcRecoilDamage(damageDealt, move) { - // @ts-ignore - return this.dex.clampIntRange(Math.floor(damageDealt * move.recoil[0] / move.recoil[1]), 1); + return this.dex.clampIntRange(Math.floor(damageDealt * move.recoil![0] / move.recoil![1]), 1); }, }; - -exports.BattleScripts = BattleScripts; diff --git a/data/mods/gen4/statuses.js b/data/mods/gen4/statuses.ts similarity index 92% rename from data/mods/gen4/statuses.js rename to data/mods/gen4/statuses.ts index 5fa50e0c79..5a041387d9 100644 --- a/data/mods/gen4/statuses.js +++ b/data/mods/gen4/statuses.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedPureEffectData}} */ -let BattleStatuses = { +export const BattleStatuses: {[k: string]: ModdedPureEffectData} = { par: { inherit: true, onBeforeMove(pokemon) { @@ -79,5 +76,3 @@ let BattleStatuses = { counterMax: 8, }, }; - -exports.BattleStatuses = BattleStatuses; diff --git a/data/mods/gen5/abilities.js b/data/mods/gen5/abilities.ts similarity index 87% rename from data/mods/gen5/abilities.js rename to data/mods/gen5/abilities.ts index 8d32a68bcb..8becad1883 100644 --- a/data/mods/gen5/abilities.js +++ b/data/mods/gen5/abilities.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedAbilityData}} */ -let BattleAbilities = { +export const BattleAbilities: {[k: string]: ModdedAbilityData} = { anticipation: { inherit: true, desc: "On switch-in, this Pokemon is alerted if any opposing Pokemon has an attack that is super effective on this Pokemon, or an OHKO move. Counter, Metal Burst, and Mirror Coat count as attacking moves of their respective types, while Hidden Power, Judgment, Natural Gift, Techno Blast, and Weather Ball are considered Normal-type moves.", @@ -10,7 +7,10 @@ let BattleAbilities = { if (!target || target.fainted) continue; for (const moveSlot of target.moveSlots) { const move = this.dex.getMove(moveSlot.move); - if (move.category !== 'Status' && (this.dex.getImmunity(move.type, pokemon) && this.dex.getEffectiveness(move.type, pokemon) > 0 || move.ohko)) { + if (move.category !== 'Status' && ( + this.dex.getImmunity(move.type, pokemon) && this.dex.getEffectiveness(move.type, pokemon) > 0 || + move.ohko + )) { this.add('-ability', pokemon, 'Anticipation'); return; } @@ -22,8 +22,8 @@ let BattleAbilities = { inherit: true, shortDesc: "On switch-in, this Pokemon identifies a random foe's held item.", onStart(pokemon) { - let target = pokemon.side.foe.randomActive(); - if (target && target.item) { + const target = pokemon.side.foe.randomActive(); + if (target?.item) { this.add('-item', target, target.getItem().name, '[from] ability: Frisk', '[of] ' + pokemon); } }, @@ -85,5 +85,3 @@ let BattleAbilities = { onAllyTryHitSide() {}, }, }; - -exports.BattleAbilities = BattleAbilities; diff --git a/data/mods/gen5/formats-data.js b/data/mods/gen5/formats-data.ts similarity index 99% rename from data/mods/gen5/formats-data.js rename to data/mods/gen5/formats-data.ts index 6e70fda410..62e5c00123 100644 --- a/data/mods/gen5/formats-data.js +++ b/data/mods/gen5/formats-data.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesFormatsData}} */ -let BattleFormatsData = { +export const BattleFormatsData: {[k: string]: ModdedSpeciesFormatsData} = { bulbasaur: { maleOnlyHidden: true, tier: "LC", @@ -2948,5 +2945,3 @@ let BattleFormatsData = { tier: "Illegal", }, }; - -exports.BattleFormatsData = BattleFormatsData; diff --git a/data/mods/gen5/items.js b/data/mods/gen5/items.ts similarity index 98% rename from data/mods/gen5/items.js rename to data/mods/gen5/items.ts index abdb870229..ecad8f2f13 100644 --- a/data/mods/gen5/items.js +++ b/data/mods/gen5/items.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedItemData}} */ -let BattleItems = { +export const BattleItems: {[k: string]: ModdedItemData} = { aguavberry: { inherit: true, naturalGift: { @@ -540,5 +537,3 @@ let BattleItems = { }, }, }; - -exports.BattleItems = BattleItems; diff --git a/data/mods/gen5/moves.js b/data/mods/gen5/moves.ts similarity index 97% rename from data/mods/gen5/moves.js rename to data/mods/gen5/moves.ts index e634059be6..aa4085252b 100644 --- a/data/mods/gen5/moves.js +++ b/data/mods/gen5/moves.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedMoveData}} */ -let BattleMovedex = { +export const BattleMovedex: {[k: string]: ModdedMoveData} = { absorb: { inherit: true, flags: {protect: 1, mirror: 1}, @@ -32,12 +29,12 @@ let BattleMovedex = { inherit: true, desc: "A random move among those known by the user's party members is selected for use. Does not select Assist, Bestow, Chatter, Circle Throw, Copycat, Counter, Covet, Destiny Bond, Detect, Dragon Tail, Endure, Feint, Focus Punch, Follow Me, Helping Hand, Me First, Metronome, Mimic, Mirror Coat, Mirror Move, Nature Power, Protect, Rage Powder, Sketch, Sleep Talk, Snatch, Struggle, Switcheroo, Thief, Transform, or Trick.", onHit(target) { - let moves = []; + const moves = []; for (const pokemon of target.side.pokemon) { if (pokemon === target) continue; for (const moveSlot of pokemon.moveSlots) { - let moveid = moveSlot.id; - let noAssist = [ + const moveid = moveSlot.id; + const noAssist = [ 'assist', 'bestow', 'chatter', 'circlethrow', 'copycat', 'counter', 'covet', 'destinybond', 'detect', 'dragontail', 'endure', 'feint', 'focuspunch', 'followme', 'helpinghand', 'mefirst', 'metronome', 'mimic', 'mirrorcoat', 'mirrormove', 'naturepower', 'protect', 'ragepowder', 'sketch', 'sleeptalk', 'snatch', 'struggle', 'switcheroo', 'thief', 'transform', 'trick', ]; if (moveid && !noAssist.includes(moveid)) { @@ -168,8 +165,8 @@ let BattleMovedex = { desc: "The user's type changes to match the original type of one of its known moves besides this move, at random, but not either of its current types. Fails if the user cannot change its type, or if this move would only be able to select one of the user's current types.", shortDesc: "Changes user's type to match a known move.", onHit(target) { - let possibleTypes = target.moveSlots.map(moveSlot => { - let move = this.dex.getMove(moveSlot.id); + const possibleTypes = target.moveSlots.map(moveSlot => { + const move = this.dex.getMove(moveSlot.id); if (move.id !== 'conversion' && !target.hasType(move.type)) { return move.type; } @@ -178,7 +175,7 @@ let BattleMovedex = { if (!possibleTypes.length) { return false; } - let type = this.sample(possibleTypes); + const type = this.sample(possibleTypes); if (!target.setType(type)) return false; this.add('-start', target, 'typechange', type); @@ -188,7 +185,9 @@ let BattleMovedex = { inherit: true, desc: "The user uses the last move used by any Pokemon, including itself. Fails if no move has been used, or if the last move used was Assist, Bestow, Chatter, Circle Throw, Copycat, Counter, Covet, Destiny Bond, Detect, Dragon Tail, Endure, Feint, Focus Punch, Follow Me, Helping Hand, Me First, Metronome, Mimic, Mirror Coat, Mirror Move, Nature Power, Protect, Rage Powder, Sketch, Sleep Talk, Snatch, Struggle, Switcheroo, Thief, Transform, or Trick.", onHit(pokemon) { - let noCopycat = ['assist', 'bestow', 'chatter', 'circlethrow', 'copycat', 'counter', 'covet', 'destinybond', 'detect', 'dragontail', 'endure', 'feint', 'focuspunch', 'followme', 'helpinghand', 'mefirst', 'metronome', 'mimic', 'mirrorcoat', 'mirrormove', 'naturepower', 'protect', 'ragepowder', 'sketch', 'sleeptalk', 'snatch', 'struggle', 'switcheroo', 'thief', 'transform', 'trick']; + const noCopycat = [ + 'assist', 'bestow', 'chatter', 'circlethrow', 'copycat', 'counter', 'covet', 'destinybond', 'detect', 'dragontail', 'endure', 'feint', 'focuspunch', 'followme', 'helpinghand', 'mefirst', 'metronome', 'mimic', 'mirrorcoat', 'mirrormove', 'naturepower', 'protect', 'ragepowder', 'sketch', 'sleeptalk', 'snatch', 'struggle', 'switcheroo', 'thief', 'transform', 'trick', + ]; if (!this.lastMove || noCopycat.includes(this.lastMove.id)) { return false; } @@ -215,7 +214,7 @@ let BattleMovedex = { shortDesc: "-1 evasion; clears target side's hazards/screens.", onHit(pokemon) { if (!pokemon.volatiles['substitute']) this.boost({evasion: -1}); - let sideConditions = ['reflect', 'lightscreen', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock']; + const sideConditions = ['reflect', 'lightscreen', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock']; for (const condition of sideConditions) { if (pokemon.side.removeSideCondition(condition)) { this.add('-sideend', pokemon.side, this.dex.getEffect(condition).name, '[from] move: Defog', '[of] ' + pokemon); @@ -547,7 +546,7 @@ let BattleMovedex = { desc: "The target loses its held item if it is a Berry. This move cannot cause Pokemon with the Sticky Hold Ability to lose their held item. Items lost to this move cannot be regained with Recycle or the Harvest Ability.", shortDesc: "Destroys the foe(s) Berry.", onHit(pokemon, source) { - let item = pokemon.getItem(); + const item = pokemon.getItem(); if (item.isBerry && pokemon.takeItem(source)) { this.add('-enditem', pokemon, item.name, '[from] move: Incinerate'); } @@ -581,7 +580,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(target, source, effect) { - if (source && source.hasItem('lightclay')) { + if (source?.hasItem('lightclay')) { return 8; } return 5; @@ -693,7 +692,8 @@ let BattleMovedex = { }, onAnyBasePowerPriority: 1, onAnyBasePower(basePower, user, target, move) { - if (move.type === 'Electric') return this.chainModify([0x548, 0x1000]); // The Mud Sport modifier is slightly higher than the usual 0.33 modifier (0x547) + // The Mud Sport modifier is slightly higher than the usual 0.33 modifier (0x547) + if (move.type === 'Electric') return this.chainModify([0x548, 0x1000]); }, }, secondary: null, @@ -755,8 +755,8 @@ let BattleMovedex = { psychup: { inherit: true, onHit(target, source) { - for (let i in target.boosts) { - // @ts-ignore + let i: BoostName; + for (i in target.boosts) { source.boosts[i] = target.boosts[i]; } this.add('-copyboost', source, target, '[from] move: Psych Up'); @@ -797,7 +797,7 @@ let BattleMovedex = { return; } this.add('-activate', target, 'Quick Guard'); - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -818,7 +818,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(target, source, effect) { - if (source && source.hasItem('lightclay')) { + if (source?.hasItem('lightclay')) { return 8; } return 5; @@ -912,8 +912,8 @@ let BattleMovedex = { inherit: true, desc: "The user swaps its Ability with the target's Ability. Fails if either the user or the target's Ability is Illusion, Multitype, or Wonder Guard, or if both have the same Ability.", onHit(target, source) { - let targetAbility = target.ability; - let sourceAbility = source.ability; + const targetAbility = target.ability; + const sourceAbility = source.ability; if (targetAbility === sourceAbility) { return false; } @@ -1062,7 +1062,7 @@ let BattleMovedex = { return damage; } if (damage > target.volatiles['substitute'].hp) { - damage = /** @type {number} */ (target.volatiles['substitute'].hp); + damage = target.volatiles['substitute'].hp as number; } target.volatiles['substitute'].hp -= damage; source.lastDamage = damage; @@ -1079,7 +1079,7 @@ let BattleMovedex = { } this.singleEvent('AfterSubDamage', move, null, target, source, move, damage); this.runEvent('AfterSubDamage', target, source, move, damage); - return 0; // hit + return this.HIT_SUBSTITUTE; }, onEnd(target) { this.add('-end', target, 'Substitute'); @@ -1218,7 +1218,8 @@ let BattleMovedex = { }, onAnyBasePowerPriority: 1, onAnyBasePower(basePower, user, target, move) { - if (move.type === 'Fire') return this.chainModify([0x548, 0x1000]); // The Water Sport modifier is slightly higher than the usual 0.33 modifier (0x547) + // The Water Sport modifier is slightly higher than the usual 0.33 modifier (0x547) + if (move.type === 'Fire') return this.chainModify([0x548, 0x1000]); }, }, secondary: null, @@ -1266,5 +1267,3 @@ let BattleMovedex = { desc: "Prevents the target from switching for four or five turns (seven turns if the user is holding Grip Claw). Causes damage to the target equal to 1/16 of its maximum HP (1/8 if the user is holding Binding Band), rounded down, at the end of each turn during effect. The target can still switch out if it is holding Shed Shell or uses Baton Pass, U-turn, or Volt Switch. The effect ends if either the user or the target leaves the field, or if the target uses Rapid Spin or Substitute successfully. This effect is not stackable or reset by using this or another binding move.", }, }; - -exports.BattleMovedex = BattleMovedex; diff --git a/data/mods/gen5/pokedex.js b/data/mods/gen5/pokedex.ts similarity index 98% rename from data/mods/gen5/pokedex.js rename to data/mods/gen5/pokedex.ts index 6f19cb4ab8..07e84b1624 100644 --- a/data/mods/gen5/pokedex.js +++ b/data/mods/gen5/pokedex.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesData}} */ -let BattlePokedex = { +export const BattlePokedex: {[k: string]: ModdedSpeciesData} = { butterfree: { inherit: true, baseStats: {hp: 60, atk: 45, def: 50, spa: 80, spd: 80, spe: 70}, @@ -398,5 +395,3 @@ let BattlePokedex = { abilities: {0: "Flame Body"}, }, }; - -exports.BattlePokedex = BattlePokedex; diff --git a/data/mods/gen5/random-teams.js b/data/mods/gen5/random-teams.ts similarity index 88% rename from data/mods/gen5/random-teams.js rename to data/mods/gen5/random-teams.ts index 44287b3ecf..bfe3ce0f48 100644 --- a/data/mods/gen5/random-teams.js +++ b/data/mods/gen5/random-teams.ts @@ -1,31 +1,23 @@ -'use strict'; +/* eslint max-len: ["error", 240] */ -const RandomGen6Teams = require('../gen6/random-teams'); +import RandomGen6Teams from '../gen6/random-teams'; -class RandomGen5Teams extends RandomGen6Teams { - /** - * @param {string | Species} species - * @param {RandomTeamsTypes.TeamDetails} [teamDetails] - * @param {boolean} [isLead] - * @return {RandomTeamsTypes.RandomSet} - */ - randomSet(species, teamDetails = {}, isLead = false) { - let baseSpecies = (species = this.dex.getSpecies(species)); +export class RandomGen5Teams extends RandomGen6Teams { + randomSet(species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}, isLead = false): RandomTeamsTypes.RandomSet { + const baseSpecies = (species = this.dex.getSpecies(species)); let forme = species.name; - if (species.battleOnly) { + if (species.battleOnly && typeof species.battleOnly === 'string') { // Only change the forme. The species has custom moves, and may have different typing and requirements. - forme = /** @type {string} */ (species.battleOnly); + forme = species.battleOnly; } - // @ts-ignore - let movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id].learnset)).slice(); - let rejectedPool = []; - /**@type {string[]} */ - let moves = []; + const movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id]!.learnset!)).slice(); + const rejectedPool = []; + const moves: string[] = []; let ability = ''; let item = ''; - let evs = { + const evs = { hp: 85, atk: 85, def: 85, @@ -33,7 +25,7 @@ class RandomGen5Teams extends RandomGen6Teams { spd: 85, spe: 85, }; - let ivs = { + let ivs: SparseStatsTable = { hp: 31, atk: 31, def: 31, @@ -41,14 +33,12 @@ class RandomGen5Teams extends RandomGen6Teams { spd: 31, spe: 31, }; - /**@type {{[k: string]: true}} */ - let hasType = {}; + const hasType: {[k: string]: true} = {}; hasType[species.types[0]] = true; if (species.types[1]) { hasType[species.types[1]] = true; } - /**@type {{[k: string]: true}} */ - let hasAbility = {}; + const hasAbility: {[k: string]: true} = {}; hasAbility[species.abilities[0]] = true; if (species.abilities[1]) { hasAbility[species.abilities[1]] = true; @@ -62,12 +52,11 @@ class RandomGen5Teams extends RandomGen6Teams { } // These moves can be used even if we aren't setting up to use them: - let SetupException = ['closecombat', 'dracometeor', 'extremespeed', 'suckerpunch', 'superpower']; + const SetupException = ['closecombat', 'dracometeor', 'extremespeed', 'suckerpunch', 'superpower']; - let counterAbilities = ['Adaptability', 'Contrary', 'Hustle', 'Iron Fist', 'Skill Link']; + const counterAbilities = ['Adaptability', 'Contrary', 'Hustle', 'Iron Fist', 'Skill Link']; - /**@type {{[k: string]: boolean}} */ - let hasMove = {}; + let hasMove: {[k: string]: boolean} = {}; let counter; do { @@ -83,7 +72,7 @@ class RandomGen5Teams extends RandomGen6Teams { // Choose next 4 moves from learnset/viable moves and add them to moves list: while (moves.length < 4 && movePool.length) { - let moveid = this.sampleNoReplace(movePool); + const moveid = this.sampleNoReplace(movePool); if (moveid.substr(0, 11) === 'hiddenpower') { availableHP--; if (hasMove['hiddenpower']) continue; @@ -94,7 +83,7 @@ class RandomGen5Teams extends RandomGen6Teams { moves.push(moveid); } while (moves.length < 4 && rejectedPool.length) { - let moveid = this.sampleNoReplace(rejectedPool); + const moveid = this.sampleNoReplace(rejectedPool); hasMove[moveid] = true; moves.push(moveid); } @@ -103,8 +92,8 @@ class RandomGen5Teams extends RandomGen6Teams { // Iterate through the moves again, this time to cull them: for (const [i, setMoveid] of moves.entries()) { - let move = this.dex.getMove(setMoveid); - let moveid = move.id; + const move = this.dex.getMove(setMoveid); + const moveid = move.id; let rejected = false; let isSetup = false; @@ -125,7 +114,7 @@ class RandomGen5Teams extends RandomGen6Teams { case 'sleeptalk': if (!hasMove['rest']) rejected = true; if (movePool.length > 1) { - let rest = movePool.indexOf('rest'); + const rest = movePool.indexOf('rest'); if (rest >= 0) this.fastPop(movePool, rest); } break; @@ -291,15 +280,16 @@ class RandomGen5Teams extends RandomGen6Teams { // This move doesn't satisfy our setup requirements: if ((move.category === 'Physical' && counter.setupType === 'Special') || (move.category === 'Special' && counter.setupType === 'Physical')) { // Reject STABs last in case the setup type changes later on - // @ts-ignore - let stabs = counter[species.types[0]] + (counter[species.types[1]] || 0); + const stabs = counter[species.types[0]] + (counter[species.types[1]] || 0); if (!SetupException.includes(moveid) && (!hasType[move.type] || stabs > 1 || counter[move.category] < 2)) rejected = true; } - // @ts-ignore - if (counter.setupType && !isSetup && counter.setupType !== 'Mixed' && move.category !== counter.setupType && counter[counter.setupType] < 2 && !hasMove['batonpass'] && (move.category !== 'Status' || !move.flags.heal) && moveid !== 'sleeptalk') { + if ( + counter.setupType && !isSetup && counter.setupType !== 'Mixed' && move.category !== counter.setupType && + counter[counter.setupType] < 2 && !hasMove['batonpass'] && + (move.category !== 'Status' || !move.flags.heal) && moveid !== 'sleeptalk' + ) { // Mono-attacking with setup and RestTalk is allowed // Reject Status moves only if there is nothing else to reject - // @ts-ignore if (move.category !== 'Status' || counter[counter.setupType] + counter.Status > 3 && counter['physicalsetup'] + counter['specialsetup'] < 2) rejected = true; } if (counter.setupType === 'Special' && moveid === 'hiddenpower' && species.types.length > 1 && counter['Special'] <= 2 && !hasType[move.type] && !counter['Physical'] && counter['specialpool']) { @@ -308,9 +298,11 @@ class RandomGen5Teams extends RandomGen6Teams { } // Pokemon should have moves that benefit their Type/Ability/Weather, as well as moves required by its forme - // @ts-ignore - if (!rejected && (counter['physicalsetup'] + counter['specialsetup'] < 2 && (!counter.setupType || counter.setupType === 'Mixed' || (move.category !== counter.setupType && move.category !== 'Status') || counter[counter.setupType] + counter.Status > 3)) && - ((!counter.stab && !counter['damage'] && (species.types.length > 1 || (species.types[0] !== 'Normal' && species.types[0] !== 'Psychic') || !hasMove['icebeam'] || species.baseStats.spa >= species.baseStats.spd)) || + if (!rejected && ( + counter['physicalsetup'] + counter['specialsetup'] < 2 && + (!counter.setupType || counter.setupType === 'Mixed' || (move.category !== counter.setupType && move.category !== 'Status') || counter[counter.setupType] + counter.Status > 3) + ) && ( + (!counter.stab && !counter['damage'] && (species.types.length > 1 || (species.types[0] !== 'Normal' && species.types[0] !== 'Psychic') || !hasMove['icebeam'] || species.baseStats.spa >= species.baseStats.spd)) || (hasType['Dark'] && !counter['Dark']) || (hasType['Dragon'] && !counter['Dragon']) || (hasType['Electric'] && !counter['Electric']) || @@ -322,23 +314,27 @@ class RandomGen5Teams extends RandomGen6Teams { (hasType['Rock'] && !counter['Rock'] && species.baseStats.atk >= 80) || (hasType['Steel'] && hasAbility['Technician'] && !counter['Steel']) || (hasType['Water'] && !counter['Water']) || - // @ts-ignore ((hasAbility['Adaptability'] && !counter.setupType && species.types.length > 1 && (!counter[species.types[0]] || !counter[species.types[1]])) || (hasAbility['Bad Dreams'] && movePool.includes('darkvoid')) || (hasAbility['Contrary'] && !counter['contrary'] && species.name !== 'Shuckle') || (hasAbility['Guts'] && hasType['Normal'] && movePool.includes('facade')) || (hasAbility['Slow Start'] && movePool.includes('substitute')) || - (!counter.recovery && !counter.setupType && !hasMove['healingwish'] && (movePool.includes('recover') || movePool.includes('roost') || movePool.includes('softboiled')) && (counter.Status > 1 || (species.nfe && !!counter['Status']))) || - (species.requiredMove && movePool.includes(toID(species.requiredMove)))))) { + (!counter.recovery && !counter.setupType && !hasMove['healingwish'] && ( + movePool.includes('recover') || movePool.includes('roost') || movePool.includes('softboiled') + ) && (counter.Status > 1 || (species.nfe && !!counter['Status']))) || + (species.requiredMove && movePool.includes(toID(species.requiredMove)))) + )) { // Reject Status or non-STAB if (!isSetup && !move.weather && !move.damage && (move.category !== 'Status' || !move.flags.heal) && moveid !== 'judgment' && moveid !== 'sleeptalk') { - if (move.category === 'Status' || !hasType[move.type] || move.selfSwitch || move.basePower && move.basePower < 40 && !move.multihit) rejected = true; + if (move.category === 'Status' || !hasType[move.type] || move.selfSwitch || move.basePower && move.basePower < 40 && !move.multihit) { + rejected = true; + } } } // Sleep Talk shouldn't be selected without Rest if (moveid === 'rest' && rejected) { - let sleeptalk = movePool.indexOf('sleeptalk'); + const sleeptalk = movePool.indexOf('sleeptalk'); if (sleeptalk >= 0) { if (movePool.length < 2) { rejected = false; @@ -361,9 +357,9 @@ class RandomGen5Teams extends RandomGen6Teams { // Handle Hidden Power IVs if (moveid === 'hiddenpower') { - let HPivs = this.dex.getType(move.type).HPivs; - for (let iv in HPivs) { - // @ts-ignore + const HPivs = this.dex.getType(move.type).HPivs; + let iv: StatName; + for (iv in HPivs) { ivs[iv] = HPivs[iv]; } } @@ -375,7 +371,7 @@ class RandomGen5Teams extends RandomGen6Teams { ivs = {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31}; } - let abilities = Object.values(baseSpecies.abilities); + const abilities = Object.values(baseSpecies.abilities); abilities.sort((a, b) => this.dex.getAbility(b).rating - this.dex.getAbility(a).rating); let ability0 = this.dex.getAbility(abilities[0]); let ability1 = this.dex.getAbility(abilities[1]); @@ -391,12 +387,11 @@ class RandomGen5Teams extends RandomGen6Teams { } ability = ability0.name; - let rejectAbility; + let rejectAbility: boolean; do { rejectAbility = false; if (counterAbilities.includes(ability)) { // Adaptability, Contrary, Hustle, Iron Fist, Skill Link - // @ts-ignore rejectAbility = !counter[toID(ability)]; } else if (ability === 'Chlorophyll') { rejectAbility = !hasMove['sunnyday'] && !teamDetails['sun']; @@ -457,7 +452,6 @@ class RandomGen5Teams extends RandomGen6Teams { ability = ability2.name; } else { // Default to the highest rated ability if all are rejected - // @ts-ignore ability = abilities[0]; rejectAbility = false; } @@ -530,9 +524,9 @@ class RandomGen5Teams extends RandomGen6Teams { item = hasMove['drainpunch'] ? 'Flame Orb' : 'Toxic Orb'; } else if (ability === 'Unburden' && (counter['Physical'] || counter['Special'])) { // Give Unburden mons a random Gem of the type of one of their damaging moves - let eligibleTypes = []; + const eligibleTypes = []; for (const setMoveid of moves) { - let move = this.dex.getMove(setMoveid); + const move = this.dex.getMove(setMoveid); if (!move.basePower && !move.basePowerCallback) continue; eligibleTypes.push(move.type); } @@ -552,9 +546,9 @@ class RandomGen5Teams extends RandomGen6Teams { } else if (this.dex.getEffectiveness('Ground', species) >= 2 && ability !== 'Levitate' && !hasMove['magnetrise']) { item = 'Air Balloon'; } else if (hasMove['substitute'] && hasMove['reversal']) { - let eligibleTypes = []; + const eligibleTypes = []; for (const setMoveid of moves) { - let move = this.dex.getMove(setMoveid); + const move = this.dex.getMove(setMoveid); if (!move.basePower && !move.basePowerCallback) continue; eligibleTypes.push(move.type); } @@ -590,8 +584,7 @@ class RandomGen5Teams extends RandomGen6Teams { item = 'Black Sludge'; } - /** @type {{[tier: string]: number}} */ - let levelScale = { + const levelScale: {[tier: string]: number} = { Uber: 78, OU: 80, UUBL: 81, @@ -603,8 +596,7 @@ class RandomGen5Teams extends RandomGen6Teams { '(NU)': 87, NFE: 88, }; - /** @type {{[forme: string]: number}} */ - let customScale = { + const customScale: {[forme: string]: number} = { Blaziken: 79, 'Deoxys-Defense': 79, Landorus: 79, Manaphy: 79, Thundurus: 79, 'Tornadus-Therian': 79, Unown: 100, }; let level = levelScale[species.tier] || 80; @@ -613,12 +605,12 @@ class RandomGen5Teams extends RandomGen6Teams { // Minimize confusion damage if (!counter['Physical'] && !hasMove['transform']) { evs.atk = 0; - ivs.atk = hasMove['hiddenpower'] ? ivs.atk - 28 : 0; + ivs.atk = hasMove['hiddenpower'] ? (ivs.atk || 31) - 28 : 0; } if (hasMove['gyroball'] || hasMove['metalburst'] || hasMove['trickroom']) { evs.spe = 0; - ivs.spe = hasMove['hiddenpower'] ? ivs.spe - 28 : 0; + ivs.spe = hasMove['hiddenpower'] ? (ivs.spe || 31) - 28 : 0; } return { @@ -637,29 +629,24 @@ class RandomGen5Teams extends RandomGen6Teams { randomTeam() { const seed = this.prng.seed; - let pokemon = []; + const pokemon = []; - let pokemonPool = []; - for (let id in this.dex.data.FormatsData) { - let species = this.dex.getSpecies(id); + const pokemonPool = []; + for (const id in this.dex.data.FormatsData) { + const species = this.dex.getSpecies(id); if (!species.isNonstandard && species.randomBattleMoves) { pokemonPool.push(id); } } - /**@type {{[k: string]: number}} */ - let baseFormes = {}; - /**@type {{[k: string]: number}} */ - let tierCount = {}; - /**@type {{[k: string]: number}} */ - let typeCount = {}; - /**@type {{[k: string]: number}} */ - let typeComboCount = {}; - /**@type {RandomTeamsTypes.TeamDetails} */ - let teamDetails = {}; + const baseFormes: {[k: string]: number} = {}; + const tierCount: {[k: string]: number} = {}; + const typeCount: {[k: string]: number} = {}; + const typeComboCount: {[k: string]: number} = {}; + const teamDetails: RandomTeamsTypes.TeamDetails = {}; while (pokemonPool.length && pokemon.length < 6) { - let species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); + const species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); if (!species.exists) continue; // Limit to one of each species (Species Clause) @@ -684,14 +671,14 @@ class RandomGen5Teams extends RandomGen6Teams { break; } - let tier = species.tier; + const tier = species.tier; // Limit two Pokemon per tier if (tierCount[tier] > 1 && this.gen === 5) { continue; } - let types = species.types; + const types = species.types; // Limit 2 of any type let skip = false; @@ -703,7 +690,7 @@ class RandomGen5Teams extends RandomGen6Teams { } if (skip) continue; - let set = this.randomSet(species, teamDetails, pokemon.length === 0); + const set = this.randomSet(species, teamDetails, pokemon.length === 0); // Illusion shouldn't be the last Pokemon of the team if (set.ability === 'Illusion' && pokemon.length > 4) continue; @@ -723,7 +710,7 @@ class RandomGen5Teams extends RandomGen6Teams { if (pokemon.length === 6) { // Set Zoroark's level to be the same as the last Pokemon - let illusion = teamDetails['illusion']; + const illusion = teamDetails['illusion']; if (illusion) pokemon[illusion - 1].level = pokemon[5].level; break; } @@ -769,4 +756,4 @@ class RandomGen5Teams extends RandomGen6Teams { } } -module.exports = RandomGen5Teams; +export default RandomGen5Teams; diff --git a/data/mods/gen5/rulesets.js b/data/mods/gen5/rulesets.js deleted file mode 100644 index 8b89b97990..0000000000 --- a/data/mods/gen5/rulesets.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -/**@type {{[k: string]: ModdedFormatsData}} */ -let BattleFormats = { - standard: { - inherit: true, - ruleset: ['Obtainable', 'Team Preview', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Moody Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod'], - }, - obtainablemoves: { - inherit: true, - banlist: [ - // Shell Smash: Clamperl Gen 5+ level-up - // Sucker Punch: Huntail Gen 4 tutor - 'Huntail + Shell Smash + Sucker Punch', - ], - }, -}; - -exports.BattleFormats = BattleFormats; diff --git a/data/mods/gen5/rulesets.ts b/data/mods/gen5/rulesets.ts new file mode 100644 index 0000000000..3352c6b431 --- /dev/null +++ b/data/mods/gen5/rulesets.ts @@ -0,0 +1,16 @@ +export const BattleFormats: {[k: string]: ModdedFormatsData} = { + standard: { + inherit: true, + ruleset: [ + 'Obtainable', 'Team Preview', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Moody Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod', + ], + }, + obtainablemoves: { + inherit: true, + banlist: [ + // Shell Smash: Clamperl Gen 5+ level-up + // Sucker Punch: Huntail Gen 4 tutor + 'Huntail + Shell Smash + Sucker Punch', + ], + }, +}; diff --git a/data/mods/gen5/scripts.js b/data/mods/gen5/scripts.js deleted file mode 100644 index ab5b01b20f..0000000000 --- a/data/mods/gen5/scripts.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -/**@type {ModdedBattleScriptsData} */ -let BattleScripts = { - inherit: 'gen6', - gen: 5, -}; - -exports.BattleScripts = BattleScripts; diff --git a/data/mods/gen5/scripts.ts b/data/mods/gen5/scripts.ts new file mode 100644 index 0000000000..3b0522befa --- /dev/null +++ b/data/mods/gen5/scripts.ts @@ -0,0 +1,4 @@ +export const BattleScripts: ModdedBattleScriptsData = { + inherit: 'gen6', + gen: 5, +}; diff --git a/data/mods/gen5/statuses.js b/data/mods/gen5/statuses.ts similarity index 76% rename from data/mods/gen5/statuses.js rename to data/mods/gen5/statuses.ts index 43f7eb893e..bc0529a07c 100644 --- a/data/mods/gen5/statuses.js +++ b/data/mods/gen5/statuses.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedPureEffectData}} */ -let BattleStatuses = { +export const BattleStatuses: {[k: string]: ModdedPureEffectData} = { slp: { inherit: true, onSwitchIn(target) { @@ -11,11 +8,12 @@ let BattleStatuses = { partiallytrapped: { inherit: true, onResidual(pokemon) { - if (this.effectData.source && (!this.effectData.source.isActive || this.effectData.source.hp <= 0 || !this.effectData.source.activeTurns)) { + const trapper = this.effectData.source; + if (trapper && (!trapper.isActive || trapper.hp <= 0 || !trapper.activeTurns)) { delete pokemon.volatiles['partiallytrapped']; return; } - if (this.effectData.source.hasItem('bindingband')) { + if (trapper.hasItem('bindingband')) { this.damage(pokemon.baseMaxhp / 8); } else { this.damage(pokemon.baseMaxhp / 16); @@ -32,7 +30,7 @@ let BattleStatuses = { onStallMove() { // this.effectData.counter should never be undefined here. // However, just in case, use 1 if it is undefined. - let counter = this.effectData.counter || 1; + const counter = this.effectData.counter || 1; if (counter >= 256) { // 2^32 - special-cased because Battle.random(n) can't handle n > 2^16 - 1 return (this.random() * 4294967296 < 1); @@ -57,5 +55,3 @@ let BattleStatuses = { }, }, }; - -exports.BattleStatuses = BattleStatuses; diff --git a/data/mods/gen5/typechart.js b/data/mods/gen5/typechart.ts similarity index 89% rename from data/mods/gen5/typechart.js rename to data/mods/gen5/typechart.ts index 9d53331b89..fd5bf32408 100644 --- a/data/mods/gen5/typechart.js +++ b/data/mods/gen5/typechart.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedTypeData | null}} */ -let BattleTypeChart = { +export const BattleTypeChart: {[k: string]: ModdedTypeData | null} = { Electric: { inherit: true, damageTaken: { @@ -95,5 +92,3 @@ let BattleTypeChart = { }, Fairy: null, }; - -exports.BattleTypeChart = BattleTypeChart; diff --git a/data/mods/gen6/abilities.js b/data/mods/gen6/abilities.ts similarity index 96% rename from data/mods/gen6/abilities.js rename to data/mods/gen6/abilities.ts index c2aa51e41f..b6fef79f36 100644 --- a/data/mods/gen6/abilities.js +++ b/data/mods/gen6/abilities.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedAbilityData}} */ -let BattleAbilities = { +export const BattleAbilities: {[k: string]: ModdedAbilityData} = { aerilate: { inherit: true, desc: "This Pokemon's Normal-type moves become Flying-type moves and have their power multiplied by 1.3. This effect comes after other effects that change a move's type, but before Ion Deluge and Electrify's effects.", @@ -57,9 +54,8 @@ let BattleAbilities = { inherit: true, onSourceTryHeal(damage, target, source, effect) { this.debug("Heal is occurring: " + target + " <- " + source + " :: " + effect.id); - /**@type {{[k: string]: number}} */ - let canOoze = {drain: 1, leechseed: 1}; - if (canOoze[effect.id]) { + const canOoze = ['drain', 'leechseed']; + if (canOoze.includes(effect.id)) { this.damage(damage, null, null, null, true); return 0; } @@ -154,5 +150,3 @@ let BattleAbilities = { desc: "If this Pokemon is a Darmanitan, it changes to Zen Mode if it has 1/2 or less of its maximum HP at the end of a turn. If Darmanitan's HP is above 1/2 of its maximum HP at the end of a turn, it changes back to Standard Mode. If Darmanitan loses this Ability while in Zen Mode, it reverts to Standard Mode immediately.", }, }; - -exports.BattleAbilities = BattleAbilities; diff --git a/data/mods/gen6/formats-data.js b/data/mods/gen6/formats-data.ts similarity index 99% rename from data/mods/gen6/formats-data.js rename to data/mods/gen6/formats-data.ts index a50d7533c5..25a1e7faf8 100644 --- a/data/mods/gen6/formats-data.js +++ b/data/mods/gen6/formats-data.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesFormatsData}} */ -let BattleFormatsData = { +export const BattleFormatsData: {[k: string]: ModdedSpeciesFormatsData} = { bulbasaur: { tier: "LC", }, @@ -3925,5 +3922,3 @@ let BattleFormatsData = { doublesTier: "DOU", }, }; - -exports.BattleFormatsData = BattleFormatsData; diff --git a/data/mods/gen6/items.js b/data/mods/gen6/items.ts similarity index 98% rename from data/mods/gen6/items.js rename to data/mods/gen6/items.ts index 0ce5057ad6..66b5093712 100644 --- a/data/mods/gen6/items.js +++ b/data/mods/gen6/items.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedItemData}} */ -let BattleItems = { +export const BattleItems: {[k: string]: ModdedItemData} = { aguavberry: { inherit: true, desc: "Restores 1/8 max HP at 1/2 max HP or less; confuses if -SpD Nature. Single use.", diff --git a/data/mods/gen6/learnsets.js b/data/mods/gen6/learnsets.ts similarity index 98% rename from data/mods/gen6/learnsets.js rename to data/mods/gen6/learnsets.ts index 96da571e70..0e5009be07 100644 --- a/data/mods/gen6/learnsets.js +++ b/data/mods/gen6/learnsets.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedLearnsetData}} */ -let BattleLearnsets = { +export const BattleLearnsets: {[k: string]: ModdedLearnsetData} = { tomohawk: { inherit: true, learnset: { @@ -251,5 +248,3 @@ let BattleLearnsets = { }, }, }; - -exports.BattleLearnsets = BattleLearnsets; diff --git a/data/mods/gen6/moves.js b/data/mods/gen6/moves.ts similarity index 95% rename from data/mods/gen6/moves.js rename to data/mods/gen6/moves.ts index 1fc020dd5f..40169ef9cf 100644 --- a/data/mods/gen6/moves.js +++ b/data/mods/gen6/moves.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedMoveData}} */ -let BattleMovedex = { +export const BattleMovedex: {[k: string]: ModdedMoveData} = { allyswitch: { inherit: true, desc: "The user swaps positions with its ally on the opposite side of the field. Fails if there is no Pokemon at that position, if the user is the only Pokemon on its side, or if the user is in the middle.", @@ -81,9 +78,12 @@ let BattleMovedex = { effect: { duration: 3, onStart(target) { - let noEncore = ['encore', 'mimic', 'mirrormove', 'sketch', 'struggle', 'transform']; - let moveIndex = target.lastMove ? target.moves.indexOf(target.lastMove.id) : -1; - if (!target.lastMove || noEncore.includes(target.lastMove.id) || !target.moveSlots[moveIndex] || target.moveSlots[moveIndex].pp <= 0) { + const noEncore = ['encore', 'mimic', 'mirrormove', 'sketch', 'struggle', 'transform']; + const moveIndex = target.lastMove ? target.moves.indexOf(target.lastMove.id) : -1; + if ( + !target.lastMove || noEncore.includes(target.lastMove.id) || + !target.moveSlots[moveIndex] || target.moveSlots[moveIndex].pp <= 0 + ) { // it failed return false; } @@ -98,7 +98,9 @@ let BattleMovedex = { }, onResidualOrder: 13, onResidual(target) { - if (target.moves.includes(this.effectData.move) && target.moveSlots[target.moves.indexOf(this.effectData.move)].pp <= 0) { // early termination if you run out of PP + const lockedMoveIndex = target.moves.indexOf(this.effectData.move); + if (lockedMoveIndex >= 0 && target.moveSlots[lockedMoveIndex].pp <= 0) { + // Encore ends early if you run out of PP target.removeVolatile('encore'); } }, @@ -154,7 +156,7 @@ let BattleMovedex = { inherit: true, desc: "Causes the target's Ability to be rendered ineffective as long as it remains active. If the target uses Baton Pass, the replacement will remain under this effect. If the target's Ability is Multitype or Stance Change, this move fails, and receiving the effect through Baton Pass ends the effect immediately.", onTryHit(pokemon) { - let bannedAbilities = ['multitype', 'stancechange']; + const bannedAbilities = ['multitype', 'stancechange']; if (bannedAbilities.includes(pokemon.ability)) { return false; } @@ -228,12 +230,18 @@ let BattleMovedex = { effect: { noCopy: true, onSourceModifyDamage(damage, source, target, move) { - if (['stomp', 'steamroller', 'bodyslam', 'flyingpress', 'dragonrush', 'phantomforce', 'heatcrash', 'shadowforce'].includes(move.id)) { + const boostedMoves = [ + 'stomp', 'steamroller', 'bodyslam', 'flyingpress', 'dragonrush', 'phantomforce', 'heatcrash', 'shadowforce', + ]; + if (boostedMoves.includes(move.id)) { return this.chainModify(2); } }, onAccuracy(accuracy, target, source, move) { - if (['stomp', 'steamroller', 'bodyslam', 'flyingpress', 'dragonrush', 'phantomforce', 'heatcrash', 'shadowforce'].includes(move.id)) { + const boostedMoves = [ + 'stomp', 'steamroller', 'bodyslam', 'flyingpress', 'dragonrush', 'phantomforce', 'heatcrash', 'shadowforce', + ]; + if (boostedMoves.includes(move.id)) { return true; } return accuracy; @@ -250,7 +258,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('terrainextender')) { + if (source?.hasItem('terrainextender')) { return 8; } return 5; @@ -269,7 +277,7 @@ let BattleMovedex = { } }, onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { + if (effect?.effectType === 'Ability') { this.add('-fieldstart', 'move: Misty Terrain', '[from] ability: ' + effect, '[of] ' + source); } else { this.add('-fieldstart', 'move: Misty Terrain'); @@ -389,7 +397,7 @@ let BattleMovedex = { inherit: true, desc: "Causes the target's Ability to become Simple. Fails if the target's Ability is Multitype, Simple, Stance Change, or Truant.", onTryHit(pokemon) { - let bannedAbilities = ['multitype', 'simple', 'stancechange', 'truant']; + const bannedAbilities = ['multitype', 'simple', 'stancechange', 'truant']; if (bannedAbilities.includes(pokemon.ability)) { return false; } @@ -399,7 +407,7 @@ let BattleMovedex = { inherit: true, desc: "The user swaps its Ability with the target's Ability. Fails if either the user or the target's Ability is Illusion, Multitype, Stance Change, or Wonder Guard.", onTryHit(target, source) { - let bannedAbilities = ['illusion', 'multitype', 'stancechange', 'wonderguard']; + const bannedAbilities = ['illusion', 'multitype', 'stancechange', 'wonderguard']; if (bannedAbilities.includes(target.ability) || bannedAbilities.includes(source.ability)) { return false; } @@ -433,7 +441,7 @@ let BattleMovedex = { this.boost({def: 1, spd: 1}, target, target); }, onEnd(target) { - let layers = this.effectData.layers * -1; + const layers = this.effectData.layers * -1; this.effectData.layers = 0; this.boost({def: layers, spd: layers}, target, target); this.add('-end', target, 'Stockpile'); @@ -521,11 +529,14 @@ let BattleMovedex = { onTryHitPriority: 4, onTryHit(target, source, effect) { // Wide Guard blocks damaging spread moves - if (effect && (effect.category === 'Status' || (effect.target !== 'allAdjacent' && effect.target !== 'allAdjacentFoes'))) { + if ( + effect && + (effect.category === 'Status' || (effect.target !== 'allAdjacent' && effect.target !== 'allAdjacentFoes')) + ) { return; } this.add('-activate', target, 'move: Wide Guard'); - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -540,12 +551,10 @@ let BattleMovedex = { inherit: true, desc: "Causes the target's Ability to become Insomnia. Fails if the target's Ability is Insomnia, Multitype, Stance Change, or Truant.", onTryHit(pokemon) { - let bannedAbilities = ['insomnia', 'multitype', 'stancechange', 'truant']; + const bannedAbilities = ['insomnia', 'multitype', 'stancechange', 'truant']; if (bannedAbilities.includes(pokemon.ability)) { return false; } }, }, }; - -exports.BattleMovedex = BattleMovedex; diff --git a/data/mods/gen6/pokedex.js b/data/mods/gen6/pokedex.ts similarity index 98% rename from data/mods/gen6/pokedex.js rename to data/mods/gen6/pokedex.ts index f4eb86b53f..b2781cf4b9 100644 --- a/data/mods/gen6/pokedex.js +++ b/data/mods/gen6/pokedex.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesData}} */ -let BattlePokedex = { +export const BattlePokedex: {[k: string]: ModdedSpeciesData} = { charizardmegax: { inherit: true, color: "Red", @@ -370,5 +367,3 @@ let BattlePokedex = { abilities: {0: "Volt Absorb", 1: "Lightning Rod"}, }, }; - -exports.BattlePokedex = BattlePokedex; diff --git a/data/mods/gen6/random-teams.js b/data/mods/gen6/random-teams.ts similarity index 86% rename from data/mods/gen6/random-teams.js rename to data/mods/gen6/random-teams.ts index dc6761b1db..4a0af09792 100644 --- a/data/mods/gen6/random-teams.js +++ b/data/mods/gen6/random-teams.ts @@ -1,46 +1,34 @@ -'use strict'; +/* eslint max-len: ["error", 240] */ -const RandomGen7Teams = require('../gen7/random-teams'); +import {TeamData} from '../../random-teams'; +import RandomGen7Teams from '../gen7/random-teams'; +import {PRNG, PRNGSeed} from '../../../sim/prng'; -class RandomGen6Teams extends RandomGen7Teams { - /** - * @param {Format | string} format - * @param {?PRNG | [number, number, number, number]} [prng] - */ - constructor(format, prng) { +export class RandomGen6Teams extends RandomGen7Teams { + constructor(format: Format | string, prng: PRNG | PRNGSeed | null) { super(format, prng); - /**@type {AnyObject} */ - // @ts-ignore this.randomFactorySets = require('./factory-sets.json'); } - /** - * @param {string | Species} species - * @param {RandomTeamsTypes.TeamDetails} [teamDetails] - * @param {boolean} [isLead] - * @return {RandomTeamsTypes.RandomSet} - */ - randomSet(species, teamDetails = {}, isLead = false) { - let baseSpecies = (species = this.dex.getSpecies(species)); + randomSet(species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}, isLead = false): RandomTeamsTypes.RandomSet { + const baseSpecies = (species = this.dex.getSpecies(species)); let forme = species.name; - if (species.battleOnly) { + if (species.battleOnly && typeof species.battleOnly === 'string') { // Only change the forme. The species has custom moves, and may have different typing and requirements. - forme = /** @type {string} */ (species.battleOnly); + forme = species.battleOnly; } - let battleForme = this.checkBattleForme(species); - if (battleForme && battleForme.randomBattleMoves && species.otherFormes && (battleForme.isMega ? !teamDetails.megaStone : this.random(2))) { + const battleForme = this.checkBattleForme(species); + if (battleForme?.randomBattleMoves && species.otherFormes && (battleForme?.isMega ? !teamDetails.megaStone : this.random(2))) { species = this.dex.getSpecies(species.otherFormes.length >= 2 ? this.sample(species.otherFormes) : species.otherFormes[0]); } - // @ts-ignore - let movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id].learnset)).slice(); - let rejectedPool = []; - /**@type {string[]} */ - let moves = []; + const movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id]!.learnset!)).slice(); + const rejectedPool = []; + const moves: string[] = []; let ability = ''; let item = ''; - let evs = { + const evs = { hp: 85, atk: 85, def: 85, @@ -56,14 +44,12 @@ class RandomGen6Teams extends RandomGen7Teams { spd: 31, spe: 31, }; - /**@type {{[k: string]: true}} */ - let hasType = {}; + const hasType: {[k: string]: true} = {}; hasType[species.types[0]] = true; if (species.types[1]) { hasType[species.types[1]] = true; } - /**@type {{[k: string]: true}} */ - let hasAbility = {}; + const hasAbility: {[k: string]: true} = {}; hasAbility[species.abilities[0]] = true; if (species.abilities[1]) { hasAbility[species.abilities[1]] = true; @@ -77,13 +63,12 @@ class RandomGen6Teams extends RandomGen7Teams { } // These moves can be used even if we aren't setting up to use them: - let SetupException = ['closecombat', 'diamondstorm', 'extremespeed', 'suckerpunch', 'superpower', 'dracometeor']; + const SetupException = ['closecombat', 'diamondstorm', 'extremespeed', 'suckerpunch', 'superpower', 'dracometeor']; - let counterAbilities = ['Adaptability', 'Contrary', 'Iron Fist', 'Skill Link', 'Strong Jaw']; - let ateAbilities = ['Aerilate', 'Pixilate', 'Refrigerate']; + const counterAbilities = ['Adaptability', 'Contrary', 'Iron Fist', 'Skill Link', 'Strong Jaw']; + const ateAbilities = ['Aerilate', 'Pixilate', 'Refrigerate']; - /**@type {{[k: string]: boolean}} */ - let hasMove = {}; + let hasMove: {[k: string]: boolean} = {}; let counter; do { @@ -99,7 +84,7 @@ class RandomGen6Teams extends RandomGen7Teams { // Choose next 4 moves from learnset/viable moves and add them to moves list: while (moves.length < 4 && movePool.length) { - let moveid = this.sampleNoReplace(movePool); + const moveid = this.sampleNoReplace(movePool); if (moveid.substr(0, 11) === 'hiddenpower') { availableHP--; if (hasMove['hiddenpower']) continue; @@ -110,7 +95,7 @@ class RandomGen6Teams extends RandomGen7Teams { moves.push(moveid); } while (moves.length < 4 && rejectedPool.length) { - let moveid = this.sampleNoReplace(rejectedPool); + const moveid = this.sampleNoReplace(rejectedPool); hasMove[moveid] = true; moves.push(moveid); } @@ -119,8 +104,8 @@ class RandomGen6Teams extends RandomGen7Teams { // Iterate through the moves again, this time to cull them: for (const [i, setMoveid] of moves.entries()) { - let move = this.dex.getMove(setMoveid); - let moveid = move.id; + const move = this.dex.getMove(setMoveid); + const moveid = move.id; let rejected = false; let isSetup = false; @@ -138,7 +123,7 @@ class RandomGen6Teams extends RandomGen7Teams { case 'reflect': if (!hasMove['calmmind'] && !hasMove['lightscreen']) rejected = true; if (movePool.length > 1) { - let screen = movePool.indexOf('lightscreen'); + const screen = movePool.indexOf('lightscreen'); if (screen >= 0) this.fastPop(movePool, screen); } break; @@ -148,7 +133,7 @@ class RandomGen6Teams extends RandomGen7Teams { case 'sleeptalk': if (!hasMove['rest']) rejected = true; if (movePool.length > 1) { - let rest = movePool.indexOf('rest'); + const rest = movePool.indexOf('rest'); if (rest >= 0) this.fastPop(movePool, rest); } break; @@ -406,7 +391,7 @@ class RandomGen6Teams extends RandomGen7Teams { if ((hasMove['psychic'] || hasMove['psyshock']) && counter.setupType !== 'Physical') rejected = true; break; case 'psyshock': - let psychic = movePool.indexOf('psychic'); + const psychic = movePool.indexOf('psychic'); if (psychic >= 0) this.fastPop(movePool, psychic); break; case 'headsmash': @@ -455,10 +440,10 @@ class RandomGen6Teams extends RandomGen7Teams { if (counter.Physical + counter.Special < 2 || hasMove['rest'] && hasMove['sleeptalk']) rejected = true; if (!hasAbility['Chlorophyll'] && !hasAbility['Flower Gift'] && !hasMove['solarbeam']) rejected = true; if (rejected && movePool.length > 1) { - let solarbeam = movePool.indexOf('solarbeam'); + const solarbeam = movePool.indexOf('solarbeam'); if (solarbeam >= 0) this.fastPop(movePool, solarbeam); if (movePool.length > 1) { - let weatherball = movePool.indexOf('weatherball'); + const weatherball = movePool.indexOf('weatherball'); if (weatherball >= 0) this.fastPop(movePool, weatherball); } } @@ -480,15 +465,19 @@ class RandomGen6Teams extends RandomGen7Teams { if ((move.category === 'Physical' && counter.setupType === 'Special') || (move.category === 'Special' && counter.setupType === 'Physical')) { // Reject STABs last in case the setup type changes later on // @ts-ignore - let stabs = counter[species.types[0]] + (counter[species.types[1]] || 0); + const stabs = counter[species.types[0]] + (counter[species.types[1]] || 0); if (!SetupException.includes(moveid) && (!hasType[move.type] || stabs > 1 || counter[move.category] < 2)) rejected = true; } - // @ts-ignore - if (counter.setupType && !isSetup && counter.setupType !== 'Mixed' && move.category !== counter.setupType && counter[counter.setupType] < 2 && (move.category !== 'Status' || !move.flags.heal) && moveid !== 'sleeptalk' && !hasType['Dark'] && !hasMove['darkpulse']) { + if ( + counter.setupType && !isSetup && counter.setupType !== 'Mixed' && move.category !== counter.setupType && + counter[counter.setupType] < 2 && (move.category !== 'Status' || !move.flags.heal) && + moveid !== 'sleeptalk' && !hasType['Dark'] && !hasMove['darkpulse'] + ) { // Mono-attacking with setup and RestTalk is allowed // Reject Status moves only if there is nothing else to reject - // @ts-ignore - if (move.category !== 'Status' || counter[counter.setupType] + counter.Status > 3 && counter['physicalsetup'] + counter['specialsetup'] < 2) rejected = true; + if (move.category !== 'Status' || counter[counter.setupType] + counter.Status > 3 && counter['physicalsetup'] + counter['specialsetup'] < 2) { + rejected = true; + } } if (counter.setupType === 'Special' && moveid === 'hiddenpower' && species.types.length > 1 && counter['Special'] <= 2 && !hasType[move.type] && !counter['Physical'] && counter['specialpool']) { // Hidden Power isn't good enough @@ -496,9 +485,13 @@ class RandomGen6Teams extends RandomGen7Teams { } // Pokemon should have moves that benefit their Type/Ability/Weather, as well as moves required by its forme - // @ts-ignore - if (!rejected && (counter['physicalsetup'] + counter['specialsetup'] < 2 && (!counter.setupType || counter.setupType === 'Mixed' || (move.category !== counter.setupType && move.category !== 'Status') || counter[counter.setupType] + counter.Status > 3)) && - ((!counter.stab && !hasMove['nightshade'] && !hasMove['seismictoss'] && (species.types.length > 1 || (species.types[0] !== 'Normal' && species.types[0] !== 'Psychic') || !hasMove['icebeam'] || species.baseStats.spa >= species.baseStats.spd)) || + if (!rejected && ( + counter['physicalsetup'] + counter['specialsetup'] < 2 && + (!counter.setupType || counter.setupType === 'Mixed' || (move.category !== counter.setupType && move.category !== 'Status') || counter[counter.setupType] + counter.Status > 3) + ) && ( + (!counter.stab && !hasMove['nightshade'] && !hasMove['seismictoss'] && ( + species.types.length > 1 || (species.types[0] !== 'Normal' && species.types[0] !== 'Psychic') || !hasMove['icebeam'] || species.baseStats.spa >= species.baseStats.spd + )) || (hasType['Bug'] && (movePool.includes('megahorn') || movePool.includes('pinmissile'))) || ((hasType['Dark'] && !counter['Dark'] && !hasAbility['Protean']) || hasMove['suckerpunch'] && counter.stab < species.types.length) || (hasType['Dragon'] && !counter['Dragon'] && !hasAbility['Aerilate'] && !hasAbility['Pixilate'] && !hasMove['rest'] && !hasMove['sleeptalk']) || @@ -522,9 +515,12 @@ class RandomGen6Teams extends RandomGen7Teams { (hasAbility['Bad Dreams'] && movePool.includes('darkvoid')) || (hasAbility['Contrary'] && !counter['contrary'] && species.name !== 'Shuckle') || (hasAbility['Slow Start'] && movePool.includes('substitute')) || - (!counter.recovery && !counter.setupType && !hasMove['healingwish'] && (movePool.includes('recover') || movePool.includes('roost') || movePool.includes('softboiled')) && (counter.Status > 1 || (species.nfe && !!counter['Status']))) || + (!counter.recovery && !counter.setupType && !hasMove['healingwish'] && ( + movePool.includes('recover') || movePool.includes('roost') || movePool.includes('softboiled') + ) && (counter.Status > 1 || (species.nfe && !!counter['Status']))) || (movePool.includes('stickyweb') && !counter.setupType && !teamDetails.stickyWeb) || - (species.requiredMove && movePool.includes(toID(species.requiredMove)))))) { + (species.requiredMove && movePool.includes(toID(species.requiredMove)))) + )) { // Reject Status or non-STAB if (!isSetup && !move.weather && !move.damage && (move.category !== 'Status' || !move.flags.heal) && moveid !== 'judgment' && moveid !== 'sleeptalk') { if (move.category === 'Status' || !hasType[move.type] || move.selfSwitch || move.basePower && move.basePower < 40 && !move.multihit) rejected = true; @@ -533,7 +529,7 @@ class RandomGen6Teams extends RandomGen7Teams { // Sleep Talk shouldn't be selected without Rest if (moveid === 'rest' && rejected) { - let sleeptalk = movePool.indexOf('sleeptalk'); + const sleeptalk = movePool.indexOf('sleeptalk'); if (sleeptalk >= 0) { if (movePool.length < 2) { rejected = false; @@ -556,8 +552,8 @@ class RandomGen6Teams extends RandomGen7Teams { // Handle Hidden Power IVs if (moveid === 'hiddenpower') { - let HPivs = this.dex.getType(move.type).HPivs; - for (let iv in HPivs) { + const HPivs = this.dex.getType(move.type).HPivs; + for (const iv in HPivs) { // @ts-ignore ivs[iv] = HPivs[iv]; } @@ -578,7 +574,7 @@ class RandomGen6Teams extends RandomGen7Teams { ivs = {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31}; } - let abilities = Object.values(baseSpecies.abilities); + const abilities = Object.values(baseSpecies.abilities); abilities.sort((a, b) => this.dex.getAbility(b).rating - this.dex.getAbility(a).rating); let ability0 = this.dex.getAbility(abilities[0]); let ability1 = this.dex.getAbility(abilities[1]); @@ -594,7 +590,7 @@ class RandomGen6Teams extends RandomGen7Teams { } ability = ability0.name; - let rejectAbility; + let rejectAbility: boolean; do { rejectAbility = false; if (counterAbilities.includes(ability)) { @@ -644,9 +640,9 @@ class RandomGen6Teams extends RandomGen7Teams { } else if (ability === 'Prankster') { rejectAbility = !counter['Status']; } else if (ability === 'Pressure' || ability === 'Synchronize') { - rejectAbility = counter.Status < 2 || !!counter['recoil'] || species.isMega; + rejectAbility = counter.Status < 2 || !!counter['recoil'] || !!species.isMega; } else if (ability === 'Reckless' || ability === 'Rock Head') { - rejectAbility = !counter['recoil'] || species.isMega; + rejectAbility = !counter['recoil'] || !!species.isMega; } else if (ability === 'Regenerator') { rejectAbility = abilities.includes('Magic Guard'); } else if (ability === 'Sand Force' || ability === 'Sand Rush' || ability === 'Sand Veil') { @@ -656,27 +652,27 @@ class RandomGen6Teams extends RandomGen7Teams { } else if (ability === 'Serene Grace') { rejectAbility = !counter['serenegrace'] || species.name === 'Blissey'; } else if (ability === 'Sheer Force') { - rejectAbility = !counter['sheerforce'] || abilities.includes('Guts') || hasMove['doubleedge'] || species.isMega; + rejectAbility = !counter['sheerforce'] || abilities.includes('Guts') || hasMove['doubleedge'] || !!species.isMega; } else if (ability === 'Simple') { rejectAbility = !counter.setupType && !hasMove['flamecharge']; } else if (ability === 'Solar Power') { - rejectAbility = !counter['Special'] || !teamDetails['sun'] || species.isMega; + rejectAbility = !counter['Special'] || !teamDetails['sun'] || !!species.isMega; } else if (ability === 'Speed Boost') { rejectAbility = hasMove['uturn']; } else if (ability === 'Swarm') { - rejectAbility = !counter['Bug'] || species.isMega; + rejectAbility = !counter['Bug'] || !!species.isMega; } else if (ability === 'Sweet Veil') { rejectAbility = hasType['Grass']; } else if (ability === 'Technician') { - rejectAbility = !counter['technician'] || hasMove['tailslap'] || species.isMega; + rejectAbility = !counter['technician'] || hasMove['tailslap'] || !!species.isMega; } else if (ability === 'Tinted Lens') { rejectAbility = abilities.includes('Prankster') || hasMove['protect'] || counter['damage'] >= counter.damagingMoves.length || (counter.Status > 2 && !counter.setupType); } else if (ability === 'Torrent') { - rejectAbility = !counter['Water'] || species.isMega; + rejectAbility = !counter['Water'] || !!species.isMega; } else if (ability === 'Unaware') { rejectAbility = hasMove['stealthrock']; } else if (ability === 'Unburden') { - rejectAbility = abilities.includes('Prankster') || (!counter.setupType && !hasMove['acrobatics']) || species.isMega; + rejectAbility = abilities.includes('Prankster') || (!counter.setupType && !hasMove['acrobatics']) || !!species.isMega; } else if (ability === 'Water Absorb') { rejectAbility = abilities.includes('Volt Absorb') || hasMove['raindance']; } else if (ability === 'Weak Armor') { @@ -774,8 +770,8 @@ class RandomGen6Teams extends RandomGen7Teams { item = 'Sitrus Berry'; } else { item = 'Red Card'; - for (let m in moves) { - let move = this.dex.getMove(moves[m]); + for (const moveid of moves) { + const move = this.dex.getMove(moveid); if (hasType[move.type] && move.basePower >= 90) { item = move.type + ' Gem'; break; @@ -845,25 +841,23 @@ class RandomGen6Teams extends RandomGen7Teams { item = 'Black Sludge'; } - /** @type {{[tier: string]: number}} */ - const levelScale = { + const levelScale: {[k: string]: number} = { uber: 78, ou: 80, uu: 82, ru: 84, nu: 86, pu: 88, }; - /** @type {{[forme: string]: number}} */ - const customScale = { + const customScale: {[k: string]: number} = { // Banned Ability Dugtrio: 82, Gothitelle: 82, Ninetales: 84, Politoed: 84, Wobbuffet: 82, // Holistic judgement Castform: 100, Delibird: 100, 'Genesect-Douse': 80, Spinda: 100, Unown: 100, }; - let tier = toID(species.tier).replace('bl', ''); + const tier = toID(species.tier).replace('bl', ''); let level = levelScale[tier] || (species.nfe ? 90 : 80); if (customScale[forme]) level = customScale[forme]; // Prepare optimal HP - let srWeakness = this.dex.getEffectiveness('Rock', species); + const srWeakness = this.dex.getEffectiveness('Rock', species); while (evs.hp > 1) { - let hp = Math.floor(Math.floor(2 * species.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10); + const hp = Math.floor(Math.floor(2 * species.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10); if (hasMove['substitute'] && hasMove['reversal']) { // Reversal users should be able to use four Substitutes if (hp % 4 > 0) break; @@ -906,53 +900,41 @@ class RandomGen6Teams extends RandomGen7Teams { }; } - /** - * @param {Species} species - * @param {number} slot - * @param {RandomTeamsTypes.FactoryTeamDetails} teamData - * @param {string} tier - * @return {RandomTeamsTypes.RandomFactorySet | false} - */ - randomFactorySet(species, slot, teamData, tier) { - let id = toID(species.name); - // let flags = this.randomFactorySets[tier][id].flags; - let setList = this.randomFactorySets[tier][id].sets; + randomFactorySet(species: Species, teamData: RandomTeamsTypes.FactoryTeamDetails, tier: string): RandomTeamsTypes.RandomFactorySet | null { + const id = toID(species.name); + // const flags = this.randomFactorySets[tier][id].flags; + const setList = this.randomFactorySets[tier][id].sets; - /**@type {{[k: string]: number}} */ - let itemsMax = {choicespecs: 1, choiceband: 1, choicescarf: 1}; - /**@type {{[k: string]: number}} */ - let movesMax = {rapidspin: 1, batonpass: 1, stealthrock: 1, defog: 1, spikes: 1, toxicspikes: 1}; - /**@type {{[k: string]: string}} */ - let requiredMoves = {stealthrock: 'hazardSet', rapidspin: 'hazardClear', defog: 'hazardClear'}; - /**@type {{[k: string]: string}} */ - let weatherAbilitiesRequire = { + const itemsMax: {[K: string]: number} = {choicespecs: 1, choiceband: 1, choicescarf: 1}; + const movesMax: {[k: string]: number} = {rapidspin: 1, batonpass: 1, stealthrock: 1, defog: 1, spikes: 1, toxicspikes: 1}; + const requiredMoves: {[k: string]: string} = {stealthrock: 'hazardSet', rapidspin: 'hazardClear', defog: 'hazardClear'}; + const weatherAbilitiesRequire: {[k: string]: string} = { hydration: 'raindance', swiftswim: 'raindance', leafguard: 'sunnyday', solarpower: 'sunnyday', chlorophyll: 'sunnyday', sandforce: 'sandstorm', sandrush: 'sandstorm', sandveil: 'sandstorm', snowcloak: 'hail', }; - let weatherAbilities = ['drizzle', 'drought', 'snowwarning', 'sandstream']; + const weatherAbilities = ['drizzle', 'drought', 'snowwarning', 'sandstream']; // Build a pool of eligible sets, given the team partners // Also keep track of sets with moves the team requires - /**@type {{set: AnyObject, moveVariants?: number[], itemVariants?: number, abilityVariants?: number}[]} */ - let effectivePool = []; - let priorityPool = []; + let effectivePool: {set: AnyObject, moveVariants?: number[], itemVariants?: number, abilityVariants?: number}[] = []; + const priorityPool = []; for (const curSet of setList) { - let itemData = this.dex.getItem(curSet.item); + const itemData = this.dex.getItem(curSet.item); if (teamData.megaCount > 0 && itemData.megaStone) continue; // reject 2+ mega stones if (itemsMax[itemData.id] && teamData.has[itemData.id] >= itemsMax[itemData.id]) continue; - let abilityData = this.dex.getAbility(curSet.ability); + const abilityData = this.dex.getAbility(curSet.ability); if (weatherAbilitiesRequire[abilityData.id] && teamData.weather !== weatherAbilitiesRequire[abilityData.id]) continue; if (teamData.weather && weatherAbilities.includes(abilityData.id)) continue; // reject 2+ weather setters let reject = false; let hasRequiredMove = false; - let curSetVariants = []; + const curSetVariants = []; for (const move of curSet.moves) { - let variantIndex = this.random(move.length); - let moveId = toID(move[variantIndex]); + const variantIndex = this.random(move.length); + const moveId = toID(move[variantIndex]); if (movesMax[moveId] && teamData.has[moveId] >= movesMax[moveId]) { reject = true; break; @@ -969,14 +951,14 @@ class RandomGen6Teams extends RandomGen7Teams { if (priorityPool.length) effectivePool = priorityPool; if (!effectivePool.length) { - if (!teamData.forceResult) return false; + if (!teamData.forceResult) return null; for (const curSet of setList) { effectivePool.push({set: curSet}); } } - let setData = this.sample(effectivePool); - let moves = []; + const setData = this.sample(effectivePool); + const moves = []; for (const [i, moveSlot] of setData.set.moves.entries()) { moves.push(setData.moveVariants ? moveSlot[setData.moveVariants[i]] : this.sample(moveSlot)); } @@ -997,32 +979,23 @@ class RandomGen6Teams extends RandomGen7Teams { }; } - /** - * @param {PlayerOptions} [side] - * @param {number} [depth] - * @return {RandomTeamsTypes.RandomFactorySet[]} - */ - randomFactoryTeam(side, depth = 0) { - let forceResult = (depth >= 4); + randomFactoryTeam(side: PlayerOptions, depth = 0): RandomTeamsTypes.RandomFactorySet[] { + const forceResult = (depth >= 4); // The teams generated depend on the tier choice in such a way that // no exploitable information is leaked from rolling the tier in getTeam(p1). - if (!this.FactoryTier) this.FactoryTier = this.sample(['Uber', 'OU', 'UU', 'RU', 'NU', 'PU']); - const chosenTier = this.FactoryTier; + if (!this.factoryTier) this.factoryTier = this.sample(['Uber', 'OU', 'UU', 'RU', 'NU', 'PU']); + const chosenTier = this.factoryTier; - let pokemon = []; + const pokemon = []; - let pokemonPool = Object.keys(this.randomFactorySets[chosenTier]); + const pokemonPool = Object.keys(this.randomFactorySets[chosenTier]); - /**@type {import('../../random-teams').TeamData} */ - let teamData = {typeCount: {}, typeComboCount: {}, baseFormes: {}, megaCount: 0, has: {}, forceResult: forceResult, weaknesses: {}, resistances: {}}; - let requiredMoveFamilies = ['hazardSet', 'hazardClear']; - /**@type {{[k: string]: string}} */ - let requiredMoves = {stealthrock: 'hazardSet', rapidspin: 'hazardClear', defog: 'hazardClear'}; - /**@type {{[k: string]: string}} */ - let weatherAbilitiesSet = {drizzle: 'raindance', drought: 'sunnyday', snowwarning: 'hail', sandstream: 'sandstorm'}; - /**@type {{[k: string]: string[]}} */ - let resistanceAbilities = { + const teamData: TeamData = {typeCount: {}, typeComboCount: {}, baseFormes: {}, megaCount: 0, has: {}, forceResult: forceResult, weaknesses: {}, resistances: {}}; + const requiredMoveFamilies = ['hazardSet', 'hazardClear']; + const requiredMoves: {[k: string]: string} = {stealthrock: 'hazardSet', rapidspin: 'hazardClear', defog: 'hazardClear'}; + const weatherAbilitiesSet: {[k: string]: string} = {drizzle: 'raindance', drought: 'sunnyday', snowwarning: 'hail', sandstream: 'sandstorm'}; + const resistanceAbilities: {[k: string]: string[]} = { dryskin: ['Water'], waterabsorb: ['Water'], stormdrain: ['Water'], flashfire: ['Fire'], heatproof: ['Fire'], lightningrod: ['Electric'], motordrive: ['Electric'], voltabsorb: ['Electric'], @@ -1032,10 +1005,10 @@ class RandomGen6Teams extends RandomGen7Teams { }; while (pokemonPool.length && pokemon.length < 6) { - let species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); + const species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); if (!species.exists) continue; - let speciesFlags = this.randomFactorySets[chosenTier][species.id].flags; + const speciesFlags = this.randomFactorySets[chosenTier][species.id].flags; // Limit to one of each species (Species Clause) if (teamData.baseFormes[species.baseSpecies]) continue; @@ -1044,7 +1017,7 @@ class RandomGen6Teams extends RandomGen7Teams { if (teamData.megaCount >= 1 && speciesFlags.megaOnly) continue; // Limit 2 of any type - let types = species.types; + const types = species.types; let skip = false; for (const type of types) { if (teamData.typeCount[type] > 1 && this.randomChance(4, 5)) { @@ -1054,7 +1027,7 @@ class RandomGen6Teams extends RandomGen7Teams { } if (skip) continue; - let set = this.randomFactorySet(species, pokemon.length, teamData, chosenTier); + const set = this.randomFactorySet(species, teamData, chosenTier); if (!set) continue; // Limit 1 of any type combination @@ -1080,7 +1053,7 @@ class RandomGen6Teams extends RandomGen7Teams { teamData.baseFormes[species.baseSpecies] = 1; - let itemData = this.dex.getItem(set.item); + const itemData = this.dex.getItem(set.item); if (itemData.megaStone) teamData.megaCount++; if (itemData.id in teamData.has) { teamData.has[itemData.id]++; @@ -1088,13 +1061,13 @@ class RandomGen6Teams extends RandomGen7Teams { teamData.has[itemData.id] = 1; } - let abilityData = this.dex.getAbility(set.ability); + const abilityData = this.dex.getAbility(set.ability); if (abilityData.id in weatherAbilitiesSet) { teamData.weather = weatherAbilitiesSet[abilityData.id]; } for (const move of set.moves) { - let moveId = toID(move); + const moveId = toID(move); if (moveId in teamData.has) { teamData.has[moveId]++; } else { @@ -1105,7 +1078,7 @@ class RandomGen6Teams extends RandomGen7Teams { } } - for (let typeName in this.dex.data.TypeChart) { + for (const typeName in this.dex.data.TypeChart) { // Cover any major weakness (3+) with at least one resistance if (teamData.resistances[typeName] >= 1) continue; if (resistanceAbilities[abilityData.id] && resistanceAbilities[abilityData.id].includes(typeName) || !this.dex.getImmunity(typeName, types)) { @@ -1114,7 +1087,7 @@ class RandomGen6Teams extends RandomGen7Teams { if (teamData.resistances[typeName] >= 1) teamData.weaknesses[typeName] = 0; continue; } - let typeMod = this.dex.getEffectiveness(typeName, types); + const typeMod = this.dex.getEffectiveness(typeName, types); if (typeMod < 0) { teamData.resistances[typeName] = (teamData.resistances[typeName] || 0) + 1; if (teamData.resistances[typeName] >= 1) teamData.weaknesses[typeName] = 0; @@ -1130,7 +1103,7 @@ class RandomGen6Teams extends RandomGen7Teams { for (const requiredFamily of requiredMoveFamilies) { if (!teamData.has[requiredFamily]) return this.randomFactoryTeam(side, ++depth); } - for (let type in teamData.weaknesses) { + for (const type in teamData.weaknesses) { if (teamData.weaknesses[type] >= 3) return this.randomFactoryTeam(side, ++depth); } } @@ -1139,4 +1112,4 @@ class RandomGen6Teams extends RandomGen7Teams { } } -module.exports = RandomGen6Teams; +export default RandomGen6Teams; diff --git a/data/mods/gen6/scripts.js b/data/mods/gen6/scripts.js deleted file mode 100644 index 02d68b1aef..0000000000 --- a/data/mods/gen6/scripts.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -/** @type {ModdedBattleScriptsData} */ -let BattleScripts = { - inherit: 'gen7', - gen: 6, -}; - -exports.BattleScripts = BattleScripts; diff --git a/data/mods/gen6/scripts.ts b/data/mods/gen6/scripts.ts new file mode 100644 index 0000000000..50bd850e02 --- /dev/null +++ b/data/mods/gen6/scripts.ts @@ -0,0 +1,4 @@ +export const BattleScripts: ModdedBattleScriptsData = { + inherit: 'gen7', + gen: 6, +}; diff --git a/data/mods/gen6/statuses.js b/data/mods/gen6/statuses.ts similarity index 74% rename from data/mods/gen6/statuses.js rename to data/mods/gen6/statuses.ts index 3a351d763d..a6fd0c19db 100644 --- a/data/mods/gen6/statuses.js +++ b/data/mods/gen6/statuses.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedPureEffectData}} */ -let BattleStatuses = { +export const BattleStatuses: {[k: string]: ModdedPureEffectData} = { brn: { inherit: true, onResidual(pokemon) { @@ -28,13 +25,13 @@ let BattleStatuses = { if (this.randomChance(1, 2)) { return; } - let damage = this.getDamage(pokemon, pokemon, 40); + const damage = this.getDamage(pokemon, pokemon, 40); if (typeof damage !== 'number') throw new Error("Confusion damage not dealt"); - this.damage(damage, pokemon, pokemon, /** @type {ActiveMove} */ ({ + this.damage(damage, pokemon, pokemon, { id: 'confused', effectType: 'Move', type: '???', - })); + } as ActiveMove); return false; }, }, @@ -43,5 +40,3 @@ let BattleStatuses = { onBeforeMove() {}, }, }; - -exports.BattleStatuses = BattleStatuses; diff --git a/data/mods/gen6/typechart.js b/data/mods/gen6/typechart.ts similarity index 69% rename from data/mods/gen6/typechart.js rename to data/mods/gen6/typechart.ts index 5820b372b4..016d6b08e0 100644 --- a/data/mods/gen6/typechart.js +++ b/data/mods/gen6/typechart.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedTypeData}} */ -let BattleTypeChart = { +export const BattleTypeChart: {[k: string]: ModdedTypeData} = { Dark: { inherit: true, damageTaken: { @@ -26,5 +23,3 @@ let BattleTypeChart = { }, }, }; - -exports.BattleTypeChart = BattleTypeChart; diff --git a/data/mods/gen7/abilities.js b/data/mods/gen7/abilities.ts similarity index 89% rename from data/mods/gen7/abilities.js rename to data/mods/gen7/abilities.ts index b5d5906a89..cbef858777 100644 --- a/data/mods/gen7/abilities.js +++ b/data/mods/gen7/abilities.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedAbilityData}} */ -let BattleAbilities = { +export const BattleAbilities: {[k: string]: ModdedAbilityData} = { chlorophyll: { inherit: true, desc: "If Sunny Day is active, this Pokemon's Speed is doubled.", @@ -12,7 +9,7 @@ let BattleAbilities = { shortDesc: "(Mimikyu only) First hit deals 0 damage, breaks disguise.", onUpdate(pokemon) { if (['mimikyu', 'mimikyutotem'].includes(pokemon.species.id) && this.effectData.busted) { - let speciesid = pokemon.species.id === 'mimikyutotem' ? 'Mimikyu-Busted-Totem' : 'Mimikyu-Busted'; + const speciesid = pokemon.species.id === 'mimikyutotem' ? 'Mimikyu-Busted-Totem' : 'Mimikyu-Busted'; pokemon.formeChange(speciesid, this.effect, true); } }, @@ -67,27 +64,25 @@ let BattleAbilities = { desc: "This Pokemon has a random stat raised by 2 stages and another stat lowered by 1 stage at the end of each turn.", shortDesc: "Raises a random stat by 2 and lowers another stat by 1 at the end of each turn.", onResidual(pokemon) { - let stats = []; - let boost = {}; - for (let statPlus in pokemon.boosts) { - // @ts-ignore + let stats: BoostName[] = []; + const boost: SparseBoostsTable = {}; + let statPlus: BoostName; + for (statPlus in pokemon.boosts) { if (pokemon.boosts[statPlus] < 6) { stats.push(statPlus); } } - let randomStat = stats.length ? this.sample(stats) : ""; - // @ts-ignore + let randomStat = stats.length ? this.sample(stats) : undefined; if (randomStat) boost[randomStat] = 2; stats = []; - for (let statMinus in pokemon.boosts) { - // @ts-ignore + let statMinus: BoostName; + for (statMinus in pokemon.boosts) { if (pokemon.boosts[statMinus] > -6 && statMinus !== randomStat) { stats.push(statMinus); } } - randomStat = stats.length ? this.sample(stats) : ""; - // @ts-ignore + randomStat = stats.length ? this.sample(stats) : undefined; if (randomStat) boost[randomStat] = -1; this.boost(boost); @@ -138,5 +133,3 @@ let BattleAbilities = { onBasePowerPriority: 19, }, }; - -exports.BattleAbilities = BattleAbilities; diff --git a/data/mods/gen7/formats-data.js b/data/mods/gen7/formats-data.ts similarity index 99% rename from data/mods/gen7/formats-data.js rename to data/mods/gen7/formats-data.ts index d305ed7dd6..0bc0b08c98 100644 --- a/data/mods/gen7/formats-data.js +++ b/data/mods/gen7/formats-data.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesFormatsData}} */ -let BattleFormatsData = { +export const BattleFormatsData: {[k: string]: ModdedSpeciesFormatsData} = { bulbasaur: { tier: "LC", }, @@ -4664,5 +4661,3 @@ let BattleFormatsData = { tier: "Illegal", }, }; - -exports.BattleFormatsData = BattleFormatsData; diff --git a/data/mods/gen7/items.js b/data/mods/gen7/items.ts old mode 100755 new mode 100644 similarity index 99% rename from data/mods/gen7/items.js rename to data/mods/gen7/items.ts index 4e0a2ceec7..7173590259 --- a/data/mods/gen7/items.js +++ b/data/mods/gen7/items.ts @@ -1,6 +1,4 @@ -'use strict'; -/**@type {{[k: string]: ModdedItemData}} */ -let BattleItems = { +export const BattleItems: {[k: string]: ModdedItemData} = { abomasite: { inherit: true, isNonstandard: null, @@ -702,4 +700,3 @@ let BattleItems = { isNonstandard: null, }, }; -exports.BattleItems = BattleItems; diff --git a/data/mods/gen7/moves.js b/data/mods/gen7/moves.ts similarity index 95% rename from data/mods/gen7/moves.js rename to data/mods/gen7/moves.ts index 065cbaf7a0..f83828bfe7 100644 --- a/data/mods/gen7/moves.js +++ b/data/mods/gen7/moves.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedMoveData}} */ -let BattleMovedex = { +export const BattleMovedex: {[k: string]: ModdedMoveData} = { "10000000voltthunderbolt": { inherit: true, isNonstandard: null, @@ -162,8 +159,10 @@ let BattleMovedex = { onHit(target, source, move) { let success = false; if (!target.volatiles['substitute'] || move.infiltrates) success = !!this.boost({evasion: -1}); - let removeTarget = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; - let removeAll = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; + const removeTarget = [ + 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', + ]; + const removeAll = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; for (const targetCondition of removeTarget) { if (target.side.removeSideCondition(targetCondition)) { if (!removeAll.includes(targetCondition)) continue; @@ -226,7 +225,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('terrainextender')) { + if (source?.hasItem('terrainextender')) { return 8; } return 5; @@ -351,13 +350,13 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('terrainextender')) { + if (source?.hasItem('terrainextender')) { return 8; } return 5; }, onBasePower(basePower, attacker, defender, move) { - let weakenedMoves = ['earthquake', 'bulldoze', 'magnitude']; + const weakenedMoves = ['earthquake', 'bulldoze', 'magnitude']; if (weakenedMoves.includes(move.id)) { this.debug('move weakened by grassy terrain'); return this.chainModify(0.5); @@ -602,7 +601,7 @@ let BattleMovedex = { return; } this.add('-activate', target, 'move: Protect'); - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -692,7 +691,9 @@ let BattleMovedex = { metronome: { inherit: true, desc: "A random move is selected for use, other than After You, Assist, Baneful Bunker, Beak Blast, Belch, Bestow, Celebrate, Chatter, Copycat, Counter, Covet, Crafty Shield, Destiny Bond, Detect, Diamond Storm, Dragon Ascent, Endure, Feint, Fleur Cannon, Focus Punch, Follow Me, Freeze Shock, Helping Hand, Hold Hands, Hyperspace Fury, Hyperspace Hole, Ice Burn, Instruct, King's Shield, Light of Ruin, Mat Block, Me First, Metronome, Mimic, Mind Blown, Mirror Coat, Mirror Move, Nature Power, Origin Pulse, Photon Geyser, Plasma Fists, Precipice Blades, Protect, Quash, Quick Guard, Rage Powder, Relic Song, Secret Sword, Shell Trap, Sketch, Sleep Talk, Snarl, Snatch, Snore, Spectral Thief, Spiky Shield, Spotlight, Steam Eruption, Struggle, Switcheroo, Techno Blast, Thief, Thousand Arrows, Thousand Waves, Transform, Trick, V-create, or Wide Guard.", - noMetronome: ['afteryou', 'assist', 'banefulbunker', 'beakblast', 'belch', 'bestow', 'celebrate', 'chatter', 'copycat', 'counter', 'covet', 'craftyshield', 'destinybond', 'detect', 'diamondstorm', 'dragonascent', 'endure', 'feint', 'fleurcannon', 'focuspunch', 'followme', 'freezeshock', 'helpinghand', 'holdhands', 'hyperspacefury', 'hyperspacehole', 'iceburn', 'instruct', 'kingsshield', 'lightofruin', 'matblock', 'mefirst', 'metronome', 'mimic', 'mindblown', 'mirrorcoat', 'mirrormove', 'naturepower', 'originpulse', 'photongeyser', 'plasmafists', 'precipiceblades', 'protect', 'quash', 'quickguard', 'ragepowder', 'relicsong', 'secretsword', 'shelltrap', 'sketch', 'sleeptalk', 'snarl', 'snatch', 'snore', 'spectralthief', 'spikyshield', 'spotlight', 'steameruption', 'struggle', 'switcheroo', 'technoblast', 'thief', 'thousandarrows', 'thousandwaves', 'transform', 'trick', 'vcreate', 'wideguard'], + noMetronome: [ + 'afteryou', 'assist', 'banefulbunker', 'beakblast', 'belch', 'bestow', 'celebrate', 'chatter', 'copycat', 'counter', 'covet', 'craftyshield', 'destinybond', 'detect', 'diamondstorm', 'dragonascent', 'endure', 'feint', 'fleurcannon', 'focuspunch', 'followme', 'freezeshock', 'helpinghand', 'holdhands', 'hyperspacefury', 'hyperspacehole', 'iceburn', 'instruct', 'kingsshield', 'lightofruin', 'matblock', 'mefirst', 'metronome', 'mimic', 'mindblown', 'mirrorcoat', 'mirrormove', 'naturepower', 'originpulse', 'photongeyser', 'plasmafists', 'precipiceblades', 'protect', 'quash', 'quickguard', 'ragepowder', 'relicsong', 'secretsword', 'shelltrap', 'sketch', 'sleeptalk', 'snarl', 'snatch', 'snore', 'spectralthief', 'spikyshield', 'spotlight', 'steameruption', 'struggle', 'switcheroo', 'technoblast', 'thief', 'thousandarrows', 'thousandwaves', 'transform', 'trick', 'vcreate', 'wideguard', + ], }, milkdrink: { inherit: true, @@ -796,7 +797,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('terrainextender')) { + if (source?.hasItem('terrainextender')) { return 8; } return 5; @@ -854,7 +855,7 @@ let BattleMovedex = { inherit: true, onHit(target) { if (target.side.active.length < 2) return false; // fails in singles - let action = this.queue.willMove(target); + const action = this.queue.willMove(target); if (!action) return false; action.priority = -7.1; @@ -1170,5 +1171,3 @@ let BattleMovedex = { secondary: null, }, }; - -exports.BattleMovedex = BattleMovedex; diff --git a/data/mods/gen7/pokedex.js b/data/mods/gen7/pokedex.ts similarity index 89% rename from data/mods/gen7/pokedex.js rename to data/mods/gen7/pokedex.ts index c1af19351c..3bf68b8f1a 100644 --- a/data/mods/gen7/pokedex.js +++ b/data/mods/gen7/pokedex.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesData}} */ -let BattlePokedex = { +export const BattlePokedex: {[k: string]: ModdedSpeciesData} = { charizard: { inherit: true, otherFormes: ["charizardmegax", "charizardmegay"], @@ -12,7 +9,9 @@ let BattlePokedex = { }, pikachu: { inherit: true, - otherFormes: ["pikachucosplay", "pikachurockstar", "pikachubelle", "pikachupopstar", "pikachuphd", "pikachulibre", "pikachuoriginal", "pikachuhoenn", "pikachusinnoh", "pikachuunova", "pikachukalos", "pikachualola", "pikachupartner", "pikachustarter"], + otherFormes: [ + "pikachucosplay", "pikachurockstar", "pikachubelle", "pikachupopstar", "pikachuphd", "pikachulibre", "pikachuoriginal", "pikachuhoenn", "pikachusinnoh", "pikachuunova", "pikachukalos", "pikachualola", "pikachupartner", "pikachustarter", + ], }, pikachuoriginal: { inherit: true, @@ -194,5 +193,3 @@ let BattlePokedex = { otherFormes: undefined, }, }; - -exports.BattlePokedex = BattlePokedex; diff --git a/data/mods/gen7/random-teams.js b/data/mods/gen7/random-teams.ts similarity index 72% rename from data/mods/gen7/random-teams.js rename to data/mods/gen7/random-teams.ts index e5b0ce0896..ba1905b4df 100644 --- a/data/mods/gen7/random-teams.js +++ b/data/mods/gen7/random-teams.ts @@ -1,54 +1,43 @@ -'use strict'; +import {RandomTeams, TeamData} from '../../random-teams'; +import {PRNG, PRNGSeed} from '../../../sim/prng'; -const RandomTeams = require('../../random-teams'); - -/** @typedef {import('../../random-teams').TeamData} TeamData */ - -class RandomGen7Teams extends RandomTeams { - /** - * @param {Format | string} format - * @param {?PRNG | [number, number, number, number]} [prng] - */ - constructor(format, prng) { +export class RandomGen7Teams extends RandomTeams { + randomFactorySets: AnyObject; + randomBSSFactorySets: AnyObject; + constructor(format: Format | string, prng: PRNG | PRNGSeed | null) { super(format, prng); - /**@type {AnyObject} */ - // @ts-ignore this.randomFactorySets = require('./factory-sets.json'); - /**@type {AnyObject} */ - // @ts-ignore this.randomBSSFactorySets = require('./bss-factory-sets.json'); } - - /** - * @param {string | Species} species - * @param {RandomTeamsTypes.TeamDetails} [teamDetails] - * @param {boolean} [isLead] - * @param {boolean} [isDoubles] - * @return {RandomTeamsTypes.RandomSet} - */ - randomSet(species, teamDetails = {}, isLead = false, isDoubles = false) { + randomSet( + species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}, isLead = false, isDoubles = false + ): RandomTeamsTypes.RandomSet { species = this.dex.getSpecies(species); - let baseSpecies = species; + const baseSpecies = species; let forme = species.name; - if (species.battleOnly) { + if (species.battleOnly && typeof species.battleOnly === 'string') { // Only change the forme. The species has custom moves, and may have different typing and requirements. - forme = /** @type {string} */ (species.battleOnly); + forme = species.battleOnly; } - let battleForme = this.checkBattleForme(species); - if (battleForme && battleForme.randomBattleMoves && species.otherFormes && (battleForme.isMega ? !teamDetails.megaStone : this.random(2))) { - species = this.dex.getSpecies(species.otherFormes.length >= 2 ? this.sample(species.otherFormes) : species.otherFormes[0]); + const battleForme = this.checkBattleForme(species); + if ( + battleForme?.randomBattleMoves && species.otherFormes && + (battleForme.isMega ? !teamDetails.megaStone : this.random(2)) + ) { + species = this.dex.getSpecies( + species.otherFormes.length >= 2 ? this.sample(species.otherFormes) : species.otherFormes[0] + ); } - const randMoves = !isDoubles ? species.randomBattleMoves : (species.randomDoubleBattleMoves || species.randomBattleMoves); - // @ts-ignore - let movePool = (randMoves || Object.keys(this.dex.data.Learnsets[species.id].learnset)).slice(); - let rejectedPool = []; - /**@type {string[]} */ - let moves = []; + const randMoves = !isDoubles ? + species.randomBattleMoves : (species.randomDoubleBattleMoves || species.randomBattleMoves); + const movePool = (randMoves || Object.keys(this.dex.data.Learnsets[species.id]!.learnset!)).slice(); + const rejectedPool = []; + const moves: string[] = []; let ability = ''; let item = ''; - let evs = { + const evs = { hp: 85, atk: 85, def: 85, @@ -56,7 +45,7 @@ class RandomGen7Teams extends RandomTeams { spd: 85, spe: 85, }; - let ivs = { + const ivs = { hp: 31, atk: 31, def: 31, @@ -64,14 +53,12 @@ class RandomGen7Teams extends RandomTeams { spd: 31, spe: 31, }; - /**@type {{[k: string]: true}} */ - let hasType = {}; + const hasType: {[k: string]: true} = {}; hasType[species.types[0]] = true; if (species.types[1]) { hasType[species.types[1]] = true; } - /**@type {{[k: string]: true}} */ - let hasAbility = {}; + const hasAbility: {[k: string]: true} = {}; hasAbility[species.abilities[0]] = true; if (species.abilities[1]) { hasAbility[species.abilities[1]] = true; @@ -85,13 +72,12 @@ class RandomGen7Teams extends RandomTeams { } // These moves can be used even if we aren't setting up to use them: - let SetupException = ['closecombat', 'diamondstorm', 'extremespeed', 'superpower', 'clangingscales', 'dracometeor']; + const SetupException = ['closecombat', 'diamondstorm', 'extremespeed', 'superpower', 'clangingscales', 'dracometeor']; - let counterAbilities = ['Adaptability', 'Contrary', 'Iron Fist', 'Skill Link', 'Strong Jaw']; - let ateAbilities = ['Aerilate', 'Galvanize', 'Pixilate', 'Refrigerate']; + const counterAbilities = ['Adaptability', 'Contrary', 'Iron Fist', 'Skill Link', 'Strong Jaw']; + const ateAbilities = ['Aerilate', 'Galvanize', 'Pixilate', 'Refrigerate']; - /**@type {{[k: string]: boolean}} */ - let hasMove = {}; + let hasMove: {[k: string]: boolean} = {}; let counter; do { @@ -107,7 +93,7 @@ class RandomGen7Teams extends RandomTeams { // Choose next 4 moves from learnset/viable moves and add them to moves list: while (moves.length < 4 && movePool.length) { - let moveid = this.sampleNoReplace(movePool); + const moveid = this.sampleNoReplace(movePool); if (moveid.startsWith('hiddenpower')) { availableHP--; if (hasMove['hiddenpower']) continue; @@ -118,7 +104,7 @@ class RandomGen7Teams extends RandomTeams { moves.push(moveid); } while (moves.length < 4 && rejectedPool.length) { - let moveid = this.sampleNoReplace(rejectedPool); + const moveid = this.sampleNoReplace(rejectedPool); hasMove[moveid] = true; moves.push(moveid); } @@ -127,8 +113,8 @@ class RandomGen7Teams extends RandomTeams { // Iterate through the moves again, this time to cull them: for (const [k, moveId] of moves.entries()) { - let move = this.dex.getMove(moveId); - let moveid = move.id; + const move = this.dex.getMove(moveId); + const moveid = move.id; let rejected = false; let isSetup = false; @@ -155,7 +141,7 @@ class RandomGen7Teams extends RandomTeams { case 'reflect': if (!hasMove['calmmind'] && !hasMove['lightscreen']) rejected = true; if (movePool.length > 1) { - let screen = movePool.indexOf('lightscreen'); + const screen = movePool.indexOf('lightscreen'); if (screen >= 0) this.fastPop(movePool, screen); } break; @@ -165,7 +151,7 @@ class RandomGen7Teams extends RandomTeams { case 'sleeptalk': if (!hasMove['rest']) rejected = true; if (movePool.length > 1) { - let rest = movePool.indexOf('rest'); + const rest = movePool.indexOf('rest'); if (rest >= 0) this.fastPop(movePool, rest); } break; @@ -210,9 +196,14 @@ class RandomGen7Teams extends RandomTeams { // Bad after setup case 'circlethrow': case 'dragontail': if (counter.setupType && ((!hasMove['rest'] && !hasMove['sleeptalk']) || hasMove['stormthrow'])) rejected = true; - if (!!counter['speedsetup'] || hasMove['encore'] || hasMove['raindance'] || hasMove['roar'] || hasMove['trickroom'] || hasMove['whirlwind']) rejected = true; - // @ts-ignore - if ((counter[move.type] > 1 && counter.Status > 1) || (hasAbility['Sheer Force'] && !!counter['sheerforce'])) rejected = true; + if ( + !!counter['speedsetup'] || hasMove['encore'] || hasMove['raindance'] || + hasMove['roar'] || hasMove['trickroom'] || hasMove['whirlwind'] + ) rejected = true; + if ( + (counter[move.type] > 1 && counter.Status > 1) || + (hasAbility['Sheer Force'] && !!counter['sheerforce']) + ) rejected = true; break; case 'defog': if (counter.setupType || hasMove['spikes'] || hasMove['stealthrock'] || teamDetails.defog) rejected = true; @@ -221,7 +212,10 @@ class RandomGen7Teams extends RandomTeams { if (counter.setupType || hasMove['substitute'] || hasMove['switcheroo'] || hasMove['trick']) rejected = true; break; case 'foulplay': - if (counter.setupType || !!counter['speedsetup'] || counter['Dark'] > 2 || hasMove['clearsmog'] || hasMove['rest'] && hasMove['sleeptalk']) rejected = true; + if ( + counter.setupType || !!counter['speedsetup'] || counter['Dark'] > 2 || + hasMove['clearsmog'] || hasMove['rest'] && hasMove['sleeptalk'] + ) rejected = true; if (counter.damagingMoves.length - 1 === counter['priority']) rejected = true; break; case 'haze': case 'spikes': @@ -244,7 +238,10 @@ class RandomGen7Teams extends RandomTeams { if (hasMove['rest'] || hasMove['lightscreen'] && hasMove['reflect']) rejected = true; break; case 'pursuit': - if (counter.setupType || counter.Status > 1 || counter['Dark'] > 2 || hasMove['knockoff'] && !hasType['Dark']) rejected = true; + if ( + counter.setupType || counter.Status > 1 || counter['Dark'] > 2 || + hasMove['knockoff'] && !hasType['Dark'] + ) rejected = true; break; case 'rapidspin': if (counter.setupType || teamDetails.rapidSpin) rejected = true; @@ -253,7 +250,10 @@ class RandomGen7Teams extends RandomTeams { if (hasMove['substitute'] && teamDetails.zMove) rejected = true; break; case 'stealthrock': - if (counter.setupType || !!counter['speedsetup'] || hasMove['rest'] || hasMove['substitute'] || hasMove['trickroom'] || teamDetails.stealthRock) rejected = true; + if ( + counter.setupType || !!counter['speedsetup'] || hasMove['rest'] || + hasMove['substitute'] || hasMove['trickroom'] || teamDetails.stealthRock + ) rejected = true; break; case 'stickyweb': if (teamDetails.stickyWeb) rejected = true; @@ -266,11 +266,17 @@ class RandomGen7Teams extends RandomTeams { if (hasMove['lightscreen'] || hasMove['reflect']) rejected = true; break; case 'uturn': - if (counter.setupType || !!counter['speedsetup'] || hasType['Bug'] && counter.stab < 2 && counter.damagingMoves.length > 2 && !hasAbility['Adaptability'] && !hasAbility['Download']) rejected = true; + if ( + counter.setupType || !!counter['speedsetup'] || hasType['Bug'] && counter.stab < 2 && + counter.damagingMoves.length > 2 && !hasAbility['Adaptability'] && !hasAbility['Download'] + ) rejected = true; if ((hasAbility['Speed Boost'] && hasMove['protect']) || (hasAbility['Protean'] && counter.Status > 2)) rejected = true; break; case 'voltswitch': - if (counter.setupType || !!counter['speedsetup'] || hasMove['electricterrain'] || hasMove['raindance'] || hasMove['uturn']) rejected = true; + if ( + counter.setupType || !!counter['speedsetup'] || hasMove['electricterrain'] || + hasMove['raindance'] || hasMove['uturn'] + ) rejected = true; break; // Bit redundant to have both @@ -282,7 +288,10 @@ class RandomGen7Teams extends RandomTeams { if (hasMove['knockoff'] || hasMove['pursuit']) rejected = true; break; case 'darkpulse': - if ((hasMove['crunch'] || hasMove['knockoff'] || hasMove['hyperspacefury']) && counter.setupType !== 'Special') rejected = true; + if ( + (hasMove['crunch'] || hasMove['knockoff'] || hasMove['hyperspacefury']) && + counter.setupType !== 'Special' + ) rejected = true; break; case 'suckerpunch': if (counter.damagingMoves.length < 2 || hasMove['glare'] || !hasType['Dark'] && counter['Dark'] > 1) rejected = true; @@ -318,7 +327,11 @@ class RandomGen7Teams extends RandomTeams { if ((hasMove['focusblast'] || hasMove['superpower']) && counter.setupType !== 'Physical') rejected = true; break; case 'closecombat': case 'highjumpkick': - if ((hasMove['aurasphere'] || hasMove['focusblast'] || movePool.includes('aurasphere') || movePool.includes('focusblast')) && counter.setupType === 'Special') rejected = true; + if ( + ( + hasMove['aurasphere'] || hasMove['focusblast'] || movePool.includes('aurasphere') || movePool.includes('focusblast') + ) && counter.setupType === 'Special' + ) rejected = true; if (hasMove['bulkup'] && hasMove['drainpunch']) rejected = true; break; case 'stormthrow': @@ -436,7 +449,7 @@ class RandomGen7Teams extends RandomTeams { if (hasAbility['Contrary'] && !counter.setupType && !!counter['physicalpool']) rejected = true; break; case 'psyshock': - let psychic = movePool.indexOf('psychic'); + const psychic = movePool.indexOf('psychic'); if (psychic >= 0) this.fastPop(movePool, psychic); break; case 'headsmash': @@ -455,8 +468,14 @@ class RandomGen7Teams extends RandomTeams { if ((hasMove['ironhead'] || hasMove['meteormash']) && counter.setupType !== 'Special') rejected = true; break; case 'hydropump': - if (hasMove['liquidation'] || hasMove['razorshell'] || hasMove['waterfall'] || (hasMove['rest'] && hasMove['sleeptalk'])) rejected = true; - if (hasMove['scald'] && ((counter.Special < 4 && !hasMove['uturn']) || (species.types.length > 1 && counter.stab < 3))) rejected = true; + if ( + hasMove['liquidation'] || hasMove['razorshell'] || hasMove['waterfall'] || + (hasMove['rest'] && hasMove['sleeptalk']) + ) rejected = true; + if ( + hasMove['scald'] && + ((counter.Special < 4 && !hasMove['uturn']) || (species.types.length > 1 && counter.stab < 3)) + ) rejected = true; break; case 'originpulse': case 'surf': if (hasMove['hydropump'] || hasMove['scald']) rejected = true; @@ -468,7 +487,10 @@ class RandomGen7Teams extends RandomTeams { // Status: case 'electroweb': case 'stunspore': case 'thunderwave': if (counter.setupType || !!counter['speedsetup'] || (hasMove['rest'] && hasMove['sleeptalk'])) rejected = true; - if (hasMove['discharge'] || hasMove['spore'] || hasMove['toxic'] || hasMove['trickroom'] || hasMove['yawn']) rejected = true; + if ( + hasMove['discharge'] || hasMove['spore'] || hasMove['toxic'] || + hasMove['trickroom'] || hasMove['yawn'] + ) rejected = true; break; case 'glare': case 'headbutt': if (hasMove['bodyslam'] || !hasMove['glare']) rejected = true; @@ -488,10 +510,10 @@ class RandomGen7Teams extends RandomTeams { if (counter.Physical + counter.Special < 2 || hasMove['rest'] && hasMove['sleeptalk']) rejected = true; if (!hasAbility['Chlorophyll'] && !hasAbility['Flower Gift'] && !hasMove['solarbeam']) rejected = true; if (rejected && movePool.length > 1) { - let solarbeam = movePool.indexOf('solarbeam'); + const solarbeam = movePool.indexOf('solarbeam'); if (solarbeam >= 0) this.fastPop(movePool, solarbeam); if (movePool.length > 1) { - let weatherball = movePool.indexOf('weatherball'); + const weatherball = movePool.indexOf('weatherball'); if (weatherball >= 0) this.fastPop(movePool, weatherball); } } @@ -504,7 +526,12 @@ class RandomGen7Teams extends RandomTeams { break; case 'substitute': if (hasMove['dracometeor'] || hasMove['leafstorm'] && !hasAbility['Contrary']) rejected = true; - if (hasMove['pursuit'] || hasMove['rest'] || hasMove['taunt'] || hasMove['uturn'] || hasMove['voltswitch'] || hasMove['whirlwind']) rejected = true; + if ( + hasMove['pursuit'] || hasMove['rest'] || hasMove['taunt'] || + hasMove['uturn'] || hasMove['voltswitch'] || hasMove['whirlwind'] + ) { + rejected = true; + } if (movePool.includes('copycat')) rejected = true; break; case 'powersplit': @@ -516,57 +543,136 @@ class RandomGen7Teams extends RandomTeams { } // This move doesn't satisfy our setup requirements: - if ((move.category === 'Physical' && counter.setupType === 'Special') || (move.category === 'Special' && counter.setupType === 'Physical')) { + if ( + (move.category === 'Physical' && counter.setupType === 'Special') || + (move.category === 'Special' && counter.setupType === 'Physical') + ) { // Reject STABs last in case the setup type changes later on - // @ts-ignore - let stabs = counter[species.types[0]] + (counter[species.types[1]] || 0); - if (!SetupException.includes(moveid) && (!hasType[move.type] || stabs > 1 || counter[move.category] < 2)) rejected = true; + const stabs = counter[species.types[0]] + (counter[species.types[1]] || 0); + if ( + !SetupException.includes(moveid) && + (!hasType[move.type] || stabs > 1 || counter[move.category] < 2) + ) rejected = true; } - if (counter.setupType === 'Special' && moveid === 'hiddenpower' && species.types.length > 1 && counter['Special'] <= 2 && !hasType[move.type] && !counter['Physical'] && counter['specialpool']) { + if ( + counter.setupType === 'Special' && moveid === 'hiddenpower' && + species.types.length > 1 && counter['Special'] <= 2 && !hasType[move.type] && + !counter['Physical'] && counter['specialpool'] + ) { // Hidden Power isn't good enough rejected = true; } // Pokemon should have moves that benefit their Type/Ability/Weather, as well as moves required by its forme - // @ts-ignore - if (!rejected && !['judgment', 'sleeptalk'].includes(moveid) && (counter['physicalsetup'] + counter['specialsetup'] < 2 && (!counter.setupType || counter.setupType === 'Mixed' || (move.category !== counter.setupType && move.category !== 'Status') || (counter[counter.setupType] + counter.Status > 3 && !counter.hazards))) && - ((!counter.stab && !hasMove['nightshade'] && !hasMove['seismictoss'] && (species.types.length > 1 || (species.types[0] !== 'Normal' && species.types[0] !== 'Psychic') || !hasMove['icebeam'] || species.baseStats.spa >= species.baseStats.spd)) || - (hasType['Bug'] && (movePool.includes('megahorn') || movePool.includes('pinmissile'))) || - ((hasType['Dark'] && !counter['Dark'] && !hasAbility['Protean']) || hasMove['suckerpunch'] && !hasAbility['Contrary'] && counter.stab < species.types.length) || - (hasType['Dragon'] && !counter['Dragon'] && !hasAbility['Aerilate'] && !hasAbility['Pixilate'] && !hasMove['fly'] && !hasMove['rest'] && !hasMove['sleeptalk']) || - (hasType['Electric'] && (!counter['Electric'] || (hasMove['voltswitch'] && counter.stab < 2)) && !hasAbility['Galvanize']) || + if ( + !rejected && !['judgment', 'sleeptalk'].includes(moveid) && + ( + counter['physicalsetup'] + counter['specialsetup'] < 2 && + ( + !counter.setupType || counter.setupType === 'Mixed' || + (move.category !== counter.setupType && move.category !== 'Status') || + (counter[counter.setupType] + counter.Status > 3 && !counter.hazards) + ) + ) && + ( + ( + !counter.stab && !hasMove['nightshade'] && !hasMove['seismictoss'] && + ( + species.types.length > 1 || + (species.types[0] !== 'Normal' && species.types[0] !== 'Psychic') || + !hasMove['icebeam'] || species.baseStats.spa >= species.baseStats.spd + ) + ) || + ( + hasType['Bug'] && + (movePool.includes('megahorn') || movePool.includes('pinmissile')) + ) || + ( + (hasType['Dark'] && !counter['Dark'] && !hasAbility['Protean']) || + hasMove['suckerpunch'] && !hasAbility['Contrary'] && counter.stab < species.types.length + ) || + ( + hasType['Dragon'] && !counter['Dragon'] && !hasAbility['Aerilate'] && + !hasAbility['Pixilate'] && !hasMove['fly'] && !hasMove['rest'] && !hasMove['sleeptalk'] + ) || + ( + hasType['Electric'] && (!counter['Electric'] || + (hasMove['voltswitch'] && counter.stab < 2)) && !hasAbility['Galvanize'] + ) || (hasType['Fairy'] && !counter['Fairy'] && !hasAbility['Pixilate'] && (counter.setupType || !counter['Status'])) || - (hasType['Fighting'] && !counter['Fighting'] && (species.baseStats.atk >= 110 || hasAbility['Justified'] || hasAbility['Unburden'] || counter.setupType || !counter['Status'])) || + ( + hasType['Fighting'] && !counter['Fighting'] && + ( + species.baseStats.atk >= 110 || hasAbility['Justified'] || + hasAbility['Unburden'] || counter.setupType || !counter['Status'] + ) + ) || (hasType['Fire'] && !counter['Fire']) || - (hasType['Flying'] && !counter['Flying'] && (hasAbility['Gale Wings'] || hasAbility['Serene Grace'] || (hasType['Normal'] && (movePool.includes('beakblast') || movePool.includes('bravebird'))))) || + ( + hasType['Flying'] && !counter['Flying'] && + ( + hasAbility['Gale Wings'] || hasAbility['Serene Grace'] || + (hasType['Normal'] && (movePool.includes('beakblast') || movePool.includes('bravebird'))) + ) + ) || (hasType['Ghost'] && !hasType['Dark'] && !counter['Ghost'] && !hasAbility['Steelworker']) || (hasType['Grass'] && !counter['Grass'] && !hasType['Fairy'] && !hasType['Poison'] && !hasType['Steel']) || (hasType['Ground'] && !counter['Ground'] && !hasMove['rest'] && !hasMove['sleeptalk']) || (hasType['Ice'] && !counter['Ice'] && !hasAbility['Refrigerate']) || (hasType['Normal'] && (movePool.includes('boomburst') || hasAbility['Guts'] && movePool.includes('facade'))) || (hasType['Poison'] && !counter['Poison'] && (hasAbility['Sheer Force'] || counter.setupType)) || - (hasType['Psychic'] && !!counter['Psychic'] && (movePool.includes('psychicfangs') || !hasType['Flying'] && !hasAbility['Pixilate'] && counter.stab < species.types.length)) || - (hasType['Rock'] && !counter['Rock'] && !hasType['Fairy'] && (species.baseStats.atk >= 105 || hasAbility['Rock Head'] || counter.setupType === 'Physical')) || - (((hasType['Steel'] && (hasAbility['Technician'] || hasMove['trickroom'])) || hasAbility['Steelworker']) && !counter['Steel']) || + ( + hasType['Psychic'] && !!counter['Psychic'] && + ( + movePool.includes('psychicfangs') || !hasType['Flying'] && + !hasAbility['Pixilate'] && counter.stab < species.types.length + ) + ) || + ( + hasType['Rock'] && !counter['Rock'] && !hasType['Fairy'] && + (species.baseStats.atk >= 105 || hasAbility['Rock Head'] || counter.setupType === 'Physical') + ) || + ( + ( + (hasType['Steel'] && (hasAbility['Technician'] || hasMove['trickroom'])) || + hasAbility['Steelworker'] + ) && !counter['Steel'] + ) || (hasType['Water'] && (!counter['Water'] || !counter.stab) && !hasAbility['Protean']) || - // @ts-ignore - ((hasAbility['Adaptability'] && !counter.setupType && species.types.length > 1 && (!counter[species.types[0]] || !counter[species.types[1]])) || - ((hasAbility['Aerilate'] || (hasAbility['Galvanize'] && !counter['Electric']) || hasAbility['Pixilate'] || (hasAbility['Refrigerate'] && !hasMove['blizzard'])) && !counter['Normal']) || + ( + ( + hasAbility['Adaptability'] && !counter.setupType && species.types.length > 1 && + (!counter[species.types[0]] || !counter[species.types[1]]) + ) || + ( + ( + hasAbility['Aerilate'] || (hasAbility['Galvanize'] && !counter['Electric']) || + hasAbility['Pixilate'] || (hasAbility['Refrigerate'] && !hasMove['blizzard']) + ) && !counter['Normal'] + ) || (hasAbility['Contrary'] && !counter['contrary'] && species.name !== 'Shuckle') || (hasAbility['Psychic Surge'] && !counter['Psychic']) || (hasAbility['Slow Start'] && movePool.includes('substitute')) || - (!counter.recovery && !counter.setupType && !hasMove['healingwish'] && !hasMove['trick'] && (movePool.includes('recover') || movePool.includes('roost') || movePool.includes('softboiled')) && (counter.Status > 1 || (species.nfe && !!counter['Status']))) || + ( + !counter.recovery && !counter.setupType && !hasMove['healingwish'] && !hasMove['trick'] && + (movePool.includes('recover') || movePool.includes('roost') || movePool.includes('softboiled')) && + (counter.Status > 1 || (species.nfe && !!counter['Status'])) + ) || (movePool.includes('stickyweb') && !counter.setupType && !teamDetails.stickyWeb) || (species.requiredMove && movePool.includes(toID(species.requiredMove)))))) { // Reject Status or non-STAB - if (!isSetup && !move.weather && !move.sideCondition && !move.stallingMove && !move.damage && (move.category !== 'Status' || !move.flags.heal)) { - if (move.category === 'Status' || move.selfSwitch || !hasType[move.type] || move.basePower && move.basePower < 40 && !move.multihit) rejected = true; + if (!isSetup && !move.weather && !move.sideCondition && !move.stallingMove && + !move.damage && (move.category !== 'Status' || !move.flags.heal)) { + if (move.category === 'Status' || move.selfSwitch || !hasType[move.type] || + move.basePower && move.basePower < 40 && !move.multihit) { + rejected = true; + } } } // Sleep Talk shouldn't be selected without Rest if (moveid === 'rest' && rejected) { - let sleeptalk = movePool.indexOf('sleeptalk'); + const sleeptalk = movePool.indexOf('sleeptalk'); if (sleeptalk >= 0) { if (movePool.length < 2) { rejected = false; @@ -577,8 +683,17 @@ class RandomGen7Teams extends RandomTeams { } // Remove rejected moves from the move list - if (rejected && (movePool.length - availableHP || availableHP && (moveid === 'hiddenpower' || !hasMove['hiddenpower']))) { - if (move.category !== 'Status' && !move.damage && !move.flags.charge && (moveid !== 'hiddenpower' || !availableHP)) rejectedPool.push(moves[k]); + if ( + rejected && + (movePool.length - availableHP || availableHP && + (moveid === 'hiddenpower' || !hasMove['hiddenpower'])) + ) { + if ( + move.category !== 'Status' && !move.damage && + !move.flags.charge && (moveid !== 'hiddenpower' || !availableHP) + ) { + rejectedPool.push(moves[k]); + } moves.splice(k, 1); break; } @@ -604,9 +719,7 @@ class RandomGen7Teams extends RandomTeams { moves[moves.indexOf('workup')] = 'bulkup'; } - /**@type {[string, string | undefined, string | undefined]} */ - // @ts-ignore - let abilities = Object.values(baseSpecies.abilities); + const abilities: string[] = Object.values(baseSpecies.abilities); abilities.sort((a, b) => this.dex.getAbility(b).rating - this.dex.getAbility(a).rating); let ability0 = this.dex.getAbility(abilities[0]); let ability1 = this.dex.getAbility(abilities[1]); @@ -627,7 +740,6 @@ class RandomGen7Teams extends RandomTeams { rejectAbility = false; if (counterAbilities.includes(ability)) { // Adaptability, Contrary, Iron Fist, Skill Link, Strong Jaw - // @ts-ignore rejectAbility = !counter[toID(ability)]; } else if (ateAbilities.includes(ability)) { rejectAbility = !counter['Normal']; @@ -636,7 +748,8 @@ class RandomGen7Teams extends RandomTeams { } else if (ability === 'Battle Bond' || ability === 'Flare Boost' || ability === 'Moody') { rejectAbility = true; } else if (ability === 'Chlorophyll' || ability === 'Leaf Guard') { - rejectAbility = species.baseStats.spe > 100 || abilities.includes('Harvest') || (!hasMove['sunnyday'] && !teamDetails['sun']); + rejectAbility = species.baseStats.spe > 100 || abilities.includes('Harvest') || + (!hasMove['sunnyday'] && !teamDetails['sun']); } else if (ability === 'Competitive') { rejectAbility = !counter['Special'] || (hasMove['rest'] && hasMove['sleeptalk']); } else if (ability === 'Compound Eyes' || ability === 'No Guard') { @@ -672,7 +785,8 @@ class RandomGen7Teams extends RandomTeams { } else if (ability === 'Magnet Pull') { rejectAbility = !!counter['Normal'] || !hasType['Electric'] && !hasMove['earthpower']; } else if (ability === 'Mold Breaker') { - rejectAbility = hasMove['acrobatics'] || abilities.includes('Adaptability') || abilities.includes('Sheer Force') && !!counter['sheerforce']; + rejectAbility = hasMove['acrobatics'] || abilities.includes('Adaptability') || + abilities.includes('Sheer Force') && !!counter['sheerforce']; } else if (ability === 'Overgrow') { rejectAbility = !counter['Grass']; } else if (ability === 'Poison Heal') { @@ -708,7 +822,10 @@ class RandomGen7Teams extends RandomTeams { } else if (ability === 'Technician') { rejectAbility = !counter['technician'] || hasMove['tailslap'] || species.isMega; } else if (ability === 'Tinted Lens') { - rejectAbility = (abilities.includes('Magic Guard') && !!counter['Status']) || abilities.includes('Prankster') || hasMove['protect'] || counter['damage'] >= counter.damagingMoves.length || (counter.Status > 2 && !counter.setupType); + rejectAbility = (abilities.includes('Magic Guard') && !!counter['Status']) || + abilities.includes('Prankster') || hasMove['protect'] || + counter['damage'] >= counter.damagingMoves.length || + (counter.Status > 2 && !counter.setupType); } else if (ability === 'Torrent') { rejectAbility = !counter['Water'] || species.isMega; } else if (ability === 'Unaware') { @@ -734,7 +851,9 @@ class RandomGen7Teams extends RandomTeams { } } while (rejectAbility); - if (abilities.includes('Guts') && ability !== 'Quick Feet' && (hasMove['facade'] || (hasMove['protect'] && !isDoubles) || (hasMove['rest'] && hasMove['sleeptalk']))) { + if (abilities.includes('Guts') && ability !== 'Quick Feet' && + (hasMove['facade'] || (hasMove['protect'] && !isDoubles) || + (hasMove['rest'] && hasMove['sleeptalk']))) { ability = 'Guts'; } else if (abilities.includes('Triage') && !!counter['drain']) { ability = 'Triage'; @@ -753,7 +872,6 @@ class RandomGen7Teams extends RandomTeams { item = !isDoubles ? 'Leftovers' : 'Sitrus Berry'; if (species.requiredItems) { - // @ts-ignore if (species.baseSpecies === 'Arceus' && (hasMove['judgment'] || !counter[species.types[0]] || teamDetails.zMove)) { // Judgment doesn't change type with Z-Crystals item = species.requiredItems[0]; @@ -792,7 +910,7 @@ class RandomGen7Teams extends RandomTeams { item = 'Ultranecrozium Z'; if (!hasMove['photongeyser']) { for (const moveid of moves) { - let move = this.dex.getMove(moveid); + const move = this.dex.getMove(moveid); if (move.category === 'Status' || hasType[move.type]) continue; moves[moves.indexOf(moveid)] = 'photongeyser'; break; @@ -800,10 +918,13 @@ class RandomGen7Teams extends RandomTeams { } } } else if (species.name === 'Pikachu') { - if (!isDoubles) forme = 'Pikachu' + this.sample(['', '-Original', '-Hoenn', '-Sinnoh', '-Unova', '-Kalos', '-Alola', '-Partner']); + if (!isDoubles) { + forme = `Pikachu${this.sample(['', '-Original', '-Hoenn', '-Sinnoh', '-Unova', '-Kalos', '-Alola', '-Partner'])}`; + } if (forme !== 'Pikachu') ability = 'Static'; item = 'Light Ball'; - } else if (species.name === 'Porygon-Z' && hasMove['nastyplot'] && !hasMove['trick'] && !['nastyplot', 'icebeam', 'triattack'].includes(moves[0]) && !teamDetails.zMove && !isDoubles) { + } else if (species.name === 'Porygon-Z' && hasMove['nastyplot'] && !hasMove['trick'] && + !['nastyplot', 'icebeam', 'triattack'].includes(moves[0]) && !teamDetails.zMove && !isDoubles) { moves[moves.indexOf('nastyplot')] = 'conversion'; moves[moves.indexOf('triattack')] = 'recover'; item = 'Normalium Z'; @@ -851,7 +972,8 @@ class RandomGen7Teams extends RandomTeams { item = (ability === 'Solid Rock' && !!counter['priority']) ? 'Weakness Policy' : 'White Herb'; } else if ((ability === 'Guts' || hasMove['facade']) && !hasMove['sleeptalk']) { item = (hasType['Fire'] || ability === 'Quick Feet' || ability === 'Toxic Boost') ? 'Toxic Orb' : 'Flame Orb'; - } else if ((ability === 'Magic Guard' && counter.damagingMoves.length > 1) || (ability === 'Sheer Force' && !!counter['sheerforce'])) { + } else if ((ability === 'Magic Guard' && counter.damagingMoves.length > 1) || + (ability === 'Sheer Force' && !!counter['sheerforce'])) { item = 'Life Orb'; } else if (ability === 'Poison Heal') { item = 'Toxic Orb'; @@ -859,7 +981,8 @@ class RandomGen7Teams extends RandomTeams { item = hasMove['fakeout'] ? 'Normal Gem' : 'Sitrus Berry'; } else if (hasMove['acrobatics']) { item = ''; - } else if (((hasMove['darkpulse'] && ability === 'Fur Coat' && counter.setupType) || (hasMove['suckerpunch'] && ability === 'Moxie' && counter['Dark'] < 2)) && !teamDetails.zMove) { + } else if (((hasMove['darkpulse'] && ability === 'Fur Coat' && counter.setupType) || + (hasMove['suckerpunch'] && ability === 'Moxie' && counter['Dark'] < 2)) && !teamDetails.zMove) { item = 'Darkinium Z'; } else if (hasMove['outrage'] && counter.setupType && !hasMove['fly'] && !teamDetails.zMove) { item = 'Dragonium Z'; @@ -867,13 +990,17 @@ class RandomGen7Teams extends RandomTeams { item = 'Electrium Z'; } else if (hasMove['fleurcannon'] && !!counter['speedsetup'] && !teamDetails.zMove) { item = 'Fairium Z'; - } else if (((hasMove['focusblast'] && hasMove['nastyplot'] && hasType['Fighting']) || (hasMove['reversal'] && hasMove['substitute'])) && !teamDetails.zMove) { + } else if (((hasMove['focusblast'] && hasMove['nastyplot'] && hasType['Fighting']) || + (hasMove['reversal'] && hasMove['substitute'])) && !teamDetails.zMove) { item = 'Fightinium Z'; } else if ((hasMove['magmastorm'] || hasMove['mindblown'] && !!counter['Status']) && !teamDetails.zMove) { item = 'Firium Z'; - } else if (!teamDetails.zMove && (hasMove['fly'] || (hasMove['hurricane'] && species.baseStats.spa >= 125 && (!!counter.Status || hasMove['superpower'])) || ((hasMove['bounce'] || hasMove['bravebird']) && counter.setupType))) { + } else if (!teamDetails.zMove && (hasMove['fly'] || + (hasMove['hurricane'] && species.baseStats.spa >= 125 && (!!counter.Status || hasMove['superpower'])) || + ((hasMove['bounce'] || hasMove['bravebird']) && counter.setupType))) { item = 'Flyinium Z'; - } else if (hasMove['sleeppowder'] && hasType['Grass'] && counter.setupType && species.baseStats.spe <= 70 && !teamDetails.zMove) { + } else if (hasMove['sleeppowder'] && hasType['Grass'] && + counter.setupType && species.baseStats.spe <= 70 && !teamDetails.zMove) { item = 'Grassium Z'; } else if (hasMove['dig'] && !teamDetails.zMove) { item = 'Groundium Z'; @@ -901,27 +1028,42 @@ class RandomGen7Teams extends RandomTeams { } } else if (hasMove['auroraveil'] || hasMove['lightscreen'] && hasMove['reflect']) { item = 'Light Clay'; - } else if (hasMove['rest'] && !hasMove['sleeptalk'] && ability !== 'Natural Cure' && ability !== 'Shed Skin' && ability !== 'Shadow Tag') { + } else if (hasMove['rest'] && !hasMove['sleeptalk'] && + ability !== 'Natural Cure' && ability !== 'Shed Skin' && ability !== 'Shadow Tag') { item = 'Chesto Berry'; // Medium priority - } else if ((ability === 'Speed Boost' || ability === 'Stance Change' || species.name === 'Pheromosa') && counter.Physical + counter.Special > 2 && !hasMove['uturn']) { + } else if ((ability === 'Speed Boost' || ability === 'Stance Change' || species.name === 'Pheromosa') && + counter.Physical + counter.Special > 2 && !hasMove['uturn']) { item = 'Life Orb'; - } else if (counter.Physical >= 4 && !hasMove['bodyslam'] && !hasMove['dragontail'] && !hasMove['fakeout'] && !hasMove['flamecharge'] && !hasMove['rapidspin'] && !hasMove['suckerpunch'] && !isDoubles) { - item = (species.baseStats.atk >= 100 || ability === 'Huge Power') && species.baseStats.spe >= 60 && species.baseStats.spe <= 108 && !counter['priority'] && this.randomChance(2, 3) ? 'Choice Scarf' : 'Choice Band'; - } else if (counter.Special >= 4 && !hasMove['acidspray'] && !hasMove['clearsmog'] && !hasMove['fierydance'] && !isDoubles) { - item = species.baseStats.spa >= 100 && species.baseStats.spe >= 60 && species.baseStats.spe <= 108 && ability !== 'Tinted Lens' && !counter['priority'] && this.randomChance(2, 3) ? 'Choice Scarf' : 'Choice Specs'; - } else if (counter.Physical >= 3 && hasMove['defog'] && species.baseStats.spe >= 60 && species.baseStats.spe <= 108 && !counter['priority'] && !hasMove['foulplay'] && !isDoubles) { + } else if (counter.Physical >= 4 && !hasMove['bodyslam'] && !hasMove['dragontail'] && + !hasMove['fakeout'] && !hasMove['flamecharge'] && !hasMove['rapidspin'] && !hasMove['suckerpunch'] && !isDoubles) { + item = (species.baseStats.atk >= 100 || ability === 'Huge Power') && + species.baseStats.spe >= 60 && species.baseStats.spe <= 108 && !counter['priority'] && this.randomChance(2, 3) ? + 'Choice Scarf' : 'Choice Band'; + } else if (counter.Special >= 4 && !hasMove['acidspray'] && + !hasMove['clearsmog'] && !hasMove['fierydance'] && !isDoubles) { + item = species.baseStats.spa >= 100 && species.baseStats.spe >= 60 && + species.baseStats.spe <= 108 && ability !== 'Tinted Lens' && !counter['priority'] && this.randomChance(2, 3) ? + 'Choice Scarf' : 'Choice Specs'; + } else if (counter.Physical >= 3 && hasMove['defog'] && + species.baseStats.spe >= 60 && species.baseStats.spe <= 108 && + !counter['priority'] && !hasMove['foulplay'] && !isDoubles) { item = 'Choice Scarf'; } else if (counter.Special >= 3 && hasMove['uturn'] && !hasMove['acidspray'] && !isDoubles) { item = 'Choice Specs'; - } else if ((ability === 'Drizzle' || ability === 'Slow Start' || hasMove['bite'] || hasMove['clearsmog'] || hasMove['curse'] || hasMove['protect'] || hasMove['sleeptalk'] || species.name.includes('Rotom-')) && !isDoubles) { + } else if ((ability === 'Drizzle' || ability === 'Slow Start' || hasMove['bite'] || hasMove['clearsmog'] || + hasMove['curse'] || hasMove['protect'] || hasMove['sleeptalk'] || + species.name.includes('Rotom-')) && !isDoubles) { item = 'Leftovers'; } else if ((hasMove['endeavor'] || hasMove['flail'] || hasMove['reversal']) && ability !== 'Sturdy') { item = (ability === 'Defeatist') ? 'Expert Belt' : 'Focus Sash'; } else if (hasMove['outrage'] && counter.setupType) { item = 'Lum Berry'; - } else if (isDoubles && counter.damagingMoves.length >= 4 && species.baseStats.spe >= 60 && !hasMove['fakeout'] && !hasMove['flamecharge'] && !hasMove['suckerpunch'] && ability !== 'Multiscale' && ability !== 'Sturdy') { + } else if (isDoubles && + counter.damagingMoves.length >= 4 && species.baseStats.spe >= 60 && + !hasMove['fakeout'] && !hasMove['flamecharge'] && !hasMove['suckerpunch'] && + ability !== 'Multiscale' && ability !== 'Sturdy') { item = 'Life Orb'; } else if (isDoubles && this.dex.getEffectiveness('Ice', species) >= 2) { item = 'Yache Berry'; @@ -929,7 +1071,8 @@ class RandomGen7Teams extends RandomTeams { item = 'Charti Berry'; } else if (isDoubles && this.dex.getEffectiveness('Fire', species) >= 2) { item = 'Occa Berry'; - } else if (isDoubles && this.dex.getImmunity('Fighting', species) && this.dex.getEffectiveness('Fighting', species) >= 2) { + } else if (isDoubles && + this.dex.getImmunity('Fighting', species) && this.dex.getEffectiveness('Fighting', species) >= 2) { item = 'Chople Berry'; } else if (hasMove['substitute']) { item = counter.damagingMoves.length > 2 && !!counter['drain'] ? 'Life Orb' : 'Leftovers'; @@ -937,7 +1080,9 @@ class RandomGen7Teams extends RandomTeams { item = 'Air Balloon'; } else if ((ability === 'Iron Barbs' || ability === 'Rough Skin') && this.randomChance(1, 2)) { item = 'Rocky Helmet'; - } else if (counter.Physical + counter.Special >= 4 && species.baseStats.spd >= 50 && species.baseStats.hp + species.baseStats.def + species.baseStats.spd >= 235) { + } else if (counter.Physical + counter.Special >= 4 && + species.baseStats.spd >= 50 && + species.baseStats.hp + species.baseStats.def + species.baseStats.spd >= 235) { item = 'Assault Vest'; } else if ((species.name === 'Latias' || species.name === 'Latios') && !!counter['Dragon'] && !!counter['Psychic']) { item = 'Soul Dew'; @@ -945,9 +1090,12 @@ class RandomGen7Teams extends RandomTeams { item = 'Lustrous Orb'; } else if (counter.damagingMoves.length >= 4) { item = (!!counter['Dragon'] || !!counter['Dark'] || !!counter['Normal']) ? 'Life Orb' : 'Expert Belt'; - } else if (counter.damagingMoves.length >= 3 && !!counter['speedsetup'] && species.baseStats.hp + species.baseStats.def + species.baseStats.spd >= 300) { + } else if (counter.damagingMoves.length >= 3 && !!counter['speedsetup'] && + species.baseStats.hp + species.baseStats.def + species.baseStats.spd >= 300) { item = 'Weakness Policy'; - } else if (isLead && ability !== 'Regenerator' && ability !== 'Sturdy' && !counter['recoil'] && !counter['recovery'] && species.baseStats.hp + species.baseStats.def + species.baseStats.spd <= 275) { + } else if (isLead && ability !== 'Regenerator' && ability !== 'Sturdy' && + !counter['recoil'] && !counter['recovery'] && + species.baseStats.hp + species.baseStats.def + species.baseStats.spd <= 275) { item = 'Focus Sash'; // This is the "REALLY can't think of a good item" cutoff @@ -959,8 +1107,12 @@ class RandomGen7Teams extends RandomTeams { item = 'Custap Berry'; } else if (ability === 'Super Luck') { item = 'Scope Lens'; - } else if (counter.damagingMoves.length >= 3 && ability !== 'Sturdy' && !hasMove['acidspray'] && !hasMove['dragontail'] && !hasMove['foulplay'] && !hasMove['rapidspin'] && !hasMove['superfang'] && !hasMove['uturn']) { - if (!!counter['speedsetup'] || hasMove['trickroom'] || species.baseStats.spe > 40 && species.baseStats.hp + species.baseStats.def + species.baseStats.spd <= 275) item = 'Life Orb'; + } else if (counter.damagingMoves.length >= 3 && + ability !== 'Sturdy' && !hasMove['acidspray'] && !hasMove['dragontail'] && !hasMove['foulplay'] && + !hasMove['rapidspin'] && !hasMove['superfang'] && !hasMove['uturn']) { + if (!!counter['speedsetup'] || hasMove['trickroom'] || + species.baseStats.spe > 40 && + species.baseStats.hp + species.baseStats.def + species.baseStats.spd <= 275) item = 'Life Orb'; } // For Trick / Switcheroo @@ -968,21 +1120,19 @@ class RandomGen7Teams extends RandomTeams { item = 'Black Sludge'; } - let level; + let level: number; if (!isDoubles) { - /** @type {{[tier: string]: number}} */ - const levelScale = { + const levelScale: {[tier: string]: number} = { uber: 78, ou: 80, uu: 82, ru: 84, nu: 86, pu: 88, }; - /** @type {{[forme: string]: number}} */ - const customScale = { + const customScale: {[forme: string]: number} = { // Banned Ability Dugtrio: 82, Gothitelle: 82, Pelipper: 84, Politoed: 84, Wobbuffet: 82, // Holistic judgement Castform: 100, Delibird: 100, Spinda: 100, Unown: 100, }; - let tier = toID(species.tier).replace('bl', ''); + const tier = toID(species.tier).replace('bl', ''); level = levelScale[tier] || (species.nfe ? 90 : 80); if (customScale[forme]) level = customScale[forme]; @@ -998,7 +1148,7 @@ class RandomGen7Teams extends RandomTeams { let bst = baseStats.hp + baseStats.atk + baseStats.def + baseStats.spa + baseStats.spd + baseStats.spe; // Adjust levels of mons based on abilities (Pure Power, Sheer Force, etc.) and also Eviolite // For the stat boosted, treat the Pokemon's base stat as if it were multiplied by the boost. (Actual effective base stats are higher.) - let speciesAbility = (baseSpecies === species ? ability : species.abilities[0]); + const speciesAbility = (baseSpecies === species ? ability : species.abilities[0]); if (speciesAbility === 'Huge Power' || speciesAbility === 'Pure Power') { bst += baseStats.atk; } else if (speciesAbility === 'Parental Bond') { @@ -1021,13 +1171,15 @@ class RandomGen7Teams extends RandomTeams { } // Prepare optimal HP - let srWeakness = this.dex.getEffectiveness('Rock', species); + const srWeakness = this.dex.getEffectiveness('Rock', species); while (evs.hp > 1) { - let hp = Math.floor(Math.floor(2 * species.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10); + const hp = Math.floor(Math.floor(2 * species.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10); if (hasMove['substitute'] && hasMove['reversal']) { // Reversal users should be able to use four Substitutes if (hp % 4 > 0) break; - } else if (hasMove['substitute'] && (item === 'Petaya Berry' || item === 'Sitrus Berry' || ability === 'Power Construct' && item !== 'Leftovers')) { + } else if (hasMove['substitute'] && + (item === 'Petaya Berry' || item === 'Sitrus Berry' || + ability === 'Power Construct' && item !== 'Leftovers')) { // Three Substitutes should activate Petaya Berry for Dedenne // Two Substitutes should activate Sitrus Berry or Power Construct if (hp % 4 === 0) break; @@ -1071,58 +1223,63 @@ class RandomGen7Teams extends RandomTeams { }; } - /** - * @param {Species} species - * @param {number} slot - * @param {RandomTeamsTypes.FactoryTeamDetails} teamData - * @param {string} tier - * @return {RandomTeamsTypes.RandomFactorySet | false} - */ - randomFactorySet(species, slot, teamData, tier) { - let id = toID(species.name); - // let flags = this.randomFactorySets[tier][id].flags; - let setList = this.randomFactorySets[tier][id].sets; + randomFactorySet( + species: Species, teamData: RandomTeamsTypes.FactoryTeamDetails, tier: string + ): RandomTeamsTypes.RandomFactorySet | null { + const id = toID(species.name); + // const flags = this.randomFactorySets[tier][id].flags; + const setList = this.randomFactorySets[tier][id].sets; - /**@type {{[k: string]: number}} */ - let itemsMax = {choicespecs: 1, choiceband: 1, choicescarf: 1}; - /**@type {{[k: string]: number}} */ - let movesMax = {rapidspin: 1, batonpass: 1, stealthrock: 1, defog: 1, spikes: 1, toxicspikes: 1}; - let requiredMoves = {stealthrock: 'hazardSet', rapidspin: 'hazardClear', defog: 'hazardClear'}; - let weatherAbilitiesRequire = { + const itemsMax: {[k: string]: number} = { + choicespecs: 1, + choiceband: 1, + choicescarf: 1, + }; + const movesMax: {[k: string]: number} = { + rapidspin: 1, + batonpass: 1, + stealthrock: 1, + defog: 1, + spikes: 1, + toxicspikes: 1, + }; + const requiredMoves: {[k: string]: string} = { + stealthrock: 'hazardSet', + rapidspin: 'hazardClear', + defog: 'hazardClear', + }; + const weatherAbilitiesRequire: {[k: string]: string} = { hydration: 'raindance', swiftswim: 'raindance', leafguard: 'sunnyday', solarpower: 'sunnyday', chlorophyll: 'sunnyday', sandforce: 'sandstorm', sandrush: 'sandstorm', sandveil: 'sandstorm', slushrush: 'hail', snowcloak: 'hail', }; - let weatherAbilities = ['drizzle', 'drought', 'snowwarning', 'sandstream']; + const weatherAbilities = ['drizzle', 'drought', 'snowwarning', 'sandstream']; // Build a pool of eligible sets, given the team partners // Also keep track of sets with moves the team requires - /**@type {{set: AnyObject, moveVariants?: number[]}[]} */ - let effectivePool = []; - let priorityPool = []; + let effectivePool: {set: AnyObject, moveVariants?: number[]}[] = []; + const priorityPool = []; for (const curSet of setList) { - let item = this.dex.getItem(curSet.item); + const item = this.dex.getItem(curSet.item); if (teamData.megaCount > 0 && item.megaStone) continue; // reject 2+ mega stones if (teamData.zCount && teamData.zCount > 0 && item.zMove) continue; // reject 2+ Z stones if (itemsMax[item.id] && teamData.has[item.id] >= itemsMax[item.id]) continue; - let ability = this.dex.getAbility(curSet.ability); - // @ts-ignore + const ability = this.dex.getAbility(curSet.ability); if (weatherAbilitiesRequire[ability.id] && teamData.weather !== weatherAbilitiesRequire[ability.id]) continue; if (teamData.weather && weatherAbilities.includes(ability.id)) continue; // reject 2+ weather setters let reject = false; let hasRequiredMove = false; - let curSetVariants = []; + const curSetVariants = []; for (const move of curSet.moves) { - let variantIndex = this.random(move.length); - let moveId = toID(move[variantIndex]); + const variantIndex = this.random(move.length); + const moveId = toID(move[variantIndex]); if (movesMax[moveId] && teamData.has[moveId] >= movesMax[moveId]) { reject = true; break; } - // @ts-ignore if (requiredMoves[moveId] && !teamData.has[requiredMoves[moveId]]) { hasRequiredMove = true; } @@ -1135,21 +1292,21 @@ class RandomGen7Teams extends RandomTeams { if (priorityPool.length) effectivePool = priorityPool; if (!effectivePool.length) { - if (!teamData.forceResult) return false; + if (!teamData.forceResult) return null; for (const curSet of setList) { effectivePool.push({set: curSet}); } } - let setData = this.sample(effectivePool); - let moves = []; + const setData = this.sample(effectivePool); + const moves = []; for (const [i, moveSlot] of setData.set.moves.entries()) { moves.push(setData.moveVariants ? moveSlot[setData.moveVariants[i]] : this.sample(moveSlot)); } - let item = Array.isArray(setData.set.item) ? this.sample(setData.set.item) : setData.set.item; - let ability = Array.isArray(setData.set.ability) ? this.sample(setData.set.ability) : setData.set.ability; - let nature = Array.isArray(setData.set.nature) ? this.sample(setData.set.nature) : setData.set.nature; + const item = Array.isArray(setData.set.item) ? this.sample(setData.set.item) : setData.set.item; + const ability = Array.isArray(setData.set.ability) ? this.sample(setData.set.ability) : setData.set.ability; + const nature = Array.isArray(setData.set.nature) ? this.sample(setData.set.nature) : setData.set.nature; return { name: setData.set.name || species.baseSpecies, @@ -1167,22 +1324,16 @@ class RandomGen7Teams extends RandomTeams { }; } - /** - * @param {PlayerOptions} [side] - * @param {number} [depth] - * @return {RandomTeamsTypes.RandomFactorySet[]} - */ - randomFactoryTeam(side, depth = 0) { - let forceResult = (depth >= 4); + randomFactoryTeam(side: PlayerOptions, depth = 0): RandomTeamsTypes.RandomFactorySet[] { + const forceResult = (depth >= 4); // The teams generated depend on the tier choice in such a way that // no exploitable information is leaked from rolling the tier in getTeam(p1). - let availableTiers = ['Uber', 'OU', 'UU', 'RU', 'NU', 'PU', 'LC', 'Mono']; - if (!this.FactoryTier) this.FactoryTier = this.sample(availableTiers); - const chosenTier = this.FactoryTier; + const availableTiers = ['Uber', 'OU', 'UU', 'RU', 'NU', 'PU', 'LC', 'Mono']; + if (!this.factoryTier) this.factoryTier = this.sample(availableTiers); + const chosenTier = this.factoryTier; - /**@type {{[k: string]: number}} */ - const tierValues = { + const tierValues: {[k: string]: number} = { Uber: 5, OU: 4, UUBL: 4, UU: 3, RUBL: 3, @@ -1191,21 +1342,29 @@ class RandomGen7Teams extends RandomTeams { PU: 0, }; - let pokemon = []; - let pokemonPool = Object.keys(this.randomFactorySets[chosenTier]); + const pokemon = []; + const pokemonPool = Object.keys(this.randomFactorySets[chosenTier]); - let typePool = Object.keys(this.dex.data.TypeChart); + const typePool = Object.keys(this.dex.data.TypeChart); const type = this.sample(typePool); - /**@type {TeamData} */ - let teamData = {typeCount: {}, typeComboCount: {}, baseFormes: {}, megaCount: 0, zCount: 0, has: {}, forceResult: forceResult, weaknesses: {}, resistances: {}}; - let requiredMoveFamilies = ['hazardSet', 'hazardClear']; - /**@type {{[k: string]: string}} */ - let requiredMoves = {stealthrock: 'hazardSet', rapidspin: 'hazardClear', defog: 'hazardClear'}; - /**@type {{[k: string]: string}} */ - let weatherAbilitiesSet = {drizzle: 'raindance', drought: 'sunnyday', snowwarning: 'hail', sandstream: 'sandstorm'}; - /**@type {{[k: string]: string[]}} */ - let resistanceAbilities = { + const teamData: TeamData = { + typeCount: {}, typeComboCount: {}, baseFormes: {}, megaCount: 0, zCount: 0, + has: {}, forceResult: forceResult, weaknesses: {}, resistances: {}, + }; + const requiredMoveFamilies = ['hazardSet', 'hazardClear']; + const requiredMoves: {[k: string]: string} = { + stealthrock: 'hazardSet', + rapidspin: 'hazardClear', + defog: 'hazardClear', + }; + const weatherAbilitiesSet: {[k: string]: string} = { + drizzle: 'raindance', + drought: 'sunnyday', + snowwarning: 'hail', + sandstream: 'sandstorm', + }; + const resistanceAbilities: {[k: string]: string[]} = { dryskin: ['Water'], waterabsorb: ['Water'], stormdrain: ['Water'], flashfire: ['Fire'], heatproof: ['Fire'], lightningrod: ['Electric'], motordrive: ['Electric'], voltabsorb: ['Electric'], @@ -1215,13 +1374,16 @@ class RandomGen7Teams extends RandomTeams { }; while (pokemonPool.length && pokemon.length < 6) { - let species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); + const species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); if (!species.exists) continue; // Lessen the need of deleting sets of Pokemon after tier shifts - if (chosenTier in tierValues && species.tier in tierValues && tierValues[species.tier] > tierValues[chosenTier]) continue; + if ( + chosenTier in tierValues && species.tier in tierValues && + tierValues[species.tier] > tierValues[chosenTier] + ) continue; - let speciesFlags = this.randomFactorySets[chosenTier][species.id].flags; + const speciesFlags = this.randomFactorySets[chosenTier][species.id].flags; // Limit to one of each species (Species Clause) if (teamData.baseFormes[species.baseSpecies]) continue; @@ -1229,16 +1391,16 @@ class RandomGen7Teams extends RandomTeams { // Limit the number of Megas to one if (teamData.megaCount >= 1 && speciesFlags.megaOnly) continue; - let set = this.randomFactorySet(species, pokemon.length, teamData, chosenTier); + const set = this.randomFactorySet(species, teamData, chosenTier); if (!set) continue; - let itemData = this.dex.getItem(set.item); + const itemData = this.dex.getItem(set.item); // Actually limit the number of Megas to one if (teamData.megaCount >= 1 && itemData.megaStone) continue; // Limit the number of Z moves to one - if (teamData.zCount >= 1 && itemData.zMove) continue; + if (teamData.zCount && teamData.zCount >= 1 && itemData.zMove) continue; let types = species.types; @@ -1246,7 +1408,7 @@ class RandomGen7Teams extends RandomTeams { if (chosenTier === 'Mono') { // Prevents Mega Evolutions from breaking the type limits if (itemData.megaStone) { - let megaSpecies = this.dex.getSpecies(itemData.megaStone); + const megaSpecies = this.dex.getSpecies(itemData.megaStone); if (types.length > megaSpecies.types.length) types = [species.types[0]]; // Only check the second type because a Mega Evolution should always share the first type with its base forme. if (megaSpecies.types[1] && types[1] && megaSpecies.types[1] !== types[1]) { @@ -1255,10 +1417,10 @@ class RandomGen7Teams extends RandomTeams { } if (!types.includes(type)) continue; } else { - // If not Monotype, limit to two of each type + // If not Monotype, limit to two of each type let skip = false; - for (const type of types) { - if (teamData.typeCount[type] > 1 && this.randomChance(4, 5)) { + for (const typeName of types) { + if (teamData.typeCount[typeName] > 1 && this.randomChance(4, 5)) { skip = true; break; } @@ -1276,13 +1438,13 @@ class RandomGen7Teams extends RandomTeams { // Okay, the set passes, add it to our team pokemon.push(set); - let typeCombo = types.slice().sort().join(); + const typeCombo = types.slice().sort().join(); // Now that our Pokemon has passed all checks, we can update team data: - for (const type of types) { - if (type in teamData.typeCount) { - teamData.typeCount[type]++; + for (const typeName of types) { + if (typeName in teamData.typeCount) { + teamData.typeCount[typeName]++; } else { - teamData.typeCount[type] = 1; + teamData.typeCount[typeName] = 1; } } teamData.typeComboCount[typeCombo] = 1; @@ -1290,20 +1452,23 @@ class RandomGen7Teams extends RandomTeams { teamData.baseFormes[species.baseSpecies] = 1; if (itemData.megaStone) teamData.megaCount++; - if (itemData.zMove) teamData.zCount++; + if (itemData.zMove) { + if (!teamData.zCount) teamData.zCount = 0; + teamData.zCount++; + } if (itemData.id in teamData.has) { teamData.has[itemData.id]++; } else { teamData.has[itemData.id] = 1; } - let abilityData = this.dex.getAbility(set.ability); + const abilityData = this.dex.getAbility(set.ability); if (abilityData.id in weatherAbilitiesSet) { teamData.weather = weatherAbilitiesSet[abilityData.id]; } for (const move of set.moves) { - let moveId = toID(move); + const moveId = toID(move); if (moveId in teamData.has) { teamData.has[moveId]++; } else { @@ -1314,16 +1479,19 @@ class RandomGen7Teams extends RandomTeams { } } - for (let typeName in this.dex.data.TypeChart) { + for (const typeName in this.dex.data.TypeChart) { // Cover any major weakness (3+) with at least one resistance if (teamData.resistances[typeName] >= 1) continue; - if (resistanceAbilities[abilityData.id] && resistanceAbilities[abilityData.id].includes(typeName) || !this.dex.getImmunity(typeName, types)) { + if ( + resistanceAbilities[abilityData.id] && resistanceAbilities[abilityData.id].includes(typeName) || + !this.dex.getImmunity(typeName, types) + ) { // Heuristic: assume that Pokémon with these abilities don't have (too) negative typing. teamData.resistances[typeName] = (teamData.resistances[typeName] || 0) + 1; if (teamData.resistances[typeName] >= 1) teamData.weaknesses[typeName] = 0; continue; } - let typeMod = this.dex.getEffectiveness(typeName, types); + const typeMod = this.dex.getEffectiveness(typeName, types); if (typeMod < 0) { teamData.resistances[typeName] = (teamData.resistances[typeName] || 0) + 1; if (teamData.resistances[typeName] >= 1) teamData.weaknesses[typeName] = 0; @@ -1339,48 +1507,47 @@ class RandomGen7Teams extends RandomTeams { for (const requiredFamily of requiredMoveFamilies) { if (!teamData.has[requiredFamily]) return this.randomFactoryTeam(side, ++depth); } - for (let type in teamData.weaknesses) { - if (teamData.weaknesses[type] >= 3) return this.randomFactoryTeam(side, ++depth); + for (const typeName in teamData.weaknesses) { + if (teamData.weaknesses[typeName] >= 3) return this.randomFactoryTeam(side, ++depth); } } return pokemon; } - /** - * @param {Species} species - * @param {number} slot - * @param {RandomTeamsTypes.FactoryTeamDetails} teamData - * @return {RandomTeamsTypes.RandomFactorySet | false} - */ - randomBSSFactorySet(species, slot, teamData) { - let id = toID(species.name); - // let flags = this.randomBSSFactorySets[tier][id].flags; - let setList = this.randomBSSFactorySets[id].sets; + randomBSSFactorySet( + species: Species, teamData: RandomTeamsTypes.FactoryTeamDetails + ): RandomTeamsTypes.RandomFactorySet | null { + const id = toID(species.name); + // const flags = this.randomBSSFactorySets[tier][id].flags; + const setList = this.randomBSSFactorySets[id].sets; - /**@type {{[k: string]: number}} */ - let movesMax = {batonpass: 1, stealthrock: 1, spikes: 1, toxicspikes: 1, doubleedge: 1, trickroom: 1}; - /**@type {{[k: string]: string}} */ - let requiredMoves = {}; - /**@type {{[k: string]: string}} */ - let weatherAbilitiesRequire = { + const movesMax: {[k: string]: number} = { + batonpass: 1, + stealthrock: 1, + spikes: 1, + toxicspikes: 1, + doubleedge: 1, + trickroom: 1, + }; + const requiredMoves: {[k: string]: number} = {}; + const weatherAbilitiesRequire: {[k: string]: string} = { swiftswim: 'raindance', sandrush: 'sandstorm', sandveil: 'sandstorm', }; - let weatherAbilities = ['drizzle', 'drought', 'snowwarning', 'sandstream']; + const weatherAbilities = ['drizzle', 'drought', 'snowwarning', 'sandstream']; // Build a pool of eligible sets, given the team partners // Also keep track of sets with moves the team requires - /**@type {{set: AnyObject, moveVariants?: number[], itemVariants?: number, abilityVariants?: number}[]} */ - let effectivePool = []; - let priorityPool = []; + let effectivePool: {set: AnyObject, moveVariants?: number[], itemVariants?: number, abilityVariants?: number}[] = []; + const priorityPool = []; for (const curSet of setList) { - let item = this.dex.getItem(curSet.item); + const item = this.dex.getItem(curSet.item); if (teamData.megaCount > 1 && item.megaStone) continue; // reject 3+ mega stones if (teamData.zCount && teamData.zCount > 1 && item.zMove) continue; // reject 3+ Z stones if (teamData.has[item.id]) continue; // Item clause - let ability = this.dex.getAbility(curSet.ability); + const ability = this.dex.getAbility(curSet.ability); if (weatherAbilitiesRequire[ability.id] && teamData.weather !== weatherAbilitiesRequire[ability.id]) continue; if (teamData.weather && weatherAbilities.includes(ability.id)) continue; // reject 2+ weather setters @@ -1388,10 +1555,10 @@ class RandomGen7Teams extends RandomTeams { let reject = false; let hasRequiredMove = false; - let curSetVariants = []; + const curSetVariants = []; for (const move of curSet.moves) { - let variantIndex = this.random(move.length); - let moveId = toID(move[variantIndex]); + const variantIndex = this.random(move.length); + const moveId = toID(move[variantIndex]); if (movesMax[moveId] && teamData.has[moveId] >= movesMax[moveId]) { reject = true; break; @@ -1408,14 +1575,14 @@ class RandomGen7Teams extends RandomTeams { if (priorityPool.length) effectivePool = priorityPool; if (!effectivePool.length) { - if (!teamData.forceResult) return false; + if (!teamData.forceResult) return null; for (const curSet of setList) { effectivePool.push({set: curSet}); } } - let setData = this.sample(effectivePool); - let moves = []; + const setData = this.sample(effectivePool); + const moves = []; for (const [i, moveSlot] of setData.set.moves.entries()) { moves.push(setData.moveVariants ? moveSlot[setData.moveVariants[i]] : this.sample(moveSlot)); } @@ -1436,28 +1603,26 @@ class RandomGen7Teams extends RandomTeams { }; } - /** - * @param {PlayerOptions} [side] - * @param {number} [depth] - * @return {RandomTeamsTypes.RandomFactorySet[]} - */ - randomBSSFactoryTeam(side, depth = 0) { - let forceResult = (depth >= 4); + randomBSSFactoryTeam(side: PlayerOptions, depth = 0): RandomTeamsTypes.RandomFactorySet[] { + const forceResult = (depth >= 4); - let pokemon = []; + const pokemon = []; - let pokemonPool = Object.keys(this.randomBSSFactorySets); + const pokemonPool = Object.keys(this.randomBSSFactorySets); - /**@type {TeamData} */ - let teamData = {typeCount: {}, typeComboCount: {}, baseFormes: {}, megaCount: 0, zCount: 0, eeveeLimCount: 0, has: {}, forceResult: forceResult, weaknesses: {}, resistances: {}}; - /**@type {string[]} */ - let requiredMoveFamilies = []; - /**@type {{[k: string]: string}} */ - let requiredMoves = {}; - /**@type {{[k: string]: string}} */ - let weatherAbilitiesSet = {drizzle: 'raindance', drought: 'sunnyday', snowwarning: 'hail', sandstream: 'sandstorm'}; - /**@type {{[k: string]: string[]}} */ - let resistanceAbilities = { + const teamData: TeamData = { + typeCount: {}, typeComboCount: {}, baseFormes: {}, megaCount: 0, zCount: 0, + eeveeLimCount: 0, has: {}, forceResult: forceResult, weaknesses: {}, resistances: {}, + }; + const requiredMoveFamilies: string[] = []; + const requiredMoves: {[k: string]: string} = {}; + const weatherAbilitiesSet: {[k: string]: string} = { + drizzle: 'raindance', + drought: 'sunnyday', + snowwarning: 'hail', + sandstream: 'sandstorm', + }; + const resistanceAbilities: {[k: string]: string[]} = { waterabsorb: ['Water'], flashfire: ['Fire'], lightningrod: ['Electric'], voltabsorb: ['Electric'], @@ -1466,19 +1631,19 @@ class RandomGen7Teams extends RandomTeams { }; while (pokemonPool.length && pokemon.length < 6) { - let species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); + const species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); if (!species.exists) continue; - let speciesFlags = this.randomBSSFactorySets[species.id].flags; + const speciesFlags = this.randomBSSFactorySets[species.id].flags; // Limit to one of each species (Species Clause) if (teamData.baseFormes[species.baseSpecies]) continue; // Limit the number of Megas + Z-moves to 3 - if (teamData.megaCount + teamData.zCount >= 3 && speciesFlags.megaOnly) continue; + if (teamData.megaCount + (teamData.zCount ? teamData.zCount : 0) >= 3 && speciesFlags.megaOnly) continue; // Limit 2 of any type - let types = species.types; + const types = species.types; let skip = false; for (const type of types) { if (teamData.typeCount[type] > 1 && this.randomChance(4, 5)) { @@ -1489,10 +1654,13 @@ class RandomGen7Teams extends RandomTeams { if (skip) continue; // Restrict Eevee with certain Pokemon - if (speciesFlags.limEevee) teamData.eeveeLimCount++; - if (teamData.eeveeLimCount >= 1 && speciesFlags.limEevee) continue; + if (speciesFlags.limEevee) { + if (!teamData.eeveeLimCount) teamData.eeveeLimCount = 0; + teamData.eeveeLimCount++; + } + if (teamData.eeveeLimCount && teamData.eeveeLimCount >= 1 && speciesFlags.limEevee) continue; - let set = this.randomBSSFactorySet(species, pokemon.length, teamData); + const set = this.randomBSSFactorySet(species, teamData); if (!set) continue; // Limit 1 of any type combination @@ -1519,18 +1687,21 @@ class RandomGen7Teams extends RandomTeams { teamData.baseFormes[species.baseSpecies] = 1; // Limit Mega and Z-move - let itemData = this.dex.getItem(set.item); + const itemData = this.dex.getItem(set.item); if (itemData.megaStone) teamData.megaCount++; - if (itemData.zMove) teamData.zCount++; + if (itemData.zMove) { + if (!teamData.zCount) teamData.zCount = 0; + teamData.zCount++; + } teamData.has[itemData.id] = 1; - let abilityData = this.dex.getAbility(set.ability); + const abilityData = this.dex.getAbility(set.ability); if (abilityData.id in weatherAbilitiesSet) { teamData.weather = weatherAbilitiesSet[abilityData.id]; } for (const move of set.moves) { - let moveId = toID(move); + const moveId = toID(move); if (moveId in teamData.has) { teamData.has[moveId]++; } else { @@ -1541,16 +1712,19 @@ class RandomGen7Teams extends RandomTeams { } } - for (let typeName in this.dex.data.TypeChart) { + for (const typeName in this.dex.data.TypeChart) { // Cover any major weakness (3+) with at least one resistance if (teamData.resistances[typeName] >= 1) continue; - if (resistanceAbilities[abilityData.id] && resistanceAbilities[abilityData.id].includes(typeName) || !this.dex.getImmunity(typeName, types)) { + if ( + resistanceAbilities[abilityData.id] && resistanceAbilities[abilityData.id].includes(typeName) || + !this.dex.getImmunity(typeName, types) + ) { // Heuristic: assume that Pokémon with these abilities don't have (too) negative typing. teamData.resistances[typeName] = (teamData.resistances[typeName] || 0) + 1; if (teamData.resistances[typeName] >= 1) teamData.weaknesses[typeName] = 0; continue; } - let typeMod = this.dex.getEffectiveness(typeName, types); + const typeMod = this.dex.getEffectiveness(typeName, types); if (typeMod < 0) { teamData.resistances[typeName] = (teamData.resistances[typeName] || 0) + 1; if (teamData.resistances[typeName] >= 1) teamData.weaknesses[typeName] = 0; @@ -1566,7 +1740,7 @@ class RandomGen7Teams extends RandomTeams { for (const requiredFamily of requiredMoveFamilies) { if (!teamData.has[requiredFamily]) return this.randomBSSFactoryTeam(side, ++depth); } - for (let type in teamData.weaknesses) { + for (const type in teamData.weaknesses) { if (teamData.weaknesses[type] >= 3) return this.randomBSSFactoryTeam(side, ++depth); } } @@ -1575,4 +1749,4 @@ class RandomGen7Teams extends RandomTeams { } } -module.exports = RandomGen7Teams; +export default RandomGen7Teams; diff --git a/data/mods/gen7/rulesets.js b/data/mods/gen7/rulesets.ts similarity index 90% rename from data/mods/gen7/rulesets.js rename to data/mods/gen7/rulesets.ts index fdf2718cbf..243f212122 100644 --- a/data/mods/gen7/rulesets.js +++ b/data/mods/gen7/rulesets.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedFormatsData}} */ -let BattleFormats = { +export const BattleFormats: {[k: string]: ModdedFormatsData} = { standard: { inherit: true, ruleset: ['Obtainable', 'Team Preview', 'Sleep Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Moody Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod'], @@ -30,5 +27,3 @@ let BattleFormats = { }, }, }; - -exports.BattleFormats = BattleFormats; diff --git a/data/mods/gen7/scripts.js b/data/mods/gen7/scripts.js deleted file mode 100644 index 7fbd723f49..0000000000 --- a/data/mods/gen7/scripts.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -/** @type {ModdedBattleScriptsData} */ -let BattleScripts = { - gen: 7, -}; - -exports.BattleScripts = BattleScripts; diff --git a/data/mods/gen7/scripts.ts b/data/mods/gen7/scripts.ts new file mode 100644 index 0000000000..01dd5afd40 --- /dev/null +++ b/data/mods/gen7/scripts.ts @@ -0,0 +1,3 @@ +export const BattleScripts: ModdedBattleScriptsData = { + gen: 7, +}; diff --git a/data/mods/gennext/abilities.js b/data/mods/gennext/abilities.ts similarity index 98% rename from data/mods/gennext/abilities.js rename to data/mods/gennext/abilities.ts index 19d024b3be..74734bba41 100644 --- a/data/mods/gennext/abilities.js +++ b/data/mods/gennext/abilities.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedAbilityData}} */ -let BattleAbilities = { +export const BattleAbilities: {[k: string]: ModdedAbilityData} = { swiftswim: { inherit: true, onModifySpe(spe, pokemon) { @@ -42,7 +39,7 @@ let BattleAbilities = { inherit: true, onModifyMove(move) { if (move.weather) { - let weather = move.weather; + const weather = move.weather; move.weather = ''; move.onHit = function (target, source) { this.field.setWeather(weather, source, this.dex.getAbility('forecast')); @@ -171,9 +168,7 @@ let BattleAbilities = { inherit: true, onModifyMove(move) { if (move.id === 'sunnyday') { - /**@type {string} */ - // @ts-ignore - let weather = move.weather; + const weather = move.weather as string; move.weather = ''; move.onHit = function (target, source) { this.field.setWeather(weather, source, this.dex.getAbility('flowergift')); @@ -296,7 +291,7 @@ let BattleAbilities = { clearbody: { inherit: true, onBoost(boost, target, source) { - for (let i in boost) { + for (const i in boost) { // @ts-ignore if (boost[i] < 0) { // @ts-ignore @@ -310,7 +305,7 @@ let BattleAbilities = { whitesmoke: { inherit: true, onBoost(boost, target, source) { - for (let i in boost) { + for (const i in boost) { // @ts-ignore if (boost[i] < 0) { // @ts-ignore @@ -612,7 +607,9 @@ let BattleAbilities = { parentalbond: { inherit: true, onModifyMove(move, pokemon, target) { - if (move.category !== 'Status' && !move.selfdestruct && !move.multihit && ((target.side && target.side.active.length < 2) || ['any', 'normal', 'randomNormal'].includes(move.target))) { + if (move.category === 'Status' || move.selfdestruct || move.multihit) return; + // singles, or single-target move + if (target.side.active.length < 2 || ['any', 'normal', 'randomNormal'].includes(move.target)) { move.multihit = 2; move.accuracy = true; pokemon.addVolatile('parentalbond'); diff --git a/data/mods/gennext/formats-data.js b/data/mods/gennext/formats-data.ts similarity index 60% rename from data/mods/gennext/formats-data.js rename to data/mods/gennext/formats-data.ts index 237761fd3b..0bb56e4553 100644 --- a/data/mods/gennext/formats-data.js +++ b/data/mods/gennext/formats-data.ts @@ -1,67 +1,62 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesFormatsData}} */ -let BattleFormatsData = { +export const BattleFormatsData: {[k: string]: ModdedSpeciesFormatsData} = { aegislash: { inherit: true, - tier: 'OU', + tier: "OU", }, blaziken: { inherit: true, - tier: 'OU', + tier: "OU", }, blazikenmega: { inherit: true, - tier: 'OU', + tier: "OU", }, genesect: { inherit: true, - tier: 'OU', + tier: "OU", }, gengarmega: { inherit: true, - tier: 'OU', + tier: "OU", }, greninja: { inherit: true, - tier: 'OU', + tier: "OU", }, kangaskhanmega: { inherit: true, - tier: 'OU', + tier: "OU", }, landorus: { inherit: true, - tier: 'OU', + tier: "OU", }, mawilemega: { inherit: true, - tier: 'OU', + tier: "OU", }, salamencemega: { inherit: true, - tier: 'OU', + tier: "OU", }, deoxysdefense: { inherit: true, - tier: 'Uber', + tier: "Uber", }, deoxysspeed: { inherit: true, - tier: 'Uber', + tier: "Uber", }, hoopaunbound: { inherit: true, - tier: 'OU', + tier: "OU", }, kyurem: { inherit: true, - tier: 'Uber', + tier: "Uber", }, kyuremblack: { inherit: true, - tier: 'Uber', + tier: "Uber", }, }; - -exports.BattleFormatsData = BattleFormatsData; diff --git a/data/mods/gennext/items.js b/data/mods/gennext/items.ts similarity index 87% rename from data/mods/gennext/items.js rename to data/mods/gennext/items.ts index be28a8aa48..ddb71efcbb 100644 --- a/data/mods/gennext/items.js +++ b/data/mods/gennext/items.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedItemData}} */ -let BattleItems = { +export const BattleItems: {[k: string]: ModdedItemData} = { burndrive: { inherit: true, onBasePower(basePower, user, target, move) {}, @@ -74,7 +71,7 @@ let BattleItems = { focusband: { inherit: true, onDamage(damage, target, source, effect) { - let types = target.getTypes(); + const types = target.getTypes(); if (types.length === 1 && types[0] === 'Fighting' && effect && effect.effectType === 'Move' && target.useItem()) { @@ -92,7 +89,7 @@ let BattleItems = { inherit: true, onBasePower(basePower, user, target, move) { if (move.category === 'Special') { - let types = user.getTypes(); + const types = user.getTypes(); if (types.length === 1 && types[0] === 'Psychic') { return basePower * 1.2; } @@ -106,7 +103,7 @@ let BattleItems = { inherit: true, onBasePower(basePower, user, target, move) { if (move.category === 'Physical') { - let types = user.getTypes(); + const types = user.getTypes(); if (types.length === 1 && types[0] === 'Fighting') { return basePower * 1.2; } @@ -149,16 +146,16 @@ let BattleItems = { } }, onFoeBasePower(basePower, attacker, defender, move) { - let GossamerWingUsers = ["Butterfree", "Masquerain", "Beautifly", "Mothim", "Vivillon"]; + const GossamerWingUsers = ["Butterfree", "Masquerain", "Beautifly", "Mothim", "Vivillon"]; if (GossamerWingUsers.includes(defender.species.name)) { - if (move.type === 'Rock' || move.type === 'Electric' || move.type === 'Ice') { + if (['Rock', 'Electric', 'Ice'].includes(move.type)) { this.add('-message', "The attack was weakened by GoassamerWing!"); return basePower / 2; } } }, onDamage(damage, defender, attacker, effect) { - let GossamerWingUsers = ["Butterfree", "Masquerain", "Beautifly", "Mothim", "Vivillon"]; + const GossamerWingUsers = ["Butterfree", "Masquerain", "Beautifly", "Mothim", "Vivillon"]; if (GossamerWingUsers.includes(defender.species.name)) { if (effect && effect.id === 'stealthrock') { return damage / 2; @@ -166,8 +163,8 @@ let BattleItems = { } }, onAfterMoveSecondarySelf(source, target, move) { - let GossamerWingUsers = ["Butterfree", "Masquerain", "Beautifly", "Mothim", "Vivillon"]; - if (move && move.effectType === 'Move' && move.category === 'Status' && GossamerWingUsers.includes(source.species.name)) { + const GossamerWingUsers = ["Butterfree", "Masquerain", "Beautifly", "Mothim", "Vivillon"]; + if (move.effectType === 'Move' && move.category === 'Status' && GossamerWingUsers.includes(source.species.name)) { this.heal(source.baseMaxhp / 16); } }, @@ -179,5 +176,3 @@ let BattleItems = { desc: "Raises Farfetch'd's critical hit rate two stages.", }, }; - -exports.BattleItems = BattleItems; diff --git a/data/mods/gennext/moves.js b/data/mods/gennext/moves.ts similarity index 97% rename from data/mods/gennext/moves.js rename to data/mods/gennext/moves.ts index 1222030572..0755e0f23e 100644 --- a/data/mods/gennext/moves.js +++ b/data/mods/gennext/moves.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedMoveData}} */ -let BattleMovedex = { +export const BattleMovedex: {[k: string]: ModdedMoveData} = { /****************************************************************** Perfect accuracy moves: - base power increased to 90 @@ -165,7 +162,7 @@ let BattleMovedex = { return damage; } if (damage > target.volatiles['substitute'].hp) { - damage = /** @type {number} */ (target.volatiles['substitute'].hp); + damage = target.volatiles['substitute'].hp as number; } target.volatiles['substitute'].hp -= damage; source.lastDamage = damage; @@ -181,7 +178,7 @@ let BattleMovedex = { this.heal(Math.ceil(damage * move.drain[0] / move.drain[1]), source, target, 'drain'); } this.runEvent('AfterSubDamage', target, source, move, damage); - return 0; // hit + return this.HIT_SUBSTITUTE; }, onEnd(target) { this.add('-end', target, 'Substitute'); @@ -199,7 +196,7 @@ let BattleMovedex = { onTryHit(target, source, move) { if (target.volatiles.substitute || !move.flags['protect']) return; this.add('-activate', target, 'Protect'); - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -221,7 +218,7 @@ let BattleMovedex = { onTryHit(target, source, move) { if (target.volatiles.substitute || !move.flags['protect'] || move.category === 'Status') return; this.add('-activate', target, 'Protect'); - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -654,7 +651,7 @@ let BattleMovedex = { pokemon.removeVolatile('bidestall'); } }, - onBeforeMove(pokemon, target, move) { + onBeforeMove(pokemon, t, move) { if (this.effectData.duration === 1) { if (!this.effectData.totalDamage) { this.add('-end', pokemon, 'Bide'); @@ -662,12 +659,11 @@ let BattleMovedex = { return false; } this.add('-end', pokemon, 'Bide'); - let target = this.effectData.sourceSide.active[this.effectData.sourcePosition]; - let moveData = /** @type {ActiveMove} */ ({ + const target = this.effectData.sourceSide.active[this.effectData.sourcePosition]; + const moveData = { damage: this.effectData.totalDamage * 2, - }); - // @ts-ignore FIXME - this.moveHit(target, pokemon, 'bide', moveData); + } as unknown as ActiveMove; + this.moveHit(target, pokemon, this.dex.getActiveMove('bide'), moveData); return false; } this.add('-activate', pokemon, 'Bide'); @@ -757,15 +753,14 @@ let BattleMovedex = { if (pokemon.baseSpecies.name !== 'Meloetta' || pokemon.transformed) { return; } - /**@type {{[k: string]: string}} */ - let natureChange = { + const natureChange: {[k: string]: string} = { Modest: 'Adamant', Adamant: 'Modest', Timid: 'Jolly', Jolly: 'Timid', }; - let tmpAtkEVs; - let Atk2SpA; + let tmpAtkEVs: number; + let Atk2SpA: number; if (pokemon.species.id === 'meloettapirouette' && pokemon.formeChange('Meloetta', this.effect, false, '[msg]')) { tmpAtkEVs = pokemon.set.evs.atk; pokemon.set.evs.atk = pokemon.set.evs.spa; @@ -868,17 +863,16 @@ let BattleMovedex = { chance: 100, self: { onHit(target, source) { - let stats = []; - for (let stat in target.boosts) { - // @ts-ignore + const stats: BoostName[] = []; + let stat: BoostName; + for (stat in target.boosts) { if (stat !== 'accuracy' && stat !== 'evasion' && stat !== 'atk' && target.boosts[stat] < 6) { stats.push(stat); } } if (stats.length) { - let randomStat = this.sample(stats); - /**@type {{[k: string]: number}} */ - let boost = {}; + const randomStat = this.sample(stats); + const boost: SparseBoostsTable = {}; boost[randomStat] = 1; this.boost(boost); } else { @@ -902,17 +896,16 @@ let BattleMovedex = { chance: 100, self: { onHit(target, source) { - let stats = []; - for (let stat in target.boosts) { - // @ts-ignore + const stats: BoostName[] = []; + let stat: BoostName; + for (stat in target.boosts) { if (stat !== 'accuracy' && stat !== 'evasion' && stat !== 'atk' && target.boosts[stat] < 6) { stats.push(stat); } } if (stats.length) { - let randomStat = this.sample(stats); - /**@type {{[k: string]: number}} */ - let boost = {}; + const randomStat = this.sample(stats); + const boost: SparseBoostsTable = {}; boost[randomStat] = 1; this.boost(boost); } else { @@ -930,17 +923,16 @@ let BattleMovedex = { chance: 100, self: { onHit(target, source) { - let stats = []; - for (let stat in target.boosts) { - // @ts-ignore + const stats: BoostName[] = []; + let stat: BoostName; + for (stat in target.boosts) { if (stat !== 'accuracy' && stat !== 'evasion' && stat !== 'atk' && target.boosts[stat] < 6) { stats.push(stat); } } if (stats.length) { - let randomStat = this.sample(stats); - /**@type {{[k: string]: number}} */ - let boost = {}; + const randomStat = this.sample(stats); + const boost: SparseBoostsTable = {}; boost[randomStat] = 1; this.boost(boost); } else { @@ -962,7 +954,7 @@ let BattleMovedex = { avalanche: { inherit: true, basePowerCallback(pokemon, source) { - let lastAttackedBy = pokemon.getLastAttackedBy(); + const lastAttackedBy = pokemon.getLastAttackedBy(); if (lastAttackedBy) { if (lastAttackedBy.damage > 0 && lastAttackedBy.thisTurn) { this.debug('Boosted for getting hit by ' + lastAttackedBy.move); @@ -1106,7 +1098,9 @@ let BattleMovedex = { inherit: true, basePower: 80, onBasePower(power, user) { - let GossamerWingUsers = ["Butterfree", "Venomoth", "Masquerain", "Dustox", "Beautifly", "Mothim", "Lilligant", "Volcarona", "Vivillon"]; + const GossamerWingUsers = [ + "Butterfree", "Venomoth", "Masquerain", "Dustox", "Beautifly", "Mothim", "Lilligant", "Volcarona", "Vivillon", + ]; if (user.hasItem('stick') && GossamerWingUsers.includes(user.species.name)) { return power * 1.5; } @@ -1223,8 +1217,7 @@ let BattleMovedex = { onModifyMove(move) { switch (this.field.effectiveWeather()) { case 'sunnyday': - // @ts-ignore - move.secondary.chance = 60; + move.secondary!.chance = 60; break; } }, @@ -1236,8 +1229,7 @@ let BattleMovedex = { onModifyMove(move) { switch (this.field.effectiveWeather()) { case 'sunnyday': - // @ts-ignore - move.secondary.chance = 60; + move.secondary!.chance = 60; break; } }, @@ -1315,8 +1307,8 @@ let BattleMovedex = { this.add('-end', user, 'Leech Seed', '[from] move: Rapid Spin', '[of] ' + user); doubled = true; } - let sideConditions = ['spikes', 'toxicspikes', 'stealthrock']; - for (let condition in sideConditions) { + const sideConditions = ['spikes', 'toxicspikes', 'stealthrock']; + for (const condition of sideConditions) { if (user.side.removeSideCondition(condition)) { this.add('-sideend', user.side, this.dex.getEffect(condition).name, '[from] move: Rapid Spin', '[of] ' + user); doubled = true; @@ -2022,7 +2014,7 @@ let BattleMovedex = { accuracy: 100, onModifyMove(move, user) { if (user.illusion) { - let illusionMoves = user.illusion.moves.filter(move => this.dex.getMove(move).category !== 'Status'); + const illusionMoves = user.illusion.moves.filter(m => this.dex.getMove(m).category !== 'Status'); if (!illusionMoves.length) return; move.name = this.dex.getMove(this.sample(illusionMoves)).name; } @@ -2072,7 +2064,7 @@ let BattleMovedex = { secondary: { chance: 10, onHit(target, source) { - let result = this.random(3); + const result = this.random(3); if (result === 0) { target.trySetStatus('brn', source); } else if (result === 1) { diff --git a/data/mods/gennext/pokedex.js b/data/mods/gennext/pokedex.ts similarity index 68% rename from data/mods/gennext/pokedex.js rename to data/mods/gennext/pokedex.ts index 89c7c594d8..09b86e1e94 100644 --- a/data/mods/gennext/pokedex.js +++ b/data/mods/gennext/pokedex.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesData}} */ -let BattlePokedex = { +export const BattlePokedex: {[k: string]: ModdedSpeciesData} = { genesectdouse: { inherit: true, types: ["Bug", "Water"], @@ -19,5 +16,3 @@ let BattlePokedex = { types: ["Bug", "Ice"], }, }; - -exports.BattlePokedex = BattlePokedex; diff --git a/data/mods/gennext/scripts.js b/data/mods/gennext/scripts.ts similarity index 97% rename from data/mods/gennext/scripts.js rename to data/mods/gennext/scripts.ts index 3e2aae0dca..d4ff20c08a 100644 --- a/data/mods/gennext/scripts.js +++ b/data/mods/gennext/scripts.ts @@ -1,7 +1,5 @@ -'use strict'; - -/**@type {ModdedBattleScriptsData} */ -let BattleScripts = { +export const BattleScripts: ModdedBattleScriptsData = { + inherit: 'gen6', init() { this.modData('Pokedex', 'cherrimsunshine').types = ['Grass', 'Fire']; @@ -145,10 +143,8 @@ let BattleScripts = { this.modData('Pokedex', 'vespiquen').abilities['1'] = 'Swarm'; // Every hidden ability becomes released - for (let i in this.data.FormatsData) { + for (const i in this.data.FormatsData) { this.modData('FormatsData', i).unreleasedHidden = false; } }, }; - -exports.BattleScripts = BattleScripts; diff --git a/data/mods/gennext/statuses.js b/data/mods/gennext/statuses.ts similarity index 75% rename from data/mods/gennext/statuses.js rename to data/mods/gennext/statuses.ts index d8b5d4baeb..c696f10f7c 100644 --- a/data/mods/gennext/statuses.js +++ b/data/mods/gennext/statuses.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedPureEffectData}} */ -let BattleStatuses = { +export const BattleStatuses: {[k: string]: ModdedPureEffectData} = { frz: { name: 'frz', id: 'frz', @@ -38,9 +35,7 @@ let BattleStatuses = { return this.random(2, 4); }, onResidual(target) { - /**@type {Move} */ - // @ts-ignore - let move = target.lastMove; + const move = target.lastMove as Move; if (!move.self || (move.self !== true && move.self.volatileStatus !== 'lockedmove')) { // don't lock, and bypass confusion for calming delete target.volatiles['lockedmove']; @@ -53,8 +48,7 @@ let BattleStatuses = { target.addVolatile('confusion'); }, onLockMove(pokemon) { - // @ts-ignore - return pokemon.lastMove.id; + return pokemon.lastMove!.id; }, }, confusion: { @@ -79,7 +73,7 @@ let BattleStatuses = { pokemon.removeVolatile('confusion'); return; } - let damage = this.getDamage(pokemon, pokemon, 40); + const damage = this.getDamage(pokemon, pokemon, 40); if (typeof damage !== 'number') throw new Error("Confusion damage not dealt"); this.directDamage(damage); }, @@ -136,8 +130,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('shadowtag'); - pokemon.baseAbility = /** @type {ID} */('shadowtag'); + pokemon.ability = 'shadowtag' as ID; + pokemon.baseAbility = 'shadowtag' as ID; } if (pokemon.transformed) return; pokemon.setType(pokemon.hpType || 'Dark'); @@ -150,8 +144,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('heatproof'); - pokemon.baseAbility = /** @type {ID} */('heatproof'); + pokemon.ability = 'heatproof' as ID; + pokemon.baseAbility = 'heatproof' as ID; } }, }, @@ -162,8 +156,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('aftermath'); - pokemon.baseAbility = /** @type {ID} */('aftermath'); + pokemon.ability = 'aftermath' as ID; + pokemon.baseAbility = 'aftermath' as ID; } }, }, @@ -174,8 +168,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('compoundeyes'); - pokemon.baseAbility = /** @type {ID} */('compoundeyes'); + pokemon.ability = 'compoundeyes' as ID; + pokemon.baseAbility = 'compoundeyes' as ID; } }, }, @@ -186,8 +180,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('poisonheal'); - pokemon.baseAbility = /** @type {ID} */('poisonheal'); + pokemon.ability = 'poisonheal' as ID; + pokemon.baseAbility = 'poisonheal' as ID; } }, }, @@ -198,8 +192,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('filter'); - pokemon.baseAbility = /** @type {ID} */('filter'); + pokemon.ability = 'filter' as ID; + pokemon.baseAbility = 'filter' as ID; } }, }, @@ -210,8 +204,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('cursedbody'); - pokemon.baseAbility = /** @type {ID} */('cursedbody'); + pokemon.ability = 'cursedbody' as ID; + pokemon.baseAbility = 'cursedbody' as ID; } }, }, @@ -222,8 +216,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('cursedbody'); - pokemon.baseAbility = /** @type {ID} */('cursedbody'); + pokemon.ability = 'cursedbody' as ID; + pokemon.baseAbility = 'cursedbody' as ID; } }, }, @@ -234,8 +228,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('serenegrace'); - pokemon.baseAbility = /** @type {ID} */('serenegrace'); + pokemon.ability = 'serenegrace' as ID; + pokemon.baseAbility = 'serenegrace' as ID; } }, }, @@ -246,8 +240,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('synchronize'); - pokemon.baseAbility = /** @type {ID} */('synchronize'); + pokemon.ability = 'synchronize' as ID; + pokemon.baseAbility = 'synchronize' as ID; } }, }, @@ -258,8 +252,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('steadfast'); - pokemon.baseAbility = /** @type {ID} */('steadfast'); + pokemon.ability = 'steadfast' as ID; + pokemon.baseAbility = 'steadfast' as ID; } }, }, @@ -270,8 +264,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('sheerforce'); - pokemon.baseAbility = /** @type {ID} */('sheerforce'); + pokemon.ability = 'sheerforce' as ID; + pokemon.baseAbility = 'sheerforce' as ID; } }, }, @@ -282,8 +276,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('trace'); - pokemon.baseAbility = /** @type {ID} */('trace'); + pokemon.ability = 'trace' as ID; + pokemon.baseAbility = 'trace' as ID; } }, }, @@ -294,8 +288,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('trace'); - pokemon.baseAbility = /** @type {ID} */('trace'); + pokemon.ability = 'trace' as ID; + pokemon.baseAbility = 'trace' as ID; } }, }, @@ -306,8 +300,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('trace'); - pokemon.baseAbility = /** @type {ID} */('trace'); + pokemon.ability = 'trace' as ID; + pokemon.baseAbility = 'trace' as ID; } }, }, @@ -318,8 +312,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('trace'); - pokemon.baseAbility = /** @type {ID} */('trace'); + pokemon.ability = 'trace' as ID; + pokemon.baseAbility = 'trace' as ID; } }, }, @@ -330,8 +324,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('trace'); - pokemon.baseAbility = /** @type {ID} */('trace'); + pokemon.ability = 'trace' as ID; + pokemon.baseAbility = 'trace' as ID; } }, }, @@ -342,8 +336,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('trace'); - pokemon.baseAbility = /** @type {ID} */('trace'); + pokemon.ability = 'trace' as ID; + pokemon.baseAbility = 'trace' as ID; } }, }, @@ -351,9 +345,7 @@ let BattleStatuses = { // Cryogonal: infinite hail, Ice Body onModifyMove(move) { if (move.id === 'hail') { - /**@type {string} */ - // @ts-ignore - let weather = move.weather; + const weather = move.weather as string; move.weather = ''; move.onHit = function (target, source) { this.field.setWeather(weather, source, this.dex.getAbility('snowwarning')); @@ -367,8 +359,8 @@ let BattleStatuses = { }, onStart(pokemon) { if (pokemon.ability === 'levitate') { - pokemon.ability = /** @type {ID} */('icebody'); - pokemon.baseAbility = /** @type {ID} */('icebody'); + pokemon.ability = 'icebody' as ID; + pokemon.baseAbility = 'icebody' as ID; } }, }, @@ -376,9 +368,7 @@ let BattleStatuses = { // Probopass: infinite sand onModifyMove(move) { if (move.id === 'sandstorm') { - /**@type {string} */ - // @ts-ignore - let weather = move.weather; + const weather = move.weather as string; move.weather = ''; move.onHit = function (target, source) { this.field.setWeather(weather, source, this.dex.getAbility('sandstream')); @@ -392,9 +382,7 @@ let BattleStatuses = { // Phione: infinite rain onModifyMove(move) { if (move.id === 'raindance') { - /**@type {string} */ - // @ts-ignore - let weather = move.weather; + const weather = move.weather as string; move.weather = ''; move.onHit = function (target, source) { this.field.setWeather(weather, source, this.dex.getAbility('drizzle')); @@ -405,5 +393,3 @@ let BattleStatuses = { }, }, }; - -exports.BattleStatuses = BattleStatuses; diff --git a/data/mods/letsgo/formats-data.js b/data/mods/letsgo/formats-data.ts similarity index 99% rename from data/mods/letsgo/formats-data.js rename to data/mods/letsgo/formats-data.ts index 72acc1b480..528a39a439 100644 --- a/data/mods/letsgo/formats-data.js +++ b/data/mods/letsgo/formats-data.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesFormatsData}} */ -let BattleFormatsData = { +export const BattleFormatsData: {[k: string]: ModdedSpeciesFormatsData} = { bulbasaur: { inherit: true, tier: "LC", @@ -979,5 +976,3 @@ let BattleFormatsData = { doublesTier: "DOU", }, }; - -exports.BattleFormatsData = BattleFormatsData; diff --git a/data/mods/letsgo/learnsets.js b/data/mods/letsgo/learnsets.ts similarity index 99% rename from data/mods/letsgo/learnsets.js rename to data/mods/letsgo/learnsets.ts index d79dbc3faa..58deba5dc3 100644 --- a/data/mods/letsgo/learnsets.js +++ b/data/mods/letsgo/learnsets.ts @@ -1,7 +1,6 @@ -'use strict'; +/* eslint-disable max-len */ -/**@type {{[k: string]: ModdedLearnsetData}} */ -let BattleLearnsets = { +export const BattleLearnsets: {[k: string]: ModdedLearnsetData} = { bulbasaur: { learnset: { doubleedge: ["7L32"], @@ -5397,5 +5396,3 @@ let BattleLearnsets = { }, }, }; - -exports.BattleLearnsets = BattleLearnsets; diff --git a/data/mods/letsgo/moves.js b/data/mods/letsgo/moves.ts similarity index 85% rename from data/mods/letsgo/moves.js rename to data/mods/letsgo/moves.ts index 7df7395135..f4706a4425 100644 --- a/data/mods/letsgo/moves.js +++ b/data/mods/letsgo/moves.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedMoveData}} */ -let BattleMovedex = { +export const BattleMovedex: {[k: string]: ModdedMoveData} = { absorb: { inherit: true, basePower: 40, @@ -43,13 +40,12 @@ let BattleMovedex = { metronome: { inherit: true, onHit(target, source, effect) { - let moves = []; - for (let i in exports.BattleMovedex) { - let move = this.dex.getMove(i); + const moves = []; + for (const i in exports.BattleMovedex) { + const move = this.dex.getMove(i); if (i !== move.id) continue; if (move.gen !== 1) continue; - // @ts-ignore - if (effect.noMetronome.includes(move.id)) continue; + if (effect.noMetronome!.includes(move.id)) continue; moves.push(move); } let randomMove = ''; @@ -98,5 +94,3 @@ let BattleMovedex = { isNonstandard: null, }, }; - -exports.BattleMovedex = BattleMovedex; diff --git a/data/mods/letsgo/pokedex.js b/data/mods/letsgo/pokedex.ts similarity index 94% rename from data/mods/letsgo/pokedex.js rename to data/mods/letsgo/pokedex.ts index 2feb92f316..498bcca228 100644 --- a/data/mods/letsgo/pokedex.js +++ b/data/mods/letsgo/pokedex.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesData}} */ -let BattlePokedex = { +export const BattlePokedex: {[k: string]: ModdedSpeciesData} = { pichu: { inherit: true, evos: [], @@ -176,5 +173,3 @@ let BattlePokedex = { evoLevel: undefined, }, }; - -exports.BattlePokedex = BattlePokedex; diff --git a/data/mods/letsgo/random-teams.js b/data/mods/letsgo/random-teams.ts similarity index 78% rename from data/mods/letsgo/random-teams.js rename to data/mods/letsgo/random-teams.ts index 33675ad119..4d7555ab21 100644 --- a/data/mods/letsgo/random-teams.js +++ b/data/mods/letsgo/random-teams.ts @@ -1,35 +1,26 @@ -'use strict'; +/* eslint max-len: ["error", 240] */ -const RandomTeams = require('../../random-teams'); +import RandomTeams from '../../random-teams'; -class RandomLetsGoTeams extends RandomTeams { - /** - * @param {string | Species} species - * @param {RandomTeamsTypes.TeamDetails} [teamDetails] - * @return {RandomTeamsTypes.RandomSet} - */ - randomSet(species, teamDetails = {}) { +export class RandomLetsGoTeams extends RandomTeams { + randomSet(species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}): RandomTeamsTypes.RandomSet { species = this.dex.getSpecies(species); let forme = species.name; - if (species.battleOnly) { + if (species.battleOnly && typeof species.battleOnly === 'string') { // Only change the forme. The species has custom moves, and may have different typing and requirements. - forme = /** @type {string} */ (species.battleOnly); + forme = species.battleOnly; } - // @ts-ignore - let movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id].learnset)).slice(); - /**@type {string[]} */ - let moves = []; - /**@type {{[k: string]: true}} */ - let hasType = {}; + const movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id]!.learnset!)).slice(); + const moves: string[] = []; + const hasType: {[k: string]: true} = {}; hasType[species.types[0]] = true; if (species.types[1]) { hasType[species.types[1]] = true; } - /**@type {{[k: string]: boolean}} */ - let hasMove = {}; + let hasMove: {[k: string]: boolean} = {}; let counter; do { @@ -41,7 +32,7 @@ class RandomLetsGoTeams extends RandomTeams { // Choose next 4 moves from learnset/viable moves and add them to moves list: while (moves.length < 4 && movePool.length) { - let moveid = this.sampleNoReplace(movePool); + const moveid = this.sampleNoReplace(movePool); hasMove[moveid] = true; moves.push(moveid); } @@ -50,8 +41,8 @@ class RandomLetsGoTeams extends RandomTeams { // Iterate through the moves again, this time to cull them: for (const [i, setMoveid] of moves.entries()) { - let move = this.dex.getMove(setMoveid); - let moveid = move.id; + const move = this.dex.getMove(setMoveid); + const moveid = move.id; let rejected = false; let isSetup = false; @@ -146,18 +137,21 @@ class RandomLetsGoTeams extends RandomTeams { // Reject STABs last in case the setup type changes later on if (!hasType[move.type] || counter.stab > 1 || counter[move.category] < 2) rejected = true; } - // @ts-ignore if (counter.setupType && !isSetup && counter.setupType !== 'Mixed' && move.category !== counter.setupType && counter[counter.setupType] < 2) { // Mono-attacking with setup and RestTalk is allowed // Reject Status moves only if there is nothing else to reject - // @ts-ignore - if (move.category !== 'Status' || counter[counter.setupType] + counter.Status > 3 && counter['physicalsetup'] + counter['specialsetup'] < 2) rejected = true; + if (move.category !== 'Status' || counter[counter.setupType] + counter.Status > 3 && counter['physicalsetup'] + counter['specialsetup'] < 2) { + rejected = true; + } } // Pokemon should have moves that benefit their Type, as well as moves required by its forme - // @ts-ignore - if (!rejected && (counter['physicalsetup'] + counter['specialsetup'] < 2 && (!counter.setupType || counter.setupType === 'Mixed' || (move.category !== counter.setupType && move.category !== 'Status') || counter[counter.setupType] + counter.Status > 3)) && - (((counter.damagingMoves.length === 0 || !counter.stab) && (counter['physicalpool'] || counter['specialpool'])) || + if (!rejected && (counter['physicalsetup'] + counter['specialsetup'] < 2 && ( + !counter.setupType || counter.setupType === 'Mixed' || + (move.category !== counter.setupType && move.category !== 'Status') || + counter[counter.setupType] + counter.Status > 3) + ) && ( + ((counter.damagingMoves.length === 0 || !counter.stab) && (counter['physicalpool'] || counter['specialpool'])) || (hasType['Dark'] && !counter['Dark']) || (hasType['Dragon'] && !counter['Dragon']) || (hasType['Electric'] && !counter['Electric']) || @@ -166,7 +160,8 @@ class RandomLetsGoTeams extends RandomTeams { (hasType['Ghost'] && !hasType['Dark'] && !counter['Ghost']) || (hasType['Ground'] && !counter['Ground']) || (hasType['Ice'] && !counter['Ice']) || - (hasType['Water'] && (!counter['Water'] || !counter.stab)))) { + (hasType['Water'] && (!counter['Water'] || !counter.stab)) + )) { // Reject Status or non-STAB if (!isSetup && !move.damage && (move.category !== 'Status' || !move.flags.heal)) { if (move.category === 'Status' || !hasType[move.type] || move.selfSwitch || move.basePower && move.basePower < 40 && !move.multihit) rejected = true; @@ -181,7 +176,7 @@ class RandomLetsGoTeams extends RandomTeams { } } while (moves.length < 4 && movePool.length); - let ivs = { + const ivs = { hp: 31, atk: 31, def: 31, @@ -211,32 +206,31 @@ class RandomLetsGoTeams extends RandomTeams { } randomTeam() { - let pokemon = []; + const pokemon: RandomTeamsTypes.RandomSet[] = []; - let pokemonPool = []; - for (let id in this.dex.data.FormatsData) { - let species = this.dex.getSpecies(id); - if (species.num < 1 || (species.num > 151 && ![808, 809].includes(species.num)) || species.gen > 7 || species.nfe || !species.randomBattleMoves || !species.randomBattleMoves.length) continue; + const pokemonPool: string[] = []; + for (const id in this.dex.data.FormatsData) { + const species = this.dex.getSpecies(id); + if ( + species.num < 1 || (species.num > 151 && ![808, 809].includes(species.num)) || species.gen > 7 || + species.nfe || !species.randomBattleMoves || !species.randomBattleMoves.length + ) continue; pokemonPool.push(id); } - /**@type {{[k: string]: number}} */ - let typeCount = {}; - /**@type {{[k: string]: number}} */ - let typeComboCount = {}; - /**@type {{[k: string]: number}} */ - let baseFormes = {}; - /**@type {RandomTeamsTypes.TeamDetails} */ - let teamDetails = {}; + const typeCount: {[k: string]: number} = {}; + const typeComboCount: {[k: string]: number} = {}; + const baseFormes: {[k: string]: number} = {}; + const teamDetails: RandomTeamsTypes.TeamDetails = {}; while (pokemonPool.length && pokemon.length < 6) { - let species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); + const species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool)); if (!species.exists) continue; // Limit to one of each species (Species Clause) if (baseFormes[species.baseSpecies]) continue; - let types = species.types; + const types = species.types; // Limit 2 of any type let skip = false; @@ -248,10 +242,10 @@ class RandomLetsGoTeams extends RandomTeams { } if (skip) continue; - let set = this.randomSet(species, teamDetails); + const set = this.randomSet(species, teamDetails); // Limit 1 of any type combination - let typeCombo = types.slice().sort().join(); + const typeCombo = types.slice().sort().join(); if (typeComboCount[typeCombo] >= 1) continue; // Okay, the set passes, add it to our team @@ -282,4 +276,4 @@ class RandomLetsGoTeams extends RandomTeams { } } -module.exports = RandomLetsGoTeams; +export default RandomLetsGoTeams; diff --git a/data/mods/letsgo/rulesets.js b/data/mods/letsgo/rulesets.ts similarity index 65% rename from data/mods/letsgo/rulesets.js rename to data/mods/letsgo/rulesets.ts index d5fc157425..c63c936192 100644 --- a/data/mods/letsgo/rulesets.js +++ b/data/mods/letsgo/rulesets.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedFormatsData}} */ -let BattleFormats = { +export const BattleFormats: {[k: string]: ModdedFormatsData} = { allowavs: { effectType: 'ValidatorRule', name: 'Allow AVs', @@ -9,5 +6,3 @@ let BattleFormats = { // implemented in TeamValidator#validateStats }, }; - -exports.BattleFormats = BattleFormats; diff --git a/data/mods/letsgo/scripts.js b/data/mods/letsgo/scripts.ts similarity index 63% rename from data/mods/letsgo/scripts.js rename to data/mods/letsgo/scripts.ts index 71b9ca0208..7ef0b6d061 100644 --- a/data/mods/letsgo/scripts.js +++ b/data/mods/letsgo/scripts.ts @@ -1,31 +1,23 @@ -'use strict'; - -/**@type {ModdedBattleScriptsData} */ -let BattleScripts = { +export const BattleScripts: ModdedBattleScriptsData = { inherit: 'gen7', init() { - this.modData('Abilities', 'noability').isNonstandard = false; - for (let i in this.data.Pokedex) { + this.modData('Abilities', 'noability').isNonstandard = null; + for (const i in this.data.Pokedex) { this.modData('Pokedex', i).abilities = {0: 'No Ability'}; } }, /** * Given a table of base stats and a pokemon set, return the actual stats. - * @param {StatsTable} baseStats - * @param {PokemonSet} set - * @return {StatsTable} */ spreadModify(baseStats, set) { - /** @type {any} */ - const modStats = {atk: 10, def: 10, spa: 10, spd: 10, spe: 10}; - for (let statName in modStats) { - // @ts-ignore - let stat = baseStats[statName]; - // @ts-ignore + const modStats: StatsTable = {hp: 10, atk: 10, def: 10, spa: 10, spd: 10, spe: 10}; + let statName: StatName; + for (statName in modStats) { + const stat = baseStats[statName]; modStats[statName] = Math.floor((Math.floor(2 * stat + set.ivs[statName]) * set.level / 100 + 5)); } if ('hp' in baseStats) { - let stat = baseStats['hp']; + const stat = baseStats['hp']; modStats['hp'] = Math.floor(Math.floor(2 * stat + set.ivs['hp'] + 100) * set.level / 100 + 10); } return this.dex.natureModify(modStats, set); @@ -37,16 +29,14 @@ let BattleScripts = { * @return {StatsTable} */ natureModify(stats, set) { - let nature = this.dex.getNature(set.nature); - // @ts-ignore + const nature = this.dex.getNature(set.nature); if (nature.plus) stats[nature.plus] = Math.floor(stats[nature.plus] * 1.1); - // @ts-ignore if (nature.minus) stats[nature.minus] = Math.floor(stats[nature.minus] * 0.9); set.happiness = 70; - let friendshipValue = Math.floor((set.happiness / 255 / 10 + 1) * 100); - for (const stat in stats) { + const friendshipValue = Math.floor((set.happiness / 255 / 10 + 1) * 100); + let stat: StatName; + for (stat in stats) { if (stat !== 'hp') { - // @ts-ignore stats[stat] = Math.floor(stats[stat] * friendshipValue / 100); } // @ts-ignore @@ -59,7 +49,7 @@ let BattleScripts = { getWeight() { let weighthg = this.battle.runEvent('ModifyWeight', this, null, null, this.weighthg); if (weighthg < 1) weighthg = 1; - let weightModifierFinal = 20 * Math.random() * 0.01; + const weightModifierFinal = 20 * Math.random() * 0.01; return weighthg + (weighthg * (this.battle.random(2) === 1 ? 1 : -1) * weightModifierFinal); }, }, diff --git a/data/mods/mixandmega/items.js b/data/mods/mixandmega/items.ts similarity index 96% rename from data/mods/mixandmega/items.js rename to data/mods/mixandmega/items.ts index 2e59464804..eeff41b00f 100644 --- a/data/mods/mixandmega/items.js +++ b/data/mods/mixandmega/items.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedItemData}} */ -let BattleItems = { +export const BattleItems: {[k: string]: ModdedItemData} = { abomasite: { inherit: true, isNonstandard: null, @@ -191,5 +188,3 @@ let BattleItems = { isNonstandard: null, }, }; - -exports.BattleItems = BattleItems; diff --git a/data/mods/mixandmega/scripts.js b/data/mods/mixandmega/scripts.ts similarity index 68% rename from data/mods/mixandmega/scripts.js rename to data/mods/mixandmega/scripts.ts index 44b8febcde..b478701dd0 100644 --- a/data/mods/mixandmega/scripts.js +++ b/data/mods/mixandmega/scripts.ts @@ -1,12 +1,9 @@ -'use strict'; - -/**@type {ModdedBattleScriptsData} */ -let BattleScripts = { +export const BattleScripts: ModdedBattleScriptsData = { init() { - for (let i in this.data.Items) { + for (const i in this.data.Items) { if (!this.data.Items[i].megaStone) continue; this.modData('Items', i).onTakeItem = false; - let id = toID(this.data.Items[i].megaStone); + const id = toID(this.data.Items[i].megaStone); this.modData('FormatsData', id).isNonstandard = null; } }, @@ -24,9 +21,8 @@ let BattleScripts = { runMegaEvo(pokemon) { if (pokemon.species.isMega) return false; - /**@type {Species} */ // @ts-ignore - const species = this.getMixedSpecies(pokemon.m.originalSpecies, pokemon.canMegaEvo); + const species: Species = this.getMixedSpecies(pokemon.m.originalSpecies, pokemon.canMegaEvo); const side = pokemon.side; // Pokémon affected by Sky Drop cannot Mega Evolve. Enforce it here for now. @@ -37,13 +33,12 @@ let BattleScripts = { } // Do we have a proper sprite for it? - // @ts-ignore assert non-null pokemon.canMegaEvo - if (this.dex.getSpecies(pokemon.canMegaEvo).baseSpecies === pokemon.m.originalSpecies) { + if (this.dex.getSpecies(pokemon.canMegaEvo!).baseSpecies === pokemon.m.originalSpecies) { pokemon.formeChange(species, pokemon.getItem(), true); } else { - let oSpecies = this.dex.getSpecies(pokemon.m.originalSpecies); + const oSpecies = this.dex.getSpecies(pokemon.m.originalSpecies); // @ts-ignore - let oMegaSpecies = this.dex.getSpecies(species.originalMega); + const oMegaSpecies = this.dex.getSpecies(species.originalMega); pokemon.formeChange(species, pokemon.getItem(), true); this.add('-start', pokemon, oMegaSpecies.requiredItem, '[silent]'); if (oSpecies.types.length !== pokemon.species.types.length || oSpecies.types[1] !== pokemon.species.types[1]) { @@ -55,27 +50,34 @@ let BattleScripts = { return true; }, getMixedSpecies(originalForme, megaForme) { - let originalSpecies = this.dex.getSpecies(originalForme); - let megaSpecies = this.dex.getSpecies(megaForme); + const originalSpecies = this.dex.getSpecies(originalForme); + const megaSpecies = this.dex.getSpecies(megaForme); if (originalSpecies.baseSpecies === megaSpecies.baseSpecies) return megaSpecies; // @ts-ignore - let deltas = this.getMegaDeltas(megaSpecies); + const deltas = this.getMegaDeltas(megaSpecies); // @ts-ignore - let species = this.doGetMixedSpecies(originalSpecies, deltas); + const species = this.doGetMixedSpecies(originalSpecies, deltas); return species; }, getMegaDeltas(megaSpecies) { - let baseSpecies = this.dex.getSpecies(megaSpecies.baseSpecies); - /**@type {{ability: string, baseStats: {[k: string]: number}, weighthg: number, originalMega: string, requiredItem: string | undefined, type?: string, isMega?: boolean}} */ - let deltas = { + const baseSpecies = this.dex.getSpecies(megaSpecies.baseSpecies); + const deltas: { + ability: string, + baseStats: SparseStatsTable, + weighthg: number, + originalMega: string, + requiredItem: string | undefined, + type?: string, + isMega?: boolean, + } = { ability: megaSpecies.abilities['0'], baseStats: {}, weighthg: megaSpecies.weighthg - baseSpecies.weighthg, originalMega: megaSpecies.name, requiredItem: megaSpecies.requiredItem, }; - for (let statId in megaSpecies.baseStats) { - // @ts-ignore + let statId: StatName; + for (statId in megaSpecies.baseStats) { deltas.baseStats[statId] = megaSpecies.baseStats[statId] - baseSpecies.baseStats[statId]; } if (megaSpecies.types.length > baseSpecies.types.length) { @@ -90,8 +92,8 @@ let BattleScripts = { }, doGetMixedSpecies(speciesOrForme, deltas) { if (!deltas) throw new TypeError("Must specify deltas!"); - let species = this.dex.deepClone(this.dex.getSpecies(speciesOrForme)); - species.abilities = {0: deltas.ability}; + const species = this.dex.deepClone(this.dex.getSpecies(speciesOrForme)); + species.abilities = {'0': deltas.ability}; if (species.types[0] === deltas.type) { species.types = [deltas.type]; } else if (deltas.type === 'mono') { @@ -99,8 +101,8 @@ let BattleScripts = { } else if (deltas.type) { species.types = [species.types[0], deltas.type]; } - let baseStats = species.baseStats; - for (let statName in baseStats) { + const baseStats = species.baseStats; + for (const statName in baseStats) { baseStats[statName] = this.dex.clampIntRange(baseStats[statName] + deltas.baseStats[statName], 1, 255); } species.weighthg = Math.max(1, species.weighthg + deltas.weighthg); @@ -110,5 +112,3 @@ let BattleScripts = { return species; }, }; - -exports.BattleScripts = BattleScripts; diff --git a/data/mods/roulettemons/formats-data.js b/data/mods/roulettemons/formats-data.ts similarity index 94% rename from data/mods/roulettemons/formats-data.js rename to data/mods/roulettemons/formats-data.ts index 789b72da63..6825e5cf6d 100644 --- a/data/mods/roulettemons/formats-data.js +++ b/data/mods/roulettemons/formats-data.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesFormatsData}} */ -let BattleFormatsData = { +export const BattleFormatsData: {[k: string]: ModdedSpeciesFormatsData} = { koatric: { tier: "OU", doublesTier: "DOU", @@ -191,5 +188,3 @@ let BattleFormatsData = { doublesTier: "DOU", }, }; - -exports.BattleFormatsData = BattleFormatsData; diff --git a/data/mods/roulettemons/items.js b/data/mods/roulettemons/items.ts similarity index 78% rename from data/mods/roulettemons/items.js rename to data/mods/roulettemons/items.ts index e9a8e7fb50..8c744cd34b 100644 --- a/data/mods/roulettemons/items.js +++ b/data/mods/roulettemons/items.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedItemData}} */ -let BattleItems = { +export const BattleItems: {[k: string]: ModdedItemData} = { chillytite: { id: "chillytite", name: "Chillytite", @@ -19,5 +16,3 @@ let BattleItems = { desc: "If held by a Chillyte, this item allows it to Mega Evolve in battle.", }, }; - -exports.BattleItems = BattleItems; diff --git a/data/mods/roulettemons/learnsets.js b/data/mods/roulettemons/learnsets.js deleted file mode 100644 index 7eb7a5864b..0000000000 --- a/data/mods/roulettemons/learnsets.js +++ /dev/null @@ -1,2964 +0,0 @@ -'use strict'; - -/**@type {{[k: string]: ModdedLearnsetData}} */ -let BattleLearnsets = { - koatric: {learnset: { - barrage: ["7L1"], - boomburst: ["7L1"], - charge: ["7L1"], - crunch: ["7L1"], - curse: ["7L1"], - detect: ["7L1"], - dragondance: ["7L1"], - dreameater: ["7L1"], - feint: ["7L1"], - forcepalm: ["7L1"], - fusionbolt: ["7L1"], - glare: ["7L1"], - gravity: ["7L1"], - haze: ["7L1"], - lightofruin: ["7L1"], - magicalleaf: ["7L1"], - meteormash: ["7L1"], - moonlight: ["7L1"], - naturesmadness: ["7L1"], - outrage: ["7L1"], - pinmissile: ["7L1"], - poisonsting: ["7L1"], - psychic: ["7L1"], - rockthrow: ["7L1"], - skillswap: ["7L1"], - snatch: ["7L1"], - soak: ["7L1"], - spite: ["7L1"], - steelwing: ["7L1"], - superfang: ["7L1"], - superpower: ["7L1"], - supersonic: ["7L1"], - surf: ["7L1"], - taunt: ["7L1"], - thunderbolt: ["7L1"], - thunderpunch: ["7L1"], - toxicspikes: ["7L1"], - venoshock: ["7L1"], - wingattack: ["7L1"], - wrap: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - aquazelle: {learnset: { - aromaticmist: ["7L1"], - bide: ["7L1"], - boneclub: ["7L1"], - bulletpunch: ["7L1"], - crushgrip: ["7L1"], - discharge: ["7L1"], - falseswipe: ["7L1"], - firstimpression: ["7L1"], - forestscurse: ["7L1"], - frostbreath: ["7L1"], - fusionflare: ["7L1"], - grassknot: ["7L1"], - gunkshot: ["7L1"], - hypervoice: ["7L1"], - iceburn: ["7L1"], - irontail: ["7L1"], - liquidation: ["7L1"], - machpunch: ["7L1"], - mefirst: ["7L1"], - nightslash: ["7L1"], - nobleroar: ["7L1"], - playrough: ["7L1"], - psychup: ["7L1"], - purify: ["7L1"], - revelationdance: ["7L1"], - roleplay: ["7L1"], - rollingkick: ["7L1"], - roost: ["7L1"], - selfdestruct: ["7L1"], - simplebeam: ["7L1"], - snatch: ["7L1"], - steameruption: ["7L1"], - strength: ["7L1"], - supersonic: ["7L1"], - surf: ["7L1"], - sweetscent: ["7L1"], - thunderbolt: ["7L1"], - toxicspikes: ["7L1"], - triattack: ["7L1"], - waterfall: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - salamalix: {learnset: { - agility: ["7L1"], - bonemerang: ["7L1"], - bounce: ["7L1"], - counter: ["7L1"], - crunch: ["7L1"], - crushclaw: ["7L1"], - darkpulse: ["7L1"], - dragonbreath: ["7L1"], - dragontail: ["7L1"], - earthpower: ["7L1"], - fellstinger: ["7L1"], - flamewheel: ["7L1"], - flowershield: ["7L1"], - focusblast: ["7L1"], - headsmash: ["7L1"], - heartswap: ["7L1"], - holdhands: ["7L1"], - jumpkick: ["7L1"], - lowkick: ["7L1"], - lunge: ["7L1"], - mistyterrain: ["7L1"], - octazooka: ["7L1"], - outrage: ["7L1"], - pinmissile: ["7L1"], - poisonjab: ["7L1"], - powergem: ["7L1"], - psyshock: ["7L1"], - quickattack: ["7L1"], - recycle: ["7L1"], - rockwrecker: ["7L1"], - rollingkick: ["7L1"], - sacredfire: ["7L1"], - sacredsword: ["7L1"], - shadowpunch: ["7L1"], - simplebeam: ["7L1"], - skyattack: ["7L1"], - spiderweb: ["7L1"], - stoneedge: ["7L1"], - thousandwaves: ["7L1"], - vitalthrow: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - brawnkey: {learnset: { - airslash: ["7L1"], - aurasphere: ["7L1"], - avalanche: ["7L1"], - closecombat: ["7L1"], - destinybond: ["7L1"], - disarmingvoice: ["7L1"], - dragondance: ["7L1"], - dynamicpunch: ["7L1"], - falseswipe: ["7L1"], - firepunch: ["7L1"], - focusenergy: ["7L1"], - haze: ["7L1"], - honeclaws: ["7L1"], - hyperfang: ["7L1"], - lastresort: ["7L1"], - lavaplume: ["7L1"], - leafstorm: ["7L1"], - lusterpurge: ["7L1"], - metalburst: ["7L1"], - metalsound: ["7L1"], - mirrorshot: ["7L1"], - naturesmadness: ["7L1"], - needlearm: ["7L1"], - rockblast: ["7L1"], - rockpolish: ["7L1"], - sacredfire: ["7L1"], - skyuppercut: ["7L1"], - snatch: ["7L1"], - snore: ["7L1"], - spikyshield: ["7L1"], - stealthrock: ["7L1"], - storedpower: ["7L1"], - tailwhip: ["7L1"], - thunderpunch: ["7L1"], - tropkick: ["7L1"], - uproar: ["7L1"], - vicegrip: ["7L1"], - wakeupslap: ["7L1"], - withdraw: ["7L1"], - yawn: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - stuneleon: {learnset: { - aircutter: ["7L1"], - bonemerang: ["7L1"], - bulldoze: ["7L1"], - clangingscales: ["7L1"], - conversion2: ["7L1"], - craftyshield: ["7L1"], - crunch: ["7L1"], - crushgrip: ["7L1"], - dragonclaw: ["7L1"], - earthpower: ["7L1"], - falseswipe: ["7L1"], - frostbreath: ["7L1"], - fusionbolt: ["7L1"], - heatwave: ["7L1"], - holdhands: ["7L1"], - iceball: ["7L1"], - ironhead: ["7L1"], - knockoff: ["7L1"], - machpunch: ["7L1"], - magnetrise: ["7L1"], - meditate: ["7L1"], - metronome: ["7L1"], - moonlight: ["7L1"], - naturesmadness: ["7L1"], - poisonjab: ["7L1"], - pound: ["7L1"], - rage: ["7L1"], - reflect: ["7L1"], - scratch: ["7L1"], - shadowball: ["7L1"], - sing: ["7L1"], - sludgebomb: ["7L1"], - struggle: ["7L1"], - teleport: ["7L1"], - thousandarrows: ["7L1"], - thunderbolt: ["7L1"], - tickle: ["7L1"], - trick: ["7L1"], - wakeupslap: ["7L1"], - yawn: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - chillyte: {learnset: { - assist: ["7L1"], - auroraveil: ["7L1"], - blastburn: ["7L1"], - blueflare: ["7L1"], - bodyslam: ["7L1"], - bugbuzz: ["7L1"], - defendorder: ["7L1"], - earthpower: ["7L1"], - electroweb: ["7L1"], - embargo: ["7L1"], - entrainment: ["7L1"], - fellstinger: ["7L1"], - grassknot: ["7L1"], - icebeam: ["7L1"], - iceburn: ["7L1"], - icepunch: ["7L1"], - iciclecrash: ["7L1"], - icywind: ["7L1"], - irondefense: ["7L1"], - leafage: ["7L1"], - meteormash: ["7L1"], - mirrormove: ["7L1"], - moonlight: ["7L1"], - payday: ["7L1"], - poisongas: ["7L1"], - psychic: ["7L1"], - rage: ["7L1"], - rototiller: ["7L1"], - scald: ["7L1"], - seedbomb: ["7L1"], - skillswap: ["7L1"], - snarl: ["7L1"], - spark: ["7L1"], - submission: ["7L1"], - suckerpunch: ["7L1"], - telekinesis: ["7L1"], - triattack: ["7L1"], - vacuumwave: ["7L1"], - vcreate: ["7L1"], - wildcharge: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - spikes: ["7L1"], - pursuit: ["7L1"], - }}, - eartharoo: {learnset: { - aquatail: ["7L1"], - auroraveil: ["7L1"], - brutalswing: ["7L1"], - covet: ["7L1"], - defog: ["7L1"], - drainingkiss: ["7L1"], - earthpower: ["7L1"], - earthquake: ["7L1"], - electricterrain: ["7L1"], - encore: ["7L1"], - explosion: ["7L1"], - fairywind: ["7L1"], - foresight: ["7L1"], - growl: ["7L1"], - headsmash: ["7L1"], - hex: ["7L1"], - hypervoice: ["7L1"], - iceburn: ["7L1"], - iceshard: ["7L1"], - irontail: ["7L1"], - metalburst: ["7L1"], - milkdrink: ["7L1"], - morningsun: ["7L1"], - poisonfang: ["7L1"], - psychicfangs: ["7L1"], - punishment: ["7L1"], - pursuit: ["7L1"], - recover: ["7L1"], - recycle: ["7L1"], - relicsong: ["7L1"], - sandtomb: ["7L1"], - secretsword: ["7L1"], - storedpower: ["7L1"], - stormthrow: ["7L1"], - strength: ["7L1"], - thundershock: ["7L1"], - topsyturvy: ["7L1"], - tropkick: ["7L1"], - wingattack: ["7L1"], - zingzap: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - crazefly: {learnset: { - acidspray: ["7L1"], - anchorshot: ["7L1"], - beakblast: ["7L1"], - bugbuzz: ["7L1"], - charm: ["7L1"], - cometpunch: ["7L1"], - crosspoison: ["7L1"], - destinybond: ["7L1"], - electricterrain: ["7L1"], - fairywind: ["7L1"], - frostbreath: ["7L1"], - fusionflare: ["7L1"], - helpinghand: ["7L1"], - highhorsepower: ["7L1"], - iceshard: ["7L1"], - jumpkick: ["7L1"], - lunge: ["7L1"], - lusterpurge: ["7L1"], - magicalleaf: ["7L1"], - meditate: ["7L1"], - mimic: ["7L1"], - mistyterrain: ["7L1"], - moongeistbeam: ["7L1"], - playrough: ["7L1"], - psychic: ["7L1"], - psychoboost: ["7L1"], - quickattack: ["7L1"], - rapidspin: ["7L1"], - roost: ["7L1"], - shadowpunch: ["7L1"], - skullbash: ["7L1"], - smellingsalts: ["7L1"], - spacialrend: ["7L1"], - spotlight: ["7L1"], - steamroller: ["7L1"], - thundershock: ["7L1"], - wish: ["7L1"], - worryseed: ["7L1"], - xscissor: ["7L1"], - zenheadbutt: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - electritar: {learnset: { - afteryou: ["7L1"], - blueflare: ["7L1"], - bonerush: ["7L1"], - celebrate: ["7L1"], - clearsmog: ["7L1"], - cometpunch: ["7L1"], - conversion2: ["7L1"], - diamondstorm: ["7L1"], - dreameater: ["7L1"], - energyball: ["7L1"], - fairywind: ["7L1"], - feint: ["7L1"], - firefang: ["7L1"], - firepledge: ["7L1"], - flail: ["7L1"], - flameburst: ["7L1"], - flashcannon: ["7L1"], - flatter: ["7L1"], - flyingpress: ["7L1"], - forestscurse: ["7L1"], - furycutter: ["7L1"], - fusionbolt: ["7L1"], - hammerarm: ["7L1"], - headcharge: ["7L1"], - hornleech: ["7L1"], - ironhead: ["7L1"], - playrough: ["7L1"], - powdersnow: ["7L1"], - psychic: ["7L1"], - rage: ["7L1"], - refresh: ["7L1"], - sacredsword: ["7L1"], - safeguard: ["7L1"], - shadowclaw: ["7L1"], - shoreup: ["7L1"], - surf: ["7L1"], - takedown: ["7L1"], - thunderbolt: ["7L1"], - wideguard: ["7L1"], - yawn: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - aquatopus: {learnset: { - ancientpower: ["7L1"], - aquaring: ["7L1"], - burnup: ["7L1"], - calmmind: ["7L1"], - counter: ["7L1"], - crosschop: ["7L1"], - earthpower: ["7L1"], - electricterrain: ["7L1"], - flail: ["7L1"], - followme: ["7L1"], - grudge: ["7L1"], - headsmash: ["7L1"], - helpinghand: ["7L1"], - icehammer: ["7L1"], - infestation: ["7L1"], - lavaplume: ["7L1"], - lick: ["7L1"], - liquidation: ["7L1"], - mefirst: ["7L1"], - megahorn: ["7L1"], - metalclaw: ["7L1"], - mist: ["7L1"], - naturesmadness: ["7L1"], - nobleroar: ["7L1"], - oblivionwing: ["7L1"], - odorsleuth: ["7L1"], - payday: ["7L1"], - poisontail: ["7L1"], - sacredfire: ["7L1"], - shadowclaw: ["7L1"], - slackoff: ["7L1"], - slash: ["7L1"], - stealthrock: ["7L1"], - stickyweb: ["7L1"], - superpower: ["7L1"], - surf: ["7L1"], - uproar: ["7L1"], - wakeupslap: ["7L1"], - waterfall: ["7L1"], - wish: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - scorpita: {learnset: { - acrobatics: ["7L1"], - acupressure: ["7L1"], - aircutter: ["7L1"], - anchorshot: ["7L1"], - aromatherapy: ["7L1"], - banefulbunker: ["7L1"], - boneclub: ["7L1"], - bounce: ["7L1"], - brickbreak: ["7L1"], - chargebeam: ["7L1"], - coreenforcer: ["7L1"], - crushclaw: ["7L1"], - disarmingvoice: ["7L1"], - dracometeor: ["7L1"], - drillpeck: ["7L1"], - earthpower: ["7L1"], - flamethrower: ["7L1"], - hex: ["7L1"], - holdhands: ["7L1"], - hyperbeam: ["7L1"], - icehammer: ["7L1"], - iciclecrash: ["7L1"], - mudbomb: ["7L1"], - poisonjab: ["7L1"], - retaliate: ["7L1"], - reversal: ["7L1"], - sludgebomb: ["7L1"], - snore: ["7L1"], - soak: ["7L1"], - solarblade: ["7L1"], - spikyshield: ["7L1"], - spiritshackle: ["7L1"], - steelwing: ["7L1"], - superfang: ["7L1"], - sweetkiss: ["7L1"], - swift: ["7L1"], - telekinesis: ["7L1"], - triplekick: ["7L1"], - zenheadbutt: ["7L1"], - zingzap: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - baloon: {learnset: { - acupressure: ["7L1"], - amnesia: ["7L1"], - anchorshot: ["7L1"], - assurance: ["7L1"], - banefulbunker: ["7L1"], - clangingscales: ["7L1"], - conversion: ["7L1"], - cottonguard: ["7L1"], - darkestlariat: ["7L1"], - disarmingvoice: ["7L1"], - dragonbreath: ["7L1"], - dragondance: ["7L1"], - echoedvoice: ["7L1"], - electroball: ["7L1"], - fakeout: ["7L1"], - feintattack: ["7L1"], - firepunch: ["7L1"], - healorder: ["7L1"], - heartswap: ["7L1"], - hypervoice: ["7L1"], - icywind: ["7L1"], - machpunch: ["7L1"], - mistyterrain: ["7L1"], - moonblast: ["7L1"], - nobleroar: ["7L1"], - playrough: ["7L1"], - present: ["7L1"], - quash: ["7L1"], - reversal: ["7L1"], - rockthrow: ["7L1"], - seismictoss: ["7L1"], - spacialrend: ["7L1"], - spiritshackle: ["7L1"], - storedpower: ["7L1"], - superfang: ["7L1"], - tackle: ["7L1"], - toxicspikes: ["7L1"], - volttackle: ["7L1"], - watergun: ["7L1"], - waterspout: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - kinesel: {learnset: { - allyswitch: ["7L1"], - aquatail: ["7L1"], - blazekick: ["7L1"], - bulletpunch: ["7L1"], - dizzypunch: ["7L1"], - doublehit: ["7L1"], - dragonrage: ["7L1"], - dragonrush: ["7L1"], - explosion: ["7L1"], - firepunch: ["7L1"], - flowershield: ["7L1"], - heartswap: ["7L1"], - hex: ["7L1"], - hornattack: ["7L1"], - ingrain: ["7L1"], - iondeluge: ["7L1"], - leechlife: ["7L1"], - lightofruin: ["7L1"], - mirrorshot: ["7L1"], - moonblast: ["7L1"], - painsplit: ["7L1"], - paraboliccharge: ["7L1"], - playrough: ["7L1"], - powertrick: ["7L1"], - psybeam: ["7L1"], - psychic: ["7L1"], - relicsong: ["7L1"], - shadowbone: ["7L1"], - shadowclaw: ["7L1"], - shockwave: ["7L1"], - spikecannon: ["7L1"], - stompingtantrum: ["7L1"], - swordsdance: ["7L1"], - thousandwaves: ["7L1"], - thundershock: ["7L1"], - trickroom: ["7L1"], - uturn: ["7L1"], - wildcharge: ["7L1"], - wringout: ["7L1"], - zenheadbutt: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - glacida: {learnset: { - anchorshot: ["7L1"], - aurasphere: ["7L1"], - bonemerang: ["7L1"], - brutalswing: ["7L1"], - bubblebeam: ["7L1"], - bulletseed: ["7L1"], - encore: ["7L1"], - gearup: ["7L1"], - heatwave: ["7L1"], - hydrocannon: ["7L1"], - icebeam: ["7L1"], - iceburn: ["7L1"], - iciclecrash: ["7L1"], - incinerate: ["7L1"], - leechseed: ["7L1"], - lightscreen: ["7L1"], - magicalleaf: ["7L1"], - magicroom: ["7L1"], - milkdrink: ["7L1"], - moongeistbeam: ["7L1"], - morningsun: ["7L1"], - playrough: ["7L1"], - pound: ["7L1"], - psychoboost: ["7L1"], - punishment: ["7L1"], - quash: ["7L1"], - recycle: ["7L1"], - reversal: ["7L1"], - secretpower: ["7L1"], - skyuppercut: ["7L1"], - sludgebomb: ["7L1"], - solarbeam: ["7L1"], - spikyshield: ["7L1"], - swagger: ["7L1"], - synchronoise: ["7L1"], - toxicspikes: ["7L1"], - withdraw: ["7L1"], - woodhammer: ["7L1"], - workup: ["7L1"], - yawn: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - pidgeotine: {learnset: { - absorb: ["7L1"], - acid: ["7L1"], - aeroblast: ["7L1"], - agility: ["7L1"], - aurasphere: ["7L1"], - blizzard: ["7L1"], - crushclaw: ["7L1"], - cut: ["7L1"], - discharge: ["7L1"], - dragonbreath: ["7L1"], - dragonclaw: ["7L1"], - dragontail: ["7L1"], - drillpeck: ["7L1"], - endeavor: ["7L1"], - fleurcannon: ["7L1"], - grassyterrain: ["7L1"], - haze: ["7L1"], - healbell: ["7L1"], - healblock: ["7L1"], - hypervoice: ["7L1"], - iceburn: ["7L1"], - icywind: ["7L1"], - lavaplume: ["7L1"], - liquidation: ["7L1"], - lovelykiss: ["7L1"], - mimic: ["7L1"], - miracleeye: ["7L1"], - mudslap: ["7L1"], - multiattack: ["7L1"], - payback: ["7L1"], - powersplit: ["7L1"], - precipiceblades: ["7L1"], - roar: ["7L1"], - rocksmash: ["7L1"], - strength: ["7L1"], - struggle: ["7L1"], - teeterdance: ["7L1"], - telekinesis: ["7L1"], - uturn: ["7L1"], - yawn: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - gorilax: {learnset: { - amnesia: ["7L1"], - aurasphere: ["7L1"], - blueflare: ["7L1"], - doubleslap: ["7L1"], - drainpunch: ["7L1"], - earthpower: ["7L1"], - firepledge: ["7L1"], - flameburst: ["7L1"], - gastroacid: ["7L1"], - hammerarm: ["7L1"], - icefang: ["7L1"], - icehammer: ["7L1"], - leaftornado: ["7L1"], - lunge: ["7L1"], - magicroom: ["7L1"], - meanlook: ["7L1"], - morningsun: ["7L1"], - nightshade: ["7L1"], - ominouswind: ["7L1"], - powersplit: ["7L1"], - powerwhip: ["7L1"], - psychic: ["7L1"], - psyshock: ["7L1"], - quickguard: ["7L1"], - rockclimb: ["7L1"], - rockslide: ["7L1"], - shoreup: ["7L1"], - skullbash: ["7L1"], - skyuppercut: ["7L1"], - sludgebomb: ["7L1"], - smellingsalts: ["7L1"], - spark: ["7L1"], - spotlight: ["7L1"], - steamroller: ["7L1"], - tackle: ["7L1"], - triplekick: ["7L1"], - wildcharge: ["7L1"], - wingattack: ["7L1"], - wish: ["7L1"], - zenheadbutt: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - albatrygon: {learnset: { - aeroblast: ["7L1"], - aurasphere: ["7L1"], - aurorabeam: ["7L1"], - avalanche: ["7L1"], - beakblast: ["7L1"], - block: ["7L1"], - charge: ["7L1"], - crabhammer: ["7L1"], - crushgrip: ["7L1"], - defendorder: ["7L1"], - drillpeck: ["7L1"], - drillrun: ["7L1"], - focuspunch: ["7L1"], - foresight: ["7L1"], - foulplay: ["7L1"], - harden: ["7L1"], - headbutt: ["7L1"], - icefang: ["7L1"], - ingrain: ["7L1"], - leafage: ["7L1"], - liquidation: ["7L1"], - memento: ["7L1"], - metronome: ["7L1"], - mimic: ["7L1"], - mirrorcoat: ["7L1"], - morningsun: ["7L1"], - overheat: ["7L1"], - photongeyser: ["7L1"], - purify: ["7L1"], - rapidspin: ["7L1"], - rockclimb: ["7L1"], - secretpower: ["7L1"], - seedflare: ["7L1"], - shockwave: ["7L1"], - skillswap: ["7L1"], - stickyweb: ["7L1"], - struggle: ["7L1"], - strugglebug: ["7L1"], - sunnyday: ["7L1"], - thunderbolt: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - chillvark: {learnset: { - aquatail: ["7L1"], - assurance: ["7L1"], - aurasphere: ["7L1"], - blueflare: ["7L1"], - bonemerang: ["7L1"], - bubblebeam: ["7L1"], - crabhammer: ["7L1"], - doublekick: ["7L1"], - firepledge: ["7L1"], - flash: ["7L1"], - focusblast: ["7L1"], - furycutter: ["7L1"], - growth: ["7L1"], - gust: ["7L1"], - healingwish: ["7L1"], - holdhands: ["7L1"], - icebeam: ["7L1"], - iciclecrash: ["7L1"], - ingrain: ["7L1"], - judgment: ["7L1"], - magmastorm: ["7L1"], - miracleeye: ["7L1"], - mistball: ["7L1"], - moonblast: ["7L1"], - nightmare: ["7L1"], - ominouswind: ["7L1"], - painsplit: ["7L1"], - perishsong: ["7L1"], - rockblast: ["7L1"], - rollingkick: ["7L1"], - safeguard: ["7L1"], - skyuppercut: ["7L1"], - slackoff: ["7L1"], - slam: ["7L1"], - snarl: ["7L1"], - softboiled: ["7L1"], - stunspore: ["7L1"], - surf: ["7L1"], - wideguard: ["7L1"], - wonderroom: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - komodith: {learnset: { - auroraveil: ["7L1"], - bestow: ["7L1"], - boomburst: ["7L1"], - bravebird: ["7L1"], - bulkup: ["7L1"], - bulletseed: ["7L1"], - constrict: ["7L1"], - crushgrip: ["7L1"], - disarmingvoice: ["7L1"], - dracometeor: ["7L1"], - dragonclaw: ["7L1"], - dragonpulse: ["7L1"], - earthpower: ["7L1"], - earthquake: ["7L1"], - flashcannon: ["7L1"], - fling: ["7L1"], - floralhealing: ["7L1"], - freezeshock: ["7L1"], - gunkshot: ["7L1"], - healingwish: ["7L1"], - helpinghand: ["7L1"], - hornleech: ["7L1"], - iceshard: ["7L1"], - ironhead: ["7L1"], - landswrath: ["7L1"], - lockon: ["7L1"], - nightslash: ["7L1"], - overheat: ["7L1"], - psychoboost: ["7L1"], - razorleaf: ["7L1"], - razorwind: ["7L1"], - relicsong: ["7L1"], - secretpower: ["7L1"], - shockwave: ["7L1"], - spikecannon: ["7L1"], - sunnyday: ["7L1"], - tickle: ["7L1"], - trumpcard: ["7L1"], - workup: ["7L1"], - zenheadbutt: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - giranium: {learnset: { - aromatherapy: ["7L1"], - aromaticmist: ["7L1"], - bestow: ["7L1"], - bugbuzz: ["7L1"], - crabhammer: ["7L1"], - defog: ["7L1"], - energyball: ["7L1"], - flareblitz: ["7L1"], - flash: ["7L1"], - flashcannon: ["7L1"], - flatter: ["7L1"], - freezedry: ["7L1"], - freezeshock: ["7L1"], - furyswipes: ["7L1"], - hail: ["7L1"], - holdhands: ["7L1"], - infestation: ["7L1"], - instruct: ["7L1"], - ironhead: ["7L1"], - leafblade: ["7L1"], - meditate: ["7L1"], - metalsound: ["7L1"], - moonlight: ["7L1"], - pluck: ["7L1"], - powergem: ["7L1"], - powersplit: ["7L1"], - psychocut: ["7L1"], - pursuit: ["7L1"], - quickguard: ["7L1"], - relicsong: ["7L1"], - seismictoss: ["7L1"], - simplebeam: ["7L1"], - sing: ["7L1"], - smokescreen: ["7L1"], - speedswap: ["7L1"], - stoneedge: ["7L1"], - stormthrow: ["7L1"], - stunspore: ["7L1"], - thunderbolt: ["7L1"], - waterspout: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - flamyle: {learnset: { - bide: ["7L1"], - blazekick: ["7L1"], - boneclub: ["7L1"], - bonemerang: ["7L1"], - bubblebeam: ["7L1"], - cottonguard: ["7L1"], - cut: ["7L1"], - destinybond: ["7L1"], - dizzypunch: ["7L1"], - extrasensory: ["7L1"], - firefang: ["7L1"], - flamethrower: ["7L1"], - freezeshock: ["7L1"], - furyattack: ["7L1"], - glare: ["7L1"], - grassknot: ["7L1"], - healorder: ["7L1"], - heartswap: ["7L1"], - jumpkick: ["7L1"], - karatechop: ["7L1"], - laserfocus: ["7L1"], - leafstorm: ["7L1"], - matblock: ["7L1"], - mirrorshot: ["7L1"], - moongeistbeam: ["7L1"], - naturepower: ["7L1"], - naturesmadness: ["7L1"], - nobleroar: ["7L1"], - paraboliccharge: ["7L1"], - poweruppunch: ["7L1"], - powerwhip: ["7L1"], - scratch: ["7L1"], - seedbomb: ["7L1"], - silverwind: ["7L1"], - sludgebomb: ["7L1"], - speedswap: ["7L1"], - spiderweb: ["7L1"], - thunderfang: ["7L1"], - thunderwave: ["7L1"], - tropkick: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - voltecta: {learnset: { - acrobatics: ["7L1"], - airslash: ["7L1"], - bonerush: ["7L1"], - bulletseed: ["7L1"], - falseswipe: ["7L1"], - focusblast: ["7L1"], - fusionvolt: ["7L1"], - geargrind: ["7L1"], - gigaimpact: ["7L1"], - gunkshot: ["7L1"], - holdback: ["7L1"], - karatechop: ["7L1"], - lick: ["7L1"], - megapunch: ["7L1"], - miracleeye: ["7L1"], - mistyterrain: ["7L1"], - nightshade: ["7L1"], - nightslash: ["7L1"], - petalblizzard: ["7L1"], - pound: ["7L1"], - psychicfangs: ["7L1"], - psyshock: ["7L1"], - rapidspin: ["7L1"], - razorleaf: ["7L1"], - reflect: ["7L1"], - reversal: ["7L1"], - shadowball: ["7L1"], - shadowbone: ["7L1"], - smokescreen: ["7L1"], - sparklingaria: ["7L1"], - stockpile: ["7L1"], - superfang: ["7L1"], - sweetscent: ["7L1"], - thousandarrows: ["7L1"], - thunderbolt: ["7L1"], - toxicthread: ["7L1"], - twister: ["7L1"], - voltswitch: ["7L1"], - watergun: ["7L1"], - wildcharge: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - ostria: {learnset: { - aeroblast: ["7L1"], - airslash: ["7L1"], - ancientpower: ["7L1"], - aurasphere: ["7L1"], - auroraveil: ["7L1"], - crosschop: ["7L1"], - defog: ["7L1"], - dive: ["7L1"], - doublekick: ["7L1"], - dragonrage: ["7L1"], - drillpeck: ["7L1"], - energyball: ["7L1"], - fakeout: ["7L1"], - flowershield: ["7L1"], - highjumpkick: ["7L1"], - holdhands: ["7L1"], - honeclaws: ["7L1"], - icepunch: ["7L1"], - ironhead: ["7L1"], - leafblade: ["7L1"], - magneticflux: ["7L1"], - magnitude: ["7L1"], - metalclaw: ["7L1"], - mindreader: ["7L1"], - moonlight: ["7L1"], - nobleroar: ["7L1"], - outrage: ["7L1"], - phantomforce: ["7L1"], - poisongas: ["7L1"], - purify: ["7L1"], - quash: ["7L1"], - retaliate: ["7L1"], - roar: ["7L1"], - sacredfire: ["7L1"], - scald: ["7L1"], - steameruption: ["7L1"], - steamroller: ["7L1"], - stomp: ["7L1"], - takedown: ["7L1"], - wideguard: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - ninjoth: {learnset: { - constrict: ["7L1"], - cottonspore: ["7L1"], - counter: ["7L1"], - crunch: ["7L1"], - darkpulse: ["7L1"], - defog: ["7L1"], - diamondstorm: ["7L1"], - dragonrush: ["7L1"], - drainingkiss: ["7L1"], - fairywind: ["7L1"], - fakeout: ["7L1"], - firefang: ["7L1"], - flatter: ["7L1"], - foulplay: ["7L1"], - gearup: ["7L1"], - heavyslam: ["7L1"], - icebeam: ["7L1"], - iciclecrash: ["7L1"], - knockoff: ["7L1"], - magnetrise: ["7L1"], - moonblast: ["7L1"], - poisonsting: ["7L1"], - psystrike: ["7L1"], - pursuit: ["7L1"], - refresh: ["7L1"], - shockwave: ["7L1"], - slam: ["7L1"], - slash: ["7L1"], - snore: ["7L1"], - spitup: ["7L1"], - steamroller: ["7L1"], - steelwing: ["7L1"], - struggle: ["7L1"], - swordsdance: ["7L1"], - tackle: ["7L1"], - tailslap: ["7L1"], - thunderpunch: ["7L1"], - weatherball: ["7L1"], - wildcharge: ["7L1"], - yawn: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - herbigator: {learnset: { - assist: ["7L1"], - bestow: ["7L1"], - darkpulse: ["7L1"], - disarmingvoice: ["7L1"], - dreameater: ["7L1"], - earthquake: ["7L1"], - energyball: ["7L1"], - entrainment: ["7L1"], - flashcannon: ["7L1"], - frenzyplant: ["7L1"], - growth: ["7L1"], - headsmash: ["7L1"], - hex: ["7L1"], - landswrath: ["7L1"], - leafblade: ["7L1"], - mindblown: ["7L1"], - mindreader: ["7L1"], - miracleeye: ["7L1"], - mirrorcoat: ["7L1"], - moongeistbeam: ["7L1"], - moonlight: ["7L1"], - playnice: ["7L1"], - playrough: ["7L1"], - poisonjab: ["7L1"], - powerswap: ["7L1"], - psystrike: ["7L1"], - rockpolish: ["7L1"], - skyattack: ["7L1"], - smellingsalts: ["7L1"], - strugglebug: ["7L1"], - sunsteelstrike: ["7L1"], - surf: ["7L1"], - switcheroo: ["7L1"], - tackle: ["7L1"], - takedown: ["7L1"], - tearfullook: ["7L1"], - thunderpunch: ["7L1"], - trick: ["7L1"], - waterfall: ["7L1"], - watergun: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - anteros: {learnset: { - allyswitch: ["7L1"], - amnesia: ["7L1"], - beatup: ["7L1"], - bugbite: ["7L1"], - bugbuzz: ["7L1"], - calmmind: ["7L1"], - confusion: ["7L1"], - cosmicpower: ["7L1"], - dazzlinggleam: ["7L1"], - dragonrage: ["7L1"], - encore: ["7L1"], - entrainment: ["7L1"], - firepunch: ["7L1"], - flashcannon: ["7L1"], - healbell: ["7L1"], - healpulse: ["7L1"], - iciclecrash: ["7L1"], - infestation: ["7L1"], - ironhead: ["7L1"], - kingsshield: ["7L1"], - lavaplume: ["7L1"], - megapunch: ["7L1"], - petaldance: ["7L1"], - rocksmash: ["7L1"], - seedflare: ["7L1"], - sharpen: ["7L1"], - shoreup: ["7L1"], - skullbash: ["7L1"], - smellingsalts: ["7L1"], - softboiled: ["7L1"], - spacialrend: ["7L1"], - spikyshield: ["7L1"], - spite: ["7L1"], - suckerpunch: ["7L1"], - switcheroo: ["7L1"], - teleport: ["7L1"], - thief: ["7L1"], - twister: ["7L1"], - vicegrip: ["7L1"], - xscissor: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - gladiaster: {learnset: { - absorb: ["7L1"], - allyswitch: ["7L1"], - aurorabeam: ["7L1"], - block: ["7L1"], - bubble: ["7L1"], - bulletpunch: ["7L1"], - burnup: ["7L1"], - charm: ["7L1"], - crabhammer: ["7L1"], - crosschop: ["7L1"], - dazzlinggleam: ["7L1"], - doubleedge: ["7L1"], - dynamicpunch: ["7L1"], - eruption: ["7L1"], - finalgambit: ["7L1"], - flameburst: ["7L1"], - flashcannon: ["7L1"], - gastroacid: ["7L1"], - gunkshot: ["7L1"], - hammerarm: ["7L1"], - headcharge: ["7L1"], - iceball: ["7L1"], - icebeam: ["7L1"], - iceburn: ["7L1"], - icepunch: ["7L1"], - iciclecrash: ["7L1"], - inferno: ["7L1"], - ironhead: ["7L1"], - lunge: ["7L1"], - magnitude: ["7L1"], - pluck: ["7L1"], - pursuit: ["7L1"], - rockblast: ["7L1"], - silverwind: ["7L1"], - slam: ["7L1"], - spiderweb: ["7L1"], - steamroller: ["7L1"], - teeterdance: ["7L1"], - vcreate: ["7L1"], - volttackle: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - hyperoach: {learnset: { - acrobatics: ["7L1"], - avalanche: ["7L1"], - bubblebeam: ["7L1"], - bugbuzz: ["7L1"], - bulletseed: ["7L1"], - conversion: ["7L1"], - conversion2: ["7L1"], - dragonrage: ["7L1"], - earthpower: ["7L1"], - eerieimpulse: ["7L1"], - explosion: ["7L1"], - fling: ["7L1"], - fly: ["7L1"], - growth: ["7L1"], - hail: ["7L1"], - hypervoice: ["7L1"], - icebeam: ["7L1"], - icefang: ["7L1"], - irondefense: ["7L1"], - judgment: ["7L1"], - megadrain: ["7L1"], - mindblown: ["7L1"], - nightdaze: ["7L1"], - perishsong: ["7L1"], - petaldance: ["7L1"], - powerswap: ["7L1"], - punishment: ["7L1"], - quickguard: ["7L1"], - reflect: ["7L1"], - revenge: ["7L1"], - sludge: ["7L1"], - splash: ["7L1"], - strength: ["7L1"], - sunsteelstrike: ["7L1"], - swordsdance: ["7L1"], - tailslap: ["7L1"], - topsyturvy: ["7L1"], - uturn: ["7L1"], - wish: ["7L1"], - xscissor: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - barracoth: {learnset: { - crosspoison: ["7L1"], - dracometeor: ["7L1"], - dynamicpunch: ["7L1"], - fierydance: ["7L1"], - firstimpression: ["7L1"], - flamethrower: ["7L1"], - flash: ["7L1"], - flatter: ["7L1"], - foresight: ["7L1"], - fusionbolt: ["7L1"], - gigaimpact: ["7L1"], - guardsplit: ["7L1"], - icebeam: ["7L1"], - iciclecrash: ["7L1"], - infestation: ["7L1"], - lastresort: ["7L1"], - lowsweep: ["7L1"], - payday: ["7L1"], - photongeyser: ["7L1"], - plasmafists: ["7L1"], - poisonjab: ["7L1"], - pollenpuff: ["7L1"], - recycle: ["7L1"], - roar: ["7L1"], - rockblast: ["7L1"], - roost: ["7L1"], - sandattack: ["7L1"], - screech: ["7L1"], - sing: ["7L1"], - skydrop: ["7L1"], - softboiled: ["7L1"], - spikyshield: ["7L1"], - surf: ["7L1"], - thousandarrows: ["7L1"], - twister: ["7L1"], - voltswitch: ["7L1"], - waterfall: ["7L1"], - wideguard: ["7L1"], - worryseed: ["7L1"], - zenheadbutt: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - toados: {learnset: { - aromatherapy: ["7L1"], - attackorder: ["7L1"], - barrier: ["7L1"], - blizzard: ["7L1"], - bubble: ["7L1"], - bugbuzz: ["7L1"], - bulkup: ["7L1"], - clearsmog: ["7L1"], - dazzlinggleam: ["7L1"], - destinybond: ["7L1"], - dizzypunch: ["7L1"], - dragonclaw: ["7L1"], - dragontail: ["7L1"], - encore: ["7L1"], - floralhealing: ["7L1"], - grasspledge: ["7L1"], - grassyterrain: ["7L1"], - headbutt: ["7L1"], - iciclecrash: ["7L1"], - karatechop: ["7L1"], - lastresort: ["7L1"], - leafblade: ["7L1"], - lockon: ["7L1"], - moongeistbeam: ["7L1"], - mudslap: ["7L1"], - nightmare: ["7L1"], - octazooka: ["7L1"], - poisonjab: ["7L1"], - psystrike: ["7L1"], - quickguard: ["7L1"], - raindance: ["7L1"], - rockblast: ["7L1"], - seismictoss: ["7L1"], - skyuppercut: ["7L1"], - sludgebomb: ["7L1"], - smellingsalts: ["7L1"], - snore: ["7L1"], - toxicspikes: ["7L1"], - venoshock: ["7L1"], - watergun: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - voltarak: {learnset: { - acrobatics: ["7L1"], - allyswitch: ["7L1"], - assurance: ["7L1"], - bide: ["7L1"], - blizzard: ["7L1"], - crosschop: ["7L1"], - crosspoison: ["7L1"], - detect: ["7L1"], - drillrun: ["7L1"], - dualchop: ["7L1"], - energyball: ["7L1"], - explosion: ["7L1"], - fairylock: ["7L1"], - fierydance: ["7L1"], - flameburst: ["7L1"], - flowershield: ["7L1"], - fusionbolt: ["7L1"], - gigadrain: ["7L1"], - headsmash: ["7L1"], - hypervoice: ["7L1"], - icebeam: ["7L1"], - iciclecrash: ["7L1"], - magneticflux: ["7L1"], - milkdrink: ["7L1"], - painsplit: ["7L1"], - payday: ["7L1"], - plasmafists: ["7L1"], - poisonfang: ["7L1"], - pound: ["7L1"], - powdersnow: ["7L1"], - powerswap: ["7L1"], - sandtomb: ["7L1"], - signalbeam: ["7L1"], - skillswap: ["7L1"], - smokescreen: ["7L1"], - steelwing: ["7L1"], - switcheroo: ["7L1"], - synthesis: ["7L1"], - thunderbolt: ["7L1"], - toxicspikes: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - mosqung: {learnset: { - aeroblast: ["7L1"], - agility: ["7L1"], - aquatail: ["7L1"], - assurance: ["7L1"], - banefulbunker: ["7L1"], - belch: ["7L1"], - bugbuzz: ["7L1"], - bulletpunch: ["7L1"], - burnup: ["7L1"], - cottonspore: ["7L1"], - crabhammer: ["7L1"], - defensecurl: ["7L1"], - doubleedge: ["7L1"], - dragonclaw: ["7L1"], - drainingkiss: ["7L1"], - drillpeck: ["7L1"], - feintattack: ["7L1"], - glaciate: ["7L1"], - gunkshot: ["7L1"], - holdback: ["7L1"], - hornleech: ["7L1"], - iceball: ["7L1"], - incinerate: ["7L1"], - instruct: ["7L1"], - mindreader: ["7L1"], - naturesmadness: ["7L1"], - nightshade: ["7L1"], - powder: ["7L1"], - rapidspin: ["7L1"], - razorshell: ["7L1"], - reflect: ["7L1"], - snatch: ["7L1"], - strengthsap: ["7L1"], - synchronoise: ["7L1"], - takedown: ["7L1"], - thunderbolt: ["7L1"], - waterpledge: ["7L1"], - withdraw: ["7L1"], - xscissor: ["7L1"], - zenheadbutt: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - flamepion: {learnset: { - blazekick: ["7L1"], - closecombat: ["7L1"], - counter: ["7L1"], - covet: ["7L1"], - disable: ["7L1"], - dragonrage: ["7L1"], - eerieimpulse: ["7L1"], - energyball: ["7L1"], - finalgambit: ["7L1"], - flameburst: ["7L1"], - flamethrower: ["7L1"], - flashcannon: ["7L1"], - focusenergy: ["7L1"], - furyattack: ["7L1"], - judgment: ["7L1"], - leer: ["7L1"], - meteormash: ["7L1"], - mysticalfire: ["7L1"], - poisonjab: ["7L1"], - psychicfangs: ["7L1"], - psywave: ["7L1"], - raindance: ["7L1"], - razorleaf: ["7L1"], - roost: ["7L1"], - selfdestruct: ["7L1"], - shadowball: ["7L1"], - shadowbone: ["7L1"], - shockwave: ["7L1"], - smog: ["7L1"], - spiritshackle: ["7L1"], - spite: ["7L1"], - stealthrock: ["7L1"], - tackle: ["7L1"], - telekinesis: ["7L1"], - teleport: ["7L1"], - trickroom: ["7L1"], - venoshock: ["7L1"], - wideguard: ["7L1"], - wish: ["7L1"], - zenheadbutt: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - hyenix: {learnset: { - agility: ["7L1"], - autotomize: ["7L1"], - belch: ["7L1"], - blueflare: ["7L1"], - bugbite: ["7L1"], - cottonspore: ["7L1"], - crunch: ["7L1"], - crushgrip: ["7L1"], - darkpulse: ["7L1"], - dive: ["7L1"], - drillrun: ["7L1"], - feint: ["7L1"], - firefang: ["7L1"], - flatter: ["7L1"], - fleurcannon: ["7L1"], - foulplay: ["7L1"], - furyswipes: ["7L1"], - machpunch: ["7L1"], - magnetbomb: ["7L1"], - matblock: ["7L1"], - megahorn: ["7L1"], - megapunch: ["7L1"], - metalsound: ["7L1"], - mudsport: ["7L1"], - nobleroar: ["7L1"], - outrage: ["7L1"], - pinmissile: ["7L1"], - psychocut: ["7L1"], - rage: ["7L1"], - rockblast: ["7L1"], - roost: ["7L1"], - slash: ["7L1"], - sludgewave: ["7L1"], - strugglebug: ["7L1"], - sunsteelstrike: ["7L1"], - taunt: ["7L1"], - thundershock: ["7L1"], - waterfall: ["7L1"], - watershuriken: ["7L1"], - weatherball: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - rhinolite: {learnset: { - acrobatics: ["7L1"], - bite: ["7L1"], - bodyslam: ["7L1"], - bonemerang: ["7L1"], - bulletseed: ["7L1"], - charge: ["7L1"], - confusion: ["7L1"], - darkpulse: ["7L1"], - dragonrage: ["7L1"], - drillpeck: ["7L1"], - dualchop: ["7L1"], - earthpower: ["7L1"], - earthquake: ["7L1"], - fusionflare: ["7L1"], - guardswap: ["7L1"], - healbell: ["7L1"], - landswrath: ["7L1"], - lastresort: ["7L1"], - lightofruin: ["7L1"], - mirrormove: ["7L1"], - mirrorshot: ["7L1"], - mist: ["7L1"], - outrage: ["7L1"], - painsplit: ["7L1"], - poisontail: ["7L1"], - powergem: ["7L1"], - present: ["7L1"], - psychoboost: ["7L1"], - punishment: ["7L1"], - rocksmash: ["7L1"], - shadowsneak: ["7L1"], - sludge: ["7L1"], - sonicboom: ["7L1"], - stoneedge: ["7L1"], - strengthsap: ["7L1"], - surf: ["7L1"], - sweetkiss: ["7L1"], - thundershock: ["7L1"], - thunderwave: ["7L1"], - wish: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - bellena: {learnset: { - boltbeak: ["7L1"], - bulkup: ["7L1"], - charge: ["7L1"], - destinybond: ["7L1"], - diamondstorm: ["7L1"], - doublekick: ["7L1"], - dragonclaw: ["7L1"], - dragonpulse: ["7L1"], - dragonrush: ["7L1"], - electricterrain: ["7L1"], - energyball: ["7L1"], - flamewheel: ["7L1"], - foulplay: ["7L1"], - furyswipes: ["7L1"], - honeclaws: ["7L1"], - iceball: ["7L1"], - irontail: ["7L1"], - lowsweep: ["7L1"], - magmastorm: ["7L1"], - metalburst: ["7L1"], - metronome: ["7L1"], - moonblast: ["7L1"], - muddywater: ["7L1"], - nightshade: ["7L1"], - paraboliccharge: ["7L1"], - playrough: ["7L1"], - powder: ["7L1"], - psychicfangs: ["7L1"], - roost: ["7L1"], - searingshot: ["7L1"], - shadowbone: ["7L1"], - shelltrap: ["7L1"], - sludge: ["7L1"], - spiritshackle: ["7L1"], - spitup: ["7L1"], - stealthrock: ["7L1"], - submission: ["7L1"], - swift: ["7L1"], - throatchop: ["7L1"], - thunderfang: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - falcola: {learnset: { - amnesia: ["7L1"], - clangoroussoul: ["7L1"], - crosschop: ["7L1"], - darkvoid: ["7L1"], - discharge: ["7L1"], - doublehit: ["7L1"], - dracometeor: ["7L1"], - drillpeck: ["7L1"], - encore: ["7L1"], - energyball: ["7L1"], - entrainment: ["7L1"], - firelash: ["7L1"], - grasspledge: ["7L1"], - heartstamp: ["7L1"], - heatwave: ["7L1"], - helpinghand: ["7L1"], - hypnosis: ["7L1"], - icehammer: ["7L1"], - iciclespear: ["7L1"], - ironhead: ["7L1"], - leer: ["7L1"], - liquidation: ["7L1"], - lockon: ["7L1"], - nobleroar: ["7L1"], - octazooka: ["7L1"], - outrage: ["7L1"], - pound: ["7L1"], - psychoshift: ["7L1"], - razorwind: ["7L1"], - relicsong: ["7L1"], - retaliate: ["7L1"], - screech: ["7L1"], - snipeshot: ["7L1"], - spikecannon: ["7L1"], - steameruption: ["7L1"], - stormthrow: ["7L1"], - surf: ["7L1"], - thunderfang: ["7L1"], - vitalthrow: ["7L1"], - xscissor: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - beanium: {learnset: { - aromaticmist: ["7L1"], - avalanche: ["7L1"], - charm: ["7L1"], - clangingscales: ["7L1"], - dracometeor: ["7L1"], - energyball: ["7L1"], - finalgambit: ["7L1"], - fireblast: ["7L1"], - grasspledge: ["7L1"], - happyhour: ["7L1"], - healingwish: ["7L1"], - hornattack: ["7L1"], - hypervoice: ["7L1"], - karatechop: ["7L1"], - landswrath: ["7L1"], - lightofruin: ["7L1"], - magnitude: ["7L1"], - meanlook: ["7L1"], - megadrain: ["7L1"], - overdrive: ["7L1"], - paraboliccharge: ["7L1"], - poisonjab: ["7L1"], - pound: ["7L1"], - powder: ["7L1"], - powergem: ["7L1"], - psystrike: ["7L1"], - razorshell: ["7L1"], - reversal: ["7L1"], - rocksmash: ["7L1"], - rocktomb: ["7L1"], - sacredsword: ["7L1"], - scratch: ["7L1"], - searingshot: ["7L1"], - shadowbone: ["7L1"], - stoneedge: ["7L1"], - strength: ["7L1"], - superpower: ["7L1"], - tailslap: ["7L1"], - telekinesis: ["7L1"], - teleport: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - lemotic: {learnset: { - behemothbash: ["7L1"], - behemothblade: ["7L1"], - chargebeam: ["7L1"], - defendorder: ["7L1"], - diamondstorm: ["7L1"], - discharge: ["7L1"], - earthpower: ["7L1"], - earthquake: ["7L1"], - energyball: ["7L1"], - flash: ["7L1"], - fleurcannon: ["7L1"], - furyattack: ["7L1"], - futuresight: ["7L1"], - glaciate: ["7L1"], - heartswap: ["7L1"], - heatwave: ["7L1"], - hex: ["7L1"], - iceshard: ["7L1"], - laserfocus: ["7L1"], - leafblade: ["7L1"], - lusterpurge: ["7L1"], - mefirst: ["7L1"], - megahorn: ["7L1"], - metalburst: ["7L1"], - nightdaze: ["7L1"], - payback: ["7L1"], - payday: ["7L1"], - poisonjab: ["7L1"], - razorleaf: ["7L1"], - refresh: ["7L1"], - rockclimb: ["7L1"], - rockpolish: ["7L1"], - sandattack: ["7L1"], - shadowbone: ["7L1"], - steameruption: ["7L1"], - sweetkiss: ["7L1"], - swift: ["7L1"], - telekinesis: ["7L1"], - wish: ["7L1"], - withdraw: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - biceon: {learnset: { - aquajet: ["7L1"], - aromatherapy: ["7L1"], - assist: ["7L1"], - attackorder: ["7L1"], - bind: ["7L1"], - boltbeak: ["7L1"], - burnup: ["7L1"], - calmmind: ["7L1"], - dazzlinggleam: ["7L1"], - dreameater: ["7L1"], - earthpower: ["7L1"], - earthquake: ["7L1"], - echoedvoice: ["7L1"], - entrainment: ["7L1"], - extrasensory: ["7L1"], - feintattack: ["7L1"], - frostbreath: ["7L1"], - furyswipes: ["7L1"], - fusionflare: ["7L1"], - glaciate: ["7L1"], - gravity: ["7L1"], - headcharge: ["7L1"], - meteormash: ["7L1"], - mindreader: ["7L1"], - moonblast: ["7L1"], - nobleroar: ["7L1"], - noretreat: ["7L1"], - octazooka: ["7L1"], - perishsong: ["7L1"], - playrough: ["7L1"], - retaliate: ["7L1"], - roar: ["7L1"], - rollout: ["7L1"], - shadowpunch: ["7L1"], - stealthrock: ["7L1"], - supersonic: ["7L1"], - topsyturvy: ["7L1"], - triplekick: ["7L1"], - twister: ["7L1"], - uturn: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - skeleray: {learnset: { - allyswitch: ["7L1"], - anchorshot: ["7L1"], - ancientpower: ["7L1"], - dragonclaw: ["7L1"], - dragonhammer: ["7L1"], - dragonpulse: ["7L1"], - dragontail: ["7L1"], - earthquake: ["7L1"], - fairylock: ["7L1"], - focusenergy: ["7L1"], - iceshard: ["7L1"], - incinerate: ["7L1"], - jawlock: ["7L1"], - lovelykiss: ["7L1"], - lowkick: ["7L1"], - metalclaw: ["7L1"], - milkdrink: ["7L1"], - nastyplot: ["7L1"], - nobleroar: ["7L1"], - outrage: ["7L1"], - poisonpowder: ["7L1"], - poisonsting: ["7L1"], - powersplit: ["7L1"], - prismaticlaser: ["7L1"], - psywave: ["7L1"], - sandattack: ["7L1"], - scald: ["7L1"], - shadowball: ["7L1"], - shadowbone: ["7L1"], - sludgewave: ["7L1"], - smog: ["7L1"], - speedswap: ["7L1"], - teatime: ["7L1"], - thousandarrows: ["7L1"], - triplekick: ["7L1"], - waterpledge: ["7L1"], - watershuriken: ["7L1"], - wildcharge: ["7L1"], - wrap: ["7L1"], - zingzap: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - specyte: {learnset: { - acidarmor: ["7L1"], - amnesia: ["7L1"], - aromatherapy: ["7L1"], - bonerush: ["7L1"], - bounce: ["7L1"], - bravebird: ["7L1"], - bugbuzz: ["7L1"], - celebrate: ["7L1"], - conversion2: ["7L1"], - craftyshield: ["7L1"], - discharge: ["7L1"], - dragondance: ["7L1"], - electricterrain: ["7L1"], - fling: ["7L1"], - furycutter: ["7L1"], - harden: ["7L1"], - healblock: ["7L1"], - infestation: ["7L1"], - kingsshield: ["7L1"], - moonlight: ["7L1"], - naturesmadness: ["7L1"], - photongeyser: ["7L1"], - pluck: ["7L1"], - powdersnow: ["7L1"], - powergem: ["7L1"], - powertrip: ["7L1"], - recover: ["7L1"], - reflecttype: ["7L1"], - sandstorm: ["7L1"], - shadowball: ["7L1"], - shadowbone: ["7L1"], - shadowclaw: ["7L1"], - slam: ["7L1"], - snipeshot: ["7L1"], - strengthsap: ["7L1"], - strugglebug: ["7L1"], - synchronoise: ["7L1"], - technoblast: ["7L1"], - voltswitch: ["7L1"], - volttackle: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - ramron: {learnset: { - agility: ["7L1"], - aquaring: ["7L1"], - bugbuzz: ["7L1"], - craftyshield: ["7L1"], - dazzlinggleam: ["7L1"], - dig: ["7L1"], - dragonpulse: ["7L1"], - electrify: ["7L1"], - embargo: ["7L1"], - endeavor: ["7L1"], - flameburst: ["7L1"], - foulplay: ["7L1"], - futuresight: ["7L1"], - guardsplit: ["7L1"], - haze: ["7L1"], - heartswap: ["7L1"], - hyperspacefury: ["7L1"], - hypervoice: ["7L1"], - icebeam: ["7L1"], - landswrath: ["7L1"], - leafstorm: ["7L1"], - magnitude: ["7L1"], - meditate: ["7L1"], - nobleroar: ["7L1"], - partingshot: ["7L1"], - pound: ["7L1"], - powergem: ["7L1"], - psychocut: ["7L1"], - purify: ["7L1"], - raindance: ["7L1"], - rockpolish: ["7L1"], - rockslide: ["7L1"], - shadowpunch: ["7L1"], - sonicboom: ["7L1"], - stealthrock: ["7L1"], - stickyweb: ["7L1"], - strength: ["7L1"], - swagger: ["7L1"], - torment: ["7L1"], - willowisp: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - balar: {learnset: { - aeroblast: ["7L1"], - agility: ["7L1"], - autotomize: ["7L1"], - bounce: ["7L1"], - brutalswing: ["7L1"], - bugbuzz: ["7L1"], - conversion2: ["7L1"], - decorate: ["7L1"], - diamondstorm: ["7L1"], - disarmingvoice: ["7L1"], - drillpeck: ["7L1"], - embargo: ["7L1"], - focuspunch: ["7L1"], - gigaimpact: ["7L1"], - grasswhistle: ["7L1"], - gyroball: ["7L1"], - karatechop: ["7L1"], - kingsshield: ["7L1"], - lifedew: ["7L1"], - playnice: ["7L1"], - powergem: ["7L1"], - powerswap: ["7L1"], - precipiceblades: ["7L1"], - psychoboost: ["7L1"], - psychocut: ["7L1"], - psychup: ["7L1"], - roar: ["7L1"], - rocktomb: ["7L1"], - roleplay: ["7L1"], - screech: ["7L1"], - shadowball: ["7L1"], - soak: ["7L1"], - stealthrock: ["7L1"], - stoneedge: ["7L1"], - sunnyday: ["7L1"], - toxicspikes: ["7L1"], - trumpcard: ["7L1"], - uturn: ["7L1"], - vacuumwave: ["7L1"], - woodhammer: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - panthee: {learnset: { - aerialace: ["7L1"], - aircutter: ["7L1"], - aurasphere: ["7L1"], - boltbeak: ["7L1"], - boomburst: ["7L1"], - bugbuzz: ["7L1"], - crunch: ["7L1"], - crushgrip: ["7L1"], - darkestlariat: ["7L1"], - electrify: ["7L1"], - flameburst: ["7L1"], - flashcannon: ["7L1"], - followme: ["7L1"], - healpulse: ["7L1"], - helpinghand: ["7L1"], - ironhead: ["7L1"], - leaftornado: ["7L1"], - lightofruin: ["7L1"], - lockon: ["7L1"], - moonlight: ["7L1"], - mudslap: ["7L1"], - payday: ["7L1"], - phantomforce: ["7L1"], - poisontail: ["7L1"], - psychic: ["7L1"], - psychicterrain: ["7L1"], - psychoshift: ["7L1"], - reflect: ["7L1"], - retaliate: ["7L1"], - shadowball: ["7L1"], - skyattack: ["7L1"], - skyuppercut: ["7L1"], - spikecannon: ["7L1"], - spiritbreak: ["7L1"], - sunnyday: ["7L1"], - superpower: ["7L1"], - swordsdance: ["7L1"], - thunderwave: ["7L1"], - uturn: ["7L1"], - waterfall: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - blastora: {learnset: { - attackorder: ["7L1"], - autotomize: ["7L1"], - behemothblade: ["7L1"], - blazekick: ["7L1"], - branchpoke: ["7L1"], - burnup: ["7L1"], - coil: ["7L1"], - confuseray: ["7L1"], - defendorder: ["7L1"], - defog: ["7L1"], - dragonclaw: ["7L1"], - falsesurrender: ["7L1"], - flamethrower: ["7L1"], - followme: ["7L1"], - glare: ["7L1"], - healingwish: ["7L1"], - holdhands: ["7L1"], - irondefense: ["7L1"], - ironhead: ["7L1"], - lockon: ["7L1"], - magnitude: ["7L1"], - metalclaw: ["7L1"], - metalsound: ["7L1"], - mindreader: ["7L1"], - mist: ["7L1"], - mudsport: ["7L1"], - nightdaze: ["7L1"], - partingshot: ["7L1"], - payback: ["7L1"], - peck: ["7L1"], - phantomforce: ["7L1"], - pluck: ["7L1"], - psystrike: ["7L1"], - shadowpunch: ["7L1"], - slash: ["7L1"], - spectralthief: ["7L1"], - stickyweb: ["7L1"], - swordsdance: ["7L1"], - synthesis: ["7L1"], - wringout: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - dropacle: {learnset: { - aurorabeam: ["7L1"], - behemothblade: ["7L1"], - bonerush: ["7L1"], - chargebeam: ["7L1"], - chipaway: ["7L1"], - disarmingvoice: ["7L1"], - dragondance: ["7L1"], - fakeout: ["7L1"], - falsesurrender: ["7L1"], - fierydance: ["7L1"], - gigadrain: ["7L1"], - happyhour: ["7L1"], - holdhands: ["7L1"], - honeclaws: ["7L1"], - irondefense: ["7L1"], - leafblade: ["7L1"], - lunardance: ["7L1"], - magnetbomb: ["7L1"], - meanlook: ["7L1"], - metalburst: ["7L1"], - mist: ["7L1"], - powergem: ["7L1"], - precipiceblades: ["7L1"], - quickguard: ["7L1"], - rocksmash: ["7L1"], - screech: ["7L1"], - shadowball: ["7L1"], - shadowbone: ["7L1"], - sludgebomb: ["7L1"], - smog: ["7L1"], - smokescreen: ["7L1"], - softboiled: ["7L1"], - spark: ["7L1"], - surf: ["7L1"], - synthesis: ["7L1"], - tailslap: ["7L1"], - tarshot: ["7L1"], - toxicspikes: ["7L1"], - waterfall: ["7L1"], - waterpulse: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - dolphena: {learnset: { - armthrust: ["7L1"], - banefulbunker: ["7L1"], - belch: ["7L1"], - blazekick: ["7L1"], - bubblebeam: ["7L1"], - dragonclaw: ["7L1"], - dragonpulse: ["7L1"], - dragontail: ["7L1"], - drillpeck: ["7L1"], - extrasensory: ["7L1"], - fellstinger: ["7L1"], - firefang: ["7L1"], - frostbreath: ["7L1"], - highhorsepower: ["7L1"], - hydrocannon: ["7L1"], - irondefense: ["7L1"], - landswrath: ["7L1"], - leafstorm: ["7L1"], - magneticflux: ["7L1"], - mindreader: ["7L1"], - oblivionwing: ["7L1"], - ominouswind: ["7L1"], - outrage: ["7L1"], - powder: ["7L1"], - psychup: ["7L1"], - psywave: ["7L1"], - reflecttype: ["7L1"], - revelationdance: ["7L1"], - rockthrow: ["7L1"], - silverwind: ["7L1"], - snatch: ["7L1"], - softboiled: ["7L1"], - suckerpunch: ["7L1"], - superpower: ["7L1"], - surf: ["7L1"], - thief: ["7L1"], - thousandwaves: ["7L1"], - thunderbolt: ["7L1"], - trickortreat: ["7L1"], - waterfall: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - catelax: {learnset: { - assist: ["7L1"], - brickbreak: ["7L1"], - bugbuzz: ["7L1"], - constrict: ["7L1"], - dracometeor: ["7L1"], - dreameater: ["7L1"], - electrify: ["7L1"], - fakeout: ["7L1"], - finalgambit: ["7L1"], - flamethrower: ["7L1"], - grassyterrain: ["7L1"], - growl: ["7L1"], - harden: ["7L1"], - healblock: ["7L1"], - healingwish: ["7L1"], - heavyslam: ["7L1"], - imprison: ["7L1"], - inferno: ["7L1"], - machpunch: ["7L1"], - mistyterrain: ["7L1"], - octolock: ["7L1"], - phantomforce: ["7L1"], - poisonjab: ["7L1"], - powertrip: ["7L1"], - sandtomb: ["7L1"], - scald: ["7L1"], - shadowforce: ["7L1"], - simplebeam: ["7L1"], - stomp: ["7L1"], - strangesteam: ["7L1"], - submission: ["7L1"], - superfang: ["7L1"], - tackle: ["7L1"], - tailslap: ["7L1"], - technoblast: ["7L1"], - thunderbolt: ["7L1"], - tropkick: ["7L1"], - uturn: ["7L1"], - willowisp: ["7L1"], - xscissor: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - fluffora: {learnset: { - aircutter: ["7L1"], - assist: ["7L1"], - block: ["7L1"], - bulletseed: ["7L1"], - chipaway: ["7L1"], - discharge: ["7L1"], - doublehit: ["7L1"], - dragonrage: ["7L1"], - dragonrush: ["7L1"], - energyball: ["7L1"], - explosion: ["7L1"], - finalgambit: ["7L1"], - flail: ["7L1"], - flowershield: ["7L1"], - freezeshock: ["7L1"], - guardsplit: ["7L1"], - healbell: ["7L1"], - highhorsepower: ["7L1"], - hydropump: ["7L1"], - hypnosis: ["7L1"], - icepunch: ["7L1"], - iciclecrash: ["7L1"], - judgment: ["7L1"], - knockoff: ["7L1"], - leafblade: ["7L1"], - lowkick: ["7L1"], - metalburst: ["7L1"], - metalclaw: ["7L1"], - mysticalfire: ["7L1"], - petaldance: ["7L1"], - reflect: ["7L1"], - refresh: ["7L1"], - revelationdance: ["7L1"], - screech: ["7L1"], - seismictoss: ["7L1"], - shadowpunch: ["7L1"], - slam: ["7L1"], - spikes: ["7L1"], - thousandwaves: ["7L1"], - thunderbolt: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, - tigire: {learnset: { - acid: ["7L1"], - aurasphere: ["7L1"], - blazekick: ["7L1"], - bonemerang: ["7L1"], - bulletpunch: ["7L1"], - crabhammer: ["7L1"], - craftyshield: ["7L1"], - crosschop: ["7L1"], - dragonclaw: ["7L1"], - extrasensory: ["7L1"], - falseswipe: ["7L1"], - feint: ["7L1"], - feintattack: ["7L1"], - fellstinger: ["7L1"], - firelash: ["7L1"], - flamethrower: ["7L1"], - flatter: ["7L1"], - fusionflare: ["7L1"], - glare: ["7L1"], - guardsplit: ["7L1"], - gyroball: ["7L1"], - hammerarm: ["7L1"], - heartstamp: ["7L1"], - heatcrash: ["7L1"], - hydrocannon: ["7L1"], - hyperspacefury: ["7L1"], - laserfocus: ["7L1"], - lunge: ["7L1"], - payback: ["7L1"], - poisontail: ["7L1"], - rockpolish: ["7L1"], - roost: ["7L1"], - sandstorm: ["7L1"], - seismictoss: ["7L1"], - shadowball: ["7L1"], - solarblade: ["7L1"], - spitup: ["7L1"], - vacuumwave: ["7L1"], - wakeupslap: ["7L1"], - wish: ["7L1"], - attract: ["7L1"], - captivate: ["7L1"], - confide: ["7L1"], - endure: ["7L1"], - facade: ["7L1"], - frustration: ["7L1"], - hiddenpower: ["7L1"], - naturalgift: ["7L1"], - protect: ["7L1"], - rest: ["7L1"], - return: ["7L1"], - round: ["7L1"], - secretpower: ["7L1"], - sleeptalk: ["7L1"], - substitute: ["7L1"], - toxic: ["7L1"], - }}, -}; - -exports.BattleLearnsets = BattleLearnsets; diff --git a/data/mods/roulettemons/learnsets.ts b/data/mods/roulettemons/learnsets.ts new file mode 100644 index 0000000000..a8c8c1edca --- /dev/null +++ b/data/mods/roulettemons/learnsets.ts @@ -0,0 +1,2997 @@ +export const BattleLearnsets: {[k: string]: ModdedLearnsetData} = { + koatric: { + learnset: { + barrage: ["7L1"], + boomburst: ["7L1"], + charge: ["7L1"], + crunch: ["7L1"], + curse: ["7L1"], + detect: ["7L1"], + dragondance: ["7L1"], + dreameater: ["7L1"], + feint: ["7L1"], + forcepalm: ["7L1"], + fusionbolt: ["7L1"], + glare: ["7L1"], + gravity: ["7L1"], + haze: ["7L1"], + lightofruin: ["7L1"], + magicalleaf: ["7L1"], + meteormash: ["7L1"], + moonlight: ["7L1"], + naturesmadness: ["7L1"], + outrage: ["7L1"], + pinmissile: ["7L1"], + poisonsting: ["7L1"], + psychic: ["7L1"], + rockthrow: ["7L1"], + skillswap: ["7L1"], + snatch: ["7L1"], + soak: ["7L1"], + spite: ["7L1"], + steelwing: ["7L1"], + superfang: ["7L1"], + superpower: ["7L1"], + supersonic: ["7L1"], + surf: ["7L1"], + taunt: ["7L1"], + thunderbolt: ["7L1"], + thunderpunch: ["7L1"], + toxicspikes: ["7L1"], + venoshock: ["7L1"], + wingattack: ["7L1"], + wrap: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + aquazelle: { + learnset: { + aromaticmist: ["7L1"], + bide: ["7L1"], + boneclub: ["7L1"], + bulletpunch: ["7L1"], + crushgrip: ["7L1"], + discharge: ["7L1"], + falseswipe: ["7L1"], + firstimpression: ["7L1"], + forestscurse: ["7L1"], + frostbreath: ["7L1"], + fusionflare: ["7L1"], + grassknot: ["7L1"], + gunkshot: ["7L1"], + hypervoice: ["7L1"], + iceburn: ["7L1"], + irontail: ["7L1"], + liquidation: ["7L1"], + machpunch: ["7L1"], + mefirst: ["7L1"], + nightslash: ["7L1"], + nobleroar: ["7L1"], + playrough: ["7L1"], + psychup: ["7L1"], + purify: ["7L1"], + revelationdance: ["7L1"], + roleplay: ["7L1"], + rollingkick: ["7L1"], + roost: ["7L1"], + selfdestruct: ["7L1"], + simplebeam: ["7L1"], + snatch: ["7L1"], + steameruption: ["7L1"], + strength: ["7L1"], + supersonic: ["7L1"], + surf: ["7L1"], + sweetscent: ["7L1"], + thunderbolt: ["7L1"], + toxicspikes: ["7L1"], + triattack: ["7L1"], + waterfall: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + salamalix: { + learnset: { + agility: ["7L1"], + bonemerang: ["7L1"], + bounce: ["7L1"], + counter: ["7L1"], + crunch: ["7L1"], + crushclaw: ["7L1"], + darkpulse: ["7L1"], + dragonbreath: ["7L1"], + dragontail: ["7L1"], + earthpower: ["7L1"], + fellstinger: ["7L1"], + flamewheel: ["7L1"], + flowershield: ["7L1"], + focusblast: ["7L1"], + headsmash: ["7L1"], + heartswap: ["7L1"], + holdhands: ["7L1"], + jumpkick: ["7L1"], + lowkick: ["7L1"], + lunge: ["7L1"], + mistyterrain: ["7L1"], + octazooka: ["7L1"], + outrage: ["7L1"], + pinmissile: ["7L1"], + poisonjab: ["7L1"], + powergem: ["7L1"], + psyshock: ["7L1"], + quickattack: ["7L1"], + recycle: ["7L1"], + rockwrecker: ["7L1"], + rollingkick: ["7L1"], + sacredfire: ["7L1"], + sacredsword: ["7L1"], + shadowpunch: ["7L1"], + simplebeam: ["7L1"], + skyattack: ["7L1"], + spiderweb: ["7L1"], + stoneedge: ["7L1"], + thousandwaves: ["7L1"], + vitalthrow: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + brawnkey: { + learnset: { + airslash: ["7L1"], + aurasphere: ["7L1"], + avalanche: ["7L1"], + closecombat: ["7L1"], + destinybond: ["7L1"], + disarmingvoice: ["7L1"], + dragondance: ["7L1"], + dynamicpunch: ["7L1"], + falseswipe: ["7L1"], + firepunch: ["7L1"], + focusenergy: ["7L1"], + haze: ["7L1"], + honeclaws: ["7L1"], + hyperfang: ["7L1"], + lastresort: ["7L1"], + lavaplume: ["7L1"], + leafstorm: ["7L1"], + lusterpurge: ["7L1"], + metalburst: ["7L1"], + metalsound: ["7L1"], + mirrorshot: ["7L1"], + naturesmadness: ["7L1"], + needlearm: ["7L1"], + rockblast: ["7L1"], + rockpolish: ["7L1"], + sacredfire: ["7L1"], + skyuppercut: ["7L1"], + snatch: ["7L1"], + snore: ["7L1"], + spikyshield: ["7L1"], + stealthrock: ["7L1"], + storedpower: ["7L1"], + tailwhip: ["7L1"], + thunderpunch: ["7L1"], + tropkick: ["7L1"], + uproar: ["7L1"], + vicegrip: ["7L1"], + wakeupslap: ["7L1"], + withdraw: ["7L1"], + yawn: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + stuneleon: { + learnset: { + aircutter: ["7L1"], + bonemerang: ["7L1"], + bulldoze: ["7L1"], + clangingscales: ["7L1"], + conversion2: ["7L1"], + craftyshield: ["7L1"], + crunch: ["7L1"], + crushgrip: ["7L1"], + dragonclaw: ["7L1"], + earthpower: ["7L1"], + falseswipe: ["7L1"], + frostbreath: ["7L1"], + fusionbolt: ["7L1"], + heatwave: ["7L1"], + holdhands: ["7L1"], + iceball: ["7L1"], + ironhead: ["7L1"], + knockoff: ["7L1"], + machpunch: ["7L1"], + magnetrise: ["7L1"], + meditate: ["7L1"], + metronome: ["7L1"], + moonlight: ["7L1"], + naturesmadness: ["7L1"], + poisonjab: ["7L1"], + pound: ["7L1"], + rage: ["7L1"], + reflect: ["7L1"], + scratch: ["7L1"], + shadowball: ["7L1"], + sing: ["7L1"], + sludgebomb: ["7L1"], + struggle: ["7L1"], + teleport: ["7L1"], + thousandarrows: ["7L1"], + thunderbolt: ["7L1"], + tickle: ["7L1"], + trick: ["7L1"], + wakeupslap: ["7L1"], + yawn: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + chillyte: { + learnset: { + assist: ["7L1"], + auroraveil: ["7L1"], + blastburn: ["7L1"], + blueflare: ["7L1"], + bodyslam: ["7L1"], + bugbuzz: ["7L1"], + defendorder: ["7L1"], + earthpower: ["7L1"], + electroweb: ["7L1"], + embargo: ["7L1"], + entrainment: ["7L1"], + fellstinger: ["7L1"], + grassknot: ["7L1"], + icebeam: ["7L1"], + iceburn: ["7L1"], + icepunch: ["7L1"], + iciclecrash: ["7L1"], + icywind: ["7L1"], + irondefense: ["7L1"], + leafage: ["7L1"], + meteormash: ["7L1"], + mirrormove: ["7L1"], + moonlight: ["7L1"], + payday: ["7L1"], + poisongas: ["7L1"], + psychic: ["7L1"], + rage: ["7L1"], + rototiller: ["7L1"], + scald: ["7L1"], + seedbomb: ["7L1"], + skillswap: ["7L1"], + snarl: ["7L1"], + spark: ["7L1"], + submission: ["7L1"], + suckerpunch: ["7L1"], + telekinesis: ["7L1"], + triattack: ["7L1"], + vacuumwave: ["7L1"], + vcreate: ["7L1"], + wildcharge: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + spikes: ["7L1"], + pursuit: ["7L1"], + }, + }, + eartharoo: { + learnset: { + aquatail: ["7L1"], + auroraveil: ["7L1"], + brutalswing: ["7L1"], + covet: ["7L1"], + defog: ["7L1"], + drainingkiss: ["7L1"], + earthpower: ["7L1"], + earthquake: ["7L1"], + electricterrain: ["7L1"], + encore: ["7L1"], + explosion: ["7L1"], + fairywind: ["7L1"], + foresight: ["7L1"], + growl: ["7L1"], + headsmash: ["7L1"], + hex: ["7L1"], + hypervoice: ["7L1"], + iceburn: ["7L1"], + iceshard: ["7L1"], + irontail: ["7L1"], + metalburst: ["7L1"], + milkdrink: ["7L1"], + morningsun: ["7L1"], + poisonfang: ["7L1"], + psychicfangs: ["7L1"], + punishment: ["7L1"], + pursuit: ["7L1"], + recover: ["7L1"], + recycle: ["7L1"], + relicsong: ["7L1"], + sandtomb: ["7L1"], + secretsword: ["7L1"], + storedpower: ["7L1"], + stormthrow: ["7L1"], + strength: ["7L1"], + thundershock: ["7L1"], + topsyturvy: ["7L1"], + tropkick: ["7L1"], + wingattack: ["7L1"], + zingzap: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + crazefly: { + learnset: { + acidspray: ["7L1"], + anchorshot: ["7L1"], + beakblast: ["7L1"], + bugbuzz: ["7L1"], + charm: ["7L1"], + cometpunch: ["7L1"], + crosspoison: ["7L1"], + destinybond: ["7L1"], + electricterrain: ["7L1"], + fairywind: ["7L1"], + frostbreath: ["7L1"], + fusionflare: ["7L1"], + helpinghand: ["7L1"], + highhorsepower: ["7L1"], + iceshard: ["7L1"], + jumpkick: ["7L1"], + lunge: ["7L1"], + lusterpurge: ["7L1"], + magicalleaf: ["7L1"], + meditate: ["7L1"], + mimic: ["7L1"], + mistyterrain: ["7L1"], + moongeistbeam: ["7L1"], + playrough: ["7L1"], + psychic: ["7L1"], + psychoboost: ["7L1"], + quickattack: ["7L1"], + rapidspin: ["7L1"], + roost: ["7L1"], + shadowpunch: ["7L1"], + skullbash: ["7L1"], + smellingsalts: ["7L1"], + spacialrend: ["7L1"], + spotlight: ["7L1"], + steamroller: ["7L1"], + thundershock: ["7L1"], + wish: ["7L1"], + worryseed: ["7L1"], + xscissor: ["7L1"], + zenheadbutt: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + electritar: { + learnset: { + afteryou: ["7L1"], + blueflare: ["7L1"], + bonerush: ["7L1"], + celebrate: ["7L1"], + clearsmog: ["7L1"], + cometpunch: ["7L1"], + conversion2: ["7L1"], + diamondstorm: ["7L1"], + dreameater: ["7L1"], + energyball: ["7L1"], + fairywind: ["7L1"], + feint: ["7L1"], + firefang: ["7L1"], + firepledge: ["7L1"], + flail: ["7L1"], + flameburst: ["7L1"], + flashcannon: ["7L1"], + flatter: ["7L1"], + flyingpress: ["7L1"], + forestscurse: ["7L1"], + furycutter: ["7L1"], + fusionbolt: ["7L1"], + hammerarm: ["7L1"], + headcharge: ["7L1"], + hornleech: ["7L1"], + ironhead: ["7L1"], + playrough: ["7L1"], + powdersnow: ["7L1"], + psychic: ["7L1"], + rage: ["7L1"], + refresh: ["7L1"], + sacredsword: ["7L1"], + safeguard: ["7L1"], + shadowclaw: ["7L1"], + shoreup: ["7L1"], + surf: ["7L1"], + takedown: ["7L1"], + thunderbolt: ["7L1"], + wideguard: ["7L1"], + yawn: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + aquatopus: { + learnset: { + ancientpower: ["7L1"], + aquaring: ["7L1"], + burnup: ["7L1"], + calmmind: ["7L1"], + counter: ["7L1"], + crosschop: ["7L1"], + earthpower: ["7L1"], + electricterrain: ["7L1"], + flail: ["7L1"], + followme: ["7L1"], + grudge: ["7L1"], + headsmash: ["7L1"], + helpinghand: ["7L1"], + icehammer: ["7L1"], + infestation: ["7L1"], + lavaplume: ["7L1"], + lick: ["7L1"], + liquidation: ["7L1"], + mefirst: ["7L1"], + megahorn: ["7L1"], + metalclaw: ["7L1"], + mist: ["7L1"], + naturesmadness: ["7L1"], + nobleroar: ["7L1"], + oblivionwing: ["7L1"], + odorsleuth: ["7L1"], + payday: ["7L1"], + poisontail: ["7L1"], + sacredfire: ["7L1"], + shadowclaw: ["7L1"], + slackoff: ["7L1"], + slash: ["7L1"], + stealthrock: ["7L1"], + stickyweb: ["7L1"], + superpower: ["7L1"], + surf: ["7L1"], + uproar: ["7L1"], + wakeupslap: ["7L1"], + waterfall: ["7L1"], + wish: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + scorpita: { + learnset: { + acrobatics: ["7L1"], + acupressure: ["7L1"], + aircutter: ["7L1"], + anchorshot: ["7L1"], + aromatherapy: ["7L1"], + banefulbunker: ["7L1"], + boneclub: ["7L1"], + bounce: ["7L1"], + brickbreak: ["7L1"], + chargebeam: ["7L1"], + coreenforcer: ["7L1"], + crushclaw: ["7L1"], + disarmingvoice: ["7L1"], + dracometeor: ["7L1"], + drillpeck: ["7L1"], + earthpower: ["7L1"], + flamethrower: ["7L1"], + hex: ["7L1"], + holdhands: ["7L1"], + hyperbeam: ["7L1"], + icehammer: ["7L1"], + iciclecrash: ["7L1"], + mudbomb: ["7L1"], + poisonjab: ["7L1"], + retaliate: ["7L1"], + reversal: ["7L1"], + sludgebomb: ["7L1"], + snore: ["7L1"], + soak: ["7L1"], + solarblade: ["7L1"], + spikyshield: ["7L1"], + spiritshackle: ["7L1"], + steelwing: ["7L1"], + superfang: ["7L1"], + sweetkiss: ["7L1"], + swift: ["7L1"], + telekinesis: ["7L1"], + triplekick: ["7L1"], + zenheadbutt: ["7L1"], + zingzap: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + baloon: { + learnset: { + acupressure: ["7L1"], + amnesia: ["7L1"], + anchorshot: ["7L1"], + assurance: ["7L1"], + banefulbunker: ["7L1"], + clangingscales: ["7L1"], + conversion: ["7L1"], + cottonguard: ["7L1"], + darkestlariat: ["7L1"], + disarmingvoice: ["7L1"], + dragonbreath: ["7L1"], + dragondance: ["7L1"], + echoedvoice: ["7L1"], + electroball: ["7L1"], + fakeout: ["7L1"], + feintattack: ["7L1"], + firepunch: ["7L1"], + healorder: ["7L1"], + heartswap: ["7L1"], + hypervoice: ["7L1"], + icywind: ["7L1"], + machpunch: ["7L1"], + mistyterrain: ["7L1"], + moonblast: ["7L1"], + nobleroar: ["7L1"], + playrough: ["7L1"], + present: ["7L1"], + quash: ["7L1"], + reversal: ["7L1"], + rockthrow: ["7L1"], + seismictoss: ["7L1"], + spacialrend: ["7L1"], + spiritshackle: ["7L1"], + storedpower: ["7L1"], + superfang: ["7L1"], + tackle: ["7L1"], + toxicspikes: ["7L1"], + volttackle: ["7L1"], + watergun: ["7L1"], + waterspout: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + kinesel: { + learnset: { + allyswitch: ["7L1"], + aquatail: ["7L1"], + blazekick: ["7L1"], + bulletpunch: ["7L1"], + dizzypunch: ["7L1"], + doublehit: ["7L1"], + dragonrage: ["7L1"], + dragonrush: ["7L1"], + explosion: ["7L1"], + firepunch: ["7L1"], + flowershield: ["7L1"], + heartswap: ["7L1"], + hex: ["7L1"], + hornattack: ["7L1"], + ingrain: ["7L1"], + iondeluge: ["7L1"], + leechlife: ["7L1"], + lightofruin: ["7L1"], + mirrorshot: ["7L1"], + moonblast: ["7L1"], + painsplit: ["7L1"], + paraboliccharge: ["7L1"], + playrough: ["7L1"], + powertrick: ["7L1"], + psybeam: ["7L1"], + psychic: ["7L1"], + relicsong: ["7L1"], + shadowbone: ["7L1"], + shadowclaw: ["7L1"], + shockwave: ["7L1"], + spikecannon: ["7L1"], + stompingtantrum: ["7L1"], + swordsdance: ["7L1"], + thousandwaves: ["7L1"], + thundershock: ["7L1"], + trickroom: ["7L1"], + uturn: ["7L1"], + wildcharge: ["7L1"], + wringout: ["7L1"], + zenheadbutt: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + glacida: { + learnset: { + anchorshot: ["7L1"], + aurasphere: ["7L1"], + bonemerang: ["7L1"], + brutalswing: ["7L1"], + bubblebeam: ["7L1"], + bulletseed: ["7L1"], + encore: ["7L1"], + gearup: ["7L1"], + heatwave: ["7L1"], + hydrocannon: ["7L1"], + icebeam: ["7L1"], + iceburn: ["7L1"], + iciclecrash: ["7L1"], + incinerate: ["7L1"], + leechseed: ["7L1"], + lightscreen: ["7L1"], + magicalleaf: ["7L1"], + magicroom: ["7L1"], + milkdrink: ["7L1"], + moongeistbeam: ["7L1"], + morningsun: ["7L1"], + playrough: ["7L1"], + pound: ["7L1"], + psychoboost: ["7L1"], + punishment: ["7L1"], + quash: ["7L1"], + recycle: ["7L1"], + reversal: ["7L1"], + secretpower: ["7L1"], + skyuppercut: ["7L1"], + sludgebomb: ["7L1"], + solarbeam: ["7L1"], + spikyshield: ["7L1"], + swagger: ["7L1"], + synchronoise: ["7L1"], + toxicspikes: ["7L1"], + withdraw: ["7L1"], + woodhammer: ["7L1"], + workup: ["7L1"], + yawn: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + pidgeotine: { + learnset: { + absorb: ["7L1"], + acid: ["7L1"], + aeroblast: ["7L1"], + agility: ["7L1"], + aurasphere: ["7L1"], + blizzard: ["7L1"], + crushclaw: ["7L1"], + cut: ["7L1"], + discharge: ["7L1"], + dragonbreath: ["7L1"], + dragonclaw: ["7L1"], + dragontail: ["7L1"], + drillpeck: ["7L1"], + endeavor: ["7L1"], + fleurcannon: ["7L1"], + grassyterrain: ["7L1"], + haze: ["7L1"], + healbell: ["7L1"], + healblock: ["7L1"], + hypervoice: ["7L1"], + iceburn: ["7L1"], + icywind: ["7L1"], + lavaplume: ["7L1"], + liquidation: ["7L1"], + lovelykiss: ["7L1"], + mimic: ["7L1"], + miracleeye: ["7L1"], + mudslap: ["7L1"], + multiattack: ["7L1"], + payback: ["7L1"], + powersplit: ["7L1"], + precipiceblades: ["7L1"], + roar: ["7L1"], + rocksmash: ["7L1"], + strength: ["7L1"], + struggle: ["7L1"], + teeterdance: ["7L1"], + telekinesis: ["7L1"], + uturn: ["7L1"], + yawn: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + gorilax: { + learnset: { + amnesia: ["7L1"], + aurasphere: ["7L1"], + blueflare: ["7L1"], + doubleslap: ["7L1"], + drainpunch: ["7L1"], + earthpower: ["7L1"], + firepledge: ["7L1"], + flameburst: ["7L1"], + gastroacid: ["7L1"], + hammerarm: ["7L1"], + icefang: ["7L1"], + icehammer: ["7L1"], + leaftornado: ["7L1"], + lunge: ["7L1"], + magicroom: ["7L1"], + meanlook: ["7L1"], + morningsun: ["7L1"], + nightshade: ["7L1"], + ominouswind: ["7L1"], + powersplit: ["7L1"], + powerwhip: ["7L1"], + psychic: ["7L1"], + psyshock: ["7L1"], + quickguard: ["7L1"], + rockclimb: ["7L1"], + rockslide: ["7L1"], + shoreup: ["7L1"], + skullbash: ["7L1"], + skyuppercut: ["7L1"], + sludgebomb: ["7L1"], + smellingsalts: ["7L1"], + spark: ["7L1"], + spotlight: ["7L1"], + steamroller: ["7L1"], + tackle: ["7L1"], + triplekick: ["7L1"], + wildcharge: ["7L1"], + wingattack: ["7L1"], + wish: ["7L1"], + zenheadbutt: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + albatrygon: { + learnset: { + aeroblast: ["7L1"], + aurasphere: ["7L1"], + aurorabeam: ["7L1"], + avalanche: ["7L1"], + beakblast: ["7L1"], + block: ["7L1"], + charge: ["7L1"], + crabhammer: ["7L1"], + crushgrip: ["7L1"], + defendorder: ["7L1"], + drillpeck: ["7L1"], + drillrun: ["7L1"], + focuspunch: ["7L1"], + foresight: ["7L1"], + foulplay: ["7L1"], + harden: ["7L1"], + headbutt: ["7L1"], + icefang: ["7L1"], + ingrain: ["7L1"], + leafage: ["7L1"], + liquidation: ["7L1"], + memento: ["7L1"], + metronome: ["7L1"], + mimic: ["7L1"], + mirrorcoat: ["7L1"], + morningsun: ["7L1"], + overheat: ["7L1"], + photongeyser: ["7L1"], + purify: ["7L1"], + rapidspin: ["7L1"], + rockclimb: ["7L1"], + secretpower: ["7L1"], + seedflare: ["7L1"], + shockwave: ["7L1"], + skillswap: ["7L1"], + stickyweb: ["7L1"], + struggle: ["7L1"], + strugglebug: ["7L1"], + sunnyday: ["7L1"], + thunderbolt: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + chillvark: { + learnset: { + aquatail: ["7L1"], + assurance: ["7L1"], + aurasphere: ["7L1"], + blueflare: ["7L1"], + bonemerang: ["7L1"], + bubblebeam: ["7L1"], + crabhammer: ["7L1"], + doublekick: ["7L1"], + firepledge: ["7L1"], + flash: ["7L1"], + focusblast: ["7L1"], + furycutter: ["7L1"], + growth: ["7L1"], + gust: ["7L1"], + healingwish: ["7L1"], + holdhands: ["7L1"], + icebeam: ["7L1"], + iciclecrash: ["7L1"], + ingrain: ["7L1"], + judgment: ["7L1"], + magmastorm: ["7L1"], + miracleeye: ["7L1"], + mistball: ["7L1"], + moonblast: ["7L1"], + nightmare: ["7L1"], + ominouswind: ["7L1"], + painsplit: ["7L1"], + perishsong: ["7L1"], + rockblast: ["7L1"], + rollingkick: ["7L1"], + safeguard: ["7L1"], + skyuppercut: ["7L1"], + slackoff: ["7L1"], + slam: ["7L1"], + snarl: ["7L1"], + softboiled: ["7L1"], + stunspore: ["7L1"], + surf: ["7L1"], + wideguard: ["7L1"], + wonderroom: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + komodith: { + learnset: { + auroraveil: ["7L1"], + bestow: ["7L1"], + boomburst: ["7L1"], + bravebird: ["7L1"], + bulkup: ["7L1"], + bulletseed: ["7L1"], + constrict: ["7L1"], + crushgrip: ["7L1"], + disarmingvoice: ["7L1"], + dracometeor: ["7L1"], + dragonclaw: ["7L1"], + dragonpulse: ["7L1"], + earthpower: ["7L1"], + earthquake: ["7L1"], + flashcannon: ["7L1"], + fling: ["7L1"], + floralhealing: ["7L1"], + freezeshock: ["7L1"], + gunkshot: ["7L1"], + healingwish: ["7L1"], + helpinghand: ["7L1"], + hornleech: ["7L1"], + iceshard: ["7L1"], + ironhead: ["7L1"], + landswrath: ["7L1"], + lockon: ["7L1"], + nightslash: ["7L1"], + overheat: ["7L1"], + psychoboost: ["7L1"], + razorleaf: ["7L1"], + razorwind: ["7L1"], + relicsong: ["7L1"], + secretpower: ["7L1"], + shockwave: ["7L1"], + spikecannon: ["7L1"], + sunnyday: ["7L1"], + tickle: ["7L1"], + trumpcard: ["7L1"], + workup: ["7L1"], + zenheadbutt: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + giranium: { + learnset: { + aromatherapy: ["7L1"], + aromaticmist: ["7L1"], + bestow: ["7L1"], + bugbuzz: ["7L1"], + crabhammer: ["7L1"], + defog: ["7L1"], + energyball: ["7L1"], + flareblitz: ["7L1"], + flash: ["7L1"], + flashcannon: ["7L1"], + flatter: ["7L1"], + freezedry: ["7L1"], + freezeshock: ["7L1"], + furyswipes: ["7L1"], + hail: ["7L1"], + holdhands: ["7L1"], + infestation: ["7L1"], + instruct: ["7L1"], + ironhead: ["7L1"], + leafblade: ["7L1"], + meditate: ["7L1"], + metalsound: ["7L1"], + moonlight: ["7L1"], + pluck: ["7L1"], + powergem: ["7L1"], + powersplit: ["7L1"], + psychocut: ["7L1"], + pursuit: ["7L1"], + quickguard: ["7L1"], + relicsong: ["7L1"], + seismictoss: ["7L1"], + simplebeam: ["7L1"], + sing: ["7L1"], + smokescreen: ["7L1"], + speedswap: ["7L1"], + stoneedge: ["7L1"], + stormthrow: ["7L1"], + stunspore: ["7L1"], + thunderbolt: ["7L1"], + waterspout: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + flamyle: { + learnset: { + bide: ["7L1"], + blazekick: ["7L1"], + boneclub: ["7L1"], + bonemerang: ["7L1"], + bubblebeam: ["7L1"], + cottonguard: ["7L1"], + cut: ["7L1"], + destinybond: ["7L1"], + dizzypunch: ["7L1"], + extrasensory: ["7L1"], + firefang: ["7L1"], + flamethrower: ["7L1"], + freezeshock: ["7L1"], + furyattack: ["7L1"], + glare: ["7L1"], + grassknot: ["7L1"], + healorder: ["7L1"], + heartswap: ["7L1"], + jumpkick: ["7L1"], + karatechop: ["7L1"], + laserfocus: ["7L1"], + leafstorm: ["7L1"], + matblock: ["7L1"], + mirrorshot: ["7L1"], + moongeistbeam: ["7L1"], + naturepower: ["7L1"], + naturesmadness: ["7L1"], + nobleroar: ["7L1"], + paraboliccharge: ["7L1"], + poweruppunch: ["7L1"], + powerwhip: ["7L1"], + scratch: ["7L1"], + seedbomb: ["7L1"], + silverwind: ["7L1"], + sludgebomb: ["7L1"], + speedswap: ["7L1"], + spiderweb: ["7L1"], + thunderfang: ["7L1"], + thunderwave: ["7L1"], + tropkick: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + voltecta: { + learnset: { + acrobatics: ["7L1"], + airslash: ["7L1"], + bonerush: ["7L1"], + bulletseed: ["7L1"], + falseswipe: ["7L1"], + focusblast: ["7L1"], + fusionvolt: ["7L1"], + geargrind: ["7L1"], + gigaimpact: ["7L1"], + gunkshot: ["7L1"], + holdback: ["7L1"], + karatechop: ["7L1"], + lick: ["7L1"], + megapunch: ["7L1"], + miracleeye: ["7L1"], + mistyterrain: ["7L1"], + nightshade: ["7L1"], + nightslash: ["7L1"], + petalblizzard: ["7L1"], + pound: ["7L1"], + psychicfangs: ["7L1"], + psyshock: ["7L1"], + rapidspin: ["7L1"], + razorleaf: ["7L1"], + reflect: ["7L1"], + reversal: ["7L1"], + shadowball: ["7L1"], + shadowbone: ["7L1"], + smokescreen: ["7L1"], + sparklingaria: ["7L1"], + stockpile: ["7L1"], + superfang: ["7L1"], + sweetscent: ["7L1"], + thousandarrows: ["7L1"], + thunderbolt: ["7L1"], + toxicthread: ["7L1"], + twister: ["7L1"], + voltswitch: ["7L1"], + watergun: ["7L1"], + wildcharge: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + ostria: { + learnset: { + aeroblast: ["7L1"], + airslash: ["7L1"], + ancientpower: ["7L1"], + aurasphere: ["7L1"], + auroraveil: ["7L1"], + crosschop: ["7L1"], + defog: ["7L1"], + dive: ["7L1"], + doublekick: ["7L1"], + dragonrage: ["7L1"], + drillpeck: ["7L1"], + energyball: ["7L1"], + fakeout: ["7L1"], + flowershield: ["7L1"], + highjumpkick: ["7L1"], + holdhands: ["7L1"], + honeclaws: ["7L1"], + icepunch: ["7L1"], + ironhead: ["7L1"], + leafblade: ["7L1"], + magneticflux: ["7L1"], + magnitude: ["7L1"], + metalclaw: ["7L1"], + mindreader: ["7L1"], + moonlight: ["7L1"], + nobleroar: ["7L1"], + outrage: ["7L1"], + phantomforce: ["7L1"], + poisongas: ["7L1"], + purify: ["7L1"], + quash: ["7L1"], + retaliate: ["7L1"], + roar: ["7L1"], + sacredfire: ["7L1"], + scald: ["7L1"], + steameruption: ["7L1"], + steamroller: ["7L1"], + stomp: ["7L1"], + takedown: ["7L1"], + wideguard: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + ninjoth: { + learnset: { + constrict: ["7L1"], + cottonspore: ["7L1"], + counter: ["7L1"], + crunch: ["7L1"], + darkpulse: ["7L1"], + defog: ["7L1"], + diamondstorm: ["7L1"], + dragonrush: ["7L1"], + drainingkiss: ["7L1"], + fairywind: ["7L1"], + fakeout: ["7L1"], + firefang: ["7L1"], + flatter: ["7L1"], + foulplay: ["7L1"], + gearup: ["7L1"], + heavyslam: ["7L1"], + icebeam: ["7L1"], + iciclecrash: ["7L1"], + knockoff: ["7L1"], + magnetrise: ["7L1"], + moonblast: ["7L1"], + poisonsting: ["7L1"], + psystrike: ["7L1"], + pursuit: ["7L1"], + refresh: ["7L1"], + shockwave: ["7L1"], + slam: ["7L1"], + slash: ["7L1"], + snore: ["7L1"], + spitup: ["7L1"], + steamroller: ["7L1"], + steelwing: ["7L1"], + struggle: ["7L1"], + swordsdance: ["7L1"], + tackle: ["7L1"], + tailslap: ["7L1"], + thunderpunch: ["7L1"], + weatherball: ["7L1"], + wildcharge: ["7L1"], + yawn: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + herbigator: { + learnset: { + assist: ["7L1"], + bestow: ["7L1"], + darkpulse: ["7L1"], + disarmingvoice: ["7L1"], + dreameater: ["7L1"], + earthquake: ["7L1"], + energyball: ["7L1"], + entrainment: ["7L1"], + flashcannon: ["7L1"], + frenzyplant: ["7L1"], + growth: ["7L1"], + headsmash: ["7L1"], + hex: ["7L1"], + landswrath: ["7L1"], + leafblade: ["7L1"], + mindblown: ["7L1"], + mindreader: ["7L1"], + miracleeye: ["7L1"], + mirrorcoat: ["7L1"], + moongeistbeam: ["7L1"], + moonlight: ["7L1"], + playnice: ["7L1"], + playrough: ["7L1"], + poisonjab: ["7L1"], + powerswap: ["7L1"], + psystrike: ["7L1"], + rockpolish: ["7L1"], + skyattack: ["7L1"], + smellingsalts: ["7L1"], + strugglebug: ["7L1"], + sunsteelstrike: ["7L1"], + surf: ["7L1"], + switcheroo: ["7L1"], + tackle: ["7L1"], + takedown: ["7L1"], + tearfullook: ["7L1"], + thunderpunch: ["7L1"], + trick: ["7L1"], + waterfall: ["7L1"], + watergun: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + anteros: { + learnset: { + allyswitch: ["7L1"], + amnesia: ["7L1"], + beatup: ["7L1"], + bugbite: ["7L1"], + bugbuzz: ["7L1"], + calmmind: ["7L1"], + confusion: ["7L1"], + cosmicpower: ["7L1"], + dazzlinggleam: ["7L1"], + dragonrage: ["7L1"], + encore: ["7L1"], + entrainment: ["7L1"], + firepunch: ["7L1"], + flashcannon: ["7L1"], + healbell: ["7L1"], + healpulse: ["7L1"], + iciclecrash: ["7L1"], + infestation: ["7L1"], + ironhead: ["7L1"], + kingsshield: ["7L1"], + lavaplume: ["7L1"], + megapunch: ["7L1"], + petaldance: ["7L1"], + rocksmash: ["7L1"], + seedflare: ["7L1"], + sharpen: ["7L1"], + shoreup: ["7L1"], + skullbash: ["7L1"], + smellingsalts: ["7L1"], + softboiled: ["7L1"], + spacialrend: ["7L1"], + spikyshield: ["7L1"], + spite: ["7L1"], + suckerpunch: ["7L1"], + switcheroo: ["7L1"], + teleport: ["7L1"], + thief: ["7L1"], + twister: ["7L1"], + vicegrip: ["7L1"], + xscissor: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + gladiaster: { + learnset: { + absorb: ["7L1"], + allyswitch: ["7L1"], + aurorabeam: ["7L1"], + block: ["7L1"], + bubble: ["7L1"], + bulletpunch: ["7L1"], + burnup: ["7L1"], + charm: ["7L1"], + crabhammer: ["7L1"], + crosschop: ["7L1"], + dazzlinggleam: ["7L1"], + doubleedge: ["7L1"], + dynamicpunch: ["7L1"], + eruption: ["7L1"], + finalgambit: ["7L1"], + flameburst: ["7L1"], + flashcannon: ["7L1"], + gastroacid: ["7L1"], + gunkshot: ["7L1"], + hammerarm: ["7L1"], + headcharge: ["7L1"], + iceball: ["7L1"], + icebeam: ["7L1"], + iceburn: ["7L1"], + icepunch: ["7L1"], + iciclecrash: ["7L1"], + inferno: ["7L1"], + ironhead: ["7L1"], + lunge: ["7L1"], + magnitude: ["7L1"], + pluck: ["7L1"], + pursuit: ["7L1"], + rockblast: ["7L1"], + silverwind: ["7L1"], + slam: ["7L1"], + spiderweb: ["7L1"], + steamroller: ["7L1"], + teeterdance: ["7L1"], + vcreate: ["7L1"], + volttackle: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + hyperoach: { + learnset: { + acrobatics: ["7L1"], + avalanche: ["7L1"], + bubblebeam: ["7L1"], + bugbuzz: ["7L1"], + bulletseed: ["7L1"], + conversion: ["7L1"], + conversion2: ["7L1"], + dragonrage: ["7L1"], + earthpower: ["7L1"], + eerieimpulse: ["7L1"], + explosion: ["7L1"], + fling: ["7L1"], + fly: ["7L1"], + growth: ["7L1"], + hail: ["7L1"], + hypervoice: ["7L1"], + icebeam: ["7L1"], + icefang: ["7L1"], + irondefense: ["7L1"], + judgment: ["7L1"], + megadrain: ["7L1"], + mindblown: ["7L1"], + nightdaze: ["7L1"], + perishsong: ["7L1"], + petaldance: ["7L1"], + powerswap: ["7L1"], + punishment: ["7L1"], + quickguard: ["7L1"], + reflect: ["7L1"], + revenge: ["7L1"], + sludge: ["7L1"], + splash: ["7L1"], + strength: ["7L1"], + sunsteelstrike: ["7L1"], + swordsdance: ["7L1"], + tailslap: ["7L1"], + topsyturvy: ["7L1"], + uturn: ["7L1"], + wish: ["7L1"], + xscissor: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + barracoth: { + learnset: { + crosspoison: ["7L1"], + dracometeor: ["7L1"], + dynamicpunch: ["7L1"], + fierydance: ["7L1"], + firstimpression: ["7L1"], + flamethrower: ["7L1"], + flash: ["7L1"], + flatter: ["7L1"], + foresight: ["7L1"], + fusionbolt: ["7L1"], + gigaimpact: ["7L1"], + guardsplit: ["7L1"], + icebeam: ["7L1"], + iciclecrash: ["7L1"], + infestation: ["7L1"], + lastresort: ["7L1"], + lowsweep: ["7L1"], + payday: ["7L1"], + photongeyser: ["7L1"], + plasmafists: ["7L1"], + poisonjab: ["7L1"], + pollenpuff: ["7L1"], + recycle: ["7L1"], + roar: ["7L1"], + rockblast: ["7L1"], + roost: ["7L1"], + sandattack: ["7L1"], + screech: ["7L1"], + sing: ["7L1"], + skydrop: ["7L1"], + softboiled: ["7L1"], + spikyshield: ["7L1"], + surf: ["7L1"], + thousandarrows: ["7L1"], + twister: ["7L1"], + voltswitch: ["7L1"], + waterfall: ["7L1"], + wideguard: ["7L1"], + worryseed: ["7L1"], + zenheadbutt: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + toados: { + learnset: { + aromatherapy: ["7L1"], + attackorder: ["7L1"], + barrier: ["7L1"], + blizzard: ["7L1"], + bubble: ["7L1"], + bugbuzz: ["7L1"], + bulkup: ["7L1"], + clearsmog: ["7L1"], + dazzlinggleam: ["7L1"], + destinybond: ["7L1"], + dizzypunch: ["7L1"], + dragonclaw: ["7L1"], + dragontail: ["7L1"], + encore: ["7L1"], + floralhealing: ["7L1"], + grasspledge: ["7L1"], + grassyterrain: ["7L1"], + headbutt: ["7L1"], + iciclecrash: ["7L1"], + karatechop: ["7L1"], + lastresort: ["7L1"], + leafblade: ["7L1"], + lockon: ["7L1"], + moongeistbeam: ["7L1"], + mudslap: ["7L1"], + nightmare: ["7L1"], + octazooka: ["7L1"], + poisonjab: ["7L1"], + psystrike: ["7L1"], + quickguard: ["7L1"], + raindance: ["7L1"], + rockblast: ["7L1"], + seismictoss: ["7L1"], + skyuppercut: ["7L1"], + sludgebomb: ["7L1"], + smellingsalts: ["7L1"], + snore: ["7L1"], + toxicspikes: ["7L1"], + venoshock: ["7L1"], + watergun: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + voltarak: { + learnset: { + acrobatics: ["7L1"], + allyswitch: ["7L1"], + assurance: ["7L1"], + bide: ["7L1"], + blizzard: ["7L1"], + crosschop: ["7L1"], + crosspoison: ["7L1"], + detect: ["7L1"], + drillrun: ["7L1"], + dualchop: ["7L1"], + energyball: ["7L1"], + explosion: ["7L1"], + fairylock: ["7L1"], + fierydance: ["7L1"], + flameburst: ["7L1"], + flowershield: ["7L1"], + fusionbolt: ["7L1"], + gigadrain: ["7L1"], + headsmash: ["7L1"], + hypervoice: ["7L1"], + icebeam: ["7L1"], + iciclecrash: ["7L1"], + magneticflux: ["7L1"], + milkdrink: ["7L1"], + painsplit: ["7L1"], + payday: ["7L1"], + plasmafists: ["7L1"], + poisonfang: ["7L1"], + pound: ["7L1"], + powdersnow: ["7L1"], + powerswap: ["7L1"], + sandtomb: ["7L1"], + signalbeam: ["7L1"], + skillswap: ["7L1"], + smokescreen: ["7L1"], + steelwing: ["7L1"], + switcheroo: ["7L1"], + synthesis: ["7L1"], + thunderbolt: ["7L1"], + toxicspikes: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + mosqung: { + learnset: { + aeroblast: ["7L1"], + agility: ["7L1"], + aquatail: ["7L1"], + assurance: ["7L1"], + banefulbunker: ["7L1"], + belch: ["7L1"], + bugbuzz: ["7L1"], + bulletpunch: ["7L1"], + burnup: ["7L1"], + cottonspore: ["7L1"], + crabhammer: ["7L1"], + defensecurl: ["7L1"], + doubleedge: ["7L1"], + dragonclaw: ["7L1"], + drainingkiss: ["7L1"], + drillpeck: ["7L1"], + feintattack: ["7L1"], + glaciate: ["7L1"], + gunkshot: ["7L1"], + holdback: ["7L1"], + hornleech: ["7L1"], + iceball: ["7L1"], + incinerate: ["7L1"], + instruct: ["7L1"], + mindreader: ["7L1"], + naturesmadness: ["7L1"], + nightshade: ["7L1"], + powder: ["7L1"], + rapidspin: ["7L1"], + razorshell: ["7L1"], + reflect: ["7L1"], + snatch: ["7L1"], + strengthsap: ["7L1"], + synchronoise: ["7L1"], + takedown: ["7L1"], + thunderbolt: ["7L1"], + waterpledge: ["7L1"], + withdraw: ["7L1"], + xscissor: ["7L1"], + zenheadbutt: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + flamepion: { + learnset: { + blazekick: ["7L1"], + closecombat: ["7L1"], + counter: ["7L1"], + covet: ["7L1"], + disable: ["7L1"], + dragonrage: ["7L1"], + eerieimpulse: ["7L1"], + energyball: ["7L1"], + finalgambit: ["7L1"], + flameburst: ["7L1"], + flamethrower: ["7L1"], + flashcannon: ["7L1"], + focusenergy: ["7L1"], + furyattack: ["7L1"], + judgment: ["7L1"], + leer: ["7L1"], + meteormash: ["7L1"], + mysticalfire: ["7L1"], + poisonjab: ["7L1"], + psychicfangs: ["7L1"], + psywave: ["7L1"], + raindance: ["7L1"], + razorleaf: ["7L1"], + roost: ["7L1"], + selfdestruct: ["7L1"], + shadowball: ["7L1"], + shadowbone: ["7L1"], + shockwave: ["7L1"], + smog: ["7L1"], + spiritshackle: ["7L1"], + spite: ["7L1"], + stealthrock: ["7L1"], + tackle: ["7L1"], + telekinesis: ["7L1"], + teleport: ["7L1"], + trickroom: ["7L1"], + venoshock: ["7L1"], + wideguard: ["7L1"], + wish: ["7L1"], + zenheadbutt: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + hyenix: { + learnset: { + agility: ["7L1"], + autotomize: ["7L1"], + belch: ["7L1"], + blueflare: ["7L1"], + bugbite: ["7L1"], + cottonspore: ["7L1"], + crunch: ["7L1"], + crushgrip: ["7L1"], + darkpulse: ["7L1"], + dive: ["7L1"], + drillrun: ["7L1"], + feint: ["7L1"], + firefang: ["7L1"], + flatter: ["7L1"], + fleurcannon: ["7L1"], + foulplay: ["7L1"], + furyswipes: ["7L1"], + machpunch: ["7L1"], + magnetbomb: ["7L1"], + matblock: ["7L1"], + megahorn: ["7L1"], + megapunch: ["7L1"], + metalsound: ["7L1"], + mudsport: ["7L1"], + nobleroar: ["7L1"], + outrage: ["7L1"], + pinmissile: ["7L1"], + psychocut: ["7L1"], + rage: ["7L1"], + rockblast: ["7L1"], + roost: ["7L1"], + slash: ["7L1"], + sludgewave: ["7L1"], + strugglebug: ["7L1"], + sunsteelstrike: ["7L1"], + taunt: ["7L1"], + thundershock: ["7L1"], + waterfall: ["7L1"], + watershuriken: ["7L1"], + weatherball: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + rhinolite: { + learnset: { + acrobatics: ["7L1"], + bite: ["7L1"], + bodyslam: ["7L1"], + bonemerang: ["7L1"], + bulletseed: ["7L1"], + charge: ["7L1"], + confusion: ["7L1"], + darkpulse: ["7L1"], + dragonrage: ["7L1"], + drillpeck: ["7L1"], + dualchop: ["7L1"], + earthpower: ["7L1"], + earthquake: ["7L1"], + fusionflare: ["7L1"], + guardswap: ["7L1"], + healbell: ["7L1"], + landswrath: ["7L1"], + lastresort: ["7L1"], + lightofruin: ["7L1"], + mirrormove: ["7L1"], + mirrorshot: ["7L1"], + mist: ["7L1"], + outrage: ["7L1"], + painsplit: ["7L1"], + poisontail: ["7L1"], + powergem: ["7L1"], + present: ["7L1"], + psychoboost: ["7L1"], + punishment: ["7L1"], + rocksmash: ["7L1"], + shadowsneak: ["7L1"], + sludge: ["7L1"], + sonicboom: ["7L1"], + stoneedge: ["7L1"], + strengthsap: ["7L1"], + surf: ["7L1"], + sweetkiss: ["7L1"], + thundershock: ["7L1"], + thunderwave: ["7L1"], + wish: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + bellena: { + learnset: { + boltbeak: ["7L1"], + bulkup: ["7L1"], + charge: ["7L1"], + destinybond: ["7L1"], + diamondstorm: ["7L1"], + doublekick: ["7L1"], + dragonclaw: ["7L1"], + dragonpulse: ["7L1"], + dragonrush: ["7L1"], + electricterrain: ["7L1"], + energyball: ["7L1"], + flamewheel: ["7L1"], + foulplay: ["7L1"], + furyswipes: ["7L1"], + honeclaws: ["7L1"], + iceball: ["7L1"], + irontail: ["7L1"], + lowsweep: ["7L1"], + magmastorm: ["7L1"], + metalburst: ["7L1"], + metronome: ["7L1"], + moonblast: ["7L1"], + muddywater: ["7L1"], + nightshade: ["7L1"], + paraboliccharge: ["7L1"], + playrough: ["7L1"], + powder: ["7L1"], + psychicfangs: ["7L1"], + roost: ["7L1"], + searingshot: ["7L1"], + shadowbone: ["7L1"], + shelltrap: ["7L1"], + sludge: ["7L1"], + spiritshackle: ["7L1"], + spitup: ["7L1"], + stealthrock: ["7L1"], + submission: ["7L1"], + swift: ["7L1"], + throatchop: ["7L1"], + thunderfang: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + falcola: { + learnset: { + amnesia: ["7L1"], + clangoroussoul: ["7L1"], + crosschop: ["7L1"], + darkvoid: ["7L1"], + discharge: ["7L1"], + doublehit: ["7L1"], + dracometeor: ["7L1"], + drillpeck: ["7L1"], + encore: ["7L1"], + energyball: ["7L1"], + entrainment: ["7L1"], + firelash: ["7L1"], + grasspledge: ["7L1"], + heartstamp: ["7L1"], + heatwave: ["7L1"], + helpinghand: ["7L1"], + hypnosis: ["7L1"], + icehammer: ["7L1"], + iciclespear: ["7L1"], + ironhead: ["7L1"], + leer: ["7L1"], + liquidation: ["7L1"], + lockon: ["7L1"], + nobleroar: ["7L1"], + octazooka: ["7L1"], + outrage: ["7L1"], + pound: ["7L1"], + psychoshift: ["7L1"], + razorwind: ["7L1"], + relicsong: ["7L1"], + retaliate: ["7L1"], + screech: ["7L1"], + snipeshot: ["7L1"], + spikecannon: ["7L1"], + steameruption: ["7L1"], + stormthrow: ["7L1"], + surf: ["7L1"], + thunderfang: ["7L1"], + vitalthrow: ["7L1"], + xscissor: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + beanium: { + learnset: { + aromaticmist: ["7L1"], + avalanche: ["7L1"], + charm: ["7L1"], + clangingscales: ["7L1"], + dracometeor: ["7L1"], + energyball: ["7L1"], + finalgambit: ["7L1"], + fireblast: ["7L1"], + grasspledge: ["7L1"], + happyhour: ["7L1"], + healingwish: ["7L1"], + hornattack: ["7L1"], + hypervoice: ["7L1"], + karatechop: ["7L1"], + landswrath: ["7L1"], + lightofruin: ["7L1"], + magnitude: ["7L1"], + meanlook: ["7L1"], + megadrain: ["7L1"], + overdrive: ["7L1"], + paraboliccharge: ["7L1"], + poisonjab: ["7L1"], + pound: ["7L1"], + powder: ["7L1"], + powergem: ["7L1"], + psystrike: ["7L1"], + razorshell: ["7L1"], + reversal: ["7L1"], + rocksmash: ["7L1"], + rocktomb: ["7L1"], + sacredsword: ["7L1"], + scratch: ["7L1"], + searingshot: ["7L1"], + shadowbone: ["7L1"], + stoneedge: ["7L1"], + strength: ["7L1"], + superpower: ["7L1"], + tailslap: ["7L1"], + telekinesis: ["7L1"], + teleport: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + lemotic: { + learnset: { + behemothbash: ["7L1"], + behemothblade: ["7L1"], + chargebeam: ["7L1"], + defendorder: ["7L1"], + diamondstorm: ["7L1"], + discharge: ["7L1"], + earthpower: ["7L1"], + earthquake: ["7L1"], + energyball: ["7L1"], + flash: ["7L1"], + fleurcannon: ["7L1"], + furyattack: ["7L1"], + futuresight: ["7L1"], + glaciate: ["7L1"], + heartswap: ["7L1"], + heatwave: ["7L1"], + hex: ["7L1"], + iceshard: ["7L1"], + laserfocus: ["7L1"], + leafblade: ["7L1"], + lusterpurge: ["7L1"], + mefirst: ["7L1"], + megahorn: ["7L1"], + metalburst: ["7L1"], + nightdaze: ["7L1"], + payback: ["7L1"], + payday: ["7L1"], + poisonjab: ["7L1"], + razorleaf: ["7L1"], + refresh: ["7L1"], + rockclimb: ["7L1"], + rockpolish: ["7L1"], + sandattack: ["7L1"], + shadowbone: ["7L1"], + steameruption: ["7L1"], + sweetkiss: ["7L1"], + swift: ["7L1"], + telekinesis: ["7L1"], + wish: ["7L1"], + withdraw: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + biceon: { + learnset: { + aquajet: ["7L1"], + aromatherapy: ["7L1"], + assist: ["7L1"], + attackorder: ["7L1"], + bind: ["7L1"], + boltbeak: ["7L1"], + burnup: ["7L1"], + calmmind: ["7L1"], + dazzlinggleam: ["7L1"], + dreameater: ["7L1"], + earthpower: ["7L1"], + earthquake: ["7L1"], + echoedvoice: ["7L1"], + entrainment: ["7L1"], + extrasensory: ["7L1"], + feintattack: ["7L1"], + frostbreath: ["7L1"], + furyswipes: ["7L1"], + fusionflare: ["7L1"], + glaciate: ["7L1"], + gravity: ["7L1"], + headcharge: ["7L1"], + meteormash: ["7L1"], + mindreader: ["7L1"], + moonblast: ["7L1"], + nobleroar: ["7L1"], + noretreat: ["7L1"], + octazooka: ["7L1"], + perishsong: ["7L1"], + playrough: ["7L1"], + retaliate: ["7L1"], + roar: ["7L1"], + rollout: ["7L1"], + shadowpunch: ["7L1"], + stealthrock: ["7L1"], + supersonic: ["7L1"], + topsyturvy: ["7L1"], + triplekick: ["7L1"], + twister: ["7L1"], + uturn: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + skeleray: { + learnset: { + allyswitch: ["7L1"], + anchorshot: ["7L1"], + ancientpower: ["7L1"], + dragonclaw: ["7L1"], + dragonhammer: ["7L1"], + dragonpulse: ["7L1"], + dragontail: ["7L1"], + earthquake: ["7L1"], + fairylock: ["7L1"], + focusenergy: ["7L1"], + iceshard: ["7L1"], + incinerate: ["7L1"], + jawlock: ["7L1"], + lovelykiss: ["7L1"], + lowkick: ["7L1"], + metalclaw: ["7L1"], + milkdrink: ["7L1"], + nastyplot: ["7L1"], + nobleroar: ["7L1"], + outrage: ["7L1"], + poisonpowder: ["7L1"], + poisonsting: ["7L1"], + powersplit: ["7L1"], + prismaticlaser: ["7L1"], + psywave: ["7L1"], + sandattack: ["7L1"], + scald: ["7L1"], + shadowball: ["7L1"], + shadowbone: ["7L1"], + sludgewave: ["7L1"], + smog: ["7L1"], + speedswap: ["7L1"], + teatime: ["7L1"], + thousandarrows: ["7L1"], + triplekick: ["7L1"], + waterpledge: ["7L1"], + watershuriken: ["7L1"], + wildcharge: ["7L1"], + wrap: ["7L1"], + zingzap: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + specyte: { + learnset: { + acidarmor: ["7L1"], + amnesia: ["7L1"], + aromatherapy: ["7L1"], + bonerush: ["7L1"], + bounce: ["7L1"], + bravebird: ["7L1"], + bugbuzz: ["7L1"], + celebrate: ["7L1"], + conversion2: ["7L1"], + craftyshield: ["7L1"], + discharge: ["7L1"], + dragondance: ["7L1"], + electricterrain: ["7L1"], + fling: ["7L1"], + furycutter: ["7L1"], + harden: ["7L1"], + healblock: ["7L1"], + infestation: ["7L1"], + kingsshield: ["7L1"], + moonlight: ["7L1"], + naturesmadness: ["7L1"], + photongeyser: ["7L1"], + pluck: ["7L1"], + powdersnow: ["7L1"], + powergem: ["7L1"], + powertrip: ["7L1"], + recover: ["7L1"], + reflecttype: ["7L1"], + sandstorm: ["7L1"], + shadowball: ["7L1"], + shadowbone: ["7L1"], + shadowclaw: ["7L1"], + slam: ["7L1"], + snipeshot: ["7L1"], + strengthsap: ["7L1"], + strugglebug: ["7L1"], + synchronoise: ["7L1"], + technoblast: ["7L1"], + voltswitch: ["7L1"], + volttackle: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + ramron: { + learnset: { + agility: ["7L1"], + aquaring: ["7L1"], + bugbuzz: ["7L1"], + craftyshield: ["7L1"], + dazzlinggleam: ["7L1"], + dig: ["7L1"], + dragonpulse: ["7L1"], + electrify: ["7L1"], + embargo: ["7L1"], + endeavor: ["7L1"], + flameburst: ["7L1"], + foulplay: ["7L1"], + futuresight: ["7L1"], + guardsplit: ["7L1"], + haze: ["7L1"], + heartswap: ["7L1"], + hyperspacefury: ["7L1"], + hypervoice: ["7L1"], + icebeam: ["7L1"], + landswrath: ["7L1"], + leafstorm: ["7L1"], + magnitude: ["7L1"], + meditate: ["7L1"], + nobleroar: ["7L1"], + partingshot: ["7L1"], + pound: ["7L1"], + powergem: ["7L1"], + psychocut: ["7L1"], + purify: ["7L1"], + raindance: ["7L1"], + rockpolish: ["7L1"], + rockslide: ["7L1"], + shadowpunch: ["7L1"], + sonicboom: ["7L1"], + stealthrock: ["7L1"], + stickyweb: ["7L1"], + strength: ["7L1"], + swagger: ["7L1"], + torment: ["7L1"], + willowisp: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + balar: { + learnset: { + aeroblast: ["7L1"], + agility: ["7L1"], + autotomize: ["7L1"], + bounce: ["7L1"], + brutalswing: ["7L1"], + bugbuzz: ["7L1"], + conversion2: ["7L1"], + decorate: ["7L1"], + diamondstorm: ["7L1"], + disarmingvoice: ["7L1"], + drillpeck: ["7L1"], + embargo: ["7L1"], + focuspunch: ["7L1"], + gigaimpact: ["7L1"], + grasswhistle: ["7L1"], + gyroball: ["7L1"], + karatechop: ["7L1"], + kingsshield: ["7L1"], + lifedew: ["7L1"], + playnice: ["7L1"], + powergem: ["7L1"], + powerswap: ["7L1"], + precipiceblades: ["7L1"], + psychoboost: ["7L1"], + psychocut: ["7L1"], + psychup: ["7L1"], + roar: ["7L1"], + rocktomb: ["7L1"], + roleplay: ["7L1"], + screech: ["7L1"], + shadowball: ["7L1"], + soak: ["7L1"], + stealthrock: ["7L1"], + stoneedge: ["7L1"], + sunnyday: ["7L1"], + toxicspikes: ["7L1"], + trumpcard: ["7L1"], + uturn: ["7L1"], + vacuumwave: ["7L1"], + woodhammer: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + panthee: { + learnset: { + aerialace: ["7L1"], + aircutter: ["7L1"], + aurasphere: ["7L1"], + boltbeak: ["7L1"], + boomburst: ["7L1"], + bugbuzz: ["7L1"], + crunch: ["7L1"], + crushgrip: ["7L1"], + darkestlariat: ["7L1"], + electrify: ["7L1"], + flameburst: ["7L1"], + flashcannon: ["7L1"], + followme: ["7L1"], + healpulse: ["7L1"], + helpinghand: ["7L1"], + ironhead: ["7L1"], + leaftornado: ["7L1"], + lightofruin: ["7L1"], + lockon: ["7L1"], + moonlight: ["7L1"], + mudslap: ["7L1"], + payday: ["7L1"], + phantomforce: ["7L1"], + poisontail: ["7L1"], + psychic: ["7L1"], + psychicterrain: ["7L1"], + psychoshift: ["7L1"], + reflect: ["7L1"], + retaliate: ["7L1"], + shadowball: ["7L1"], + skyattack: ["7L1"], + skyuppercut: ["7L1"], + spikecannon: ["7L1"], + spiritbreak: ["7L1"], + sunnyday: ["7L1"], + superpower: ["7L1"], + swordsdance: ["7L1"], + thunderwave: ["7L1"], + uturn: ["7L1"], + waterfall: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + blastora: { + learnset: { + attackorder: ["7L1"], + autotomize: ["7L1"], + behemothblade: ["7L1"], + blazekick: ["7L1"], + branchpoke: ["7L1"], + burnup: ["7L1"], + coil: ["7L1"], + confuseray: ["7L1"], + defendorder: ["7L1"], + defog: ["7L1"], + dragonclaw: ["7L1"], + falsesurrender: ["7L1"], + flamethrower: ["7L1"], + followme: ["7L1"], + glare: ["7L1"], + healingwish: ["7L1"], + holdhands: ["7L1"], + irondefense: ["7L1"], + ironhead: ["7L1"], + lockon: ["7L1"], + magnitude: ["7L1"], + metalclaw: ["7L1"], + metalsound: ["7L1"], + mindreader: ["7L1"], + mist: ["7L1"], + mudsport: ["7L1"], + nightdaze: ["7L1"], + partingshot: ["7L1"], + payback: ["7L1"], + peck: ["7L1"], + phantomforce: ["7L1"], + pluck: ["7L1"], + psystrike: ["7L1"], + shadowpunch: ["7L1"], + slash: ["7L1"], + spectralthief: ["7L1"], + stickyweb: ["7L1"], + swordsdance: ["7L1"], + synthesis: ["7L1"], + wringout: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + dropacle: { + learnset: { + aurorabeam: ["7L1"], + behemothblade: ["7L1"], + bonerush: ["7L1"], + chargebeam: ["7L1"], + chipaway: ["7L1"], + disarmingvoice: ["7L1"], + dragondance: ["7L1"], + fakeout: ["7L1"], + falsesurrender: ["7L1"], + fierydance: ["7L1"], + gigadrain: ["7L1"], + happyhour: ["7L1"], + holdhands: ["7L1"], + honeclaws: ["7L1"], + irondefense: ["7L1"], + leafblade: ["7L1"], + lunardance: ["7L1"], + magnetbomb: ["7L1"], + meanlook: ["7L1"], + metalburst: ["7L1"], + mist: ["7L1"], + powergem: ["7L1"], + precipiceblades: ["7L1"], + quickguard: ["7L1"], + rocksmash: ["7L1"], + screech: ["7L1"], + shadowball: ["7L1"], + shadowbone: ["7L1"], + sludgebomb: ["7L1"], + smog: ["7L1"], + smokescreen: ["7L1"], + softboiled: ["7L1"], + spark: ["7L1"], + surf: ["7L1"], + synthesis: ["7L1"], + tailslap: ["7L1"], + tarshot: ["7L1"], + toxicspikes: ["7L1"], + waterfall: ["7L1"], + waterpulse: ["7L1"], + attract: ["7L1"], + captivate: ["7L1"], + confide: ["7L1"], + endure: ["7L1"], + facade: ["7L1"], + frustration: ["7L1"], + hiddenpower: ["7L1"], + naturalgift: ["7L1"], + protect: ["7L1"], + rest: ["7L1"], + return: ["7L1"], + round: ["7L1"], + secretpower: ["7L1"], + sleeptalk: ["7L1"], + substitute: ["7L1"], + toxic: ["7L1"], + }, + }, + dolphena: { + learnset: { + armthrust: ["7L1"], + banefulbunker: ["7L1"], + belch: ["7L1"], + blazekick: ["7L1"], + bubblebeam: ["7L1"], + dragonclaw: ["7L1"], + dragonpulse: ["7L1"], + dragontail: ["7L1"], + drillpeck: ["7L1"], + extrasensory: ["7L1"], + fellstinger: ["7L1"], + firefang: ["7L1"], + frostbreath: ["7L1"], + highhorsepower: ["7L1"], + hydrocannon: ["7L1"], + irondefense: ["7L1"], + landswrath: ["7L1"], + leafstorm: ["7L1"], + magneticflux: ["7L1"], + mindreader: ["7L1"], + oblivionwing: ["7L1"], + ominouswind: ["7L1"], + outrage: ["7L1"], + powder: ["7L1"], + psychup: ["7L1"], + psywave: ["7L1"], + reflecttype: ["7L1"], + revelationdance: ["7L1"], + rockthrow: ["7L1"], + silverwind: ["7L1"], + snatch: ["7L1"], + softboiled: ["7L1"], + suckerpunch: ["7L1"], + superpower: ["7L1"], + surf: ["7L1"], + thief: ["7L1"], + thousandwaves: ["7L1"], + thunderbolt: ["7L1"], + trickortreat: ["7L1"], + waterfall: ["7L1"], + }, + }, + catelax: { + learnset: { + assist: ["7L1"], + brickbreak: ["7L1"], + bugbuzz: ["7L1"], + constrict: ["7L1"], + dracometeor: ["7L1"], + dreameater: ["7L1"], + electrify: ["7L1"], + fakeout: ["7L1"], + finalgambit: ["7L1"], + flamethrower: ["7L1"], + grassyterrain: ["7L1"], + growl: ["7L1"], + harden: ["7L1"], + healblock: ["7L1"], + healingwish: ["7L1"], + heavyslam: ["7L1"], + imprison: ["7L1"], + inferno: ["7L1"], + machpunch: ["7L1"], + mistyterrain: ["7L1"], + octolock: ["7L1"], + phantomforce: ["7L1"], + poisonjab: ["7L1"], + powertrip: ["7L1"], + sandtomb: ["7L1"], + scald: ["7L1"], + shadowforce: ["7L1"], + simplebeam: ["7L1"], + stomp: ["7L1"], + strangesteam: ["7L1"], + submission: ["7L1"], + superfang: ["7L1"], + tackle: ["7L1"], + tailslap: ["7L1"], + technoblast: ["7L1"], + thunderbolt: ["7L1"], + tropkick: ["7L1"], + uturn: ["7L1"], + willowisp: ["7L1"], + xscissor: ["7L1"], + }, + }, + fluffora: { + learnset: { + aircutter: ["7L1"], + assist: ["7L1"], + block: ["7L1"], + bulletseed: ["7L1"], + chipaway: ["7L1"], + discharge: ["7L1"], + doublehit: ["7L1"], + dragonrage: ["7L1"], + dragonrush: ["7L1"], + energyball: ["7L1"], + explosion: ["7L1"], + finalgambit: ["7L1"], + flail: ["7L1"], + flowershield: ["7L1"], + freezeshock: ["7L1"], + guardsplit: ["7L1"], + healbell: ["7L1"], + highhorsepower: ["7L1"], + hydropump: ["7L1"], + hypnosis: ["7L1"], + icepunch: ["7L1"], + iciclecrash: ["7L1"], + judgment: ["7L1"], + knockoff: ["7L1"], + leafblade: ["7L1"], + lowkick: ["7L1"], + metalburst: ["7L1"], + metalclaw: ["7L1"], + mysticalfire: ["7L1"], + petaldance: ["7L1"], + reflect: ["7L1"], + refresh: ["7L1"], + revelationdance: ["7L1"], + screech: ["7L1"], + seismictoss: ["7L1"], + shadowpunch: ["7L1"], + slam: ["7L1"], + spikes: ["7L1"], + thousandwaves: ["7L1"], + thunderbolt: ["7L1"], + }, + }, + tigire: { + learnset: { + acid: ["7L1"], + aurasphere: ["7L1"], + blazekick: ["7L1"], + bonemerang: ["7L1"], + bulletpunch: ["7L1"], + crabhammer: ["7L1"], + craftyshield: ["7L1"], + crosschop: ["7L1"], + dragonclaw: ["7L1"], + extrasensory: ["7L1"], + falseswipe: ["7L1"], + feint: ["7L1"], + feintattack: ["7L1"], + fellstinger: ["7L1"], + firelash: ["7L1"], + flamethrower: ["7L1"], + flatter: ["7L1"], + fusionflare: ["7L1"], + glare: ["7L1"], + guardsplit: ["7L1"], + gyroball: ["7L1"], + hammerarm: ["7L1"], + heartstamp: ["7L1"], + heatcrash: ["7L1"], + hydrocannon: ["7L1"], + hyperspacefury: ["7L1"], + laserfocus: ["7L1"], + lunge: ["7L1"], + payback: ["7L1"], + poisontail: ["7L1"], + rockpolish: ["7L1"], + roost: ["7L1"], + sandstorm: ["7L1"], + seismictoss: ["7L1"], + shadowball: ["7L1"], + solarblade: ["7L1"], + spitup: ["7L1"], + vacuumwave: ["7L1"], + wakeupslap: ["7L1"], + wish: ["7L1"], + }, + }, +}; diff --git a/data/mods/roulettemons/pokedex.js b/data/mods/roulettemons/pokedex.ts similarity index 98% rename from data/mods/roulettemons/pokedex.js rename to data/mods/roulettemons/pokedex.ts index 32df601ea2..01ef124ffb 100644 --- a/data/mods/roulettemons/pokedex.js +++ b/data/mods/roulettemons/pokedex.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesData}} */ -let BattlePokedex = { +export const BattlePokedex: {[k: string]: ModdedSpeciesData} = { koatric: { num: 1.5, name: "Koatric", @@ -423,5 +420,3 @@ let BattlePokedex = { weightkg: 150, }, }; - -exports.BattlePokedex = BattlePokedex; diff --git a/data/mods/ssb/abilities.js b/data/mods/ssb/abilities.ts similarity index 93% rename from data/mods/ssb/abilities.js rename to data/mods/ssb/abilities.ts index 40690fd6a8..c4a7b07976 100644 --- a/data/mods/ssb/abilities.js +++ b/data/mods/ssb/abilities.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedAbilityData}} */ -let BattleAbilities = { +export const BattleAbilities: {[k: string]: ModdedAbilityData} = { /* // Example "abilityid": { @@ -55,7 +52,7 @@ let BattleAbilities = { if (target === source || move.hasBounced || !move.flags['reflectable']) { return; } - let newMove = this.dex.getActiveMove(move.id); + const newMove = this.dex.getActiveMove(move.id); newMove.hasBounced = true; newMove.pranksterBoosted = false; this.useMove(newMove, target, source, this.dex.getAbility('magicbounce')); @@ -65,7 +62,7 @@ let BattleAbilities = { if (target.side === source.side || move.hasBounced || !move.flags['reflectable']) { return; } - let newMove = this.dex.getActiveMove(move.id); + const newMove = this.dex.getActiveMove(move.id); newMove.hasBounced = true; newMove.pranksterBoosted = false; this.useMove(newMove, this.effectData.target, source, this.dex.getAbility('magicbounce')); @@ -73,7 +70,7 @@ let BattleAbilities = { }, onDamagePriority: -100, onDamage(damage, target, source, effect) { - if (target.hp === target.maxhp && damage >= target.hp && effect && effect.effectType === 'Move') { + if (target.hp === target.maxhp && damage >= target.hp && effect?.effectType === 'Move') { this.add('-ability', target, 'Sturdy'); return target.hp - 1; } @@ -106,16 +103,14 @@ let BattleAbilities = { id: "toxicswap", name: "Toxic Swap", onStart(pokemon) { - let target = pokemon.side.foe.active[0]; + const target = pokemon.side.foe.active[0]; if (!target) return; - let targetBoosts = {}; - let pokemonBoosts = {}; + const targetBoosts: SparseBoostsTable = {}; + const pokemonBoosts: SparseBoostsTable = {}; - // @ts-ignore - for (let i in target.boosts) { - // @ts-ignore + let i: BoostName; + for (i in target.boosts) { targetBoosts[i] = target.boosts[i]; - // @ts-ignore pokemonBoosts[i] = pokemon.boosts[i]; } target.setBoost(pokemonBoosts); @@ -195,7 +190,7 @@ let BattleAbilities = { if (target === source || move.hasBounced || !move.flags['reflectable']) { return; } - let newMove = this.dex.getActiveMove(move.id); + const newMove = this.dex.getActiveMove(move.id); newMove.hasBounced = true; newMove.pranksterBoosted = false; this.useMove(newMove, target, source); @@ -205,14 +200,14 @@ let BattleAbilities = { if (target.side === source.side || move.hasBounced || !move.flags['reflectable']) { return; } - let newMove = this.dex.getActiveMove(move.id); + const newMove = this.dex.getActiveMove(move.id); newMove.hasBounced = true; newMove.pranksterBoosted = false; this.useMove(newMove, this.effectData.target, source); return null; }, onAnyModifyBoost(boosts, target) { - let source = this.effectData.target; + const source = this.effectData.target; if (source === target) return; if (source === this.activePokemon && target === this.activeTarget) { boosts['def'] = 0; @@ -238,7 +233,7 @@ let BattleAbilities = { name: "Logia", isNonstandard: "Custom", onTryHit(target, source, move) { - let plateType = this.dex.getItem(target.item).onPlate; + const plateType = this.dex.getItem(target.item).onPlate; if (target !== source && (move.type === 'Normal' || plateType === move.type)) { this.add('-immune', target, '[from] ability: Logia'); return null; @@ -258,7 +253,7 @@ let BattleAbilities = { source.m.hasTransformed = false; return; } - let formes = ['oricorio', 'oricoriosensu', 'oricoriopompom', 'oricoriopau']; + const formes = ['oricorio', 'oricoriosensu', 'oricoriopompom', 'oricoriopau']; if (formes.includes(toID(source.species.name))) { formes.splice(formes.indexOf(toID(source.species.name)), 1); this.add('-activate', source, 'ability: Arabesque'); @@ -275,7 +270,10 @@ let BattleAbilities = { name: "Gracidea Mastery", isNonstandard: "Custom", onTryHit(target, source, move) { - if ((target === source || move.category === 'Status') && target.species.id !== 'shayminsky' && target.transformed) return; + if ( + (target === source || move.category === 'Status') && + target.species.id !== 'shayminsky' && target.transformed + ) return; target.formeChange('Shaymin', this.effect); }, onDamagingHit(damage, target, source, move) { @@ -321,7 +319,10 @@ let BattleAbilities = { id: "shadowartifice", name: "Shadow Artifice", onFoeTrapPokemon(pokemon) { - if (!pokemon.hasAbility('shadowartifice') && !pokemon.hasAbility('shadowtag') && this.isAdjacent(pokemon, this.effectData.target)) { + if ( + !pokemon.hasAbility('shadowartifice') && !pokemon.hasAbility('shadowtag') && + this.isAdjacent(pokemon, this.effectData.target) + ) { pokemon.tryTrap(true); } }, @@ -462,7 +463,8 @@ let BattleAbilities = { this.debug('illusion cleared'); let disguisedAs = toID(pokemon.illusion.name); pokemon.illusion = null; - let details = pokemon.species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) + (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : ''); + const details = pokemon.species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) + + (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : ''); this.add('replace', pokemon, details); this.add('-end', pokemon, 'Illusion'); // Handle hippopotas @@ -471,8 +473,8 @@ let BattleAbilities = { pokemon.removeVolatile(disguisedAs); } if (!pokemon.volatiles[toID(pokemon.name)]) { - let status = this.dex.getEffect(toID(pokemon.name)); - if (status && status.exists) { + const status = this.dex.getEffect(toID(pokemon.name)); + if (status?.exists) { pokemon.addVolatile(toID(pokemon.name), pokemon); } } @@ -593,7 +595,8 @@ let BattleAbilities = { this.field.setWeather('primordialsea'); }, onAnySetWeather(target, source, weather) { - if (this.field.getWeather().id === 'primordialsea' && !['desolateland', 'primordialsea', 'deltastream'].includes(weather.id)) return false; + const strongWeathers = ['desolateland', 'primordialsea', 'deltastream']; + if (this.field.getWeather().id === 'primordialsea' && !strongWeathers.includes(weather.id)) return false; }, onEnd(pokemon) { if (this.field.weatherData.source !== pokemon) return; @@ -710,7 +713,7 @@ let BattleAbilities = { } if (pokemon.isActive && forme) { pokemon.formeChange(forme, this.effect, false, '[msg]'); - const sets = { + const sets: {[k: string]: string[]} = { 'Castform-Sunny': ['Fire Blast', 'Solar Beam', 'Synthesis', 'Weather Forecast'], 'Castform-Rainy': ['Hydro Pump', 'Hurricane', 'Thunder', 'Weather Forecast'], 'Castform-Snowy': ['Blizzard', 'Thunder', 'Earth Power', 'Weather Forecast'], @@ -724,16 +727,18 @@ let BattleAbilities = { while (carryOver.length < 4) { carryOver.push(1); } - // @ts-ignore - let set = sets[forme]; + const set = sets[forme]; pokemon.moveSlots = []; - for (let i = 0; i < set.length; i++) { - let newMove = set[i]; - let moveSpecies = this.dex.getMove(newMove); + for (const [i, newMove] of set.entries()) { + const moveSpecies = this.dex.getMove(newMove); pokemon.moveSlots.push({ move: moveSpecies.name, id: moveSpecies.id, - pp: ((moveSpecies.noPPBoosts || moveSpecies.isZ) ? Math.floor(moveSpecies.pp * carryOver[i]) : Math.floor((moveSpecies.pp * 8 / 5) * carryOver[i])), + pp: ( + (moveSpecies.noPPBoosts || moveSpecies.isZ) ? + Math.floor(moveSpecies.pp * carryOver[i]) : + Math.floor((moveSpecies.pp * 8 / 5) * carryOver[i]) + ), maxpp: ((moveSpecies.noPPBoosts || moveSpecies.isZ) ? moveSpecies.pp : moveSpecies.pp * 8 / 5), target: moveSpecies.target, disabled: false, @@ -779,7 +784,8 @@ let BattleAbilities = { isNonstandard: "Custom", onModifyMovePriority: -1, onModifyMove(move, pokemon) { - if (move.type === 'Normal' && !['judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'weatherball'].includes(move.id) && !(move.isZ && move.category !== 'Status')) { + const noTypeChange = ['judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'weatherball']; + if (move.type === 'Normal' && !noTypeChange.includes(move.id) && !(move.isZ && move.category !== 'Status')) { move.type = 'Electric'; move.galvanizeBoosted = true; } @@ -997,37 +1003,34 @@ let BattleAbilities = { onResidualOrder: 26, onResidualSubOrder: 1, onResidual(pokemon) { - let stats = []; - let boost = {}; - for (let statPlus in pokemon.boosts) { + let stats: BoostName[] = []; + const boost: SparseBoostsTable = {}; + let statPlus: BoostName; + for (statPlus in pokemon.boosts) { if (statPlus === 'accuracy' || statPlus === 'evasion') continue; - // @ts-ignore if (pokemon.boosts[statPlus] < 6) { stats.push(statPlus); } } - let randomStat = stats.length ? this.sample(stats) : ""; + let randomStat = stats.length ? this.sample(stats) : undefined; if (randomStat) { - // @ts-ignore boost[randomStat] = 1; // Prevent picking the same stat twice, sampleNoReplace dosen't exists on type Battle stats.splice(stats.indexOf(randomStat), 1); } - randomStat = stats.length ? this.sample(stats) : ""; - // @ts-ignore + randomStat = stats.length ? this.sample(stats) : undefined; if (randomStat) boost[randomStat] = 1; stats = []; - for (let statMinus in pokemon.boosts) { + let statMinus: BoostName; + for (statMinus in pokemon.boosts) { if (statMinus === 'accuracy' || statMinus === 'evasion') continue; - // @ts-ignore if (pokemon.boosts[statMinus] > -6 && !(statMinus in boost)) { stats.push(statMinus); } } - randomStat = stats.length ? this.sample(stats) : ""; - // @ts-ignore + randomStat = stats.length ? this.sample(stats) : undefined; if (randomStat) boost[randomStat] = -1; this.boost(boost); @@ -1065,9 +1068,10 @@ let BattleAbilities = { name: "Beast Boost 2", isNonstandard: "Custom", onSourceFaint(target, source, effect) { - if (effect && effect.effectType === 'Move') { - // @ts-ignore - let statOrder = Object.keys(source.storedStats).sort((stat1, stat2) => source.storedStats[stat2] - source.storedStats[stat1]); + if (effect?.effectType === 'Move') { + const statOrder = Object.keys(source.storedStats).sort( + (stat1, stat2) => source.storedStats[stat2 as StatNameExceptHP] - source.storedStats[stat1 as StatNameExceptHP] + ); this.boost({[statOrder[0]]: 1, [statOrder[1]]: 1}, source); } }, @@ -1172,7 +1176,7 @@ let BattleAbilities = { name: "Snow Storm", isNonstandard: "Custom", onStart() { - let snowStorm = this.dex.getEffect('hail'); + const snowStorm = this.dex.getEffect('hail'); this.field.setWeather(snowStorm); }, }, @@ -1184,7 +1188,8 @@ let BattleAbilities = { this.debug('illusion cleared'); let disguisedAs = toID(pokemon.illusion.name); pokemon.illusion = null; - let details = pokemon.species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) + (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : ''); + const details = pokemon.species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) + + (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : ''); this.add('replace', pokemon, details); this.add('-end', pokemon, 'Illusion'); // Handle hippopotas @@ -1193,8 +1198,8 @@ let BattleAbilities = { pokemon.removeVolatile(disguisedAs); } if (!pokemon.volatiles[toID(pokemon.name)]) { - let status = this.dex.getEffect(toID(pokemon.name)); - if (status && status.exists) { + const status = this.dex.getEffect(toID(pokemon.name)); + if (status?.exists) { pokemon.addVolatile(toID(pokemon.name), pokemon); } } @@ -1217,5 +1222,3 @@ let BattleAbilities = { }, }, }; - -exports.BattleAbilities = BattleAbilities; diff --git a/data/mods/ssb/items.js b/data/mods/ssb/items.ts similarity index 98% rename from data/mods/ssb/items.js rename to data/mods/ssb/items.ts index 256598db54..1aed97c0b7 100644 --- a/data/mods/ssb/items.js +++ b/data/mods/ssb/items.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedItemData}} */ -let BattleItems = { +export const BattleItems: {[k: string]: ModdedItemData} = { // Aeonic noseiumz: { id: "noseiumz", diff --git a/data/mods/ssb/moves.js b/data/mods/ssb/moves.ts similarity index 89% rename from data/mods/ssb/moves.js rename to data/mods/ssb/moves.ts index 488153fcd2..f46dc58bea 100644 --- a/data/mods/ssb/moves.js +++ b/data/mods/ssb/moves.ts @@ -1,12 +1,8 @@ -'use strict'; +// Used for Asheviere and Snaquaza's moves +import {RandomStaffBrosTeams} from './random-teams'; +import {Pokemon, EffectState} from '../../../sim/pokemon'; -// Used for Asheviere and Snaquaza's move -const RandomStaffBrosTeams = require('./random-teams'); -/** @type {typeof import('../../../sim/pokemon').Pokemon} */ -const Pokemon = require(/** @type {any} */ ('../../../.sim-dist/pokemon')).Pokemon; - -/** @type {{[k: string]: ModdedMoveData}} */ -let BattleMovedex = { +export const BattleMovedex: {[k: string]: ModdedMoveData} = { /* // Example "moveid": { @@ -63,8 +59,10 @@ let BattleMovedex = { if (!(['', 'slp', 'frz'].includes(source.status))) { source.cureStatus(); } - let removeTarget = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; - let removeAll = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; + const removeTarget = [ + 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', + ]; + const removeAll = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; for (const targetCondition of removeTarget) { if (target.side.removeSideCondition(targetCondition)) { if (!removeAll.includes(targetCondition)) continue; @@ -160,7 +158,7 @@ let BattleMovedex = { this.add('-anim', source, "Skill Swap", target); }, onHit(target, source) { - let koed; + let koed: Pokemon; if (Math.round(this.random())) { koed = target; this.add(`c|@Aeonic|What a buncha jokers`); @@ -257,7 +255,7 @@ let BattleMovedex = { }, onHit(target, source) { let didSomething = false; - let side = source.side; + const side = source.side; if (side.faintedLastTurn || side.foe.faintedLastTurn) { this.add('-anim', source, "Wish", target); side.addSlotCondition(source, 'wish', source); @@ -341,7 +339,7 @@ let BattleMovedex = { return; } this.add('-activate', target, 'move: Protect'); - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -376,7 +374,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('terrainextender')) { + if (source?.hasItem('terrainextender')) { return 8; } return 5; @@ -447,18 +445,17 @@ let BattleMovedex = { this.add('-anim', target, 'Dark Void', target); }, onHit(target, source, move) { - let wouldMove = this.queue.cancelMove(target); + const wouldMove = this.queue.cancelMove(target); // Generate a new team - let team = this.teamGenerator.getTeam({name: target.side.name, inBattle: true}); + const team = this.teamGenerator.getTeam({name: target.side.name, inBattle: true}); let set = team.shift(); if (set.name === target.set.name) set = team.shift(); // Must be a new set const oldName = target.name; // Bit of a hack so client doesn't crash when formeChange is called for the new pokemon - let effect = this.effect; - this.effect = /** @type {Effect} */ ({id: ''}); - // @ts-ignore - let pokemon = new Pokemon(set, target.side); + const effect = this.effect; + this.effect = {id: ''} as Effect; + const pokemon = new Pokemon(set, target.side); this.effect = effect; pokemon.hp = Math.floor(pokemon.maxhp * (target.hp / target.maxhp)) || 1; @@ -466,7 +463,9 @@ let BattleMovedex = { delete target.volatiles[target.name]; if (target.statusData) pokemon.statusData = target.statusData; for (const [j, moveSlot] of pokemon.moveSlots.entries()) { - moveSlot.pp = Math.floor(moveSlot.maxpp * (target.moveSlots[j] ? (target.moveSlots[j].pp / target.moveSlots[j].maxpp) : 1)); + moveSlot.pp = Math.floor( + moveSlot.maxpp * (target.moveSlots[j] ? (target.moveSlots[j].pp / target.moveSlots[j].maxpp) : 1) + ); } this.add('faint', target); pokemon.position = target.position; @@ -479,12 +478,13 @@ let BattleMovedex = { target.setAbility(set.ability); const format = this.format; - if (format && format.onSwitchIn) format.onSwitchIn.call(this, target); + if (format.exists && format.onSwitchIn) format.onSwitchIn.call(this, target); this.add('-message', `${oldName} was sent to the Distortion World and replaced with somebody else!`); - for (let stat of Object.keys(target.boosts)) { - // @ts-ignore Iterate through stat changes to update client + let stat: BoostName; + for (stat in target.boosts) { + // Iterate through stat changes to update client if (target.boosts[stat] !== 0) { - // @ts-ignore Iterate through stat changes to update client + // Iterate through stat changes to update client this.add('-setboost', target, stat, target.boosts[stat], '[silent]'); } } @@ -733,9 +733,9 @@ let BattleMovedex = { }, onHit(target, source) { // Store percent of HP left, percent of PP left, and status for each pokemon on the user's team - let carryOver = []; - let currentTeam = source.side.pokemon.slice(); - for (let pokemon of currentTeam) { + const carryOver: {hp: number, status: ID, statusData: EffectState, pp: number[]}[] = []; + const currentTeam = source.side.pokemon.slice(); + for (const pokemon of currentTeam) { carryOver.push({ hp: pokemon.hp / pokemon.maxhp, status: pokemon.status, @@ -750,16 +750,15 @@ let BattleMovedex = { } } // Generate a new team - /** @type {Pokemon[]} */ - let team = this.teamGenerator.getTeam({name: source.side.name, inBattle: true}); + let team: Pokemon[] = this.teamGenerator.getTeam({name: source.side.name, inBattle: true}); // Remove Asheviere from generated teams to not allow duplicates team = team.filter(pokemon => !(pokemon.name === 'Asheviere')); // Overwrite un-fainted pokemon other than the user - for (let i = 0; i < currentTeam.length; i++) { - if (currentTeam[i].fainted || !currentTeam[i].hp || currentTeam[i].position === source.position) continue; + for (const [i, mon] of currentTeam.entries()) { + if (mon.fainted || !mon.hp || mon.position === source.position) continue; let set = team.shift(); if (!set) throw new Error('Not enough pokemon left to wonder trade to.'); - let oldSet = carryOver[i]; + const oldSet = carryOver[i]; if (set.name === 'Flare' && currentTeam.filter(p => !p.fainted && p.hp).length <= 2) { // Don't select Super Illusion when there are only 2 unfainted pokemon @@ -768,10 +767,9 @@ let BattleMovedex = { } // Bit of a hack so client doesn't crash when formeChange is called for the new pokemon - let effect = this.effect; - this.effect = /** @type {Effect} */ ({id: ''}); - // @ts-ignore - let pokemon = new Pokemon(set, source.side); + const effect = this.effect; + this.effect = {id: ''} as Effect; + const pokemon = new Pokemon(set, source.side); this.effect = effect; pokemon.hp = Math.floor(pokemon.maxhp * oldSet.hp) || 1; @@ -780,16 +778,16 @@ let BattleMovedex = { for (const [j, moveSlot] of pokemon.moveSlots.entries()) { moveSlot.pp = Math.floor(moveSlot.maxpp * oldSet.pp[j]); } - pokemon.position = currentTeam[i].position; + pokemon.position = mon.position; currentTeam[i] = pokemon; } // Move flare to the front of the team if Super Illusion would not activate - let newTeam = currentTeam.slice().map(p => (!p.fainted && p.hp) ? p.name : null); + const newTeam = currentTeam.slice().map(p => (!p.fainted && p.hp) ? p.name : null); if (newTeam.filter(n => n).pop() === 'Flare') { // Don't swap with the current pokemon or client will softlock - let newIdx = source.position === 0 ? 1 : 0; - let idx = newTeam.indexOf('Flare'); - let original = currentTeam[newIdx]; + const newIdx = source.position === 0 ? 1 : 0; + const idx = newTeam.indexOf('Flare'); + const original = currentTeam[newIdx]; currentTeam[newIdx] = currentTeam[idx]; currentTeam[idx] = original; // Update pokemon.position flags to prevent errors @@ -896,8 +894,10 @@ let BattleMovedex = { }, onHit(target, source, effect) { this.heal(source.baseMaxhp / 3, source); - let dancemoves = ['dragondance', 'featherdance', 'fierydance', 'petaldance', 'quiverdance', 'revelationdance', 'swordsdance', 'teeterdance']; - let randomMove = dancemoves[this.random(dancemoves.length)]; + const dancemoves = [ + 'dragondance', 'featherdance', 'fierydance', 'petaldance', 'quiverdance', 'revelationdance', 'swordsdance', 'teeterdance', + ]; + const randomMove = dancemoves[this.random(dancemoves.length)]; this.useMove(randomMove, target); this.useMove(randomMove, target); // Kill getting locked into petal dance @@ -970,14 +970,20 @@ let BattleMovedex = { this.add('-anim', source, "Defog", target); }, onHit(target, source, move) { - let removeAll = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; - let silentRemove = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist']; + const removeAll = [ + 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', + ]; + const silentRemove = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist']; for (const sideCondition of removeAll) { if (target.side.removeSideCondition(sideCondition)) { - if (!(silentRemove.includes(sideCondition))) this.add('-sideend', target.side, this.dex.getEffect(sideCondition).name, '[from] move: Blustery Winds', '[of] ' + source); + if (!(silentRemove.includes(sideCondition))) { + this.add('-sideend', target.side, this.dex.getEffect(sideCondition).name, '[from] move: Blustery Winds', '[of] ' + source); + } } if (source.side.removeSideCondition(sideCondition)) { - if (!(silentRemove.includes(sideCondition))) this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] move: Blustery Winds', '[of] ' + source); + if (!(silentRemove.includes(sideCondition))) { + this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] move: Blustery Winds', '[of] ' + source); + } } } this.field.clearWeather(); @@ -1091,8 +1097,10 @@ let BattleMovedex = { } let success = false; if (!target.volatiles['substitute'] || move.infiltrates) success = !!this.boost({evasion: -1}); - let removeTarget = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; - let removeAll = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; + const removeTarget = [ + 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', + ]; + const removeAll = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; for (const targetCondition of removeTarget) { if (target.side.removeSideCondition(targetCondition)) { if (!removeAll.includes(targetCondition)) continue; @@ -1451,16 +1459,15 @@ let BattleMovedex = { this.attrLastMove('[still]'); }, onHit(target, source, move) { - let baseForme = source.species.id; - /** @type {{[forme: string]: string[]}} */ - let formes = { + const baseForme = source.species.id; + const formes: {[forme: string]: string[]} = { celebi: ['Future Sight', 'Recover'], jirachi: ['Doom Desire', 'Wish'], manaphy: ['Tail Glow', 'Surf'], shaymin: ['Seed Flare', 'Leech Seed'], victini: ['V-create', 'Blue Flare'], }; - let forme = Object.keys(formes)[this.random(5)]; + const forme = Object.keys(formes)[this.random(5)]; // Suppress Ability now to prevent starting new abilities when transforming source.addVolatile('gastroacid', source); source.formeChange(forme, this.dex.getAbility('psychicsurge'), true); @@ -1605,8 +1612,8 @@ let BattleMovedex = { this.add('-anim', source, "Conversion", source); }, onModifyMove(move, pokemon) { - let types = ['Fire', 'Water', 'Grass']; - let randomType = this.sample(types); + const types = ['Fire', 'Water', 'Grass']; + const randomType = this.sample(types); move.type = randomType; pokemon.addType(randomType); this.add('-start', pokemon, 'typeadd', randomType); @@ -1641,7 +1648,7 @@ let BattleMovedex = { }, onHit(target, source) { this.useMove('Defog', source, target); - let move = this.dex.getActiveMove('uturn'); + const move = this.dex.getActiveMove('uturn'); move.basePower = 90; this.useMove(move, source, target); }, @@ -1713,7 +1720,7 @@ let BattleMovedex = { this.add('-anim', source, 'Let\'s Snuggle Forever', target); }, onHit() { - let messages = ["SPL players don't want you to know about this secret", + const messages = ["SPL players don't want you to know about this secret", "North American player reveals the concerning secret how to make money with Pokemon that will crack you up", "10 amazing facts about Zarel you have never heard of", "Veteran player shared his best team with a beginner - here's what happened after", @@ -1826,8 +1833,8 @@ let BattleMovedex = { this.boost({atk: 2, spa: 2, spe: 2, def: -1, spd: -1}, source); if (source.species.id !== 'miniormeteor' || source.transformed) return; - let rainbow = ['', '-Orange', '-Yellow', '-Green', '-Blue', '-Indigo', '-Violet']; - let color = rainbow[this.random(rainbow.length)]; + const rainbow = ['', '-Orange', '-Yellow', '-Green', '-Blue', '-Indigo', '-Violet']; + const color = rainbow[this.random(rainbow.length)]; source.formeChange(`Minior${color}`, move, true); // Display correct color on client if (color) { @@ -1878,8 +1885,8 @@ let BattleMovedex = { accuracy: 100, basePower: 0, basePowerCallback(pokemon, target) { - let targetWeight = target.getWeight(); - let pokemonWeight = pokemon.getWeight(); + const targetWeight = target.getWeight(); + const pokemonWeight = pokemon.getWeight(); if (pokemonWeight > targetWeight * 5) { return 120; } @@ -1956,8 +1963,7 @@ let BattleMovedex = { }, onHitSide(target, source) { // All possible hazards, and their maximum possible layer count - /** @type {{[key: string]: number}} */ - let hazards = {stealthrock: 1, spikes: 3, toxicspikes: 2, stickyweb: 1}; + const hazards: {[k: string]: number} = {stealthrock: 1, spikes: 3, toxicspikes: 2, stickyweb: 1}; // Check how many layers of each hazard can still be added to the foe's side if (target.getSideCondition('stealthrock')) delete hazards.stealthrock; if (target.getSideCondition('spikes')) { @@ -1970,11 +1976,11 @@ let BattleMovedex = { } if (target.getSideCondition('stickyweb')) delete hazards.stickyweb; // Create a list of hazards not yet at their maximum layer count - let hazardTypes = Object.keys(hazards); + const hazardTypes = Object.keys(hazards); // If there are no possible hazards, don't do anything if (!hazardTypes.length) return false; // Pick a random hazard, and set it - let hazard1 = this.sample(hazardTypes); + const hazard1 = this.sample(hazardTypes); // Theoretically, this should always work this.add('-anim', source, this.dex.getMove(hazard1).name, target); target.addSideCondition(hazard1, source, this.effect); @@ -1985,7 +1991,7 @@ let BattleMovedex = { if (!hazardTypes.length) return true; } // Set the last hazard and animate the switch - let hazard2 = this.sample(hazardTypes); + const hazard2 = this.sample(hazardTypes); this.add('-anim', source, this.dex.getMove(hazard2).name, target); target.addSideCondition(hazard2, source, this.effect); this.add('-anim', source, "Baton Pass", target); @@ -2020,7 +2026,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('terrainextender')) { + if (source?.hasItem('terrainextender')) { return 8; } return 5; @@ -2060,11 +2066,11 @@ let BattleMovedex = { this.debug('Scripted terrain corrupt'); this.add('message', `${pokemon.name} was corrupted by a bug in the Scripted Terrain!`); // generate a movepool - let moves = []; - let pool = this.dex.shuffle(Object.keys(this.dex.data.Movedex)); - let metronome = this.dex.getMove('metronome'); - for (let i of pool) { - let move = this.dex.getMove(i); + const moves = []; + const pool = this.dex.shuffle(Object.keys(this.dex.data.Movedex)); + const metronome = this.dex.getMove('metronome'); + for (const i of pool) { + const move = this.dex.getMove(i); if (i !== move.id) continue; if (move.isZ || move.isNonstandard) continue; if (metronome.noMetronome && metronome.noMetronome.includes(move.id)) continue; @@ -2073,11 +2079,13 @@ let BattleMovedex = { if (moves.length >= 3) break; } moves.push('glitchout'); - if (toID(pokemon.ability).includes('illusion') && pokemon.illusion) this.singleEvent('End', this.dex.getAbility('Illusion'), pokemon.abilityData, pokemon, pokemon); + if (toID(pokemon.ability).includes('illusion') && pokemon.illusion) { + this.singleEvent('End', this.dex.getAbility('Illusion'), pokemon.abilityData, pokemon, pokemon); + } pokemon.formeChange('missingno'); pokemon.moveSlots = []; - for (let moveid of moves) { - let move = this.dex.getMove(moveid); + for (const moveid of moves) { + const move = this.dex.getMove(moveid); if (!move.id) continue; pokemon.moveSlots.push({ move: move.name, @@ -2114,7 +2122,9 @@ let BattleMovedex = { pp: 10, priority: 0, flags: {}, - noMetronome: ['afteryou', 'assist', 'banefulbunker', 'beakblast', 'belch', 'bestow', 'celebrate', 'chatter', 'copycat', 'counter', 'covet', 'craftyshield', 'destinybond', 'detect', 'diamondstorm', 'dragonascent', 'endure', 'feint', 'fleurcannon', 'focuspunch', 'followme', 'freezeshock', 'helpinghand', 'holdhands', 'hyperspacefury', 'hyperspacehole', 'iceburn', 'instruct', 'kingsshield', 'lightofruin', 'matblock', 'mefirst', 'metronome', 'mimic', 'mindblown', 'mirrorcoat', 'mirrormove', 'naturepower', 'originpulse', 'photongeyser', 'plasmafists', 'precipiceblades', 'protect', 'quash', 'quickguard', 'ragepowder', 'relicsong', 'secretsword', 'shelltrap', 'sketch', 'sleeptalk', 'snarl', 'snatch', 'snore', 'spectralthief', 'spikyshield', 'spotlight', 'steameruption', 'struggle', 'switcheroo', 'technoblast', 'thief', 'thousandarrows', 'thousandwaves', 'transform', 'trick', 'trumpcard', 'vcreate', 'wideguard'], + noMetronome: [ + 'afteryou', 'assist', 'banefulbunker', 'beakblast', 'belch', 'bestow', 'celebrate', 'chatter', 'copycat', 'counter', 'covet', 'craftyshield', 'destinybond', 'detect', 'diamondstorm', 'dragonascent', 'endure', 'feint', 'fleurcannon', 'focuspunch', 'followme', 'freezeshock', 'helpinghand', 'holdhands', 'hyperspacefury', 'hyperspacehole', 'iceburn', 'instruct', 'kingsshield', 'lightofruin', 'matblock', 'mefirst', 'metronome', 'mimic', 'mindblown', 'mirrorcoat', 'mirrormove', 'naturepower', 'originpulse', 'photongeyser', 'plasmafists', 'precipiceblades', 'protect', 'quash', 'quickguard', 'ragepowder', 'relicsong', 'secretsword', 'shelltrap', 'sketch', 'sleeptalk', 'snarl', 'snatch', 'snore', 'spectralthief', 'spikyshield', 'spotlight', 'steameruption', 'struggle', 'switcheroo', 'technoblast', 'thief', 'thousandarrows', 'thousandwaves', 'transform', 'trick', 'trumpcard', 'vcreate', 'wideguard', + ], onTryMove() { this.attrLastMove('[still]'); }, @@ -2124,9 +2134,9 @@ let BattleMovedex = { source.addVolatile('glitchout'); }, onHit(target, source, effect) { - let moves = []; - for (let i in this.dex.data.Movedex) { - let move = this.dex.data.Movedex[i]; + const moves = []; + for (const i in this.dex.data.Movedex) { + const move = this.dex.data.Movedex[i]; if (i !== move.id) continue; if (move.isZ || move.isNonstandard) continue; if (effect.noMetronome && effect.noMetronome.includes(move.id)) continue; @@ -2170,7 +2180,7 @@ let BattleMovedex = { secondary: { chance: 20, onHit(target, source) { - let result = this.random(2); + const result = this.random(2); if (result === 0) { target.trySetStatus('psn', source); } else { @@ -2260,7 +2270,7 @@ let BattleMovedex = { this.add('-anim', source, 'Acid Armor', source); }, onHit(pokemon, source, move) { - //this.add('-activate', source, 'move: Víbora'); + // this.add('-activate', source, 'move: Víbora'); let success = false; for (const ally of pokemon.side.pokemon) { if (ally.cureStatus()) success = true; @@ -2434,9 +2444,9 @@ let BattleMovedex = { }, onAfterHit(target, source) { - let boost = {}; - let stat = ['atk', 'def', 'spa', 'spd', 'spe'][this.random(5)]; - // @ts-ignore + const boost: SparseBoostsTable = {}; + const stats: BoostName[] = ['atk', 'def', 'spa', 'spd', 'spe']; + const stat: BoostName = stats[this.random(5)]; boost[stat] = 1; this.boost(boost, source); }, @@ -2504,15 +2514,15 @@ let BattleMovedex = { onHit(pokemon) { pokemon.addVolatile('stall'); this.add(`c|+Kipkluif|o7`); - let target = pokemon.side.foe.active[0]; + const target = pokemon.side.foe.active[0]; if (!target) return; - let targetBoosts = {}; - let sourceBoosts = {}; + const targetBoosts: SparseBoostsTable = {}; + const sourceBoosts: SparseBoostsTable = {}; - for (const stat of ['def', 'spd']) { - // @ts-ignore + const defSpd: BoostName[] = ['def', 'spd']; + let stat: BoostName; + for (stat of defSpd) { targetBoosts[stat] = target.boosts[stat]; - // @ts-ignore sourceBoosts[stat] = pokemon.boosts[stat]; } @@ -2604,20 +2614,23 @@ let BattleMovedex = { }, onHit(pokemon) { const species = pokemon.species; - // @ts-ignore - pokemon.level += 5; - pokemon.set.level = pokemon.level; + let level = pokemon.level; + level += 5; + pokemon.set.level = level; pokemon.formeChange(species); - pokemon.details = species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) + (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : ''); + pokemon.details = species.name + (level === 100 ? '' : ', L' + level) + + (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : ''); this.add('detailschange', pokemon, pokemon.details); - const newHP = Math.floor(Math.floor(2 * species.baseStats['hp'] + pokemon.set.ivs['hp'] + Math.floor(pokemon.set.evs['hp'] / 4) + 100) * pokemon.level / 100 + 10); + const newHP = Math.floor(Math.floor( + 2 * species.baseStats['hp'] + pokemon.set.ivs['hp'] + Math.floor(pokemon.set.evs['hp'] / 4) + 100 + ) * level / 100 + 10); pokemon.hp = newHP - (pokemon.maxhp - pokemon.hp); pokemon.maxhp = newHP; this.add('-heal', pokemon, pokemon.getHealth, '[silent]'); - this.add('-message', `${pokemon.name} advanced 5 levels! It is now level ${pokemon.level}!`); + this.add('-message', `${pokemon.name} advanced 5 levels! It is now level ${level}!`); }, secondary: null, target: "self", @@ -2718,7 +2731,7 @@ let BattleMovedex = { this.add('-anim', source, "Aromatic Mist", target); }, onHit(target, source, move) { - let napWeather = this.field.pseudoWeather['naptime']; + const napWeather = this.field.pseudoWeather['naptime']; // Trigger sleep clause if not the original user if (target !== napWeather.source) { for (const ally of target.side.pokemon) { @@ -2789,9 +2802,13 @@ let BattleMovedex = { accuracy: true, basePower: 0, damageCallback(pokemon, target) { - let damage = pokemon.hp; + const damage = pokemon.hp; pokemon.faint(); - if (target.volatiles['banefulbunker'] || target.volatiles['kingsshield'] || target.side.sideConditions['matblock'] || target.volatiles['protect'] || target.volatiles['spikyshield'] || target.volatiles['lilypadshield'] || target.volatiles['backoffgrrr']) { + if ( + target.volatiles['banefulbunker'] || target.volatiles['kingsshield'] || + target.side.sideConditions['matblock'] || target.volatiles['protect'] || target.volatiles['spikyshield'] || + target.volatiles['lilypadshield'] || target.volatiles['backoffgrrr'] + ) { this.add('-zbroken', target); return Math.floor(damage / 4); } @@ -2839,7 +2856,7 @@ let BattleMovedex = { onPrepareHit(target, source) { this.add('-anim', source, "Protect", source); this.add('-anim', source, "Quiver Dance", source); - let result = !!this.queue.willAct() && this.runEvent('StallMove', source); + const result = !!this.queue.willAct() && this.runEvent('StallMove', source); return result; }, onHit(target, source) { @@ -2925,20 +2942,20 @@ let BattleMovedex = { return false; } } - let weaknesses = []; - for (let type in this.dex.data.TypeChart) { - let typeMod = this.dex.getEffectiveness(type, targetTypes); + const weaknesses = []; + for (const type in this.dex.data.TypeChart) { + const typeMod = this.dex.getEffectiveness(type, targetTypes); if (typeMod > 0 && this.dex.getImmunity(type, target)) weaknesses.push(type); } if (!weaknesses.length) { return false; } - let randomType = this.sample(weaknesses); + const randomType = this.sample(weaknesses); source.setItem(randomType + 'memory'); this.add('-item', source, source.getItem(), '[from] move: Type Analysis'); - let species = this.dex.getSpecies('Silvally-' + randomType); + const species = this.dex.getSpecies('Silvally-' + randomType); source.formeChange(species, this.dex.getAbility('rkssystem'), true); - let move = this.dex.getActiveMove('multiattack'); + const move = this.dex.getActiveMove('multiattack'); move.basePower = 80; this.useMove(move, source, target); }, @@ -2969,13 +2986,12 @@ let BattleMovedex = { }, onHit(target, source) { this.boost({atk: -2, spa: -2}, source, source, this.dex.getActiveMove('/scavenges u')); - let targetBoosts = {}; - let sourceBoosts = {}; + const targetBoosts: SparseBoostsTable = {}; + const sourceBoosts: SparseBoostsTable = {}; - for (let i in target.boosts) { - // @ts-ignore + let i: BoostName; + for (i in target.boosts) { targetBoosts[i] = target.boosts[i]; - // @ts-ignore sourceBoosts[i] = source.boosts[i]; } @@ -3016,7 +3032,7 @@ let BattleMovedex = { onAfterMoveSecondarySelf(pokemon) { let stockpileLayers = 0; if (pokemon.volatiles['stockpile']) stockpileLayers = pokemon.volatiles['stockpile'].layers; - let boosts = {}; + const boosts: SparseBoostsTable = {}; if (pokemon.boosts.def > stockpileLayers) boosts.def = stockpileLayers - pokemon.boosts.def; if (pokemon.boosts.spd > stockpileLayers) boosts.spd = stockpileLayers - pokemon.boosts.spd; if (boosts.def || boosts.spd) this.boost(boosts, pokemon, pokemon); @@ -3105,7 +3121,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('terrainextender')) { + if (source?.hasItem('terrainextender')) { return 8; } return 5; @@ -3123,7 +3139,9 @@ let BattleMovedex = { this.add('-fieldstart', 'move: Prismatic Terrain'); } this.add('-message', 'The battlefield suddenly got a refractive high poly count!'); - let removeAll = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; + const removeAll = [ + 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', + ]; for (const sideCondition of removeAll) { if (source.side.foe.removeSideCondition(sideCondition)) { this.add('-sideend', source.side.foe, this.dex.getEffect(sideCondition).name, '[from] move: Prismatic Terrain', '[of] ' + source); @@ -3249,7 +3267,7 @@ let BattleMovedex = { }, onAfterHit(target, source) { if (source.hp) { - let item = target.takeItem(); + const item = target.takeItem(); if (item) { this.add('-enditem', target, item.name, '[from] move: Ultimate Slash', '[of] ' + source); } @@ -3448,13 +3466,12 @@ let BattleMovedex = { } }, onHit(target, source) { - let targetBoosts = {}; - let sourceBoosts = {}; + const targetBoosts: SparseBoostsTable = {}; + const sourceBoosts: SparseBoostsTable = {}; - for (let i in target.boosts) { - // @ts-ignore + let i: BoostName; + for (i in target.boosts) { targetBoosts[i] = target.boosts[i]; - // @ts-ignore sourceBoosts[i] = source.boosts[i]; } @@ -3510,7 +3527,7 @@ let BattleMovedex = { return; } this.add('-activate', target, 'move: Protect'); - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -3585,8 +3602,8 @@ let BattleMovedex = { duration: 7, onBoost(boost, target, source, effect) { let positiveBoost = false; - let values = Object.values(boost); - for (let i of values) { + const values = Object.values(boost); + for (const i of values) { if (i !== undefined && i > 0) { positiveBoost = true; break; @@ -3642,7 +3659,7 @@ let BattleMovedex = { }, { chance: 20, onHit(target, source) { - let result = this.random(2); + const result = this.random(2); if (result === 0) { target.trySetStatus('par', source); } else { @@ -3744,10 +3761,9 @@ let BattleMovedex = { }, onTryHit(target, source, move) { let success = false; - for (let i in target.boosts) { - // @ts-ignore + let i: BoostName; + for (i in target.boosts) { if (target.boosts[i] === 0) continue; - // @ts-ignore target.boosts[i] = -target.boosts[i]; success = true; } @@ -4010,47 +4026,60 @@ let BattleMovedex = { priority: 0, flags: {}, onModifyMove(move) { + move.type = move.baseMove ? this.dex.getMove(move.baseMove).type : move.type; + // Hack for Snaquaza's Z move + move.basePower = move.baseMove ? this.dex.getMove(move.baseMove).basePower : move.basePower; + // Hack for Snaquaza's Z move + move.category = move.baseMove ? this.dex.getMove(move.baseMove).category : move.category; // @ts-ignore Hack for Snaquaza's Z move - move.type = move.baseMove ? move.baseMove.type : move.type; - // @ts-ignore Hack for Snaquaza's Z move - move.basePower = move.baseMove ? move.baseMove.basePower : move.basePower; - // @ts-ignore Hack for Snaquaza's Z move - move.category = move.baseMove ? move.baseMove.category : move.category; - // @ts-ignore Hack for Snaquaza's Z move - this.claimMove = move.baseMove; + this.claimMove = move.baseMove ? this.dex.getMove(move.baseMove) : this.dex.getMove('bravebird'); }, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source, move) { - let zmove = this.dex.getMove(this.zMoveTable[move.type]); + const zmove = this.dex.getMove(this.zMoveTable[move.type]); this.add('-anim', source, zmove.name, target); this.add('-anim', source, "Transform", source); }, onAfterMoveSecondarySelf(pokemon, move) { - /** @type {{[move: string]: string[]}} */ - let claims = { - bravebird: ['Braviary', 'Crobat', 'Decidueye', 'Dodrio', 'Farfetch\'d', 'Golbat', 'Mandibuzz', 'Pidgeot', 'Skarmory', 'Staraptor', 'Swanna', 'Swellow', 'Talonflame', 'Tapu Koko', 'Toucannon'], - superpower: ['Absol', 'Aggron', 'Armaldo', 'Avalugg', 'Azumarill', 'Barbaracle', 'Basculin', 'Beartic', 'Bewear', 'Bibarel', 'Bouffalant', 'Braviary', 'Breloom', 'Buzzwole', 'Cacturne', 'Carracosta', 'Celesteela', 'Chesnaught', 'Cobalion', 'Conkeldurr', 'Crabominable', 'Crawdaunt', 'Darmanitan', 'Diggersby', 'Donphan', 'Dragonite', 'Drampa', 'Druddigon', 'Durant', 'Eelektross', 'Emboar', 'Exeggutor-Alola', 'Feraligatr', 'Flareon', 'Flygon', 'Gigalith', 'Gogoat', 'Golem', 'Golurk', 'Goodra', 'Granbull', 'Gurdurr', 'Hariyama', 'Hawlucha', 'Haxorus', 'Heatmor', 'Hippowdon', 'Hitmonlee', 'Hydreigon', 'Incineroar', 'Kabutops', 'Keldeo', 'Kingler', 'Komala', 'Kommo-o', 'Krookodile', 'Landorus-Therian', 'Lurantis', 'Luxray', 'Machamp', 'Malamar', 'Mamoswine', 'Mew', 'Mudsdale', 'Nidoking', 'Nidoqueen', 'Pangoro', 'Passimian', 'Piloswine', 'Pinsir', 'Rampardos', 'Regice', 'Regigigas', 'Regirock', 'Registeel', 'Reuniclus', 'Rhydon', 'Rhyperior', 'Samurott', 'Sawk', 'Scizor', 'Scolipede', 'Simipour', 'Simisage', 'Simisear', 'Smeargle', 'Snorlax', 'Spinda', 'Stakataka', 'Stoutland', 'Swampert', 'Tapu Bulu', 'Terrakion', 'Throh', 'Thundurus', 'Torkoal', 'Tornadus', 'Torterra', 'Tyranitar', 'Tyrantrum', 'Ursaring', 'Virizion', 'Zeraora'], - suckerpunch: ['Absol', 'Arbok', 'Ariados', 'Banette', 'Bisharp', 'Cacturne', 'Celebi', 'Corsola', 'Decidueye', 'Delcatty', 'Drifblim', 'Druddigon', 'Dugtrio', 'Dusknoir', 'Electrode', 'Emboar', 'Froslass', 'Furfrou', 'Furret', 'Galvantula', 'Gengar', 'Girafarig', 'Golem', 'Golisopod', 'Heatmor', 'Hitmonlee', 'Hitmontop', 'Houndoom', 'Huntail', 'Kangaskhan', 'Kecleon', 'Komala', 'Lanturn', 'Latias', 'Liepard', 'Lycanroc', 'Maractus', 'Mawile', 'Meowstic', 'Mew', 'Mightyena', 'Mismagius', 'Nidoking', 'Nidoqueen', 'Purugly', 'Raticate', 'Rotom', 'Sableye', 'Seviper', 'Shiftry', 'Skuntank', 'Slaking', 'Smeargle', 'Spinda', 'Spiritomb', 'Stantler', 'Sudowoodo', 'Toxicroak', 'Umbreon', 'Victreebel', 'Wormadam', 'Xatu'], - flamethrower: ['Absol', 'Aerodactyl', 'Aggron', 'Altaria', 'Arcanine', 'Audino', 'Azelf', 'Bastiodon', 'Blacephalon', 'Blissey', 'Camerupt', 'Castform', 'Celesteela', 'Chandelure', 'Chansey', 'Charizard', 'Clefable', 'Clefairy', 'Darmanitan', 'Delphox', 'Dragonite', 'Drampa', 'Druddigon', 'Dunsparce', 'Eelektross', 'Electivire', 'Emboar', 'Entei', 'Exeggutor-Alola', 'Exploud', 'Flareon', 'Flygon', 'Furret', 'Garchomp', 'Golem', 'Goodra', 'Gourgeist', 'Granbull', 'Guzzlord', 'Gyarados', 'Heatmor', 'Heatran', 'Houndoom', 'Hydreigon', 'Incineroar', 'Infernape', 'Kangaskhan', 'Kecleon', 'Kommo-o', 'Lickilicky', 'Machamp', 'Magcargo', 'Magmortar', 'Malamar', 'Manectric', 'Marowak', 'Mawile', 'Mew', 'Moltres', 'Muk', 'Nidoking', 'Nidoqueen', 'Ninetales', 'Noivern', 'Octillery', 'Pyroar', 'Rampardos', 'Rapidash', 'Rhydon', 'Rhyperior', 'Salamence', 'Salazzle', 'Seviper', 'Silvally', 'Simisear', 'Skuntank', 'Slaking', 'Slowbro', 'Slowking', 'Slurpuff', 'Smeargle', 'Snorlax', 'Solrock', 'Talonflame', 'Tauros', 'Togekiss', 'Torkoal', 'Turtonator', 'Typhlosion', 'Tyranitar', 'Watchog', 'Weezing', 'Wigglytuff', 'Zangoose'], - thunderbolt: ['Absol', 'Aggron', 'Ambipom', 'Ampharos', 'Aromatisse', 'Audino', 'Aurorus', 'Azelf', 'Banette', 'Bastiodon', 'Beheeyem', 'Bibarel', 'Blissey', 'Castform', 'Chansey', 'Cinccino', 'Clefable', 'Clefairy', 'Dedenne', 'Delcatty', 'Dragalge', 'Dragonite', 'Drampa', 'Drifblim', 'Dunsparce', 'Eelektross', 'Electivire', 'Electrode', 'Emolga', 'Ferroseed', 'Ferrothorn', 'Froslass', 'Furret', 'Gallade', 'Galvantula', 'Garbodor', 'Gardevoir', 'Gengar', 'Girafarig', 'Golem-Alola', 'Golurk', 'Goodra', 'Gothitelle', 'Granbull', 'Gyarados', 'Heliolisk', 'Illumise', 'Jirachi', 'Jolteon', 'Kangaskhan', 'Kecleon', 'Klinklang', 'Lanturn', 'Lapras', 'Latias', 'Latios', 'Lickilicky', 'Linoone', 'Lopunny', 'Luxray', 'Magearna', 'Magmortar', 'Magneton', 'Magnezone', 'Malamar', 'Manectric', 'Marowak-Alola', 'Marowak-Alola-Totem', 'Meloetta', 'Meowstic', 'Mesprit', 'Mew', 'Miltank', 'Mimikyu', 'Minun', 'Mismagius', 'Mr. Mime', 'Muk', 'Nidoking', 'Nidoqueen', 'Nihilego', 'Oranguru', 'Pachirisu', 'Persian', 'Plusle', 'Porygon-Z', 'Porygon2', 'Primeape', 'Probopass', 'Purugly', 'Raichu', 'Raikou', 'Rampardos', 'Raticate', 'Regice', 'Regigigas', 'Regirock', 'Registeel', 'Rhydon', 'Rhyperior', 'Rotom', 'Silvally', 'Slaking', 'Slurpuff', 'Smeargle', 'Snorlax', 'Stantler', 'Starmie', 'Stoutland', 'Stunfisk', 'Tapu Koko', 'Tapu Lele', 'Tauros', 'Thundurus', 'Togedemaru', 'Tyranitar', 'Uxie', 'Vikavolt', 'Volbeat', 'Watchog', 'Weezing', 'Wigglytuff', 'Xurkitree', 'Zangoose', 'Zapdos', 'Zebstrika', 'Zeraora'], - icebeam: ['Abomasnow', 'Absol', 'Aggron', 'Alomomola', 'Altaria', 'Araquanid', 'Articuno', 'Audino', 'Aurorus', 'Avalugg', 'Azumarill', 'Barbaracle', 'Basculin', 'Bastiodon', 'Beartic', 'Bibarel', 'Blastoise', 'Blissey', 'Bruxish', 'Carracosta', 'Castform', 'Chansey', 'Clawitzer', 'Claydol', 'Clefable', 'Clefairy', 'Cloyster', 'Corsola', 'Crabominable', 'Crawdaunt', 'Cresselia', 'Cryogonal', 'Delcatty', 'Delibird', 'Dewgong', 'Dragonite', 'Drampa', 'Dunsparce', 'Dusknoir', 'Empoleon', 'Exploud', 'Feraligatr', 'Floatzel', 'Froslass', 'Furret', 'Gastrodon', 'Glaceon', 'Glalie', 'Golduck', 'Golisopod', 'Golurk', 'Goodra', 'Gorebyss', 'Greninja', 'Gyarados', 'Huntail', 'Jellicent', 'Jynx', 'Kabutops', 'Kangaskhan', 'Kecleon', 'Kingdra', 'Kingler', 'Kyurem', 'Lanturn', 'Lapras', 'Latias', 'Latios', 'Lickilicky', 'Linoone', 'Lopunny', 'Ludicolo', 'Lumineon', 'Lunatone', 'Luvdisc', 'Magearna', 'Mamoswine', 'Manaphy', 'Mantine', 'Marowak', 'Masquerain', 'Mawile', 'Mesprit', 'Mew', 'Milotic', 'Miltank', 'Nidoking', 'Nidoqueen', 'Ninetales-Alola', 'Octillery', 'Omastar', 'Pelipper', 'Phione', 'Piloswine', 'Politoed', 'Poliwrath', 'Porygon-Z', 'Porygon2', 'Primarina', 'Quagsire', 'Qwilfish', 'Rampardos', 'Raticate', 'Regice', 'Relicanth', 'Rhydon', 'Rhyperior', 'Samurott', 'Seaking', 'Sharpedo', 'Sigilyph', 'Silvally', 'Simipour', 'Slaking', 'Slowbro', 'Slowking', 'Smeargle', 'Sneasel', 'Snorlax', 'Starmie', 'Suicune', 'Swalot', 'Swampert', 'Swanna', 'Tapu Fini', 'Tauros', 'Tentacruel', 'Toxapex', 'Tyranitar', 'Vanilluxe', 'Vaporeon', 'Wailord', 'Walrein', 'Weavile', 'Whiscash', 'Wigglytuff', 'Wishiwashi', 'Zangoose'], + const claims: {[move: string]: string[]} = { + bravebird: [ + 'Braviary', 'Crobat', 'Decidueye', 'Dodrio', 'Farfetch\'d', 'Golbat', 'Mandibuzz', 'Pidgeot', 'Skarmory', 'Staraptor', 'Swanna', 'Swellow', 'Talonflame', 'Tapu Koko', 'Toucannon', + ], + superpower: [ + 'Absol', 'Aggron', 'Armaldo', 'Avalugg', 'Azumarill', 'Barbaracle', 'Basculin', 'Beartic', 'Bewear', 'Bibarel', 'Bouffalant', 'Braviary', 'Breloom', 'Buzzwole', 'Cacturne', 'Carracosta', 'Celesteela', 'Chesnaught', 'Cobalion', 'Conkeldurr', 'Crabominable', 'Crawdaunt', 'Darmanitan', 'Diggersby', 'Donphan', 'Dragonite', 'Drampa', 'Druddigon', 'Durant', 'Eelektross', 'Emboar', 'Exeggutor-Alola', 'Feraligatr', 'Flareon', 'Flygon', 'Gigalith', 'Gogoat', 'Golem', 'Golurk', 'Goodra', 'Granbull', 'Gurdurr', 'Hariyama', 'Hawlucha', 'Haxorus', 'Heatmor', 'Hippowdon', 'Hitmonlee', 'Hydreigon', 'Incineroar', 'Kabutops', 'Keldeo', 'Kingler', 'Komala', 'Kommo-o', 'Krookodile', 'Landorus-Therian', 'Lurantis', 'Luxray', 'Machamp', 'Malamar', 'Mamoswine', 'Mew', 'Mudsdale', 'Nidoking', 'Nidoqueen', 'Pangoro', 'Passimian', 'Piloswine', 'Pinsir', 'Rampardos', 'Regice', 'Regigigas', 'Regirock', 'Registeel', 'Reuniclus', 'Rhydon', 'Rhyperior', 'Samurott', 'Sawk', 'Scizor', 'Scolipede', 'Simipour', 'Simisage', 'Simisear', 'Smeargle', 'Snorlax', 'Spinda', 'Stakataka', 'Stoutland', 'Swampert', 'Tapu Bulu', 'Terrakion', 'Throh', 'Thundurus', 'Torkoal', 'Tornadus', 'Torterra', 'Tyranitar', 'Tyrantrum', 'Ursaring', 'Virizion', 'Zeraora', + ], + suckerpunch: [ + 'Absol', 'Arbok', 'Ariados', 'Banette', 'Bisharp', 'Cacturne', 'Celebi', 'Corsola', 'Decidueye', 'Delcatty', 'Drifblim', 'Druddigon', 'Dugtrio', 'Dusknoir', 'Electrode', 'Emboar', 'Froslass', 'Furfrou', 'Furret', 'Galvantula', 'Gengar', 'Girafarig', 'Golem', 'Golisopod', 'Heatmor', 'Hitmonlee', 'Hitmontop', 'Houndoom', 'Huntail', 'Kangaskhan', 'Kecleon', 'Komala', 'Lanturn', 'Latias', 'Liepard', 'Lycanroc', 'Maractus', 'Mawile', 'Meowstic', 'Mew', 'Mightyena', 'Mismagius', 'Nidoking', 'Nidoqueen', 'Purugly', 'Raticate', 'Rotom', 'Sableye', 'Seviper', 'Shiftry', 'Skuntank', 'Slaking', 'Smeargle', 'Spinda', 'Spiritomb', 'Stantler', 'Sudowoodo', 'Toxicroak', 'Umbreon', 'Victreebel', 'Wormadam', 'Xatu', + ], + flamethrower: [ + 'Absol', 'Aerodactyl', 'Aggron', 'Altaria', 'Arcanine', 'Audino', 'Azelf', 'Bastiodon', 'Blacephalon', 'Blissey', 'Camerupt', 'Castform', 'Celesteela', 'Chandelure', 'Chansey', 'Charizard', 'Clefable', 'Clefairy', 'Darmanitan', 'Delphox', 'Dragonite', 'Drampa', 'Druddigon', 'Dunsparce', 'Eelektross', 'Electivire', 'Emboar', 'Entei', 'Exeggutor-Alola', 'Exploud', 'Flareon', 'Flygon', 'Furret', 'Garchomp', 'Golem', 'Goodra', 'Gourgeist', 'Granbull', 'Guzzlord', 'Gyarados', 'Heatmor', 'Heatran', 'Houndoom', 'Hydreigon', 'Incineroar', 'Infernape', 'Kangaskhan', 'Kecleon', 'Kommo-o', 'Lickilicky', 'Machamp', 'Magcargo', 'Magmortar', 'Malamar', 'Manectric', 'Marowak', 'Mawile', 'Mew', 'Moltres', 'Muk', 'Nidoking', 'Nidoqueen', 'Ninetales', 'Noivern', 'Octillery', 'Pyroar', 'Rampardos', 'Rapidash', 'Rhydon', 'Rhyperior', 'Salamence', 'Salazzle', 'Seviper', 'Silvally', 'Simisear', 'Skuntank', 'Slaking', 'Slowbro', 'Slowking', 'Slurpuff', 'Smeargle', 'Snorlax', 'Solrock', 'Talonflame', 'Tauros', 'Togekiss', 'Torkoal', 'Turtonator', 'Typhlosion', 'Tyranitar', 'Watchog', 'Weezing', 'Wigglytuff', 'Zangoose', + ], + thunderbolt: [ + 'Absol', 'Aggron', 'Ambipom', 'Ampharos', 'Aromatisse', 'Audino', 'Aurorus', 'Azelf', 'Banette', 'Bastiodon', 'Beheeyem', 'Bibarel', 'Blissey', 'Castform', 'Chansey', 'Cinccino', 'Clefable', 'Clefairy', 'Dedenne', 'Delcatty', 'Dragalge', 'Dragonite', 'Drampa', 'Drifblim', 'Dunsparce', 'Eelektross', 'Electivire', 'Electrode', 'Emolga', 'Ferroseed', 'Ferrothorn', 'Froslass', 'Furret', 'Gallade', 'Galvantula', 'Garbodor', 'Gardevoir', 'Gengar', 'Girafarig', 'Golem-Alola', 'Golurk', 'Goodra', 'Gothitelle', 'Granbull', 'Gyarados', 'Heliolisk', 'Illumise', 'Jirachi', 'Jolteon', 'Kangaskhan', 'Kecleon', 'Klinklang', 'Lanturn', 'Lapras', 'Latias', 'Latios', 'Lickilicky', 'Linoone', 'Lopunny', 'Luxray', 'Magearna', 'Magmortar', 'Magneton', 'Magnezone', 'Malamar', 'Manectric', 'Marowak-Alola', 'Marowak-Alola-Totem', 'Meloetta', 'Meowstic', 'Mesprit', 'Mew', 'Miltank', 'Mimikyu', 'Minun', 'Mismagius', 'Mr. Mime', 'Muk', 'Nidoking', 'Nidoqueen', 'Nihilego', 'Oranguru', 'Pachirisu', 'Persian', 'Plusle', 'Porygon-Z', 'Porygon2', 'Primeape', 'Probopass', 'Purugly', 'Raichu', 'Raikou', 'Rampardos', 'Raticate', 'Regice', 'Regigigas', 'Regirock', 'Registeel', 'Rhydon', 'Rhyperior', 'Rotom', 'Silvally', 'Slaking', 'Slurpuff', 'Smeargle', 'Snorlax', 'Stantler', 'Starmie', 'Stoutland', 'Stunfisk', 'Tapu Koko', 'Tapu Lele', 'Tauros', 'Thundurus', 'Togedemaru', 'Tyranitar', 'Uxie', 'Vikavolt', 'Volbeat', 'Watchog', 'Weezing', 'Wigglytuff', 'Xurkitree', 'Zangoose', 'Zapdos', 'Zebstrika', 'Zeraora', + ], + icebeam: [ + 'Abomasnow', 'Absol', 'Aggron', 'Alomomola', 'Altaria', 'Araquanid', 'Articuno', 'Audino', 'Aurorus', 'Avalugg', 'Azumarill', 'Barbaracle', 'Basculin', 'Bastiodon', 'Beartic', 'Bibarel', 'Blastoise', 'Blissey', 'Bruxish', 'Carracosta', 'Castform', 'Chansey', 'Clawitzer', 'Claydol', 'Clefable', 'Clefairy', 'Cloyster', 'Corsola', 'Crabominable', 'Crawdaunt', 'Cresselia', 'Cryogonal', 'Delcatty', 'Delibird', 'Dewgong', 'Dragonite', 'Drampa', 'Dunsparce', 'Dusknoir', 'Empoleon', 'Exploud', 'Feraligatr', 'Floatzel', 'Froslass', 'Furret', 'Gastrodon', 'Glaceon', 'Glalie', 'Golduck', 'Golisopod', 'Golurk', 'Goodra', 'Gorebyss', 'Greninja', 'Gyarados', 'Huntail', 'Jellicent', 'Jynx', 'Kabutops', 'Kangaskhan', 'Kecleon', 'Kingdra', 'Kingler', 'Kyurem', 'Lanturn', 'Lapras', 'Latias', 'Latios', 'Lickilicky', 'Linoone', 'Lopunny', 'Ludicolo', 'Lumineon', 'Lunatone', 'Luvdisc', 'Magearna', 'Mamoswine', 'Manaphy', 'Mantine', 'Marowak', 'Masquerain', 'Mawile', 'Mesprit', 'Mew', 'Milotic', 'Miltank', 'Nidoking', 'Nidoqueen', 'Ninetales-Alola', 'Octillery', 'Omastar', 'Pelipper', 'Phione', 'Piloswine', 'Politoed', 'Poliwrath', 'Porygon-Z', 'Porygon2', 'Primarina', 'Quagsire', 'Qwilfish', 'Rampardos', 'Raticate', 'Regice', 'Relicanth', 'Rhydon', 'Rhyperior', 'Samurott', 'Seaking', 'Sharpedo', 'Sigilyph', 'Silvally', 'Simipour', 'Slaking', 'Slowbro', 'Slowking', 'Smeargle', 'Sneasel', 'Snorlax', 'Starmie', 'Suicune', 'Swalot', 'Swampert', 'Swanna', 'Tapu Fini', 'Tauros', 'Tentacruel', 'Toxapex', 'Tyranitar', 'Vanilluxe', 'Vaporeon', 'Wailord', 'Walrein', 'Weavile', 'Whiscash', 'Wigglytuff', 'Wishiwashi', 'Zangoose', + ], }; // @ts-ignore Hack for Snaquaza's Z move - const baseMove = this.claimMove ? this.claimMove.id : 'bravebird'; + const baseMove = this.claimMove.id; const pool = claims[baseMove]; - if (!pool) throw new Error(`SSB: Unable to find fake claim movepool for the move: "${baseMove}".`); // Should never happen + if (!pool) { + // Should never happen + throw new Error(`SSB: Unable to find fake claim movepool for the move: "${baseMove}".`); + } const claim = claims[baseMove][this.random(pool.length)]; // Generate new set const generator = new RandomStaffBrosTeams('gen7randombattle', this.prng); - let set = generator.randomSet(claim); + const set = generator.randomSet(claim); // Suppress Ability now to prevent starting new abilities when transforming pokemon.addVolatile('gastroacid', pokemon); // Tranform into it pokemon.formeChange(set.species); - for (let newMove of set.moves) { - let moveSpecies = this.dex.getMove(newMove); + for (const newMove of set.moves) { + const moveSpecies = this.dex.getMove(newMove); if (pokemon.moves.includes(moveSpecies.id)) continue; pokemon.moveSlots.push({ move: moveSpecies.name, @@ -4064,7 +4093,7 @@ let BattleMovedex = { }); } // Update HP - // @ts-ignore Hack for Snaquaza's Z Move + // Hack for Snaquaza's Z Move pokemon.m.claimHP = pokemon.hp; pokemon.heal(pokemon.maxhp - pokemon.hp, pokemon); this.add('-heal', pokemon, pokemon.getHealth, '[silent]'); @@ -4257,7 +4286,7 @@ let BattleMovedex = { this.queue.insertChoice({choice: 'event', event: 'SSBRotate', pokemon: pokemon, priority: -69}); }, // @ts-ignore unsupported custom event - onSSBRotate(/** @type {Pokemon} */ pokemon) { + onSSBRotate(pokemon: Pokemon) { // @ts-ignore Unsupported custom event, this is refering to a battle this.add('-message', `${pokemon.illusion ? pokemon.illusion.name : pokemon.name} is preparing to switch out!`); pokemon.switchFlag = true; @@ -4394,7 +4423,7 @@ let BattleMovedex = { }, onHit(target, source) { const sideConditions = {spikes: 1, toxicspikes: 1, stealthrock: 1, stickyweb: 1}; - for (let i in sideConditions) { + for (const i in sideConditions) { let layers = source.side.sideConditions[i] ? (source.side.sideConditions[i].layers || 1) : 1; if (source.side.removeSideCondition(i)) { this.add('-sideend', source.side, this.dex.getEffect(i).name, '[from] move: Smoke Bomb', '[of] ' + source); @@ -4413,7 +4442,7 @@ let BattleMovedex = { accuracy: 55, basePower: 0, basePowerCallback(pokemon, target) { - let targetWeight = target.getWeight(); + const targetWeight = target.getWeight(); if (targetWeight >= 2000) { this.debug('120 bp'); return 120; @@ -4454,9 +4483,13 @@ let BattleMovedex = { this.add('-anim', source, "Flash", target); // Really feel like this could be done better (blocked by protect and alike moves.) - if (!(target.volatiles['banefulbunker'] || target.volatiles['kingsshield'] || target.side.sideConditions['matblock'] || target.volatiles['protect'] || target.volatiles['spikyshield'] || target.volatiles['lilypadshield'] || target.volatiles['backoffgrrr'])) { + if (!( + target.volatiles['banefulbunker'] || target.volatiles['kingsshield'] || + target.side.sideConditions['matblock'] || target.volatiles['protect'] || target.volatiles['spikyshield'] || + target.volatiles['lilypadshield'] || target.volatiles['backoffgrrr'] + )) { target.addVolatile('weightdoubler', source); - let item = target.takeItem(); + const item = target.takeItem(); if (!target.item) { if (item) this.add('-enditem', target, item.name, '[from] move: Mini Singularity', '[of] ' + source); target.setItem('ironball'); @@ -4594,11 +4627,10 @@ let BattleMovedex = { if (this.dex.getMove(i).gen > this.gen) continue; moves.push(move); } - let randomMove; + let randomMove: string; if (moves.length) { randomMove = this.sample(moves).id; - } - if (!randomMove) { + } else { return false; } this.useMove(randomMove, target); @@ -4764,7 +4796,7 @@ let BattleMovedex = { return null; } this.attrLastMove('[still]'); - let move = pokemon.species.id === 'meloettapirouette' ? 'Brick Break' : 'Relic Song'; + const move = pokemon.species.id === 'meloettapirouette' ? 'Brick Break' : 'Relic Song'; this.add('-anim', pokemon, move, target); }, onHit(target, pokemon, move) { @@ -4829,16 +4861,17 @@ let BattleMovedex = { sleeptalk: { inherit: true, onHit(pokemon) { - let moves = []; + const moves = []; for (const moveSlot of pokemon.moveSlots) { - const move = moveSlot.id; + const move = this.dex.getMove(moveSlot.id); const noSleepTalk = [ 'assist', 'beakblast', 'belch', 'bide', 'celebrate', 'chatter', 'copycat', 'focuspunch', 'mefirst', 'metronome', 'mimic', 'mirrormove', 'naturepower', 'shelltrap', 'sketch', 'sleeptalk', 'uproar', 'teabreak', 'glitzerpopping', // Modded banlist ]; - if (move && !(noSleepTalk.includes(move) || this.dex.getMove(move).flags['charge'] || (this.dex.getMove(move).isZ && this.dex.getMove(move).basePower !== 1))) { - moves.push(move); + if (noSleepTalk.includes(move.id) || move.flags['charge'] || (move.isZ && move.basePower !== 1)) { + continue; } + moves.push(move.id); } let randomMove = ''; if (moves.length) randomMove = this.sample(moves); @@ -4849,5 +4882,3 @@ let BattleMovedex = { }, }, }; - -exports.BattleMovedex = BattleMovedex; diff --git a/data/mods/ssb/pokedex.js b/data/mods/ssb/pokedex.ts similarity index 96% rename from data/mods/ssb/pokedex.js rename to data/mods/ssb/pokedex.ts index f41e275163..c0ce340106 100644 --- a/data/mods/ssb/pokedex.js +++ b/data/mods/ssb/pokedex.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesData}} */ -let BattlePokedex = { +export const BattlePokedex: {[k: string]: ModdedSpeciesData} = { /* // Example id: { @@ -192,5 +189,3 @@ let BattlePokedex = { baseStats: {hp: 85, atk: 100, def: 90, spa: 20, spd: 90, spe: 90}, }, }; - -exports.BattlePokedex = BattlePokedex; diff --git a/data/mods/ssb/random-teams.js b/data/mods/ssb/random-teams.ts similarity index 95% rename from data/mods/ssb/random-teams.js rename to data/mods/ssb/random-teams.ts index 050b4a3296..ebd280bdf4 100644 --- a/data/mods/ssb/random-teams.js +++ b/data/mods/ssb/random-teams.ts @@ -1,44 +1,36 @@ -'use strict'; +import RandomTeams from '../../random-teams'; +import {PRNG, PRNGSeed} from '../../../sim/prng'; -/** @typedef {{[name: string]: SSBSet}} SSBSets */ -/** - * @typedef {Object} SSBSet - * @property {string} species - * @property {string | string[]} ability - * @property {string | string[]} item - * @property {GenderName} gender - * @property {(string | string[])[]} moves - * @property {string} signatureMove - * @property {{hp?: number, atk?: number, def?: number, spa?: number, spd?: number, spe?: number}=} evs - * @property {{hp?: number, atk?: number, def?: number, spa?: number, spd?: number, spe?: number}=} ivs - * @property {string | string[]} nature - * @property {number=} level - * @property {number=} happiness - * @property {(number|boolean)=} shiny - */ +interface SSBSet { + species: string; + ability: string | string[]; + item: string | string[]; + gender: GenderName; + moves: (string | string[])[]; + signatureMove: string; + evs: {hp?: number, atk?: number, def?: number, spa?: number, spd?: number, spe?: number}; + ivs?: {hp?: number, atk?: number, def?: number, spa?: number, spd?: number, spe?: number}; + nature: string | string[]; + shiny?: number | boolean; + level?: number; + happiness?: number; +} +interface SSBSets {[k: string]: SSBSet} -const RandomTeams = require('../../random-teams'); -class RandomStaffBrosTeams extends RandomTeams { - /** - * @param {Format | string} format - * @param {?PRNG | [number, number, number, number]} [prng] - */ - constructor(format, prng) { +export class RandomStaffBrosTeams extends RandomTeams { + allXfix: boolean; + constructor(format: Format | string, prng: PRNG | PRNGSeed | null = null) { super(format, prng); this.allXfix = (this.random(500) === 360); } - /** - * @param {{inBattle?: boolean}} options - */ - randomStaffBrosTeam(options = {}) { + randomStaffBrosTeam(options: {inBattle?: boolean} = {}) { /** @type {PokemonSet[]} */ - let team = []; - /** @type {SSBSets} */ - let sets = { + const team: PokemonSet[] = []; + const sets: SSBSets = { /* // Example: - 'Username': { + Username: { species: 'Species', ability: 'Ability', item: 'Item', gender: '', moves: ['Move Name', ['Move Name', 'Move Name']], signatureMove: 'Move Name', @@ -199,11 +191,17 @@ class RandomStaffBrosTeams extends RandomTeams { evs: {hp: 32, atk: 252, spd: 4, spe: 220}, nature: 'Jolly', }, Catalystic: { - species: 'Ho-oh', ability: 'Magic Guard', item: ['Toxic Orb'], gender: 'M', + species: 'Ho-Oh', ability: 'Magic Guard', item: ['Toxic Orb'], gender: 'M', moves: ['Sacred Fire', 'Brave Bird', 'Strength Sap'], signatureMove: 'Birb to the Rescue', evs: {hp: 252, atk: 252, spe: 4}, nature: 'Adamant', }, + Celestial: { + species: 'Dragonite', ability: 'Ice Absorb', item: 'Sky Plate', gender: 'M', + moves: ['Dragon Dance', 'Extreme Speed', 'Thousand Arrows'], + signatureMove: 'Hyperforce Strike', + evs: {atk: 252, spd: 4, spe: 252}, nature: 'Jolly', shiny: true, + }, Ceteris: { species: 'Greninja', ability: 'Protean', item: 'Expert Belt', gender: 'M', moves: ['Dark Pulse', 'Origin Pulse', 'Gunk Shot', 'Shadow Sneak'], @@ -246,12 +244,6 @@ class RandomStaffBrosTeams extends RandomTeams { signatureMove: 'Galvanized Strike', evs: {hp: 28, atk: 244, def: 188, spe: 48}, nature: 'Adamant', shiny: true, }, - Dragontite: { - species: 'Dragonite', ability: 'Ice Absorb', item: 'Sky Plate', gender: 'M', - moves: ['Dragon Dance', 'Extreme Speed', 'Thousand Arrows'], - signatureMove: 'Hyperforce Strike', - evs: {atk: 252, spd: 4, spe: 252}, nature: 'Jolly', shiny: true, - }, DragonWhale: { species: 'Garchomp', ability: 'Hustle', item: 'Groundium Z', gender: 'M', moves: ['Earthquake', 'Dragon Rush', 'Diamond Storm'], @@ -373,6 +365,12 @@ class RandomStaffBrosTeams extends RandomTeams { signatureMove: 'Mean Look', evs: {hp: 4, atk: 252, spe: 252}, nature: 'Jolly', }, + Instruct: { + species: 'Magearna-Original', ability: 'Numb Numb Juice', item: 'Leftovers', gender: 'N', + moves: ['Sparkly Swirl', 'Defog', ['Toxic', 'Thunder Wave']], + signatureMove: 'Express Yourself', + evs: {hp: 252, def: 164, spd: 92}, ivs: {atk: 0}, nature: 'Calm', shiny: true, + }, irritated: { species: 'Jirachi', ability: 'Serene Grace', item: 'Leftovers', gender: 'F', moves: ['Double Iron Bash', 'Rock Slide', 'Icicle Crash', 'Zing Zap'], @@ -613,12 +611,6 @@ class RandomStaffBrosTeams extends RandomTeams { signatureMove: 'Thicc', evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', }, - Salamander: { - species: 'Magearna-Original', ability: 'Numb Numb Juice', item: 'Leftovers', gender: 'N', - moves: ['Sparkly Swirl', 'Defog', ['Toxic', 'Thunder Wave']], - signatureMove: 'Express Yourself', - evs: {hp: 252, def: 164, spd: 92}, ivs: {atk: 0}, nature: 'Calm', shiny: true, - }, Schiavetto: { species: 'Scolipede', ability: 'RVS', item: 'Mariahcarium Z', gender: 'M', moves: ['Fell Stinger', 'Diamond Storm', ['Hammer Arm', 'Ice Hammer', 'Baneful Bunker']], @@ -770,22 +762,21 @@ class RandomStaffBrosTeams extends RandomTeams { evs: {hp: 188, atk: 68, def: 252}, nature: 'Adamant', }, }; - let pool = Object.keys(sets); - /** @type {{[type: string]: number}} */ - let typePool = {}; + const pool = Object.keys(sets); + const typePool: {[k: string]: number} = {}; let depth = 0; if (options.inBattle) this.allXfix = false; while (pool.length && team.length < 6) { if (depth >= 200) throw new Error(`Infinite loop in Super Staff Bros team generation.`); depth++; - let name = this.allXfix ? 'xfix' : this.sampleNoReplace(pool); - let ssbSet = sets[name]; + const name = this.allXfix ? 'xfix' : this.sampleNoReplace(pool); + const ssbSet = sets[name]; if (!this.allXfix) { // Enforce typing limits - let types = this.dex.getSpecies(ssbSet.species).types; + const types = this.dex.getSpecies(ssbSet.species).types; let rejected = false; - for (let type of types) { + for (const type of types) { if (typePool[type] === undefined) typePool[type] = 0; if (typePool[type] >= 2) { // Reject @@ -795,12 +786,11 @@ class RandomStaffBrosTeams extends RandomTeams { } if (rejected) continue; // Update type counts - for (let type of types) { + for (const type of types) { typePool[type]++; } } - /** @type {PokemonSet} */ - let set = { + const set: PokemonSet = { name: name, species: ssbSet.species, item: Array.isArray(ssbSet.item) ? this.sampleNoReplace(ssbSet.item) : ssbSet.item, @@ -815,17 +805,17 @@ class RandomStaffBrosTeams extends RandomTeams { shiny: typeof ssbSet.shiny === 'number' ? this.randomChance(1, ssbSet.shiny) : ssbSet.shiny, }; if (ssbSet.ivs) { - for (let iv in ssbSet.ivs) { + let iv: StatName; + for (iv in ssbSet.ivs) { // IVs from the set override the default of 31, assume the hardcoded IVs are legal - // @ts-ignore StatsTable has no index signature - set.ivs[iv] = ssbSet.ivs[iv]; + set.ivs[iv] = ssbSet.ivs[iv]!; } } if (ssbSet.evs) { - for (let ev in ssbSet.evs) { + let ev: StatName; + for (ev in ssbSet.evs) { // EVs from the set override the default of 0, assume the hardcoded EVs are legal - // @ts-ignore StatsTable has no index signature - set.evs[ev] = ssbSet.evs[ev]; + set.evs[ev] = ssbSet.evs[ev]!; } } else { set.evs = {hp: 84, atk: 84, def: 84, spa: 84, spd: 84, spe: 84}; @@ -853,4 +843,4 @@ class RandomStaffBrosTeams extends RandomTeams { } } -module.exports = RandomStaffBrosTeams; +export default RandomStaffBrosTeams; diff --git a/data/mods/ssb/scripts.js b/data/mods/ssb/scripts.ts similarity index 82% rename from data/mods/ssb/scripts.js rename to data/mods/ssb/scripts.ts index 2a63daee82..e5d7bcd402 100644 --- a/data/mods/ssb/scripts.js +++ b/data/mods/ssb/scripts.ts @@ -1,7 +1,4 @@ -'use strict'; - -/** @type {ModdedBattleScriptsData} */ -let BattleScripts = { +export const BattleScripts: ModdedBattleScriptsData = { inherit: 'gen7', runMove(moveOrMoveName, pokemon, targetLoc, sourceEffect, zMove, externalMove) { pokemon.activeMoveActions++; @@ -9,7 +6,7 @@ let BattleScripts = { let baseMove = this.dex.getActiveMove(moveOrMoveName); const pranksterBoosted = baseMove.pranksterBoosted; if (!sourceEffect && baseMove.id !== 'struggle' && !zMove) { - let changedMove = this.runEvent('OverrideAction', pokemon, target, baseMove); + const changedMove = this.runEvent('OverrideAction', pokemon, target, baseMove); if (changedMove && changedMove !== true) { baseMove = this.dex.getActiveMove(changedMove); if (pranksterBoosted) baseMove.pranksterBoosted = pranksterBoosted; @@ -30,7 +27,7 @@ let BattleScripts = { this.clearActiveMove(true); return; } */ - let willTryMove = this.runEvent('BeforeMove', pokemon, target, move); + const willTryMove = this.runEvent('BeforeMove', pokemon, target, move); if (!willTryMove) { this.runEvent('MoveAborted', pokemon, target, move); this.clearActiveMove(true); @@ -55,7 +52,9 @@ let BattleScripts = { if (!lockedMove) { if (!pokemon.deductPP(baseMove, null, target) && (move.id !== 'struggle')) { this.add('cant', pokemon, 'nopp', move); - let gameConsole = [null, 'Game Boy', 'Game Boy Color', 'Game Boy Advance', 'DS', 'DS', '3DS', '3DS'][this.gen] || 'Switch'; + const gameConsole = [ + null, 'Game Boy', 'Game Boy Color', 'Game Boy Advance', 'DS', 'DS', '3DS', '3DS', + ][this.gen] || 'Switch'; this.hint(`This is not a bug, this is really how it works on the ${gameConsole}; try it yourself if you don't believe us.`); this.clearActiveMove(true); pokemon.moveThisTurnResult = false; @@ -69,7 +68,7 @@ let BattleScripts = { // Dancer Petal Dance hack // TODO: implement properly - let noLock = externalMove && !pokemon.volatiles.lockedmove; + const noLock = externalMove && !pokemon.volatiles.lockedmove; if (zMove) { if (pokemon.illusion) { @@ -78,14 +77,14 @@ let BattleScripts = { this.add('-zpower', pokemon); pokemon.m.zMoveUsed = true; } - let moveDidSomething = this.useMove(baseMove, pokemon, target, sourceEffect, zMove); + const moveDidSomething = this.useMove(baseMove, pokemon, target, sourceEffect, zMove); if (this.activeMove) move = this.activeMove; this.singleEvent('AfterMove', move, null, pokemon, target, move); this.runEvent('AfterMove', pokemon, target, move); // Dancer's activation order is completely different from any other event, so it's handled separately if (move.flags['dance'] && moveDidSomething && !move.isExternal) { - let dancers = []; + const dancers = []; for (const currentPoke of this.getAllActive()) { if (pokemon === currentPoke) continue; if (currentPoke.hasAbility('dancer') && !currentPoke.isSemiInvulnerable()) { @@ -109,10 +108,19 @@ let BattleScripts = { }, // Modded to allow arrays as Mega Stone options canMegaEvo(pokemon) { - let altForme = pokemon.baseSpecies.otherFormes && this.dex.getSpecies(pokemon.baseSpecies.otherFormes[0]); - let item = pokemon.getItem(); - if (altForme && altForme.isMega && altForme.requiredMove && pokemon.baseMoves.includes(toID(altForme.requiredMove)) && !item.zMove) return altForme.name; - if (item.megaEvolves !== pokemon.baseSpecies.name || (Array.isArray(item.megaStone) && item.megaStone.includes(pokemon.species)) || (typeof item.megaStone === 'string' && item.megaStone === pokemon.forme)) { + const altForme = pokemon.baseSpecies.otherFormes && this.dex.getSpecies(pokemon.baseSpecies.otherFormes[0]); + const item = pokemon.getItem(); + if ( + altForme?.isMega && altForme?.requiredMove && + pokemon.baseMoves.includes(toID(altForme.requiredMove)) && !item.zMove + ) { + return altForme.name; + } + if ( + item.megaEvolves !== pokemon.baseSpecies.name || + (Array.isArray(item.megaStone) && item.megaStone.includes(pokemon.species)) || + (typeof item.megaStone === 'string' && item.megaStone === pokemon.forme) + ) { return null; } if (Array.isArray(item.megaStone)) { @@ -148,17 +156,18 @@ let BattleScripts = { return true; }, getZMove(move, pokemon, skipChecks) { - let item = pokemon.getItem(); + const item = pokemon.getItem(); if (!skipChecks) { if (!item.zMove) return; if (item.itemUser && !item.itemUser.includes(pokemon.species.name)) return; - let moveData = pokemon.getMoveData(move); - if (!moveData || !moveData.pp) return; // Draining the PP of the base move prevents the corresponding Z-move from being used. + const moveData = pokemon.getMoveData(move); + // Draining the PP of the base move prevents the corresponding Z-move from being used. + if (!moveData || !moveData.pp) return; } if (item.zMoveFrom) { if (Array.isArray(item.zMoveFrom)) { - if (item.zMoveFrom.includes(move.name)) return /** @type {string} */ (item.zMove); + if (item.zMoveFrom.includes(move.name)) return item.zMove; } else { if (move.name === item.zMoveFrom) return /** @type {string} */ (item.zMove); } @@ -175,12 +184,12 @@ let BattleScripts = { getActiveZMove(move, pokemon) { let zMove; if (pokemon) { - let item = pokemon.getItem(); - if (item.zMoveFrom && Array.isArray(item.zMoveFrom) ? item.zMoveFrom.includes(move.name) : item.zMoveFrom === move.name) { - // @ts-ignore - zMove = this.dex.getActiveMove(item.zMove); - // @ts-ignore Hack for Snaquaza's Z move - zMove.baseMove = move; + const item = pokemon.getItem(); + const zMoveFrom = Array.isArray(item.zMoveFrom) ? item.zMoveFrom : item.zMoveFrom ? [item.zMoveFrom] : null; + if (zMoveFrom?.includes(move.name)) { + zMove = this.dex.getActiveMove(item.zMove as string); + // Hack for Snaquaza's Z move + zMove.baseMove = move.id; zMove.isZPowered = true; return zMove; } @@ -201,14 +210,18 @@ let BattleScripts = { }, // Modded to allow each Pokemon on a team to use a Z move once per battle canZMove(pokemon) { - if ((pokemon.m && pokemon.m.zMoveUsed) || (pokemon.transformed && (pokemon.species.isMega || pokemon.species.isPrimal || pokemon.species.forme === "Ultra"))) return; - let item = pokemon.getItem(); + if ( + pokemon.m?.zMoveUsed || + (pokemon.transformed && (pokemon.species.isMega || pokemon.species.isPrimal || pokemon.species.forme === "Ultra")) + ) { + return; + } + const item = pokemon.getItem(); if (!item.zMove) return; if (item.itemUser && !item.itemUser.includes(pokemon.species.name)) return; let atLeastOne = false; let mustStruggle = true; - /**@type {ZMoveOptions} */ - let zMoves = []; + const zMoves: ZMoveOptions = []; for (const moveSlot of pokemon.moveSlots) { if (moveSlot.pp <= 0) { zMoves.push(null); @@ -217,10 +230,10 @@ let BattleScripts = { if (!moveSlot.disabled) { mustStruggle = false; } - let move = this.dex.getMove(moveSlot.move); + const move = this.dex.getMove(moveSlot.move); let zMoveName = this.getZMove(move, pokemon, true) || ''; if (zMoveName) { - let zMove = this.dex.getMove(zMoveName); + const zMove = this.dex.getMove(zMoveName); if (!zMove.isZ && zMove.category === 'Status') zMoveName = "Z-" + zMoveName; zMoves.push({move: zMoveName, target: zMove.target}); } else { @@ -253,10 +266,9 @@ let BattleScripts = { move.self = {sideCondition: 'boostreplacement'}; break; case 'clearnegativeboost': - /** @type {{[k: string]: number}} */ - let boosts = {}; - for (let i in pokemon.boosts) { - // @ts-ignore + const boosts: SparseBoostsTable = {}; + let i: BoostName; + for (i in pokemon.boosts) { if (pokemon.boosts[i] < 0) { boosts[i] = 0; } @@ -335,7 +347,10 @@ let BattleScripts = { if (isCrit && !suppressMessages) this.add('-crit', target); - if (pokemon.status === 'brn' && move.category === 'Physical' && !pokemon.hasAbility('guts') && !pokemon.hasAbility('superguarda') && !pokemon.hasAbility('radioactive')) { + if ( + pokemon.status === 'brn' && move.category === 'Physical' && !pokemon.hasAbility('guts') && + !pokemon.hasAbility('superguarda') && !pokemon.hasAbility('radioactive') + ) { if (this.gen < 6 || move.id !== 'facade') { baseDamage = this.modify(baseDamage, 0.5); } @@ -367,11 +382,11 @@ let BattleScripts = { let sporeEffect = false; for (const foeActive of this.side.foe.active) { // foeActive can be null when a pokemon isn't active - if (foeActive && foeActive.ability.includes('neutralizingspores') && !foeActive.volatiles['gastroacid']) sporeEffect = true; + if (foeActive?.ability.includes('neutralizingspores') && !foeActive?.volatiles['gastroacid']) sporeEffect = true; } for (const allyActive of this.side.active) { // allyActive can be null when a pokemon isn't active - if (allyActive && allyActive.ability.includes('neutralizingspores') && !allyActive.volatiles['gastroacid']) sporeEffect = true; + if (allyActive?.ability.includes('neutralizingspores') && !allyActive?.volatiles['gastroacid']) sporeEffect = true; } return !!((this.battle.gen >= 5 && !this.isActive) || (this.volatiles['gastroacid'] && !abilities.includes(this.ability)) || @@ -413,9 +428,9 @@ let BattleScripts = { if (!source) source = this; if (this.status === status.id) { - if (sourceEffect && sourceEffect.status === this.status) { + if (sourceEffect?.status === this.status) { this.battle.add('-fail', this, this.status); - } else if (sourceEffect && sourceEffect.status) { + } else if (sourceEffect?.status) { this.battle.add('-fail', source); this.battle.attrLastMove('[still]'); } @@ -423,20 +438,19 @@ let BattleScripts = { } if (!ignoreImmunities && status.id && - !(source && source.hasAbility('corrosion') && ['tox', 'psn'].includes(status.id)) && - !(sourceEffect && sourceEffect.id === 'corrosivetoxic')) { + !(source?.hasAbility('corrosion') && ['tox', 'psn'].includes(status.id)) && + !(sourceEffect?.id === 'corrosivetoxic')) { // the game currently never ignores immunities if (!this.runStatusImmunity(status.id === 'tox' ? 'psn' : status.id)) { this.battle.debug('immune to status'); - if (sourceEffect && sourceEffect.status) this.battle.add('-immune', this); + if (sourceEffect?.status) this.battle.add('-immune', this); return false; } } const prevStatus = this.status; const prevStatusData = this.statusData; if (status.id) { - /** @type {boolean} */ - const result = this.battle.runEvent('SetStatus', this, source, sourceEffect, status); + const result: boolean = this.battle.runEvent('SetStatus', this, source, sourceEffect, status); if (!result) { this.battle.debug('set status [' + status.id + '] interrupted'); return result; @@ -465,5 +479,3 @@ let BattleScripts = { }, }, }; - -exports.BattleScripts = BattleScripts; diff --git a/data/mods/ssb/statuses.js b/data/mods/ssb/statuses.ts similarity index 93% rename from data/mods/ssb/statuses.js rename to data/mods/ssb/statuses.ts index c1bccd2df8..9c030a3038 100644 --- a/data/mods/ssb/statuses.js +++ b/data/mods/ssb/statuses.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedPureEffectData}} */ -let BattleStatuses = { +export const BattleStatuses: {[k: string]: ModdedPureEffectData} = { /* // Example: userid: { @@ -184,7 +181,7 @@ let BattleStatuses = { if (move.id === 'knockoff') { move.onAfterHit = function (target, source) { if (source.hp) { - let item = target.takeItem(); + const item = target.takeItem(); if (item) { this.add('-enditem', target, item.name, '[from] move: Knock Off', '[of] ' + source); this.add(`c|%Arcticblast|+20 ;)`); @@ -194,9 +191,9 @@ let BattleStatuses = { } }, onFaint(pokemon) { - let activeMon = pokemon.side.foe.active[0].species.id; + const activeMon = pokemon.side.foe.active[0].species.id; if (activeMon === 'greninja') { - this.add(`c|%Arcticblast|FRIENDS DON’T LET FRIENDS PLAY FROGS`); + this.add(`c|%Arcticblast|FRIENDS DON’T const FRIENDS PLAY FROGS`); } else if (activeMon === 'pumpkaboosuper') { this.add(`c|%Arcticblast|WHY IS MY KAREN PRIZED`); } else { @@ -205,7 +202,7 @@ let BattleStatuses = { }, onSourceFaint(target) { if (target.species.id === 'greninja') { - this.add(`c|%Arcticblast|FRIENDS DON’T LET FRIENDS PLAY FROGS`); + this.add(`c|%Arcticblast|FRIENDS DON’T const FRIENDS PLAY FROGS`); } }, }, @@ -509,7 +506,7 @@ let BattleStatuses = { }, onDamagePriority: 1, onDamage(damage, target, source, effect) { - if (effect && effect.effectType === 'Move' && ['mimikyu', 'mimikyutotem'].includes(target.species.id) && !target.transformed) { + if (effect?.effectType === 'Move' && ['mimikyu', 'mimikyutotem'].includes(target.species.id) && !target.transformed) { this.add('-activate', target, 'ability: Disguise'); this.effectData.busted = true; return 0; @@ -517,13 +514,18 @@ let BattleStatuses = { }, onEffectiveness(typeMod, target, type, move) { if (!target) return; - if (!['mimikyu', 'mimikyutotem'].includes(target.species.id) || target.transformed || (target.volatiles['substitute'] && !(move.flags['authentic'] || move.infiltrates))) return; + if ( + !['mimikyu', 'mimikyutotem'].includes(target.species.id) || target.transformed || + (target.volatiles['substitute'] && !(move.flags['authentic'] || move.infiltrates)) + ) { + return; + } if (!target.runImmunity(move.type)) return; return 0; }, onUpdate(pokemon) { if (['mimikyu', 'mimikyutotem'].includes(pokemon.species.id) && this.effectData.busted) { - let speciesid = pokemon.species.id === 'mimikyutotem' ? 'Mimikyu-Busted-Totem' : 'Mimikyu-Busted'; + const speciesid = pokemon.species.id === 'mimikyutotem' ? 'Mimikyu-Busted-Totem' : 'Mimikyu-Busted'; pokemon.formeChange(speciesid, this.effect, true); } }, @@ -574,9 +576,13 @@ let BattleStatuses = { noCopy: true, onStart(target, source) { this.add('-start', source, 'typechange', `Fairy/Steel`); - let activeMon = toID(source.side.foe.active[0].illusion ? source.side.foe.active[0].illusion.name : source.side.foe.active[0].name); - let family = ['aethernum', 'ceteris', 'flare', 'ransei', 'trickster', 'gimm1ck', 'zalm', 'aelita', 'biggie', 'deetah', 'birdy', 'sundar', 'celestial']; - if (activeMon === 'hoeenhero' || activeMon === 'salamander') { + const activeMon = toID( + source.side.foe.active[0].illusion ? source.side.foe.active[0].illusion.name : source.side.foe.active[0].name + ); + const family = [ + 'aethernum', 'ceteris', 'flare', 'ransei', 'trickster', 'gimm1ck', 'zalm', 'aelita', 'biggie', 'deetah', 'birdy', 'sundar', 'celestial', + ]; + if (activeMon === 'hoeenhero' || activeMon === 'instruct') { this.add(`c|%fart|what song should I sing?`); } else if (activeMon === 'lifeisdank' || activeMon === 'nui' || activeMon === 'grimauxiliatrix') { this.add(`c|%fart|the gang's all here!`); @@ -590,7 +596,9 @@ let BattleStatuses = { this.add(`c|%fart|this boy is not correct. he is **flawed.**`); }, onFaint(pokemon) { - let activeMon = toID(pokemon.side.foe.active[0].illusion ? pokemon.side.foe.active[0].illusion.name : pokemon.side.foe.active[0].name); + const activeMon = toID( + pokemon.side.foe.active[0].illusion ? pokemon.side.foe.active[0].illusion.name : pokemon.side.foe.active[0].name + ); if (activeMon === 'felucia') { this.add(`c|%fart|Felucia I'm deleting your mon`); } else { @@ -784,7 +792,12 @@ let BattleStatuses = { if (pokemon.hp > pokemon.maxhp / 2) return; this.add('-activate', pokemon, 'ability: Kaiju Rage'); pokemon.formeChange('Gliscor', this.effect, true); - let newHP = Math.floor(Math.floor(2 * pokemon.species.baseStats['hp'] + pokemon.set.ivs['hp'] + Math.floor(pokemon.set.evs['hp'] / 4) + 100) * pokemon.level / 100 + 10); + const newHP = Math.floor( + Math.floor( + 2 * pokemon.species.baseStats['hp'] + pokemon.set.ivs['hp'] + Math.floor(pokemon.set.evs['hp'] / 4) + 100 + ) * pokemon.level / 100 + + 10 + ); pokemon.hp = newHP - (pokemon.maxhp - pokemon.hp); pokemon.maxhp = newHP; pokemon.heal(pokemon.baseMaxhp / 4); @@ -838,11 +851,11 @@ let BattleStatuses = { }, // Simple Innate onBoost(boost, target, source, effect) { - if (target && target.illusion) return; - if (effect && effect.id === 'zpower') return; - for (let i in boost) { - // @ts-ignore - boost[i] *= 2; + if (target?.illusion) return; + if (effect?.id === 'zpower') return; + let i: BoostName; + for (i in boost) { + boost[i]! *= 2; } }, }, @@ -899,7 +912,9 @@ let BattleStatuses = { for (const moveSlot of target.moveSlots) { const move = this.dex.getMove(moveSlot.move); const moveType = move.id === 'hiddenpower' ? target.hpType : move.type; - if (move.category !== 'Status' && (this.dex.getImmunity(moveType, pokemon) && this.dex.getEffectiveness(moveType, pokemon) > 0 || move.ohko)) { + if (move.category !== 'Status' && ( + this.dex.getImmunity(moveType, pokemon) && this.dex.getEffectiveness(moveType, pokemon) > 0 || move.ohko + )) { this.add('-ability', pokemon, 'Anticipation'); return; } @@ -940,7 +955,7 @@ let BattleStatuses = { }, // Mountaineer innate onDamage(damage, target, source, effect) { - if (effect && effect.id === 'stealthrock') { + if (effect?.id === 'stealthrock') { return false; } }, @@ -1077,7 +1092,7 @@ let BattleStatuses = { }, onFaint() { // Morfent returns something so im doing it this way - let endquote = this.random(3); + const endquote = this.random(3); if (endquote === 1) { this.add(`c|@Morfent ( _̀> ̀)|Hacking claims the lives of over 2,000 registered laddering alts every day.`); } else if (endquote === 2) { @@ -1194,7 +1209,9 @@ let BattleStatuses = { onStart(source) { this.add(`c|~pre|let's go, in and out, 20 minute adventure`); // Easter Egg - let activeMon = toID(source.side.foe.active[0].illusion ? source.side.foe.active[0].illusion.name : source.side.foe.active[0].name); + const activeMon = toID( + source.side.foe.active[0].illusion ? source.side.foe.active[0].illusion.name : source.side.foe.active[0].name + ); if (activeMon === 'anubis') { this.add(`c|+Anubis|ohey it's pre`); this.add(`c|+Anubis|!showimage https://pokemonshowdown.com/images/ssbkitten.jpg`); @@ -1318,16 +1335,16 @@ let BattleStatuses = { this.add(`c|+SamJo|Oof, gotta get thiccer……. ;(`); }, }, - salamander: { + instruct: { noCopy: true, onStart() { - this.add(`c|+Salamander|I am here to bully Aroma Electra`); + this.add(`c|+Instruct|I am here to bully Aroma Electra`); }, onSwitchOut() { - this.add(`c|+Salamander|Remember these two things: Lucario•1582 will always be cute, and Swagn will never be able to dab`); + this.add(`c|+Instruct|Remember these two things: Lucario•1582 will always be cute, and Swagn will never be able to dab`); }, onFaint() { - this.add(`c|+Salamander|Noctrine help this isn't working as well as we planned`); + this.add(`c|+Instruct|Noctrine help this isn't working as well as we planned`); }, }, schiavetto: { @@ -1465,16 +1482,22 @@ let BattleStatuses = { onStart(source) { this.add(`c|+Teremiare|<('o'<)`); if (source.illusion) return; - let target = source.side.foe.active[0]; + const target = source.side.foe.active[0]; - let removeAll = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; - let silentRemove = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist']; + const removeAll = [ + 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', + ]; + const silentRemove = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist']; for (const sideCondition of removeAll) { if (target.side.removeSideCondition(sideCondition)) { - if (!(silentRemove.includes(sideCondition))) this.add('-sideend', target.side, this.dex.getEffect(sideCondition).name, '[from] move: No Fun Zone', '[of] ' + source); + if (!(silentRemove.includes(sideCondition))) { + this.add('-sideend', target.side, this.dex.getEffect(sideCondition).name, '[from] move: No Fun Zone', '[of] ' + source); + } } if (source.side.removeSideCondition(sideCondition)) { - if (!(silentRemove.includes(sideCondition))) this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] move: No Fun Zone', '[of] ' + source); + if (!(silentRemove.includes(sideCondition))) { + this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] move: No Fun Zone', '[of] ' + source); + } } } this.add('-clearallboost'); @@ -1607,7 +1630,7 @@ let BattleStatuses = { this.add(`c|@xJoelituh|h-hi, im joel, not joe, tyvm`); // Terrifying Demeanor Innate if (source.illusion) return; - let target = source.side.foe.active[0]; + const target = source.side.foe.active[0]; if (target.getStat('spe', true, true) > source.getStat('spe', true, true)) this.boost({spe: -1}, target, source); }, onSwitchOut() { @@ -1710,8 +1733,7 @@ let BattleStatuses = { inherit: true, onType(types, pokemon) { if (pokemon.transformed) return types; - /** @type {string | undefined} */ - let type = 'Normal'; + let type: string | undefined = 'Normal'; if (pokemon.ability === 'multitype' || pokemon.ability === 'logia') { type = pokemon.getItem().onPlate; if (!type) { @@ -1755,7 +1777,7 @@ let BattleStatuses = { } }, onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { + if (effect?.effectType === 'Ability') { if (this.gen <= 5) this.effectData.duration = 0; this.add('-weather', 'AcidRain', '[from] ability: ' + effect, '[of] ' + source); } else { @@ -1833,7 +1855,7 @@ let BattleStatuses = { hail: { inherit: true, onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { + if (effect?.effectType === 'Ability') { if (this.gen <= 5 || effect.id === 'snowstorm') this.effectData.duration = 0; this.add('-weather', 'Hail', '[from] ability: ' + effect, '[of] ' + source); } else { @@ -1846,11 +1868,11 @@ let BattleStatuses = { duration: 1, onSwitchInPriority: 1000, onSwitchIn(target) { - for (let boost in target.boosts) { - // @ts-ignore Element implictly has type any due to lack of index signature + let boost: BoostName; + for (boost in target.boosts) { if (target.boosts[boost]) this.add('-boost', target, boost, target.boosts[boost], '[silent]'); } - for (let v in target.volatiles) { + for (const v in target.volatiles) { if (v !== toID(target.name) && v !== 'batonpasshelper') this.add('-start', target, target.volatiles[v].id); } }, @@ -1859,7 +1881,7 @@ let BattleStatuses = { auroraveil: { duration: 5, durationCallback(target, source, effect) { - if (source && source.hasItem('lightclay')) { + if (source?.hasItem('lightclay')) { return 8; } return 5; @@ -1893,7 +1915,7 @@ let BattleStatuses = { lightscreen: { duration: 5, durationCallback(target, source, effect) { - if (source && source.hasItem('lightclay')) { + if (source?.hasItem('lightclay')) { return 8; } return 5; @@ -1926,15 +1948,14 @@ let BattleStatuses = { if (effect.effectType === 'Move' && effect.infiltrates && target.side !== source.side) return; if (source && target !== source) { let showMsg = false; - for (let i in boost) { - // @ts-ignore - if (boost[i] < 0) { - // @ts-ignore + let i: BoostName; + for (i in boost) { + if (boost[i]! < 0) { delete boost[i]; showMsg = true; } } - if (showMsg && !(/** @type {ActiveMove} */(effect)).secondaries) { + if (showMsg && !(effect as ActiveMove).secondaries) { this.add('-activate', target, 'move: Mist'); } } @@ -1955,7 +1976,7 @@ let BattleStatuses = { reflect: { duration: 5, durationCallback(target, source, effect) { - if (source && source.hasItem('lightclay')) { + if (source?.hasItem('lightclay')) { return 8; } return 5; @@ -1984,7 +2005,7 @@ let BattleStatuses = { safeguard: { duration: 5, durationCallback(target, source, effect) { - if (source && source.hasAbility('persistent')) { + if (source?.hasAbility('persistent')) { this.add('-activate', source, 'ability: Persistent', effect); return 7; } @@ -2038,7 +2059,7 @@ let BattleStatuses = { }, onSwitchIn(pokemon) { if (!pokemon.isGrounded()) return; - let damageAmounts = [0, 3, 4, 6]; // 1/8, 1/6, 1/4 + const damageAmounts = [0, 3, 4, 6]; // 1/8, 1/6, 1/4 this.damage(damageAmounts[this.effectData.layers] * pokemon.maxhp / 24); }, }, @@ -2051,7 +2072,7 @@ let BattleStatuses = { this.add('-sidestart', side, 'move: Stealth Rock'); }, onSwitchIn(pokemon) { - let typeMod = this.dex.clampIntRange(pokemon.runEffectiveness(this.dex.getActiveMove('stealthrock')), -6, 6); + const typeMod = this.dex.clampIntRange(pokemon.runEffectiveness(this.dex.getActiveMove('stealthrock')), -6, 6); this.damage(pokemon.maxhp * Math.pow(2, typeMod) / 8); }, }, @@ -2098,5 +2119,3 @@ let BattleStatuses = { }, }, }; - -exports.BattleStatuses = BattleStatuses; diff --git a/data/mods/stadium/moves.js b/data/mods/stadium/moves.ts similarity index 90% rename from data/mods/stadium/moves.js rename to data/mods/stadium/moves.ts index 7ae941288a..5de79aac41 100644 --- a/data/mods/stadium/moves.js +++ b/data/mods/stadium/moves.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedMoveData}} */ -let BattleMovedex = { +export const BattleMovedex: {[k: string]: ModdedMoveData} = { bind: { inherit: true, // FIXME: onBeforeMove() {}, @@ -41,13 +38,13 @@ let BattleMovedex = { }, onAfterMoveSelfPriority: 1, onAfterMoveSelf(pokemon) { - let leecher = pokemon.side.foe.active[pokemon.volatiles['leechseed'].sourcePosition]; + const leecher = pokemon.side.foe.active[pokemon.volatiles['leechseed'].sourcePosition]; if (!leecher || leecher.fainted || leecher.hp <= 0) { this.debug('Nothing to leech into'); return; } - let toLeech = this.dex.clampIntRange(Math.floor(pokemon.maxhp / 16), 1); - let damage = this.damage(toLeech, pokemon, leecher); + const toLeech = this.dex.clampIntRange(Math.floor(pokemon.maxhp / 16), 1); + const damage = this.damage(toLeech, pokemon, leecher); if (damage) this.heal(damage, leecher, pokemon); }, }, @@ -128,7 +125,7 @@ let BattleMovedex = { return null; } if (move.category === 'Status') { - let SubBlocked = ['leechseed', 'lockon', 'mindreader', 'nightmare']; + const SubBlocked = ['leechseed', 'lockon', 'mindreader', 'nightmare']; if (move.status || move.boosts || move.volatileStatus === 'confusion' || SubBlocked.includes(move.id)) { this.add('-activate', target, 'Substitute', '[block] ' + move.name); return null; @@ -157,7 +154,7 @@ let BattleMovedex = { } this.runEvent('AfterSubDamage', target, source, move, damage); // Add here counter damage - let lastAttackedBy = target.getLastAttackedBy(); + const lastAttackedBy = target.getLastAttackedBy(); if (!lastAttackedBy) { target.attackedBy.push({source: source, move: move.id, damage: damage, thisTurn: true}); } else { @@ -179,5 +176,3 @@ let BattleMovedex = { // FIXME: onBeforeMove() {}, }, }; - -exports.BattleMovedex = BattleMovedex; diff --git a/data/mods/stadium/rulesets.js b/data/mods/stadium/rulesets.ts similarity index 64% rename from data/mods/stadium/rulesets.js rename to data/mods/stadium/rulesets.ts index 5cd4264e83..86d4651605 100644 --- a/data/mods/stadium/rulesets.js +++ b/data/mods/stadium/rulesets.ts @@ -1,12 +1,7 @@ -'use strict'; - -/**@type {{[k: string]: ModdedFormatsData}} */ -let BattleFormats = { +export const BattleFormats: {[k: string]: ModdedFormatsData} = { standard: { effectType: 'ValidatorRule', name: 'Standard', ruleset: ['Obtainable', 'Sleep Clause Mod', 'Freeze Clause Mod', 'Species Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Exact HP Mod', 'Cancel Mod'], }, }; - -exports.BattleFormats = BattleFormats; diff --git a/data/mods/stadium/scripts.js b/data/mods/stadium/scripts.ts similarity index 88% rename from data/mods/stadium/scripts.js rename to data/mods/stadium/scripts.ts index d327bbec54..1ff5fffeaf 100644 --- a/data/mods/stadium/scripts.js +++ b/data/mods/stadium/scripts.ts @@ -1,10 +1,7 @@ -'use strict'; - /** * Stadium mechanics inherit from gen 1 mechanics, but fixes some stuff. */ -/**@type {ModdedBattleScriptsData} */ -let BattleScripts = { +export const BattleScripts: ModdedBattleScriptsData = { inherit: 'gen1', gen: 1, // BattlePokemon scripts. Stadium shares gen 1 code but it fixes some problems with it. @@ -14,35 +11,28 @@ let BattleScripts = { // Modified stats are declared in the Pokemon object in sim/pokemon.js in about line 681. modifyStat(statName, modifier) { if (!(statName in this.storedStats)) throw new Error("Invalid `statName` passed to `modifyStat`"); - // @ts-ignore - this.modifiedStats[statName] = this.battle.dex.clampIntRange(Math.floor(this.modifiedStats[statName] * modifier), 1); + this.modifiedStats![statName] = this.battle.dex.clampIntRange(Math.floor(this.modifiedStats![statName] * modifier), 1); }, // This is run on Stadium after boosts and status changes. recalculateStats() { - for (let statName in this.storedStats) { - /**@type {number} */ - // @ts-ignore + let statName: StatNameExceptHP; + for (statName in this.storedStats) { let stat = this.species.baseStats[statName]; - // @ts-ignore - stat = Math.floor(Math.floor(2 * stat + this.set.ivs[statName] + Math.floor(this.set.evs[statName] / 4)) * this.level / 100 + 5); - // @ts-ignore + stat = Math.floor( + Math.floor( + 2 * stat + this.set.ivs[statName] + Math.floor(this.set.evs[statName] / 4) + ) * this.level / 100 + 5 + ); this.baseStoredStats[statName] = this.storedStats[statName] = Math.floor(stat); - // @ts-ignore - this.modifiedStats[statName] = Math.floor(stat); + this.modifiedStats![statName] = Math.floor(stat); // Re-apply drops, if necessary. - // @ts-ignore - if (this.status === 'par') this.modifyStat('spe', 0.25); - // @ts-ignore - if (this.status === 'brn') this.modifyStat('atk', 0.5); - // @ts-ignore + if (this.status === 'par') this.modifyStat!('spe', 0.25); + if (this.status === 'brn') this.modifyStat!('atk', 0.5); if (this.boosts[statName] !== 0) { - // @ts-ignore if (this.boosts[statName] >= 0) { - // @ts-ignore - this.modifyStat(statName, [1, 1.5, 2, 2.5, 3, 3.5, 4][this.boosts[statName]]); + this.modifyStat!(statName, [1, 1.5, 2, 2.5, 3, 3.5, 4][this.boosts[statName]]); } else { - // @ts-ignore - this.modifyStat(statName, [100, 66, 50, 40, 33, 28, 25][-this.boosts[statName]] / 100); + this.modifyStat!(statName, [100, 66, 50, 40, 33, 28, 25][-this.boosts[statName]] / 100); } } } @@ -50,24 +40,17 @@ let BattleScripts = { // Stadium's fixed boosting function. boostBy(boost) { let changed = false; - for (let i in boost) { - // @ts-ignore + let i: BoostName; + for (i in boost) { let delta = boost[i]; if (delta === undefined) continue; - // @ts-ignore this.boosts[i] += delta; - // @ts-ignore if (this.boosts[i] > 6) { - // @ts-ignore delta -= this.boosts[i] - 6; - // @ts-ignore this.boosts[i] = 6; } - // @ts-ignore if (this.boosts[i] < -6) { - // @ts-ignore delta -= this.boosts[i] - (-6); - // @ts-ignore this.boosts[i] = -6; } if (delta) changed = true; @@ -79,9 +62,9 @@ let BattleScripts = { }, // Battle scripts. runMove(moveOrMoveName, pokemon, targetLoc, sourceEffect) { - let move = this.dex.getActiveMove(moveOrMoveName); - let target = this.getTarget(pokemon, move, targetLoc); - if (target && target.subFainted) target.subFainted = null; + const move = this.dex.getActiveMove(moveOrMoveName); + const target = this.getTarget(pokemon, move, targetLoc); + if (target?.subFainted) target.subFainted = null; this.setActiveMove(move, pokemon, target); @@ -101,7 +84,10 @@ let BattleScripts = { pokemon.lastDamage = 0; let lockedMove = this.runEvent('LockMove', pokemon); if (lockedMove === true) lockedMove = false; - if (!lockedMove && (!pokemon.volatiles['partialtrappinglock'] || pokemon.volatiles['partialtrappinglock'].locked !== target)) { + if ( + !lockedMove && + (!pokemon.volatiles['partialtrappinglock'] || pokemon.volatiles['partialtrappinglock'].locked !== target) + ) { pokemon.deductPP(move, null, target); pokemon.side.lastMove = move; pokemon.lastMove = move; @@ -136,11 +122,13 @@ let BattleScripts = { }, tryMoveHit(target, pokemon, move) { let doSelfDestruct = true; - /** @type {number | undefined | false} */ - let damage = 0; + let damage: number | false | undefined = 0; // First, check if the Pokémon is immune to this move. - if ((!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) && !target.runImmunity(move.type, true)) { + if ( + (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) && + !target.runImmunity(move.type, true) + ) { if (move.selfdestruct) { this.faint(pokemon, pokemon, move); } @@ -148,7 +136,6 @@ let BattleScripts = { } // Now, let's calculate the accuracy. - /** @type {number | true} */ let accuracy = move.accuracy; // Partial trapping moves: true accuracy while it lasts @@ -208,9 +195,8 @@ let BattleScripts = { } hits = Math.floor(hits); // In gen 1, all the hits have the same damage for multihits move - /** @type {number | undefined | false} */ - let moveDamage = 0; - let i; + let moveDamage: number | false | undefined = 0; + let i: number; for (i = 0; i < hits && target.hp && pokemon.hp; i++) { move.hit = i + 1; moveDamage = this.moveHit(target, pokemon, move); @@ -256,13 +242,11 @@ let BattleScripts = { return damage; }, moveHit(target, pokemon, moveOrMoveName, moveData, isSecondary, isSelf) { - /** @type {number | null | false | undefined} */ - let damage = 0; - let move = this.dex.getActiveMove(moveOrMoveName); + let damage: number | false | null | undefined = 0; + const move = this.dex.getActiveMove(moveOrMoveName); if (!isSecondary && !isSelf) this.setActiveMove(move, pokemon, target); - /** @type {number | boolean} */ - let hitResult = true; + let hitResult: number | boolean = true; if (!moveData) moveData = move; if (move.ignoreImmunity === undefined) { @@ -329,7 +313,7 @@ let BattleScripts = { this.boost(moveData.boosts, target, pokemon, move); } if (moveData.heal && !target.fainted) { - let d = target.heal(Math.floor(target.maxhp * moveData.heal[0] / moveData.heal[1])); + const d = target.heal(Math.floor(target.maxhp * moveData.heal[0] / moveData.heal[1])); if (!d) { this.add('-fail', target); return false; @@ -402,7 +386,7 @@ let BattleScripts = { // That means that a move that does not share the type of the target can status it. // If a move that was not fire-type would exist on Gen 1, it could burn a Pokémon. if (!(secondary.status && ['par', 'brn', 'frz'].includes(secondary.status) && target && target.hasType(move.type))) { - let effectChance = Math.floor((secondary.chance || 100) * 255 / 100); + const effectChance = Math.floor((secondary.chance || 100) * 255 / 100); if (typeof secondary.chance === 'undefined' || this.randomChance(effectChance + 1, 256)) { this.moveHit(target, pokemon, move, secondary, true, isSelf); } @@ -420,13 +404,13 @@ let BattleScripts = { if (typeof move === 'string') { move = this.dex.getActiveMove(move); } else if (typeof move === 'number') { - move = /** @type {ActiveMove} */ ({ + move = { basePower: move, type: '???', category: 'Physical', willCrit: false, flags: {}, - }); + } as ActiveMove; } // Let's see if the target is immune to the move. @@ -471,11 +455,10 @@ let BattleScripts = { if (!move.defensiveCategory) move.defensiveCategory = move.category; // '???' is typeless damage: used for Struggle and Confusion etc if (!move.type) move.type = '???'; - let type = move.type; + const type = move.type; // We get the base power and apply basePowerCallback if necessary. - /** @type {number | false | null} */ - let basePower = move.basePower; + let basePower: number | false | null = move.basePower; if (move.basePowerCallback) { basePower = move.basePowerCallback.call(this, pokemon, target, move); } @@ -542,12 +525,10 @@ let BattleScripts = { // We now check attacker's and defender's stats. let level = pokemon.level; let attacker = pokemon; - let defender = target; + const defender = target; if (move.useTargetOffensive) attacker = target; - /** @type {StatNameExceptHP} */ - let atkType = (move.category === 'Physical') ? 'atk' : 'spa'; - /** @type {StatNameExceptHP} */ - let defType = (move.defensiveCategory === 'Physical') ? 'def' : 'spd'; + let atkType: StatNameExceptHP = (move.category === 'Physical') ? 'atk' : 'spa'; + const defType: StatNameExceptHP = (move.defensiveCategory === 'Physical') ? 'def' : 'spd'; if (move.useSourceDefensiveAsOffensive) atkType = defType; let attack = attacker.getStat(atkType); let defense = defender.getStat(defType); @@ -607,7 +588,7 @@ let BattleScripts = { // Type effectiveness. // The order here is not correct, must change to check the move versus each type. - let totalTypeMod = this.dex.getEffectiveness(type, target); + const totalTypeMod = this.dex.getEffectiveness(type, target); // Super effective attack if (totalTypeMod > 0) { if (!suppressMessages) this.add('-supereffective', target); @@ -637,12 +618,12 @@ let BattleScripts = { damage *= this.random(217, 256); damage = Math.floor(damage / 255); if (damage > target.hp && !target.volatiles['substitute']) damage = target.hp; - if (target.volatiles['substitute'] && damage > target.volatiles['substitute'].hp) damage = target.volatiles['substitute'].hp; + if (target.volatiles['substitute'] && damage > target.volatiles['substitute'].hp) { + damage = target.volatiles['substitute'].hp; + } } // We are done, this is the final damage. return Math.floor(damage); }, }; - -exports.BattleScripts = BattleScripts; diff --git a/data/mods/stadium/statuses.js b/data/mods/stadium/statuses.ts similarity index 96% rename from data/mods/stadium/statuses.js rename to data/mods/stadium/statuses.ts index 08c0616979..d8aeb26824 100644 --- a/data/mods/stadium/statuses.js +++ b/data/mods/stadium/statuses.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedPureEffectData}} */ -let BattleStatuses = { +export const BattleStatuses: {[k: string]: ModdedPureEffectData} = { brn: { name: 'brn', id: 'brn', @@ -146,5 +143,3 @@ let BattleStatuses = { }, }, }; - -exports.BattleStatuses = BattleStatuses; diff --git a/data/mods/vgc17/formats-data.js b/data/mods/vgc17/formats-data.ts similarity index 92% rename from data/mods/vgc17/formats-data.js rename to data/mods/vgc17/formats-data.ts index 16645485d3..f2c3fbc7d1 100644 --- a/data/mods/vgc17/formats-data.js +++ b/data/mods/vgc17/formats-data.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedSpeciesFormatsData}} */ -let BattleFormatsData = { +export const BattleFormatsData: {[k: string]: ModdedSpeciesFormatsData} = { pikachupartner: { inherit: true, isNonstandard: "Unobtainable", @@ -112,5 +109,3 @@ let BattleFormatsData = { unreleasedHidden: true, }, }; - -exports.BattleFormatsData = BattleFormatsData; diff --git a/data/mods/vgc17/items.js b/data/mods/vgc17/items.ts similarity index 78% rename from data/mods/vgc17/items.js rename to data/mods/vgc17/items.ts index a0e220f3cd..5eed4ad5fc 100644 --- a/data/mods/vgc17/items.js +++ b/data/mods/vgc17/items.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: ModdedItemData}} */ -let BattleItems = { +export const BattleItems: {[k: string]: ModdedItemData} = { kommoniumz: { inherit: true, isNonstandard: "Unobtainable", @@ -27,6 +24,3 @@ let BattleItems = { isNonstandard: "Unobtainable", }, }; - -exports.BattleItems = BattleItems; - diff --git a/data/mods/vgc17/learnsets.js b/data/mods/vgc17/learnsets.ts similarity index 99% rename from data/mods/vgc17/learnsets.js rename to data/mods/vgc17/learnsets.ts index bfeaee290e..d79d8ef4af 100644 --- a/data/mods/vgc17/learnsets.js +++ b/data/mods/vgc17/learnsets.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: {inherit: true, learnset: {[k: string]: MoveSource[]}}}} */ -let BattleLearnsets = { +export const BattleLearnsets: {[k: string]: ModdedLearnsetData} = { missingno: {inherit: true, learnset: { blizzard: ["5L1"], bubblebeam: ["5L1"], @@ -60475,5 +60472,3 @@ let BattleLearnsets = { workup: ["7M"], }}, }; - -exports.BattleLearnsets = BattleLearnsets; diff --git a/data/mods/vgc17/rulesets.js b/data/mods/vgc17/rulesets.ts similarity index 92% rename from data/mods/vgc17/rulesets.js rename to data/mods/vgc17/rulesets.ts index e97b76e049..d965e42432 100644 --- a/data/mods/vgc17/rulesets.js +++ b/data/mods/vgc17/rulesets.ts @@ -1,21 +1,18 @@ -'use strict'; - -/**@type {{[k: string]: ModdedFormatsData}} */ -let BattleFormats = { +export const BattleFormats: {[k: string]: ModdedFormatsData} = { alolapokedex: { effectType: 'ValidatorRule', name: 'Alola Pokedex', desc: "Only allows Pokémon native to the Alola region (SUMO)", - onValidateSet(set, format) { - let alolaDex = [ + onValidateSet(set) { + const species = this.dex.getSpecies(set.species || set.name); + if (this.ruleTable.has('+' + species.id)) return; + + const alolaDex = [ "Rowlet", "Dartrix", "Decidueye", "Litten", "Torracat", "Incineroar", "Popplio", "Brionne", "Primarina", "Pikipek", "Trumbeak", "Toucannon", "Yungoos", "Gumshoos", "Rattata-Alola", "Raticate-Alola", "Caterpie", "Metapod", "Butterfree", "Ledyba", "Ledian", "Spinarak", "Ariados", "Pichu", "Pikachu", "Raichu-Alola", "Grubbin", "Charjabug", "Vikavolt", "Bonsly", "Sudowoodo", "Happiny", "Chansey", "Blissey", "Munchlax", "Snorlax", "Slowpoke", "Slowbro", "Slowking", "Wingull", "Pelipper", "Abra", "Kadabra", "Alakazam", "Meowth-Alola", "Persian-Alola", "Magnemite", "Magneton", "Magnezone", "Grimer-Alola", "Muk-Alola", "Growlithe", "Arcanine", "Drowzee", "Hypno", "Makuhita", "Hariyama", "Smeargle", "Crabrawler", "Crabominable", "Gastly", "Haunter", "Gengar", "Drifloon", "Drifblim", "Misdreavus", "Mismagius", "Zubat", "Golbat", "Crobat", "Diglett-Alola", "Dugtrio-Alola", "Spearow", "Fearow", "Rufflet", "Braviary", "Vullaby", "Mandibuzz", "Mankey", "Primeape", "Delibird", "Oricorio", "Cutiefly", "Ribombee", "Petilil", "Lilligant", "Cottonee", "Whimsicott", "Psyduck", "Golduck", "Magikarp", "Gyarados", "Barboach", "Whiscash", "Machop", "Machoke", "Machamp", "Roggenrola", "Boldore", "Gigalith", "Carbink", "Sableye", "Rockruff", "Lycanroc", "Spinda", "Tentacool", "Tentacruel", "Finneon", "Lumineon", "Wishiwashi", "Luvdisc", "Corsola", "Mareanie", "Toxapex", "Shellder", "Cloyster", "Bagon", "Shelgon", "Salamence", "Lillipup", "Herdier", "Stoutland", "Eevee", "Vaporeon", "Jolteon", "Flareon", "Espeon", "Umbreon", "Leafeon", "Glaceon", "Sylveon", "Mudbray", "Mudsdale", "Igglybuff", "Jigglypuff", "Wigglytuff", "Tauros", "Miltank", "Surskit", "Masquerain", "Dewpider", "Araquanid", "Fomantis", "Lurantis", "Morelull", "Shiinotic", "Paras", "Parasect", "Poliwag", "Poliwhirl", "Poliwrath", "Politoed", "Goldeen", "Seaking", "Feebas", "Milotic", "Alomomola", "Fletchling", "Fletchinder", "Talonflame", "Salandit", "Salazzle", "Cubone", "Marowak-Alola", "Kangaskhan", "Magby", "Magmar", "Magmortar", "Stufful", "Bewear", "Bounsweet", "Steenee", "Tsareena", "Comfey", "Pinsir", "Oranguru", "Passimian", "Goomy", "Sliggoo", "Goodra", "Castform", "Wimpod", "Golisopod", "Staryu", "Starmie", "Sandygast", "Palossand", "Cranidos", "Rampardos", "Shieldon", "Bastiodon", "Archen", "Archeops", "Tirtouga", "Carracosta", "Phantump", "Trevenant", "Nosepass", "Probopass", "Pyukumuku", "Chinchou", "Lanturn", "Type: Null", "Silvally", "Zygarde", "Trubbish", "Garbodor", "Skarmory", "Ditto", "Cleffa", "Clefairy", "Clefable", "Minior", "Beldum", "Metang", "Metagross", "Porygon", "Porygon2", "Porygon-Z", "Pancham", "Pangoro", "Komala", "Torkoal", "Turtonator", "Togedemaru", "Elekid", "Electabuzz", "Electivire", "Geodude-Alola", "Graveler-Alola", "Golem-Alola", "Sandile", "Krokorok", "Krookodile", "Trapinch", "Vibrava", "Flygon", "Gible", "Gabite", "Garchomp", "Klefki", "Mimikyu", "Bruxish", "Drampa", "Absol", "Snorunt", "Glalie", "Froslass", "Sneasel", "Weavile", "Sandshrew-Alola", "Sandslash-Alola", "Vulpix-Alola", "Ninetales-Alola", "Vanillite", "Vanillish", "Vanilluxe", "Snubbull", "Granbull", "Shellos", "Gastrodon", "Relicanth", "Dhelmise", "Carvanha", "Sharpedo", "Wailmer", "Wailord", "Lapras", "Exeggcute", "Exeggutor-Alola", "Jangmo-o", "Hakamo-o", "Kommo-o", "Emolga", "Scyther", "Scizor", "Murkrow", "Honchkrow", "Riolu", "Lucario", "Dratini", "Dragonair", "Dragonite", "Aerodactyl", "Tapu Koko", "Tapu Lele", "Tapu Bulu", "Tapu Fini", "Cosmog", "Cosmoem", "Solgaleo", "Lunala", "Nihilego", "Buzzwole", "Pheromosa", "Xurkitree", "Celesteela", "Kartana", "Guzzlord", "Necrozma", "Magearna", "Marshadow", ]; - let species = this.dex.getSpecies(set.species || set.name); - if (!alolaDex.includes(species.baseSpecies) && !alolaDex.includes(species.name) && !this.ruleTable.has('+' + species.id)) { + if (!alolaDex.includes(species.baseSpecies) && !alolaDex.includes(species.name)) { return [species.baseSpecies + " is not in the Alola Pokédex."]; } }, }, }; - -exports.BattleFormats = BattleFormats; diff --git a/data/mods/vgc17/scripts.js b/data/mods/vgc17/scripts.js deleted file mode 100644 index a7a05c85a2..0000000000 --- a/data/mods/vgc17/scripts.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -/** @type {ModdedBattleScriptsData} */ -let BattleScripts = { - inherit: 'gen7', -}; - -exports.BattleScripts = BattleScripts; diff --git a/data/mods/vgc17/scripts.ts b/data/mods/vgc17/scripts.ts new file mode 100644 index 0000000000..927e340b27 --- /dev/null +++ b/data/mods/vgc17/scripts.ts @@ -0,0 +1,3 @@ +export const BattleScripts: ModdedBattleScriptsData = { + inherit: 'gen7', +}; diff --git a/data/moves.js b/data/moves.ts similarity index 97% rename from data/moves.js rename to data/moves.ts index 52b3302a77..b0cc0145a8 100644 --- a/data/moves.js +++ b/data/moves.ts @@ -26,10 +26,7 @@ sound: Has no effect on Pokemon with the Soundproof Ability. */ -'use strict'; - -/**@type {{[k: string]: MoveData}} */ -let BattleMovedex = { +export const BattleMovedex: {[moveid: string]: MoveData} = { "10000000voltthunderbolt": { num: 719, accuracy: true, @@ -207,17 +204,16 @@ let BattleMovedex = { priority: 0, flags: {}, onHit(target) { - let stats = []; - for (let stat in target.boosts) { - // @ts-ignore + const stats: BoostName[] = []; + let stat: BoostName; + for (stat in target.boosts) { if (target.boosts[stat] < 6) { stats.push(stat); } } if (stats.length) { - let randomStat = this.sample(stats); - /**@type {{[k: string]: number}} */ - let boost = {}; + const randomStat = this.sample(stats); + const boost: SparseBoostsTable = {}; boost[randomStat] = 2; this.boost(boost); } else { @@ -281,7 +277,7 @@ let BattleMovedex = { flags: {authentic: 1, mystery: 1}, onHit(target, source, move) { if (target.side.active.length < 2) return false; // fails in singles - let action = this.queue.willMove(target); + const action = this.queue.willMove(target); if (action) { this.queue.prioritizeAction(action, move); this.add('-activate', target, 'move: After You'); @@ -391,7 +387,7 @@ let BattleMovedex = { if (source.side.active.length === 3 && source.position === 1) return false; }, onHit(pokemon) { - let newPosition = (pokemon.position === 0 ? pokemon.side.active.length - 1 : 0); + const newPosition = (pokemon.position === 0 ? pokemon.side.active.length - 1 : 0); if (!pokemon.side.active[newPosition]) return false; if (pokemon.side.active[newPosition].fainted) return false; this.swapPosition(pokemon, newPosition, '[from] move: Ally Switch'); @@ -641,15 +637,15 @@ let BattleMovedex = { priority: 0, flags: {}, onHit(target) { - let noAssist = [ + const noAssist = [ 'assist', 'banefulbunker', 'beakblast', 'belch', 'bestow', 'bounce', 'celebrate', 'chatter', 'circlethrow', 'copycat', 'counter', 'covet', 'destinybond', 'detect', 'dig', 'dive', 'dragontail', 'endure', 'feint', 'fly', 'focuspunch', 'followme', 'helpinghand', 'holdhands', 'kingsshield', 'matblock', 'mefirst', 'metronome', 'mimic', 'mirrorcoat', 'mirrormove', 'naturepower', 'phantomforce', 'protect', 'ragepowder', 'roar', 'shadowforce', 'shelltrap', 'sketch', 'skydrop', 'sleeptalk', 'snatch', 'spikyshield', 'spotlight', 'struggle', 'switcheroo', 'thief', 'transform', 'trick', 'whirlwind', ]; - let moves = []; + const moves = []; for (const pokemon of target.side.pokemon) { if (pokemon === target) continue; for (const moveSlot of pokemon.moveSlots) { - let moveid = moveSlot.id; + const moveid = moveSlot.id; if (noAssist.includes(moveid)) continue; const move = this.dex.getMove(moveid); if (move.isZ || move.isMax) { @@ -888,7 +884,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(target, source, effect) { - if (source && source.hasItem('lightclay')) { + if (source?.hasItem('lightclay')) { return 8; } return 5; @@ -935,7 +931,7 @@ let BattleMovedex = { priority: 0, flags: {snatch: 1}, onTryHit(pokemon) { - let hasContrary = pokemon.hasAbility('contrary'); + const hasContrary = pokemon.hasAbility('contrary'); if ((!hasContrary && pokemon.boosts.spe === 6) || (hasContrary && pokemon.boosts.spe === -6)) { return false; } @@ -960,7 +956,7 @@ let BattleMovedex = { accuracy: 100, basePower: 60, basePowerCallback(pokemon, target, move) { - let damagedByTarget = pokemon.attackedBy.some( + const damagedByTarget = pokemon.attackedBy.some( p => p.source === target && p.damage > 0 && p.thisTurn ); if (damagedByTarget) { @@ -1063,7 +1059,7 @@ let BattleMovedex = { } else { this.add('-activate', target, 'move: Protect'); } - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -1189,8 +1185,7 @@ let BattleMovedex = { accuracy: 100, basePower: 0, basePowerCallback(pokemon, target, move) { - // @ts-ignore - return 5 + Math.floor(move.allies.shift().species.baseStats.atk / 10); + return 5 + Math.floor(move.allies!.shift()!.species.baseStats.atk / 10); }, category: "Physical", desc: "Hits one time for the user and one time for each unfainted Pokemon without a major status condition in the user's party. The power of each hit is equal to 5+(X/10), where X is each participating Pokemon's base Attack; each hit is considered to come from the user.", @@ -1301,7 +1296,7 @@ let BattleMovedex = { if (target.item) { return false; } - let myItem = source.takeItem(); + const myItem = source.takeItem(); if (!myItem) return false; if (!this.singleEvent('TakeItem', myItem, source.itemData, target, source, move, myItem) || !target.setItem(myItem)) { source.item = myItem.id; @@ -1363,8 +1358,8 @@ let BattleMovedex = { } target = possibleTarget; } - let moveData = { - id: 'bide', + const moveData: Partial = { + id: 'bide' as ID, name: "Bide", accuracy: true, damage: this.effectData.totalDamage * 2, @@ -1374,8 +1369,7 @@ let BattleMovedex = { effectType: 'Move', type: 'Normal', }; - // @ts-ignore - this.tryMoveHit(target, pokemon, moveData); + this.tryMoveHit(target, pokemon, moveData as ActiveMove); return false; } this.add('-activate', pokemon, 'move: Bide'); @@ -2001,7 +1995,7 @@ let BattleMovedex = { priority: 0, flags: {contact: 1, protect: 1, mirror: 1}, onHit(target, source) { - let item = target.getItem(); + const item = target.getItem(); if (source.hp && item.isBerry && target.takeItem(source)) { this.add('-enditem', target, item.name, '[from] stealeat', '[move] Bug Bite', '[of] ' + source); if (this.singleEvent('Eat', item, null, source, null, null)) { @@ -2316,7 +2310,7 @@ let BattleMovedex = { onRestart(pokemon) { this.effectData.duration = 2; }, - onBasePowerPriority: 9, + onBasePowerPriority: 3, onBasePower(basePower, attacker, defender, move) { if (move.type === 'Electric') { this.debug('charge boost'); @@ -2497,8 +2491,7 @@ let BattleMovedex = { if (pokemon.hp <= pokemon.maxhp / 3 || pokemon.maxhp === 1) { return false; } - // @ts-ignore - if (!this.boost(move.boosts)) return null; + if (!this.boost(move.boosts as SparseBoostsTable)) return null; delete move.boosts; }, onHit(pokemon) { @@ -2750,7 +2743,7 @@ let BattleMovedex = { priority: 0, flags: {snatch: 1}, onHit(target) { - let type = this.dex.getMove(target.moveSlots[0].id).type; + const type = this.dex.getMove(target.moveSlots[0].id).type; if (target.hasType(type) || !target.setType(type)) return false; this.add('-start', target, 'typechange', type); }, @@ -2777,11 +2770,11 @@ let BattleMovedex = { if (!target.lastMove) { return false; } - let possibleTypes = []; - let attackType = target.lastMove.type; - for (let type in this.dex.data.TypeChart) { + const possibleTypes = []; + const attackType = target.lastMove.type; + for (const type in this.dex.data.TypeChart) { if (source.hasType(type)) continue; - let typeCheck = this.dex.data.TypeChart[type].damageTaken[attackType]; + const typeCheck = this.dex.data.TypeChart[type].damageTaken[attackType]; if (typeCheck === 2 || typeCheck === 3) { possibleTypes.push(type); } @@ -2789,7 +2782,7 @@ let BattleMovedex = { if (!possibleTypes.length) { return false; } - let randomType = this.sample(possibleTypes); + const randomType = this.sample(possibleTypes); if (!source.setType(randomType)) return false; this.add('-start', source, 'typechange', randomType); @@ -2846,12 +2839,18 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, mirror: 1}, onHit(target) { - if (['battlebond', 'comatose', 'disguise', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'zenmode'].includes(target.ability)) return; + const noAbilityChange = [ + 'battlebond', 'comatose', 'disguise', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'zenmode', + ]; + if (noAbilityChange.includes(target.ability)) return; if (target.newlySwitched || this.queue.willMove(target)) return; target.addVolatile('gastroacid'); }, onAfterSubDamage(damage, target) { - if (['battlebond', 'comatose', 'disguise', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'zenmode'].includes(target.ability)) return; + const noAbilityChange = [ + 'battlebond', 'comatose', 'disguise', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'zenmode', + ]; + if (noAbilityChange.includes(target.ability)) return; if (target.newlySwitched || this.queue.willMove(target)) return; target.addVolatile('gastroacid'); }, @@ -3012,10 +3011,12 @@ let BattleMovedex = { 'mist', 'lightscreen', 'reflect', 'spikes', 'safeguard', 'tailwind', 'toxicspikes', 'stealthrock', 'waterpledge', 'firepledge', 'grasspledge', 'stickyweb', 'auroraveil', 'gmaxsteelsurge', 'gmaxwildfire', ]; let success = false; - for (let id of sideConditions) { + for (const id of sideConditions) { const effectName = this.dex.getEffect(id).name; if (sourceSide.sideConditions[id] && targetSide.sideConditions[id]) { - [sourceSide.sideConditions[id], targetSide.sideConditions[id]] = [targetSide.sideConditions[id], sourceSide.sideConditions[id]]; + [sourceSide.sideConditions[id], targetSide.sideConditions[id]] = [ + targetSide.sideConditions[id], sourceSide.sideConditions[id], + ]; this.add('-sideend', sourceSide, effectName, '[silent]'); this.add('-sideend', targetSide, effectName, '[silent]'); } else if (sourceSide.sideConditions[id] && !targetSide.sideConditions[id]) { @@ -3062,11 +3063,14 @@ let BattleMovedex = { if (source.item || source.volatiles['gem']) { return; } - let yourItem = target.takeItem(source); + const yourItem = target.takeItem(source); if (!yourItem) { return; } - if (!this.singleEvent('TakeItem', yourItem, target.itemData, source, target, move, yourItem) || !source.setItem(yourItem)) { + if ( + !this.singleEvent('TakeItem', yourItem, target.itemData, source, target, move, yourItem) || + !source.setItem(yourItem) + ) { target.item = yourItem.id; // bypass setItem so we don't break choicelock or anything return; } @@ -3253,8 +3257,7 @@ let BattleMovedex = { volatileStatus: 'curse', onModifyMove(move, source, target) { if (!source.hasType('Ghost')) { - // @ts-ignore - move.target = move.nonGhostTarget; + move.target = move.nonGhostTarget as MoveTarget; } }, onTryHit(target, source, move) { @@ -3471,8 +3474,12 @@ let BattleMovedex = { onHit(target, source, move) { let success = false; if (!target.volatiles['substitute'] || move.infiltrates) success = !!this.boost({evasion: -1}); - let removeTarget = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', 'gmaxsteelsurge']; - let removeAll = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb', 'gmaxsteelsurge']; + const removeTarget = [ + 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', 'gmaxsteelsurge', + ]; + const removeAll = [ + 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', 'gmaxsteelsurge', + ]; for (const targetCondition of removeTarget) { if (target.side.removeSideCondition(targetCondition)) { if (!removeAll.includes(targetCondition)) continue; @@ -3683,7 +3690,10 @@ let BattleMovedex = { noCopy: true, // doesn't get copied by Baton Pass onStart(pokemon, source, effect) { // The target hasn't taken its turn, or Cursed Body activated and the move was not used through Dancer or Instruct - if (this.queue.willMove(pokemon) || (pokemon === this.activePokemon && this.activeMove && !this.activeMove.isExternal)) { + if ( + this.queue.willMove(pokemon) || + (pokemon === this.activePokemon && this.activeMove && !this.activeMove.isExternal) + ) { this.effectData.duration--; } if (!pokemon.lastMove) { @@ -4525,7 +4535,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('terrainextender')) { + if (source?.hasItem('terrainextender')) { return 8; } return 5; @@ -4545,7 +4555,6 @@ let BattleMovedex = { return null; } }, - onBasePowerPriority: 6, onBasePower(basePower, attacker, defender, move) { if (move.type === 'Electric' && attacker.isGrounded() && !attacker.isSemiInvulnerable()) { this.debug('electric terrain boost'); @@ -4553,7 +4562,7 @@ let BattleMovedex = { } }, onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { + if (effect?.effectType === 'Ability') { this.add('-fieldstart', 'move: Electric Terrain', '[from] ability: ' + effect, '[of] ' + source); } else { this.add('-fieldstart', 'move: Electric Terrain'); @@ -4730,7 +4739,7 @@ let BattleMovedex = { if (!move || target.volatiles['dynamax']) return false; if (move.maxPowered) move = this.dex.getMove(move.baseMove); - let moveIndex = target.moves.indexOf(move.id); + const moveIndex = target.moves.indexOf(move.id); if (move.isZ || noEncore.includes(move.id) || !target.moveSlots[moveIndex] || target.moveSlots[moveIndex].pp <= 0) { // it failed return false; @@ -4746,7 +4755,8 @@ let BattleMovedex = { }, onResidualOrder: 13, onResidual(target) { - if (target.moves.includes(this.effectData.move) && target.moveSlots[target.moves.indexOf(this.effectData.move)].pp <= 0) { + if (target.moves.includes(this.effectData.move) && + target.moveSlots[target.moves.indexOf(this.effectData.move)].pp <= 0) { // early termination if you run out of PP target.removeVolatile('encore'); } @@ -4824,7 +4834,7 @@ let BattleMovedex = { }, onDamagePriority: -10, onDamage(damage, target, source, effect) { - if (effect && effect.effectType === 'Move' && damage >= target.hp) { + if (effect?.effectType === 'Move' && damage >= target.hp) { this.add('-activate', target, 'move: Endure'); return target.hp - 1; } @@ -4864,7 +4874,7 @@ let BattleMovedex = { accuracy: 100, basePower: 0, category: "Status", - desc: "Causes the target's Ability to become the same as the user's. Fails if the target's Ability is Battle Bond, Comatose, Disguise, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Truant, or the same Ability as the user, or if the user's Ability is Battle Bond, Comatose, Disguise, Flower Gift, Forecast, Illusion, Imposter, Multitype, Neutralizing Gas, Power Construct, Power of Alchemy, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, or Zen Mode.", + desc: "Causes the target's Ability to become the same as the user's. Fails if the target's Ability is Battle Bond, Comatose, Disguise, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Truant, or the same Ability as the user, or if the user's Ability is Battle Bond, Comatose, Disguise, Flower Gift, Forecast, Illusion, Imposter, Multitype, Power Construct, Power of Alchemy, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, or Zen Mode.", shortDesc: "The target's Ability changes to match the user's.", id: "entrainment", name: "Entrainment", @@ -4873,14 +4883,21 @@ let BattleMovedex = { flags: {protect: 1, reflectable: 1, mirror: 1, mystery: 1}, onTryHit(target, source) { if (target === source || target.volatiles['dynamax']) return false; - let bannedTargetAbilities = ['battlebond', 'comatose', 'disguise', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'truant']; - let bannedSourceAbilities = ['battlebond', 'comatose', 'disguise', 'flowergift', 'forecast', 'illusion', 'imposter', 'multitype', 'neutralizinggas', 'powerconstruct', 'powerofalchemy', 'receiver', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'trace', 'zenmode']; - if (bannedTargetAbilities.includes(target.ability) || bannedSourceAbilities.includes(source.ability) || target.ability === source.ability) { + const bannedTargetAbilities = [ + 'battlebond', 'comatose', 'disguise', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'truant', + ]; + const bannedSourceAbilities = [ + 'battlebond', 'comatose', 'disguise', 'flowergift', 'forecast', 'illusion', 'imposter', 'multitype', 'powerconstruct', 'powerofalchemy', 'receiver', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'trace', 'zenmode', + ]; + if ( + bannedTargetAbilities.includes(target.ability) || bannedSourceAbilities.includes(source.ability) || + target.ability === source.ability + ) { return false; } }, onHit(target, source) { - let oldAbility = target.setAbility(source.ability); + const oldAbility = target.setAbility(source.ability); if (oldAbility) { this.add('-ability', target, target.getAbility().name, '[from] move: Entrainment'); return; @@ -5277,7 +5294,7 @@ let BattleMovedex = { accuracy: 100, basePower: 0, damageCallback(pokemon) { - let damage = pokemon.hp; + const damage = pokemon.hp; pokemon.faint(); return damage; }, @@ -5387,8 +5404,12 @@ let BattleMovedex = { flags: {protect: 1, mirror: 1, nonsky: 1}, onPrepareHit(target, source, move) { for (const action of this.queue) { - // @ts-ignore - if (!action.move || !action.pokemon || !action.pokemon.isActive || action.pokemon.fainted || action.maxMove || action.zmove) { + if ( + // @ts-ignore + !action.move || !action.pokemon || !action.pokemon.isActive || + // @ts-ignore + action.pokemon.fainted || action.maxMove || action.zmove + ) { continue; } // @ts-ignore @@ -5555,7 +5576,7 @@ let BattleMovedex = { accuracy: 100, basePower: 0, basePowerCallback(pokemon, target) { - let ratio = pokemon.hp * 48 / pokemon.maxhp; + const ratio = pokemon.hp * 48 / pokemon.maxhp; if (ratio < 2) { return 200; } @@ -5819,7 +5840,7 @@ let BattleMovedex = { flags: {protect: 1, mirror: 1, mystery: 1}, onPrepareHit(target, source, move) { if (source.ignoringItem()) return false; - let item = source.getItem(); + const item = source.getItem(); if (!this.singleEvent('TakeItem', item, source.itemData, source, source, move, item)) return false; if (!item.fling) return false; move.basePower = item.fling.basePower; @@ -5917,8 +5938,8 @@ let BattleMovedex = { pp: 10, priority: 0, flags: {distance: 1}, - onHitField(target, source, move) { - let targets = []; + onHitField(t, source, move) { + const targets: Pokemon[] = []; for (const pokemon of this.getAllActive()) { if (pokemon.hasType('Grass')) { // This move affects every Grass-type Pokemon in play. @@ -6038,7 +6059,7 @@ let BattleMovedex = { volatileStatus: 'focusenergy', effect: { onStart(target, source, effect) { - if (effect && effect.id === 'zpower') { + if (effect?.id === 'zpower') { this.add('-start', target, 'move: Focus Energy', '[zeffect]'); } else if (effect && (['imposter', 'psychup', 'transform'].includes(effect.id))) { this.add('-start', target, 'move: Focus Energy', '[silent]'); @@ -6113,7 +6134,7 @@ let BattleMovedex = { effect: { duration: 1, onStart(target, source, effect) { - if (effect && effect.id === 'zpower') { + if (effect?.id === 'zpower') { this.add('-singleturn', target, 'move: Follow Me', '[zeffect]'); } else { this.add('-singleturn', target, 'move: Follow Me'); @@ -6551,7 +6572,9 @@ let BattleMovedex = { flags: {protect: 1, reflectable: 1, mirror: 1, mystery: 1}, volatileStatus: 'gastroacid', onTryHit(pokemon) { - let bannedAbilities = ['battlebond', 'comatose', 'disguise', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'zenmode']; + const bannedAbilities = [ + 'battlebond', 'comatose', 'disguise', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'zenmode', + ]; if (bannedAbilities.includes(pokemon.ability)) { return false; } @@ -6813,6 +6836,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, each Pokemon on the opposing side either falls asleep, becomes poisoned, or becomes paralyzed, even if they have a substitute.", shortDesc: "Base move affects power. Foes: slp or psn or par.", id: "gmaxbefuddle", + isNonstandard: "Unobtainable", name: "G-Max Befuddle", pp: 5, priority: 0, @@ -6820,8 +6844,8 @@ let BattleMovedex = { isMax: "Butterfree", self: { onHit(source) { - for (let pokemon of source.side.foe.active) { - let result = this.random(3); + for (const pokemon of source.side.foe.active) { + const result = this.random(3); if (result === 0) { pokemon.trySetStatus('slp', source); } else if (result === 1) { @@ -6844,6 +6868,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, each Pokemon on the opposing side is prevented from switching for four or five turns (seven turns if the user is holding Grip Claw), even if they have a substitute. Causes damage equal to 1/8 of their maximum HP (1/6 if the user is holding Binding Band), rounded down, at the end of each turn during effect. They can still switch out if they are holding Shed Shell or use Baton Pass, Parting Shot, Teleport, U-turn, or Volt Switch. The effect ends for a target if it leaves the field, or if it uses Rapid Spin or Substitute successfully. This effect is not stackable or reset by using this or another binding move.", shortDesc: "Base move affects power. Foes: bound 4-5 turns.", id: "gmaxcentiferno", + isNonstandard: "Unobtainable", name: "G-Max Centiferno", pp: 5, priority: 0, @@ -6851,7 +6876,7 @@ let BattleMovedex = { isMax: "Centiskorch", self: { onHit(source) { - for (let pokemon of source.side.foe.active) { + for (const pokemon of source.side.foe.active) { pokemon.addVolatile('partiallytrapped', source, this.dex.getActiveMove('G-Max Centiferno'), 'trapper'); } }, @@ -6869,6 +6894,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, each Pokemon on the user's side gains the Focus Energy effect, even if they have a substitute.", shortDesc: "Base move affects power. Allies: Focus Energy.", id: "gmaxchistrike", + isNonstandard: "Unobtainable", name: "G-Max Chi Strike", pp: 5, priority: 0, @@ -6876,7 +6902,7 @@ let BattleMovedex = { isMax: "Machamp", self: { onHit(source) { - for (let pokemon of source.side.active) { + for (const pokemon of source.side.active) { pokemon.addVolatile('focusenergy'); } }, @@ -6894,6 +6920,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, each Pokemon on the opposing side becomes infatuated, even if they have a substitute. This effect does not happen for a target if both it and the user are the same gender, if either is genderless, or if the target is already infatuated.", shortDesc: "Base move affects power. Foes: infatuated.", id: "gmaxcuddle", + isNonstandard: "Unobtainable", name: "G-Max Cuddle", pp: 5, priority: 0, @@ -6901,7 +6928,7 @@ let BattleMovedex = { isMax: "Eevee", self: { onHit(source) { - for (let pokemon of source.side.foe.active) { + for (const pokemon of source.side.foe.active) { pokemon.addVolatile('attract'); } }, @@ -6919,6 +6946,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, each Pokemon on the opposing side loses 2 PP from its last move used, even if they have a substitute.", shortDesc: "Base move affects power. Foes: last move -2 PP.", id: "gmaxdepletion", + isNonstandard: "Unobtainable", name: "G-Max Depletion", pp: 5, priority: 0, @@ -6926,10 +6954,10 @@ let BattleMovedex = { isMax: "Duraludon", self: { onHit(source) { - for (let pokemon of source.side.foe.active) { + for (const pokemon of source.side.foe.active) { const move = pokemon.lastMove; if (move && !move.isZ && !move.isMax) { - let ppDeducted = pokemon.deductPP(move.id, 4); + const ppDeducted = pokemon.deductPP(move.id, 4); if (ppDeducted) { this.add("-activate", pokemon, 'move: Max Depletion', move.name, ppDeducted); return; @@ -6952,6 +6980,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, each Pokemon on the user's side restores 1/6 of its current maximum HP, even if they have a substitute.", shortDesc: "Base move affects power. Allies: +1/6 max HP.", id: "gmaxfinale", + isNonstandard: "Unobtainable", name: "G-Max Finale", pp: 5, priority: 0, @@ -6959,7 +6988,7 @@ let BattleMovedex = { isMax: "Alcremie", self: { onHit(target, source, move) { - for (let pokemon of source.side.active) { + for (const pokemon of source.side.active) { this.heal(pokemon.maxhp / 6, pokemon, source, move); } }, @@ -6977,6 +7006,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, the Speed of each Pokemon on the opposing side is lowered by 2 stages, even if they have a substitute.", shortDesc: "Base move affects power. Foes: -2 Speed.", id: "gmaxfoamburst", + isNonstandard: "Unobtainable", name: "G-Max Foam Burst", pp: 5, priority: 0, @@ -6984,7 +7014,7 @@ let BattleMovedex = { isMax: "Kingler", self: { onHit(source) { - for (let pokemon of source.side.foe.active) { + for (const pokemon of source.side.foe.active) { this.boost({spe: -2}, pokemon); } }, @@ -7002,6 +7032,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, each Pokemon on the opposing side becomes confused, even if they have a substitute.", shortDesc: "Base move affects power. Foes: confused.", id: "gmaxgoldrush", + isNonstandard: "Unobtainable", name: "G-Max Gold Rush", pp: 5, priority: 0, @@ -7009,7 +7040,7 @@ let BattleMovedex = { isMax: "Meowth", self: { onHit(source) { - for (let pokemon of source.side.foe.active) { + for (const pokemon of source.side.foe.active) { pokemon.addVolatile('confusion'); } }, @@ -7027,6 +7058,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, the effect of Gravity begins.", shortDesc: "Base move affects power. Starts Gravity.", id: "gmaxgravitas", + isNonstandard: "Unobtainable", name: "G-Max Gravitas", pp: 5, priority: 0, @@ -7047,6 +7079,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, each Pokemon on the opposing side becomes poisoned, even if they have a substitute.", shortDesc: "Base move affects power. Foes: poisoned.", id: "gmaxmalodor", + isNonstandard: "Unobtainable", name: "G-Max Malodor", pp: 5, priority: 0, @@ -7054,7 +7087,7 @@ let BattleMovedex = { isMax: "Garbodor", self: { onHit(source) { - for (let pokemon of source.side.foe.active) { + for (const pokemon of source.side.foe.active) { pokemon.trySetStatus('psn', source); } }, @@ -7071,6 +7104,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, the effect of Torment begins for each Pokemon on the opposing side, even if they have a substitute.", shortDesc: "Base move affects power. Foes: Tormented.", id: "gmaxmeltdown", + isNonstandard: "Unobtainable", name: "G-Max Meltdown", pp: 5, priority: 0, @@ -7078,7 +7112,7 @@ let BattleMovedex = { isMax: "Melmetal", self: { onHit(source) { - for (let pokemon of source.side.foe.active) { + for (const pokemon of source.side.foe.active) { if (!pokemon.volatiles['dynamax']) pokemon.addVolatile('torment'); } }, @@ -7096,6 +7130,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, there is a 50% chance every Pokemon on the user's side has its Berry restored, even if they have a substitute.", shortDesc: "Base move affects power. 50% restores Berries.", id: "gmaxreplenish", + isNonstandard: "Unobtainable", name: "G-Max Replenish", pp: 5, priority: 0, @@ -7104,9 +7139,9 @@ let BattleMovedex = { self: { onHit(source) { if (this.random(2) === 0) return; - for (let pokemon of source.side.active) { + for (const pokemon of source.side.active) { if (!pokemon.item && pokemon.lastItem && this.dex.getItem(pokemon.lastItem).isBerry) { - let item = pokemon.lastItem; + const item = pokemon.lastItem; pokemon.lastItem = ''; this.add('-item', pokemon, this.dex.getItem(item), '[from] move: G-Max Replenish'); pokemon.setItem(item); @@ -7127,6 +7162,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, the effect of Aurora Veil begins on the user's side.", shortDesc: "Base move affects power. Allies: Aurora Veil.", id: "gmaxresonance", + isNonstandard: "Unobtainable", name: "G-Max Resonance", pp: 5, priority: 0, @@ -7148,6 +7184,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, each Pokemon on the opposing side is prevented from switching for four or five turns (seven turns if the user is holding Grip Claw), even if they have a substitute. Causes damage equal to 1/8 of their maximum HP (1/6 if the user is holding Binding Band), rounded down, at the end of each turn during effect. They can still switch out if they are holding Shed Shell or use Baton Pass, Parting Shot, Teleport, U-turn, or Volt Switch. The effect ends for a target if it leaves the field, or if it uses Rapid Spin or Substitute successfully. This effect is not stackable or reset by using this or another binding move.", shortDesc: "Base move affects power. Foes: bound 4-5 turns.", id: "gmaxsandblast", + isNonstandard: "Unobtainable", name: "G-Max Sandblast", pp: 5, priority: 0, @@ -7155,7 +7192,7 @@ let BattleMovedex = { isMax: "Sandaconda", self: { onHit(source) { - for (let pokemon of source.side.foe.active) { + for (const pokemon of source.side.foe.active) { pokemon.addVolatile('partiallytrapped', source, this.dex.getActiveMove('G-Max Sandblast'), 'trapper'); } }, @@ -7173,6 +7210,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, each Pokemon on the opposing side becomes confused, even if they have a substitute.", shortDesc: "Base move affects power. Foes: confused.", id: "gmaxsmite", + isNonstandard: "Unobtainable", name: "G-Max Smite", pp: 5, priority: 0, @@ -7180,7 +7218,7 @@ let BattleMovedex = { isMax: "Hatterene", self: { onHit(source) { - for (let pokemon of source.side.foe.active) { + for (const pokemon of source.side.foe.active) { pokemon.addVolatile('confusion', source); } }, @@ -7198,6 +7236,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, there is a 50% chance the effect of Yawn begins on the target, even if it has a substitute.", shortDesc: "Base move affects power. Target: 50% Yawn.", id: "gmaxsnooze", + isNonstandard: "Unobtainable", name: "G-Max Snooze", pp: 5, priority: 0, @@ -7226,6 +7265,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, it sets up a hazard on the opposing side of the field, damaging each opposing Pokemon that switches in. Foes lose 1/32, 1/16, 1/8, 1/4, or 1/2 of their maximum HP, rounded down, based on their weakness to the Steel type; 0.25x, 0.5x, neutral, 2x, or 4x, respectively. Can be removed from the opposing side if any opposing Pokemon uses Rapid Spin or Defog successfully, or is hit by Defog.", shortDesc: "Base move affects power. Foes: Steel hazard.", id: "gmaxsteelsurge", + isNonstandard: "Unobtainable", name: "G-Max Steelsurge", pp: 5, priority: 0, @@ -7248,7 +7288,7 @@ let BattleMovedex = { // so we're going to test the damage of a Steel-type Stealth Rock instead. const steelHazard = this.dex.getActiveMove('Stealth Rock'); steelHazard.type = 'Steel'; - let typeMod = this.dex.clampIntRange(pokemon.runEffectiveness(steelHazard), -6, 6); + const typeMod = this.dex.clampIntRange(pokemon.runEffectiveness(steelHazard), -6, 6); this.damage(pokemon.maxhp * Math.pow(2, typeMod) / 8); }, }, @@ -7265,6 +7305,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, it sets up a hazard on the opposing side of the field, damaging each opposing Pokemon that switches in. Foes lose 1/32, 1/16, 1/8, 1/4, or 1/2 of their maximum HP, rounded down, based on their weakness to the Rock type; 0.25x, 0.5x, neutral, 2x, or 4x, respectively. Can be removed from the opposing side if any opposing Pokemon uses Rapid Spin or Defog successfully, or is hit by Defog.", shortDesc: "Base move affects power. Foes: Stealth Rock.", id: "gmaxstonesurge", + isNonstandard: "Unobtainable", name: "G-Max Stonesurge", pp: 5, priority: 0, @@ -7288,6 +7329,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, each Pokemon on the opposing side either becomes poisoned or paralyzed, even if they have a substitute.", shortDesc: "Base move affects power. Foes: psn or par.", id: "gmaxstunshock", + isNonstandard: "Unobtainable", name: "G-Max Stun Shock", pp: 10, priority: 0, @@ -7295,8 +7337,8 @@ let BattleMovedex = { isMax: "Toxtricity", self: { onHit(source) { - for (let pokemon of source.side.foe.active) { - let result = this.random(2); + for (const pokemon of source.side.foe.active) { + const result = this.random(2); if (result === 0) { pokemon.trySetStatus('par', source); } else { @@ -7318,6 +7360,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, each Pokemon on the user's side has its status condition cured, even if they have a substitute.", shortDesc: "Base move affects power. Allies: status cured.", id: "gmaxsweetness", + isNonstandard: "Unobtainable", name: "G-Max Sweetness", pp: 10, priority: 0, @@ -7343,6 +7386,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, the evasiveness of each Pokemon on the opposing side is lowered by 1 stage, even if they have a substitute.", shortDesc: "Base move affects power. Foes: -1 evasiveness.", id: "gmaxtartness", + isNonstandard: "Unobtainable", name: "G-Max Tartness", pp: 10, priority: 0, @@ -7368,6 +7412,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, each Pokemon on the opposing side is prevented from switching out, even if they have a substitute. They can still switch out if they are holding Shed Shell or use Baton Pass, Parting Shot, U-turn, or Volt Switch. If a target leaves the field using Baton Pass, the replacement will remain trapped. The effect ends if the user leaves the field.", shortDesc: "Base move affects power. Foes: trapped.", id: "gmaxterror", + isNonstandard: "Unobtainable", name: "G-Max Terror", pp: 10, priority: 0, @@ -7393,6 +7438,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, for 4 turns each non-Rock-type Pokemon on the opposing side takes damage equal to 1/6 of its maximum HP, rounded down, at the end of each turn during effect, including the last turn.", shortDesc: "Base move affects power. Foes: -1/6 HP, 4 turns.", id: "gmaxvolcalith", + isNonstandard: "Unobtainable", name: "G-Max Volcalith", pp: 10, priority: 0, @@ -7435,6 +7481,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, each Pokemon on the opposing side becomes paralyzed, even if they have a substitute.", shortDesc: "Base move affects power. Foes: paralyzed.", id: "gmaxvoltcrash", + isNonstandard: "Unobtainable", name: "G-Max Volt Crash", pp: 10, priority: 0, @@ -7460,6 +7507,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, for 4 turns each non-Fire-type Pokemon on the opposing side takes damage equal to 1/6 of its maximum HP, rounded down, at the end of each turn during effect, including the last turn.", shortDesc: "Base move affects power. Foes: -1/6 HP, 4 turns.", id: "gmaxwildfire", + isNonstandard: "Unobtainable", name: "G-Max Wildfire", pp: 10, priority: 0, @@ -7502,6 +7550,7 @@ let BattleMovedex = { desc: "Power is equal to the base move's Max Move power. If this move is successful, the effects of Electric Terrain, Grassy Terrain, Misty Terrain, and Psychic Terrain end, the effects of Reflect, Light Screen, Aurora Veil, Safeguard, Mist, G-Max Steelsurge, Spikes, Toxic Spikes, Stealth Rock, and Sticky Web end for the target's side, and the effects of G-Max Steelsurge, Spikes, Toxic Spikes, Stealth Rock, and Sticky Web end for the user's side.", shortDesc: "Base move affects power. Ends Terrain, hazards.", id: "gmaxwindrage", + isNonstandard: "Unobtainable", name: "G-Max Wind Rage", pp: 10, priority: 0, @@ -7510,8 +7559,10 @@ let BattleMovedex = { self: { onHit(source) { let success = false; - let removeTarget = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; - let removeAll = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb', 'gmaxsteelsurge']; + const removeTarget = [ + 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', + ]; + const removeAll = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb', 'gmaxsteelsurge']; for (const targetCondition of removeTarget) { if (source.side.foe.removeSideCondition(targetCondition)) { if (!removeAll.includes(targetCondition)) continue; @@ -7539,7 +7590,7 @@ let BattleMovedex = { accuracy: 100, basePower: 0, basePowerCallback(pokemon, target) { - let targetWeight = target.getWeight(); + const targetWeight = target.getWeight(); if (targetWeight >= 2000) { this.debug('120 bp'); return 120; @@ -7607,8 +7658,12 @@ let BattleMovedex = { flags: {protect: 1, mirror: 1, nonsky: 1}, onPrepareHit(target, source, move) { for (const action of this.queue) { - // @ts-ignore - if (!action.move || !action.pokemon || !action.pokemon.isActive || action.pokemon.fainted || action.maxMove || action.zmove) { + if ( + // @ts-ignore + !action.move || !action.pokemon || !action.pokemon.isActive || + // @ts-ignore + action.pokemon.fainted || action.maxMove || action.zmove + ) { continue; } // @ts-ignore @@ -7684,14 +7739,13 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('terrainextender')) { + if (source?.hasItem('terrainextender')) { return 8; } return 5; }, - onBasePowerPriority: 6, onBasePower(basePower, attacker, defender, move) { - let weakenedMoves = ['earthquake', 'bulldoze', 'magnitude']; + const weakenedMoves = ['earthquake', 'bulldoze', 'magnitude']; if (weakenedMoves.includes(move.id)) { this.debug('move weakened by grassy terrain'); return this.chainModify(0.5); @@ -7702,7 +7756,7 @@ let BattleMovedex = { } }, onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { + if (effect?.effectType === 'Ability') { this.add('-fieldstart', 'move: Grassy Terrain', '[from] ability: ' + effect, '[of] ' + source); } else { this.add('-fieldstart', 'move: Grassy Terrain'); @@ -7772,7 +7826,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasAbility('persistent')) { + if (source?.hasAbility('persistent')) { this.add('-activate', source, 'ability: Persistent', effect); return 7; } @@ -7930,7 +7984,10 @@ let BattleMovedex = { basePower: 0, damageCallback(pokemon, target) { const hp75 = Math.floor(target.getUndynamaxedHP() * 3 / 4); - if (target.volatiles['banefulbunker'] || target.volatiles['kingsshield'] || target.side.getSideCondition('matblock') || target.volatiles['protect'] || target.volatiles['spikyshield']) { + if ( + target.volatiles['protect'] || target.volatiles['banefulbunker'] || target.volatiles['kingsshield'] || + target.volatiles['spikyshield'] || target.side.getSideCondition('matblock') + ) { this.add('-zbroken', target); return this.dex.clampIntRange(Math.ceil(hp75 / 4 - 0.5), 1); } @@ -7964,10 +8021,10 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, mystery: 1}, onHit(target, source) { - let newdef = Math.floor((target.storedStats.def + source.storedStats.def) / 2); + const newdef = Math.floor((target.storedStats.def + source.storedStats.def) / 2); target.storedStats.def = newdef; source.storedStats.def = newdef; - let newspd = Math.floor((target.storedStats.spd + source.storedStats.spd) / 2); + const newspd = Math.floor((target.storedStats.spd + source.storedStats.spd) / 2); target.storedStats.spd = newspd; source.storedStats.spd = newspd; this.add('-activate', source, 'move: Guard Split', '[of] ' + target); @@ -7991,13 +8048,12 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, mirror: 1, authentic: 1, mystery: 1}, onHit(target, source) { - let targetBoosts = {}; - let sourceBoosts = {}; + const targetBoosts: SparseBoostsTable = {}; + const sourceBoosts: SparseBoostsTable = {}; - for (const stat of ['def', 'spd']) { - // @ts-ignore + const defSpd: BoostName[] = ['def', 'spd']; + for (const stat of defSpd) { targetBoosts[stat] = target.boosts[stat]; - // @ts-ignore sourceBoosts[stat] = source.boosts[stat]; } @@ -8279,7 +8335,7 @@ let BattleMovedex = { flags: {snatch: 1, sound: 1, distance: 1, authentic: 1}, onHit(pokemon, source) { this.add('-activate', source, 'move: Heal Bell'); - let side = pokemon.side; + const side = pokemon.side; let success = false; for (const ally of side.pokemon) { if (ally.hasAbility('soundproof')) continue; @@ -8309,7 +8365,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(target, source, effect) { - if (source && source.hasAbility('persistent')) { + if (source?.hasAbility('persistent')) { this.add('-activate', source, 'ability: Persistent', effect); return 7; } @@ -8337,7 +8393,7 @@ let BattleMovedex = { this.add('-end', pokemon, 'move: Heal Block'); }, onTryHeal(damage, target, source, effect) { - if ((effect && effect.id === 'zpower') || this.effectData.isZ) return damage; + if ((effect?.id === 'zpower') || this.effectData.isZ) return damage; return false; }, }, @@ -8469,13 +8525,12 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, mirror: 1, authentic: 1, mystery: 1}, onHit(target, source) { - let targetBoosts = {}; - let sourceBoosts = {}; + const targetBoosts: SparseBoostsTable = {}; + const sourceBoosts: SparseBoostsTable = {}; - for (let i in target.boosts) { - // @ts-ignore + let i: BoostName; + for (i in target.boosts) { targetBoosts[i] = target.boosts[i]; - // @ts-ignore sourceBoosts[i] = source.boosts[i]; } @@ -8495,8 +8550,8 @@ let BattleMovedex = { accuracy: 100, basePower: 0, basePowerCallback(pokemon, target) { - let targetWeight = target.getWeight(); - let pokemonWeight = pokemon.getWeight(); + const targetWeight = target.getWeight(); + const pokemonWeight = pokemon.getWeight(); if (pokemonWeight > targetWeight * 5) { return 120; } @@ -8559,8 +8614,8 @@ let BattleMovedex = { accuracy: 100, basePower: 0, basePowerCallback(pokemon, target) { - let targetWeight = target.getWeight(); - let pokemonWeight = pokemon.getWeight(); + const targetWeight = target.getWeight(); + const pokemonWeight = pokemon.getWeight(); if (pokemonWeight > targetWeight * 5) { return 120; } @@ -8624,7 +8679,7 @@ let BattleMovedex = { this.effectData.multiplier *= 1.5; this.add('-singleturn', target, 'Helping Hand', '[of] ' + source); }, - onBasePowerPriority: 10, + onBasePowerPriority: 3, onBasePower(basePower) { this.debug('Boosting from Helping Hand: ' + this.effectData.multiplier); return this.chainModify(this.effectData.multiplier); @@ -9161,7 +9216,7 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, mirror: 1, distance: 1}, onModifyMove(move, pokemon, target) { - switch (target.effectiveWeather()) { + switch (pokemon.effectiveWeather()) { case 'raindance': case 'primordialsea': move.accuracy = true; @@ -9675,7 +9730,7 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, mirror: 1}, onHit(pokemon, source) { - let item = pokemon.getItem(); + const item = pokemon.getItem(); if ((item.isBerry || item.isGem) && pokemon.takeItem(source)) { this.add('-enditem', pokemon, item.name, '[from] move: Incinerate'); } @@ -9805,7 +9860,7 @@ let BattleMovedex = { return false; } this.add('-singleturn', target, 'move: Instruct', '[of] ' + source); - this.runMove(target.lastMove.id, target, /** @type {number} */(target.lastMoveTargetLoc)); + this.runMove(target.lastMove.id, target, target.lastMoveTargetLoc!); }, secondary: null, target: "normal", @@ -10057,7 +10112,7 @@ let BattleMovedex = { } else { this.add('-activate', target, 'move: Protect'); } - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -10095,7 +10150,7 @@ let BattleMovedex = { priority: 0, flags: {contact: 1, protect: 1, mirror: 1}, onBasePower(basePower, source, target, move) { - let item = target.getItem(); + const item = target.getItem(); if (!this.singleEvent('TakeItem', item, target.itemData, target, target, move, item)) return; if (item.id) { return this.chainModify(1.5); @@ -10103,7 +10158,7 @@ let BattleMovedex = { }, onAfterHit(target, source) { if (source.hp) { - let item = target.takeItem(); + const item = target.takeItem(); if (item) { this.add('-enditem', target, item.name, '[from] move: Knock Off', '[of] ' + source); } @@ -10341,12 +10396,12 @@ let BattleMovedex = { }, onResidualOrder: 8, onResidual(pokemon) { - let target = this.effectData.source.side.active[pokemon.volatiles['leechseed'].sourcePosition]; + const target = this.effectData.source.side.active[pokemon.volatiles['leechseed'].sourcePosition]; if (!target || target.fainted || target.hp <= 0) { this.debug('Nothing to leech into'); return; } - let damage = this.damage(pokemon.baseMaxhp / 8, pokemon, target); + const damage = this.damage(pokemon.baseMaxhp / 8, pokemon, target); if (damage) { this.heal(damage, target, pokemon); } @@ -10476,7 +10531,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(target, source, effect) { - if (source && source.hasItem('lightclay')) { + if (source?.hasItem('lightclay')) { return 8; } return 5; @@ -10612,7 +10667,7 @@ let BattleMovedex = { accuracy: 100, basePower: 0, basePowerCallback(pokemon, target) { - let targetWeight = target.getWeight(); + const targetWeight = target.getWeight(); if (targetWeight >= 2000) { return 120; } @@ -10742,7 +10797,7 @@ let BattleMovedex = { }, onSwitchInPriority: 1, onSwitchIn(target) { - const positions = /**@type {boolean[]} */ (this.effectData.positions); + const positions: boolean[] = this.effectData.positions; if (target.position !== this.effectData.sourcePosition) { return; } @@ -10863,7 +10918,7 @@ let BattleMovedex = { duration: 1, onStart(target, source, effect) { this.add('-singleturn', target, 'move: Magic Coat'); - if (effect && effect.effectType === 'Move') { + if (effect?.effectType === 'Move') { this.effectData.pranksterBoosted = effect.pranksterBoosted; } }, @@ -10872,7 +10927,7 @@ let BattleMovedex = { if (target === source || move.hasBounced || !move.flags['reflectable']) { return; } - let newMove = this.dex.getActiveMove(move.id); + const newMove = this.dex.getActiveMove(move.id); newMove.hasBounced = true; newMove.pranksterBoosted = this.effectData.pranksterBoosted; this.useMove(newMove, target, source); @@ -10882,7 +10937,7 @@ let BattleMovedex = { if (target.side === source.side || move.hasBounced || !move.flags['reflectable']) { return; } - let newMove = this.dex.getActiveMove(move.id); + const newMove = this.dex.getActiveMove(move.id); newMove.hasBounced = true; newMove.pranksterBoosted = false; this.useMove(newMove, this.effectData.target, source); @@ -10931,7 +10986,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasAbility('persistent')) { + if (source?.hasAbility('persistent')) { this.add('-activate', source, 'ability: Persistent', effect); return 7; } @@ -11113,7 +11168,7 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, mirror: 1, nonsky: 1}, onModifyMove(move, pokemon) { - let i = this.random(100); + const i = this.random(100); if (i < 5) { move.magnitude = 4; move.basePower = 10; @@ -11200,7 +11255,7 @@ let BattleMovedex = { } if (move && (move.target === 'self' || move.category === 'Status')) return; this.add('-activate', target, 'move: Mat Block', move.name); - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -11232,7 +11287,7 @@ let BattleMovedex = { self: { onHit(source) { if (!source.volatiles['dynamax']) return; - for (let pokemon of source.side.active) { + for (const pokemon of source.side.active) { this.boost({spe: 1}, pokemon); } }, @@ -11257,7 +11312,7 @@ let BattleMovedex = { self: { onHit(source) { if (!source.volatiles['dynamax']) return; - for (let pokemon of source.side.foe.active) { + for (const pokemon of source.side.foe.active) { this.boost({spd: -1}, pokemon); } }, @@ -11306,7 +11361,7 @@ let BattleMovedex = { self: { onHit(source) { if (!source.volatiles['dynamax']) return; - for (let pokemon of source.side.foe.active) { + for (const pokemon of source.side.foe.active) { this.boost({spa: -1}, pokemon); } }, @@ -11380,7 +11435,7 @@ let BattleMovedex = { } else { this.add('-activate', target, 'move: Max Guard'); } - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -11434,7 +11489,7 @@ let BattleMovedex = { self: { onHit(source) { if (!source.volatiles['dynamax']) return; - for (let pokemon of source.side.active) { + for (const pokemon of source.side.active) { this.boost({atk: 1}, pokemon); } }, @@ -11505,7 +11560,7 @@ let BattleMovedex = { self: { onHit(source) { if (!source.volatiles['dynamax']) return; - for (let pokemon of source.side.active) { + for (const pokemon of source.side.active) { this.boost({spa: 1}, pokemon); } }, @@ -11553,7 +11608,7 @@ let BattleMovedex = { self: { onHit(source) { if (!source.volatiles['dynamax']) return; - for (let pokemon of source.side.foe.active) { + for (const pokemon of source.side.foe.active) { this.boost({def: -1}, pokemon); } }, @@ -11578,7 +11633,7 @@ let BattleMovedex = { self: { onHit(source) { if (!source.volatiles['dynamax']) return; - for (let pokemon of source.side.active) { + for (const pokemon of source.side.active) { this.boost({spd: 1}, pokemon); } }, @@ -11649,7 +11704,7 @@ let BattleMovedex = { self: { onHit(source) { if (!source.volatiles['dynamax']) return; - for (let pokemon of source.side.active) { + for (const pokemon of source.side.active) { this.boost({def: 1}, pokemon); } }, @@ -11674,7 +11729,7 @@ let BattleMovedex = { self: { onHit(source) { if (!source.volatiles['dynamax']) return; - for (let pokemon of source.side.foe.active) { + for (const pokemon of source.side.foe.active) { this.boost({spe: -1}, pokemon); } }, @@ -11699,7 +11754,7 @@ let BattleMovedex = { self: { onHit(source) { if (!source.volatiles['dynamax']) return; - for (let pokemon of source.side.foe.active) { + for (const pokemon of source.side.foe.active) { this.boost({atk: -1}, pokemon); } }, @@ -11781,7 +11836,7 @@ let BattleMovedex = { }, effect: { duration: 1, - onBasePowerPriority: 12, + onBasePowerPriority: 4, onBasePower(basePower) { return this.chainModify(1.5); }, @@ -12053,15 +12108,16 @@ let BattleMovedex = { pp: 10, priority: 0, flags: {}, - noMetronome: ['afteryou', 'appleacid', 'assist', 'aurawheel', 'banefulbunker', 'beakblast', 'behemothbash', 'behemothblade', 'belch', 'bestow', 'bodypress', 'branchpoke', 'breakingswipe', 'celebrate', 'chatter', 'clangoroussoul', 'copycat', 'counter', 'covet', 'craftyshield', 'decorate', 'destinybond', 'detect', 'diamondstorm', 'doubleironbash', 'dragonascent', 'drumbeating', 'dynamaxcannon', 'endure', 'eternabeam', 'falsesurrender', 'feint', 'fleurcannon', 'focuspunch', 'followme', 'freezeshock', 'gravapple', 'helpinghand', 'holdhands', 'hyperspacefury', 'hyperspacehole', 'iceburn', 'instruct', 'kingsshield', 'lifedew', 'lightofruin', 'matblock', 'mefirst', 'meteorassault', 'metronome', 'mimic', 'mindblown', 'mirrorcoat', 'mirrormove', 'moongeistbeam', 'naturepower', 'naturesmadness', 'obstruct', 'originpulse', 'overdrive', 'photongeyser', 'plasmafists', 'precipiceblades', 'protect', 'pyroball', 'quash', 'quickguard', 'ragepowder', 'relicsong', 'secretsword', 'shelltrap', 'sketch', 'sleeptalk', 'snaptrap', 'snarl', 'snatch', 'snore', 'spectralthief', 'spikyshield', 'spiritbreak', 'spotlight', 'steameruption', 'steelbeam', 'strangesteam', 'struggle', 'sunsteelstrike', 'switcheroo', 'technoblast', 'thief', 'thousandarrows', 'thousandwaves', 'transform', 'trick', 'vcreate', 'wideguard'], + noMetronome: [ + 'afteryou', 'appleacid', 'assist', 'aurawheel', 'banefulbunker', 'beakblast', 'behemothbash', 'behemothblade', 'belch', 'bestow', 'bodypress', 'branchpoke', 'breakingswipe', 'celebrate', 'chatter', 'clangoroussoul', 'copycat', 'counter', 'covet', 'craftyshield', 'decorate', 'destinybond', 'detect', 'diamondstorm', 'doubleironbash', 'dragonascent', 'drumbeating', 'dynamaxcannon', 'endure', 'eternabeam', 'falsesurrender', 'feint', 'fleurcannon', 'focuspunch', 'followme', 'freezeshock', 'gravapple', 'helpinghand', 'holdhands', 'hyperspacefury', 'hyperspacehole', 'iceburn', 'instruct', 'kingsshield', 'lifedew', 'lightofruin', 'matblock', 'mefirst', 'meteorassault', 'metronome', 'mimic', 'mindblown', 'mirrorcoat', 'mirrormove', 'moongeistbeam', 'naturepower', 'naturesmadness', 'obstruct', 'originpulse', 'overdrive', 'photongeyser', 'plasmafists', 'precipiceblades', 'protect', 'pyroball', 'quash', 'quickguard', 'ragepowder', 'relicsong', 'secretsword', 'shelltrap', 'sketch', 'sleeptalk', 'snaptrap', 'snarl', 'snatch', 'snore', 'spectralthief', 'spikyshield', 'spiritbreak', 'spotlight', 'steameruption', 'steelbeam', 'strangesteam', 'struggle', 'sunsteelstrike', 'switcheroo', 'technoblast', 'thief', 'thousandarrows', 'thousandwaves', 'transform', 'trick', 'vcreate', 'wideguard', + ], onHit(target, source, effect) { - const moves = []; + const moves: MoveData[] = []; for (const id in exports.BattleMovedex) { const move = exports.BattleMovedex[id]; if (id !== move.id) continue; if (move.isZ || move.isMax || move.isNonstandard) continue; - // @ts-ignore - if (effect.noMetronome.includes(move.id)) continue; + if (effect.noMetronome!.includes(move.id)) continue; if (this.dex.getMove(id).gen > this.gen) continue; moves.push(move); } @@ -12120,7 +12176,7 @@ let BattleMovedex = { return false; } if (move.isZ || move.isMax) return false; - let mimicIndex = source.moves.indexOf('mimic'); + const mimicIndex = source.moves.indexOf('mimic'); if (mimicIndex < 0) return false; source.moveSlots[mimicIndex] = { @@ -12207,12 +12263,18 @@ let BattleMovedex = { effect: { noCopy: true, onSourceModifyDamage(damage, source, target, move) { - if (['stomp', 'steamroller', 'bodyslam', 'flyingpress', 'dragonrush', 'heatcrash', 'heavyslam', 'maliciousmoonsault'].includes(move.id)) { + const boostedMoves = [ + 'stomp', 'steamroller', 'bodyslam', 'flyingpress', 'dragonrush', 'heatcrash', 'heavyslam', 'maliciousmoonsault', + ]; + if (boostedMoves.includes(move.id)) { return this.chainModify(2); } }, onAccuracy(accuracy, target, source, move) { - if (['stomp', 'steamroller', 'bodyslam', 'flyingpress', 'dragonrush', 'heatcrash', 'heavyslam', 'maliciousmoonsault'].includes(move.id)) { + const boostedMoves = [ + 'stomp', 'steamroller', 'bodyslam', 'flyingpress', 'dragonrush', 'heatcrash', 'heavyslam', 'maliciousmoonsault', + ]; + if (boostedMoves.includes(move.id)) { return true; } return accuracy; @@ -12380,15 +12442,14 @@ let BattleMovedex = { if (effect.effectType === 'Move' && effect.infiltrates && target.side !== source.side) return; if (source && target !== source) { let showMsg = false; - for (let i in boost) { - // @ts-ignore - if (boost[i] < 0) { - // @ts-ignore + let i: BoostName; + for (i in boost) { + if (boost[i]! < 0) { delete boost[i]; showMsg = true; } } - if (showMsg && !(/** @type {ActiveMove} */(effect)).secondaries) { + if (showMsg && !(effect as ActiveMove).secondaries) { this.add('-activate', target, 'move: Mist'); } } @@ -12447,7 +12508,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('terrainextender')) { + if (source?.hasItem('terrainextender')) { return 8; } return 5; @@ -12466,7 +12527,6 @@ let BattleMovedex = { return null; } }, - onBasePowerPriority: 6, onBasePower(basePower, attacker, defender, move) { if (move.type === 'Dragon' && defender.isGrounded() && !defender.isSemiInvulnerable()) { this.debug('misty terrain weaken'); @@ -12474,7 +12534,7 @@ let BattleMovedex = { } }, onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { + if (effect?.effectType === 'Ability') { this.add('-fieldstart', 'move: Misty Terrain', '[from] ability: ' + effect, '[of] ' + source); } else { this.add('-fieldstart', 'move: Misty Terrain'); @@ -12812,13 +12872,13 @@ let BattleMovedex = { flags: {protect: 1, mirror: 1}, onModifyType(move, pokemon) { if (pokemon.ignoringItem()) return; - let item = pokemon.getItem(); + const item = pokemon.getItem(); if (!item.naturalGift) return; move.type = item.naturalGift.type; }, onPrepareHit(target, pokemon, move) { if (pokemon.ignoringItem()) return false; - let item = pokemon.getItem(); + const item = pokemon.getItem(); if (!item.naturalGift) return false; move.basePower = item.naturalGift.basePower; pokemon.setItem(''); @@ -13157,7 +13217,7 @@ let BattleMovedex = { } else { this.add('-activate', target, 'move: Protect'); } - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -13409,8 +13469,8 @@ let BattleMovedex = { flags: {protect: 1, mirror: 1, mystery: 1}, onHit(target, pokemon) { const targetHP = target.getUndynamaxedHP(); - let averagehp = Math.floor((targetHP + pokemon.hp) / 2) || 1; - let targetChange = targetHP - averagehp; + const averagehp = Math.floor((targetHP + pokemon.hp) / 2) || 1; + const targetChange = targetHP - averagehp; target.sethp(target.hp - targetChange); this.add('-sethp', target, target.getHealth, '[from] move: Pain Split', '[silent]'); pokemon.sethp(averagehp); @@ -13478,7 +13538,7 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, reflectable: 1, mirror: 1, sound: 1, authentic: 1}, onHit(target, source, move) { - let success = this.boost({atk: -1, spa: -1}, target, source); + const success = this.boost({atk: -1, spa: -1}, target, source); if (!success && !target.hasAbility('mirrorarmor')) { delete move.selfSwitch; } @@ -13591,7 +13651,7 @@ let BattleMovedex = { }, onResidualOrder: 20, onResidual(pokemon) { - let duration = pokemon.volatiles['perishsong'].duration; + const duration = pokemon.volatiles['perishsong'].duration; this.add('-start', pokemon, 'perish' + duration); }, }, @@ -13817,7 +13877,7 @@ let BattleMovedex = { priority: 0, flags: {contact: 1, protect: 1, mirror: 1, distance: 1}, onHit(target, source) { - let item = target.getItem(); + const item = target.getItem(); if (source.hp && item.isBerry && target.takeItem(source)) { this.add('-enditem', target, item.name, '[from] stealeat', '[move] Pluck', '[of] ' + source); if (this.singleEvent('Eat', item, null, source, null, null)) { @@ -14079,10 +14139,10 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, mystery: 1}, onHit(target, source) { - let newatk = Math.floor((target.storedStats.atk + source.storedStats.atk) / 2); + const newatk = Math.floor((target.storedStats.atk + source.storedStats.atk) / 2); target.storedStats.atk = newatk; source.storedStats.atk = newatk; - let newspa = Math.floor((target.storedStats.spa + source.storedStats.spa) / 2); + const newspa = Math.floor((target.storedStats.spa + source.storedStats.spa) / 2); target.storedStats.spa = newspa; source.storedStats.spa = newspa; this.add('-activate', source, 'move: Power Split', '[of] ' + target); @@ -14106,13 +14166,12 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, mirror: 1, authentic: 1, mystery: 1}, onHit(target, source) { - let targetBoosts = {}; - let sourceBoosts = {}; + const targetBoosts: SparseBoostsTable = {}; + const sourceBoosts: SparseBoostsTable = {}; - for (const stat of ['atk', 'spa']) { - // @ts-ignore + const atkSpa: BoostName[] = ['atk', 'spa']; + for (const stat of atkSpa) { targetBoosts[stat] = target.boosts[stat]; - // @ts-ignore sourceBoosts[stat] = source.boosts[stat]; } @@ -14143,21 +14202,21 @@ let BattleMovedex = { effect: { onStart(pokemon) { this.add('-start', pokemon, 'Power Trick'); - let newatk = pokemon.storedStats.def; - let newdef = pokemon.storedStats.atk; + const newatk = pokemon.storedStats.def; + const newdef = pokemon.storedStats.atk; pokemon.storedStats.atk = newatk; pokemon.storedStats.def = newdef; }, onCopy(pokemon) { - let newatk = pokemon.storedStats.def; - let newdef = pokemon.storedStats.atk; + const newatk = pokemon.storedStats.def; + const newdef = pokemon.storedStats.atk; pokemon.storedStats.atk = newatk; pokemon.storedStats.def = newdef; }, onEnd(pokemon) { this.add('-end', pokemon, 'Power Trick'); - let newatk = pokemon.storedStats.def; - let newdef = pokemon.storedStats.atk; + const newatk = pokemon.storedStats.def; + const newdef = pokemon.storedStats.atk; pokemon.storedStats.atk = newatk; pokemon.storedStats.def = newdef; }, @@ -14266,7 +14325,7 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, mirror: 1}, onModifyMove(move, pokemon, target) { - let rand = this.random(10); + const rand = this.random(10); if (rand < 2) { move.heal = [1, 4]; } else if (rand < 6) { @@ -14339,7 +14398,7 @@ let BattleMovedex = { } else { this.add('-activate', target, 'move: Protect'); } - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -14388,8 +14447,8 @@ let BattleMovedex = { priority: 0, flags: {authentic: 1, mystery: 1}, onHit(target, source) { - for (let i in target.boosts) { - // @ts-ignore + let i: BoostName; + for (i in target.boosts) { source.boosts[i] = target.boosts[i]; } const volatilesToCopy = ['focusenergy', 'laserfocus']; @@ -14473,7 +14532,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('terrainextender')) { + if (source?.hasItem('terrainextender')) { return 8; } return 5; @@ -14494,7 +14553,6 @@ let BattleMovedex = { this.add('-activate', target, 'move: Psychic Terrain'); return null; }, - onBasePowerPriority: 6, onBasePower(basePower, attacker, defender, move) { if (move.type === 'Psychic' && attacker.isGrounded() && !attacker.isSemiInvulnerable()) { this.debug('psychic terrain boost'); @@ -14502,7 +14560,7 @@ let BattleMovedex = { } }, onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { + if (effect?.effectType === 'Ability') { this.add('-fieldstart', 'move: Psychic Terrain', '[from] ability: ' + effect, '[of] ' + source); } else { this.add('-fieldstart', 'move: Psychic Terrain'); @@ -14749,7 +14807,7 @@ let BattleMovedex = { } }, onModifyMove(move, source, target) { - if (target && target.beingCalledBack) move.accuracy = true; + if (target?.beingCalledBack) move.accuracy = true; }, onTryHit(target, pokemon) { target.side.removeSideCondition('pursuit'); @@ -14820,7 +14878,7 @@ let BattleMovedex = { flags: {protect: 1, mirror: 1}, onHit(target) { if (target.side.active.length < 2) return false; // fails in singles - let action = this.queue.willMove(target); + const action = this.queue.willMove(target); if (!action) return false; action.order = 201; @@ -14884,7 +14942,7 @@ let BattleMovedex = { return; } this.add('-activate', target, 'move: Quick Guard'); - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -14983,10 +15041,13 @@ let BattleMovedex = { }, onFoeRedirectTargetPriority: 1, onFoeRedirectTarget(target, source, source2, move) { - if (!this.effectData.target.isSkyDropped() && source.runStatusImmunity('powder') && this.validTarget(this.effectData.target, source, move.target)) { + const ragePowderUser = this.effectData.target; + if (ragePowderUser.isSkyDropped()) return; + + if (source.runStatusImmunity('powder') && this.validTarget(ragePowderUser, source, move.target)) { if (move.smartTarget) move.smartTarget = false; this.debug("Rage Powder redirected target of move"); - return this.effectData.target; + return ragePowderUser; } }, }, @@ -15032,7 +15093,7 @@ let BattleMovedex = { if (pokemon.hp && pokemon.removeVolatile('leechseed')) { this.add('-end', pokemon, 'Leech Seed', '[from] move: Rapid Spin', '[of] ' + pokemon); } - let sideConditions = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb', 'gmaxsteelsurge']; + const sideConditions = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb', 'gmaxsteelsurge']; for (const condition of sideConditions) { if (pokemon.hp && pokemon.side.removeSideCondition(condition)) { this.add('-sideend', pokemon.side, this.dex.getEffect(condition).name, '[from] move: Rapid Spin', '[of] ' + pokemon); @@ -15046,7 +15107,7 @@ let BattleMovedex = { if (pokemon.hp && pokemon.removeVolatile('leechseed')) { this.add('-end', pokemon, 'Leech Seed', '[from] move: Rapid Spin', '[of] ' + pokemon); } - let sideConditions = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb', 'gmaxsteelsurge']; + const sideConditions = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb', 'gmaxsteelsurge']; for (const condition of sideConditions) { if (pokemon.hp && pokemon.side.removeSideCondition(condition)) { this.add('-sideend', pokemon.side, this.dex.getEffect(condition).name, '[from] move: Rapid Spin', '[of] ' + pokemon); @@ -15173,7 +15234,7 @@ let BattleMovedex = { flags: {snatch: 1}, onHit(pokemon) { if (pokemon.item || !pokemon.lastItem) return false; - let item = pokemon.lastItem; + const item = pokemon.lastItem; pokemon.lastItem = ''; this.add('-item', pokemon, this.dex.getItem(item), '[from] move: Recycle'); pokemon.setItem(item); @@ -15201,7 +15262,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(target, source, effect) { - if (source && source.hasItem('lightclay')) { + if (source?.hasItem('lightclay')) { return 8; } return 5; @@ -15310,7 +15371,8 @@ let BattleMovedex = { }, onAfterMoveSecondarySelf(pokemon, target, move) { if (move.willChangeForme) { - pokemon.formeChange(pokemon.species.id === 'meloettapirouette' ? 'Meloetta' : 'Meloetta-Pirouette', this.effect, false, '[msg]'); + const meloettaForme = pokemon.species.id === 'meloettapirouette' ? '' : '-Pirouette'; + pokemon.formeChange('Meloetta' + meloettaForme, this.effect, false, '[msg]'); } }, target: "allAdjacentFoes", @@ -15428,7 +15490,7 @@ let BattleMovedex = { accuracy: 100, basePower: 60, basePowerCallback(pokemon, target, move) { - let damagedByTarget = pokemon.attackedBy.some( + const damagedByTarget = pokemon.attackedBy.some( p => p.source === target && p.damage > 0 && p.thisTurn ); if (damagedByTarget) { @@ -15455,7 +15517,7 @@ let BattleMovedex = { accuracy: 100, basePower: 0, basePowerCallback(pokemon, target) { - let ratio = pokemon.hp * 48 / pokemon.maxhp; + const ratio = pokemon.hp * 48 / pokemon.maxhp; if (ratio < 2) { return 200; } @@ -15698,7 +15760,7 @@ let BattleMovedex = { accuracy: true, basePower: 0, category: "Status", - desc: "The user's Ability changes to match the target's Ability. Fails if the user's Ability is Battle Bond, Comatose, Disguise, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, or already matches the target, or if the target's Ability is Battle Bond, Comatose, Disguise, Flower Gift, Forecast, Illusion, Imposter, Multitype, Neutralizing Gas, Power Construct, Power of Alchemy, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, Wonder Guard, or Zen Mode.", + desc: "The user's Ability changes to match the target's Ability. Fails if the user's Ability is Battle Bond, Comatose, Disguise, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, or already matches the target, or if the target's Ability is Battle Bond, Comatose, Disguise, Flower Gift, Forecast, Illusion, Imposter, Multitype, Power Construct, Power of Alchemy, Receiver, RKS System, Schooling, Shields Down, Stance Change, Trace, Wonder Guard, or Zen Mode.", shortDesc: "User replaces its Ability with the target's.", id: "roleplay", name: "Role Play", @@ -15706,14 +15768,20 @@ let BattleMovedex = { priority: 0, flags: {authentic: 1, mystery: 1}, onTryHit(target, source) { - let bannedTargetAbilities = ['battlebond', 'comatose', 'disguise', 'flowergift', 'forecast', 'illusion', 'imposter', 'multitype', 'neutralizinggas', 'powerconstruct', 'powerofalchemy', 'receiver', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'trace', 'wonderguard', 'zenmode']; - let bannedSourceAbilities = ['battlebond', 'comatose', 'disguise', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange']; - if (bannedTargetAbilities.includes(target.ability) || bannedSourceAbilities.includes(source.ability) || target.ability === source.ability) { + if (target.ability === source.ability) return false; + + const bannedTargetAbilities = [ + 'battlebond', 'comatose', 'disguise', 'flowergift', 'forecast', 'illusion', 'imposter', 'multitype', 'powerconstruct', 'powerofalchemy', 'receiver', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'trace', 'wonderguard', 'zenmode', + ]; + const bannedSourceAbilities = [ + 'battlebond', 'comatose', 'disguise', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', + ]; + if (bannedTargetAbilities.includes(target.ability) || bannedSourceAbilities.includes(source.ability)) { return false; } }, onHit(target, source) { - let oldAbility = source.setAbility(target.ability); + const oldAbility = source.setAbility(target.ability); if (oldAbility) { this.add('-ability', source, source.getAbility().name, '[from] move: Role Play', '[of] ' + target); return; @@ -15844,7 +15912,7 @@ let BattleMovedex = { priority: 0, flags: {distance: 1, nonsky: 1}, onHitField(target, source) { - let targets = []; + const targets: Pokemon[] = []; let anyAirborne = false; for (const pokemon of this.getAllActive()) { if (!pokemon.runImmunity('Ground')) { @@ -15891,7 +15959,7 @@ let BattleMovedex = { // @ts-ignore if (!action.pokemon || !action.move || action.maxMove || action.zmove) continue; // @ts-ignore - if (action.move.id === 'round') { + if (action.move?.id === 'round') { // @ts-ignore this.queue.prioritizeAction(action, move); return; @@ -15961,7 +16029,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(target, source, effect) { - if (source && source.hasAbility('persistent')) { + if (source?.hasAbility('persistent')) { this.add('-activate', source, 'ability: Persistent', effect); return 7; } @@ -16647,7 +16715,7 @@ let BattleMovedex = { onHit(pokemon, source, move) { if (pokemon.side !== source.side && move.category === 'Physical') { pokemon.volatiles['shelltrap'].gotHit = true; - let action = this.queue.willMove(pokemon); + const action = this.queue.willMove(pokemon); if (action) { this.queue.prioritizeAction(action, move); } @@ -16788,13 +16856,15 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, reflectable: 1, mirror: 1, mystery: 1}, onTryHit(pokemon) { - let bannedAbilities = ['battlebond', 'comatose', 'disguise', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'simple', 'stancechange', 'truant', 'zenmode']; + const bannedAbilities = [ + 'battlebond', 'comatose', 'disguise', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'simple', 'stancechange', 'truant', 'zenmode', + ]; if (bannedAbilities.includes(pokemon.ability)) { return false; } }, onHit(pokemon) { - let oldAbility = pokemon.setAbility('simple'); + const oldAbility = pokemon.setAbility('simple'); if (oldAbility) { this.add('-ability', pokemon, 'Simple', '[from] move: Simple Beam'); return; @@ -16911,7 +16981,7 @@ let BattleMovedex = { accuracy: true, basePower: 0, category: "Status", - desc: "The user swaps its Ability with the target's Ability. Fails if either the user or the target's Ability is Battle Bond, Comatose, Disguise, Gulp Missile, Hunger Switch, Ice Face, Illusion, Multitype, Neutralizing Gas, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Wonder Guard, or Zen Mode.", + desc: "The user swaps its Ability with the target's Ability. Fails if either the user or the target's Ability is Battle Bond, Comatose, Disguise, Gulp Missile, Hunger Switch, Ice Face, Illusion, Multitype, Power Construct, RKS System, Schooling, Shields Down, Stance Change, Wonder Guard, or Zen Mode.", shortDesc: "The user and the target trade Abilities.", id: "skillswap", name: "Skill Swap", @@ -16919,14 +16989,18 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, mirror: 1, authentic: 1, mystery: 1}, onTryHit(target, source) { - const bannedAbilities = ['battlebond', 'comatose', 'disguise', 'gulpmissile', 'hungerswitch', 'iceface', 'illusion', 'multitype', 'neutralizinggas', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'wonderguard', 'zenmode']; - if (target.volatiles['dynamax'] || bannedAbilities.includes(target.ability) || bannedAbilities.includes(source.ability)) { + const bannedAbilities = [ + 'battlebond', 'comatose', 'disguise', 'gulpmissile', 'hungerswitch', 'iceface', 'illusion', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'wonderguard', 'zenmode', + ]; + if ( + target.volatiles['dynamax'] || bannedAbilities.includes(target.ability) || bannedAbilities.includes(source.ability) + ) { return false; } }, onHit(target, source, move) { - let targetAbility = target.getAbility(); - let sourceAbility = source.getAbility(); + const targetAbility = target.getAbility(); + const sourceAbility = source.getAbility(); if (target.side === source.side) { this.add('-activate', source, 'move: Skill Swap', '', '', '[of] ' + target); } else { @@ -17060,7 +17134,7 @@ let BattleMovedex = { } }, onHit(target, source) { - if (target.hp) this.add('-end', target, 'Sky Drop'); + this.add('-end', target, 'Sky Drop'); }, effect: { duration: 2, @@ -17335,7 +17409,8 @@ let BattleMovedex = { onStart(pokemon) { let applies = false; if (pokemon.hasType('Flying') || pokemon.hasAbility('levitate')) applies = true; - if (pokemon.hasItem('ironball') || pokemon.volatiles['ingrain'] || this.field.getPseudoWeather('gravity')) applies = false; + if (pokemon.hasItem('ironball') || pokemon.volatiles['ingrain'] || + this.field.getPseudoWeather('gravity')) applies = false; if (pokemon.removeVolatile('fly') || pokemon.removeVolatile('bounce')) { applies = true; this.queue.cancelMove(pokemon); @@ -17936,7 +18011,7 @@ let BattleMovedex = { onSwitchIn(pokemon) { if (!pokemon.isGrounded()) return; if (pokemon.hasItem('heavydutyboots')) return; - let damageAmounts = [0, 3, 4, 6]; // 1/8, 1/6, 1/4 + const damageAmounts = [0, 3, 4, 6]; // 1/8, 1/6, 1/4 this.damage(damageAmounts[this.effectData.layers] * pokemon.maxhp / 24); }, }, @@ -17983,7 +18058,7 @@ let BattleMovedex = { } else { this.add('-activate', target, 'move: Protect'); } - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -18248,7 +18323,7 @@ let BattleMovedex = { }, onSwitchIn(pokemon) { if (pokemon.hasItem('heavydutyboots')) return; - let typeMod = this.dex.clampIntRange(pokemon.runEffectiveness(this.dex.getActiveMove('stealthrock')), -6, 6); + const typeMod = this.dex.clampIntRange(pokemon.runEffectiveness(this.dex.getActiveMove('stealthrock')), -6, 6); this.damage(pokemon.maxhp * Math.pow(2, typeMod) / 8); }, }, @@ -18403,7 +18478,7 @@ let BattleMovedex = { this.effectData.def = 0; this.effectData.spd = 0; this.add('-start', target, 'stockpile' + this.effectData.layers); - let [curDef, curSpD] = [target.boosts.def, target.boosts.spd]; + const [curDef, curSpD] = [target.boosts.def, target.boosts.spd]; this.boost({def: 1, spd: 1}, target, target); if (curDef !== target.boosts.def) this.effectData.def--; if (curSpD !== target.boosts.spd) this.effectData.spd--; @@ -18420,8 +18495,7 @@ let BattleMovedex = { }, onEnd(target) { if (this.effectData.def || this.effectData.spd) { - /** @type {SparseBoostsTable} */ - let boosts = {}; + const boosts: SparseBoostsTable = {}; if (this.effectData.def) boosts.def = this.effectData.def; if (this.effectData.spd) boosts.spd = this.effectData.spd; this.boost(boosts, target, target); @@ -18613,8 +18687,8 @@ let BattleMovedex = { flags: {protect: 1, reflectable: 1, mirror: 1, heal: 1}, onHit(target, source) { if (target.boosts.atk === -6) return false; - let atk = target.getStat('atk', false, true); - let success = this.boost({atk: -1}, target, source, null, false, true); + const atk = target.getStat('atk', false, true); + const success = this.boost({atk: -1}, target, source, null, false, true); return !!(this.heal(atk, source, target) || success); }, secondary: null, @@ -18704,7 +18778,7 @@ let BattleMovedex = { priority: 0, flags: {snatch: 1}, onTryHit(target, source, move) { - let item = source.getItem(); + const item = source.getItem(); if (item.isBerry && source.eatItem(true)) { this.boost({def: 2}, source, null, null, false, true); } else { @@ -18804,7 +18878,7 @@ let BattleMovedex = { return damage; } if (damage > target.volatiles['substitute'].hp) { - damage = /** @type {number} */ (target.volatiles['substitute'].hp); + damage = target.volatiles['substitute'].hp as number; } target.volatiles['substitute'].hp -= damage; source.lastDamage = damage; @@ -18821,7 +18895,7 @@ let BattleMovedex = { } this.singleEvent('AfterSubDamage', move, null, target, source, move, damage); this.runEvent('AfterSubDamage', target, source, move, damage); - return 0; // hit + return this.HIT_SUBSTITUTE; }, onEnd(target) { this.add('-end', target, 'Substitute'); @@ -18865,8 +18939,9 @@ let BattleMovedex = { priority: 1, flags: {contact: 1, protect: 1, mirror: 1}, onTry(source, target) { - let action = this.queue.willMove(target); - if (!action || action.choice !== 'move' || (action.move.category === 'Status' && action.move.id !== 'mefirst') || target.volatiles.mustrecharge) { + const action = this.queue.willMove(target); + const move = action?.choice === 'move' ? action.move : null; + if (!move || (move.category === 'Status' && move.id !== 'mefirst') || target.volatiles.mustrecharge) { this.add('-fail', source); this.attrLastMove('[still]'); return null; @@ -19051,8 +19126,8 @@ let BattleMovedex = { if (!pokemon.volatiles['stockpile'] || !pokemon.volatiles['stockpile'].layers) return false; }, onHit(pokemon) { - let healAmount = [0.25, 0.5, 1]; - let healedBy = this.heal(this.modify(pokemon.maxhp, healAmount[(pokemon.volatiles['stockpile'].layers - 1)])); + const healAmount = [0.25, 0.5, 1]; + const healedBy = this.heal(this.modify(pokemon.maxhp, healAmount[(pokemon.volatiles['stockpile'].layers - 1)])); pokemon.removeVolatile('stockpile'); return !!healedBy; }, @@ -19135,14 +19210,17 @@ let BattleMovedex = { return !target.hasAbility('stickyhold'); }, onHit(target, source, move) { - let yourItem = target.takeItem(source); - let myItem = source.takeItem(); + const yourItem = target.takeItem(source); + const myItem = source.takeItem(); if (target.item || source.item || (!yourItem && !myItem)) { if (yourItem) target.item = yourItem.id; if (myItem) source.item = myItem.id; return false; } - if ((myItem && !this.singleEvent('TakeItem', myItem, source.itemData, target, source, move, myItem)) || (yourItem && !this.singleEvent('TakeItem', yourItem, target.itemData, source, target, move, yourItem))) { + if ( + (myItem && !this.singleEvent('TakeItem', myItem, source.itemData, target, source, move, myItem)) || + (yourItem && !this.singleEvent('TakeItem', yourItem, target.itemData, source, target, move, yourItem)) + ) { if (yourItem) target.item = yourItem.id; if (myItem) source.item = myItem.id; return false; @@ -19343,7 +19421,7 @@ let BattleMovedex = { effect: { duration: 4, durationCallback(target, source, effect) { - if (source && source.hasAbility('persistent')) { + if (source?.hasAbility('persistent')) { this.add('-activate', source, 'ability: Persistent', effect); return 6; } @@ -19504,7 +19582,7 @@ let BattleMovedex = { this.add('-miss', source, active); result = true; } else { - let item = active.getItem(); + const item = active.getItem(); if (active.hp && item.isBerry) { // bypasses Unnerve active.eatItem(true); @@ -19662,11 +19740,12 @@ let BattleMovedex = { if (source.item || source.volatiles['gem']) { return; } - let yourItem = target.takeItem(source); + const yourItem = target.takeItem(source); if (!yourItem) { return; } - if (!this.singleEvent('TakeItem', yourItem, target.itemData, source, target, move, yourItem) || !source.setItem(yourItem)) { + if (!this.singleEvent('TakeItem', yourItem, target.itemData, source, target, move, yourItem) || + !source.setItem(yourItem)) { target.item = yourItem.id; // bypass setItem so we don't break choicelock or anything return; } @@ -19701,7 +19780,7 @@ let BattleMovedex = { } }, volatileStatus: 'smackdown', - ignoreImmunity: {Ground: true}, + ignoreImmunity: {'Ground': true}, secondary: null, target: "allAdjacentFoes", type: "Ground", @@ -19816,7 +19895,7 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, mirror: 1}, onModifyMove(move, pokemon, target) { - switch (target.effectiveWeather()) { + switch (pokemon.effectiveWeather()) { case 'raindance': case 'primordialsea': move.accuracy = true; @@ -19979,10 +20058,9 @@ let BattleMovedex = { flags: {protect: 1, reflectable: 1, mirror: 1, mystery: 1}, onHit(target) { let success = false; - for (let i in target.boosts) { - // @ts-ignore + let i: BoostName; + for (i in target.boosts) { if (target.boosts[i] === 0) continue; - // @ts-ignore target.boosts[i] = -target.boosts[i]; success = true; } @@ -20158,7 +20236,7 @@ let BattleMovedex = { secondary: { chance: 20, onHit(target, source) { - let result = this.random(3); + const result = this.random(3); if (result === 0) { target.trySetStatus('brn', source); } else if (result === 1) { @@ -20189,14 +20267,17 @@ let BattleMovedex = { return !target.hasAbility('stickyhold'); }, onHit(target, source, move) { - let yourItem = target.takeItem(source); - let myItem = source.takeItem(); + const yourItem = target.takeItem(source); + const myItem = source.takeItem(); if (target.item || source.item || (!yourItem && !myItem)) { if (yourItem) target.item = yourItem.id; if (myItem) source.item = myItem.id; return false; } - if ((myItem && !this.singleEvent('TakeItem', myItem, source.itemData, target, source, move, myItem)) || (yourItem && !this.singleEvent('TakeItem', yourItem, target.itemData, source, target, move, yourItem))) { + if ( + (myItem && !this.singleEvent('TakeItem', myItem, source.itemData, target, source, move, myItem)) || + (yourItem && !this.singleEvent('TakeItem', yourItem, target.itemData, source, target, move, yourItem)) + ) { if (yourItem) target.item = yourItem.id; if (myItem) source.item = myItem.id; return false; @@ -20268,7 +20349,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasAbility('persistent')) { + if (source?.hasAbility('persistent')) { this.add('-activate', source, 'ability: Persistent', effect); return 7; } @@ -20474,7 +20555,7 @@ let BattleMovedex = { onTryHit(target) { for (const [i, allyActive] of target.side.active.entries()) { if (allyActive && allyActive.status === 'slp') allyActive.cureStatus(); - let foeActive = target.side.foe.active[i]; + const foeActive = target.side.foe.active[i]; if (foeActive && foeActive.status === 'slp') foeActive.cureStatus(); } }, @@ -20794,15 +20875,18 @@ let BattleMovedex = { flags: {protect: 1, mirror: 1, nonsky: 1}, onPrepareHit(target, source, move) { for (const action of this.queue) { - // @ts-ignore - if (!action.move || !action.pokemon || !action.pokemon.isActive || action.pokemon.fainted || action.maxMove || action.zmove) { + if (action.choice !== 'move') continue; + const otherMove = action.move; + const otherMoveUser = action.pokemon; + if ( + !otherMove || !action.pokemon || !otherMoveUser.isActive || + otherMoveUser.fainted || action.maxMove || action.zmove + ) { continue; } - // @ts-ignore - if (action.pokemon.side === source.side && ['firepledge', 'grasspledge'].includes(action.move.id)) { - // @ts-ignore + if (otherMoveUser.side === source.side && ['firepledge', 'grasspledge'].includes(otherMove.id)) { this.queue.prioritizeAction(action, move); - this.add('-waiting', source, action.pokemon); + this.add('-waiting', source, otherMoveUser); return null; } } @@ -21064,7 +21148,7 @@ let BattleMovedex = { onTryHitPriority: 4, onTryHit(target, source, move) { // Wide Guard blocks all spread moves - if (move && move.target !== 'allAdjacent' && move.target !== 'allAdjacentFoes') { + if (move?.target !== 'allAdjacent' && move.target !== 'allAdjacentFoes') { return; } if (move.isZ || move.isMax) { @@ -21072,7 +21156,7 @@ let BattleMovedex = { return; } this.add('-activate', target, 'move: Wide Guard'); - let lockedmove = source.getVolatile('lockedmove'); + const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset if (source.volatiles['lockedmove'].duration === 2) { @@ -21165,7 +21249,7 @@ let BattleMovedex = { onResidualOrder: 4, onEnd(target) { if (target && !target.fainted) { - let damage = this.heal(this.effectData.hp, target, target); + const damage = this.heal(this.effectData.hp, target, target); if (damage) this.add('-heal', target, target.getHealth, '[from] move: Wish', '[wisher] ' + this.effectData.source.name); } }, @@ -21213,7 +21297,7 @@ let BattleMovedex = { effect: { duration: 5, durationCallback(source, effect) { - if (source && source.hasAbility('persistent')) { + if (source?.hasAbility('persistent')) { this.add('-activate', source, 'ability: Persistent', effect); return 7; } @@ -21291,13 +21375,15 @@ let BattleMovedex = { priority: 0, flags: {protect: 1, reflectable: 1, mirror: 1, mystery: 1}, onTryHit(pokemon) { - let bannedAbilities = ['battlebond', 'comatose', 'disguise', 'insomnia', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'truant', 'zenmode']; + const bannedAbilities = [ + 'battlebond', 'comatose', 'disguise', 'insomnia', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', 'truant', 'zenmode', + ]; if (bannedAbilities.includes(pokemon.ability)) { return false; } }, onHit(pokemon) { - let oldAbility = pokemon.setAbility('insomnia'); + const oldAbility = pokemon.setAbility('insomnia'); if (oldAbility) { this.add('-ability', pokemon, 'Insomnia', '[from] move: Worry Seed'); if (pokemon.status === 'slp') { @@ -21496,5 +21582,3 @@ let BattleMovedex = { contestType: "Cool", }, }; - -exports.BattleMovedex = BattleMovedex; diff --git a/data/pokedex.js b/data/pokedex.ts similarity index 99% rename from data/pokedex.js rename to data/pokedex.ts index c490b13d17..343003e40f 100644 --- a/data/pokedex.js +++ b/data/pokedex.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: SpeciesData}} */ -let BattlePokedex = { +export const BattlePokedex: {[speciesid: string]: SpeciesData} = { bulbasaur: { num: 1, name: "Bulbasaur", @@ -16262,5 +16259,3 @@ let BattlePokedex = { gen: 5, }, }; - -exports.BattlePokedex = BattlePokedex; diff --git a/data/random-teams.js b/data/random-teams.ts similarity index 83% rename from data/random-teams.js rename to data/random-teams.ts index 09c16c43ab..e4303ea571 100644 --- a/data/random-teams.js +++ b/data/random-teams.ts @@ -1,33 +1,30 @@ -'use strict'; +/* eslint max-len: ["error", 240] */ -/** @typedef {import('../sim/prng').PRNG} PRNG */ +import {Dex} from '../sim/dex'; +import {PRNG, PRNGSeed} from '../sim/prng'; -/** @type {typeof import('../sim/dex').Dex} */ -const Dex = require(/** @type {any} */ ('../.sim-dist/dex')).Dex; -/** @type {typeof import('../sim/prng').PRNG} */ -const PRNG = require(/** @type {any} */ ('../.sim-dist/prng')).PRNG; +export interface TeamData { + typeCount: {[k: string]: number}; + typeComboCount: {[k: string]: number}; + baseFormes: {[k: string]: number}; + megaCount: number; + zCount?: number; + has: {[k: string]: number}; + forceResult: boolean; + weaknesses: {[k: string]: number}; + resistances: {[k: string]: number}; + weather?: string; + eeveeLimCount?: number; +} -/** - * @typedef {Object} TeamData - * @property {{[k: string]: number}} typeCount - * @property {{[k: string]: number}} typeComboCount - * @property {{[k: string]: number}} baseFormes - * @property {number} megaCount - * @property {number} [zCount] - * @property {{[k: string]: number}} has - * @property {boolean} forceResult - * @property {{[k: string]: number}} weaknesses - * @property {{[k: string]: number}} resistances - * @property {string} [weather] - * @property {number} [eeveeLimCount] - */ +export class RandomTeams { + dex: ModdedDex; + gen: number; + factoryTier: string; + format: Format; + prng: PRNG; -class RandomTeams { - /** - * @param {Format | string} format - * @param {?PRNG | [number, number, number, number]} [prng] - */ - constructor(format, prng) { + constructor(format: Format | string, prng: PRNG | PRNGSeed | null) { format = Dex.getFormat(format); this.dex = Dex.forFormat(format); this.gen = this.dex.gen; @@ -39,63 +36,38 @@ class RandomTeams { this.prng = prng && !Array.isArray(prng) ? prng : new PRNG(prng); } - /** - * @param {?PRNG | [number, number, number, number]} [prng] - */ - setSeed(prng) { + setSeed(prng?: PRNG | PRNGSeed) { this.prng = prng && !Array.isArray(prng) ? prng : new PRNG(prng); } - /** - * @param {PlayerOptions | null} [options] - * @return {PokemonSet[]} - */ - getTeam(options) { + getTeam(options?: PlayerOptions | null): PokemonSet[] { const generatorName = typeof this.format.team === 'string' && this.format.team.startsWith('random') ? this.format.team + 'Team' : ''; // @ts-ignore return this[generatorName || 'randomTeam'](options); } - /** - * @param {number} numerator - the top part of the probability fraction - * @param {number} denominator - the bottom part of the probability fraction - * @return {boolean} - randomly true or false - */ - randomChance(numerator, denominator) { + randomChance(numerator: number, denominator: number) { return this.prng.randomChance(numerator, denominator); } - /** - * @param {ReadonlyArray} items - the items to choose from - * @return {T} - a random item from items - * @template T - */ - sample(items) { + sample(items: readonly T[]): T { return this.prng.sample(items); } - /** - * @param {number} [m] - * @param {number} [n] - * @return {number} - */ - random(m, n) { + random(m?: number, n?: number) { return this.prng.next(m, n); } /** * Remove an element from an unsorted array significantly faster * than .splice - * - * @param {any[]} list - * @param {number} index */ - fastPop(list, index) { + fastPop(list: any[], index: number) { // If an array doesn't need to be in order, replacing the // element at the given index with the removed element // is much, much faster than using list.splice(index, 1). - let length = list.length; - let element = list[index]; + const length = list.length; + const element = list[index]; list[index] = list[length - 1]; list.pop(); return element; @@ -104,34 +76,29 @@ class RandomTeams { /** * Remove a random element from an unsorted array and return it. * Uses the battle's RNG if in a battle. - * - * @param {any[]} list */ - sampleNoReplace(list) { - let length = list.length; - let index = this.random(length); + sampleNoReplace(list: any[]) { + const length = list.length; + const index = this.random(length); return this.fastPop(list, index); } - /** - * @param {Species} species - */ - checkBattleForme(species) { + checkBattleForme(species: Species) { // If the Pokémon has a Mega or Primal alt forme, that's its preferred battle forme. // No randomization, no choice. We are just checking its existence. // Returns a Pokémon species for further details. if (!species.otherFormes) return null; - let firstForme = this.dex.getSpecies(species.otherFormes[0]); + const firstForme = this.dex.getSpecies(species.otherFormes[0]); if (firstForme.isMega || firstForme.isPrimal) return firstForme; return null; } // checkAbilities(selectedAbilities, defaultAbilities) { // if (!selectedAbilities.length) return true; - // let selectedAbility = selectedAbilities.pop(); - // let isValid = false; - // for (let i = 0; i < defaultAbilities.length; i++) { - // let defaultAbility = defaultAbilities[i]; + // const selectedAbility = selectedAbilities.pop(); + // const isValid = false; + // for (const i = 0; i < defaultAbilities.length; i++) { + // const defaultAbility = defaultAbilities[i]; // if (!defaultAbility) break; // if (defaultAbility.includes(selectedAbility)) { // defaultAbilities.splice(i, 1); @@ -145,19 +112,16 @@ class RandomTeams { // } // hasMegaEvo(species) { // if (!species.otherFormes) return false; - // let firstForme = this.dex.getSpecies(species.otherFormes[0]); + // const firstForme = this.dex.getSpecies(species.otherFormes[0]); // return !!firstForme.isMega; // } - /** - * @return {RandomTeamsTypes.RandomSet[]} - */ - randomCCTeam() { - let team = []; + randomCCTeam(): RandomTeamsTypes.RandomSet[] { + const team = []; - let natures = Object.keys(this.dex.data.Natures); - let items = Object.keys(this.dex.data.Items); + const natures = Object.keys(this.dex.data.Natures); + const items = Object.keys(this.dex.data.Items); - let random6 = this.random6Pokemon(); + const random6 = this.random6Pokemon(); for (let i = 0; i < 6; i++) { let forme = random6[i]; @@ -188,23 +152,30 @@ class RandomTeams { } // Random legal ability - let abilities = Object.values(species.abilities).filter(a => this.dex.getAbility(a).gen <= this.gen); - /**@type {string} */ - let ability = this.gen <= 2 ? 'None' : this.sample(abilities); + const abilities = Object.values(species.abilities).filter(a => this.dex.getAbility(a).gen <= this.gen); + const ability: string = this.gen <= 2 ? 'None' : this.sample(abilities); // Four random unique moves from the movepool let moves; let pool = ['struggle']; if (forme === 'Smeargle') { - pool = Object.keys(this.dex.data.Movedex).filter(moveid => !(this.dex.data.Movedex[moveid].isNonstandard || this.dex.data.Movedex[moveid].isZ || this.dex.data.Movedex[moveid].id === 'hiddenpower' && moveid !== 'hiddenpower')); + pool = Object.keys(this.dex.data.Movedex).filter( + moveid => !(this.dex.data.Movedex[moveid].isNonstandard || this.dex.data.Movedex[moveid].isZ || this.dex.data.Movedex[moveid].id === 'hiddenpower' && moveid !== 'hiddenpower') + ); } else { - let learnset = this.dex.data.Learnsets[species.id] && this.dex.data.Learnsets[species.id].learnset ? this.dex.data.Learnsets[species.id].learnset : this.dex.data.Learnsets[this.dex.getSpecies(species.baseSpecies).id].learnset; - // @ts-ignore - if (learnset) pool = Object.keys(learnset).filter(moveid => learnset[moveid].find(learned => learned.startsWith(this.gen))); + let learnset = this.dex.data.Learnsets[species.id] && this.dex.data.Learnsets[species.id].learnset ? + this.dex.data.Learnsets[species.id].learnset : + this.dex.data.Learnsets[this.dex.getSpecies(species.baseSpecies).id].learnset; + if (learnset) { + pool = Object.keys(learnset).filter( + moveid => learnset![moveid].find(learned => learned.startsWith(String(this.gen))) + ); + } if (species.inheritsFrom) { learnset = this.dex.data.Learnsets[species.inheritsFrom].learnset; - // @ts-ignore - const basePool = Object.keys(learnset).filter(moveid => learnset[moveid].find(learned => learned.startsWith(this.gen))); + const basePool = Object.keys(learnset!).filter( + moveid => learnset![moveid].find(learned => learned.startsWith(String(this.gen))) + ); pool = [...new Set(pool.concat(basePool))]; } } @@ -215,26 +186,24 @@ class RandomTeams { } // Random EVs - let evs = {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0}; - let s = ["hp", "atk", "def", "spa", "spd", "spe"]; + const evs: StatsTable = {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0}; + const s: StatName[] = ["hp", "atk", "def", "spa", "spd", "spe"]; let evpool = 510; do { - let x = this.sample(s); - // @ts-ignore - let y = this.random(Math.min(256 - evs[x], evpool + 1)); - // @ts-ignore + const x = this.sample(s); + const y = this.random(Math.min(256 - evs[x], evpool + 1)); evs[x] += y; evpool -= y; } while (evpool > 0); // Random IVs - let ivs = {hp: this.random(32), atk: this.random(32), def: this.random(32), spa: this.random(32), spd: this.random(32), spe: this.random(32)}; + const ivs = {hp: this.random(32), atk: this.random(32), def: this.random(32), spa: this.random(32), spd: this.random(32), spe: this.random(32)}; // Random nature - let nature = this.sample(natures); + const nature = this.sample(natures); // Level balance--calculate directly from stats rather than using some silly lookup table - let mbstmin = 1307; // Sunkern has the lowest modified base stat total, and that total is 807 + const mbstmin = 1307; // Sunkern has the lowest modified base stat total, and that total is 807 let stats = species.baseStats; // If Wishiwashi, use the school-forme's much higher stats @@ -263,10 +232,10 @@ class RandomTeams { } // Random happiness - let happiness = this.random(256); + const happiness = this.random(256); // Random shininess - let shiny = this.randomChance(1, 1024); + const shiny = this.randomChance(1, 1024); team.push({ name: species.baseSpecies, @@ -291,36 +260,33 @@ class RandomTeams { // Pick six 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 - let last = [0, 151, 251, 386, 493, 649, 721, 807, 890][this.gen]; + const last = [0, 151, 251, 386, 493, 649, 721, 807, 890][this.gen]; - /**@type {number[]} */ - let pool = []; - for (let id in this.dex.data.FormatsData) { + const pool: number[] = []; + for (const id in this.dex.data.FormatsData) { if (!this.dex.data.Pokedex[id] || this.dex.data.FormatsData[id].isNonstandard && this.dex.data.FormatsData[id].isNonstandard === 'Past') continue; - let num = this.dex.data.Pokedex[id].num; + const num = this.dex.data.Pokedex[id].num; if (num <= 0 || pool.includes(num)) continue; if (num > last) break; pool.push(num); } - /**@type {{[k: string]: number}} */ - let hasDexNumber = {}; + const hasDexNumber: {[k: string]: number} = {}; for (let i = 0; i < 6; i++) { - let num = this.sampleNoReplace(pool); + const num = this.sampleNoReplace(pool); hasDexNumber[num] = i; } - /**@type {string[][]} */ - let formes = [[], [], [], [], [], []]; - for (let id in this.dex.data.Pokedex) { + const formes: string[][] = [[], [], [], [], [], []]; + for (const id in this.dex.data.Pokedex) { if (!(this.dex.data.Pokedex[id].num in hasDexNumber)) continue; - let species = this.dex.getSpecies(id); + const species = this.dex.getSpecies(id); if (species.gen <= this.gen && species.isNonstandard !== 'Past' && species.isNonstandard !== 'LGPE') { formes[hasDexNumber[species.num]].push(species.name); } } - let sixPokemon = []; + const sixPokemon = []; for (let i = 0; i < 6; i++) { if (!formes[i].length) { throw new Error("Invalid pokemon gen " + this.gen + ": " + JSON.stringify(formes) + " numbers " + JSON.stringify(hasDexNumber)); @@ -330,19 +296,19 @@ class RandomTeams { return sixPokemon; } - randomHCTeam() { - let team = []; + randomHCTeam(): PokemonSet[] { + const team = []; - let itemPool = Object.keys(this.dex.data.Items); - let abilityPool = Object.keys(this.dex.data.Abilities); - let movePool = Object.keys(this.dex.data.Movedex); - let naturePool = Object.keys(this.dex.data.Natures); + const itemPool = Object.keys(this.dex.data.Items); + const abilityPool = Object.keys(this.dex.data.Abilities); + const movePool = Object.keys(this.dex.data.Movedex); + const naturePool = Object.keys(this.dex.data.Natures); - let random6 = this.random6Pokemon(); + const random6 = this.random6Pokemon(); for (let i = 0; i < 6; i++) { // Choose forme - let species = this.dex.getSpecies(random6[i]); + const species = this.dex.getSpecies(random6[i]); // Random unique item let item = ''; @@ -361,43 +327,47 @@ class RandomTeams { } // Random unique moves - let m = []; + const m = []; do { - let moveid = this.sampleNoReplace(movePool); + const moveid = this.sampleNoReplace(movePool); if (this.dex.getMove(moveid).gen <= this.gen && !this.dex.data.Movedex[moveid].isNonstandard && (moveid === 'hiddenpower' || moveid.substr(0, 11) !== 'hiddenpower')) { m.push(moveid); } } while (m.length < 4); // Random EVs - let evs = {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0}; - let s = ['hp', 'atk', 'def', 'spa', 'spd', 'spe']; + const evs = {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0}; + const s: StatName[] = ['hp', 'atk', 'def', 'spa', 'spd', 'spe']; if (this.gen === 6) { let evpool = 510; do { - let x = this.sample(s); - // @ts-ignore - let y = this.random(Math.min(256 - evs[x], evpool + 1)); - // @ts-ignore + const x = this.sample(s); + const y = this.random(Math.min(256 - evs[x], evpool + 1)); evs[x] += y; evpool -= y; } while (evpool > 0); } else { for (const x of s) { - // @ts-ignore evs[x] = this.random(256); } } // Random IVs - let ivs = {hp: this.random(32), atk: this.random(32), def: this.random(32), spa: this.random(32), spd: this.random(32), spe: this.random(32)}; + const ivs: StatsTable = { + hp: this.random(32), + atk: this.random(32), + def: this.random(32), + spa: this.random(32), + spd: this.random(32), + spe: this.random(32), + }; // Random nature - let nature = this.sample(naturePool); + const nature = this.sample(naturePool); // Level balance - let mbstmin = 1307; - let stats = species.baseStats; + const mbstmin = 1307; + const stats = species.baseStats; let mbst = (stats['hp'] * 2 + 31 + 21 + 100) + 10; mbst += (stats['atk'] * 2 + 31 + 21 + 100) + 5; mbst += (stats['def'] * 2 + 31 + 21 + 100) + 5; @@ -417,10 +387,10 @@ class RandomTeams { } // Random happiness - let happiness = this.random(256); + const happiness = this.random(256); // Random shininess - let shiny = this.randomChance(1, 1024); + const shiny = this.randomChance(1, 1024); team.push({ name: species.baseSpecies, @@ -441,68 +411,60 @@ class RandomTeams { return team; } - /** - * @param {?string[]} moves - * @param {{[k: string]: boolean}} [hasType] - * @param {{[k: string]: boolean}} [hasAbility] - * @param {string[]} [movePool] - */ - queryMoves(moves, hasType = {}, hasAbility = {}, movePool = []) { + queryMoves(moves: string[] | null, hasType: {[k: string]: boolean} = {}, hasAbility: {[k: string]: boolean} = {}, movePool: string[] = []) { // This is primarily a helper function for random setbuilder functions. - let counter = { + const counter: {[k: string]: any} = { Physical: 0, Special: 0, Status: 0, damage: 0, recovery: 0, stab: 0, inaccurate: 0, priority: 0, recoil: 0, drain: 0, sound: 0, adaptability: 0, contrary: 0, ironfist: 0, serenegrace: 0, sheerforce: 0, skilllink: 0, strongjaw: 0, technician: 0, physicalsetup: 0, specialsetup: 0, mixedsetup: 0, speedsetup: 0, physicalpool: 0, specialpool: 0, hazards: 0, - /**@type {Move[]} */ damagingMoves: [], - /**@type {{[k: string]: number}} */ damagingMoveIndex: {}, setupType: '', Bug: 0, Dark: 0, Dragon: 0, Electric: 0, Fairy: 0, Fighting: 0, Fire: 0, Flying: 0, Ghost: 0, Grass: 0, Ground: 0, Ice: 0, Normal: 0, Poison: 0, Psychic: 0, Rock: 0, Steel: 0, Water: 0, }; - for (let type in Dex.data.TypeChart) { - // @ts-ignore - counter[type] = 0; + let typeDef: string; + for (typeDef in this.dex.data.TypeChart) { + counter[typeDef] = 0; } if (!moves || !moves.length) return counter; // Moves that restore HP: - let RecoveryMove = [ + const RecoveryMove = [ 'healorder', 'milkdrink', 'moonlight', 'morningsun', 'recover', 'roost', 'shoreup', 'slackoff', 'softboiled', 'strengthsap', 'synthesis', ]; // Moves which drop stats: - let ContraryMove = [ + const ContraryMove = [ 'closecombat', 'leafstorm', 'overheat', 'superpower', 'vcreate', ]; // Moves that boost Attack: - let PhysicalSetup = [ + const PhysicalSetup = [ 'bellydrum', 'bulkup', 'coil', 'curse', 'dragondance', 'honeclaws', 'howl', 'poweruppunch', 'swordsdance', ]; // Moves which boost Special Attack: - let SpecialSetup = [ + const SpecialSetup = [ 'calmmind', 'chargebeam', 'geomancy', 'nastyplot', 'quiverdance', 'tailglow', ]; // Moves which boost Attack AND Special Attack: - let MixedSetup = [ + const MixedSetup = [ 'clangoroussoul', 'growth', 'happyhour', 'holdhands', 'noretreat', 'shellsmash', 'workup', ]; // Moves which boost Speed: - let SpeedSetup = [ + const SpeedSetup = [ 'agility', 'autotomize', 'flamecharge', 'rockpolish', 'shiftgear', ]; // Moves that shouldn't be the only STAB moves: - let NoStab = [ + const NoStab = [ 'accelerock', 'aquajet', 'bounce', 'explosion', 'fakeout', 'firstimpression', 'flamecharge', 'iceshard', 'machpunch', 'pluck', 'pursuit', 'quickattack', 'selfdestruct', 'suckerpunch', 'watershuriken', 'clearsmog', 'eruption', 'vacuumwave', 'waterspout', ]; // Iterate through all moves we've chosen so far and keep track of what they do: for (const [k, moveId] of moves.entries()) { - let move = this.dex.getMove(moveId); - let moveid = move.id; + const move = this.dex.getMove(moveId); + const moveid = move.id; let movetype = move.type; if (['judgment', 'multiattack', 'revelationdance'].includes(moveid)) movetype = Object.keys(hasType)[0]; if (move.damage || move.damageCallback) { @@ -522,7 +484,6 @@ class RandomTeams { if (move.drain) counter['drain']++; // Moves which have a base power, but aren't super-weak like Rapid Spin: if (move.basePower > 30 || move.multihit || move.basePowerCallback || moveid === 'infestation' || moveid === 'naturepower') { - // @ts-ignore counter[movetype]++; if (hasType[movetype] || movetype === 'Normal' && (hasAbility['Aerilate'] || hasAbility['Galvanize'] || hasAbility['Pixilate'] || hasAbility['Refrigerate'])) { counter['adaptability']++; @@ -573,7 +534,7 @@ class RandomTeams { // Keep track of the available moves for (const moveid of movePool) { - let move = this.dex.getMove(moveid); + const move = this.dex.getMove(moveid); if (move.damageCallback) continue; if (move.category === 'Physical') counter['physicalpool']++; if (move.category === 'Special') counter['specialpool']++; @@ -583,7 +544,7 @@ class RandomTeams { if (counter['mixedsetup']) { counter.setupType = 'Mixed'; } else if (counter['physicalsetup'] && counter['specialsetup']) { - let pool = { + const pool = { Physical: counter.Physical + counter['physicalpool'], Special: counter.Special + counter['specialpool'], }; @@ -609,45 +570,34 @@ class RandomTeams { return counter; } - /** - * @param {string | Species} species - * @param {RandomTeamsTypes.TeamDetails} [teamDetails] - * @param {boolean} [isLead] - * @param {boolean} [isDoubles] - * @return {RandomTeamsTypes.RandomSet} - */ - randomSet(species, teamDetails = {}, isLead = false, isDoubles = false) { + randomSet(species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}, isLead = false, isDoubles = false): RandomTeamsTypes.RandomSet { species = this.dex.getSpecies(species); - let baseSpecies = species; + const baseSpecies = species; let forme = species.name; - if (species.battleOnly && !species.isGigantamax) { + if (species.battleOnly && !species.isGigantamax && typeof species.battleOnly === 'string') { // Only change the forme. The species has custom moves, and may have different typing and requirements. - forme = /** @type {string} */ (species.battleOnly); + forme = species.battleOnly; } const randMoves = !isDoubles ? species.randomBattleMoves : (species.randomDoubleBattleMoves || species.randomBattleMoves); - // @ts-ignore - let movePool = (randMoves || Object.keys(this.dex.data.Learnsets[species.id].learnset)).slice(); - let rejectedPool = []; - /**@type {string[]} */ - let moves = []; + const movePool = (randMoves || Object.keys(this.dex.data.Learnsets[species.id]!.learnset!)).slice(); + const rejectedPool = []; + const moves: string[] = []; let ability = ''; let item = ''; - let evs = { + const evs = { hp: 85, atk: 85, def: 85, spa: 85, spd: 85, spe: 85, }; - let ivs = { + const ivs = { hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31, }; - /**@type {{[k: string]: true}} */ - let hasType = {}; + const hasType: {[k: string]: true} = {}; hasType[species.types[0]] = true; if (species.types[1]) { hasType[species.types[1]] = true; } - /**@type {{[k: string]: true}} */ - let hasAbility = {}; + const hasAbility: {[k: string]: true} = {}; hasAbility[species.abilities[0]] = true; if (species.abilities[1]) { hasAbility[species.abilities[1]] = true; @@ -656,8 +606,7 @@ class RandomTeams { hasAbility[species.abilities['H']] = true; } - /**@type {{[k: string]: boolean}} */ - let hasMove = {}; + let hasMove: {[k: string]: boolean} = {}; let counter; do { @@ -668,9 +617,9 @@ class RandomTeams { } // Choose next 4 moves from learnset/viable moves and add them to moves list: - let pool = (movePool.length ? movePool : rejectedPool); + const pool = (movePool.length ? movePool : rejectedPool); while (moves.length < 4 && pool.length) { - let moveid = this.sampleNoReplace(pool); + const moveid = this.sampleNoReplace(pool); hasMove[moveid] = true; moves.push(moveid); } @@ -679,8 +628,8 @@ class RandomTeams { // Iterate through the moves again, this time to cull them: for (const [k, moveId] of moves.entries()) { - let move = this.dex.getMove(moveId); - let moveid = move.id; + const move = this.dex.getMove(moveId); + const moveid = move.id; let rejected = false; let isSetup = false; @@ -703,7 +652,7 @@ class RandomTeams { case 'sleeptalk': if (!hasMove['rest']) rejected = true; if (movePool.length > 1 && !hasAbility['Contrary']) { - let rest = movePool.indexOf('rest'); + const rest = movePool.indexOf('rest'); if (rest >= 0) this.fastPop(movePool, rest); } break; @@ -962,15 +911,16 @@ class RandomTeams { // This move doesn't satisfy our setup requirements: if ((move.category === 'Physical' && counter.setupType === 'Special') || (move.category === 'Special' && counter.setupType === 'Physical')) { // Reject STABs last in case the setup type changes later on - // @ts-ignore - let stabs = counter[species.types[0]] + (counter[species.types[1]] || 0); + const stabs: number = counter[species.types[0]] + (counter[species.types[1]] || 0); if (!hasType[move.type] || stabs > 1 || counter[move.category] < 2) rejected = true; } // Pokemon should have moves that benefit their types, stats, or ability - // @ts-ignore - if (!rejected && !['sleeptalk', 'teleport'].includes(moveid) && (counter['physicalsetup'] + counter['specialsetup'] < 2 && (!counter.setupType || counter.setupType === 'Mixed' || (move.category !== counter.setupType && move.category !== 'Status') || (counter[counter.setupType] + counter.Status > 3 && !counter.hazards))) && - ((!counter.stab && counter['physicalpool'] + counter['specialpool'] > 0) || + if (!rejected && !['sleeptalk', 'teleport'].includes(moveid) && ( + counter['physicalsetup'] + counter['specialsetup'] < 2 && + (!counter.setupType || counter.setupType === 'Mixed' || (move.category !== counter.setupType && move.category !== 'Status') || (counter[counter.setupType] + counter.Status > 3 && !counter.hazards)) + ) && ( + (!counter.stab && counter['physicalpool'] + counter['specialpool'] > 0) || (hasType['Bug'] && movePool.includes('megahorn')) || (hasType['Dark'] && !counter['Dark']) || (hasType['Dragon'] && !counter['Dragon'] && !(hasMove['rest'] && hasMove['sleeptalk'])) || @@ -991,17 +941,22 @@ class RandomTeams { (hasType['Water'] && !counter['Water']) || ((hasAbility['Moody'] || hasMove['wish']) && movePool.includes('protect')) || (((hasMove['lightscreen'] && movePool.includes('reflect')) || (hasMove['reflect'] && movePool.includes('lightscreen'))) && move.id !== 'teleport') || - (!counter['recovery'] && !counter.setupType && !hasMove['trick'] && !hasMove['trickroom'] && (movePool.includes('recover') || movePool.includes('roost') || movePool.includes('slackoff') || movePool.includes('softboiled')) && !!counter.Status) || - (movePool.includes('quiverdance') || movePool.includes('stickyweb') && !counter.setupType && !teamDetails.stickyWeb))) { + (!counter['recovery'] && !counter.setupType && !hasMove['trick'] && !hasMove['trickroom'] && ( + movePool.includes('recover') || movePool.includes('roost') || movePool.includes('slackoff') || movePool.includes('softboiled') + ) && !!counter.Status) || + (movePool.includes('quiverdance') || movePool.includes('stickyweb') && !counter.setupType && !teamDetails.stickyWeb) + )) { // Reject Status or non-STAB if (!isSetup && !move.weather && !move.sideCondition && !move.stallingMove && !move.damage && (move.category !== 'Status' || !move.flags.heal)) { - if (move.category === 'Status' || move.selfSwitch || !hasType[move.type] || move.basePower && move.basePower < 50 && !move.multihit) rejected = true; + if (move.category === 'Status' || move.selfSwitch || !hasType[move.type] || move.basePower && move.basePower < 50 && !move.multihit) { + rejected = true; + } } } // Sleep Talk shouldn't be selected without Rest if (moveid === 'rest' && rejected) { - let sleeptalk = movePool.indexOf('sleeptalk'); + const sleeptalk = movePool.indexOf('sleeptalk'); if (sleeptalk >= 0) { if (movePool.length < 2) { rejected = false; @@ -1032,8 +987,7 @@ class RandomTeams { hasMove['calmmind'] = true; } - // @ts-ignore - let abilities = Object.values(baseSpecies.abilities); + const abilities: string[] = Object.values(baseSpecies.abilities); abilities.sort((a, b) => this.dex.getAbility(b).rating - this.dex.getAbility(a).rating); let ability0 = this.dex.getAbility(abilities[0]); let ability1 = this.dex.getAbility(abilities[1]); @@ -1049,13 +1003,12 @@ class RandomTeams { } ability = ability0.name; - let rejectAbility; + let rejectAbility: boolean; do { rejectAbility = false; if (['Flare Boost', 'Hydration', 'Ice Body', 'Inner Focus', 'Insomnia', 'Liquid Voice', 'Misty Surge', 'Quick Feet', 'Rain Dish', 'Snow Cloak', 'Weak Armor'].includes(ability)) { rejectAbility = true; } else if (['Adaptability', 'Contrary', 'Serene Grace', 'Skill Link', 'Strong Jaw'].includes(ability)) { - // @ts-ignore rejectAbility = !counter[toID(ability)]; } else if (ability === 'Bulletproof' || ability === 'Overcoat') { rejectAbility = (counter.setupType && hasAbility['Soundproof']); @@ -1266,8 +1219,14 @@ class RandomTeams { item = 'Air Balloon'; } else if (counter.damagingMoves.length >= 4 && !counter['Dark'] && !counter['Dragon'] && !counter['Normal']) { item = 'Expert Belt'; - } else if (counter.damagingMoves.length >= 3 && ability !== 'Sturdy' && !hasMove['dragontail'] && !hasMove['foulplay'] && !hasMove['rapidspin'] && !hasMove['substitute'] && !hasMove['uturn']) { - if (!!counter['speedsetup'] || hasMove['trickroom'] || !!counter['drain'] || hasMove['psystrike'] || species.baseStats.spe > 40 && species.baseStats.hp + species.baseStats.def + species.baseStats.spd < 275) item = 'Life Orb'; + } else if ( + counter.damagingMoves.length >= 3 && ability !== 'Sturdy' && !hasMove['dragontail'] && !hasMove['foulplay'] && + !hasMove['rapidspin'] && !hasMove['substitute'] && !hasMove['uturn'] + ) { + if (!!counter['speedsetup'] || hasMove['trickroom'] || !!counter['drain'] || hasMove['psystrike'] || + species.baseStats.spe > 40 && species.baseStats.hp + species.baseStats.def + species.baseStats.spd < 275) { + item = 'Life Orb'; + } } // For Trick / Switcheroo @@ -1275,22 +1234,20 @@ class RandomTeams { item = 'Black Sludge'; } - let level; + let level: number; if (!isDoubles) { - /** @type {{[tier: string]: number}} */ - const levelScale = { + const levelScale: {[tier: string]: number} = { uber: 72, ou: 80, uu: 82, ru: 84, nu: 86, pu: 88, }; - /** @type {{[species: string]: number}} */ - const customScale = { + const customScale: {[species: string]: number} = { Glalie: 72, 'Darmanitan-Galar-Zen': 80, Wobbuffet: 80, Zygarde: 80, Delibird: 100, Shedinja: 100, }; let tier = toID((species.isGigantamax ? this.dex.getSpecies(species.baseSpecies) : species).tier).replace('bl', ''); // For future DLC Pokemon if (tier === 'illegal') { - tier = toID(Dex.mod('gen7').getSpecies(species.name).tier); + tier = toID(this.dex.mod('gen7').getSpecies(species.name).tier); switch (tier) { case 'uubl': case 'uu': tier = 'ou'; @@ -1308,12 +1265,12 @@ class RandomTeams { } else { // We choose level based on BST. Min level is 70, max level is 99. 600+ BST is 70, less than 300 is 99. Calculate with those values. // Every 10.34 BST adds a level from 70 up to 99. Results are floored. - let baseStats = species.baseStats; + const baseStats = species.baseStats; let bst = baseStats.hp + baseStats.atk + baseStats.def + baseStats.spa + baseStats.spd + baseStats.spe; // Adjust levels of mons based on abilities (Pure Power, Sheer Force, etc.) and also Eviolite // For the stat boosted, treat the Pokemon's base stat as if it were multiplied by the boost. (Actual effective base stats are higher.) - let speciesAbility = (baseSpecies === species ? ability : species.abilities[0]); + const speciesAbility = (baseSpecies === species ? ability : species.abilities[0]); if (speciesAbility === 'Huge Power' || speciesAbility === 'Pure Power') { bst += baseStats.atk; } @@ -1326,9 +1283,9 @@ class RandomTeams { } // Prepare optimal HP - let srWeakness = this.dex.getEffectiveness('Rock', species); + const srWeakness = this.dex.getEffectiveness('Rock', species); while (evs.hp > 1) { - let hp = Math.floor(Math.floor(2 * species.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10); + const hp = Math.floor(Math.floor(2 * species.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10); if (hasMove['substitute'] && hasMove['reversal']) { // Reversal users should be able to use four Substitutes if (hp % 4 > 0) break; @@ -1370,24 +1327,19 @@ class RandomTeams { }; } - /** - * @param {string} type - * @param {RandomTeamsTypes.RandomSet[]} pokemon - * @param {boolean=} isMonotype - */ - getPokemonPool(type, pokemon = [], isMonotype = false) { + getPokemonPool(type: string, pokemon: RandomTeamsTypes.RandomSet[] = [], isMonotype = false) { const exclude = pokemon.map(p => toID(p.species)); const pokemonPool = []; - for (let id in this.dex.data.FormatsData) { + for (const id in this.dex.data.FormatsData) { let species = this.dex.getSpecies(id); if (species.gen > this.gen || exclude.includes(species.id)) continue; if (species.isMega || species.isPrimal) continue; if (!species.randomBattleMoves) continue; if (isMonotype) { - if (species.types.indexOf(type) < 0) continue; - if (species.battleOnly) { - species = this.dex.getSpecies(/** @type {string} */ (species.battleOnly)); - if (species.types.indexOf(type) < 0) continue; + if (!species.types.includes(type)) continue; + if (species.battleOnly && typeof species.battleOnly === 'string') { + species = this.dex.getSpecies(species.battleOnly); + if (!species.types.includes(type)) continue; } } pokemonPool.push(id); @@ -1398,7 +1350,7 @@ class RandomTeams { randomTeam() { const seed = this.prng.seed; const ruleTable = this.dex.getRuleTable(this.format); - let pokemon = []; + const pokemon = []; // For Monotype const isMonotype = ruleTable.has('sametypeclause'); @@ -1406,21 +1358,16 @@ class RandomTeams { const type = this.sample(typePool); // PotD stuff - let potd; + let potd: Species | false = false; if (global.Config && Config.potd && ruleTable.has('potd')) { potd = this.dex.getSpecies(Config.potd); } - /**@type {{[k: string]: number}} */ - let baseFormes = {}; - /**@type {{[k: string]: number}} */ - let tierCount = {}; - /**@type {{[k: string]: number}} */ - let typeCount = {}; - /**@type {{[k: string]: number}} */ - let typeComboCount = {}; - /**@type {RandomTeamsTypes.TeamDetails} */ - let teamDetails = {}; + const baseFormes: {[k: string]: number} = {}; + const tierCount: {[k: string]: number} = {}; + const typeCount: {[k: string]: number} = {}; + const typeComboCount: {[k: string]: number} = {}; + const teamDetails: RandomTeamsTypes.TeamDetails = {}; // We make at most two passes through the potential Pokemon pool when creating a team - if the first pass doesn't // result in a team of six Pokemon we perform a second iteration relaxing as many restrictions as possible. @@ -1434,9 +1381,9 @@ class RandomTeams { // Limit to one of each species (Species Clause) if (baseFormes[species.baseSpecies]) continue; - let tier = species.tier; - let types = species.types; - let typeCombo = types.slice().sort().join(); + const tier = species.tier; + const types = species.types; + const typeCombo = types.slice().sort().join(); // Adjust rate for species with multiple formes switch (species.baseSpecies) { @@ -1452,17 +1399,20 @@ class RandomTeams { case 'Castform': case 'Kyurem': case 'Lycanroc': case 'Necrozma': case 'Wormadam': if (this.randomChance(2, 3)) continue; break; - case 'Aegislash': case 'Basculin': case 'Cherrim': case 'Floette': case 'Giratina': case 'Hoopa': case 'Landorus': case 'Meloetta': case 'Meowstic': case 'Shaymin': case 'Thundurus': case 'Tornadus': + case 'Aegislash': case 'Basculin': case 'Cherrim': case 'Floette': case 'Giratina': case 'Hoopa': + case 'Landorus': case 'Meloetta': case 'Meowstic': case 'Shaymin': case 'Thundurus': case 'Tornadus': if (this.randomChance(1, 2)) continue; break; - case 'Dugtrio': case 'Exeggutor': case 'Golem': case 'Greninja': case 'Marowak': case 'Muk': case 'Ninetales': case 'Persian': case 'Raichu': case 'Sandslash': case 'Zygarde': + case 'Dugtrio': case 'Exeggutor': case 'Golem': case 'Greninja': case 'Marowak': case 'Muk': + case 'Ninetales': case 'Persian': case 'Raichu': case 'Sandslash': case 'Zygarde': if (this.gen >= 7 && this.randomChance(1, 2)) continue; break; case 'Darmanitan': if (this.gen >= 8 && this.randomChance(2, 3)) continue; break; - case 'Corsola': case 'Indeedee': case 'Mr. Mime': case 'Rapidash': case 'Stunfisk': case 'Toxtricity': case 'Weezing': case 'Zacian': case 'Zamazenta': - case 'Appletun': case 'Butterfree': case 'Copperajah': case 'Grimmsnarl': + case 'Corsola': case 'Indeedee': case 'Mr. Mime': case 'Rapidash': case 'Stunfisk': case 'Toxtricity': + case 'Weezing': case 'Zacian': case 'Zamazenta': case 'Appletun': case 'Butterfree': case 'Copperajah': + case 'Grimmsnarl': if (this.gen >= 8 && this.randomChance(1, 2)) continue; break; } @@ -1476,8 +1426,8 @@ class RandomTeams { if (!isMonotype) { // Limit two of any type let skip = false; - for (const type of types) { - if (typeCount[type] > 1) { + for (const typeName of types) { + if (typeCount[typeName] > 1) { skip = true; break; } @@ -1490,11 +1440,11 @@ class RandomTeams { } // The Pokemon of the Day - if (potd && potd.exists && pokemon.length < 1) species = potd; + if (!!potd && potd.exists && pokemon.length < 1) species = potd; - let set = this.randomSet(species, teamDetails, pokemon.length === 5, this.format.gameType !== 'singles'); + const set = this.randomSet(species, teamDetails, pokemon.length === 5, this.format.gameType !== 'singles'); - let item = this.dex.getItem(set.item); + const item = this.dex.getItem(set.item); // Limit 1 Z-Move per team if (item.zMove && teamDetails['zMove']) continue; @@ -1522,11 +1472,11 @@ class RandomTeams { } // Increment type counters - for (const type of types) { - if (type in typeCount) { - typeCount[type]++; + for (const typeName of types) { + if (typeName in typeCount) { + typeCount[typeName]++; } else { - typeCount[type] = 1; + typeCount[typeName] = 1; } } if (typeCombo in typeComboCount) { @@ -1556,4 +1506,4 @@ class RandomTeams { } } -module.exports = RandomTeams; +export default RandomTeams; diff --git a/data/rulesets.js b/data/rulesets.ts similarity index 85% rename from data/rulesets.js rename to data/rulesets.ts index 2861b798ad..05af3bd1fc 100644 --- a/data/rulesets.js +++ b/data/rulesets.ts @@ -1,10 +1,7 @@ // Note: These are the rules that formats use // The list of formats is stored in config/formats.js -'use strict'; - -/**@type {{[k: string]: FormatsData}} */ -let BattleFormats = { +export const BattleFormats: {[k: string]: FormatsData} = { // Rulesets /////////////////////////////////////////////////////////////////// @@ -13,13 +10,17 @@ let BattleFormats = { effectType: 'ValidatorRule', name: 'Standard', desc: "The standard ruleset for all offical Smogon singles tiers (Ubers, OU, etc.)", - ruleset: ['Obtainable', 'Team Preview', 'Sleep Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod'], + ruleset: [ + 'Obtainable', 'Team Preview', 'Sleep Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod', + ], }, standardnext: { effectType: 'ValidatorRule', name: 'Standard NEXT', desc: "The standard ruleset for the NEXT mod", - ruleset: ['+Unreleased', 'Sleep Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'HP Percentage Mod', 'Cancel Mod'], + ruleset: [ + '+Unreleased', 'Sleep Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'HP Percentage Mod', 'Cancel Mod', + ], banlist: ['Soul Dew'], }, standardgbu: { @@ -67,13 +68,17 @@ let BattleFormats = { effectType: 'ValidatorRule', name: 'Standard Doubles', desc: "The standard ruleset for all official Smogon doubles tiers", - ruleset: ['Obtainable', 'Team Preview', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Gravity Sleep Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod'], + ruleset: [ + 'Obtainable', 'Team Preview', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Gravity Sleep Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod', + ], }, standardnatdex: { effectType: 'ValidatorRule', name: 'Standard NatDex', desc: "The standard ruleset for all National Dex tiers", - ruleset: ['Obtainable', '+Unobtainable', '+Past', 'Team Preview', 'Nickname Clause', 'HP Percentage Mod', 'Cancel Mod', 'Endless Battle Clause'], + ruleset: [ + 'Obtainable', '+Unobtainable', '+Past', 'Team Preview', 'Nickname Clause', 'HP Percentage Mod', 'Cancel Mod', 'Endless Battle Clause', + ], onValidateSet(set) { // These Pokemon are still unobtainable const unobtainables = [ @@ -84,8 +89,9 @@ let BattleFormats = { } // Items other than Z-Crystals and Pokémon-specific items should be illegal if (!set.item) return; - let item = this.dex.getItem(set.item); - if ((item.isNonstandard === 'Unobtainable' || item.isNonstandard === 'Past') && !item.zMove && !item.itemUser && !item.forcedForme) { + const item = this.dex.getItem(set.item); + if (!item.isNonstandard) return; + if (['Past', 'Unobtainable'].includes(item.isNonstandard) && !item.zMove && !item.itemUser && !item.forcedForme) { return [`${set.name}'s item ${item.name} does not exist in Gen ${this.dex.gen}.`]; } }, @@ -153,7 +159,7 @@ let BattleFormats = { desc: "Validate all obtainability things that aren't moves/abilities (Hidden Power type, gender, stats, etc).", // Mostly hardcoded in team-validator.ts onChangeSet(set) { - let species = this.dex.getSpecies(set.species); + const species = this.dex.getSpecies(set.species); if (species.gender) { if (set.gender !== species.gender) { @@ -166,13 +172,12 @@ let BattleFormats = { } // limit one of each move - let moves = []; + const moves = []; if (set.moves) { - /**@type {{[k: string]: true}} */ - let hasMove = {}; + const hasMove: {[k: string]: true} = {}; for (const moveId of set.moves) { - let move = this.dex.getMove(moveId); - let moveid = move.id; + const move = this.dex.getMove(moveId); + const moveid = move.id; if (hasMove[moveid]) continue; hasMove[moveid] = true; moves.push(moveId); @@ -186,10 +191,10 @@ let BattleFormats = { name: 'Hoenn Pokedex', desc: "Only allows Pokémon native to the Hoenn region (OR/AS)", onValidateSet(set, format) { - let hoennDex = [ + const hoennDex = [ "Abra", "Absol", "Aggron", "Alakazam", "Altaria", "Anorith", "Armaldo", "Aron", "Azumarill", "Azurill", "Bagon", "Baltoy", "Banette", "Barboach", "Beautifly", "Beldum", "Bellossom", "Blaziken", "Breloom", "Budew", "Cacnea", "Cacturne", "Camerupt", "Carvanha", "Cascoon", "Castform", "Chimecho", "Chinchou", "Chingling", "Clamperl", "Claydol", "Combusken", "Corphish", "Corsola", "Cradily", "Crawdaunt", "Crobat", "Delcatty", "Dodrio", "Doduo", "Donphan", "Dusclops", "Dusknoir", "Duskull", "Dustox", "Electrike", "Electrode", "Exploud", "Feebas", "Flygon", "Froslass", "Gallade", "Gardevoir", "Geodude", "Girafarig", "Glalie", "Gloom", "Golbat", "Goldeen", "Golduck", "Golem", "Gorebyss", "Graveler", "Grimer", "Grovyle", "Grumpig", "Gulpin", "Gyarados", "Hariyama", "Heracross", "Horsea", "Huntail", "Igglybuff", "Illumise", "Jigglypuff", "Kadabra", "Kecleon", "Kingdra", "Kirlia", "Koffing", "Lairon", "Lanturn", "Latias", "Latios", "Lileep", "Linoone", "Lombre", "Lotad", "Loudred", "Ludicolo", "Lunatone", "Luvdisc", "Machamp", "Machoke", "Machop", "Magcargo", "Magikarp", "Magnemite", "Magneton", "Magnezone", "Makuhita", "Manectric", "Marill", "Marshtomp", "Masquerain", "Mawile", "Medicham", "Meditite", "Metagross", "Metang", "Mightyena", "Milotic", "Minun", "Mudkip", "Muk", "Natu", "Nincada", "Ninetales", "Ninjask", "Nosepass", "Numel", "Nuzleaf", "Oddish", "Pelipper", "Phanpy", "Pichu", "Pikachu", "Pinsir", "Plusle", "Poochyena", "Probopass", "Psyduck", "Raichu", "Ralts", "Regice", "Regirock", "Registeel", "Relicanth", "Rhydon", "Rhyhorn", "Rhyperior", "Roselia", "Roserade", "Sableye", "Salamence", "Sandshrew", "Sandslash", "Sceptile", "Seadra", "Seaking", "Sealeo", "Seedot", "Seviper", "Sharpedo", "Shedinja", "Shelgon", "Shiftry", "Shroomish", "Shuppet", "Silcoon", "Skarmory", "Skitty", "Slaking", "Slakoth", "Slugma", "Snorunt", "Solrock", "Spheal", "Spinda", "Spoink", "Starmie", "Staryu", "Surskit", "Swablu", "Swalot", "Swampert", "Swellow", "Taillow", "Tentacool", "Tentacruel", "Torchic", "Torkoal", "Trapinch", "Treecko", "Tropius", "Vibrava", "Vigoroth", "Vileplume", "Volbeat", "Voltorb", "Vulpix", "Wailmer", "Wailord", "Walrein", "Weezing", "Whiscash", "Whismur", "Wigglytuff", "Wingull", "Wobbuffet", "Wurmple", "Wynaut", "Xatu", "Zangoose", "Zigzagoon", "Zubat", ]; - let species = this.dex.getSpecies(set.species || set.name); + const species = this.dex.getSpecies(set.species || set.name); if (!hoennDex.includes(species.baseSpecies) && !this.ruleTable.has('+' + species.id)) { return [species.baseSpecies + " is not in the Hoenn Pokédex."]; } @@ -200,10 +205,10 @@ let BattleFormats = { name: 'Sinnoh Pokedex', desc: "Only allows Pokémon native to the Sinnoh region (Platinum)", onValidateSet(set, format) { - let sinnohDex = [ + const sinnohDex = [ "Turtwig", "Grotle", "Torterra", "Chimchar", "Monferno", "Infernape", "Piplup", "Prinplup", "Empoleon", "Starly", "Staravia", "Staraptor", "Bidoof", "Bibarel", "Kricketot", "Kricketune", "Shinx", "Luxio", "Luxray", "Abra", "Kadabra", "Alakazam", "Magikarp", "Gyarados", "Budew", "Roselia", "Roserade", "Zubat", "Golbat", "Crobat", "Geodude", "Graveler", "Golem", "Onix", "Steelix", "Cranidos", "Rampardos", "Shieldon", "Bastiodon", "Machop", "Machoke", "Machamp", "Psyduck", "Golduck", "Burmy", "Wormadam", "Mothim", "Wurmple", "Silcoon", "Beautifly", "Cascoon", "Dustox", "Combee", "Vespiquen", "Pachirisu", "Buizel", "Floatzel", "Cherubi", "Cherrim", "Shellos", "Gastrodon", "Heracross", "Aipom", "Ambipom", "Drifloon", "Drifblim", "Buneary", "Lopunny", "Gastly", "Haunter", "Gengar", "Misdreavus", "Mismagius", "Murkrow", "Honchkrow", "Glameow", "Purugly", "Goldeen", "Seaking", "Barboach", "Whiscash", "Chingling", "Chimecho", "Stunky", "Skuntank", "Meditite", "Medicham", "Bronzor", "Bronzong", "Ponyta", "Rapidash", "Bonsly", "Sudowoodo", "Mime Jr.", "Mr. Mime", "Happiny", "Chansey", "Blissey", "Cleffa", "Clefairy", "Clefable", "Chatot", "Pichu", "Pikachu", "Raichu", "Hoothoot", "Noctowl", "Spiritomb", "Gible", "Gabite", "Garchomp", "Munchlax", "Snorlax", "Unown", "Riolu", "Lucario", "Wooper", "Quagsire", "Wingull", "Pelipper", "Girafarig", "Hippopotas", "Hippowdon", "Azurill", "Marill", "Azumarill", "Skorupi", "Drapion", "Croagunk", "Toxicroak", "Carnivine", "Remoraid", "Octillery", "Finneon", "Lumineon", "Tentacool", "Tentacruel", "Feebas", "Milotic", "Mantyke", "Mantine", "Snover", "Abomasnow", "Sneasel", "Weavile", "Uxie", "Mesprit", "Azelf", "Dialga", "Palkia", "Manaphy", "Rotom", "Gligar", "Gliscor", "Nosepass", "Probopass", "Ralts", "Kirlia", "Gardevoir", "Gallade", "Lickitung", "Lickilicky", "Eevee", "Vaporeon", "Jolteon", "Flareon", "Espeon", "Umbreon", "Leafeon", "Glaceon", "Swablu", "Altaria", "Togepi", "Togetic", "Togekiss", "Houndour", "Houndoom", "Magnemite", "Magneton", "Magnezone", "Tangela", "Tangrowth", "Yanma", "Yanmega", "Tropius", "Rhyhorn", "Rhydon", "Rhyperior", "Duskull", "Dusclops", "Dusknoir", "Porygon", "Porygon2", "Porygon-Z", "Scyther", "Scizor", "Elekid", "Electabuzz", "Electivire", "Magby", "Magmar", "Magmortar", "Swinub", "Piloswine", "Mamoswine", "Snorunt", "Glalie", "Froslass", "Absol", "Giratina", ]; - let species = this.dex.getSpecies(set.species || set.name); + const species = this.dex.getSpecies(set.species || set.name); if ((!sinnohDex.includes(species.baseSpecies) || species.gen > 4) && !this.ruleTable.has('+' + species.id)) { return [`${species.name} is not in the Sinnoh Pokédex.`]; } @@ -214,12 +219,12 @@ let BattleFormats = { name: 'Kalos Pokedex', desc: "Only allows Pokémon native to the Kalos region (XY)", onValidateSet(set, format) { - let kalosDex = [ + const kalosDex = [ "Chespin", "Quilladin", "Chesnaught", "Fennekin", "Braixen", "Delphox", "Froakie", "Frogadier", "Greninja", "Bunnelby", "Diggersby", "Zigzagoon", "Linoone", "Fletchling", "Fletchinder", "Talonflame", "Pidgey", "Pidgeotto", "Pidgeot", "Scatterbug", "Spewpa", "Vivillon", "Caterpie", "Metapod", "Butterfree", "Weedle", "Kakuna", "Beedrill", "Pansage", "Simisage", "Pansear", "Simisear", "Panpour", "Simipour", "Pichu", "Pikachu", "Raichu", "Bidoof", "Bibarel", "Dunsparce", "Azurill", "Marill", "Azumarill", "Burmy", "Wormadam", "Mothim", "Surskit", "Masquerain", "Magikarp", "Gyarados", "Corphish", "Crawdaunt", "Goldeen", "Seaking", "Carvanha", "Sharpedo", "Litleo", "Pyroar", "Psyduck", "Golduck", "Farfetch'd", "Riolu", "Lucario", "Ralts", "Kirlia", "Gardevoir", "Gallade", "Flabe\u0301be\u0301", "Floette", "Florges", "Budew", "Roselia", "Roserade", "Ledyba", "Ledian", "Combee", "Vespiquen", "Skitty", "Delcatty", "Bulbasaur", "Ivysaur", "Venusaur", "Charmander", "Charmeleon", "Charizard", "Squirtle", "Wartortle", "Blastoise", "Skiddo", "Gogoat", "Pancham", "Pangoro", "Furfrou", "Doduo", "Dodrio", "Plusle", "Minun", "Gulpin", "Swalot", "Scraggy", "Scrafty", "Abra", "Kadabra", "Alakazam", "Oddish", "Gloom", "Vileplume", "Bellossom", "Sentret", "Furret", "Nincada", "Ninjask", "Shedinja", "Espurr", "Meowstic", "Kecleon", "Honedge", "Doublade", "Aegislash", "Venipede", "Whirlipede", "Scolipede", "Audino", "Smeargle", "Croagunk", "Toxicroak", "Ducklett", "Swanna", "Spritzee", "Aromatisse", "Swirlix", "Slurpuff", "Volbeat", "Illumise", "Hoppip", "Skiploom", "Jumpluff", "Munchlax", "Snorlax", "Whismur", "Loudred", "Exploud", "Meditite", "Medicham", "Zubat", "Golbat", "Crobat", "Axew", "Fraxure", "Haxorus", "Diancie", "Hoopa", "Volcanion", "Drifloon", "Drifblim", "Mienfoo", "Mienshao", "Zangoose", "Seviper", "Spoink", "Grumpig", "Absol", "Inkay", "Malamar", "Lunatone", "Solrock", "Bagon", "Shelgon", "Salamence", "Wingull", "Pelipper", "Taillow", "Swellow", "Binacle", "Barbaracle", "Dwebble", "Crustle", "Tentacool", "Tentacruel", "Wailmer", "Wailord", "Luvdisc", "Skrelp", "Dragalge", "Clauncher", "Clawitzer", "Staryu", "Starmie", "Shellder", "Cloyster", "Qwilfish", "Horsea", "Seadra", "Kingdra", "Relicanth", "Sandile", "Krokorok", "Krookodile", "Helioptile", "Heliolisk", "Hippopotas", "Hippowdon", "Rhyhorn", "Rhydon", "Rhyperior", "Onix", "Steelix", "Woobat", "Swoobat", "Machop", "Machoke", "Machamp", "Cubone", "Marowak", "Kangaskhan", "Mawile", "Tyrunt", "Tyrantrum", "Amaura", "Aurorus", "Aerodactyl", "Ferroseed", "Ferrothorn", "Snubbull", "Granbull", "Electrike", "Manectric", "Houndour", "Houndoom", "Eevee", "Vaporeon", "Jolteon", "Flareon", "Espeon", "Umbreon", "Leafeon", "Glaceon", "Sylveon", "Emolga", "Yanma", "Yanmega", "Hawlucha", "Sigilyph", "Golett", "Golurk", "Nosepass", "Probopass", "Makuhita", "Hariyama", "Throh", "Sawk", "Starly", "Staravia", "Staraptor", "Stunky", "Skuntank", "Nidoran-F", "Nidorina", "Nidoqueen", "Nidoran-M", "Nidorino", "Nidoking", "Dedenne", "Chingling", "Chimecho", "Mime Jr.", "Mr. Mime", "Solosis", "Duosion", "Reuniclus", "Wynaut", "Wobbuffet", "Roggenrola", "Boldore", "Gigalith", "Sableye", "Carbink", "Tauros", "Miltank", "Mareep", "Flaaffy", "Ampharos", "Pinsir", "Heracross", "Pachirisu", "Slowpoke", "Slowbro", "Slowking", "Exeggcute", "Exeggutor", "Chatot", "Mantyke", "Mantine", "Clamperl", "Huntail", "Gorebyss", "Remoraid", "Octillery", "Corsola", "Chinchou", "Lanturn", "Alomomola", "Lapras", "Articuno", "Zapdos", "Moltres", "Diglett", "Dugtrio", "Trapinch", "Vibrava", "Flygon", "Gible", "Gabite", "Garchomp", "Geodude", "Graveler", "Golem", "Slugma", "Magcargo", "Shuckle", "Skorupi", "Drapion", "Wooper", "Quagsire", "Goomy", "Sliggoo", "Goodra", "Karrablast", "Escavalier", "Shelmet", "Accelgor", "Bellsprout", "Weepinbell", "Victreebel", "Carnivine", "Gastly", "Haunter", "Gengar", "Poliwag", "Poliwhirl", "Poliwrath", "Politoed", "Ekans", "Arbok", "Stunfisk", "Barboach", "Whiscash", "Purrloin", "Liepard", "Poochyena", "Mightyena", "Patrat", "Watchog", "Pawniard", "Bisharp", "Klefki", "Murkrow", "Honchkrow", "Foongus", "Amoonguss", "Lotad", "Lombre", "Ludicolo", "Buizel", "Floatzel", "Basculin", "Phantump", "Trevenant", "Pumpkaboo", "Gourgeist", "Litwick", "Lampent", "Chandelure", "Rotom", "Magnemite", "Magneton", "Magnezone", "Voltorb", "Electrode", "Trubbish", "Garbodor", "Swinub", "Piloswine", "Mamoswine", "Bergmite", "Avalugg", "Cubchoo", "Beartic", "Smoochum", "Jynx", "Vanillite", "Vanillish", "Vanilluxe", "Snover", "Abomasnow", "Delibird", "Sneasel", "Weavile", "Timburr", "Gurdurr", "Conkeldurr", "Torkoal", "Sandshrew", "Sandslash", "Aron", "Lairon", "Aggron", "Larvitar", "Pupitar", "Tyranitar", "Heatmor", "Durant", "Spinarak", "Ariados", "Spearow", "Fearow", "Cryogonal", "Skarmory", "Noibat", "Noivern", "Gligar", "Gliscor", "Hoothoot", "Noctowl", "Igglybuff", "Jigglypuff", "Wigglytuff", "Shuppet", "Banette", "Zorua", "Zoroark", "Gothita", "Gothorita", "Gothitelle", "Bonsly", "Sudowoodo", "Spinda", "Teddiursa", "Ursaring", "Lickitung", "Lickilicky", "Scyther", "Scizor", "Ditto", "Swablu", "Altaria", "Druddigon", "Deino", "Zweilous", "Hydreigon", "Dratini", "Dragonair", "Dragonite", "Xerneas", "Yveltal", "Zygarde", "Mewtwo", ]; - let species = this.dex.getSpecies(set.species || set.name); + const species = this.dex.getSpecies(set.species || set.name); if ((!kalosDex.includes(species.baseSpecies) || species.gen > 6) && !this.ruleTable.has('+' + species.id)) { return [`${species.name} is not in the Kalos Pokédex.`]; } @@ -230,11 +235,12 @@ let BattleFormats = { name: 'Alola Pokedex', desc: "Only allows Pokémon native to the Alola region (US/UM)", onValidateSet(set, format) { - let alolaDex = [ + const alolaDex = [ "Rowlet", "Dartrix", "Decidueye", "Litten", "Torracat", "Incineroar", "Popplio", "Brionne", "Primarina", "Pikipek", "Trumbeak", "Toucannon", "Yungoos", "Gumshoos", "Rattata-Alola", "Raticate-Alola", "Caterpie", "Metapod", "Butterfree", "Ledyba", "Ledian", "Spinarak", "Ariados", "Buneary", "Lopunny", "Inkay", "Malamar", "Zorua", "Zoroark", "Furfrou", "Pichu", "Pikachu", "Raichu-Alola", "Grubbin", "Charjabug", "Vikavolt", "Bonsly", "Sudowoodo", "Happiny", "Chansey", "Blissey", "Munchlax", "Snorlax", "Slowpoke", "Slowbro", "Slowking", "Wingull", "Pelipper", "Abra", "Kadabra", "Alakazam", "Meowth-Alola", "Persian-Alola", "Magnemite", "Magneton", "Magnezone", "Grimer-Alola", "Muk-Alola", "Mime Jr.", "Mr. Mime", "Ekans", "Arbok", "Dunsparce", "Growlithe", "Arcanine", "Drowzee", "Hypno", "Makuhita", "Hariyama", "Smeargle", "Crabrawler", "Crabominable", "Gastly", "Haunter", "Gengar", "Drifloon", "Drifblim", "Murkrow", "Honchkrow", "Zubat", "Golbat", "Crobat", "Noibat", "Noivern", "Diglett-Alola", "Dugtrio-Alola", "Spearow", "Fearow", "Rufflet", "Braviary", "Vullaby", "Mandibuzz", "Mankey", "Primeape", "Delibird", "Hawlucha", "Oricorio", "Cutiefly", "Ribombee", "Flabe\u0301be\u0301", "Floette", "Florges", "Petilil", "Lilligant", "Cottonee", "Whimsicott", "Psyduck", "Golduck", "Smoochum", "Jynx", "Magikarp", "Gyarados", "Barboach", "Whiscash", "Seal", "Dewgong", "Machop", "Machoke", "Machamp", "Roggenrola", "Boldore", "Gigalith", "Carbink", "Sableye", "Mawile", "Rockruff", "Lycanroc", "Spinda", "Tentacool", "Tentacruel", "Finneon", "Lumineon", "Wishiwashi", "Luvdisc", "Corsola", "Mareanie", "Toxapex", "Shellder", "Cloyster", "Clamperl", "Huntail", "Gorebyss", "Remoraid", "Octillery", "Mantyke", "Mantine", "Bagon", "Shelgon", "Salamence", "Lillipup", "Herdier", "Stoutland", "Eevee", "Vaporeon", "Jolteon", "Flareon", "Espeon", "Umbreon", "Leafeon", "Glaceon", "Sylveon", "Mareep", "Flaaffy", "Ampharos", "Mudbray", "Mudsdale", "Igglybuff", "Jigglypuff", "Wigglytuff", "Tauros", "Miltank", "Surskit", "Masquerain", "Dewpider", "Araquanid", "Fomantis", "Lurantis", "Morelull", "Shiinotic", "Paras", "Parasect", "Poliwag", "Poliwhirl", "Poliwrath", "Politoed", "Goldeen", "Seaking", "Basculin", "Feebas", "Milotic", "Alomomola", "Fletchling", "Fletchinder", "Talonflame", "Salandit", "Salazzle", "Cubone", "Marowak-Alola", "Kangaskhan", "Magby", "Magmar", "Magmortar", "Larvesta", "Volcarona", "Stufful", "Bewear", "Bounsweet", "Steenee", "Tsareena", "Comfey", "Pinsir", "Hoothoot", "Noctowl", "Kecleon", "Oranguru", "Passimian", "Goomy", "Sliggoo", "Goodra", "Castform", "Wimpod", "Golisopod", "Staryu", "Starmie", "Sandygast", "Palossand", "Omanyte", "Omastar", "Kabuto", "Kabutops", "Lileep", "Cradily", "Anorith", "Armaldo", "Cranidos", "Rampardos", "Shieldon", "Bastiodon", "Tirtouga", "Carracosta", "Archen", "Archeops", "Tyrunt", "Tyrantrum", "Amaura", "Aurorus", "Pupitar", "Larvitar", "Tyranitar", "Phantump", "Trevenant", "Natu", "Xatu", "Nosepass", "Probopass", "Pyukumuku", "Chinchou", "Lanturn", "Type: Null", "Silvally", "Poipole", "Naganadel", "Zygarde", "Trubbish", "Garbodor", "Minccino", "Cinccino", "Pineco", "Forretress", "Skarmory", "Ditto", "Cleffa", "Clefairy", "Clefable", "Elgyem", "Beheeyem", "Minior", "Beldum", "Metang", "Metagross", "Porygon", "Porygon2", "Porygon-Z", "Pancham", "Pangoro", "Komala", "Torkoal", "Turtonator", "Houndour", "Houndoom", "Dedenne", "Togedemaru", "Electrike", "Manectric", "Elekid", "Electabuzz", "Electivire", "Geodude-Alola", "Graveler-Alola", "Golem-Alola", "Sandile", "Krokorok", "Krookodile", "Trapinch", "Vibrava", "Flygon", "Gible", "Gabite", "Garchomp", "Baltoy", "Claydol", "Golett", "Golurk", "Klefki", "Mimikyu", "Shuppet", "Banette", "Frillish", "Jellicent", "Bruxish", "Drampa", "Absol", "Snorunt", "Glalie", "Froslass", "Sneasel", "Weavile", "Sandshrew-Alola", "Sandslash-Alola", "Vulpix-Alola", "Ninetales-Alola", "Vanillite", "Vanillish", "Vanilluxe", "Scraggy", "Scrafty", "Pawniard", "Bisharp", "Snubbull", "Granbull", "Shellos", "Gastrodon", "Relicanth", "Dhelmise", "Carvanha", "Sharpedo", "Skrelp", "Dragalge", "Clauncher", "Clawitzer", "Wailmer", "Wailord", "Lapras", "Tropius", "Exeggcute", "Exeggutor-Alola", "Corphish", "Crawdaunt", "Mienfoo", "Mienshao", "Jangmo-o", "Hakamo-o", "Kommo-o", "Emolga", "Scyther", "Scizor", "Heracross", "Aipom", "Ampibom", "Litleo", "Pyroar", "Misdreavus", "Mismagius", "Druddigon", "Lickitung", "Lickilicky", "Riolu", "Lucario", "Dratini", "Dragonair", "Dragonite", "Aerodactyl", "Tapu Koko", "Tapu Lele", "Tapu Bulu", "Tapu Fini", "Cosmog", "Cosmoem", "Solgaleo", "Lunala", "Nihilego", "Stakataka", "Blacephalon", "Buzzwole", "Pheromosa", "Xurkitree", "Celesteela", "Kartana", "Guzzlord", "Necrozma", "Magearna", "Marshadow", "Zeraora", ]; - let species = this.dex.getSpecies(set.species || set.name); - if (!alolaDex.includes(species.baseSpecies) && !alolaDex.includes(species.name) && !this.ruleTable.has('+' + species.id)) { + const species = this.dex.getSpecies(set.species || set.name); + if (!alolaDex.includes(species.baseSpecies) && !alolaDex.includes(species.name) && + !this.ruleTable.has('+' + species.id)) { return [`${species.baseSpecies} is not in the Alola Pokédex.`]; } }, @@ -245,11 +251,12 @@ let BattleFormats = { desc: "Only allows Pokémon native to the Galar region (Sw/Sh)", banlist: ['Raichu-Alola', 'Weezing-Base'], onValidateSet(set, format) { - let galarDex = [ + const galarDex = [ "Grookey", "Thwackey", "Rillaboom", "Scorbunny", "Raboot", "Cinderace", "Sobble", "Drizzile", "Inteleon", "Blipbug", "Dottler", "Orbeetle", "Caterpie", "Metapod", "Butterfree", "Grubbin", "Charjabug", "Vikavolt", "Hoothoot", "Noctowl", "Rookidee", "Corvisquire", "Corviknight", "Skwovet", "Greedent", "Pidove", "Tranquill", "Unfezant", "Nickit", "Thievul", "Zigzagoon", "Linoone", "Obstagoon", "Wooloo", "Dubwool", "Lotad", "Lombre", "Ludicolo", "Seedot", "Nuzleaf", "Shiftry", "Chewtle", "Drednaw", "Purrloin", "Liepard", "Yamper", "Boltund", "Bunnelby", "Diggersby", "Minccino", "Cinccino", "Bounsweet", "Steenee", "Tsareena", "Oddish", "Gloom", "Vileplume", "Bellossom", "Budew", "Roselia", "Roserade", "Wingull", "Pelipper", "Joltik", "Galvantula", "Electrike", "Manectric", "Vulpix", "Ninetales", "Growlithe", "Arcanine", "Vanillite", "Vanillish", "Vanilluxe", "Swinub", "Piloswine", "Mamoswine", "Delibird", "Snorunt", "Glalie", "Froslass", "Baltoy", "Claydol", "Mudbray", "Mudsdale", "Dwebble", "Crustle", "Golett", "Golurk", "Munna", "Musharna", "Natu", "Xatu", "Stufful", "Bewear", "Snover", "Abomasnow", "Krabby", "Kingler", "Wooper", "Quagsire", "Corphish", "Crawdaunt", "Nincada", "Ninjask", "Shedinja", "Tyrogue", "Hitmonlee", "Hitmonchan", "Hitmontop", "Pancham", "Pangoro", "Klink", "Klang", "Klinklang", "Combee", "Vespiquen", "Bronzor", "Bronzong", "Ralts", "Kirlia", "Gardevoir", "Gallade", "Drifloon", "Drifblim", "Gossifleur", "Eldegoss", "Cherubi", "Cherrim", "Stunky", "Skuntank", "Tympole", "Palpitoad", "Seismitoad", "Duskull", "Dusclops", "Dusknoir", "Machop", "Machoke", "Machamp", "Gastly", "Haunter", "Gengar", "Magikarp", "Gyarados", "Goldeen", "Seaking", "Remoraid", "Octillery", "Shellder", "Cloyster", "Feebas", "Milotic", "Basculin", "Wishiwashi", "Pyukumuku", "Trubbish", "Garbodor", "Sizzlipede", "Centiskorch", "Rolycoly", "Carkol", "Coalossal", "Diglett", "Dugtrio", "Drilbur", "Excadrill", "Roggenrola", "Boldore", "Gigalith", "Timburr", "Gurdurr", "Conkeldurr", "Woobat", "Swoobat", "Noibat", "Noivern", "Onix", "Steelix", "Arrokuda", "Barraskewda", "Meowth", "Perrserker", "Persian", "Milcery", "Alcremie", "Cutiefly", "Ribombee", "Ferroseed", "Ferrothorn", "Pumpkaboo", "Gourgeist", "Pichu", "Pikachu", "Raichu", "Eevee", "Vaporeon", "Jolteon", "Flareon", "Espeon", "Umbreon", "Leafeon", "Glaceon", "Sylveon", "Applin", "Flapple", "Appletun", "Espurr", "Meowstic", "Swirlix", "Slurpuff", "Spritzee", "Aromatisse", "Dewpider", "Araquanid", "Wynaut", "Wobbuffet", "Farfetch'd", "Sirfetch'd", "Chinchou", "Lanturn", "Croagunk", "Toxicroak", "Scraggy", "Scrafty", "Stunfisk", "Shuckle", "Barboach", "Whiscash", "Shellos", "Gastrodon", "Wimpod", "Golisopod", "Binacle", "Barbaracle", "Corsola", "Cursola", "Impidimp", "Morgrem", "Grimmsnarl", "Hatenna", "Hattrem", "Hatterene", "Salandit", "Salazzle", "Pawniard", "Bisharp", "Throh", "Sawk", "Koffing", "Weezing", "Bonsly", "Sudowoodo", "Cleffa", "Clefairy", "Clefable", "Togepi", "Togetic", "Togekiss", "Munchlax", "Snorlax", "Cottonee", "Whimsicott", "Rhyhorn", "Rhydon", "Rhyperior", "Gothita", "Gothorita", "Gothitelle", "Solosis", "Duosion", "Reuniclus", "Karrablast", "Escavalier", "Shelmet", "Accelgor", "Elgyem", "Beheeyem", "Cubchoo", "Beartic", "Rufflet", "Braviary", "Vullaby", "Mandibuzz", "Skorupi", "Drapion", "Litwick", "Lampent", "Chandelure", "Inkay", "Malamar", "Sneasel", "Weavile", "Sableye", "Mawile", "Maractus", "Sigilyph", "Riolu", "Lucario", "Torkoal", "Mimikyu", "Cufant", "Copperajah", "Qwilfish", "Frillish", "Jellicent", "Mareanie", "Toxapex", "Cramorant", "Toxel", "Toxtricity", "Toxtricity-Low-Key", "Silicobra", "Sandaconda", "Hippopotas", "Hippowdon", "Durant", "Heatmor", "Helioptile", "Heliolisk", "Hawlucha", "Trapinch", "Vibrava", "Flygon", "Axew", "Fraxure", "Haxorus", "Yamask", "Runerigus", "Cofagrigus", "Honedge", "Doublade", "Aegislash", "Ponyta", "Rapidash", "Sinistea", "Polteageist", "Indeedee", "Phantump", "Trevenant", "Morelull", "Shiinotic", "Oranguru", "Passimian", "Morpeko", "Falinks", "Drampa", "Turtonator", "Togedemaru", "Snom", "Frosmoth", "Clobbopus", "Grapploct", "Pincurchin", "Mantyke", "Mantine", "Wailmer", "Wailord", "Bergmite", "Avalugg", "Dhelmise", "Lapras", "Lunatone", "Solrock", "Mime Jr.", "Mr. Mime", "Mr. Rime", "Darumaka", "Darmanitan", "Stonjourner", "Eiscue", "Duraludon", "Rotom", "Ditto", "Dracozolt", "Arctozolt", "Dracovish", "Arctovish", "Charmander", "Charmeleon", "Charizard", "Type: Null", "Silvally", "Larvitar", "Pupitar", "Tyranitar", "Deino", "Zweilous", "Hydreigon", "Goomy", "Sliggoo", "Goodra", "Jangmo-o", "Hakamo-o", "Kommo-o", "Dreepy", "Drakloak", "Dragapult", ]; - let species = this.dex.getSpecies(set.species || set.name); - if (!galarDex.includes(species.baseSpecies) && !galarDex.includes(species.name) && !this.ruleTable.has('+' + species.id)) { + const species = this.dex.getSpecies(set.species || set.name); + if (!galarDex.includes(species.baseSpecies) && !galarDex.includes(species.name) && + !this.ruleTable.has('+' + species.id)) { return [`${species.baseSpecies} is not in the Galar Pokédex.`]; } }, @@ -270,7 +277,8 @@ let BattleFormats = { onBegin() { this.add('clearpoke'); for (const pokemon of this.getAllPokemon()) { - let details = pokemon.details.replace(/(Arceus|Gourgeist|Genesect|Pumpkaboo|Silvally)(-[a-zA-Z?]+)?/g, '$1-*').replace(', shiny', ''); + const details = pokemon.details.replace(/(Arceus|Gourgeist|Genesect|Pumpkaboo|Silvally)(-[a-zA-Z?]+)?/g, '$1-*') + .replace(', shiny', ''); this.add('poke', pokemon.side.id, details, this.gen < 8 && pokemon.item ? 'item' : ''); } }, @@ -283,11 +291,11 @@ let BattleFormats = { name: 'Little Cup', desc: "Only allows Pokémon that can evolve and don't have any prior evolutions", onValidateSet(set) { - let species = this.dex.getSpecies(set.species || set.name); + const species = this.dex.getSpecies(set.species || set.name); if (species.prevo && this.dex.getSpecies(species.prevo).gen <= this.gen) { return [set.species + " isn't the first in its evolution family."]; } - let futureGenEvo = species.evos && this.dex.getSpecies(species.evos[0]).gen > this.gen; + const futureGenEvo = species.evos && this.dex.getSpecies(species.evos[0]).gen > this.gen; if (!species.nfe || futureGenEvo) { return [set.species + " doesn't have an evolution family."]; } @@ -312,7 +320,10 @@ let BattleFormats = { effectType: 'Rule', name: 'VGC Timer', desc: "VGC's timer: 90 second Team Preview, 7 minutes Your Time, 1 minute per turn", - timer: {starting: 7 * 60, addPerTurn: 0, maxPerTurn: 55, maxFirstTurn: 90, grace: 90, timeoutAutoChoose: true, dcTimerBank: false}, + timer: { + starting: 7 * 60, addPerTurn: 0, maxPerTurn: 55, maxFirstTurn: 90, + grace: 90, timeoutAutoChoose: true, dcTimerBank: false, + }, }, speciesclause: { effectType: 'ValidatorRule', @@ -322,14 +333,13 @@ let BattleFormats = { this.add('rule', 'Species Clause: Limit one of each Pokémon'); }, onValidateTeam(team, format) { - /**@type {{[k: string]: true}} */ - let speciesTable = {}; + const speciesTable: Set = new Set(); for (const set of team) { - let species = this.dex.getSpecies(set.species); - if (speciesTable[species.num]) { - return ["You are limited to one of each Pokémon by Species Clause.", "(You have more than one " + species.baseSpecies + ")"]; + const species = this.dex.getSpecies(set.species); + if (speciesTable.has(species.num)) { + return [`You are limited to one of each Pokémon by Species Clause.`, `(You have more than one ${species.baseSpecies})`]; } - speciesTable[species.num] = true; + speciesTable.add(species.num); } }, }, @@ -338,16 +348,15 @@ let BattleFormats = { name: 'Nickname Clause', desc: "Prevents teams from having more than one Pokémon with the same nickname", onValidateTeam(team, format) { - /**@type {{[k: string]: true}} */ - let nameTable = {}; + const nameTable: Set = new Set(); for (const set of team) { - let name = set.name; + const name = set.name; if (name) { if (name === this.dex.getSpecies(set.species).baseSpecies) continue; - if (nameTable[name]) { - return ["Your Pokémon must have different nicknames.", "(You have more than one " + name + ")"]; + if (nameTable.has(name)) { + return [`Your Pokémon must have different nicknames.`, `(You have more than one ${name})`]; } - nameTable[name] = true; + nameTable.add(name); } } // Illegality of impersonation of other species is @@ -361,16 +370,18 @@ let BattleFormats = { onBegin() { this.add('rule', 'Item Clause: Limit one of each item'); }, - onValidateTeam(team, format) { - /**@type {{[k: string]: true}} */ - let itemTable = {}; + onValidateTeam(team) { + const itemTable: Set = new Set(); for (const set of team) { - let item = toID(set.item); + const item = toID(set.item); if (!item) continue; - if (itemTable[item]) { - return ["You are limited to one of each item by Item Clause.", "(You have more than one " + this.dex.getItem(item).name + ")"]; + if (itemTable.has(item)) { + return [ + `You are limited to one of each item by Item Clause.`, + `(You have more than one ${this.dex.getItem(item).name})`, + ]; } - itemTable[item] = true; + itemTable.add(item); } }, }, @@ -381,11 +392,9 @@ let BattleFormats = { onBegin() { this.add('rule', '2 Ability Clause: Limit two of each ability'); }, - onValidateTeam(team, format) { - /**@type {{[k: string]: number}} */ - let abilityTable = {}; - /**@type {{[k: string]: string}} */ - let base = { + onValidateTeam(team) { + const abilityTable: {[k: string]: number} = {}; + const base: {[k: string]: string} = { airlock: 'cloudnine', battlearmor: 'shellarmor', clearbody: 'whitesmoke', @@ -402,12 +411,15 @@ let BattleFormats = { turboblaze: 'moldbreaker', }; for (const set of team) { - let ability = toID(set.ability); + let ability: string = toID(set.ability); if (!ability) continue; - if (ability in base) ability = /** @type {ID} */(base[ability]); + if (ability in base) ability = base[ability]; if (ability in abilityTable) { if (abilityTable[ability] >= 2) { - return ["You are limited to two of each ability by the Ability Clause.", `(You have more than two ${this.dex.getAbility(ability).name} variants)`]; + return [ + `You are limited to two of each ability by 2 Ability Clause.`, + `(You have more than two ${this.dex.getAbility(ability).name} variants)`, + ]; } abilityTable[ability]++; } else { @@ -424,10 +436,10 @@ let BattleFormats = { this.add('rule', 'OHKO Clause: OHKO moves are banned'); }, onValidateSet(set) { - let problems = []; + const problems: string[] = []; if (set.moves) { for (const moveId of set.moves) { - let move = this.dex.getMove(moveId); + const move = this.dex.getMove(moveId); if (move.ohko) problems.push(move.name + ' is banned by OHKO Clause.'); } } @@ -456,7 +468,9 @@ let BattleFormats = { effectType: 'ValidatorRule', name: 'Accuracy Moves Clause', desc: "Bans moves that have a chance to lower the target's accuracy when used", - banlist: ['Flash', 'Kinesis', 'Leaf Tornado', 'Mirror Shot', 'Mud Bomb', 'Mud-Slap', 'Muddy Water', 'Night Daze', 'Octazooka', 'Sand Attack', 'Smokescreen'], + banlist: [ + 'Flash', 'Kinesis', 'Leaf Tornado', 'Mirror Shot', 'Mud Bomb', 'Mud-Slap', 'Muddy Water', 'Night Daze', 'Octazooka', 'Sand Attack', 'Smokescreen', + ], onBegin() { this.add('rule', 'Accuracy Moves Clause: Accuracy-lowering moves are banned'); }, @@ -470,10 +484,10 @@ let BattleFormats = { this.add('rule', 'Sleep Clause: Sleep-inducing moves are banned'); }, onValidateSet(set) { - let problems = []; + const problems = []; if (set.moves) { for (const id of set.moves) { - let move = this.dex.getMove(id); + const move = this.dex.getMove(id); if (move.status && move.status === 'slp') problems.push(move.name + ' is banned by Sleep Clause.'); } } @@ -489,7 +503,7 @@ let BattleFormats = { 'Orbeetle-Gmax ++ Grass Whistle', 'Orbeetle-Gmax ++ Hypnosis', 'Orbeetle-Gmax ++ Lovely Kiss', 'Orbeetle-Gmax ++ Sing', 'Orbeetle-Gmax ++ Sleep Powder', ], onBegin() { - this.add('rule', 'Gravity Sleep Clause: The combination of sleep-inducing moves with imperfect accuracy and Gravity is banned'); + this.add('rule', 'Gravity Sleep Clause: The combination of sleep-inducing moves with imperfect accuracy and Gravity or Gigantamax Orbeetle are banned'); }, }, endlessbattleclause: { @@ -530,39 +544,54 @@ let BattleFormats = { onValidateSet(set, format, setHas) { if (!('move:batonpass' in setHas)) return; - let item = this.dex.getItem(set.item); - let ability = toID(set.ability); - /**@type {boolean | string} */ - let speedBoosted = false; - /**@type {boolean | string} */ - let nonSpeedBoosted = false; + const item = this.dex.getItem(set.item); + const ability = toID(set.ability); + let speedBoosted: boolean | string = false; + let nonSpeedBoosted: boolean | string = false; for (const moveId of set.moves) { - let move = this.dex.getMove(moveId); + const move = this.dex.getMove(moveId); if (move.id === 'flamecharge' || (move.boosts && move.boosts.spe && move.boosts.spe > 0)) { speedBoosted = true; } - if (['acupressure', 'bellydrum', 'chargebeam', 'curse', 'diamondstorm', 'fellstinger', 'fierydance', 'flowershield', 'poweruppunch', 'rage', 'rototiller', 'skullbash', 'stockpile'].includes(move.id) || - move.boosts && ((move.boosts.atk && move.boosts.atk > 0) || (move.boosts.def && move.boosts.def > 0) || (move.boosts.spa && move.boosts.spa > 0) || (move.boosts.spd && move.boosts.spd > 0))) { + const nonSpeedBoostedMoves = [ + 'acupressure', 'bellydrum', 'chargebeam', 'curse', 'diamondstorm', 'fellstinger', 'fierydance', + 'flowershield', 'poweruppunch', 'rage', 'rototiller', 'skullbash', 'stockpile', + ]; + if (nonSpeedBoostedMoves.includes(move.id) || + move.boosts && ((move.boosts.atk && move.boosts.atk > 0) || (move.boosts.def && move.boosts.def > 0) || + (move.boosts.spa && move.boosts.spa > 0) || (move.boosts.spd && move.boosts.spd > 0))) { nonSpeedBoosted = true; } - if (item.zMove && move.type === item.zMoveType) { - if (move.zMoveBoost && move.zMoveBoost.spe && move.zMoveBoost.spe > 0) { + if (item.zMove && move.type === item.zMoveType && move.zMoveBoost) { + const boosts = move.zMoveBoost; + if (boosts.spe && boosts.spe > 0) { if (!speedBoosted) speedBoosted = move.name; } - if (move.zMoveBoost && ((move.zMoveBoost.atk && move.zMoveBoost.atk > 0) || (move.zMoveBoost.def && move.zMoveBoost.def > 0) || - (move.zMoveBoost.spa && move.zMoveBoost.spa > 0) || (move.zMoveBoost.spd && move.zMoveBoost.spd > 0))) { + if ( + ((boosts.atk && boosts.atk > 0) || (boosts.def && boosts.def > 0) || + (boosts.spa && boosts.spa > 0) || (boosts.spd && boosts.spd > 0)) + ) { if (!nonSpeedBoosted || move.name === speedBoosted) nonSpeedBoosted = move.name; } } } - if (['motordrive', 'rattled', 'speedboost', 'steadfast', 'weakarmor'].includes(ability) || ['blazikenite', 'eeviumz', 'kommoniumz', 'salacberry'].includes(item.id)) { + const speedBoostedAbilities = ['motordrive', 'rattled', 'speedboost', 'steadfast', 'weakarmor']; + const speedBoostedItems = ['blazikenite', 'eeviumz', 'kommoniumz', 'salacberry']; + if (speedBoostedAbilities.includes(ability) || speedBoostedItems.includes(item.id)) { speedBoosted = true; } if (!speedBoosted) return; - if (['angerpoint', 'competitive', 'defiant', 'download', 'justified', 'lightningrod', 'moxie', 'sapsipper', 'stormdrain'].includes(ability) || ['absorbbulb', 'apicotberry', 'cellbattery', 'eeviumz', 'ganlonberry', 'keeberry', 'kommoniumz', 'liechiberry', 'luminousmoss', 'marangaberry', 'petayaberry', 'snowball', 'starfberry', 'weaknesspolicy'].includes(item.id)) { + const nonSpeedBoostedAbilities = [ + 'angerpoint', 'competitive', 'defiant', 'download', 'justified', 'lightningrod', 'moxie', 'sapsipper', 'stormdrain', + ]; + const nonSpeedBoostedItems = [ + 'absorbbulb', 'apicotberry', 'cellbattery', 'eeviumz', 'ganlonberry', 'keeberry', 'kommoniumz', 'liechiberry', + 'luminousmoss', 'marangaberry', 'petayaberry', 'snowball', 'starfberry', 'weaknesspolicy', + ]; + if (nonSpeedBoostedAbilities.includes(ability) || nonSpeedBoostedItems.includes(item.id)) { nonSpeedBoosted = true; } if (!nonSpeedBoosted) return; @@ -570,7 +599,9 @@ let BattleFormats = { // if both boost sources are Z-moves, and they're distinct if (speedBoosted !== nonSpeedBoosted && typeof speedBoosted === 'string' && typeof nonSpeedBoosted === 'string') return; - return [(set.name || set.species) + " can Baton Pass both Speed and a different stat, which is banned by Baton Pass Clause."]; + return [ + `${set.name || set.species} can Baton Pass both Speed and a different stat, which is banned by Baton Pass Clause.`, + ]; }, }, "3batonpassclause": { @@ -586,7 +617,16 @@ let BattleFormats = { effectType: 'ValidatorRule', name: 'CFZ Clause', desc: "Bans the use of crystal-free Z-Moves", - banlist: ['10,000,000 Volt Thunderbolt', 'Acid Downpour', 'All-Out Pummeling', 'Black Hole Eclipse', 'Bloom Doom', 'Breakneck Blitz', 'Catastropika', 'Clangorous Soulblaze', 'Continental Crush', 'Corkscrew Crash', 'Devastating Drake', 'Extreme Evoboost', 'Genesis Supernova', 'Gigavolt Havoc', 'Guardian of Alola', 'Hydro Vortex', 'Inferno Overdrive', 'Let\'s Snuggle Forever', 'Light That Burns the Sky', 'Malicious Moonsault', 'Menacing Moonraze Maelstrom', 'Never-Ending Nightmare', 'Oceanic Operetta', 'Pulverizing Pancake', 'Savage Spin-Out', 'Searing Sunraze Smash', 'Shattered Psyche', 'Sinister Arrow Raid', 'Soul-Stealing 7-Star Strike', 'Splintered Stormshards', 'Stoked Sparksurfer', 'Subzero Slammer', 'Supersonic Skystrike', 'Tectonic Rage', 'Twinkle Tackle'], + banlist: [ + '10,000,000 Volt Thunderbolt', 'Acid Downpour', 'All-Out Pummeling', 'Black Hole Eclipse', 'Bloom Doom', + 'Breakneck Blitz', 'Catastropika', 'Clangorous Soulblaze', 'Continental Crush', 'Corkscrew Crash', + 'Devastating Drake', 'Extreme Evoboost', 'Genesis Supernova', 'Gigavolt Havoc', 'Guardian of Alola', + 'Hydro Vortex', 'Inferno Overdrive', 'Let\'s Snuggle Forever', 'Light That Burns the Sky', + 'Malicious Moonsault', 'Menacing Moonraze Maelstrom', 'Never-Ending Nightmare', 'Oceanic Operetta', + 'Pulverizing Pancake', 'Savage Spin-Out', 'Searing Sunraze Smash', 'Shattered Psyche', 'Sinister Arrow Raid', + 'Soul-Stealing 7-Star Strike', 'Splintered Stormshards', 'Stoked Sparksurfer', 'Subzero Slammer', + 'Supersonic Skystrike', 'Tectonic Rage', 'Twinkle Tackle', + ], onBegin() { this.add('rule', 'CFZ Clause: Crystal-free Z-Moves are banned'); }, @@ -701,19 +741,17 @@ let BattleFormats = { this.add('rule', 'Same Type Clause: Pokémon in a team must share a type'); }, onValidateTeam(team) { - /**@type {string[]} */ - let typeTable; + let typeTable: string[] = []; for (const [i, set] of team.entries()) { let species = this.dex.getSpecies(set.species); if (!species.types) return [`Invalid pokemon ${set.name || set.species}`]; if (i === 0) { typeTable = species.types; } else { - // @ts-ignore typeTable = typeTable.filter(type => species.types.includes(type)); } if (this.gen >= 7) { - let item = this.dex.getItem(set.item); + const item = this.dex.getItem(set.item); if (item.megaStone && species.name === item.megaEvolves) { species = this.dex.getSpecies(item.megaStone); typeTable = typeTable.filter(type => species.types.includes(type)); @@ -743,7 +781,7 @@ let BattleFormats = { name: 'Dynamax Clause', desc: "Prevents Pokémon from dynamaxing", onBegin() { - for (let pokemon of this.getAllPokemon()) { + for (const pokemon of this.getAllPokemon()) { pokemon.canDynamax = false; } this.add('rule', 'Dynamax Clause: You cannot dynamax'); @@ -755,7 +793,7 @@ let BattleFormats = { desc: "Prevents Pokémon on the Ubers dynamax banlist from dynamaxing", onBegin() { const cannotDynamax = this.format.restricted || []; - for (let pokemon of this.getAllPokemon()) { + for (const pokemon of this.getAllPokemon()) { if (cannotDynamax.includes(pokemon.forme)) pokemon.canDynamax = false; } this.add('html', 'Ubers Dynamax Clause: Pokémon on the Ubers Dynamax Banlist cannot Dynamax.'); @@ -766,10 +804,9 @@ let BattleFormats = { name: 'Arceus EV Limit', desc: "Restricts Arceus to a maximum of 100 EVs in any one stat, and only multiples of 10", onValidateSet(set) { - let species = this.dex.getSpecies(set.species); + const species = this.dex.getSpecies(set.species); if (species.num === 493 && set.evs) { - /** @type {StatName} */ - let stat; + let stat: StatName; for (stat in set.evs) { const ev = set.evs[stat]; if (ev > 100) { @@ -813,13 +850,16 @@ let BattleFormats = { checkLearnset(move, species, setSources, set) { const restrictedMoves = this.format.restricted || []; if (!restrictedMoves.includes(move.name) && !move.isNonstandard && !move.isMax) { - let dex = this.dex; + const dex = this.dex; let types = species.types; - let baseSpecies = dex.getSpecies(species.baseSpecies); - if (species.prevo) types = types.concat(dex.getSpecies(dex.getSpecies(species.prevo).prevo || species.prevo).types); + const baseSpecies = dex.getSpecies(species.baseSpecies); + while (species.prevo) { + const prevoSpecies = dex.getSpecies(species.prevo); + types = types.concat(prevoSpecies.types); + } if (baseSpecies.otherFormes) { for (const formeid of baseSpecies.otherFormes) { - let forme = dex.getSpecies(formeid); + const forme = dex.getSpecies(formeid); if (!forme.battleOnly) { if (!forme.forme.includes('Alola') && forme.forme !== 'Galar' && forme.baseSpecies !== 'Wormadam') { types = types.concat(forme.types).concat(baseSpecies.types); @@ -871,18 +911,23 @@ let BattleFormats = { this.add('rule', 'Forme Clause: Limit one of each forme of a Pokémon'); }, onValidateTeam(team) { - /** @type {Set} */ - const formeTable = new Set(); + const formeTable: Set = new Set(); for (const set of team) { let species = this.dex.getSpecies(set.species); if (species.name !== species.baseSpecies) { - let baseSpecies = this.dex.getSpecies(species.baseSpecies); - if (species.types.join('/') === baseSpecies.types.join('/') && Object.values(species.baseStats).join('/') === Object.values(baseSpecies.baseStats).join('/')) { + const baseSpecies = this.dex.getSpecies(species.baseSpecies); + if ( + species.types.join('/') === baseSpecies.types.join('/') && + Object.values(species.baseStats).join('/') === Object.values(baseSpecies.baseStats).join('/') + ) { species = baseSpecies; } } if (formeTable.has(species.name)) { - return [`You are limited to one of each forme of a Pokémon by Forme Clause.`, `(You have more than one of ${species.name})`]; + return [ + `You are limited to one of each forme of a Pokémon by Forme Clause.`, + `(You have more than one of ${species.name})`, + ]; } formeTable.add(species.name); } @@ -895,14 +940,12 @@ let BattleFormats = { onBegin() { this.add('rule', 'Scalemons Mod: Every Pokemon\'s stats, barring HP, are scaled to come as close to a BST of 600 as possible'); }, - onModifySpecies(species, target, source) { + onModifySpecies(species) { const newSpecies = this.dex.deepClone(species); newSpecies.baseStats = this.dex.deepClone(newSpecies.baseStats); - /** @type {StatName[]} */ - let stats = ['atk', 'def', 'spa', 'spd', 'spe']; - /** @type {number} */ - let pst = stats.map(stat => newSpecies.baseStats[stat]).reduce((x, y) => x + y); - let scale = 600 - newSpecies.baseStats['hp']; + const stats: StatName[] = ['atk', 'def', 'spa', 'spd', 'spe']; + const pst: number = stats.map(stat => newSpecies.baseStats[stat]).reduce((x, y) => x + y); + const scale = 600 - newSpecies.baseStats['hp']; for (const stat of stats) { newSpecies.baseStats[stat] = this.dex.clampIntRange(newSpecies.baseStats[stat] * scale / pst, 1, 255); } @@ -910,5 +953,3 @@ let BattleFormats = { }, }, }; - -exports.BattleFormats = BattleFormats; diff --git a/data/scripts.js b/data/scripts.ts similarity index 86% rename from data/scripts.js rename to data/scripts.ts index 84b477d6fc..da38e8315d 100644 --- a/data/scripts.js +++ b/data/scripts.ts @@ -1,9 +1,6 @@ -'use strict'; - const CHOOSABLE_TARGETS = new Set(['normal', 'any', 'adjacentAlly', 'adjacentAllyOrSelf', 'adjacentFoe']); -/**@type {BattleScriptsData} */ -let BattleScripts = { +export const BattleScripts: BattleScriptsData = { gen: 8, /** * runMove is the "outside" move caller. It handles deducting PP, @@ -22,7 +19,7 @@ let BattleScripts = { let baseMove = this.dex.getActiveMove(moveOrMoveName); const pranksterBoosted = baseMove.pranksterBoosted; if (baseMove.id !== 'struggle' && !zMove && !maxMove && !externalMove) { - let changedMove = this.runEvent('OverrideAction', pokemon, target, baseMove); + const changedMove = this.runEvent('OverrideAction', pokemon, target, baseMove); if (changedMove && changedMove !== true) { baseMove = this.dex.getActiveMove(changedMove); if (pranksterBoosted) baseMove.pranksterBoosted = pranksterBoosted; @@ -48,7 +45,7 @@ let BattleScripts = { this.clearActiveMove(true); return; } */ - let willTryMove = this.runEvent('BeforeMove', pokemon, target, move); + const willTryMove = this.runEvent('BeforeMove', pokemon, target, move); if (!willTryMove) { this.runEvent('MoveAborted', pokemon, target, move); this.clearActiveMove(true); @@ -73,7 +70,9 @@ let BattleScripts = { if (!lockedMove) { if (!pokemon.deductPP(baseMove, null, target) && (move.id !== 'struggle')) { this.add('cant', pokemon, 'nopp', move); - let gameConsole = [null, 'Game Boy', 'Game Boy Color', 'Game Boy Advance', 'DS', 'DS', '3DS', '3DS'][this.gen] || 'Switch'; + const gameConsole = [ + null, 'Game Boy', 'Game Boy Color', 'Game Boy Advance', 'DS', 'DS', '3DS', '3DS', + ][this.gen] || 'Switch'; this.hint(`This is not a bug, this is really how it works on the ${gameConsole}; try it yourself if you don't believe us.`); this.clearActiveMove(true); pokemon.moveThisTurnResult = false; @@ -87,7 +86,7 @@ let BattleScripts = { // Dancer Petal Dance hack // TODO: implement properly - let noLock = externalMove && !pokemon.volatiles.lockedmove; + const noLock = externalMove && !pokemon.volatiles.lockedmove; if (zMove) { if (pokemon.illusion) { @@ -96,14 +95,14 @@ let BattleScripts = { this.add('-zpower', pokemon); pokemon.side.zMoveUsed = true; } - let moveDidSomething = this.useMove(baseMove, pokemon, target, sourceEffect, zMove, maxMove); + const moveDidSomething = this.useMove(baseMove, pokemon, target, sourceEffect, zMove, maxMove); if (this.activeMove) move = this.activeMove; this.singleEvent('AfterMove', move, null, pokemon, target, move); this.runEvent('AfterMove', pokemon, target, move); // Dancer's activation order is completely different from any other event, so it's handled separately if (move.flags['dance'] && moveDidSomething && !move.isExternal) { - let dancers = []; + const dancers = []; for (const currentPoke of this.getAllActive()) { if (pokemon === currentPoke) continue; if (currentPoke.hasAbility('dancer') && !currentPoke.isSemiInvulnerable()) { @@ -140,9 +139,8 @@ let BattleScripts = { */ useMove(move, pokemon, target, sourceEffect, zMove, maxMove) { pokemon.moveThisTurnResult = undefined; - /** @type {boolean? | undefined} */ // Typescript bug - let oldMoveResult = pokemon.moveThisTurnResult; - let moveResult = this.useMoveInner(move, pokemon, target, sourceEffect, zMove, maxMove); + const oldMoveResult: boolean | null | undefined = pokemon.moveThisTurnResult; + const moveResult = this.useMoveInner(move, pokemon, target, sourceEffect, zMove, maxMove); if (oldMoveResult === pokemon.moveThisTurnResult) pokemon.moveThisTurnResult = moveResult; return moveResult; }, @@ -157,7 +155,7 @@ let BattleScripts = { this.singleEvent('ModifyType', move, null, pokemon, target, move, move); if (move.type !== 'Normal') sourceEffect = move; } - if (zMove || (move.category !== 'Status' && sourceEffect && /** @type {ActiveMove} */(sourceEffect).isZ)) { + if (zMove || (move.category !== 'Status' && sourceEffect && (sourceEffect as ActiveMove).isZ)) { move = this.getActiveZMove(move, pokemon); } if (maxMove && move.category !== 'Status') { @@ -165,7 +163,7 @@ let BattleScripts = { this.singleEvent('ModifyType', move, null, pokemon, target, move, move); this.runEvent('ModifyType', pokemon, target, move, move); } - if (maxMove || (move.category !== 'Status' && sourceEffect && /** @type {ActiveMove} */(sourceEffect).isMax)) { + if (maxMove || (move.category !== 'Status' && sourceEffect && (sourceEffect as ActiveMove).isMax)) { move = this.getActiveMaxMove(move, pokemon); } @@ -173,7 +171,7 @@ let BattleScripts = { move.priority = this.activeMove.priority; if (!move.hasBounced) move.pranksterBoosted = this.activeMove.pranksterBoosted; } - let baseTarget = move.target; + const baseTarget = move.target; if (target === undefined) target = this.getRandomTarget(pokemon, move); if (move.target === 'self' || move.target === 'allies') { target = pokemon; @@ -231,7 +229,7 @@ let BattleScripts = { if (!sourceEffect || sourceEffect.id === 'pursuit') { let extraPP = 0; for (const source of pressureTargets) { - let ppDrop = this.runEvent('DeductPP', source, pokemon, move); + const ppDrop = this.runEvent('DeductPP', source, pokemon, move); if (ppDrop !== true) { extraPP += ppDrop || 0; } @@ -257,8 +255,7 @@ let BattleScripts = { this.faint(pokemon, pokemon, move); } - /** @type {number | false | undefined | ''} */ - let damage = false; + let damage: number | false | undefined | '' = false; if (move.target === 'all' || move.target === 'foeSide' || move.target === 'allySide' || move.target === 'allyTeam') { damage = this.tryMoveHit(target, pokemon, move); if (damage === this.NOT_FAIL) pokemon.moveThisTurnResult = null; @@ -295,8 +292,8 @@ let BattleScripts = { trySpreadMoveHit(targets, pokemon, move) { if (targets.length > 1 && !move.smartTarget) move.spreadHit = true; - /** @type {((targets: Pokemon[], pokemon: Pokemon, move: ActiveMove) => (number | boolean | "" | undefined)[] | undefined)[]} */ - let moveSteps = [ + const moveSteps: ((targets: Pokemon[], pokemon: Pokemon, move: ActiveMove) => + (number | boolean | "" | undefined)[] | undefined)[] = [ // 0. check for semi invulnerability this.hitStepInvulnerabilityEvent, @@ -351,10 +348,10 @@ let BattleScripts = { return false; } - let atLeastOneFailure; + let atLeastOneFailure!: boolean; for (const step of moveSteps) { - /** @type {(number | boolean | "" | undefined)[] | undefined} */ - let hitResults = step.call(this, targets, pokemon, move); + // @ts-ignore + const hitResults: (number | boolean | "" | undefined)[] | undefined = step.call(this, targets, pokemon, move); if (!hitResults) continue; // @ts-ignore targets = targets.filter((val, i) => hitResults[i] || hitResults[i] === 0); @@ -367,7 +364,7 @@ let BattleScripts = { const moveResult = !!targets.length; if (!moveResult && !atLeastOneFailure) pokemon.moveThisTurnResult = null; - const hitSlot = targets.map(pokemon => pokemon.getSlot()); + const hitSlot = targets.map(p => p.getSlot()); if (move.spreadHit) this.attrLastMove('[spread] ' + hitSlot.join(',')); return moveResult; }, @@ -394,7 +391,7 @@ let BattleScripts = { this.add('-fail', pokemon); this.attrLastMove('[still]'); } - for (let i = 0; i < targets.length; i++) { + for (const i of targets.keys()) { if (hitResults[i] !== this.NOT_FAIL) hitResults[i] = hitResults[i] || false; } return hitResults; @@ -405,8 +402,9 @@ let BattleScripts = { } const hitResults = []; - for (let i = 0; i < targets.length; i++) { - hitResults[i] = (move.ignoreImmunity && (move.ignoreImmunity === true || move.ignoreImmunity[move.type])) || targets[i].runImmunity(move.type, !move.smartTarget); + for (const i of targets.keys()) { + hitResults[i] = (move.ignoreImmunity && (move.ignoreImmunity === true || move.ignoreImmunity[move.type])) || + targets[i].runImmunity(move.type, !move.smartTarget); if (move.smartTarget && !hitResults[i]) move.smartTarget = false; } @@ -414,7 +412,7 @@ let BattleScripts = { }, hitStepTryImmunity(targets, pokemon, move) { const hitResults = []; - for (let [i, target] of targets.entries()) { + for (const [i, target] of targets.entries()) { if (this.gen >= 6 && move.flags['powder'] && target !== pokemon && !this.dex.getImmunity('powder', target)) { this.debug('natural powder immunity'); this.add('-immune', target); @@ -422,7 +420,8 @@ let BattleScripts = { } else if (!this.singleEvent('TryImmunity', move, {}, target, pokemon, move)) { this.add('-immune', target); hitResults[i] = false; - } else if (this.gen >= 7 && move.pranksterBoosted && pokemon.hasAbility('prankster') && targets[i].side !== pokemon.side && !this.dex.getImmunity('prankster', target)) { + } else if (this.gen >= 7 && move.pranksterBoosted && pokemon.hasAbility('prankster') && + targets[i].side !== pokemon.side && !this.dex.getImmunity('prankster', target)) { this.debug('natural prankster immunity'); if (!target.illusion) this.hint("Since gen 7, Dark is immune to Prankster moves."); this.add('-immune', target); @@ -435,10 +434,9 @@ let BattleScripts = { }, hitStepAccuracy(targets, pokemon, move) { const hitResults = []; - for (let [i, target] of targets.entries()) { + for (const [i, target] of targets.entries()) { this.activeTarget = target; // calculate true accuracy - /** @type {number | true} */ // TypeScript bug: incorrectly infers {number | true} as {number | boolean} let accuracy = move.accuracy; if (move.ohko) { // bypasses accuracy modifiers if (!target.isSemiInvulnerable()) { @@ -446,7 +444,8 @@ let BattleScripts = { if (move.ohko === 'Ice' && this.gen >= 7 && !pokemon.hasType('Ice')) { accuracy = 20; } - if (!target.volatiles['dynamax'] && pokemon.level >= target.level && (move.ohko === true || !target.hasType(move.ohko))) { + if (!target.volatiles['dynamax'] && pokemon.level >= target.level && + (move.ohko === true || !target.hasType(move.ohko))) { accuracy += (pokemon.level - target.level); } else { this.add('-immune', target, '[ohko]'); @@ -457,7 +456,8 @@ let BattleScripts = { } else { const boostTable = [1, 4 / 3, 5 / 3, 2, 7 / 3, 8 / 3, 3]; - let boosts, boost; + let boosts; + let boost!: number; if (accuracy !== true) { if (!move.ignoreAccuracy) { boosts = this.runEvent('ModifyBoost', pokemon, null, null, Object.assign({}, pokemon.boosts)); @@ -529,11 +529,10 @@ let BattleScripts = { hitStepStealBoosts(targets, pokemon, move) { const target = targets[0]; // hardcoded if (move.stealsBoosts) { - /** @type {{[k: string]: number}} */ - const boosts = {}; + const boosts: SparseBoostsTable = {}; let stolen = false; - for (const statName in target.boosts) { - // @ts-ignore + let statName: BoostName; + for (statName in target.boosts) { const stage = target.boosts[statName]; if (stage > 0) { boosts[statName] = stage; @@ -545,8 +544,9 @@ let BattleScripts = { this.add('-clearpositiveboost', target, pokemon, 'move: ' + move.name); this.boost(boosts, pokemon, pokemon); - for (const statName in boosts) { - boosts[statName] = 0; + let statName2: BoostName; + for (statName2 in boosts) { + boosts[statName2] = 0; } target.setBoost(boosts); this.addMove('-anim', pokemon, "Spectral Thief", target); @@ -594,10 +594,11 @@ let BattleScripts = { } return this.moveHit(target, pokemon, move); }, - hitStepMoveHitLoop(targets, pokemon, move) { // temp name - /** @type {(number | boolean | undefined)[]} */ - const damage = []; - for (let i = 0; i < targets.length; i++) damage[i] = 0; + hitStepMoveHitLoop(targets, pokemon, move) { // Temporary name + const damage: (number | boolean | undefined)[] = []; + for (const i of targets.keys()) { + damage[i] = 0; + } move.totalDamage = 0; pokemon.lastDamage = 0; let targetHits = move.multihit || 1; @@ -615,14 +616,12 @@ let BattleScripts = { } targetHits = Math.floor(targetHits); let nullDamage = true; - /** @type {(number | boolean | undefined)[]} */ - let moveDamage; + let moveDamage: (number | boolean | undefined)[]; // There is no need to recursively check the ´sleepUsable´ flag as Sleep Talk can only be used while asleep. - let isSleepUsable = move.sleepUsable || this.dex.getMove(move.sourceEffect).sleepUsable; + const isSleepUsable = move.sleepUsable || this.dex.getMove(move.sourceEffect).sleepUsable; - /** @type {(Pokemon | false | null)[]} */ - let targetsCopy = targets.slice(0); - let hit; + let targetsCopy: (Pokemon | false | null)[] = targets.slice(0); + let hit: number; for (hit = 1; hit <= targetHits; hit++) { if (damage.includes(false)) break; if (hit > 1 && pokemon.status === 'slp' && !isSleepUsable) break; @@ -633,7 +632,7 @@ let BattleScripts = { } else { targetsCopy = targets.slice(0); } - let target = targetsCopy[0]; // some relevant-to-single-target-moves-only things are hardcoded + const target = targetsCopy[0]; // some relevant-to-single-target-moves-only things are hardcoded if (target && typeof move.smartTarget === 'boolean') { if (hit > 1) { this.addMove('-anim', pokemon, move.name, target); @@ -719,11 +718,9 @@ let BattleScripts = { // smartTarget messes up targetsCopy, but smartTarget should in theory ensure that targets will never fail, anyway if (move.smartTarget) targetsCopy = targets.slice(0); - for (let i = 0; i < targetsCopy.length; i++) { - let target = targetsCopy[i]; + for (const [i, target] of targetsCopy.entries()) { if (target && pokemon !== target) { - // @ts-ignore damage[i] can't be true if target is truthy - target.gotAttacked(move, damage[i], pokemon); + target.gotAttacked(move, damage[i] as number | false | undefined, pokemon); } } @@ -733,14 +730,13 @@ let BattleScripts = { this.eachEvent('Update'); - // @ts-ignore - this.afterMoveSecondaryEvent(targetsCopy.filter(val => !!val), pokemon, move); + this.afterMoveSecondaryEvent(targetsCopy.filter(val => !!val) as Pokemon[], pokemon, move); if (!move.negateSecondary && !(move.hasSheerForce && pokemon.hasAbility('sheerforce'))) { - for (let i = 0; i < damage.length; i++) { + for (const [i, d] of damage.entries()) { // There are no multihit spread moves, so it's safe to use move.totalDamage for multihit moves // The previous check was for `move.multihit`, but that fails for Dragon Darts - const curDamage = targets.length === 1 ? move.totalDamage : damage[i]; + const curDamage = targets.length === 1 ? move.totalDamage : d; if (typeof curDamage === 'number' && targets[i].hp) { if (targets[i].hp <= targets[i].maxhp / 2 && targets[i].hp + curDamage > targets[i].maxhp / 2) { this.runEvent('EmergencyExit', targets[i], pokemon); @@ -755,12 +751,12 @@ let BattleScripts = { // Hardcoded for single-target purposes // (no spread moves have any kind of onTryHit handler) const target = targets[0]; - /** @type {(number | boolean | undefined)[]} */ - let damage = []; - for (let i = 0; i < targets.length; i++) damage[i] = true; + let damage: (number | boolean | undefined)[] = []; + for (const i of targets.keys()) { + damage[i] = true; + } const move = this.dex.getActiveMove(moveOrMoveName); - /** @type {?boolean | number} */ - let hitResult = true; + let hitResult: boolean | number | null = true; if (!moveData) moveData = move; if (!moveData.flags) moveData.flags = {}; if (move.target === 'all' && !isSelf) { @@ -785,9 +781,8 @@ let BattleScripts = { } } - for (let i = 0; i < targets.length; i++) { - if (damage[i] === 0) { - // special substitute flag + for (const i of targets.keys()) { + if (damage[i] === this.HIT_SUBSTITUTE) { damage[i] = true; targets[i] = null; } @@ -799,14 +794,14 @@ let BattleScripts = { // 1. call to this.getDamage damage = this.getSpreadDamage(damage, targets, pokemon, move, moveData, isSecondary, isSelf); - for (let i = 0; i < targets.length; i++) { + for (const i of targets.keys()) { if (damage[i] === false) targets[i] = false; } // 2. call to this.spreadDamage damage = this.spreadDamage(damage, targets, pokemon, move); - for (let i = 0; i < targets.length; i++) { + for (const i of targets.keys()) { if (!damage && damage !== 0) { this.debug('damage interrupted'); targets[i] = false; @@ -816,7 +811,7 @@ let BattleScripts = { // 3. onHit event happens here damage = this.runMoveEffects(damage, targets, pokemon, move, moveData, isSecondary, isSelf); - for (let i = 0; i < targets.length; i++) { + for (const i of targets.keys()) { if (!damage[i] && damage[i] !== 0) targets[i] = false; } @@ -829,16 +824,15 @@ let BattleScripts = { // 6. force switch if (moveData.forceSwitch) damage = this.forceSwitch(damage, targets, pokemon, move, moveData, isSecondary, isSelf); - for (let j = 0; j < targets.length; j++) { - if (!damage[j] && damage[j] !== 0) targets[j] = false; + for (const i of targets.keys()) { + if (!damage[i] && damage[i] !== 0) targets[i] = false; } - /** @type {Pokemon[]} */ - let damagedTargets = []; - let damagedDamage = []; - for (let i = 0; i < targets.length; i++) { - if (typeof damage[i] === 'number' && targets[i]) { - damagedTargets.push(/** @type {Pokemon} */ (targets[i])); + const damagedTargets: Pokemon[] = []; + const damagedDamage = []; + for (const [i, t] of targets.entries()) { + if (typeof damage[i] === 'number' && t) { + damagedTargets.push(t); damagedDamage.push(damage[i]); } } @@ -846,8 +840,8 @@ let BattleScripts = { if (damagedDamage.length && !isSecondary && !isSelf) { this.runEvent('DamagingHit', damagedTargets, pokemon, move, damagedDamage); if (moveData.onAfterHit) { - for (const target of damagedTargets) { - this.singleEvent('AfterHit', moveData, {}, target, pokemon, move); + for (const t of damagedTargets) { + this.singleEvent('AfterHit', moveData, {}, t, pokemon, move); } } if (pokemon.hp && pokemon.hp <= pokemon.maxhp / 2 && pokemonOriginalHP > pokemon.maxhp / 2) { @@ -858,20 +852,18 @@ let BattleScripts = { return [damage, targets]; }, tryPrimaryHitEvent(damage, targets, pokemon, move, moveData, isSecondary) { - for (let i = 0; i < targets.length; i++) { - const target = targets[i]; + for (const [i, target] of targets.entries()) { if (!target) continue; damage[i] = this.runEvent('TryPrimaryHit', target, pokemon, moveData); } return damage; }, getSpreadDamage(damage, targets, pokemon, move, moveData, isSecondary, isSelf) { - for (let i = 0; i < targets.length; i++) { - let target = targets[i]; + for (const [i, target] of targets.entries()) { if (!target) continue; this.activeTarget = target; damage[i] = undefined; - let curDamage = this.getDamage(pokemon, target, moveData); + const curDamage = this.getDamage(pokemon, target, moveData); // getDamage has several possible return values: // // a number: @@ -911,13 +903,11 @@ let BattleScripts = { return damage; }, runMoveEffects(damage, targets, pokemon, move, moveData, isSecondary, isSelf) { - /**@type {?boolean | number | undefined} */ - let didAnything = damage.reduce(this.combineResults); + let didAnything: number | boolean | null | undefined = damage.reduce(this.combineResults); for (const [i, target] of targets.entries()) { if (target === false) continue; let hitResult; - /**@type {?boolean | number | undefined} */ - let didSomething = undefined; + let didSomething: number | boolean | null | undefined = undefined; if (target) { if (moveData.boosts && !target.fainted) { @@ -932,8 +922,8 @@ let BattleScripts = { didAnything = this.combineResults(didAnything, null); continue; } - let amount = target.baseMaxhp * moveData.heal[0] / moveData.heal[1]; - let d = target.heal((this.gen < 5 ? Math.floor : Math.round)(amount)); + const amount = target.baseMaxhp * moveData.heal[0] / moveData.heal[1]; + const d = target.heal((this.gen < 5 ? Math.floor : Math.round)(amount)); if (!d && d !== 0) { this.add('-fail', pokemon); this.attrLastMove('[still]'); @@ -1038,8 +1028,7 @@ let BattleScripts = { return damage; }, selfDrops(targets, pokemon, move, moveData, isSecondary) { - for (let i = 0; i < targets.length; i++) { - let target = targets[i]; + for (const target of targets) { if (target === false) continue; if (moveData.self && !move.selfDropped) { let selfRoll = 0; @@ -1058,10 +1047,10 @@ let BattleScripts = { if (!moveData.secondaries) return; for (const target of targets) { if (target === false) continue; - /** @type {SecondaryEffect[]} */ - let secondaries = this.runEvent('ModifySecondaries', target, pokemon, moveData, moveData.secondaries.slice()); + const secondaries: SecondaryEffect[] = + this.runEvent('ModifySecondaries', target, pokemon, moveData, moveData.secondaries.slice()); for (const secondary of secondaries) { - let secondaryRoll = this.random(100); + const secondaryRoll = this.random(100); if (typeof secondary.chance === 'undefined' || secondaryRoll < secondary.chance) { this.moveHit(target, pokemon, move, secondary, true, isSelf); } @@ -1071,7 +1060,7 @@ let BattleScripts = { forceSwitch(damage, targets, pokemon, move) { for (const [i, target] of targets.entries()) { if (target && target.hp > 0 && pokemon.hp > 0 && this.canSwitch(target.side)) { - let hitResult = this.runEvent('DragOut', target, pokemon, move); + const hitResult = this.runEvent('DragOut', target, pokemon, move); if (hitResult) { target.forceSwitchFlag = true; } else if (hitResult === false && move.category === 'Status') { @@ -1084,7 +1073,7 @@ let BattleScripts = { return damage; }, moveHit(target, pokemon, moveOrMoveName, moveData, isSecondary, isSelf) { - let retVal = this.spreadMoveHit([target], pokemon, moveOrMoveName, moveData, isSecondary, isSelf)[0][0]; + const retVal = this.spreadMoveHit([target], pokemon, moveOrMoveName, moveData, isSecondary, isSelf)[0][0]; return retVal === true ? undefined : retVal; }, @@ -1115,17 +1104,18 @@ let BattleScripts = { }, getZMove(move, pokemon, skipChecks) { - let item = pokemon.getItem(); + const item = pokemon.getItem(); if (!skipChecks) { if (pokemon.side.zMoveUsed) return; if (!item.zMove) return; if (item.itemUser && !item.itemUser.includes(pokemon.species.name)) return; - let moveData = pokemon.getMoveData(move); - if (!moveData || !moveData.pp) return; // Draining the PP of the base move prevents the corresponding Z-move from being used. + const moveData = pokemon.getMoveData(move); + // Draining the PP of the base move prevents the corresponding Z-move from being used. + if (!moveData || !moveData.pp) return; } if (item.zMoveFrom) { - if (move.name === item.zMoveFrom) return /** @type {string} */ (item.zMove); + if (move.name === item.zMoveFrom) return item.zMove as string; } else if (item.zMove === true) { if (move.type === item.zMoveType) { if (move.category === "Status") { @@ -1139,22 +1129,22 @@ let BattleScripts = { getActiveZMove(move, pokemon) { if (pokemon) { - let item = pokemon.getItem(); + const item = pokemon.getItem(); if (move.name === item.zMoveFrom) { // @ts-ignore - let zMove = this.dex.getActiveMove(item.zMove); + const zMove = this.dex.getActiveMove(item.zMove); zMove.isZPowered = true; return zMove; } } if (move.category === 'Status') { - let zMove = this.dex.getActiveMove(move); + const zMove = this.dex.getActiveMove(move); zMove.isZ = true; zMove.isZPowered = true; return zMove; } - let zMove = this.dex.getActiveMove(this.zMoveTable[move.type]); + const zMove = this.dex.getActiveMove(this.zMoveTable[move.type]); // @ts-ignore zMove.basePower = move.zMovePower; zMove.category = move.category; @@ -1165,14 +1155,16 @@ let BattleScripts = { }, canZMove(pokemon) { - if (pokemon.side.zMoveUsed || (pokemon.transformed && (pokemon.species.isMega || pokemon.species.isPrimal || pokemon.species.forme === "Ultra"))) return; - let item = pokemon.getItem(); + if (pokemon.side.zMoveUsed || + (pokemon.transformed && + (pokemon.species.isMega || pokemon.species.isPrimal || pokemon.species.forme === "Ultra")) + ) return; + const item = pokemon.getItem(); if (!item.zMove) return; if (item.itemUser && !item.itemUser.includes(pokemon.species.name)) return; let atLeastOne = false; let mustStruggle = true; - /**@type {ZMoveOptions} */ - let zMoves = []; + const zMoves: ZMoveOptions = []; for (const moveSlot of pokemon.moveSlots) { if (moveSlot.pp <= 0) { zMoves.push(null); @@ -1181,10 +1173,10 @@ let BattleScripts = { if (!moveSlot.disabled) { mustStruggle = false; } - let move = this.dex.getMove(moveSlot.move); + const move = this.dex.getMove(moveSlot.move); let zMoveName = this.getZMove(move, pokemon, true) || ''; if (zMoveName) { - let zMove = this.dex.getMove(zMoveName); + const zMove = this.dex.getMove(zMoveName); if (!zMove.isZ && zMove.category === 'Status') zMoveName = "Z-" + zMoveName; zMoves.push({move: zMoveName, target: zMove.target}); } else { @@ -1200,7 +1192,8 @@ let BattleScripts = { const altForme = species.otherFormes && this.dex.getSpecies(species.otherFormes[0]); const item = pokemon.getItem(); // Mega Rayquaza - if (altForme && altForme.isMega && altForme.requiredMove && pokemon.baseMoves.includes(toID(altForme.requiredMove)) && !item.zMove) { + if (altForme?.isMega && altForme?.requiredMove && + pokemon.baseMoves.includes(toID(altForme.requiredMove)) && !item.zMove) { return altForme.name; } // a hacked-in Megazard X can mega evolve into Megazard Y, but not into Megazard X @@ -1244,7 +1237,10 @@ let BattleScripts = { // {gigantamax?: string, maxMoves: {[k: string]: string} | null}[] if (!skipChecks) { if (!pokemon.canDynamax) return; - if (pokemon.species.isMega || pokemon.species.isPrimal || pokemon.species.forme === "Ultra" || pokemon.getItem().zMove || this.canMegaEvo(pokemon)) { + if ( + pokemon.species.isMega || pokemon.species.isPrimal || pokemon.species.forme === "Ultra" || + pokemon.getItem().zMove || this.canMegaEvo(pokemon) + ) { return; } // Some pokemon species are unable to dynamax @@ -1253,11 +1249,10 @@ let BattleScripts = { return; } } - /** @type {DynamaxOptions} */ - let result = {maxMoves: []}; - for (let moveSlot of pokemon.moveSlots) { - let move = this.dex.getMove(moveSlot.id); - let maxMove = this.getMaxMove(move, pokemon); + const result: DynamaxOptions = {maxMoves: []}; + for (const moveSlot of pokemon.moveSlots) { + const move = this.dex.getMove(moveSlot.id); + const maxMove = this.getMaxMove(move, pokemon); if (maxMove) { if (pokemon.maxMoveDisabled(maxMove)) { result.maxMoves.push({move: maxMove.id, target: maxMove.target, disabled: true}); @@ -1273,11 +1268,11 @@ let BattleScripts = { getMaxMove(move, pokemon) { if (typeof move === 'string') move = this.dex.getMove(move); if (pokemon.canGigantamax && move.category !== 'Status') { - let gMaxSpecies = this.dex.getSpecies(pokemon.canGigantamax); - let gMaxMove = this.dex.getMove(gMaxSpecies.isGigantamax); + const gMaxSpecies = this.dex.getSpecies(pokemon.canGigantamax); + const gMaxMove = this.dex.getMove(gMaxSpecies.isGigantamax); if (gMaxMove.exists && gMaxMove.type === move.type) return gMaxMove; } - let maxMove = this.dex.getMove(this.maxMoveTable[move.category === 'Status' ? move.category : move.type]); + const maxMove = this.dex.getMove(this.maxMoveTable[move.category === 'Status' ? move.category : move.type]); if (maxMove.exists) return maxMove; }, @@ -1286,8 +1281,8 @@ let BattleScripts = { let maxMove = this.dex.getActiveMove(this.maxMoveTable[move.category === 'Status' ? move.category : move.type]); if (move.category !== 'Status') { if (pokemon.canGigantamax) { - let gMaxSpecies = this.dex.getSpecies(pokemon.canGigantamax); - let gMaxMove = this.dex.getActiveMove(gMaxSpecies.isGigantamax ? gMaxSpecies.isGigantamax : ''); + const gMaxSpecies = this.dex.getSpecies(pokemon.canGigantamax); + const gMaxMove = this.dex.getActiveMove(gMaxSpecies.isGigantamax ? gMaxSpecies.isGigantamax : ''); if (gMaxMove.exists && gMaxMove.type === move.type) maxMove = gMaxMove; } if (!move.gmaxPower) throw new Error(`${move.name} doesn't have a gmaxPower`); @@ -1314,7 +1309,7 @@ let BattleScripts = { pokemon.formeChange(speciesid, pokemon.getItem(), true); // Limit one mega evolution - let wasMega = pokemon.canMegaEvo; + const wasMega = pokemon.canMegaEvo; for (const ally of side.pokemon) { if (wasMega) { ally.canMegaEvo = null; @@ -1342,10 +1337,9 @@ let BattleScripts = { move.self = {slotCondition: 'healreplacement'}; break; case 'clearnegativeboost': - /** @type {{[k: string]: number}} */ - let boosts = {}; - for (let i in pokemon.boosts) { - // @ts-ignore + const boosts: SparseBoostsTable = {}; + let i: BoostName; + for (i in pokemon.boosts) { if (pokemon.boosts[i] < 0) { boosts[i] = 0; } @@ -1379,5 +1373,3 @@ let BattleScripts = { return CHOOSABLE_TARGETS.has(targetType); }, }; - -exports.BattleScripts = BattleScripts; diff --git a/data/statuses.js b/data/statuses.ts similarity index 94% rename from data/statuses.js rename to data/statuses.ts index b7c7afd92a..74feeb8270 100644 --- a/data/statuses.js +++ b/data/statuses.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: PureEffectData}} */ -let BattleStatuses = { +export const BattleStatuses: {[k: string]: PureEffectData} = { brn: { name: 'brn', id: 'brn', @@ -189,13 +186,10 @@ let BattleStatuses = { return; } this.activeTarget = pokemon; - let damage = this.getDamage(pokemon, pokemon, 40); + const damage = this.getDamage(pokemon, pokemon, 40); if (typeof damage !== 'number') throw new Error("Confusion damage not dealt"); - this.damage(damage, pokemon, pokemon, /** @type {ActiveMove} */ ({ - id: 'confused', - effectType: 'Move', - type: '???', - })); + const activeMove = {id: toID('confused'), effectType: 'Move', type: '???'}; + this.damage(damage, pokemon, pokemon, activeMove as ActiveMove); return false; }, }, @@ -235,7 +229,7 @@ let BattleStatuses = { num: 0, duration: 5, durationCallback(target, source) { - if (source && source.hasItem('gripclaw')) return 8; + if (source?.hasItem('gripclaw')) return 8; return this.random(5, 7); }, onStart(pokemon, source) { @@ -331,7 +325,10 @@ let BattleStatuses = { pokemon.removeVolatile('choicelock'); return; } - if (!pokemon.ignoringItem() && !pokemon.volatiles['dynamax'] && move.id !== this.effectData.move && move.id !== 'struggle') { + if ( + !pokemon.ignoringItem() && !pokemon.volatiles['dynamax'] && + move.id !== this.effectData.move && move.id !== 'struggle' + ) { // Fails unless the Choice item is being ignored, and no PP is lost this.addMove('move', pokemon, move.name); this.attrLastMove('[still]'); @@ -401,9 +398,10 @@ let BattleStatuses = { if (data.source.hasAbility('adaptability') && this.gen >= 6) { data.moveData.stab = 2; } - const hitMove = new this.dex.Data.Move(data.moveData); + // @ts-ignore + const hitMove: ActiveMove = new this.dex.Data.Move(data.moveData); - this.trySpreadMoveHit([target], data.source, /** @type {ActiveMove} */(/** @type {unknown} */(hitMove))); + this.trySpreadMoveHit([target], data.source, hitMove); }, }, healreplacement: { @@ -437,9 +435,9 @@ let BattleStatuses = { onStallMove(pokemon) { // this.effectData.counter should never be undefined here. // However, just in case, use 1 if it is undefined. - let counter = this.effectData.counter || 1; + const counter = this.effectData.counter || 1; this.debug("Success chance: " + Math.round(100 / counter) + "%"); - let success = this.randomChance(1, counter); + const success = this.randomChance(1, counter); if (!success) delete pokemon.volatiles['stall']; return success; }, @@ -473,7 +471,7 @@ let BattleStatuses = { effectType: 'Weather', duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('damprock')) { + if (source?.hasItem('damprock')) { return 8; } return 5; @@ -490,7 +488,7 @@ let BattleStatuses = { } }, onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { + if (effect?.effectType === 'Ability') { if (this.gen <= 5) this.effectData.duration = 0; this.add('-weather', 'RainDance', '[from] ability: ' + effect, '[of] ' + source); } else { @@ -547,7 +545,7 @@ let BattleStatuses = { effectType: 'Weather', duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('heatrock')) { + if (source?.hasItem('heatrock')) { return 8; } return 5; @@ -564,7 +562,7 @@ let BattleStatuses = { } }, onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { + if (effect?.effectType === 'Ability') { if (this.gen <= 5) this.effectData.duration = 0; this.add('-weather', 'SunnyDay', '[from] ability: ' + effect, '[of] ' + source); } else { @@ -629,7 +627,7 @@ let BattleStatuses = { effectType: 'Weather', duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('smoothrock')) { + if (source?.hasItem('smoothrock')) { return 8; } return 5; @@ -643,7 +641,7 @@ let BattleStatuses = { } }, onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { + if (effect?.effectType === 'Ability') { if (this.gen <= 5) this.effectData.duration = 0; this.add('-weather', 'Sandstorm', '[from] ability: ' + effect, '[of] ' + source); } else { @@ -669,13 +667,13 @@ let BattleStatuses = { effectType: 'Weather', duration: 5, durationCallback(source, effect) { - if (source && source.hasItem('icyrock')) { + if (source?.hasItem('icyrock')) { return 8; } return 5; }, onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { + if (effect?.effectType === 'Ability') { if (this.gen <= 5) this.effectData.duration = 0; this.add('-weather', 'Hail', '[from] ability: ' + effect, '[of] ' + source); } else { @@ -787,8 +785,7 @@ let BattleStatuses = { onTypePriority: 1, onType(types, pokemon) { if (pokemon.transformed || pokemon.ability !== 'multitype' && this.gen >= 8) return types; - /** @type {string | undefined} */ - let type = 'Normal'; + let type: string | undefined = 'Normal'; if (pokemon.ability === 'multitype') { type = pokemon.getItem().onPlate; if (!type) { @@ -805,8 +802,7 @@ let BattleStatuses = { onTypePriority: 1, onType(types, pokemon) { if (pokemon.transformed || pokemon.ability !== 'rkssystem' && this.gen >= 8) return types; - /** @type {string | undefined} */ - let type = 'Normal'; + let type: string | undefined = 'Normal'; if (pokemon.ability === 'rkssystem') { type = pokemon.getItem().onMemory; if (!type) { @@ -817,5 +813,3 @@ let BattleStatuses = { }, }, }; - -exports.BattleStatuses = BattleStatuses; diff --git a/data/typechart.js b/data/typechart.ts similarity index 98% rename from data/typechart.js rename to data/typechart.ts index f7210d7a87..3ab659b967 100644 --- a/data/typechart.js +++ b/data/typechart.ts @@ -1,7 +1,4 @@ -'use strict'; - -/**@type {{[k: string]: TypeData}} */ -let BattleTypeChart = { +export const BattleTypeChart: {[k: string]: TypeData} = { Bug: { damageTaken: { Bug: 0, @@ -444,5 +441,3 @@ let BattleTypeChart = { HPdvs: {atk: 14, def: 13}, }, }; - -exports.BattleTypeChart = BattleTypeChart; diff --git a/sim/battle-queue.ts b/sim/battle-queue.ts index 7badfa3d30..6842cae885 100644 --- a/sim/battle-queue.ts +++ b/sim/battle-queue.ts @@ -261,7 +261,7 @@ export class BattleQueue extends Array { } willMove(pokemon: Pokemon) { - if (pokemon.fainted) return false; + if (pokemon.fainted) return null; for (const action of this) { if (action.choice === 'move' && action.pokemon === pokemon) { return action; @@ -297,7 +297,7 @@ export class BattleQueue extends Array { return action; } } - return false; + return null; } /** diff --git a/sim/battle.ts b/sim/battle.ts index 7d4c2f2033..52c019fae2 100644 --- a/sim/battle.ts +++ b/sim/battle.ts @@ -130,6 +130,7 @@ export class Battle { readonly maxMoveTable: {[k: string]: string}; readonly NOT_FAIL: ''; + readonly HIT_SUBSTITUTE: 0; readonly FAIL: false; readonly SILENT_FAIL: null; @@ -204,6 +205,7 @@ export class Battle { this.hints = new Set(); this.NOT_FAIL = ''; + this.HIT_SUBSTITUTE = 0; this.FAIL = false; this.SILENT_FAIL = null; diff --git a/sim/dex.ts b/sim/dex.ts index 27b34f3a28..fe15ab8e8f 100644 --- a/sim/dex.ts +++ b/sim/dex.ts @@ -49,8 +49,8 @@ import {PRNG, PRNGSeed} from './prng'; const BASE_MOD = 'gen8' as ID; const DEFAULT_MOD = BASE_MOD; -const DATA_DIR = path.resolve(__dirname, '../data'); -const MODS_DIR = path.resolve(__dirname, '../data/mods'); +const DATA_DIR = path.resolve(__dirname, '../.data-dist'); +const MODS_DIR = path.resolve(__dirname, '../.data-dist/mods'); const FORMATS = path.resolve(__dirname, '../config/formats'); const dexes: {[mod: string]: ModdedDex} = Object.create(null); @@ -1133,7 +1133,7 @@ export class ModdedDex { getTeamGenerator(format: Format | string, seed: PRNG | PRNGSeed | null = null) { // eslint-disable-next-line @typescript-eslint/no-var-requires - const TeamGenerator = require(dexes['base'].forFormat(format).dataDir + '/random-teams'); + const TeamGenerator = require(dexes['base'].forFormat(format).dataDir + '/random-teams').default; return new TeamGenerator(format, seed); } @@ -1502,8 +1502,8 @@ export class ModdedDex { this.includeFormats(); } else { for (const dataType of DATA_TYPES) { - const parentTypedData = parentDex.data[dataType]; - const childTypedData = dataCache[dataType] || (dataCache[dataType] = {}); + const parentTypedData: DexTable = parentDex.data[dataType]; + const childTypedData: DexTable = dataCache[dataType] || (dataCache[dataType] = {}); for (const entryId in parentTypedData) { if (childTypedData[entryId] === null) { // null means don't inherit @@ -1523,10 +1523,8 @@ export class ModdedDex { delete childTypedData[entryId].inherit; // Merge parent into children entry, preserving existing childs' properties. - // @ts-ignore for (const key in parentTypedData[entryId]) { if (key in childTypedData[entryId]) continue; - // @ts-ignore childTypedData[entryId][key] = parentTypedData[entryId][key]; } } diff --git a/sim/global-types.ts b/sim/global-types.ts index ce5f1c677b..7aec90846b 100644 --- a/sim/global-types.ts +++ b/sim/global-types.ts @@ -1248,7 +1248,7 @@ interface BattleScriptsData { this: Battle, damage: SpreadMoveDamage, targets: SpreadMoveTargets, source: Pokemon, move: ActiveMove, moveData: ActiveMove, isSecondary?: boolean, isSelf?: boolean ) => SpreadMoveDamage; - getZMove?: (this: Battle, move: Move, pokemon: Pokemon, skipChecks?: boolean) => string | undefined; + getZMove?: (this: Battle, move: Move, pokemon: Pokemon, skipChecks?: boolean) => string | true | undefined; hitStepAccuracy?: (this: Battle, targets: Pokemon[], pokemon: Pokemon, move: ActiveMove) => boolean[]; hitStepBreakProtect?: (this: Battle, targets: Pokemon[], pokemon: Pokemon, move: ActiveMove) => undefined; hitStepMoveHitLoop?: (this: Battle, targets: Pokemon[], pokemon: Pokemon, move: ActiveMove) => SpreadMoveDamage; @@ -1369,7 +1369,7 @@ interface ModdedBattleScriptsData extends Partial { getMegaDeltas?: (this: Battle, megaSpecies: Species) => AnyObject; getMixedSpecies?: (this: Battle, originalName: string, megaName: string) => Species; getAbility?: (this: Battle, name: string | Ability) => Ability; - getZMove?: (this: Battle, move: Move, pokemon: Pokemon, skipChecks?: boolean) => string | undefined; + getZMove?: (this: Battle, move: Move, pokemon: Pokemon, skipChecks?: boolean) => string | true | undefined; getActiveZMove?: (this: Battle, move: Move, pokemon: Pokemon) => ActiveMove; canZMove?: (this: Battle, pokemon: Pokemon) => ZMoveOptions | void; } diff --git a/sim/team-validator.ts b/sim/team-validator.ts index f6b1a591b7..73b704f28c 100644 --- a/sim/team-validator.ts +++ b/sim/team-validator.ts @@ -1013,7 +1013,7 @@ export class TeamValidator { } if (eggGroups[0] === 'Undiscovered') eggGroups = dex.getSpecies(species.evos[0]).eggGroups; if (eggGroups[0] === 'Undiscovered' || !eggGroups.length) { - throw new Error(`${species.name} has no egg groups`); + throw new Error(`${species.name} has no egg groups for source ${source}`); } // no chainbreeding necessary if the father can be Smeargle if (!getAll && eggGroups.includes('Field')) return true; diff --git a/test/sim/misc/random-teams.js b/test/sim/misc/random-teams.js index 777145bf7d..e317d4ee72 100644 --- a/test/sim/misc/random-teams.js +++ b/test/sim/misc/random-teams.js @@ -136,7 +136,7 @@ describe(`Factory sets`, function () { for (const filename of ['bss-factory-sets', 'factory-sets']) { it(`should have valid sets in ${filename}.json (slow)`, function () { this.timeout(0); - const setsJSON = require(`../../../data/mods/gen7/${filename}.json`); + const setsJSON = require(`../../../.data-dist/mods/gen7/${filename}.json`); for (const type in setsJSON) { const typeTable = filename === 'bss-factory-sets' ? setsJSON : setsJSON[type]; @@ -176,7 +176,7 @@ describe(`Factory sets`, function () { for (const moveName of [].concat(moveSpec)) { const move = Dex.getMove(moveName); assert(move.exists, `invalid move "${moveName}" of ${species}`); - assert(move.name === moveName, `miscapitalized move "${moveName}" of ${species}`); + assert(move.name === moveName, `miscapitalized move "${moveName}" ≠ "${move.name}" of ${species}`); assert(validLearnset(move, set, vType), `illegal move "${moveName}" of ${species}`); } }