mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-09-10 02:05:35 -05:00
Typescript data (#6553)
This commit is contained in:
@@ -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\ )?
|
||||
|
||||
@@ -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"}],
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
36
build
@@ -47,6 +47,16 @@ function replace(file, replacements) {
|
||||
}
|
||||
});
|
||||
}
|
||||
function copyOverData(file) {
|
||||
var source = './data/' + file;
|
||||
var dest = './.data-dist/' + file;
|
||||
fs.readFile(source, function (err, text) {
|
||||
if (err) throw err;
|
||||
fs.writeFile(dest, text, function (err) {
|
||||
if (err) throw err;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
var sucraseVersion = require('sucrase').getVersion().split('.');
|
||||
@@ -61,19 +71,39 @@ try {
|
||||
shell('npm install --production');
|
||||
}
|
||||
|
||||
sucrase('./data', './.data-dist');
|
||||
sucrase('./sim', './.sim-dist');
|
||||
sucrase('./lib', './.lib-dist');
|
||||
sucrase('./server', './.server-dist');
|
||||
sucrase('./tools/set-import', './tools/set-import', '--exclude-dirs=sets');
|
||||
|
||||
if (!fs.existsSync('./.data-dist/README.md')) {
|
||||
const text = '**NOTE**: This folder contains the compiled output of the `data/` directory.\n' +
|
||||
'You should be editing the `.ts` files there and then running `npm run build` or\n' +
|
||||
'`./pokemon-showdown` to force these `.js` files to be recreated.\n';
|
||||
fs.writeFile('./.data-dist/README.md', text, function () {});
|
||||
fs.writeFile('./.sim-dist/README.md', text.replace('data/', 'sim/'), function () {});
|
||||
fs.writeFile('./.server-dist/README.md', text.replace('data/', 'server/'), function () {});
|
||||
fs.writeFile('./.lib-dist/README.md', text.replace('data/', 'lib/'), function () {});
|
||||
}
|
||||
|
||||
// sucrase doesn't copy JSON over, so we'll have to do it ourselves
|
||||
copyOverData('mods/gen7/factory-sets.json');
|
||||
copyOverData('mods/gen7/bss-factory-sets.json');
|
||||
copyOverData('mods/gen6/factory-sets.json');
|
||||
|
||||
// NOTE: replace is asynchronous - add additional replacements for the same path in one call instead of making multiple calls.
|
||||
replace(path.join(__dirname, '.data-dist'), [
|
||||
{regex: /(require\(.*?)(lib|sim)/g, replace: `$1.$2-dist`},
|
||||
]);
|
||||
replace(path.join(__dirname, '.sim-dist'), [
|
||||
{regex: new RegExp(`(require\\\(.*?)(lib)(.*?\\\))`, 'g'), replace: `$1.lib-dist$3`},
|
||||
{regex: /(require\(.*?)(lib)/g, replace: `$1.lib-dist`},
|
||||
]);
|
||||
replace(path.join(__dirname, '.server-dist'), [
|
||||
{regex: new RegExp(`(require\\\(.*?)(lib|sim)(.*?\\\))`, 'g'), replace: `$1.$2-dist$3`},
|
||||
{regex: /(require\(.*?)(lib|sim)/g, replace: `$1.$2-dist`},
|
||||
]);
|
||||
replace(path.join(__dirname, './tools/set-import/importer.js'), [
|
||||
{regex: new RegExp(`(require\\\(.*?)(lib|sim)(.*?\\\))`, 'g'), replace: `$1.$2-dist$3`},
|
||||
{regex: /(require\(.*?)(lib|sim)/g, replace: `$1.$2-dist`},
|
||||
]);
|
||||
|
||||
// Make sure config.js exists. If not, copy it over synchronously from
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
7
data/learnsets.js → data/learnsets.ts
Executable file → Normal 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;
|
||||
@@ -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;
|
||||
@@ -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
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
28
data/mods/gen4/rulesets.ts
Normal file
28
data/mods/gen4/rulesets.ts
Normal 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."];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
16
data/mods/gen5/rulesets.ts
Normal file
16
data/mods/gen5/rulesets.ts
Normal 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',
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**@type {ModdedBattleScriptsData} */
|
||||
let BattleScripts = {
|
||||
inherit: 'gen6',
|
||||
gen: 5,
|
||||
};
|
||||
|
||||
exports.BattleScripts = BattleScripts;
|
||||
4
data/mods/gen5/scripts.ts
Normal file
4
data/mods/gen5/scripts.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const BattleScripts: ModdedBattleScriptsData = {
|
||||
inherit: 'gen6',
|
||||
gen: 5,
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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.",
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -1,9 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/** @type {ModdedBattleScriptsData} */
|
||||
let BattleScripts = {
|
||||
inherit: 'gen7',
|
||||
gen: 6,
|
||||
};
|
||||
|
||||
exports.BattleScripts = BattleScripts;
|
||||
4
data/mods/gen6/scripts.ts
Normal file
4
data/mods/gen6/scripts.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const BattleScripts: ModdedBattleScriptsData = {
|
||||
inherit: 'gen7',
|
||||
gen: 6,
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
5
data/mods/gen7/items.js → data/mods/gen7/items.ts
Executable file → Normal 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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
@@ -1,8 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/** @type {ModdedBattleScriptsData} */
|
||||
let BattleScripts = {
|
||||
gen: 7,
|
||||
};
|
||||
|
||||
exports.BattleScripts = BattleScripts;
|
||||
3
data/mods/gen7/scripts.ts
Normal file
3
data/mods/gen7/scripts.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const BattleScripts: ModdedBattleScriptsData = {
|
||||
gen: 7,
|
||||
};
|
||||
@@ -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');
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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) {
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
},
|
||||
},
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
2997
data/mods/roulettemons/learnsets.ts
Normal file
2997
data/mods/roulettemons/learnsets.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
@@ -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
Reference in New Issue
Block a user