Typescript data (#6553)

This commit is contained in:
Kris Johnson
2020-04-23 10:16:09 -06:00
committed by GitHub
parent 2c08cde63a
commit 28bf388b98
129 changed files with 6599 additions and 6930 deletions

View File

@@ -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\ )?

View File

@@ -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"}],

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

36
build
View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

7
data/learnsets.js → data/learnsets.ts Executable file → Normal file
View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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."];
}
}
}
}
},
},
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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',
],
},
};

View File

@@ -1,9 +0,0 @@
'use strict';
/**@type {ModdedBattleScriptsData} */
let BattleScripts = {
inherit: 'gen6',
gen: 5,
};
exports.BattleScripts = BattleScripts;

View File

@@ -0,0 +1,4 @@
export const BattleScripts: ModdedBattleScriptsData = {
inherit: 'gen6',
gen: 5,
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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.",

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,9 +0,0 @@
'use strict';
/** @type {ModdedBattleScriptsData} */
let BattleScripts = {
inherit: 'gen7',
gen: 6,
};
exports.BattleScripts = BattleScripts;

View File

@@ -0,0 +1,4 @@
export const BattleScripts: ModdedBattleScriptsData = {
inherit: 'gen7',
gen: 6,
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

5
data/mods/gen7/items.js → data/mods/gen7/items.ts Executable file → Normal file
View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,8 +0,0 @@
'use strict';
/** @type {ModdedBattleScriptsData} */
let BattleScripts = {
gen: 7,
};
exports.BattleScripts = BattleScripts;

View File

@@ -0,0 +1,3 @@
export const BattleScripts: ModdedBattleScriptsData = {
gen: 7,
};

View File

@@ -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');

View File

@@ -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;

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
},
},

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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;

View File

@@ -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;

Some files were not shown because too many files have changed in this diff Show More