Merge branch 'master' into psychoshift

This commit is contained in:
André Bastos Dias 2026-01-04 13:57:05 +00:00 committed by GitHub
commit 8dddfb0352
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
310 changed files with 171757 additions and 33880 deletions

View File

@ -17,7 +17,15 @@ jobs:
strategy:
matrix:
node-version: [18.x]
# Testing multiple Node versions makes CI take forever, and basically
# never actually finds anything we care about. Testing one is enough.
#
# This number should be left at the oldest Node LTS version capable of
# running Node without errors (it doesn't matter if it's unsupported).
# Please freely bump this version (and the check in `pokemon-showdown`
# and `server/index.ts`) if you want to use features from newer versions;
# this is purely for our own reference, not to constrain programmers.
node-version: ['18.x']
steps:
- uses: actions/checkout@v4

View File

@ -3,12 +3,12 @@ name: Update the npm package version
on:
workflow_dispatch:
inputs:
version:
required: true
type: choice
description: Version type
default: patch
options:
version:
required: true
type: choice
description: Version type
default: patch
options:
- major
- minor
- patch
@ -16,15 +16,18 @@ on:
- preminor
- prepatch
- prerelease
jobs:
update_version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'npm'
- id: bump_version
run: |
echo "old_version=$(jq -r .version package.json)" >> "$GITHUB_OUTPUT"
@ -33,10 +36,10 @@ jobs:
- uses: peter-evans/create-pull-request@v7
with:
title: Bump package.json version to v${{ steps.bump_version.outputs.new_version }}}
title: Bump package.json version to v${{ steps.bump_version.outputs.new_version }}
body: |
Bump package.json version from `v${{ steps.bump_version.outputs.old_version }}` to `v${{ steps.bump_version.outputs.new_version }}`
commit-message: Bump package.json version to v${{ steps.bump_version.outputs.new_version }}
branch: npm-version-bump
base: master

View File

@ -1,8 +1,7 @@
config/formats.ts @KrisXV @Marty-D
data/mods/gen9ssb/ @HoeenCoder @HisuianZoroark @KrisXV
data/random-battles/ @AnnikaCodes @KrisXV @MathyFurret @ACakeWearingAHat @livid-washed @adrivrie
data/random-battles/ @KrisXV @MathyFurret @ACakeWearingAHat @livid-washed @adrivrie
data/text/ @Marty-D
data/cg-team*.ts @KrisXV @pyuk
databases/ @monsanto
lib/sql.ts @mia-pi-git
server/artemis/* @mia-pi-git

View File

@ -301,13 +301,13 @@ In general, we prefer modern ways of writing things as long as they're supported
- Multiline template strings: A frequent source of bugs (and also weird for readability), so we prefer to explicitly use `\n` and concatenate over multiple lines.
- `async`/`await`: We prefer it for readability, but in certain cases we use raw Promises or even callbacks for performance. Don't worry about it too much; we usually won't nitpick code that uses any async implementation (although we might insist on `async`/`await` if the readability difference is huge).
- `async`/`await`: We prefer it for readability, but in certain cases we use raw Promises or even callbacks for performance. Don't worry about it too much; we usually won't nitpick code that uses either async implementation (although we might insist on `async`/`await` if the readability difference is huge).
- getters/setters/`Proxy`: We are generally very anti-magic. There are certain places in the code we do use magic where it's massively DRYer (or for historical reasons), but we prefer to explicitly mark that setting a variable is actually running a function with many and varied side effects. Please have a better reason than "`.foo` is less visual clutter than `.getFoo()`".
- Constant Enums: Don't use; we prefer constant union types, like `type Category = 'Physical' | 'Special' | 'Status'`
- Default Properties: Mediocre performance when compiled with `sucrase`. This is fine for objects that are rarely created, but prefer setting properties directly in a constructor, for objects created in inner loops.
- Default Properties: Use.
Dependencies

17
build
View File

@ -2,10 +2,12 @@
"use strict";
try {
// technically this was introduced in Node 17, but we'll ask for the most recent LTS with it to be safe
structuredClone({});
// fetch was introduced in Node 18, which is EOL,
// so we'll ask for the most recent "Active LTS" with it to be safe
// https://nodejs.org/en/about/previous-releases
fetch;
} catch (e) {
console.log("We require Node.js version 18 or later; you're using " + process.version);
console.error("We require Node.js version 22 or later; you're using " + process.version);
process.exit(1);
}
@ -22,13 +24,10 @@ function shell(cmd) {
// Check to make sure the most recently added or updated dependency is installed at the correct version
try {
var version = require('esbuild').version.split('.');
if (parseInt(version[1]) < 16) {
throw new Error("esbuild version too old");
}
require.resolve('ts-chacha20');
} catch (e) {
console.log('Installing dependencies...');
shell('npm install');
shell('npm ci');
}
// Make sure config.js exists. If not, copy it over synchronously from
@ -48,7 +47,5 @@ try {
// for some reason, esbuild won't be requirable until a tick has passed
// see https://stackoverflow.com/questions/53270058/node-cant-find-certain-modules-after-synchronous-install
setImmediate(() => {
// npm package, don't rebuild
if (process.argv[2] === 'postinstall' && fs.existsSync('dist')) return;
require('./tools/build-utils').transpile(force, decl);
});

View File

@ -45,7 +45,7 @@ Bans are just a `-` followed by the thing you want to ban.
`- Future` - ban things that only appears in a future generation (such as Arceus in Gen 1)
`- Custom` - ban made-up things other than CAP (such as Magikarp's Revenge, or Staff Bros moves)
`- Custom` - (DEPRECATED) ban miscellaneous other things
`- Nonexistent` - catch-all to ban all nonexistent Pokémon, items, etc. Includes: `- CAP, - Past, - Future, - LGPE`

View File

@ -15,23 +15,6 @@ exports.port = 8000;
*/
exports.bindaddress = '0.0.0.0';
/**
* workers - the number of networking child processes to spawn
* This should be no greater than the number of threads available on your
* server's CPU. If you're not sure how many you have, you can check from a
* terminal by running:
*
* $ node -e "console.log(require('os').cpus().length)"
*
* Using more workers than there are available threads will cause performance
* issues. Keeping a couple threads available for use for OS-related work and
* other PS processes will likely give you the best performance, if your
* server's CPU is capable of multithreading. If you don't know what any of
* this means or you are unfamiliar with PS' networking code, leave this set
* to 1.
*/
exports.workers = 1;
/**
* wsdeflate - compresses WebSocket messages
* Toggles use of the Sec-WebSocket-Extension permessage-deflate extension.
@ -92,6 +75,50 @@ Main's SSL deploy script from Let's Encrypt looks like:
*/
exports.proxyip = false;
// subprocesses - the number of child processes to use for various tasks.
// Can be set to `0` instead of `{...}` to stop using subprocesses, if you're running out of RAM.
exports.subprocesses = {
/**
* network - the number of networking child processes to spawn
* This should be no greater than the number of threads available on your
* server's CPU. If you're not sure how many you have, you can check from a
* terminal by running:
*
* $ node -e "console.log(require('os').cpus().length)"
*
* Using more workers than there are available threads will cause performance
* issues. Keeping a couple threads available for use for OS-related work and
* other PS processes will likely give you the best performance, if your
* server's CPU is capable of multithreading. If you don't know what any of
* this means or you are unfamiliar with PS' networking code, leave this set
* to 1.
*/
network: 1,
/**
* for simulating battles
* You should leave this at 1 unless your server has a very large
* amount of traffic (i.e. hundreds of concurrent battles).
*/
simulator: 1,
// beyond this point, it'd be very weird if you needed more than one of each of these
/** for validating teams */
validator: 1,
/** for user authentication */
verifier: 1,
localartemis: 1,
remoteartemis: 1,
friends: 1,
chatdb: 1,
modlog: 1,
pm: 1,
/** for the battlesearch chat plugin */
battlesearch: 1,
/** datasearch - for the datasearch chat plugin */
datasearch: 1,
};
/**
* Various debug options
*
@ -401,15 +428,6 @@ exports.logchallenges = false;
*/
exports.loguserstats = 1000 * 60 * 10; // 10 minutes
/**
* validatorprocesses - the number of processes to use for validating teams
* simulatorprocesses - the number of processes to use for handling battles
* You should leave both of these at 1 unless your server has a very large
* amount of traffic (i.e. hundreds of concurrent battles).
*/
exports.validatorprocesses = 1;
exports.simulatorprocesses = 1;
/**
* inactiveuserthreshold - how long a user must be inactive before being pruned
* from the `users` array. The default is 1 hour.

File diff suppressed because it is too large Load Diff

View File

@ -127,6 +127,7 @@ List of all in-battle forme changes:
- Darmanitan (Zen Mode)
- Meloetta (Relic Song)
- Shaymin-Sky (Frozen status)
- Ramnarok (Polar Flare)
- Mega evolutions
- Primal reversions
- Ultra Burst

View File

@ -60,7 +60,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
const noModifyType = [
'judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'terrainpulse', 'weatherball',
];
if (move.type === 'Normal' && !noModifyType.includes(move.id) &&
if (move.type === 'Normal' && (!noModifyType.includes(move.id) || this.activeMove?.isMax) &&
!(move.isZ && move.category !== 'Status') && !(move.name === 'Tera Blast' && pokemon.terastallized)) {
move.type = 'Flying';
move.typeChangerBoosted = this.effect;
@ -740,33 +740,29 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
num: 238,
},
cudchew: {
onEatItem(item, pokemon) {
if (item.isBerry && pokemon.addVolatile('cudchew')) {
pokemon.volatiles['cudchew'].berry = item;
onEatItem(item, pokemon, source, effect) {
if (item.isBerry && (!effect || !['bugbite', 'pluck'].includes(effect.id))) {
this.effectState.berry = item;
this.effectState.counter = 2;
// This is needed in case the berry was eaten during residuals, preventing the timer from decreasing this turn
if (!this.queue.peek()) this.effectState.counter--;
}
},
onEnd(pokemon) {
delete pokemon.volatiles['cudchew'];
},
condition: {
noCopy: true,
duration: 2,
onRestart() {
this.effectState.duration = 2;
},
onResidualOrder: 28,
onResidualSubOrder: 2,
onEnd(pokemon) {
if (pokemon.hp) {
const item = this.effectState.berry;
this.add('-activate', pokemon, 'ability: Cud Chew');
this.add('-enditem', pokemon, item.name, '[eat]');
if (this.singleEvent('Eat', item, null, pokemon, null, null)) {
this.runEvent('EatItem', pokemon, null, null, item);
}
if (item.onEat) pokemon.ateBerry = true;
onResidualOrder: 28,
onResidualSubOrder: 2,
onResidual(pokemon) {
if (!this.effectState.berry || !pokemon.hp) return;
if (--this.effectState.counter <= 0) {
const item = this.effectState.berry;
this.add('-activate', pokemon, 'ability: Cud Chew');
this.add('-enditem', pokemon, item.name, '[eat]');
if (this.singleEvent('Eat', item, null, pokemon, null, null)) {
this.runEvent('EatItem', pokemon, null, null, item);
}
},
if (item.onEat) pokemon.ateBerry = true;
delete this.effectState.berry;
delete this.effectState.counter;
}
},
flags: {},
name: "Cud Chew",
@ -1166,8 +1162,8 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
embodyaspectcornerstone: {
onStart(pokemon) {
if (pokemon.baseSpecies.name === 'Ogerpon-Cornerstone-Tera' && pokemon.terastallized &&
this.effectState.embodied !== pokemon.previouslySwitchedIn) {
this.effectState.embodied = pokemon.previouslySwitchedIn;
!this.effectState.embodied) {
this.effectState.embodied = true;
this.boost({ def: 1 }, pokemon);
}
},
@ -1179,8 +1175,8 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
embodyaspecthearthflame: {
onStart(pokemon) {
if (pokemon.baseSpecies.name === 'Ogerpon-Hearthflame-Tera' && pokemon.terastallized &&
this.effectState.embodied !== pokemon.previouslySwitchedIn) {
this.effectState.embodied = pokemon.previouslySwitchedIn;
!this.effectState.embodied) {
this.effectState.embodied = true;
this.boost({ atk: 1 }, pokemon);
}
},
@ -1192,8 +1188,8 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
embodyaspectteal: {
onStart(pokemon) {
if (pokemon.baseSpecies.name === 'Ogerpon-Teal-Tera' && pokemon.terastallized &&
this.effectState.embodied !== pokemon.previouslySwitchedIn) {
this.effectState.embodied = pokemon.previouslySwitchedIn;
!this.effectState.embodied) {
this.effectState.embodied = true;
this.boost({ spe: 1 }, pokemon);
}
},
@ -1205,8 +1201,8 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
embodyaspectwellspring: {
onStart(pokemon) {
if (pokemon.baseSpecies.name === 'Ogerpon-Wellspring-Tera' && pokemon.terastallized &&
this.effectState.embodied !== pokemon.previouslySwitchedIn) {
this.effectState.embodied = pokemon.previouslySwitchedIn;
!this.effectState.embodied) {
this.effectState.embodied = true;
this.boost({ spd: 1 }, pokemon);
}
},
@ -1547,7 +1543,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
const noModifyType = [
'judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'terrainpulse', 'weatherball',
];
if (move.type === 'Normal' && !noModifyType.includes(move.id) &&
if (move.type === 'Normal' && (!noModifyType.includes(move.id) || this.activeMove?.isMax) &&
!(move.isZ && move.category !== 'Status') && !(move.name === 'Tera Blast' && pokemon.terastallized)) {
move.type = 'Electric';
move.typeChangerBoosted = this.effect;
@ -2263,12 +2259,12 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
},
libero: {
onPrepareHit(source, target, move) {
if (this.effectState.libero === source.previouslySwitchedIn) return;
if (this.effectState.libero) return;
if (move.hasBounced || move.flags['futuremove'] || move.sourceEffect === 'snatch' || move.callsMove) return;
const type = move.type;
if (type && type !== '???' && source.getTypes().join() !== type) {
if (!source.setType(type)) return;
this.effectState.libero = source.previouslySwitchedIn;
this.effectState.libero = true;
this.add('-start', source, 'typechange', type, '[from] ability: Libero');
}
},
@ -2406,9 +2402,6 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
move.hasBounced = true; // only bounce once in free-for-all battles
return null;
},
condition: {
duration: 1,
},
flags: { breakable: 1 },
name: "Magic Bounce",
rating: 4,
@ -2930,9 +2923,9 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
const noModifyType = [
'hiddenpower', 'judgment', 'multiattack', 'naturalgift', 'revelationdance', 'struggle', 'technoblast', 'terrainpulse', 'weatherball',
];
if (!(move.isZ && move.category !== 'Status') && !noModifyType.includes(move.id) &&
if (!(move.isZ && move.category !== 'Status') &&
// TODO: Figure out actual interaction
!(move.name === 'Tera Blast' && pokemon.terastallized)) {
(!noModifyType.includes(move.id) || this.activeMove?.isMax) && !(move.name === 'Tera Blast' && pokemon.terastallized)) {
move.type = 'Normal';
move.typeChangerBoosted = this.effect;
}
@ -3226,7 +3219,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
const noModifyType = [
'judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'terrainpulse', 'weatherball',
];
if (move.type === 'Normal' && !noModifyType.includes(move.id) &&
if (move.type === 'Normal' && (!noModifyType.includes(move.id) || this.activeMove?.isMax) &&
!(move.isZ && move.category !== 'Status') && !(move.name === 'Tera Blast' && pokemon.terastallized)) {
move.type = 'Fairy';
move.typeChangerBoosted = this.effect;
@ -3316,14 +3309,8 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
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.canMegaEvo = pokemon.canMegaEvo === false ? false : this.actions.canMegaEvo(pokemon);
pokemon.formeRegression = 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);
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]');
},
flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1 },
name: "Power Construct",
@ -3335,9 +3322,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
if (!this.effectState.target.hp) return;
const ability = target.getAbility();
if (ability.flags['noreceiver'] || ability.id === 'noability') return;
if (this.effectState.target.setAbility(ability)) {
this.add('-ability', this.effectState.target, ability, '[from] ability: Power of Alchemy', `[of] ${target}`);
}
this.effectState.target.setAbility(ability, target);
},
flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1 },
name: "Power of Alchemy",
@ -3431,12 +3416,12 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
},
protean: {
onPrepareHit(source, target, move) {
if (this.effectState.protean === source.previouslySwitchedIn) return;
if (this.effectState.protean) return;
if (move.hasBounced || move.flags['futuremove'] || move.sourceEffect === 'snatch' || move.callsMove) return;
const type = move.type;
if (type && type !== '???' && source.getTypes().join() !== type) {
if (!source.setType(type)) return;
this.effectState.protean = source.previouslySwitchedIn;
this.effectState.protean = true;
this.add('-start', source, 'typechange', type, '[from] ability: Protean');
}
},
@ -3724,9 +3709,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
if (!this.effectState.target.hp) return;
const ability = target.getAbility();
if (ability.flags['noreceiver'] || ability.id === 'noability') return;
if (this.effectState.target.setAbility(ability)) {
this.add('-ability', this.effectState.target, ability, '[from] ability: Receiver', `[of] ${target}`);
}
this.effectState.target.setAbility(ability, target);
},
flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1 },
name: "Receiver",
@ -3752,7 +3735,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
const noModifyType = [
'judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'terrainpulse', 'weatherball',
];
if (move.type === 'Normal' && !noModifyType.includes(move.id) &&
if (move.type === 'Normal' && (!noModifyType.includes(move.id) || this.activeMove?.isMax) &&
!(move.isZ && move.category !== 'Status') && !(move.name === 'Tera Blast' && pokemon.terastallized)) {
move.type = 'Ice';
move.typeChangerBoosted = this.effect;
@ -4247,34 +4230,30 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
},
slowstart: {
onStart(pokemon) {
pokemon.addVolatile('slowstart');
this.add('-start', pokemon, 'ability: Slow Start');
this.effectState.counter = 5;
},
onEnd(pokemon) {
delete pokemon.volatiles['slowstart'];
this.add('-end', pokemon, 'Slow Start', '[silent]');
},
condition: {
duration: 5,
onResidualOrder: 28,
onResidualSubOrder: 2,
onStart(target) {
this.add('-start', target, 'ability: Slow Start');
},
onResidual(pokemon) {
if (!pokemon.activeTurns) {
this.effectState.duration! += 1;
onResidualOrder: 28,
onResidualSubOrder: 2,
onResidual(pokemon) {
if (pokemon.activeTurns && this.effectState.counter) {
this.effectState.counter--;
if (!this.effectState.counter) {
this.add('-end', pokemon, 'Slow Start');
delete this.effectState.counter;
}
},
onModifyAtkPriority: 5,
onModifyAtk(atk, pokemon) {
}
},
onModifyAtkPriority: 5,
onModifyAtk(atk, pokemon) {
if (this.effectState.counter) {
return this.chainModify(0.5);
},
onModifySpe(spe, pokemon) {
}
},
onModifySpe(spe, pokemon) {
if (this.effectState.counter) {
return this.chainModify(0.5);
},
onEnd(target) {
this.add('-end', target, 'Slow Start');
},
}
},
flags: {},
name: "Slow Start",
@ -4743,6 +4722,24 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
rating: 3,
num: 33,
},
swordofruin: {
onStart(pokemon) {
if (this.suppressingAbility(pokemon)) return;
this.add('-ability', pokemon, 'Sword of Ruin');
},
onAnyModifyDef(def, target, source, move) {
const abilityHolder = this.effectState.target;
if (target.hasAbility('Sword of Ruin')) return;
if (!move.ruinedDef?.hasAbility('Sword of Ruin')) move.ruinedDef = abilityHolder;
if (move.ruinedDef !== abilityHolder) return;
this.debug('Sword of Ruin Def drop');
return this.chainModify(0.75);
},
flags: {},
name: "Sword of Ruin",
rating: 4.5,
num: 285,
},
symbiosis: {
onAllyAfterUseItem(item, pokemon) {
if (pokemon.switchFlag) return;
@ -4778,24 +4775,6 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
rating: 2,
num: 28,
},
swordofruin: {
onStart(pokemon) {
if (this.suppressingAbility(pokemon)) return;
this.add('-ability', pokemon, 'Sword of Ruin');
},
onAnyModifyDef(def, target, source, move) {
const abilityHolder = this.effectState.target;
if (target.hasAbility('Sword of Ruin')) return;
if (!move.ruinedDef?.hasAbility('Sword of Ruin')) move.ruinedDef = abilityHolder;
if (move.ruinedDef !== abilityHolder) return;
this.debug('Sword of Ruin Def drop');
return this.chainModify(0.75);
},
flags: {},
name: "Sword of Ruin",
rating: 4.5,
num: 285,
},
tabletsofruin: {
onStart(pokemon) {
if (this.suppressingAbility(pokemon)) return;
@ -4902,13 +4881,6 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
if (pokemon.species.forme !== 'Terastal') {
this.add('-activate', pokemon, 'ability: Tera Shift');
pokemon.formeChange('Terapagos-Terastal', 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);
const newMaxHP = pokemon.baseMaxhp;
pokemon.hp = newMaxHP - (pokemon.maxhp - pokemon.hp);
pokemon.maxhp = newMaxHP;
this.add('-heal', pokemon, pokemon.getHealth, '[silent]');
}
},
flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1, notransform: 1 },
@ -5083,9 +5055,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
const target = this.sample(possibleTargets);
const ability = target.getAbility();
if (pokemon.setAbility(ability)) {
this.add('-ability', pokemon, ability, '[from] ability: Trace', `[of] ${target}`);
}
pokemon.setAbility(ability, target);
},
flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1 },
name: "Trace",
@ -5508,7 +5478,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
},
wonderguard: {
onTryHit(target, source, move) {
if (target === source || move.category === 'Status' || move.type === '???' || move.id === 'struggle') return;
if (target === source || move.category === 'Status' || move.id === 'struggle') return;
if (move.id === 'skydrop' && !source.volatiles['skydrop']) return;
this.debug('Wonder Guard immunity: ' + move.id);
if (target.runEffectiveness(move) <= 0 || !target.runImmunity(move)) {
@ -5583,13 +5553,14 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
if (pokemon.baseSpecies.baseSpecies !== 'Palafin') return;
if (pokemon.species.forme !== 'Hero') {
pokemon.formeChange('Palafin-Hero', this.effect, true);
pokemon.heroMessageDisplayed = false;
}
},
onSwitchIn(pokemon) {
if (pokemon.baseSpecies.baseSpecies !== 'Palafin') return;
if (!this.effectState.heroMessageDisplayed && pokemon.species.forme === 'Hero') {
if (!pokemon.heroMessageDisplayed && pokemon.species.forme === 'Hero') {
this.add('-activate', pokemon, 'ability: Zero to Hero');
this.effectState.heroMessageDisplayed = true;
pokemon.heroMessageDisplayed = true;
}
},
flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1, notransform: 1 },
@ -5615,7 +5586,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
flags: { breakable: 1 },
name: "Mountaineer",
rating: 3,
num: -2,
num: -1,
},
rebound: {
isNonstandard: "CAP",
@ -5623,32 +5594,32 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
onTryHit(target, source, move) {
if (this.effectState.target.activeTurns) return;
if (target === source || move.hasBounced || !move.flags['reflectable']) {
if (target === source || move.hasBounced || !move.flags['reflectable'] || target.isSemiInvulnerable()) {
return;
}
const newMove = this.dex.getActiveMove(move.id);
newMove.hasBounced = true;
newMove.pranksterBoosted = false;
this.actions.useMove(newMove, target, { target: source });
return null;
},
onAllyTryHitSide(target, source, move) {
if (this.effectState.target.activeTurns) return;
if (target.isAlly(source) || move.hasBounced || !move.flags['reflectable']) {
if (target.isAlly(source) || move.hasBounced || !move.flags['reflectable'] || target.isSemiInvulnerable()) {
return;
}
const newMove = this.dex.getActiveMove(move.id);
newMove.hasBounced = true;
newMove.pranksterBoosted = false;
this.actions.useMove(newMove, this.effectState.target, { target: source });
move.hasBounced = true; // only bounce once in free-for-all battles
return null;
},
condition: {
duration: 1,
},
flags: { breakable: 1 },
name: "Rebound",
rating: 3,
num: -3,
num: -2,
},
persistent: {
isNonstandard: "CAP",
@ -5656,6 +5627,6 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
flags: {},
name: "Persistent",
rating: 3,
num: -4,
num: -3,
},
};

View File

@ -15,9 +15,9 @@ export const Aliases: import('../sim/dex').AliasesTable = {
zeroused: "[Gen 9] ZU",
mono: "[Gen 9] Monotype",
ag: "[Gen 9] Anything Goes",
bss: "[Gen 9] BSS Reg I",
vgc: "[Gen 9] VGC 2025 Reg I",
bsd: "[Gen 9] VGC 2025 Reg I",
bss: "[Gen 9] BSS Reg J",
vgc: "[Gen 9] VGC 2026 Reg F",
bsd: "[Gen 9] VGC 2026 Reg F",
randdubs: "[Gen 9] Random Doubles Battle",
doubles: "[Gen 9] Doubles OU",
dou: "[Gen 9] Doubles OU",
@ -62,8 +62,6 @@ export const Aliases: import('../sim/dex').AliasesTable = {
gen6hackmons: "[Gen 6] Pure Hackmons",
cc1v1: "[Gen 9] Challenge Cup 1v1",
cc2v2: "[Gen 9] Challenge Cup 2v2",
cgt: "[Gen 9] Computer-Generated Teams",
compgen: "[Gen 9] Computer-Generated Teams",
hc: "[Gen 9] Hackmons Cup",
bf: "[Gen 8] Battle Factory",
bssf: "[Gen 9] BSS Factory",
@ -90,8 +88,11 @@ export const Aliases: import('../sim/dex').AliasesTable = {
gen6ag: "[Gen 6] Anything Goes",
crossevo: "[Gen 9] Cross Evolution",
mayhem: "[Gen 9] Random Battle Mayhem",
omotm: "[Gen 9] Tier Shift",
lcotm: "[Gen 9] Pure Hackmons",
zaou: "[Gen 9] Legends Z-A OU",
legendsou: "[Gen 9] Legends Z-A OU",
plzaou: "[Gen 9] Legends Z-A OU",
omotm: "[Gen 9] Pokebilities",
lcotm: "[Gen 9] Tera Override",
// mega evos --- 1st ordered alphabetically by species, 2nd by alias
megasnow: "Abomasnow-Mega",
@ -107,7 +108,10 @@ export const Aliases: import('../sim/dex').AliasesTable = {
megacharizardy: "Charizard-Mega-Y",
yard: "Charizard-Mega-Y",
zardy: "Charizard-Mega-Y",
megacruci: "Crucibelle-Mega",
mdia: "Diancie-Mega",
floetteeternalmega: "Floette-Mega",
megafloetteeternal: "Floette-Mega",
megagard: "Gardevoir-Mega",
megacross: "Heracross-Mega",
megadoom: "Houndoom-Mega",
@ -123,6 +127,7 @@ export const Aliases: import('../sim/dex').AliasesTable = {
megamaw: "Mawile-Mega",
mmedi: "Medicham-Mega",
megamedi: "Medicham-Mega",
meowsticmega: "Meowstic-M-Mega",
mmx: "Mewtwo-Mega-X",
megamewtwox: "Mewtwo-Mega-X",
mewtwox: "Mewtwo-Mega-X",
@ -133,7 +138,10 @@ export const Aliases: import('../sim/dex').AliasesTable = {
megashark: "Sharpedo-Mega",
mbro: "Slowbro-Mega",
megabro: "Slowbro-Mega",
tatsugirimega: "Tatsugiri-Curly-Mega",
megasaur: "Venusaur-Mega",
megazygardecomplete: "Zygarde-Mega",
zygardecompletemega: "Zygarde-Mega",
// Pokéstar Studios --- 1st ordered alphabetically by species, 2nd by alias
blackdoor: "Pokestar Black Door",
@ -328,8 +336,10 @@ export const Aliases: import('../sim/dex').AliasesTable = {
gourgeistxl: "Gourgeist-Super",
gourgeisth: "Gourgeist-Super",
gourgeisthuge: "Gourgeist-Super",
gourgeistjumbo: "Gourgeist-Super",
ashgreninja: "Greninja-Ash",
greninjabattlebond: "Greninja-Bond",
pdon: "Groudon-Primal",
growlh: "Growlithe-Hisui",
hoopau: "Hoopa-Unbound",
nddf: "Indeedee-F",
@ -412,7 +422,12 @@ export const Aliases: import('../sim/dex').AliasesTable = {
horsepalkia: "Palkia-Origin",
alosian: "Persian-Alola",
ponyg: "Ponyta-Galar",
pumpkaboos: "Pumpkaboo-Small",
pumpkabool: "Pumpkaboo-Large",
pumpkabooxl: "Pumpkaboo-Super",
pumpkabooh: "Pumpkaboo-Super",
pumpkaboohuge: "Pumpkaboo-Super",
pumpkaboojumbo: "Pumpkaboo-Super",
hqwil: "Qwilfish-Hisui",
gorse: "Rapidash-Galar",
galardash: "Rapidash-Galar",
@ -512,6 +527,8 @@ export const Aliases: import('../sim/dex').AliasesTable = {
giratinaa: "Giratina",
giratinaaltered: "Giratina",
gourgeistaverage: "Gourgeist",
gourgeistm: "Gourgeist",
gourgeistmedium: "Gourgeist",
hoopac: "Hoopa",
hoopaconfined: "Hoopa",
indeedeem: "Indeedee",
@ -542,6 +559,8 @@ export const Aliases: import('../sim/dex').AliasesTable = {
poltchageistcounterfeit: "Poltchageist",
polteageistphony: "Polteageist",
pumpkabooaverage: "Pumpkaboo",
pumpkaboom: "Pumpkaboo",
pumpkaboomedium: "Pumpkaboo",
rockruffmidday: "Rockruff",
shayminl: "Shaymin",
shayminland: "Shaymin",
@ -707,8 +726,6 @@ export const Aliases: import('../sim/dex').AliasesTable = {
sawsbuckautumn: "Sawsbuck",
sawsbuckwinter: "Sawsbuck",
tatsugiricurly: "Tatsugiri",
tatsugiridroopy: "Tatsugiri",
tatsugiristretchy: "Tatsugiri",
unowna: "Unown",
unownb: "Unown",
unownc: "Unown",
@ -2541,12 +2558,12 @@ export const Aliases: import('../sim/dex').AliasesTable = {
krilo: "Krilowatt",
libra: "Equilibra",
mala: "Malaconda",
megacruci: "Crucibelle-Mega",
navi: "Naviathan",
nect: "Necturna",
ohmagod: "Plasmanta",
plas: "Plasmanta",
raja: "Saharaja",
ramnarokdormant: "Ramnarok",
reve: "Revenankh",
roak: "Pyroak",
smoko: "Smokomodo",

View File

@ -1,78 +0,0 @@
// Data for computer-generated teams
export const MOVE_PAIRINGS: { [moveID: IDEntry]: IDEntry } = {
rest: 'sleeptalk',
sleeptalk: 'rest',
};
// Bonuses to move ratings by ability
export const ABILITY_MOVE_BONUSES: { [abilityID: IDEntry]: { [moveID: IDEntry]: number } } = {
contrary: { terablast: 2 },
drought: { sunnyday: 0.2, solarbeam: 2 },
drizzle: { raindance: 0.2, solarbeam: 0.2, hurricane: 2 },
};
// Bonuses to move ratings by move type
export const ABILITY_MOVE_TYPE_BONUSES: { [abilityID: IDEntry]: { [typeName: string]: number } } = {
darkaura: { Dark: 1.33 },
dragonsmaw: { Dragon: 1.5 },
fairyaura: { Fairy: 1.33 },
steelworker: { Steel: 1.5 },
steelyspirit: { Steel: 1.5 },
transistor: { Electric: 1.3 },
// -ate moves
pixilate: { Normal: 1.5 * 1.2 },
refrigerate: { Normal: 1.5 * 1.2 },
aerilate: { Normal: 1.5 * 1.2 },
normalize: { Normal: 1.2 },
// weather
drizzle: { Water: 1.4, Fire: 0.6 },
drought: { Fire: 1.4, Water: 0.6 },
};
// For moves whose quality isn't obvious from data
// USE SPARINGLY!
export const HARDCODED_MOVE_WEIGHTS: { [moveID: IDEntry]: number } = {
// Fails unless user is asleep
snore: 0,
// Hard to use
lastresort: 0.1, dreameater: 0.1,
// Useless without Berry + sucks even then
belch: 0.2,
// Power increases in conditions out of our control that may occur
avalanche: 1.2,
ficklebeam: 1.3,
hex: 1.2,
stompingtantrum: 1.2,
temperflare: 1.2,
// Attacks that set hazards on hit
// We REALLY like hazards
stoneaxe: 16,
ceaselessedge: 16,
// screens
lightscreen: 3, reflect: 3, auroraveil: 3, // TODO: make sure AVeil always gets Snow?
tailwind: 2,
// mess with the opponent
taunt: 2, disable: 2, encore: 3,
// healing moves
// TODO: should healing moves be more common on bulkier pokemon?
// 25%
junglehealing: 3, lifedew: 3,
// 50%
milkdrink: 5, moonlight: 5, morningsun: 5, recover: 5, roost: 5,
shoreup: 5, slackoff: 5, softboiled: 5, synthesis: 5,
// delayed/consequence
rest: 3, // has sleeptalk potential
wish: 2,
// requires terrain
steelroller: 0.1,
};
export const WEIGHT_BASED_MOVES = ['heatcrash', 'heavyslam', 'lowkick', 'grassknot'];
export const TARGET_HP_BASED_MOVES = ['crushgrip', 'hardpress'];

File diff suppressed because it is too large Load Diff

View File

@ -95,7 +95,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
},
onBeforeMovePriority: 10,
onBeforeMove(pokemon, target, move) {
if (move.flags['defrost']) return;
if (move.flags['defrost'] && !(move.id === 'burnup' && !pokemon.hasType('Fire'))) return;
if (this.randomChance(1, 5)) {
pokemon.cureStatus();
return;
@ -115,7 +115,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
}
},
onDamagingHit(damage, target, source, move) {
if (move.type === 'Fire' && move.category !== 'Status') {
if (move.type === 'Fire' && move.category !== 'Status' && move.id !== 'polarflare') {
target.cureStatus();
}
},
@ -270,6 +270,11 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
this.effectState.duration = 2;
}
},
onAfterMove(pokemon) {
if (this.effectState.duration === 1) {
pokemon.removeVolatile('lockedmove');
}
},
onEnd(target) {
if (this.effectState.trueDuration > 1) return;
target.addVolatile('confusion');

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,206 @@
export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTable = {
magician: {
inherit: true,
onAfterMoveSecondarySelf(source, target, move) {
if (!move || source.switchFlag === true || !move.hitTargets || source.item || source.volatiles['gem'] ||
move.id === 'fling' || move.category === 'Status') return;
const hitTargets = move.hitTargets;
this.speedSort(hitTargets);
for (const pokemon of hitTargets) {
if (pokemon !== source) {
const yourItem = pokemon.takeItem(source);
if (!yourItem) continue;
if (!source.setItem(yourItem)) {
if (!this.dex.items.get(yourItem.id).exists) {
pokemon.setItem(yourItem.id);
continue;
}
pokemon.item = yourItem.id; // bypass setItem so we don't break choicelock or anything
continue;
}
this.add('-item', source, yourItem, '[from] ability: Magician', `[of] ${pokemon}`);
return;
}
}
},
},
neutralizinggas: {
inherit: true,
onSwitchIn(pokemon) {
this.add('-ability', pokemon, 'Neutralizing Gas');
pokemon.abilityState.ending = false;
const strongWeathers = ['desolateland', 'primordialsea', 'deltastream'];
for (const target of this.getAllActive()) {
if (target.hasItem('Ability Shield')) {
this.add('-block', target, 'item: Ability Shield');
continue;
}
// Can't suppress a Tatsugiri inside of Dondozo already
if (target.volatiles['commanding']) {
continue;
}
if (target.illusion) {
this.singleEvent('End', this.dex.abilities.get('Illusion'), target.abilityState, target, pokemon, 'neutralizinggas');
}
if (target.volatiles['slowstart']) {
delete target.volatiles['slowstart'];
this.add('-end', target, 'Slow Start', '[silent]');
}
if (strongWeathers.includes(target.getAbility().id)) {
this.singleEvent('End', this.dex.abilities.get(target.getAbility().id), target.abilityState, target, pokemon, 'neutralizinggas');
}
if (!this.dex.abilities.get(target.ability).exists) {
const isItem = (target.m.scrambled.items as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability');
if (isItem >= 0) {
target.removeVolatile('item:' + this.toID(target.m.scrambled.items[isItem].thing));
} else if ((target.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability') >= 0) {
const isMove = (target.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability');
const indexOfMove = target.moveSlots.findIndex(m => this.toID(target.m.scrambled.moves[isMove].thing) === m.id);
if (indexOfMove >= 0) target.moveSlots.splice(indexOfMove, 1);
}
}
}
},
onEnd(source) {
if (source.transformed) return;
for (const pokemon of this.getAllActive()) {
if (pokemon !== source && pokemon.hasAbility('Neutralizing Gas')) {
return;
}
}
this.add('-end', source, 'ability: Neutralizing Gas');
// FIXME this happens before the pokemon switches out, should be the opposite order.
// Not an easy fix since we cant use a supported event. Would need some kind of special event that
// gathers events to run after the switch and then runs them when the ability is no longer accessible.
// (If you're tackling this, do note extreme weathers have the same issue)
// Mark this pokemon's ability as ending so Pokemon#ignoringAbility skips it
if (source.abilityState.ending) return;
source.abilityState.ending = true;
const sortedActive = this.getAllActive();
this.speedSort(sortedActive);
for (const pokemon of sortedActive) {
if (pokemon !== source) {
if (pokemon.getAbility().flags['cantsuppress']) continue; // does not interact with e.g Ice Face, Zen Mode
if (pokemon.hasItem('abilityshield')) continue; // don't restart abilities that weren't suppressed
// Will be suppressed by Pokemon#ignoringAbility if needed
this.singleEvent('Start', pokemon.getAbility(), pokemon.abilityState, pokemon);
if (pokemon.ability === "gluttony") {
pokemon.abilityState.gluttony = false;
}
}
if (!this.dex.abilities.get(pokemon.ability).exists) {
const isItem = (pokemon.m.scrambled.items as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability');
if (isItem >= 0) {
pokemon.addVolatile('item:' + this.toID(pokemon.m.scrambled.items[isItem].thing));
} else if ((pokemon.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability') >= 0) {
const findMove = (pokemon.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability');
const findSlot = pokemon.baseMoveSlots.find(e => e.id === this.toID(pokemon.m.scrambled.moves[findMove].thing));
pokemon.moveSlots.push(this.dex.deepClone(findSlot));
}
}
}
},
},
pickpocket: {
inherit: true,
onAfterMoveSecondary(target, source, move) {
if (source && source !== target && move?.flags['contact']) {
if (target.item || target.switchFlag || target.forceSwitchFlag || source.switchFlag === true) {
return;
}
const yourItem = source.takeItem(target);
if (!yourItem) {
return;
}
if (!target.setItem(yourItem)) {
if (!this.dex.items.get(yourItem.id).exists) {
target.setItem(yourItem.id);
return;
}
source.item = yourItem.id;
return;
}
this.add('-enditem', source, yourItem, '[silent]', '[from] ability: Pickpocket', `[of] ${source}`);
this.add('-item', target, yourItem, '[from] ability: Pickpocket', `[of] ${source}`);
}
},
},
trace: {
inherit: true,
onStart(pokemon) {
this.effectState.seek = true;
// n.b. only affects Hackmons
// interaction with No Ability is complicated: https://www.smogon.com/forums/threads/pokemon-sun-moon-battle-mechanics-research.3586701/page-76#post-7790209
if (pokemon.adjacentFoes().some(foeActive => foeActive.ability === 'noability')) {
this.effectState.seek = false;
}
// interaction with Ability Shield is similar to No Ability
if (pokemon.hasItem('Ability Shield') && this.toID(pokemon.ability) === 'trace') {
this.add('-block', pokemon, 'item: Ability Shield');
this.effectState.seek = false;
}
if (this.effectState.seek) {
this.singleEvent('Update', this.effect, this.effectState, pokemon);
}
},
onUpdate(pokemon) {
if (!this.effectState.seek) return;
const possibleTargets = pokemon.adjacentFoes().filter(
target => !target.getAbility().flags['notrace'] && target.ability !== 'noability'
);
if (!possibleTargets.length) return;
const target = this.sample(possibleTargets);
const ability = target.getAbility();
if (this.toID(pokemon.item) === 'trace') {
this.add('-ability', pokemon, ability.name, 'Trace');
pokemon.setItem(ability.name);
return;
} else if (pokemon.volatiles['ability:trace']?.inSlot === 'Move') {
if (this.dex.abilities.get(ability.name).exists) {
pokemon.removeVolatile('ability:trace');
pokemon.m.scrambled.abilities.splice(
(pokemon.m.scrambled.abilities as { thing: string, inSlot: string }[]).findIndex(e =>
this.toID(e.thing) === 'trace' && e.inSlot === 'Move'), 1);
this.add('-ability', pokemon, ability.name, 'Trace');
pokemon.addVolatile(`ability:${ability.id}`);
pokemon.m.scrambled.abilities.push({ thing: ability.name, inSlot: 'Move' });
} else if (this.dex.items.get(ability.name).exists) {
pokemon.removeVolatile('ability:trace');
pokemon.m.scrambled.abilities.splice(
(pokemon.m.scrambled.abilities as { thing: string, inSlot: string }[]).findIndex(e =>
this.toID(e.thing) === 'trace' && e.inSlot === 'Move'), 1);
this.add('-ability', pokemon, ability.name, 'Trace');
pokemon.addVolatile(`item:${ability.id}`);
pokemon.m.scrambled.items.push({ thing: this.dex.items.get(ability.name).name, inSlot: 'Move' });
} else {
const move = this.dex.moves.get(ability.name);
if (move.exists) {
pokemon.removeVolatile('ability:trace');
pokemon.m.scrambled.abilities.splice(
(pokemon.m.scrambled.abilities as { thing: string, inSlot: string }[]).findIndex(e =>
this.toID(e.thing) === 'trace' && e.inSlot === 'Move'), 1);
this.add('-ability', pokemon, move.name, 'Trace');
const newMove = {
move: move.name,
id: move.id,
pp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
maxpp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
target: move.target,
disabled: false,
used: false,
};
pokemon.baseMoveSlots.push(newMove);
pokemon.moveSlots.push(newMove);
}
}
return;
}
pokemon.setAbility(ability, target);
},
},
};

View File

@ -0,0 +1,44 @@
export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDataTable = {
choicelock: {
inherit: true,
onBeforeMove(pokemon, target, move) {
const choiceItem = pokemon.getItem().isChoice ||
Object.keys(pokemon.volatiles).some(v => (
v.startsWith('item:') && this.dex.items.get(v.split(':')[1]).isChoice
));
if (!choiceItem) {
pokemon.removeVolatile('choicelock');
return;
}
if (
!pokemon.ignoringItem() && !pokemon.volatiles['dynamax'] &&
move.id !== this.effectState.move && move.id !== 'struggle'
) {
// Fails unless the Choice item is being ignored, and no PP is lost
this.addMove('move', pokemon, move.name);
this.attrLastMove('[still]');
this.debug("Disabled by Choice item lock");
this.add('-fail', pokemon);
return false;
}
},
onDisableMove(pokemon) {
const choiceItem = pokemon.getItem().isChoice ||
Object.keys(pokemon.volatiles).some(v => (
v.startsWith('item:') && this.dex.items.get(v.split(':')[1]).isChoice
));
if (!choiceItem || !pokemon.hasMove(this.effectState.move)) {
pokemon.removeVolatile('choicelock');
return;
}
if (pokemon.ignoringItem() || pokemon.volatiles['dynamax']) {
return;
}
for (const moveSlot of pokemon.moveSlots) {
if (moveSlot.id !== this.effectState.move) {
pokemon.disableMove(moveSlot.id, false, this.effectState.sourceEffect);
}
}
},
},
};

View File

@ -0,0 +1,41 @@
export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
airballoon: {
inherit: true,
// airborneness implemented in sim/pokemon.js:Pokemon#isGrounded
onDamagingHit(damage, target, source, move) {
this.add('-enditem', target, 'Air Balloon');
if (target.item === 'airballoon') {
target.item = '';
this.clearEffectState(target.itemState);
} else {
const isBMM = target.volatiles['item:airballoon']?.inSlot;
if (isBMM) {
target.removeVolatile('item:airballoon');
target.m.scrambled.items.splice((target.m.scrambled.items as { thing: string, inSlot: string }[]).findIndex(e =>
this.toID(e.thing) === 'airballoon' && e.inSlot === isBMM), 1);
if (isBMM === 'Ability') target.setAbility('No Ability');
}
}
this.runEvent('AfterUseItem', target, null, null, this.dex.items.get('airballoon'));
},
onAfterSubDamage(damage, target, source, effect) {
this.debug('effect: ' + effect.id);
if (effect.effectType === 'Move') {
this.add('-enditem', target, 'Air Balloon');
if (target.item === 'airballoon') {
target.item = '';
this.clearEffectState(target.itemState);
} else {
const isBMM = target.volatiles['item:airballoon']?.inSlot;
if (isBMM) {
target.removeVolatile('item:airballoon');
target.m.scrambled.items.splice((target.m.scrambled.items as { thing: string, inSlot: string }[]).findIndex(e =>
this.toID(e.thing) === 'airballoon' && e.inSlot === isBMM), 1);
if (isBMM === 'Ability') target.setAbility('No Ability');
}
}
this.runEvent('AfterUseItem', target, null, null, this.dex.items.get('airballoon'));
}
},
},
};

View File

@ -0,0 +1,401 @@
export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
// Remember, everything deals with SLOTS not with properties as they are!
covet: {
inherit: true,
onAfterHit(target, source, move) {
if (source.item || source.volatiles['gem']) {
return;
}
const yourItem = target.takeItem(source);
if (!yourItem) {
return;
}
if (
!this.singleEvent('TakeItem', yourItem, target.itemState, source, target, move, yourItem) ||
!source.setItem(yourItem)
) {
if (!this.dex.items.get(yourItem.id).exists) {
target.setItem(yourItem.id);
return;
}
target.item = yourItem.id; // bypass setItem so we don't break choicelock or anything
return;
}
this.add('-item', source, yourItem, '[from] move: Covet', `[of] ${target}`);
},
},
embargo: {
inherit: true,
condition: {
duration: 5,
onStart(pokemon) {
this.add('-start', pokemon, 'Embargo');
this.singleEvent('End', pokemon.getItem(), pokemon.itemState, pokemon);
if (!this.dex.items.get(pokemon.item).exists) {
const isAbil = (pokemon.m.scrambled.abilities as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item');
if (isAbil >= 0) {
pokemon.removeVolatile('ability:' + this.toID(pokemon.m.scrambled.abilities[isAbil].thing));
} else if ((pokemon.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item') >= 0) {
const isMove = (pokemon.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item');
const slotNo = pokemon.moveSlots.findIndex(m => this.toID(pokemon.m.scrambled.moves[isMove].thing) === m.id);
if (slotNo >= 0) pokemon.moveSlots.splice(slotNo, 1);
}
}
},
// Item suppression implemented in Pokemon.ignoringItem() within sim/pokemon.js
onResidualOrder: 21,
onEnd(pokemon) {
this.add('-end', pokemon, 'Embargo');
if (!this.dex.items.get(pokemon.item).exists) {
const isAbil = (pokemon.m.scrambled.abilities as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item');
if (isAbil >= 0) {
pokemon.addVolatile('ability:' + this.toID(pokemon.m.scrambled.abilities[isAbil].thing));
} else if ((pokemon.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item') >= 0) {
const findMove = (pokemon.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item');
const findSlot = pokemon.baseMoveSlots.find(e => e.id === this.toID(pokemon.m.scrambled.moves[findMove].thing));
pokemon.moveSlots.push(this.dex.deepClone(findSlot));
}
}
},
},
},
magicroom: {
inherit: true,
condition: {
duration: 5,
durationCallback(source, effect) {
if (source?.hasAbility('persistent')) {
this.add('-activate', source, 'ability: Persistent', '[move] Magic Room');
return 7;
}
return 5;
},
onFieldStart(target, source) {
if (source?.hasAbility('persistent')) {
this.add('-fieldstart', 'move: Magic Room', `[of] ${source}`, '[persistent]');
} else {
this.add('-fieldstart', 'move: Magic Room', `[of] ${source}`);
}
for (const mon of this.getAllActive()) {
this.singleEvent('End', mon.getItem(), mon.itemState, mon);
if (!this.dex.items.get(mon.item).exists) {
const isAbil = (mon.m.scrambled.abilities as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item');
if (isAbil >= 0) {
mon.removeVolatile('ability:' + this.toID(mon.m.scrambled.abilities[isAbil].thing));
} else if ((mon.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item') >= 0) {
const isMove = (mon.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item');
const slotNo = mon.moveSlots.findIndex(m => this.toID(mon.m.scrambled.moves[isMove].thing) === m.id);
if (slotNo >= 0) mon.moveSlots.splice(slotNo, 1);
}
}
}
},
onFieldRestart(target, source) {
this.field.removePseudoWeather('magicroom');
},
// Item suppression implemented in Pokemon.ignoringItem() within sim/pokemon.js
onFieldResidualOrder: 27,
onFieldResidualSubOrder: 6,
onFieldEnd() {
this.add('-fieldend', 'move: Magic Room', '[of] ' + this.effectState.source);
for (const pokemon of this.getAllActive()) {
if (!this.dex.items.get(pokemon.item).exists) {
const isAbil = (pokemon.m.scrambled.abilities as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item');
if (isAbil >= 0) {
pokemon.addVolatile('ability:' + this.toID(pokemon.m.scrambled.abilities[isAbil].thing));
} else if ((pokemon.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item') >= 0) {
const findMove = (pokemon.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item');
const findSlot = pokemon.baseMoveSlots.find(e => e.id === this.toID(pokemon.m.scrambled.moves[findMove].thing));
pokemon.moveSlots.push(this.dex.deepClone(findSlot));
}
}
}
},
},
},
gastroacid: {
inherit: true,
condition: {
// Ability suppression implemented in Pokemon.ignoringAbility() within sim/pokemon.js
onStart(pokemon) {
this.add('-endability', pokemon);
this.singleEvent('End', pokemon.getAbility(), pokemon.abilityState, pokemon, pokemon, 'gastroacid');
if (!this.dex.abilities.get(pokemon.ability).exists) {
const isItem = (pokemon.m.scrambled.items as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability');
if (isItem >= 0) {
pokemon.removeVolatile('item:' + this.toID(pokemon.m.scrambled.items[isItem].thing));
} else if ((pokemon.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability') >= 0) {
const isMove = (pokemon.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability');
const slotNo = pokemon.moveSlots.findIndex(m => this.toID(pokemon.m.scrambled.moves[isMove].thing) === m.id);
if (slotNo >= 0) pokemon.moveSlots.splice(slotNo, 1);
}
}
},
},
},
trick: {
inherit: true,
onHit(target, source, move) {
const yourItem = target.takeItem(source);
const myItem = source.takeItem();
if (target.item || source.item || (!yourItem && !myItem)) {
if (yourItem) {
if (!this.dex.items.get(yourItem.id).exists) {
target.setItem(yourItem.id);
} else {
target.item = yourItem.id;
}
}
if (myItem) {
if (!this.dex.items.get(myItem.id).exists) {
source.setItem(myItem.id);
} else {
source.item = myItem.id;
}
}
return false;
}
if (
(myItem && !this.singleEvent('TakeItem', myItem, source.itemState, target, source, move, myItem)) ||
(yourItem && !this.singleEvent('TakeItem', yourItem, target.itemState, source, target, move, yourItem))
) {
if (yourItem) {
if (!this.dex.items.get(yourItem.id).exists) {
target.setItem(yourItem.id);
} else {
target.item = yourItem.id;
}
}
if (myItem) {
if (!this.dex.items.get(myItem.id).exists) {
source.setItem(myItem.id);
} else {
source.item = myItem.id;
}
}
return false;
}
this.add('-activate', source, 'move: Trick', `[of] ${target}`);
if (myItem) {
target.setItem(myItem);
this.add('-item', target, myItem, '[from] move: Trick');
} else {
this.add('-enditem', target, yourItem, '[silent]', '[from] move: Trick');
}
if (yourItem) {
source.setItem(yourItem);
this.add('-item', source, yourItem, '[from] move: Trick');
} else {
this.add('-enditem', source, myItem, '[silent]', '[from] move: Trick');
}
},
},
sketch: {
inherit: true,
onHit(target, source) {
const move = target.lastMove;
if (source.transformed || !move || source.moves.includes(move.id)) return false;
if (move.flags['nosketch'] || move.isZ || move.isMax) return false;
const sketchIndex = source.moves.indexOf('sketch');
if (sketchIndex < 0) return false;
if (this.toID(source.item) === 'sketch') {
source.setItem(move.name);
this.add('-activate', source, 'move: Sketch', move.name);
return;
} else if (this.toID(source.ability) === 'sketch') {
source.setAbility(move.name);
this.add('-activate', source, 'move: Sketch', move.name);
return;
}
const sketchedMove = {
move: move.name,
id: move.id,
pp: move.pp,
maxpp: move.pp,
target: move.target,
disabled: false,
used: false,
};
source.moveSlots[sketchIndex] = sketchedMove;
source.baseMoveSlots[sketchIndex] = sketchedMove;
this.add('-activate', source, 'move: Sketch', move.name);
},
},
skillswap: {
inherit: true,
onHit(target, source, move) {
const targetAbility = target.getAbility();
const sourceAbility = source.getAbility();
const sourceIsBMM = !this.dex.abilities.get(sourceAbility).exists;
const targetIsBMM = !this.dex.abilities.get(targetAbility).exists;
if (target.isAlly(source)) {
this.add('-activate', source, 'move: Skill Swap', '', '', `[of] ${target}`);
} else {
this.add('-activate', source, 'move: Skill Swap', targetAbility, sourceAbility, `[of] ${target}`);
}
this.singleEvent('End', sourceAbility, source.abilityState, source);
if (sourceIsBMM) {
const isItem = (source.m.scrambled.items as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability');
if (isItem >= 0) {
source.removeVolatile('item:' + this.toID(source.m.scrambled.items[isItem].thing));
source.m.scrambled.items.splice(isItem, 1);
} else if ((source.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability') >= 0) {
const isMove = (source.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability');
source.baseMoveSlots.splice(
source.baseMoveSlots.findIndex(m => this.toID(source.m.scrambled.moves[isMove].thing) === m.id), 1);
source.moveSlots.splice(source.moveSlots.findIndex(m => this.toID(source.m.scrambled.moves[isMove].thing) === m.id), 1);
source.m.scrambled.moves.splice(isMove, 1);
}
}
this.singleEvent('End', targetAbility, target.abilityState, target);
if (targetIsBMM) {
const isItem = (target.m.scrambled.items as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability');
if (isItem >= 0) {
target.removeVolatile('item:' + this.toID(target.m.scrambled.items[isItem].thing));
target.m.scrambled.items.splice(isItem, 1);
} else if ((target.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability') >= 0) {
const isMove = (target.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability');
target.baseMoveSlots.splice(
target.baseMoveSlots.findIndex(m => this.toID(target.m.scrambled.moves[isMove].thing) === m.id), 1);
target.moveSlots.splice(target.moveSlots.findIndex(m => this.toID(target.m.scrambled.moves[isMove].thing) === m.id), 1);
target.m.scrambled.moves.splice(isMove, 1);
}
}
source.ability = source.baseAbility = targetAbility.id;
target.ability = target.baseAbility = sourceAbility.id;
source.abilityState = this.initEffectState({ id: this.toID(source.ability), target: source });
target.abilityState = this.initEffectState({ id: this.toID(target.ability), target });
source.volatileStaleness = undefined;
if (!target.isAlly(source)) target.volatileStaleness = 'external';
this.singleEvent('Start', targetAbility, source.abilityState, source);
if (targetIsBMM) {
if (this.dex.items.get(targetAbility.id).exists) {
source.m.scrambled.items.push({ thing: targetAbility.id, inSlot: 'Ability' });
const effect = 'item:' + this.toID(targetAbility.id);
source.addVolatile(effect);
source.volatiles[effect].inSlot = 'Ability';
} else {
source.m.scrambled.moves.push({ thing: targetAbility.id, inSlot: 'Ability' });
const bmmMove = Dex.moves.get(targetAbility.id);
const newMove = {
move: bmmMove.name,
id: bmmMove.id,
pp: bmmMove.noPPBoosts ? bmmMove.pp : bmmMove.pp * 8 / 5,
maxpp: bmmMove.noPPBoosts ? bmmMove.pp : bmmMove.pp * 8 / 5,
target: bmmMove.target,
disabled: false,
used: false,
};
source.baseMoveSlots.push(newMove);
source.moveSlots.push(newMove);
}
}
this.singleEvent('Start', sourceAbility, target.abilityState, target);
if (sourceIsBMM) {
if (this.dex.items.get(sourceAbility.id).exists) {
target.m.scrambled.items.push({ thing: sourceAbility.id, inSlot: 'Ability' });
const effect = 'item:' + this.toID(sourceAbility.id);
target.addVolatile(effect);
target.volatiles[effect].inSlot = 'Ability';
} else {
target.m.scrambled.moves.push({ thing: sourceAbility.id, inSlot: 'Ability' });
const bmmMove = Dex.moves.get(sourceAbility.id);
const newMove = {
move: bmmMove.name,
id: bmmMove.id,
pp: bmmMove.noPPBoosts ? bmmMove.pp : bmmMove.pp * 8 / 5,
maxpp: bmmMove.noPPBoosts ? bmmMove.pp : bmmMove.pp * 8 / 5,
target: bmmMove.target,
disabled: false,
used: false,
};
target.baseMoveSlots.push(newMove);
target.moveSlots.push(newMove);
}
}
},
},
switcheroo: {
inherit: true,
onHit(target, source, move) {
const yourItem = target.takeItem(source);
const myItem = source.takeItem();
if (target.item || source.item || (!yourItem && !myItem)) {
if (yourItem) {
if (!this.dex.items.get(yourItem.id).exists) {
target.setItem(yourItem.id);
} else {
target.item = yourItem.id;
}
}
if (myItem) {
if (!this.dex.items.get(myItem.id).exists) {
source.setItem(myItem.id);
} else {
source.item = myItem.id;
}
}
return false;
}
if (
(myItem && !this.singleEvent('TakeItem', myItem, source.itemState, target, source, move, myItem)) ||
(yourItem && !this.singleEvent('TakeItem', yourItem, target.itemState, source, target, move, yourItem))
) {
if (yourItem) {
if (!this.dex.items.get(yourItem.id).exists) {
target.setItem(yourItem.id);
} else {
target.item = yourItem.id;
}
}
if (myItem) {
if (!this.dex.items.get(myItem.id).exists) {
source.setItem(myItem.id);
} else {
source.item = myItem.id;
}
}
return false;
}
this.add('-activate', source, 'move: Trick', `[of] ${target}`);
if (myItem) {
target.setItem(myItem);
this.add('-item', target, myItem, '[from] move: Switcheroo');
} else {
this.add('-enditem', target, yourItem, '[silent]', '[from] move: Switcheroo');
}
if (yourItem) {
source.setItem(yourItem);
this.add('-item', source, yourItem, '[from] move: Switcheroo');
} else {
this.add('-enditem', source, myItem, '[silent]', '[from] move: Switcheroo');
}
},
},
thief: {
inherit: true,
onAfterHit(target, source, move) {
if (source.item || source.volatiles['gem']) {
return;
}
const yourItem = target.takeItem(source);
if (!yourItem) {
return;
}
if (!this.singleEvent('TakeItem', yourItem, target.itemState, source, target, move, yourItem) ||
!source.setItem(yourItem)) {
if (!this.dex.items.get(yourItem.id).exists) {
target.setItem(yourItem.id);
return;
}
target.item = yourItem.id; // bypass setItem so we don't break choicelock or anything
return;
}
this.add('-enditem', target, yourItem, '[silent]', '[from] move: Thief', `[of] ${source}`);
this.add('-item', source, yourItem, '[from] move: Thief', `[of] ${target}`);
},
},
};

View File

@ -0,0 +1,546 @@
import { RESTORATIVE_BERRIES } from "../../../sim/pokemon";
export const Scripts: ModdedBattleScriptsData = {
pokemon: {
isGrounded(negateImmunity) {
if ('gravity' in this.battle.field.pseudoWeather) return true;
if ('ingrain' in this.volatiles && this.battle.gen >= 4) return true;
if ('smackdown' in this.volatiles) return true;
const item = (this.ignoringItem() ? '' : this.item);
if (item === 'ironball' || (this.volatiles['item:ironball'] && !this.ignoringItem())) return true;
// If a Fire/Flying type uses Burn Up and Roost, it becomes ???/Flying-type, but it's still grounded.
if (!negateImmunity && this.hasType('Flying') && !(this.hasType('???') && 'roost' in this.volatiles)) return false;
if (this.hasAbility('levitate') && !this.battle.suppressingAbility(this)) return null;
if ('magnetrise' in this.volatiles) return false;
if ('telekinesis' in this.volatiles) return false;
if (item === 'airballoon' || (this.volatiles['item:airballoon'] && !this.ignoringItem())) return false;
return true;
},
getAbility() {
const ability = this.battle.dex.abilities.getByID(this.ability);
if (ability.exists) return ability;
let abil = this.battle.dex.items.getByID(this.ability) as Item | Move;
if (!abil.exists) abil = this.battle.dex.moves.getByID(this.ability);
return {
id: this.ability,
name: abil.name || this.ability,
flags: {},
effectType: "Ability",
toString() {
return abil.name || this.id;
},
} as Ability;
},
hasAbility(ability) {
if (this.ignoringAbility()) return false;
if (Array.isArray(ability)) return ability.some(abil => this.hasAbility(abil));
const abilityid = this.battle.toID(ability);
return this.ability === abilityid || !!this.volatiles['ability:' + abilityid];
},
ignoringAbility() {
// Check if any active pokemon have the ability Neutralizing Gas
let neutralizinggas = false;
for (const pokemon of this.battle.getAllActive()) {
// can't use hasAbility because it would lead to infinite recursion
if (
(pokemon.ability === ('neutralizinggas' as ID) ||
(pokemon.m.scrambled.abilities as { thing: string }[]).some(
abils => this.battle.toID(abils.thing) === 'neutralizinggas')) &&
!pokemon.volatiles['gastroacid'] && !pokemon.abilityState.ending
) {
neutralizinggas = true;
break;
}
}
return !!(
(this.battle.gen >= 5 && !this.isActive) ||
((this.volatiles['gastroacid'] ||
(neutralizinggas && (this.ability !== ('neutralizinggas' as ID) ||
(this.m.scrambled.abilities as { thing: string }[]).some(abils => this.battle.toID(abils.thing) === 'neutralizinggas'))
)) && !this.getAbility().flags['cantsuppress']
)
);
},
setAbility(ability, source, sourceEffect, isFromFormeChange = false, isTransform = false) {
let isBMMAbil = false;
let isOldBMMAbil = false;
if (!this.hp) return false;
if (!this.battle.dex.abilities.get(ability).exists) isBMMAbil = true;
if (typeof ability === 'string') {
if (this.battle.dex.abilities.get(ability).exists) {
ability = this.battle.dex.abilities.get(ability);
} else {
const abilString = ability;
let abil = this.battle.dex.items.get(abilString) as Item | Move;
if (!abil.exists) abil = this.battle.dex.moves.get(abilString);
ability = {
id: abil.id || abilString,
name: abil.name || abilString,
flags: {},
effectType: "Ability",
toString() {
return abil.name || abilString;
},
} as Ability;
}
}
if (!sourceEffect && this.battle.effect) sourceEffect = this.battle.effect;
let oldAbility;
if (this.battle.dex.abilities.get(this.ability).exists) {
oldAbility = this.battle.dex.abilities.get(this.ability);
} else {
let abil = this.battle.dex.items.getByID(this.ability) as Item | Move;
if (!abil.exists) abil = this.battle.dex.moves.getByID(this.ability);
oldAbility = {
id: this.ability,
name: abil.name || this.ability,
flags: {},
effectType: "Ability",
toString() {
return abil.name || this.id;
},
} as Ability;
isOldBMMAbil = true;
}
if (!isFromFormeChange) {
if (ability.flags['cantsuppress'] || this.getAbility().flags['cantsuppress']) return false;
}
if (!isFromFormeChange && !isTransform) {
const setAbilityEvent: boolean | null = this.battle.runEvent('SetAbility', this, source, sourceEffect, ability);
if (!setAbilityEvent) return setAbilityEvent;
}
this.battle.singleEvent('End', oldAbility, this.abilityState, this, source);
if (isOldBMMAbil) {
const isItem = (this.m.scrambled.items as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability');
if (isItem >= 0) {
this.removeVolatile('item:' + this.battle.toID(this.m.scrambled.items[isItem].thing));
this.m.scrambled.items.splice(isItem, 1);
} else if ((this.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability') >= 0) {
const isMove = (this.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Ability');
if (!isTransform) {
let indexOfMove = this.baseMoveSlots.findIndex(m => this.battle.toID(this.m.scrambled.moves[isMove].thing) === m.id);
if (indexOfMove >= 0) this.baseMoveSlots.splice(indexOfMove, 1);
if (oldAbility.id !== 'mimic') {
indexOfMove = this.moveSlots.findIndex(m => this.battle.toID(this.m.scrambled.moves[isMove].thing) === m.id);
}
if (indexOfMove >= 0) this.moveSlots.splice(indexOfMove, 1);
}
this.m.scrambled.moves.splice(isMove, 1);
}
}
this.ability = ability.id;
// ability changes are permanent in BioMechMons
if (!isTransform && !this.transformed) this.baseAbility = ability.id;
this.abilityState = this.battle.initEffectState({ id: ability.id, target: this });
if (sourceEffect && !isFromFormeChange && !isTransform) {
if (source) {
this.battle.add('-ability', this, ability.name, oldAbility.name, `[from] ${sourceEffect.fullname}`, `[of] ${source}`);
} else {
this.battle.add('-ability', this, ability.name, oldAbility.name, `[from] ${sourceEffect.fullname}`);
}
}
if (ability.id && this.battle.gen > 3 &&
(!isTransform || oldAbility.id !== ability.id || this.battle.gen <= 4)) {
this.battle.singleEvent('Start', ability, this.abilityState, this, source);
}
if (isBMMAbil) {
if (this.battle.dex.items.get(ability.id).exists) {
this.m.scrambled.items.push({ thing: ability.id, inSlot: 'Ability' });
const effect = 'item:' + this.battle.toID(ability.id);
this.addVolatile(effect);
this.volatiles[effect].inSlot = 'Ability';
} else {
this.m.scrambled.moves.push({ thing: ability.id, inSlot: 'Ability' });
const move = Dex.moves.get(ability.id);
const newMove = {
move: move.name,
id: move.id,
pp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
maxpp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
target: move.target,
disabled: false,
used: false,
};
if (!isTransform) {
this.baseMoveSlots.push(newMove);
this.moveSlots.push(newMove);
}
}
}
return oldAbility.id;
},
getItem() {
const item = this.battle.dex.items.getByID(this.item);
if (item.exists) return item;
let bmmItem = this.battle.dex.abilities.getByID(this.item) as Ability | Move;
if (!bmmItem.exists) bmmItem = this.battle.dex.moves.getByID(this.item);
return {
id: this.item,
name: bmmItem.name || this.name,
effectType: "Item",
toString() {
return bmmItem.name || this.id;
},
} as Item;
},
hasItem(item) {
if (this.ignoringItem()) return false;
if (Array.isArray(item)) return item.some(i => this.hasItem(i));
const itemId = this.battle.toID(item);
return this.item === itemId || !!this.volatiles['item:' + itemId];
},
takeItem(source) {
if (!this.item) return false;
if (!source) source = this;
if (this.battle.gen <= 4) {
if (source.itemKnockedOff) return false;
if (this.battle.toID(this.ability) === 'multitype') return false;
if (this.battle.toID(source.ability) === 'multitype') return false;
}
const item = this.getItem();
if (this.battle.runEvent('TakeItem', this, source, null, item)) {
this.item = '';
let wrongSlot = (this.m.scrambled.abilities as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item');
if (wrongSlot >= 0) {
this.removeVolatile('ability:' + this.battle.toID(this.m.scrambled.abilities[wrongSlot].thing));
this.m.scrambled.abilities.splice(wrongSlot, 1);
} else if ((this.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item') >= 0) {
wrongSlot = (this.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item');
let indexOfMove = this.baseMoveSlots.findIndex(m => this.battle.toID(this.m.scrambled.moves[wrongSlot].thing) === m.id);
if (indexOfMove >= 0) this.baseMoveSlots.splice(indexOfMove, 1);
if (item.id !== 'mimic') {
indexOfMove = this.moveSlots.findIndex(m => this.battle.toID(this.m.scrambled.moves[wrongSlot].thing) === m.id);
}
if (indexOfMove >= 0) this.moveSlots.splice(indexOfMove, 1);
this.m.scrambled.moves.splice(wrongSlot, 1);
}
const oldItemState = this.itemState;
this.battle.clearEffectState(this.itemState);
this.pendingStaleness = undefined;
this.battle.singleEvent('End', item, oldItemState, this);
this.battle.runEvent('AfterTakeItem', this, null, null, item);
return item;
}
return false;
},
setItem(item, source, effect) {
let isBMMItem = false;
let isOldBMMItem = false;
if (!this.hp || !this.isActive) return false;
if (!this.battle.dex.items.get(item).exists) isBMMItem = true;
if (typeof item === 'string') {
if (this.battle.dex.items.get(item).exists) {
item = this.battle.dex.items.get(item);
} else {
const itemString = item;
let newData = this.battle.dex.abilities.get(itemString) as Ability | Move;
if (!newData.exists) newData = this.battle.dex.moves.get(itemString);
item = {
id: newData.id || itemString,
name: newData.name || itemString,
effectType: "Item",
toString() {
return newData.name || itemString;
},
} as Item;
}
}
const effectid = this.battle.effect ? this.battle.effect.id : '';
if (RESTORATIVE_BERRIES.has('leppaberry' as ID)) {
const inflicted = ['trick', 'switcheroo'].includes(effectid);
const external = inflicted && source && !source.isAlly(this);
this.pendingStaleness = external ? 'external' : 'internal';
} else {
this.pendingStaleness = undefined;
}
const oldItem = this.getItem();
if (!this.battle.dex.items.get(oldItem).exists) isOldBMMItem = true;
const oldItemState = this.itemState;
this.item = item.id;
this.itemState = this.battle.initEffectState({ id: item.id, target: this });
if (oldItem.exists) this.battle.singleEvent('End', oldItem, oldItemState, this);
if (isOldBMMItem) {
const isAbil = (this.m.scrambled.abilities as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item');
if (isAbil >= 0) {
this.removeVolatile('ability:' + this.battle.toID(this.m.scrambled.items[isAbil].thing));
this.m.scrambled.abilities.splice(isAbil, 1);
} else if ((this.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item') >= 0) {
const isMove = (this.m.scrambled.moves as { inSlot: string }[]).findIndex(e => e.inSlot === 'Item');
let indexOfMove = this.baseMoveSlots.findIndex(m => this.battle.toID(this.m.scrambled.moves[isMove].thing) === m.id);
if (indexOfMove >= 0) this.baseMoveSlots.splice(indexOfMove, 1);
if (oldItem.id !== 'mimic') {
indexOfMove = this.moveSlots.findIndex(m => this.battle.toID(this.m.scrambled.moves[isMove].thing) === m.id);
}
if (indexOfMove >= 0) this.moveSlots.splice(indexOfMove, 1);
this.m.scrambled.moves.splice(isMove, 1);
}
}
if (item.id) {
this.battle.singleEvent('Start', item, this.itemState, this, source, effect);
}
if (isBMMItem) {
if (this.battle.dex.abilities.get(item.id).exists) {
this.m.scrambled.abilities.push({ thing: item.id, inSlot: 'Item' });
const abileffect = 'ability:' + this.battle.toID(item.id);
this.addVolatile(abileffect);
this.volatiles[abileffect].inSlot = 'Item';
} else {
this.m.scrambled.moves.push({ thing: item.id, inSlot: 'Item' });
const move = Dex.moves.get(item.id);
const newMove = {
move: move.name,
id: move.id,
pp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
maxpp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
target: move.target,
disabled: false,
used: false,
};
this.baseMoveSlots.push(newMove);
this.moveSlots.push(newMove);
}
}
return true;
},
eatItem(force, source, sourceEffect) {
const item = sourceEffect?.effectType === 'Item' ? sourceEffect :
this.battle.effect.effectType === 'Item' ? this.battle.effect : this.getItem();
if (!item) return false;
if ((!this.hp && this.battle.toID(item.name) !== 'jabocaberry' && this.battle.toID(item.name) !== 'rowapberry') ||
!this.isActive) return false;
if (!sourceEffect && this.battle.effect) sourceEffect = this.battle.effect;
if (!source && this.battle.event?.target) source = this.battle.event.target;
// if (sourceEffect?.effectType === 'Item' && this.item !== sourceEffect.id && source === this) {
// // if an item is telling us to eat it but we aren't holding it, we probably shouldn't eat what we are holding
// return false;
// }
if (
this.battle.runEvent('UseItem', this, null, null, Dex.items.get(item.name)) &&
(force || this.battle.runEvent('TryEatItem', this, null, null, Dex.items.get(item.name)))
) {
this.battle.add('-enditem', this, Dex.items.get(item.name), '[eat]');
this.battle.singleEvent('Eat', Dex.items.get(item.name), this.itemState, this, source, sourceEffect);
this.battle.runEvent('EatItem', this, source, sourceEffect, Dex.items.get(item.name));
if (RESTORATIVE_BERRIES.has(item.id)) {
switch (this.pendingStaleness) {
case 'internal':
if (this.staleness !== 'external') this.staleness = 'internal';
break;
case 'external':
this.staleness = 'external';
break;
}
this.pendingStaleness = undefined;
}
const isBMM = this.volatiles[item.id]?.inSlot;
if (isBMM) {
this.removeVolatile(item.id);
this.m.scrambled.items.splice((this.m.scrambled.items as { thing: string, inSlot: string }[]).findIndex(e =>
e.thing === this.battle.toID(item.name) && e.inSlot === isBMM), 1);
if (isBMM === 'Ability') this.setAbility('No Ability');
} else {
this.lastItem = this.item;
this.item = '';
}
this.battle.clearEffectState(this.itemState);
this.usedItemThisTurn = true;
this.ateBerry = true;
this.battle.runEvent('AfterUseItem', this, null, null, Dex.items.get(item.name));
return true;
}
return false;
},
useItem(source, sourceEffect) {
const item = sourceEffect?.effectType === 'Item' ? sourceEffect :
this.battle.effect.effectType === 'Item' ? this.battle.effect : this.getItem();
if ((!this.hp && !item.isGem) || !this.isActive) return false;
if (!item) return false;
if (!sourceEffect && this.battle.effect) sourceEffect = this.battle.effect;
if (!source && this.battle.event?.target) source = this.battle.event.target;
// const item = this.getItem();
// if (sourceEffect?.effectType === 'Item' && this.item !== sourceEffect.id && source === this) {
// // if an item is telling us to eat it but we aren't holding it, we probably shouldn't eat what we are holding
// return false;
// }
if (this.battle.runEvent('UseItem', this, null, null, Dex.items.get(item.name))) {
switch (item.id) {
case 'redcard':
this.battle.add('-enditem', this, Dex.items.get(item.name), `[of] ${source}`);
break;
default:
if (item.isGem) {
this.battle.add('-enditem', this, Dex.items.get(item.name), '[from] gem');
} else {
this.battle.add('-enditem', this, Dex.items.get(item.name));
}
break;
}
if (item.boosts) {
this.battle.boost(item.boosts, this, source, Dex.items.get(item.name));
}
this.battle.singleEvent('Use', Dex.items.get(item.name), this.itemState, this, source, sourceEffect);
const isBMM = this.volatiles[item.id]?.inSlot;
if (isBMM) {
this.removeVolatile(item.id);
this.m.scrambled.items.splice((this.m.scrambled.items as { thing: string, inSlot: string }[]).findIndex(e =>
e.thing === this.battle.toID(item.name) && e.inSlot === isBMM), 1);
if (isBMM === 'Ability') this.setAbility('No Ability');
} else {
this.lastItem = this.item;
this.item = '';
}
this.battle.clearEffectState(this.itemState);
this.usedItemThisTurn = true;
this.battle.runEvent('AfterUseItem', this, null, null, item);
return true;
}
return false;
},
transformInto(pokemon, effect) {
const species = pokemon.species;
if (
pokemon.fainted || this.illusion || pokemon.illusion || (pokemon.volatiles['substitute'] && this.battle.gen >= 5) ||
(pokemon.transformed && this.battle.gen >= 2) || (this.transformed && this.battle.gen >= 5) ||
species.name === 'Eternatus-Eternamax' ||
(['Ogerpon', 'Terapagos'].includes(species.baseSpecies) && (this.terastallized || pokemon.terastallized)) ||
this.terastallized === 'Stellar'
) {
return false;
}
if (this.battle.dex.currentMod === 'gen1stadium' && (
species.name === 'Ditto' ||
(this.species.name === 'Ditto' && pokemon.moves.includes('transform'))
)) {
return false;
}
if (!this.setSpecies(species, effect, true)) return false;
this.transformed = true;
this.weighthg = pokemon.weighthg;
const types = pokemon.getTypes(true, true);
this.setType(pokemon.volatiles['roost'] ? pokemon.volatiles['roost'].typeWas : types, true);
this.addedType = pokemon.addedType;
this.knownType = this.isAlly(pokemon) && pokemon.knownType;
this.apparentType = pokemon.apparentType;
let statName: StatIDExceptHP;
for (statName in this.storedStats) {
this.storedStats[statName] = pokemon.storedStats[statName];
if (this.modifiedStats) this.modifiedStats[statName] = pokemon.modifiedStats![statName]; // Gen 1: Copy modified stats.
}
this.moveSlots = [];
this.hpType = (this.battle.gen >= 5 ? this.hpType : pokemon.hpType);
this.hpPower = (this.battle.gen >= 5 ? this.hpPower : pokemon.hpPower);
this.timesAttacked = pokemon.timesAttacked;
for (const moveSlot of pokemon.moveSlots) {
let moveName = moveSlot.move;
if (moveSlot.id === 'hiddenpower') {
moveName = 'Hidden Power ' + this.hpType;
}
this.moveSlots.push({
move: moveName,
id: moveSlot.id,
pp: moveSlot.maxpp === 1 ? 1 : 5,
maxpp: this.battle.gen >= 5 ? (moveSlot.maxpp === 1 ? 1 : 5) : moveSlot.maxpp,
target: moveSlot.target,
disabled: false,
used: false,
virtual: true,
});
}
let boostName: BoostID;
for (boostName in pokemon.boosts) {
this.boosts[boostName] = pokemon.boosts[boostName];
}
if (this.battle.gen >= 6) {
// we need to remove all of the overlapping crit volatiles before adding any of them
const volatilesToCopy = ['dragoncheer', 'focusenergy', 'gmaxchistrike', 'laserfocus'];
for (const volatile of volatilesToCopy) this.removeVolatile(volatile);
for (const volatile of volatilesToCopy) {
if (pokemon.volatiles[volatile]) {
this.addVolatile(volatile);
if (volatile === 'gmaxchistrike') this.volatiles[volatile].layers = pokemon.volatiles[volatile].layers;
if (volatile === 'dragoncheer') this.volatiles[volatile].hasDragonType = pokemon.volatiles[volatile].hasDragonType;
}
}
}
if (effect) {
this.battle.add('-transform', this, pokemon, '[from] ' + effect.fullname);
} else {
this.battle.add('-transform', this, pokemon);
}
if (this.terastallized) {
this.knownType = true;
this.apparentType = this.terastallized;
}
if (this.battle.gen > 2) this.setAbility(pokemon.ability, this, null, true, true);
// Change formes based on held items (for Transform)
// Only ever relevant in Generation 4 since Generation 3 didn't have item-based forme changes
if (this.battle.gen === 4) {
if (this.species.num === 487) {
// Giratina formes
if (this.species.name === 'Giratina' && this.item === 'griseousorb') {
this.formeChange('Giratina-Origin');
} else if (this.species.name === 'Giratina-Origin' && this.item !== 'griseousorb') {
this.formeChange('Giratina');
}
}
if (this.species.num === 493) {
// Arceus formes
const item = this.getItem();
const targetForme = (item?.onPlate ? 'Arceus-' + item.onPlate : 'Arceus');
if (this.species.name !== targetForme) {
this.formeChange(targetForme);
}
}
}
// Pokemon transformed into Ogerpon cannot Terastallize
// restoring their ability to tera after they untransform is handled ELSEWHERE
if (['Ogerpon', 'Terapagos'].includes(this.species.baseSpecies) && this.canTerastallize) this.canTerastallize = false;
for (const volatile in this.volatiles) {
if (this.volatiles[volatile].inSlot && this.volatiles[volatile].inSlot === 'Move') {
this.removeVolatile(volatile);
}
}
for (const volatile in pokemon.volatiles) {
if (pokemon.volatiles[volatile].inSlot && pokemon.volatiles[volatile].inSlot === 'Move') {
this.addVolatile(volatile);
this.volatiles[volatile].inSlot = 'Move';
}
}
return true;
},
},
field: {
suppressingWeather() {
for (const pokemon of this.battle.getAllActive()) {
const innates = Object.keys(pokemon.volatiles).filter(x => x.startsWith('ability:'));
if (pokemon && !pokemon.ignoringAbility() &&
(pokemon.getAbility().suppressWeather || innates.some(x => (
this.battle.dex.abilities.get(x.replace('ability:', '')).suppressWeather
)))) {
return true;
}
}
return false;
},
},
};

View File

@ -1,970 +0,0 @@
export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTable = {
absorber: {
name: "Absorber",
rating: 3,
shortDesc: "This Pokemon heals 3/16 max HP after being targeted by a NVE/immune move.",
onDamagingHit(damage, target, source, move) {
if (target.getMoveHitData(move).typeMod < 0)
this.heal(target.baseMaxhp * 0.18);
},
onImmunity(type, pokemon) {
if (this.dex.types.isName(type)) {
this.heal(pokemon.baseMaxhp * 0.18);
}
},
flags: {},
},
antimatter: {
onEffectiveness(typeMod) {
return typeMod * -1;
},
flags: {},
name: "Antimatter",
shortDesc: "This Pokemon's defending effectiveness is reversed.",
},
asymmetry: {
onStart(pokemon) {
let activated = false;
for (const target of pokemon.adjacentFoes()) {
if (!activated) {
this.add('-ability', pokemon, 'Asymmetry');
activated = true;
}
target.addVolatile('asymmetry');
const createArray = (x: number) => Array.from({ length: x }, (_, i) => i);
const pokemonArray = createArray(pokemon.moves.length);
const targetArray = createArray(target.moves.length);
const pickNum1 = this.sample(pokemonArray);
pokemonArray.splice(pokemonArray.indexOf(pickNum1), 1);
let pickNum2;
if (pokemonArray.length === 0) pickNum2 = -1;
else pickNum2 = this.sample(pokemonArray);
const pickNum3 = this.sample(targetArray);
targetArray.splice(targetArray.indexOf(pickNum3), 1);
let pickNum4;
if (targetArray.length === 0) pickNum4 = -1;
else pickNum4 = this.sample(targetArray);
const pokemonMove1 = this.dex.moves.get(pokemon.moves[pickNum1]);
const realPokemonMove1 = {
move: pokemonMove1.name,
id: pokemonMove1.id,
pp: pokemonMove1.pp * 1.6,
maxpp: pokemonMove1.pp * 1.6,
target: pokemonMove1.target,
disabled: false,
used: false,
virtual: true,
};
const targetMove1 = this.dex.moves.get(target.moves[pickNum3]);
const realTargetMove1 = {
move: targetMove1.name,
id: targetMove1.id,
pp: targetMove1.pp * 1.6,
maxpp: targetMove1.pp * 1.6,
target: targetMove1.target,
disabled: false,
used: false,
virtual: true,
};
pokemon.moveSlots[pickNum1] = realTargetMove1;
pokemon.baseMoveSlots[pickNum1] = realTargetMove1;
target.moveSlots[pickNum3] = realPokemonMove1;
target.baseMoveSlots[pickNum3] = realPokemonMove1;
if (pickNum2 === -1 || pickNum4 === -1) return;
const pokemonMove2 = this.dex.moves.get(pokemon.moves[pickNum2]);
const realPokemonMove2 = {
move: pokemonMove2.name,
id: pokemonMove2.id,
pp: pokemonMove2.pp * 1.6,
maxpp: pokemonMove2.pp * 1.6,
target: pokemonMove2.target,
disabled: false,
used: false,
virtual: true,
};
const targetMove2 = this.dex.moves.get(target.moves[pickNum4]);
const realTargetMove2 = {
move: targetMove2.name,
id: targetMove2.id,
pp: targetMove2.pp * 1.6,
maxpp: targetMove2.pp * 1.6,
target: targetMove2.target,
disabled: false,
used: false,
virtual: true,
};
pokemon.moveSlots[pickNum2] = realTargetMove2;
pokemon.baseMoveSlots[pickNum2] = realTargetMove2;
target.moveSlots[pickNum4] = realPokemonMove2;
target.baseMoveSlots[pickNum4] = realPokemonMove2;
}
},
condition: {
duration: 1,
noCopy: true,
onBeforeMovePriority: 6,
onBeforeMove(pokemon, target, move) {
if (pokemon.moveSlots.filter(m => m.id === move.id).length === 0) {
const newMove = this.dex.moves.get(move);
this.actions.useMove(newMove, pokemon);
return null;
}
},
},
flags: {},
name: "Asymmetry",
shortDesc: "On switch-in, this Pokemon randomly swaps two of its moves with the opponent's.",
},
backatya: {
onDamagingHit(damage, target, source, move) {
this.damage(target.getUndynamaxedHP(damage * 2), source, target);
},
flags: {},
name: "Back at Ya!",
shortDesc: "This Pokemon deals double damage to the opponent when damaged by a move.",
},
badpacing: {
onResidualOrder: 28,
onResidualSubOrder: 2,
onResidual(pokemon) {
pokemon.addVolatile('badpacing');
},
condition: {
noCopy: true,
onStart(target) {
this.add('-start', target, 'ability: Bad Pacing');
this.effectState.badpacing = 1;
},
onRestart() {
this.effectState.badpacing++;
},
onModifyAtkPriority: 5,
onModifyAtk(atk, pokemon) {
return this.chainModify(1 - 0.05 * this.effectState.badpacing);
},
onModifyDefPriority: 5,
onModifyDef(def, pokemon) {
return this.chainModify(1 - 0.05 * this.effectState.badpacing);
},
onModifySpAPriority: 5,
onModifySpA(spa, pokemon) {
return this.chainModify(1 - 0.05 * this.effectState.badpacing);
},
onModifySpDPriority: 5,
onModifySpD(spd, pokemon) {
return this.chainModify(1 - 0.05 * this.effectState.badpacing);
},
onModifySpePriority: 5,
onModifySpe(spe, pokemon) {
return this.chainModify(1 - 0.05 * this.effectState.badpacing);
},
},
flags: {},
name: "Bad Pacing",
shortDesc: "This Pokemon's non-HP stats are reduced by 5% each turn.",
},
bathroombreak: {
onAfterMove(target, source, move) {
if (move.type === 'Water') {
this.add('-activate', target, 'ability: Bathroom Break');
target.switchFlag = true;
}
},
onDamagingHitOrder: 1,
onDamagingHit(damage, target, source, move) {
if (move.type === 'Water') target.switchFlag = true;
},
name: "Bathroom Break",
shortDesc: "This Pokemon switches out when using or hit by a Water move.",
},
bigstick: {
onResidual(pokemon) {
if (pokemon.adjacentFoes().length === 0) return;
const branchpoke = this.dex.getActiveMove('branchpoke');
this.actions.useMove(branchpoke, pokemon);
},
flags: {},
name: "big stick",
shortDesc: "This Pokemon uses Branch Poke at the end of each turn.",
},
bloodsucking: {
onStart(pokemon) {
let activated = false;
if (!activated) {
activated = true;
pokemon.addVolatile('bloodsucking');
const leechlife = this.dex.getActiveMove('leechlife');
this.actions.useMove(leechlife, pokemon);
}
},
flags: {},
name: "Bloodsucking",
shortDesc: "On switchin, this Pokemon uses a 20 BP Bug move and heals equal to the damage dealt.",
},
braceforimpact: {
name: "Brace for Impact",
shortDesc: "This Pokemon takes half damage from attacks when switching in.",
onSourceModifyDamage(damage, source, target, move) {
if (!target.activeTurns) {
this.debug('Brace For Impact weaken');
return this.chainModify(0.5);
}
},
},
brokenwand: {
onModifyDamage(damage, source, target, move) {
if (move.category === 'Special') {
return this.chainModify(1.3);
}
},
onModifyMove(move, pokemon) {
if (move.category === 'Special') {
move.recoil = [1, 3];
}
},
name: "Broken Wand",
shortDesc: "This Pokemon's special moves have 1.3x more power but 33% recoil.",
},
capricious: {
onBasePower(basePower, pokemon) {
if (this.randomChance(3, 10)) {
this.attrLastMove('[anim] Fickle Beam All Out');
this.add('-activate', pokemon, 'move: Fickle Beam');
return this.chainModify(2);
}
},
name: "Capricious",
shortDesc: "This Pokemon's attacks have a 30% chance of dealing double damage.",
},
clinch: {
onBeforeTurn(pokemon) {
for (const side of this.sides) {
if (side.hasAlly(pokemon)) continue;
side.addSideCondition('clinch', pokemon);
const data = side.getSideConditionData('clinch');
if (!data.sources) {
data.sources = [];
}
data.sources.push(pokemon);
}
},
onBeforeMove(source, target, move) {
if (move.volatileStatus === "twoturnmove") {
delete move.volatileStatus;
move.accuracy = true;
}
},
onTryHit(source, target) {
target.side.removeSideCondition('clinch');
},
condition: {
duration: 1,
onBeforeSwitchOut(pokemon) {
const move = this.queue.willMove(pokemon.foes()[0]);
const moveName = move && move.moveid ? this.dex.getActiveMove(move.moveid.toString()) : "";
if (!moveName || !moveName.flags['charge']) return;
delete moveName.onTryMove;
this.debug('Clinch start');
let alreadyAdded = false;
pokemon.removeVolatile('destinybond');
for (const source of this.effectState.sources) {
if (!source.isAdjacent(pokemon) || !this.queue.cancelMove(source) || !source.hp) continue;
if (!alreadyAdded) {
this.add('-activate', pokemon.foes()[0], 'ability: Clinch');
alreadyAdded = true;
}
this.actions.runMove(moveName, source, source.getLocOf(pokemon));
}
},
},
flags: {},
name: "Clinch",
shortDesc: "This Pokemon's charge moves fully charge and hit a target switching out.",
},
colorwheel: {
onResidual(pokemon) {
this.add('-ability', pokemon, 'ability: Color Wheel');
const types = ['Bug', 'Dark', 'Dragon', 'Electric', 'Fairy', 'Fighting',
'Fire', 'Flying', 'Ghost', 'Grass', 'Ground', 'Ice',
'Normal', 'Poison', 'Psychic', 'Rock', 'Steel', 'Water'];
const newType1 = types[(types.indexOf(pokemon.types[0]) + 1) % 18];
let newTypes = [newType1];
if (pokemon.types.length > 1) {
const newType2 = types[(types.indexOf(pokemon.types[1]) + 1) % 18];
newTypes = [newType1, newType2];
}
if (pokemon.setType(newTypes)) this.add('-start', pokemon, 'typechange', newTypes.join('/'));
},
flags: {},
name: "Color Wheel",
shortDesc: "This Pokemon changes type(s) to the next one(s) alphabetically at the end of each turn.",
},
comeback: {
onBasePowerPriority: 23,
onBasePower(basePower, pokemon, target, move) {
if (pokemon.activeMoveActions <= 1) return this.chainModify(1.3);
},
flags: {},
name: "Comeback",
shortDesc: "For the first turn after this Pokemon is active, its attacks have 1.3x power.",
},
contagious: {
onDamagingHitOrder: 1,
onDamagingHit(damage, target, source, move) {
if (target.status && !source.status && this.checkMoveMakesContact(move, source, target, true)) {
source.setStatus(target.status);
target.cureStatus();
}
},
flags: {},
name: "Contagious",
shortDesc: "This Pokemon's non-volatile statuses transfer to Pokemon making contact with it.",
},
countermeasures: {
// coded in scripts/actions/secondaries
flags: {},
name: "Countermeasures",
shortDesc: "When an attacker's secondary activates, it loses HP equal to 100 - secondary chance.",
},
crumble: {
onFaint(pokemon) {
const side = pokemon.side.foe;
const stealthrock = side.sideConditions['stealthrock'];
if (!stealthrock) {
this.add('-activate', pokemon, 'ability: Crumble');
side.addSideCondition('stealthrock', pokemon);
}
},
flags: {},
name: "Crumble",
shortDesc: "This Pokemon sets Stealth Rock upon fainting.",
},
dewdrop: {
onBasePowerPriority: 15,
onBasePower(basePower, user, target, move) {
if (move && ['Grass', 'Water', 'Fairy'].includes(move.type)) {
return this.chainModify([4915, 4096]);
}
},
flags: {},
name: "Dewdrop",
shortDesc: "This Pokemon's Grass/Water/Fairy moves have 1.2x power.",
},
diceroller: {
onSourceDamagingHit(damage, target, source, move) {
if (!move.flags['bullet']) return;
const stats: BoostID[] = [];
let stat: BoostID;
for (stat in target.boosts) {
if (source.boosts[stat] < 6) {
if (stat === 'evasion') continue;
stats.push(stat);
}
}
if (stats.length) {
let randomStat = this.sample(stats);
const boost: SparseBoostsTable = {};
boost[randomStat] = 1;
randomStat = this.sample(stats);
boost[randomStat] = 1;
this.boost(boost, source, source);
} else return;
},
flags: {},
name: "Dice Roller",
shortDesc: "This Pokemon boosts random stats (not eva) by 1 twice after using a bullet move.",
},
diseased: {
onResidualOrder: 28,
onResidualSubOrder: 2,
onResidual(pokemon) {
this.damage(pokemon.baseMaxhp / 8, pokemon, pokemon);
},
onSourceDamagingHit(damage, target, source, move) {
// Despite not being a secondary, Shield Dust / Covert Cloak block Toxic Chain's effect
if (target.hasAbility('shielddust') || target.hasItem('covertcloak')) return;
if (this.randomChance(3, 10)) {
target.trySetStatus('psn', source);
}
},
flags: {},
name: "Diseased",
shortDesc: "This Pokemon's moves have a 30% chance to poison, but it loses 1/8 max HP every turn.",
},
drawfour: {
shortDesc: "After knocking out target, if user knows less than 12 moves, it learns target's moves.",
onSourceAfterFaint(length, source, target, effect) {
if (effect && effect.effectType === 'Move') {
for (const moveSlot of source.moveSlots) {
if (moveSlot === null) return;
if (target.moveSlots.length < 12) {
this.attrLastMove('[still]');
if (target.moveSlots.length < 0) return false;
target.moveSlots[target.moveSlots.length] = moveSlot;
target.baseMoveSlots[target.moveSlots.length - 1] = moveSlot;
}
}
}
},
name: "Draw Four",
},
electromagneticmanipulation: {
onUpdate(pokemon) {
if (pokemon.adjacentFoes().length === 0) return;
const target = this.sample(pokemon.adjacentFoes());
if (!target || target.types[0] === 'Electric') return;
target.addVolatile('electromagneticmanipulation');
},
condition: {
onStart(pokemon) {
const types = pokemon.types.length === 2 ? ['Electric', pokemon.types[1]] : ['Electric'];
pokemon.setType(types);
this.add('-start', pokemon, 'typechange', types.join('/'));
},
onUpdate(pokemon) {
if (pokemon.adjacentFoes().length === 0) pokemon.removeVolatile('electromagneticmanipulation');
else {
const target = this.sample(pokemon.adjacentFoes());
if (!target.hasAbility('electromagneticmanipulation')) pokemon.removeVolatile('electromagneticmanipulation');
}
},
onEnd(pokemon) {
const types = pokemon.baseSpecies.types;
console.log(types);
pokemon.setType(types);
this.add('-start', pokemon, 'typechange', types.join('/'));
},
},
flags: {},
name: "Electromagnetic Manipulation",
shortDesc: "While this Pokemon is active, the foe's primary type is Electric.",
},
exhaust: {
onFoeSwitchOut(pokemon) {
if (!pokemon.lastMoveUsed) return;
const moveUsed = pokemon.lastMoveUsed;
const moveIndex = pokemon.moves.indexOf(moveUsed.id);
if (moveIndex === -1) return;
console.log(pokemon.moveSlots[moveIndex]);
pokemon.moveSlots[moveIndex].pp -= 5;
},
flags: {},
name: "Exhaust",
shortDesc: "While this Pokemon is active, opponents switching out lose 5 PP on the last move they used.",
},
firstclassticket: {
onAfterMove(target, source, move) {
if (move.type === 'Flying') {
this.heal(target.baseMaxhp / 4);
}
},
name: "First-Class Ticket",
shortDesc: "This Pokemon's Flying-type moves heal it for 1/4 max HP.",
},
fumigation: {
onDamagingHitOrder: 1,
onDamagingHit(damage, target, source, move) {
const poisongas = this.dex.getActiveMove('poisongas');
this.actions.useMove(poisongas, target);
},
flags: {},
name: "Fumigation",
shortDesc: "When this Pokemon is damaged by a move, it uses Poison Gas against the attacker.",
},
gangster: {
onFractionalPriorityPriority: -1,
onFractionalPriority(priority, pokemon, target, move) {
if (move.type === 'Dark' || move.type === 'Fighting') {
return 0.1;
}
},
flags: {},
name: "Gangster",
shortDesc: "This Pokemon's Dark/Fighting moves go first in its priority bracket.",
},
hibernation: {
onResidualOrder: 28,
onResidualSubOrder: 2,
onResidual(pokemon) {
if (pokemon.status === 'slp') this.boost({ def: 1, spd: 1 });
},
flags: {},
name: "Hibernation",
shortDesc: "This Pokemon's Def/SpD are raised by 1 each turn while asleep.",
},
ironfistening: {
onStart(source) {
this.actions.useMove("Fishing Tokens", source);
},
flags: {},
name: "Iron Fistening",
shortDesc: "On switchin, this Pokemon's side gains a Fishing Token.",
},
magicmissile: {
name: "Magic Missile",
shortDesc: "Magician + when damaged, fling item for 25% max HP.",
onSourceHit(target, source, move) {
if (!move || !target) return;
if (target !== source && move.category !== 'Status') {
if (source.item || source.volatiles['gem'] || move.id === 'fling') return;
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
return;
}
this.add('-item', source, yourItem, '[from] ability: Magic Missile', target);
}
},
onDamagingHit(damage, target, source, move) {
if (target.isSemiInvulnerable()) return;
if (target.ignoringItem()) return false;
const item = target.getItem();
if (!this.singleEvent('TakeItem', item, target.itemState, target, target, move, item)) return false;
if (item.id && !item.megaStone) {
this.damage(source.baseMaxhp / 4, source, target);
target.addVolatile('fling');
if (item.isBerry
) {
if (this.singleEvent('Eat', item, null, source, null, null)) {
this.runEvent('EatItem', source, null, null, item);
if (item.id === 'leppaberry') source.staleness = 'external';
}
if (item.onEat) source.ateBerry = true;
} else if (item.id === 'mentalherb') {
const conditions = ['attract', 'taunt', 'encore', 'torment', 'disable', 'healblock'];
for (const firstCondition of conditions) {
if (source.volatiles[firstCondition]) {
for (const secondCondition of conditions) {
source.removeVolatile(secondCondition);
if (firstCondition === 'attract' && secondCondition === 'attract') {
this.add('-end', source, 'move: Attract', '[from] item: Mental Herb');
}
}
return;
}
}
} else if (item.id === 'whiteherb') {
let activate = false;
const boosts: SparseBoostsTable = {};
let boostName: BoostID;
for (boostName in source.boosts) {
if (source.boosts[boostName] < 0) {
activate = true;
boosts[boostName] = 0;
}
}
if (activate) {
source.setBoost(boosts);
this.add('-clearnegativeboost', source, '[silent]');
}
} else {
if (item.fling?.status) {
source.trySetStatus(item.fling.status, target);
} else if (item.fling?.volatileStatus) {
source.addVolatile(item.fling.volatileStatus, target);
}
}
}
},
},
medic: {
onSwitchOut(pokemon) {
pokemon.side.addSideCondition('medic');
},
condition: {
// this is a side condition
onSideStart(side) {
this.add('-sidestart', side, 'medic', '[silent]');
},
onSwitchIn(pokemon) {
this.heal(pokemon.maxhp / 6);
if (pokemon.status) pokemon.cureStatus();
pokemon.side.removeSideCondition('medic');
this.add('-sideend', pokemon.side, 'move: Medic', '[silent]');
},
},
flags: {},
name: "Medic",
shortDesc: "Upon switching out, the replacement heals 1/6 max HP and has its status cured.",
},
mindbloom: {
onModifyMove(move, pokemon) {
if (move.category === 'Status' && move.target === 'normal') {
move.boosts = {
spd: -1,
};
}
},
onAfterMove(pokemon, source, move) {
if (move.category === 'Status') {
if (pokemon.adjacentFoes().length === 0) return;
const target = this.sample(pokemon.adjacentFoes());
this.boost({ spd: -1 }, target, pokemon, null, true);
}
},
flags: {},
name: "Mind Bloom",
shortDesc: "This Pokemon's status moves lower the opponent's Sp. Def by 1.",
},
momentum: {
name: "Momentum",
shortDesc: "Damage of moves used on consecutive turns is increased. Max 2x after 5 turns.",
flags: {},
onStart(pokemon) {
pokemon.addVolatile('momentum');
},
condition: {
onStart(pokemon) {
this.effectState.lastMove = '';
this.effectState.numConsecutive = 0;
},
onTryMovePriority: -2,
onTryMove(pokemon, target, move) {
if (!pokemon.hasItem('momentum')) {
pokemon.removeVolatile('momentum');
return;
}
if (move.callsMove) return;
if (this.effectState.lastMove === move.id && pokemon.moveLastTurnResult) {
this.effectState.numConsecutive++;
} else if (pokemon.volatiles['twoturnmove']) {
if (this.effectState.lastMove !== move.id) {
this.effectState.numConsecutive = 1;
} else {
this.effectState.numConsecutive++;
}
} else {
this.effectState.numConsecutive = 0;
}
this.effectState.lastMove = move.id;
},
onModifyDamage(damage, source, target, move) {
const dmgMod = [4096, 4915, 5734, 6553, 7372, 8192];
const numConsecutive = this.effectState.numConsecutive > 5 ? 5 : this.effectState.numConsecutive;
this.debug(`Current Metronome boost: ${dmgMod[numConsecutive]}/4096`);
return this.chainModify([dmgMod[numConsecutive], 4096]);
},
},
},
nightlight: {
onSourceModifyAtkPriority: 6,
onSourceModifyAtk(atk, attacker, defender, move) {
if (move.type === 'Dark' || move.type === 'Ghost') {
this.debug('Night Light weaken');
return this.chainModify(0.5);
}
},
onSourceModifySpAPriority: 5,
onSourceModifySpA(atk, attacker, defender, move) {
if (move.type === 'Dark' || move.type === 'Ghost') {
this.debug('Night Light weaken');
return this.chainModify(0.5);
}
},
flags: { breakable: 1 },
name: "Night Light",
shortDesc: "This Pokemon takes halved damage from Dark and Ghost-type moves.",
},
nightmarch: {
onBasePower(basePower, pokemon, target, move) {
for (const ally of pokemon.side.pokemon)
if (ally !== pokemon && ally.set.moves.includes(move.name))
basePower += 20;
return;
},
name: "Night March",
shortDesc: "This Pokemon's attacks gain +20 power for each ally that also has that move.",
},
nocturnal: {
onTryHit(target, source, move) {
if (target !== source && move.type === 'Dark') {
if (!this.heal(target.baseMaxhp / 4)) {
this.add('-immune', target, '[from] ability: Nocturnal');
}
return null;
}
},
flags: { breakable: 1 },
name: "Nocturnal",
shortDesc: "This Pokemon heals 1/4 of its max HP when hit by Dark moves; Dark immunity.",
},
outclass: {
onSourceHit(target, source, move) {
if (!move || !target) return;
const targetType = target.types[0];
let sourceSecondaryType = '???';
if (source.types[1]) sourceSecondaryType = source.types[1];
if (target !== source && move.category !== 'Status' &&
!source.hasType(targetType) && targetType !== '???' &&
!(source.volatiles['outclass'] && !source.side.removeSideCondition('fishingtokens'))) {
source.setType([source.types[0]]);
if (source.addType(targetType)) {
target.setType(target.getTypes(true).map(type => type === targetType ? "???" : type));
this.add('-start', target, 'typechange', target.types.join('/'));
this.add('-start', source, 'typeadd', targetType, '[from] ability: Outclass');
source.addVolatile('outclass');
} else {
this.debug('Failed to take target type.');
if (sourceSecondaryType !== '???') source.setType([source.types[0], sourceSecondaryType]);
}
}
},
condition: {},
flags: {},
name: "Outclass",
shortDesc: "Fishing token or first hit: steals target primary type and replaces its own secondary type.",
},
peckingorder: {
name: "Pecking Order",
shortDesc: "On switch-in, this Pokemon lowers the Defense of adjacent opponents by 1 stage.",
flags: {},
onStart(pokemon) {
let activated = false;
for (const target of pokemon.adjacentFoes()) {
if (!activated) {
this.add('-ability', pokemon, 'Pecking Order', 'boost');
activated = true;
}
if (target.volatiles['substitute']) {
this.add('-immune', target);
} else {
this.boost({ def: -1 }, target, pokemon, null, true);
}
}
},
},
polychrome: {
onBasePower(basePower, pokemon, target, move) {
if (!pokemon.hasType(move.type)) return this.chainModify(1.25);
},
name: "Polychrome",
shortDesc: "This Pokemon's non-STAB moves have 1.25x power.",
},
precognition: {
onBeforeTurn(pokemon) {
if (pokemon.adjacentFoes().length === 0) return;
const target = this.sample(pokemon.adjacentFoes());
const targetAction = this.queue.willMove(target);
if (!targetAction) return;
const pokemonAction = this.queue.willMove(pokemon);
if (!pokemonAction) return;
const targetMove = this.dex.getActiveMove(targetAction.move.id);
const pokemonMove = this.dex.getActiveMove(pokemonAction.move.id);
if (!pokemon.volatiles['substitute'] && targetMove.type === pokemonMove.type) {
const substitute = this.dex.getActiveMove('substitute');
this.actions.useMove(substitute, pokemon);
}
},
flags: {},
name: "Precognition",
shortDesc: "If a foe selects the same type move as the user, the user uses Substitute at the beginning of the turn.",
},
preeminence: {
onModifyPriority(priority, pokemon, target, move) {
const basePowerAfterMultiplier = this.modify(move.basePower, this.event.modifier);
this.debug(`Base Power: ${basePowerAfterMultiplier}`);
if (basePowerAfterMultiplier <= 60) {
this.debug('Preeminence boost');
return priority + 1;
}
},
flags: {},
name: "Preeminence",
shortDesc: "This Pokemon's moves of 60 power or less have +1 priority, including Struggle.",
},
preparation: {
onAfterMove(source, target, move) {
if (move.category === 'Status' && move.name !== 'Substitute' && !move.stallingMove) {
source.addVolatile('preparation');
}
},
condition: {
duration: 2,
onBasePower(basePower, attacker, defender, move) {
return this.chainModify(2);
},
},
name: "Preparation",
shortDesc: "Deals 2x damage the turn after using a status move.",
},
puppetmaster: {
onSwitchIn(pokemon) {
this.effectState.puppetmaster = pokemon;
},
onAnyPrepareHitPriority: -1,
onAnyPrepareHit(source, target, move) {
const puppetmaster = this.effectState.puppetmaster;
if (!move || move.name !== 'Substitute' || move.isZ || move.isMax || move.sourceEffect === 'puppetmaster') return;
this.actions.useMove(move.id, puppetmaster);
return null;
},
onResidualOrder: 28,
onResidualSubOrder: 2,
onResidual(pokemon) {
if (pokemon.adjacentFoes().length === 0) return;
const target = this.sample(pokemon.adjacentFoes());
if (target.volatiles['substitute']) this.damage(target.baseMaxhp / 8, target, pokemon);
},
onDamagingHitOrder: 1,
onDamagingHit(damage, target, source, move) {
if (this.checkMoveMakesContact(move, source, target, true) && this.randomChance(1, 1000)) {
source.formeChange('Cradily');
}
},
flags: {},
name: "Puppet Master",
shortDesc: "This Pokemon steals foes' Substitute. Foes under Substitute lose 1/5 max HP per turn.",
},
quickthinking: {
onBasePowerPriority: 21,
onBasePower(basePower, pokemon) {
for (const target of this.getAllActive()) {
if (target === pokemon) continue;
if (target.newlySwitched || this.queue.willMove(target)) {
return this.chainModify(1.3);
}
}
},
onSourceModifyDamage(damage, source, target, move) {
if (this.queue.willMove(target)) {
this.debug('Slow and Steady neutralize');
return this.chainModify(0.7);
}
},
flags: { breakable: 1 },
name: "Quick Thinking",
shortDesc: "This Pokemon deals 1.3x damage when moving first and takes 0.7x damage when moving last.",
},
refraction: {
onStart(pokemon) {
this.add('-activate', pokemon, 'ability: Refraction');
pokemon.side.addSideCondition('waterpledge');
},
flags: {},
name: "Refraction",
shortDesc: "On switchin, this Pokemon sets Rainbow on its side.",
},
royalguard: {
onStart(pokemon) {
if (this.effectState.royalguard) return;
this.effectState.royalguard = true;
this.actions.useMove("substitute", pokemon);
},
name: "Royal Guard",
shortDesc: "On switchin, this Pokemon uses Substitute. Once per battle.",
},
sealedoff: {
onStart(pokemon) {
this.add('-activate', pokemon, 'ability: Sealed Off');
this.actions.useMove("imprison", pokemon);
},
name: "Sealed Off",
shortDesc: "On switchin, this Pokemon uses Imprison.",
},
searingremark: {
onSourceDamagingHit(damage, target, source, move) {
if (move.flags['sound'] && this.randomChance(3, 10)) {
if (!target.hasAbility('shielddust') && !target.hasItem('covertcloak')) target.trySetStatus('brn', source);
}
},
flags: {},
name: "Searing Remark",
shortDesc: "This Pokemon's sound moves have a 30% of burning the target.",
},
selfrepair: {
onAfterMove(target, source, move) {
if (move.category === 'Status') {
this.heal(target.baseMaxhp / 4);
}
},
name: "Self-Repair",
shortDesc: "This Pokemon heals 25% its max HP after using a Status move.",
},
snowhazard: {
onDamagingHit(damage, target, source, move) {
this.field.setWeather('snowscape');
},
flags: {},
name: "Snowhazard",
shortDesc: "When this Pokemon is hit by an attack, the effect of Snow begins.",
},
spinthewheel: {
onResidual(pokemon) {
const metronome = this.dex.getActiveMove('metronome');
this.actions.useMove(metronome, pokemon);
},
flags: {},
name: "Spin the Wheel",
shortDesc: "This Pokemon uses Metronome at the end of each turn.",
},
statleeching: {
onFoeAfterBoost(boost, target, source, effect) {
if (effect?.name === 'Opportunist' || effect?.name === 'Stat Leeching' || effect?.name === 'Mirror Herb') return;
const pokemon = this.effectState.target;
const boosts: Partial<BoostsTable> = {};
let i: BoostID;
for (i in boost) {
boost[i]! *= -1;
boosts[i] = boost[i];
}
if (Object.keys(boosts).length < 1) return;
this.boost(boosts, pokemon);
},
flags: {},
name: "Stat Leeching",
shortDesc: "This Pokemon gains the opposite stat change as opposing Pokemon.",
},
strongbreeze: {
onStart(pokemon) {
if (this.effectState.strongbreeze) return;
this.effectState.strongbreeze = true;
this.add('-activate', pokemon, 'ability: Strong Breeze');
pokemon.side.addSideCondition('tailwind');
},
name: "Strong Breeze",
shortDesc: "On switchin, this Pokemon sets Tailwind. Once per battle.",
},
superrod: {
onAfterMove(target, source, move) {
if (!source.side.sideConditions['fishingTokens']) return;
if (move.type === 'Water') {
this.heal(source.baseMaxhp / 16 * source.side.sideConditions['fishingTokens'].layers);
}
},
name: "Super Rod",
shortDesc: "This Pokemon's Water-type moves heal it for 1/16 max HP for each Fishing Token.",
},
treasurecraze: {
onAfterUseItem(item, pokemon) {
if (pokemon !== this.effectState.target) return;
this.boost({ atk: 2 }, pokemon, pokemon, null, false, true);
},
onTakeItem(item, pokemon) {
this.boost({ atk: 2 }, pokemon, pokemon, null, false, true);
},
flags: {},
name: "Treasure Craze",
shortDesc: "When this Pokemon loses its held item, its Attack is raised by 2.",
},
troubled: {
onStart(source) {
source.addVolatile('troubled');
},
condition: {
noCopy: true,
onDisableMove(pokemon) {
if (pokemon.lastMove && pokemon.lastMove.id !== 'struggle') pokemon.disableMove(pokemon.lastMove.id);
},
},
name: "Troubled",
shortDesc: "This Pokemon cannot use the same move twice in a row.",
},
};

File diff suppressed because it is too large Load Diff

View File

@ -1,44 +0,0 @@
export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
leechlife: {
inherit: true,
onModifyMove(move, pokemon) {
if (!pokemon.volatiles['bloodsucking']) return;
move.basePower = 20;
move.drain = [1, 1];
if (pokemon.getStat('atk', false, true) > pokemon.getStat('spa', false, true)) move.category = 'Physical';
pokemon.removeVolatile('bloodsucking');
},
},
// fake moves
fishingtokens: {
accuracy: true,
basePower: 0,
category: "Status",
name: "Fishing Tokens",
pp: 30,
priority: 0,
flags: { snatch: 1 },
sideCondition: 'fishingtokens',
condition: {
onSideStart(side) {
this.add('-sidestart', side, 'Fishing Tokens');
this.effectState.layers = 1;
},
onSideRestart(side) {
this.add('-sidestart', side, 'Fishing Tokens');
this.effectState.layers++;
},
onSideResidualOrder: 26,
onSideResidualSubOrder: 2,
onSideEnd(side) {
this.add('-sideend', side, 'move: Fishing Tokens');
},
},
secondary: null,
target: "allySide",
type: "Water",
zMove: { boost: { spd: 1 } },
contestType: "Beautiful", // they sure are
},
};

View File

@ -1,571 +0,0 @@
export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable = {
spreetah: {
num: 2001,
name: "Spreetah",
types: ["Electric", "Fire"],
baseStats: { hp: 64, atk: 90, def: 54, spa: 90, spd: 54, spe: 150 },
abilities: { 0: "Momentum" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
raintoad: {
num: 2002,
name: "Raintoad",
types: ["Normal"],
baseStats: { hp: 110, atk: 90, def: 70, spa: 70, spd: 80, spe: 90 },
abilities: { 0: "Color Wheel" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
pomegrenade: {
num: 2003,
name: "Pomegrenade",
types: ["Fire", "Fairy"],
baseStats: { hp: 120, atk: 50, def: 85, spa: 105, spd: 70, spe: 85 },
abilities: { 0: "Mind Bloom" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
surfsurge: {
num: 2004,
name: "Surfsurge",
types: ["Water", "Electric"],
baseStats: { hp: 86, atk: 74, def: 110, spa: 128, spd: 74, spe: 68 },
abilities: { 0: "Strong Breeze" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
anxiousoil: {
num: 2005,
name: "Anxiousoil",
types: ["Ground", "Ghost"],
baseStats: { hp: 80, atk: 130, def: 65, spa: 130, spd: 65, spe: 115 },
abilities: { 0: "Troubled" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
depresloth: {
num: 2006,
name: "Depresloth",
types: ["Electric", "Ghost"],
baseStats: { hp: 111, atk: 105, def: 65, spa: 115, spd: 75, spe: 74 },
abilities: { 0: "Exhaust" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
obsallas: {
num: 2007,
name: "Obsallas",
types: ["Rock", "Normal"],
baseStats: { hp: 102, atk: 92, def: 69, spa: 52, spd: 70, spe: 112 },
abilities: { 0: "Crumble" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
nharboard: {
num: 2008,
name: "Nharboard",
types: ["Water", "Steel"],
baseStats: { hp: 120, atk: 90, def: 70, spa: 100, spd: 50, spe: 60 },
abilities: { 0: "First-Class Ticket" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
lampyre: {
num: 2009,
name: "Lampyre",
types: ["Steel", "Fire"],
baseStats: { hp: 50, atk: 45, def: 100, spa: 145, spd: 100, spe: 80 },
abilities: { 0: "Night Light" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
noyew: {
num: 2010,
name: "Noyew",
types: ["Grass", "Steel"],
baseStats: { hp: 64, atk: 51, def: 116, spa: 51, spd: 116, spe: 42 },
abilities: { 0: "Back at Ya!" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
underhazard: {
num: 2011,
name: "Underhazard",
types: ["Dark", "Poison"],
baseStats: { hp: 140, atk: 95, def: 75, spa: 100, spd: 80, spe: 40 },
abilities: { 0: "Countermeasures" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
bleyabat: {
num: 2012,
name: "Bleyabat",
types: ["Ghost", "Flying"],
baseStats: { hp: 125, atk: 95, def: 110, spa: 55, spd: 100, spe: 55 },
abilities: { 0: "Night Light" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
nectaregal: {
num: 2013,
name: "Nectaregal",
types: ["Grass", "Electric"],
baseStats: { hp: 80, atk: 45, def: 80, spa: 90, spd: 100, spe: 70 },
abilities: { 0: "Electromagnetic Manipulation" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
nummanutts: {
num: 2014,
name: "Nummanutts",
types: ["Poison", "Dark"],
baseStats: { hp: 80, atk: 80, def: 100, spa: 100, spd: 120, spe: 40 },
abilities: { 0: "Dice Roller" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
mosstrosity: {
num: 2015,
name: "Mosstrosity",
types: ["Dark", "Grass"],
baseStats: { hp: 70, atk: 110, def: 50, spa: 100, spd: 70, spe: 130 },
abilities: { 0: "Clinch" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
faellen: {
num: 2016,
name: "Faellen",
types: ["Fairy", "Dark"],
baseStats: { hp: 80, atk: 60, def: 65, spa: 130, spd: 100, spe: 115 },
abilities: { 0: "Broken Wand" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
nucleophage: {
num: 2017,
name: "Nucleophage",
types: ["Poison", "Psychic"],
baseStats: { hp: 60, atk: 100, def: 50, spa: 130, spd: 60, spe: 125 },
abilities: { 0: "Diseased" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
tardeblade: {
num: 2018,
name: "Tardeblade",
types: ["Steel", "Psychic"],
baseStats: { hp: 130, atk: 90, def: 90, spa: 80, spd: 80, spe: 50 },
abilities: { 0: "Hibernation" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
bugsome: {
num: 2019,
name: "Bugsome",
types: ["Bug"],
baseStats: { hp: 89, atk: 110, def: 90, spa: 60, spd: 70, spe: 136 },
abilities: { 0: "Stat Leeching" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
pestifer: {
num: 2020,
name: "Pestifer",
types: ["Poison", "Ground"],
baseStats: { hp: 110, atk: 50, def: 100, spa: 130, spd: 55, spe: 96 },
abilities: { 0: "Contagious" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
fungemory: {
num: 2021,
name: "Fungemory",
types: ["Psychic", "Ghost"],
baseStats: { hp: 66, atk: 106, def: 66, spa: 116, spd: 166, spe: 46 },
abilities: { 0: "Sealed Off" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
guarden: {
num: 2022,
name: "Guarden",
types: ["Steel", "Grass"],
baseStats: { hp: 94, atk: 119, def: 120, spa: 42, spd: 52, spe: 73 },
abilities: { 0: "Royal Guard" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
hawksectiff: {
num: 2023,
name: "Hawksectiff",
types: ["Dark", "Flying"],
baseStats: { hp: 110, atk: 125, def: 90, spa: 65, spd: 80, spe: 75 },
abilities: { 0: "Pecking Order" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
boogeymancer: {
num: 2024,
name: "Boogeymancer",
types: ["Ghost", "Fire"],
baseStats: { hp: 65, atk: 118, def: 63, spa: 112, spd: 65, spe: 122 },
abilities: { 0: "Broken Wand" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
cliffilisk: {
num: 2025,
name: "Cliffilisk",
types: ["Rock", "Dragon"],
baseStats: { hp: 140, atk: 148, def: 132, spa: 40, spd: 62, spe: 28 },
abilities: { 0: "Crumble" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
aesap: {
num: 2026,
name: "Aesap",
types: ["Dark", "Fairy"],
baseStats: { hp: 75, atk: 115, def: 100, spa: 70, spd: 120, spe: 120 },
abilities: { 0: "Exhaust" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
delirirak: {
num: 2027,
name: "Delirirak",
types: ["Ice", "Ghost"],
baseStats: { hp: 75, atk: 115, def: 55, spa: 130, spd: 100, spe: 115 },
abilities: { 0: "Fumigation" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
lazahrusk: {
num: 2028,
name: "Lazahrusk",
types: ["Bug", "Ghost"],
baseStats: { hp: 89, atk: 105, def: 140, spa: 105, spd: 106, spe: 20 },
abilities: { 0: "Diseased" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
cogwyld: {
num: 2029,
name: "Cogwyld",
types: ["Dark", "Steel"],
baseStats: { hp: 97, atk: 102, def: 87, spa: 72, spd: 87, spe: 97 },
abilities: { 0: "Self-Repair" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
folibower: {
num: 2030,
name: "Folibower",
types: ["Flying", "Grass"],
baseStats: { hp: 90, atk: 101, def: 83, spa: 85, spd: 70, spe: 106 },
abilities: { 0: "Treasure Craze" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
araquisis: {
num: 2031,
name: "Araquisis",
types: ["Psychic", "Dark"],
baseStats: { hp: 109, atk: 117, def: 92, spa: 80, spd: 88, spe: 38 },
abilities: { 0: "Precognition" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
liwyzard: {
num: 2032,
name: "Liwyzard",
types: ["Dragon", "Fairy"],
baseStats: { hp: 75, atk: 75, def: 75, spa: 100, spd: 100, spe: 100 },
abilities: { 0: "Magic Missile" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
shail: {
num: 2033,
name: "Shail",
types: ["Ice", "Ground"],
baseStats: { hp: 55, atk: 65, def: 113, spa: 101, spd: 124, spe: 41 },
abilities: { 0: "Snowhazard" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
fightinfly: {
num: 2034,
name: "Fightinfly",
types: ["Bug", "Fighting"],
baseStats: { hp: 60, atk: 110, def: 70, spa: 105, spd: 70, spe: 115 },
abilities: { 0: "Nocturnal" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
arthrostrike: {
num: 2035,
name: "Arthrostrike",
types: ["Bug", "Fighting"],
baseStats: { hp: 80, atk: 120, def: 85, spa: 50, spd: 65, spe: 60 },
abilities: { 0: "Preeminence" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
magmouth: {
num: 2036,
name: "Magmouth",
types: ["Ground", "Normal"],
baseStats: { hp: 144, atk: 60, def: 100, spa: 90, spd: 53, spe: 20 },
abilities: { 0: "Searing Remark" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
orchidauntless: {
num: 2037,
name: "Orchidauntless",
types: ["Grass", "Psychic"],
baseStats: { hp: 84, atk: 121, def: 76, spa: 118, spd: 79, spe: 121 },
abilities: { 0: "Dewdrop" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
boillusk: {
num: 2038,
name: "Boillusk",
types: ["Water", "Fire"],
baseStats: { hp: 88, atk: 86, def: 132, spa: 96, spd: 118, spe: 20 },
abilities: { 0: "Absorber" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
minkai: {
num: 2039,
name: "Minkai",
types: ["Fighting", "Ice"],
baseStats: { hp: 80, atk: 60, def: 110, spa: 120, spd: 60, spe: 110 },
abilities: { 0: "Nocturnal" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
shurifluri: {
num: 2040,
name: "Shurifluri",
types: ["Ice", "Steel"],
baseStats: { hp: 66, atk: 128, def: 54, spa: 78, spd: 78, spe: 108 },
abilities: { 0: "Snowhazard" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
roolette: {
num: 2041,
name: "Roolette",
types: ["Normal", "Fighting"],
baseStats: { hp: 100, atk: 120, def: 75, spa: 100, spd: 75, spe: 100 },
abilities: { 0: "Spin the Wheel" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
frenzaiai: {
num: 2042,
name: "Frenzaiai",
types: ["Normal", "Poison"],
baseStats: { hp: 83, atk: 95, def: 85, spa: 80, spd: 92, spe: 110 },
abilities: { 0: "Asymmetry" },
weightkg: 50,
prevo: "Grafaiai",
evoType: "levelFriendship",
eggGroups: ["Undiscovered"],
},
buffball: {
num: 2043,
name: "Buffball",
types: ["Fighting", "Bug"],
baseStats: { hp: 101, atk: 127, def: 71, spa: 103, spd: 71, spe: 97 },
abilities: { 0: "Preparation" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
rootfraction: {
num: 2044,
name: "Rootfraction",
types: ["Grass", "Poison"],
baseStats: { hp: 95, atk: 60, def: 94, spa: 121, spd: 94, spe: 66 },
abilities: { 0: "Refraction" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
remnant: {
num: 2045,
name: "Remnant",
types: ["Ghost"],
baseStats: { hp: 75, atk: 75, def: 145, spa: 75, spd: 155, spe: 75 },
abilities: { 0: "Night March" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
marlord: {
num: 2046,
name: "Marlord",
types: ["Steel", "Fighting"],
baseStats: { hp: 91, atk: 124, def: 67, spa: 67, spd: 118, spe: 74 },
abilities: { 0: "Polychrome" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
marsonmallow: {
num: 2047,
name: "Marsonmallow",
types: ["Fairy", "Fire"],
baseStats: { hp: 72, atk: 107, def: 96, spa: 91, spd: 136, spe: 23 },
abilities: { 0: "big stick" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
trawlutre: {
num: 2048,
name: "Trawlutre",
types: ["Water", "Fighting"],
baseStats: { hp: 72, atk: 104, def: 75, spa: 94, spd: 75, spe: 120 },
abilities: { 0: "Super Rod" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
manticrash: {
num: 2049,
name: "Manticrash",
types: ["Normal", "Ground"],
baseStats: { hp: 105, atk: 108, def: 86, spa: 71, spd: 89, spe: 42 },
abilities: { 0: "Comeback" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
ichthyocorn: {
num: 2050,
name: "Ichthyocorn",
types: ["Water", "Fairy"],
baseStats: { hp: 85, atk: 60, def: 85, spa: 80, spd: 105, spe: 105 },
abilities: { 0: "Capricious" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
cryobser: {
num: 2051,
name: "Cryobser",
types: ["Normal", "Ice"],
baseStats: { hp: 113, atk: 70, def: 91, spa: 70, spd: 111, spe: 50 },
abilities: { 0: "Medic" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
skibidragon: {
num: 2052,
name: "Skibidragon",
types: ["Dragon"],
baseStats: { hp: 90, atk: 90, def: 98, spa: 105, spd: 92, spe: 125 },
abilities: { 0: "Bathroom Break" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
tuxquito: {
num: 2053,
name: "Tuxquito",
types: ["Bug", "Flying"],
baseStats: { hp: 95, atk: 100, def: 75, spa: 110, spd: 75, spe: 145 },
abilities: { 0: "Bloodsucking" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
cosmole: {
num: 2054,
name: "Cosmole",
types: ["Ground", "Psychic"],
baseStats: { hp: 124, atk: 108, def: 79, spa: 61, spd: 79, spe: 89 },
abilities: { 0: "Quick Thinking" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
suragon: {
num: 2055,
name: "Suragon",
types: ["Psychic", "Dragon"],
baseStats: { hp: 80, atk: 70, def: 70, spa: 122, spd: 122, spe: 86 },
abilities: { 0: "Antimatter" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
shufflux: {
num: 2056,
name: "Shufflux",
types: ["Fairy", "Normal"],
baseStats: { hp: 92, atk: 60, def: 60, spa: 114, spd: 124, spe: 84 },
abilities: { 0: "Draw Four" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
mindwyrm: {
num: 2057,
name: "Mindwyrm",
types: ["Bug", "Dragon"],
baseStats: { hp: 120, atk: 120, def: 90, spa: 60, spd: 90, spe: 90 },
abilities: { 0: "Quick Thinking" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
crashtank: {
num: 2058,
name: "Crashtank",
types: ["Normal"],
baseStats: { hp: 60, atk: 100, def: 140, spa: 50, spd: 120, spe: 70 },
abilities: { 0: "Brace for Impact" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
leviadon: {
num: 2059,
name: "Leviadon",
types: ["Dragon"],
baseStats: { hp: 131, atk: 91, def: 66, spa: 140, spd: 61, spe: 61 },
abilities: { 0: "Gangster" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
oonoonsi: {
num: 2060,
name: "Oonoonsi",
types: ["Bug", "Psychic"],
baseStats: { hp: 73, atk: 65, def: 85, spa: 125, spd: 135, spe: 80 },
abilities: { 0: "Puppet Master" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
contradox: {
num: 2061,
name: "Contradox",
types: ["Ice", "Psychic"],
baseStats: { hp: 85, atk: 120, def: 85, spa: 100, spd: 70, spe: 106 },
abilities: { 0: "Antimatter" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
roddammit: {
num: 2062,
name: "Roddammit",
types: ["Dark", "Grass"],
baseStats: { hp: 75, atk: 130, def: 134, spa: 95, spd: 86, spe: 45 },
abilities: { 0: "Outclass" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
rizzquaza: {
num: 2063,
name: "Rizzquaza",
types: ["Dragon", "Water"],
baseStats: { hp: 110, atk: 80, def: 95, spa: 75, spd: 90, spe: 90 },
abilities: { 0: "Iron Fistening" },
weightkg: 50,
eggGroups: ["Undiscovered"],
},
};

View File

@ -1,517 +0,0 @@
import RandomTeams from '../../random-battles/gen9/teams';
export interface CPMSet {
species: string;
ability: string | string[];
item: string | string[];
gender: GenderName | GenderName[];
moves: (string | string[])[];
signatureMove: string;
evs?: { hp?: number, atk?: number, def?: number, spa?: number, spd?: number, spe?: number };
ivs?: { hp?: number, atk?: number, def?: number, spa?: number, spd?: number, spe?: number };
nature?: string | string[];
shiny?: number | boolean;
level?: number;
happiness?: number;
skip?: string;
teraType?: string | string[];
}
interface CPMSets { [k: string]: CPMSet }
export const cpmSets: CPMSets = {
Aesap: {
species: 'Aesap', ability: 'Exhaust', item: 'Leppa Berry', gender: '',
moves: ['Knock Off', 'U-turn', 'Strength Sap'],
signatureMove: 'Let\'s Snuggle Forever',
evs: { hp: 4, atk: 252, spe: 252 }, nature: 'Jolly', teraType: 'Flying',
},
Anxiousoil: {
species: 'Anxiousoil', ability: 'Troubled', item: 'Leftovers', gender: '',
moves: [['Earthquake', 'Earth Power'], ['Shadow Ball', 'Poltergeist'], ['Ice Beam', 'Spikes', 'Moonblast']],
signatureMove: 'Taunt',
evs: { hp: 252, spa: 4, spe: 252 }, nature: 'Naive', teraType: 'Steel',
},
Araquisis: {
species: 'Araquisis', ability: 'Precognition', item: 'Leftovers', gender: '',
moves: ['Zen Headbutt', ['Sticky Web', 'Trick Room'], ['Moonlight', 'Pursuit']],
signatureMove: 'Knock Off',
evs: { hp: 252, atk: 252, spd: 4 }, ivs: { spe: 0 }, nature: 'Adamant', teraType: 'Dark',
},
Arthrostrike: {
species: 'Arthrostrike', ability: 'Preeminence', item: 'Loaded Dice', gender: '',
moves: ['Pin Missile', 'Earthquake', ['Fell Stinger', 'U-turn']],
signatureMove: 'Arm Thrust',
evs: { hp: 248, atk: 252, spe: 8 }, nature: 'Adamant', teraType: 'Fairy',
},
Bleyabat: {
species: 'Bleyabat', ability: 'Night Light', item: 'Heavy-Duty Boots', gender: '',
moves: ['Dual Wingbeat', 'Shadow Claw', 'Roost'],
signatureMove: 'Bulk Up',
evs: { hp: 252, def: 4, spd: 252 }, nature: 'Careful', teraType: 'Water',
},
Boillusk: {
species: 'Boillusk', ability: 'Absorber', item: 'Heavy-Duty Boots', gender: '',
moves: ['Steam Eruption', 'Earth Power', ['Scald', 'Stealth Rock', 'Overheat']],
signatureMove: 'Fire Blast',
evs: { hp: 252, spa: 252, spe: 4 }, nature: 'Modest', teraType: 'Fire',
},
Boogeymancer: {
species: 'Boogeymancer', ability: 'Broken Wand', item: 'Heavy-Duty Boots', gender: '',
moves: ['Shadow Ball', 'Energy Ball', ['Parting Shot', 'Calm Mind']],
signatureMove: 'Lava Plume',
evs: { atk: 4, spa: 252, spd: 252 }, nature: 'Timid', teraType: 'Dragon',
},
Buffball: {
species: 'Buffball', ability: 'Preparation', item: 'Leftovers', gender: '',
moves: ['Leech Life', 'Knock Off', ['Bulk Up', 'Agility']],
signatureMove: "Close Combat",
evs: { hp: 4, atk: 252, spe: 252 }, nature: 'Adamant', teraType: 'Ground',
},
Bugsome: {
species: 'Bugsome', ability: 'Stat Leeching', item: 'Heavy-Duty Boots', gender: '',
moves: ['Lunge', ['Crunch', 'Psychic Fangs'], 'Close Combat'],
signatureMove: "U-turn",
evs: { atk: 252, def: 4, spe: 252 }, nature: 'Jolly', teraType: 'Fire',
},
Cliffilisk: {
species: 'Cliffilisk', ability: 'Crumble', item: 'Leftovers', gender: '',
moves: ['Dragon Claw', ['Knock Off', 'Earthquake'], ['Stone Edge', 'Head Smash']],
signatureMove: "Collision Course",
evs: { hp: 252, atk: 252, spd: 4 }, nature: 'Adamant', teraType: 'Fighting',
},
Cogwyld: {
species: 'Cogwyld', ability: 'Self-Repair', item: 'Heavy-Duty Boots', gender: '',
moves: ['Knock Off', 'Gear Grind', ['Heal Bell', 'Thunder Wave', 'Stealth Rock']],
signatureMove: 'Parting Shot',
evs: { hp: 252, def: 4, spe: 252 }, nature: 'Jolly', teraType: 'Water',
},
Contradox: {
species: 'Contradox', ability: 'Antimatter', item: 'Life Orb', gender: '',
moves: ['Psychic Fangs', ['Earthquake', 'Fire Punch', 'Ice Shard'], 'U-turn'],
signatureMove: 'Triple Axel',
evs: { atk: 252, def: 4, spe: 252 }, nature: 'Jolly', teraType: 'Poison',
},
Cosmole: {
species: 'Cosmole', ability: 'Quick Thinking', item: 'Life Orb', gender: '',
moves: [['Swords Dance', 'Rapid Spin', 'Close Combat'], 'Earthquake', 'Psychic Fangs'],
signatureMove: 'Extreme Speed',
evs: { atk: 252, spd: 4, spe: 252 }, nature: 'Adamant', teraType: 'Ground',
},
Crashtank: {
species: 'Crashtank', ability: 'Brace for Impact', item: 'Heavy-Duty Boots', gender: 'N',
moves: ['Knock Off', 'Body Press', ['Rapid Spin', 'Spikes', 'U-turn']],
signatureMove: 'Body Slam',
evs: { hp: 248, def: 252, spd: 8 }, nature: 'Impish', teraType: 'Fairy',
},
Cryobser: {
species: 'Cryobser', ability: 'Medic', item: 'Heavy-Duty Boots', gender: '',
moves: ['Ice Beam', 'Heal Bell', ['Discharge', 'Body Slam']],
signatureMove: 'Revival Blessing',
evs: { hp: 252, spd: 252, spe: 4 }, nature: 'Calm', teraType: 'Fairy',
},
Delirirak: {
species: 'Delirirak', ability: 'Fumigation', item: 'Heavy-Duty Boots', gender: '',
moves: ['Recover', 'Ice Beam', ['Earth Power', 'Toxic', 'Will-O-Wisp']],
signatureMove: 'Hex',
evs: { hp: 4, spa: 252, spd: 252 }, nature: 'Modest', teraType: 'Stellar',
},
Depresloth: {
species: 'Depresloth', ability: 'Exhaust', item: 'Leftovers', gender: '',
moves: ['Thunderbolt', 'Shadow Ball', 'Volt Switch'],
signatureMove: 'Signal Beam',
evs: { hp: 252, def: 252, spa: 4 }, nature: 'Timid', teraType: 'Bug',
},
Faellen: {
species: 'Faellen', ability: 'Broken Wand', item: 'Choice Specs', gender: '',
moves: [['U-turn', 'Trick'], 'Dark Pulse', ['Flamethrower', 'Moonblast']],
signatureMove: 'Light of Ruin',
evs: { spa: 252, spd: 4, spe: 252 }, nature: 'Timid', teraType: 'Ghost',
},
Fightinfly: {
species: 'Fightinfly', ability: 'Nocturnal', item: 'Choice Band', gender: '',
moves: ['Close Combat', ['Knock Off', 'Stone Edge'], ['Earthquake', 'Flare Blitz']],
signatureMove: 'U-turn',
evs: { hp: 4, atk: 252, spe: 252 }, nature: 'Jolly', teraType: 'Fairy',
},
Folibower: {
species: 'Folibower', ability: 'Treasure Craze', item: 'Salac Berry', gender: '',
moves: ['Seed Bomb', 'Acrobatics', 'Recycle'],
signatureMove: 'Stuff Cheeks',
evs: { atk: 252, def: 4, spe: 252 }, nature: 'Jolly', teraType: 'Fairy',
},
Frenzaiai: {
species: 'Frenzaiai', ability: 'Asymmetry', item: 'Black Sludge', gender: '',
moves: ['Toxic', 'U-turn', 'Knock Off'],
signatureMove: 'Encore',
evs: { hp: 252, spd: 4, spe: 252 }, nature: 'Jolly', teraType: 'Fairy',
},
Fungemory: {
species: 'Fungemory', ability: 'Sealed Off', item: 'Leftovers', gender: 'M',
moves: ['Psychic Noise', 'Scald', ['Tidy Up', 'Teleport']],
signatureMove: 'Shadow Ball',
evs: { hp: 252, spa: 4, spd: 252 }, nature: 'Calm', teraType: 'Psychic',
},
Guarden: {
species: 'Guarden', ability: 'Royal Guard', item: 'Leftovers', gender: '',
moves: ['Bulk Up', 'Leaf Blade', ['Body Press', 'Bitter Blade', 'Iron Head']],
signatureMove: 'Spiky Shield',
evs: { hp: 252, spd: 252, spe: 4 }, nature: 'Careful', teraType: 'Steel',
},
Hawksectiff: {
species: 'Hawksectiff', ability: 'Pecking Order', item: 'Heavy-Duty Boots', gender: '',
moves: ['Dual Wingbeat', 'Knock Off', ['Roost', 'Sucker Punch']],
signatureMove: 'Pursuit',
evs: { hp: 4, atk: 252, spe: 252 }, nature: 'Jolly', teraType: 'Steel',
},
Ichthyocorn: {
species: 'Ichthyocorn', ability: 'Capricious', item: 'Leftovers', gender: '',
moves: [['Recover', 'Ice Beam'], 'Moonblast', ['Hydro Pump', 'Scald']],
signatureMove: 'Flip Turn',
evs: { spa: 252, spd: 4, spe: 252 }, nature: 'Timid', teraType: 'Rock',
},
Lampyre: {
species: 'Lampyre', ability: 'Night Light', item: ['Air Balloon', 'Choice Specs'], gender: '',
moves: ['Flash Cannon', 'Scald', ['Calm Mind', 'Overheat']],
signatureMove: 'Fire Blast',
evs: { hp: 252, def: 28, spd: 224 }, ivs: { atk: 0, spe: 0 }, nature: 'Relaxed',
},
Lazahrusk: {
species: 'Lazahrusk', ability: 'Diseased', item: 'Heavy-Duty Boots', gender: '',
moves: [['Leech Life', 'Shadow Ball'], 'Strength Sap', 'Revival Blessing'],
signatureMove: 'Mortal Spin',
evs: { hp: 252, def: 252, spd: 4 }, nature: 'Relaxed', teraType: 'Fairy',
},
Leviadon: {
species: 'Leviadon', ability: 'Gangster', item: 'Leftovers', gender: '',
moves: ['Close Combat', ['Glare', 'Flash Cannon', 'Fire Blast'], ['Dragon Tail', 'Stealth Rock']],
signatureMove: 'Core Enforcer',
evs: { hp: 252, spa: 252, spd: 4 }, nature: 'Modest', teraType: 'Water',
},
Liwyzard: {
species: 'Liwyzard', ability: 'Magic Missile', item: 'Light Ball', gender: '',
moves: ['Calm Mind', 'Mystical Fire', 'Moonlight'],
signatureMove: 'Strange Steam',
evs: { hp: 252, spa: 4, spe: 252 }, nature: 'Timid', teraType: 'Steel',
},
Magmouth: {
species: 'Magmouth', ability: 'Searing Remark', item: 'Leftovers', gender: '',
moves: ['Boomburst', 'Sandsear Storm', ['Parting Shot', 'Torch Song']],
signatureMove: 'Burning Bulwark',
evs: { hp: 252, def: 252, spd: 4 }, nature: 'Bold', teraType: ['Steel', 'Flying', 'Electric', 'Dark'],
},
Manticrash: {
species: 'Manticrash', ability: 'Comeback', item: 'Assault Vest', gender: '',
moves: ['Hyper Drill', ['Fake Out', 'First Impression'], ['Wild Charge', 'U-turn']],
signatureMove: 'Headlong Rush',
evs: { hp: 252, atk: 252, spd: 4 }, nature: 'Adamant', teraType: 'Grass',
},
Marlord: {
species: 'Marlord', ability: 'Polychrome', item: 'Assault Vest', gender: '',
moves: ['Close Combat', ['Head Smash', 'Knock Off'], 'Earthquake'],
signatureMove: 'Iron Head',
evs: { hp: 148, atk: 156, spd: 204 }, nature: 'Adamant', teraType: 'Steel',
},
Marsonmallow: {
species: 'Marsonmallow', ability: 'big stick', item: 'Heavy-Duty Boots', gender: '',
moves: ['Play Rough', ['Recover', 'Leaf Blade'], ['Trick Room', 'Sticky Web']],
signatureMove: 'Flare Blitz',
evs: { hp: 252, atk: 252, def: 4 }, nature: 'Brave', teraType: 'Poison',
},
Mindwyrm: {
species: 'Mindwyrm', ability: 'Quick Thinking', item: 'Sitrus Berry', gender: '',
moves: ['Leech Life', ['Knock Off', 'Temper Flare'], 'Dragon Hammer'],
signatureMove: 'Clangorous Soul',
evs: { atk: 252, def: 4, spe: 252 }, nature: 'Jolly', teraType: 'Any',
},
Minkai: {
species: 'Minkai', ability: 'Nocturnal', item: ['Life Orb', 'Choice Specs'], gender: '',
moves: ['Focus Blast', 'Earth Power', ['Calm Mind', 'Shadow Ball']],
signatureMove: 'Ice Beam',
evs: { spa: 252, spd: 4, spe: 252 }, nature: 'Timid', teraType: 'Any',
},
Mosstrosity: {
species: 'Mosstrosity', ability: 'Clinch', item: 'Power Herb', gender: '',
moves: ['Meteor Beam', 'Surf', 'Thunderbolt'],
signatureMove: 'Solar Beam',
evs: { spa: 252, spd: 4, spe: 252 }, nature: 'Timid', teraType: 'Any',
},
Nectaregal: {
species: 'Nectaregal', ability: 'Electromagnetic Manipulation', item: 'Leftovers', gender: '',
moves: ['Giga Drain', ['Volt Switch', 'Discharge', 'Mud Shot'], ['Spikes', 'Calm Mind']],
signatureMove: 'Synthesis',
evs: { hp: 252, def: 252, spd: 4 }, nature: 'Bold', teraType: 'Flying',
},
Nharboard: {
species: 'Nharboard', ability: 'First-Class Ticket', item: 'Leftovers', gender: '',
moves: ['Flash Cannon', 'Flip Turn', ['Hurricane', 'Defog']],
signatureMove: 'Scald',
evs: { hp: 252, def: 4, spa: 252 }, nature: 'Modest', teraType: 'Fairy',
},
Noyew: {
species: 'Noyew', ability: 'Back at Ya!', item: 'Leftovers', gender: '',
moves: ['Spikes', 'Toxic', 'Grass Knot'],
signatureMove: 'Leech Seed',
evs: { hp: 252, def: 128, spd: 128 }, nature: 'Careful', teraType: 'Fairy',
},
Nucleophage: {
species: 'Nucleophage', ability: 'Diseased', item: 'Black Sludge', gender: '',
moves: ['Psychic', 'Sludge Wave', 'Lava Plume'],
signatureMove: 'Nasty Plot',
evs: { spa: 252, spd: 4, spe: 252 }, nature: 'Timid', teraType: 'Psychic',
},
Nummanutts: {
species: 'Nummanutts', ability: 'Dice Roller', item: 'Black Sludge', gender: '',
moves: ['Knock Off', 'Recover', ['Mortal Spin', 'U-turn']],
signatureMove: 'Sludge Bomb',
evs: { hp: 252, spa: 4, spd: 252 }, nature: 'Sassy', teraType: 'Poison',
},
Obsallas: {
species: 'Obsallas', ability: 'Crumble', item: 'Life Orb', gender: '',
moves: [['Fire Fang', 'Explosion', 'Taunt'], 'Icicle Crash', 'Extreme Speed'],
signatureMove: 'Head Smash',
evs: { atk: 252, def: 4, spe: 252 }, nature: 'Jolly', teraType: 'Fairy',
},
Oonoonsi: {
species: 'Oonoonsi', ability: 'Puppet Master', item: 'Heavy-Duty Boots', gender: '',
moves: ['Psychic Noise', 'Bug Buzz', 'Focus Blast'],
signatureMove: 'Tail Glow',
evs: { spa: 252, spd: 4, spe: 252 }, nature: 'Timid', teraType: 'Poison',
},
Orchidauntless: {
species: 'Orchidauntless', ability: 'Dewdrop', item: 'Choice Band', gender: '',
moves: ['Zen Headbutt', ['Play Rough', 'Sacred Sword'], 'Flip Turn'],
signatureMove: 'Horn Leech',
evs: { atk: 252, def: 4, spe: 252 }, nature: 'Jolly', teraType: 'Ghost',
},
Pestifer: {
species: 'Pestifer', ability: 'Contagious', item: 'Flame Orb', gender: '',
moves: ['Nasty Plot', 'Sludge Wave', ['Thunderbolt', 'Flamethrower', 'Rapid Spin']],
signatureMove: 'Sandsear Storm',
evs: { spa: 252, spd: 4, spe: 252 }, nature: 'Timid', teraType: 'Ghost',
},
Pomegrenade: {
species: 'Pomegrenade', ability: 'Mind Bloom', item: 'Heavy-Duty Boots', gender: '',
moves: ['Moonblast', 'Synthesis', ['Calm Mind', 'Aromatherapy']],
signatureMove: 'Lava Plume',
evs: { hp: 252, spa: 4, spe: 252 }, nature: 'Timid', teraType: 'Ghost',
},
Raintoad: {
species: 'Raintoad', ability: 'Color Wheel', item: 'Leftovers', gender: '',
moves: ['Body Slam', ['U-turn', 'Zen Headbutt'], 'Toxic'],
signatureMove: 'Protect',
evs: { hp: 252, atk: 4, spe: 252 }, nature: 'Jolly', teraType: 'Ghost',
},
Remnant: {
species: 'Remnant', ability: 'Night March', item: 'Leftovers', gender: '',
moves: ['Circle Throw', 'Toxic', 'Protect'],
signatureMove: 'Poltergeist',
evs: { hp: 252, def: 128, spd: 128 }, nature: 'Impish', teraType: 'Ground',
},
Rizzquaza: {
species: 'Rizzquaza', ability: 'Iron Fistening', item: 'Mystic Water', gender: '',
moves: ['Dragon Dance', 'Ice Spinner', 'Outrage'],
signatureMove: 'Fishious Rend',
evs: { hp: 4, atk: 252, spe: 252 }, nature: 'Jolly', teraType: 'Ghost',
},
Roddammit: {
species: 'Roddammit', ability: 'Outclass', item: 'Leftovers', gender: '',
moves: [['Wicked Blow', 'Knock Off'], ['Gunk Shot', 'Synthesis', 'Spikes'], 'Chilly Reception'],
signatureMove: 'Flower Trick',
evs: { hp: 252, atk: 252, spd: 4 }, nature: 'Adamant', teraType: 'Fire',
},
Roolette: {
species: 'Roolette', ability: 'Spin the Wheel', item: 'Leftovers', gender: '',
moves: ['Close Combat', 'Knock Off', ['Rapid Spin', 'Protect', 'Triple Axel']],
signatureMove: 'Double-Edge',
evs: { atk: 252, def: 4, spe: 252 }, nature: 'Jolly', teraType: 'Fire',
},
Rootfraction: {
species: 'Rootfraction', ability: 'Refraction', item: 'Black Sludge', gender: '',
moves: ['Giga Drain', 'Synthesis', ['Flamethrower', 'U-turn']],
signatureMove: 'Malignant Chain',
evs: { hp: 252, spa: 252, spe: 4 }, nature: 'Modest', teraType: 'Dark',
},
Shail: {
species: 'Shail', ability: 'Snowhazard', item: 'White Herb', gender: '',
moves: ['Blizzard', 'Earth Power', 'Power Gem'],
signatureMove: 'Shell Smash',
evs: { spa: 252, spd: 4, spe: 252 }, nature: 'Timid', teraType: ['Dark', 'Poison', 'Ghost', 'Steel'],
},
Shufflux: {
species: 'Shufflux', ability: 'Draw Four', item: ['Choice Specs', 'Choice Scarf'], gender: '',
moves: ['Trick', 'Dazzling Gleam', 'Extrasensory'],
signatureMove: 'Tri Attack',
evs: { spa: 252, spd: 4, spe: 252 }, nature: 'Timid', teraType: 'Any',
},
Shurifluri: {
species: 'Shurifluri', ability: 'Snowhazard', item: 'Never-Melt Ice', gender: '',
moves: ['Ice Spinner', 'Ice Shard', 'Iron Head'],
signatureMove: 'Bulk Up',
evs: { atk: 252, def: 4, spe: 252 }, nature: 'Jolly', teraType: 'Fire',
},
Skibidragon: {
species: 'Skibidragon', ability: 'Bathroom Break', item: 'Choice Specs', gender: '',
moves: ['Hydro Pump', 'Searing Shot', ['Defog', 'Thunderbolt', 'Sludge Wave']],
signatureMove: 'Draco Meteor',
evs: { spa: 252, spd: 4, spe: 252 }, nature: 'Timid', teraType: 'Dark',
},
Spreetah: {
species: 'Spreetah', ability: 'Momentum', item: 'Metronome', gender: '',
moves: ['Swords Dance', 'Flare Blitz', 'Volt Tackle'],
signatureMove: 'Bonemerang',
evs: { atk: 252, spd: 4, spe: 252 }, nature: 'Jolly', teraType: 'Ghost',
},
Suragon: {
species: 'Suragon', ability: 'Antimatter', item: 'Leftovers', gender: '',
moves: ['Draco Meteor', ['Knock Off', 'Thunder Wave'], 'Psychic Noise'],
signatureMove: 'Topsy-Turvy',
evs: { hp: 252, def: 252, spa: 4 }, nature: 'Bold', teraType: 'Bug',
},
Surfsurge: {
species: 'Surfsurge', ability: 'Strong Breeze', item: 'Heavy-Duty Boots', gender: '',
moves: ['Volt Switch', ['Thunderbolt', 'Rapid Spin'], ['Ice Beam', 'Thunder Wave']],
signatureMove: 'Surf',
evs: { spa: 252, spd: 4, spe: 252 }, nature: 'Timid', teraType: 'Fairy',
},
Tardeblade: {
species: 'Tardeblade', ability: 'Hibernation', item: 'Leftovers', gender: '',
moves: ['Body Press', 'Rest', 'Sleep Talk'],
signatureMove: 'Stored Power',
evs: { hp: 252, def: 128, spd: 128 }, nature: 'Bold', teraType: 'Water',
},
Trawlutre: {
species: 'Trawlutre', ability: 'Super Rod', item: 'Leftovers', gender: '',
moves: ['Close Combat', 'Knock Off', ['U-turn', 'Bulk Up', 'Aqua Jet']],
signatureMove: 'Liquidation',
evs: { atk: 252, def: 4, spe: 252 }, nature: 'Jolly', teraType: 'Flying',
},
Tuxquito: {
species: 'Tuxquito', ability: 'Bloodsucking', item: 'Heavy-Duty Boots', gender: '',
moves: ['Hurricane', 'Fire Blast', 'Bug Buzz'],
signatureMove: 'Quiver Dance',
evs: { def: 4, spa: 252, spe: 252 }, nature: 'Modest', teraType: 'Normal',
},
Underhazard: {
species: 'Underhazard', ability: 'Countermeasures', item: 'Black Sludge', gender: '',
moves: ['Recover', 'Dark Pulse', 'Sludge Bomb'],
signatureMove: 'Toxic Spikes',
evs: { hp: 252, def: 4, spd: 252 }, nature: 'Careful', teraType: 'Water',
},
};
export class RandomCPMTeams extends RandomTeams {
randomCPMTeam(options: { inBattle?: boolean } = {}) {
this.enforceNoDirectCustomBanlistChanges();
const team: PokemonSet[] = [];
const debug: string[] = []; // Set this to a list of CPM sets to override the normal pool for debugging.
const ruleTable = this.dex.formats.getRuleTable(this.format);
const monotype = this.forceMonotype || (ruleTable.has('sametypeclause') ?
this.sample(this.dex.types.names().filter(x => x !== 'Stellar')) : false);
let pool = Object.keys(cpmSets);
if (debug.length) {
while (debug.length < 6) {
const fakemon = this.sampleNoReplace(pool);
if (debug.includes(fakemon) || cpmSets[fakemon].skip) continue;
debug.push(fakemon);
}
pool = debug;
}
if (monotype && !debug.length) {
pool = pool.filter(x => this.dex.species.get(cpmSets[x].species).types.includes(monotype));
}
if (global.Config?.disabledssbsets?.length) {
pool = pool.filter(x => !global.Config.disabledssbsets.includes(this.dex.toID(x)));
}
const typePool: { [k: string]: number } = {};
let depth = 0;
while (pool.length && team.length < this.maxTeamSize) {
if (depth >= 200) throw new Error(`Infinite loop in CPM team generation.`);
depth++;
const name = this.sampleNoReplace(pool);
const cpmSet: CPMSet = this.dex.deepClone(cpmSets[name]);
if (cpmSet.skip) continue;
// Enforce typing limits
if (!(debug.length || monotype)) { // Type limits are ignored for debugging and monotype
const species = this.dex.species.get(cpmSet.species);
const weaknesses = [];
for (const type of this.dex.types.names()) {
const typeMod = this.dex.getEffectiveness(type, species.types);
if (typeMod > 0) weaknesses.push(type);
}
let rejected = false;
for (const type of weaknesses) {
if (typePool[type] === undefined) typePool[type] = 0;
if (typePool[type] >= 3) {
// Reject
rejected = true;
break;
}
}
if (cpmSet.ability === 'Wonder Guard') {
if (!typePool['wonderguard']) {
typePool['wonderguard'] = 1;
} else {
rejected = true;
}
}
if (rejected) continue;
// Update type counts
for (const type of weaknesses) {
typePool[type]++;
}
}
let teraType: string | undefined;
if (cpmSet.teraType) {
teraType = cpmSet.teraType === 'Any' ?
this.sample(this.dex.types.names()) :
this.sampleIfArray(cpmSet.teraType);
}
const moves: string[] = [];
while (moves.length < 3 && cpmSet.moves.length > 0) {
let move = this.sampleNoReplace(cpmSet.moves);
if (Array.isArray(move)) move = this.sampleNoReplace(move);
moves.push(this.dex.moves.get(move).name);
}
moves.push(this.dex.moves.get(cpmSet.signatureMove).name);
const ivs = { hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31, ...cpmSet.ivs };
if (!moves.map(x => this.dex.moves.get(x)).some(x => x.category === 'Physical')) {
ivs.atk = 0;
}
const set: PokemonSet = {
name,
species: cpmSet.species,
item: this.sampleIfArray(cpmSet.item),
ability: this.sampleIfArray(cpmSet.ability),
moves,
nature: cpmSet.nature ? Array.isArray(cpmSet.nature) ? this.sampleNoReplace(cpmSet.nature) : cpmSet.nature : 'Serious',
gender: cpmSet.gender ? this.sampleIfArray(cpmSet.gender) : this.sample(['M', 'F', 'N']),
evs: cpmSet.evs ? { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0, ...cpmSet.evs } :
{ hp: 84, atk: 84, def: 84, spa: 84, spd: 84, spe: 84 },
ivs,
level: this.adjustLevel || cpmSet.level || 100,
happiness: typeof cpmSet.happiness === 'number' ? cpmSet.happiness : 255,
shiny: typeof cpmSet.shiny === 'number' ? this.randomChance(1, cpmSet.shiny) : !!cpmSet.shiny,
};
if (teraType) set.teraType = teraType;
team.push(set);
if (team.length === this.maxTeamSize && (set.ability === 'Illusion')) {
team[this.maxTeamSize - 1] = team[this.maxTeamSize - 2];
team[this.maxTeamSize - 2] = set;
}
}
return team;
}
}
export default RandomCPMTeams;

View File

@ -1,64 +0,0 @@
import type Dex from "../../../sim/dex";
export const Scripts: ModdedBattleScriptsData = {
gen: 9,
actions: {
secondaries(targets: SpreadMoveTargets, source: Pokemon, move: ActiveMove, moveData: ActiveMove, isSelf?: boolean) {
if (!moveData.secondaries) return;
for (const foe of targets) {
if (foe === false) continue;
const secondaries: Dex.SecondaryEffect[] =
this.battle.runEvent('ModifySecondaries', foe, source, moveData, moveData.secondaries.slice());
for (const secondary of secondaries) {
const secondaryRoll = this.battle.random(100);
// User stat boosts or foe stat drops can possibly overflow if it goes beyond 256 in Gen 8 or prior
const secondaryOverflow = (secondary.boosts || secondary.self) && this.battle.gen <= 8;
if (typeof secondary.chance === 'undefined' ||
secondaryRoll < (secondaryOverflow ? secondary.chance % 256 : secondary.chance)) {
let flag = true;
if (moveData.secondary?.status && foe) flag = foe.setStatus(moveData.secondary.status, source);
if (moveData.secondary?.volatileStatus && foe) flag = !(moveData.secondary.volatileStatus in foe.volatiles);
if (moveData.secondary?.volatileStatus === 'flinch' && foe) flag = flag && foe.activeTurns >= 1 && !foe.moveThisTurn;
this.moveHit(foe, source, move, secondary, true, isSelf);
if (moveData.secondary?.self?.boosts) {
Object.entries(moveData.secondary.self.boosts).forEach(([stat, boost]) => {
if (source.boosts[stat as BoostID] === 6) flag = false;
});
} else {
if (foe) flag = flag && !(foe.hp === undefined || foe.hp <= 0);
}
if (moveData.target !== 'self' && moveData.secondary?.boosts && foe) {
const cantLower = {
'atk': ['clearbody', 'fullmetalbody', 'hypercutter', 'whitesmoke'],
'def': ['bigpecks', 'clearbody', 'fullmetalbody', 'whitesmoke'],
'spa': ['clearbody', 'fullmetalbody', 'whitesmoke'],
'spd': ['clearbody', 'fullmetalbody', 'whitesmoke'],
'spe': ['clearbody', 'fullmetalbody', 'whitesmoke'],
'accuracy': ['clearbody', 'fullmetalbody', 'keeneye', 'whitesmoke'],
'evasion': [] };
for (const k in moveData.secondary.boosts) {
if (foe.boosts[k as BoostID] === -6) {
flag = false;
continue;
}
if (foe.hasAbility(cantLower[k as BoostID]) && !move.ignoreAbility) {
flag = false;
break;
}
}
}
if (source.hasAbility('sheerforce')) flag = false;
if (foe && foe.hasAbility('shielddust') && !move.ignoreAbility &&
move.secondary && !move.secondary.self?.boosts) {
flag = false;
}
if (flag && foe && foe.hasAbility('countermeasures') && secondary.chance) {
this.battle.add('-activate', foe, 'ability: Countermeasures');
this.battle.damage(source.baseMaxhp * (100 - secondary.chance) / 100, source, foe);
}
}
}
}
},
},
};

View File

@ -0,0 +1,956 @@
export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTable = {
thickfat: {
// prevents burning
inherit: true,
onUpdate(pokemon) {
if (pokemon.status === 'brn') {
this.add('-activate', pokemon, 'ability: Thick Fat');
pokemon.cureStatus();
}
},
onSetStatus(status, target, source, effect) {
if (status.id !== 'brn') return;
if ((effect as Move)?.status) {
this.add('-immune', target, '[from] ability: Thick Fat');
}
return false;
},
shortDesc: "-50% damage from Fire and Ice. Burn immune.",
},
callillumise: {
onDamagePriority: -30,
onDamage(damage, target, source, effect) {
if (damage >= target.hp) {
this.add('-ability', target, 'Call Illumise');
this.effectState.callillumise = true;
return target.hp - 1;
}
},
onUpdate(pokemon) {
if (!this.effectState.callillumise) return;
this.add('-message', `Volbeat calls upon Illumise for aid!`);
// Define new moves
const newMoves = ['bugbuzz', 'icebeam', 'thunderbolt', 'quiverdance'];
// Update move slots
pokemon.moveSlots = newMoves.map(move => {
const moveData = this.dex.moves.get(move);
return {
move: moveData.name,
id: moveData.id,
pp: moveData.pp,
maxpp: moveData.pp,
target: moveData.target,
disabled: false,
used: false,
};
});
// this forces the UI to update move slots visually
(pokemon as any).baseMoveSlots = pokemon.moveSlots.slice();
// removes status/boosts
pokemon.cureStatus();
pokemon.clearBoosts();
// forces the UI to update part II
this.add('-clearboost', pokemon, '[from] ability: Call Illumise', '[silent]');
for (const volatile in pokemon.volatiles) {
this.add('-end', pokemon, volatile);
}
pokemon.clearVolatile(true);
// form change + heal
pokemon.formeChange('Illumise', null, true);
this.heal(pokemon.maxhp);
// sets new ability
pokemon.setAbility('Tinted Lens', null, null, true);
pokemon.baseAbility = pokemon.ability;
this.add('-ability', pokemon, 'Tinted Lens');
},
flags: {
breakable: 1, failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1,
},
name: "Call Illumise",
rating: 5,
num: -100,
shortDesc: "When Volbeat gets low on HP, it calls Illumise for aid.",
},
callvolbeat: {
onDamagePriority: -30,
onDamage(damage, target, source, effect) {
if (damage >= target.hp) {
this.add('-ability', target, 'Call Volbeat');
this.effectState.callvolbeat = true;
return target.hp - 1;
}
},
onUpdate(pokemon) {
if (!this.effectState.callvolbeat) return;
this.add('-message', `Illumise calls upon Volbeat for aid!`);
// Define new moves
const newMoves = ['victorydance', 'lunge', 'mightycleave', 'earthquake'];
// Update move slots
pokemon.moveSlots = newMoves.map(move => {
const moveData = this.dex.moves.get(move);
return {
move: moveData.name,
id: moveData.id,
pp: moveData.pp,
maxpp: moveData.pp,
target: moveData.target,
disabled: false,
used: false,
};
});
// this forces the UI to update move slots visually
(pokemon as any).baseMoveSlots = pokemon.moveSlots.slice();
// removes status/boosts
pokemon.cureStatus();
pokemon.clearBoosts();
// forces the UI to update part II
this.add('-clearboost', pokemon, '[from] ability: Call Volbeat', '[silent]');
for (const volatile in pokemon.volatiles) {
this.add('-end', pokemon, volatile);
}
pokemon.clearVolatile(true);
// form change + heal
pokemon.formeChange('Volbeat', null, true);
this.heal(pokemon.maxhp);
// sets new ability
pokemon.setAbility('Dancer', null, null, true);
pokemon.baseAbility = pokemon.ability;
this.add('-ability', pokemon, 'Dancer');
},
flags: {
breakable: 1, failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1,
},
name: "Call Volbeat",
rating: 5,
num: -101,
shortDesc: "When Illumise gets low on HP, it calls Volbeat for aid.",
},
shortfuse: {
onDamagePriority: -30,
onDamage(damage, target, source, effect) {
if (damage >= target.hp) {
this.add('-ability', target, 'Short Fuse');
this.effectState.shortfuse = true;
return target.hp - 1;
}
},
onUpdate(pokemon) {
if (this.effectState.shortfuse) {
delete this.effectState.shortfuse;
this.actions.useMove('explosion', pokemon);
}
this.checkFainted();
},
flags: {
breakable: 1, failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1,
},
name: "Short Fuse",
rating: 5,
num: -102,
shortDesc: "If KO'd, use Explosion instead.",
},
hydroelectricdam: {
// Copied from the code for Sand Spit
onDamagingHit(damage, target, source, move) {
this.field.setWeather('raindance');
},
flags: {},
name: "Hydroelectric Dam",
rating: 5,
num: -103,
shortDesc: "Starts Rain Dance when hit by an attack.",
},
frozenarmor: {
onTryHit(target, source, move) {
if (move.category !== 'Status') {
this.add('-ability', target, 'Frozen Armor');
// reduces base power of incoming moves by 20 (math.max prevents base power from reducing below 0)
move.basePower = Math.max(move.basePower - 20, 0);
}
},
onSwitchInPriority: -1,
onUpdate(pokemon) {
// checks if Glastrier is below 50% HP, if so transforms into Caly-Ice and sets ability to As One
if (pokemon.species.id !== 'glastrier' || !pokemon.hp) return;
if (pokemon.hp < pokemon.maxhp / 2) {
if (pokemon.species.id !== 'calyrexice' && pokemon.ability === 'frozenarmor') {
pokemon.formeChange('Calyrex-Ice', null, true);
this.add('-message', `Glastrier's Frozen Armor has shattered!`);
// pokemon.setAbility('As One (Glastrier)');
pokemon.baseAbility = pokemon.ability;
// this.add('-ability', pokemon, 'As One');
}
}
},
flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1 },
name: "Frozen Armor",
rating: 5,
num: -105,
shortDesc: "-20 BP on attacks targeting Glastrier, at 50% HP become Calyrex-Ice.",
},
flipflop: {
onDamagingHitOrder: 1,
onTryHit(target, source, move) {
if (move.flags['contact']) {
let flipFlopBoosts = false;
const invertedBoosts: SparseBoostsTable = {};
for (const stat in source.boosts) {
if (source.boosts[stat as BoostID] > 0) {
// checks for boosts on source of move, inverts boosts and adds them to invertedBoosts table
invertedBoosts[stat as BoostID] = -2 * source.boosts[stat as BoostID];
if (!flipFlopBoosts) {
this.add('-ability', target, 'Flip Flop');
flipFlopBoosts = true;
}
}
}
// applies boosts
this.boost(invertedBoosts, source, target);
}
},
flags: {},
name: "Flip Flop",
rating: 5,
num: -104,
shortDesc: "When hit by contact move, invert attackers stat boosts.",
},
grasspelt: {
inherit: true,
onDamagingHit(damage, target, source, move) {
this.field.setTerrain('grassyterrain');
},
shortDesc: "Starts Grassy Terrain on hit. 1.5x Def in Grassy Terrain.",
},
aquaveil: {
onSwitchInPriority: -1,
// fakes the effect of aqua ring volatile lel
onStart(pokemon) {
this.add('-start', pokemon, 'Aqua Ring');
},
// provides effects of Water Bubble because Aqua Ring is modified to provide Water Bubble.
onResidualOrder: 6,
onResidual(pokemon) {
this.heal(pokemon.baseMaxhp / 16);
},
onSourceModifyAtkPriority: 5,
onSourceModifyAtk(atk, attacker, defender, move) {
if (move.type === 'Fire') {
return this.chainModify(0.5);
}
},
onSourceModifySpAPriority: 5,
onSourceModifySpA(atk, attacker, defender, move) {
if (move.type === 'Fire') {
return this.chainModify(0.5);
}
},
onModifyAtk(atk, attacker, defender, move) {
if (move.type === 'Water') {
return this.chainModify(2);
}
},
onModifySpA(atk, attacker, defender, move) {
if (move.type === 'Water') {
return this.chainModify(2);
}
},
// this ability is supposed to just add Aqua Ring (the volatile) to the Pokemon on switch in
flags: { cantsuppress: 1 },
name: "Aqua Veil",
rating: 5,
num: -106,
shortDesc: "Starts Aqua Ring on switch in.",
},
// unaware + water absorb
stillwater: {
onAnyModifyBoost(boosts, pokemon) {
const unawareUser = this.effectState.target;
if (unawareUser === pokemon) return;
if (unawareUser === this.activePokemon && pokemon === this.activeTarget) {
boosts['def'] = 0;
boosts['spd'] = 0;
boosts['evasion'] = 0;
}
if (pokemon === this.activePokemon && unawareUser === this.activeTarget) {
boosts['atk'] = 0;
boosts['def'] = 0;
boosts['spa'] = 0;
boosts['accuracy'] = 0;
}
},
onTryHit(target, source, move) {
if (target !== source && move.type === 'Water') {
if (!this.heal(target.baseMaxhp / 4)) {
this.add('-immune', target, '[from] ability: Still Water');
}
return null;
}
},
flags: { breakable: 1 },
name: "Still Water",
rating: 5,
num: -107,
shortDesc: "Unaware + Water Absorb",
},
kingofthehill: {
// sharpness + mountaineer + prevents hazard immunity
onDamage(damage, target, source, effect) {
if (effect && effect.id === 'stealthrock') {
return false;
}
},
onTryHit(target, source, move) {
if (move.type === 'Rock' && !target.activeTurns) {
this.add('-immune', target, '[from] ability: King of the Hill');
return null;
}
},
// sharpness
onBasePowerPriority: 19,
onBasePower(basePower, attacker, defender, move) {
if (move.flags['slicing']) {
this.debug('Sharpness boost');
return this.chainModify(1.5);
}
},
// starts side condition for foes, side condition interacts with hazard effects
onStart(pokemon) {
this.add('-ability', pokemon, 'King of the Hill');
for (const side of pokemon.side.foeSidesWithConditions()) {
side.addSideCondition('kingofthehill');
}
},
onEnd(pokemon) {
for (const side of pokemon.side.foeSidesWithConditions()) {
if (side.getSideCondition('kingofthehill')) {
side.removeSideCondition('kingofthehill');
}
}
},
condition: {},
flags: { breakable: 1 },
name: "King of the Hill",
rating: 5,
num: -108,
shortDesc: "Mountaineer + Sharpness. Opponent cannot ignore hazard damage.",
},
// stockpile on hit
omnivore: {
onDamagingHitOrder: 1,
onDamagingHit(damage, target, source, move) {
if (!target.hp) return;
this.add('-activate', target, 'ability: Omnivore');
target.addVolatile('stockpile');
},
flags: {},
name: "Omnivore",
rating: 5,
num: -109,
shortDesc: "Gain Stockpile charge when hit by attack.",
},
// disguise clone
pseudowoodo: {
onDamagePriority: 1,
onDamage(damage, target, source, effect) {
if (effect?.effectType === 'Move' && ['sudowoodo'].includes(target.species.id)) {
this.add('-activate', target, 'ability: Pseudowoodo');
this.effectState.rock = true;
return 0;
}
},
onCriticalHit(target, source, move) {
if (!target) return;
if (!['sudowoodo'].includes(target.species.id)) {
return;
}
const hitSub = target.volatiles['substitute'] && !move.flags['bypasssub'] && !(move.infiltrates && this.gen >= 6);
if (hitSub) return;
if (!target.runImmunity(move.type)) return;
return false;
},
onEffectiveness(typeMod, target, type, move) {
if (!target || move.category === 'Status') return;
if (!['sudowoodo'].includes(target.species.id)) {
return;
}
const hitSub = target.volatiles['substitute'] && !move.flags['bypasssub'] && !(move.infiltrates && this.gen >= 6);
if (hitSub) return;
if (!target.runImmunity(move.type)) return;
return 0;
},
onUpdate(pokemon) {
if (['sudowoodo'].includes(pokemon.species.id) && this.effectState.rock) {
const speciesid = 'Sudowoodo-Rock';
pokemon.formeChange(speciesid, this.effect, true);
this.damage(pokemon.baseMaxhp / 8, pokemon, pokemon, this.dex.species.get(speciesid));
}
},
flags: {
failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1,
breakable: 1, notransform: 1,
},
name: "Pseudowoodo",
rating: 5,
num: -110,
shortDesc: "Disguise. Becomes Rock type when it breaks.",
},
magicguard: {
onDamage(damage, target, source, effect) {
// prevents magic guard from blocking hazard damage while King of the Hill is active
if (target.side.getSideCondition('kingofthehill')) {
const hazards = ['stealthrock', 'spikes', 'toxicspikes', 'stickyweb'];
if (effect && hazards.includes(effect.id)) {
return;
}
}
if (effect.effectType !== 'Move') {
if (effect.effectType === 'Ability') this.add('-activate', source, 'ability: ' + effect.name);
return false;
}
},
flags: {},
name: "Magic Guard",
rating: 4,
num: 98,
},
disguise: {
onDamagePriority: 1,
onDamage(damage, target, source, effect) {
if (effect?.effectType === 'Move' && ['mimikyu', 'mimikyutotem'].includes(target.species.id)) {
this.add('-activate', target, 'ability: Disguise');
this.effectState.busted = true;
return 0;
}
},
onCriticalHit(target, source, move) {
if (!target) return;
if (!['mimikyu', 'mimikyutotem'].includes(target.species.id)) {
return;
}
const hitSub = target.volatiles['substitute'] && !move.flags['bypasssub'] && !(move.infiltrates && this.gen >= 6);
if (hitSub) return;
if (!target.runImmunity(move.type)) return;
return false;
},
onEffectiveness(typeMod, target, type, move) {
if (!target || move.category === 'Status') return;
if (!['mimikyu', 'mimikyutotem'].includes(target.species.id)) {
return;
}
const hitSub = target.volatiles['substitute'] && !move.flags['bypasssub'] && !(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.effectState.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.species.get(speciesid));
}
// sets ability to perish body
if (pokemon.species.id === 'mimikyubusted' && pokemon.ability === 'disguise') {
pokemon.setAbility("Perish Body");
pokemon.baseAbility = pokemon.ability;
}
},
// cantsuppress flag removed to allow for ability change
flags: {
failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1,
breakable: 1, notransform: 1,
},
name: "Disguise",
rating: 3.5,
num: 209,
},
gulpmissile: {
inherit: true,
onTryHit(target, source, move) {
// Storm Drain effect while cramorant-gulping
if (target !== source && move.type === 'Water' && target.species.id === 'cramorantgulping') {
if (!this.boost({ spa: 1 })) {
this.add('-immune', target, '[from] ability: Gulp Missile');
}
return null;
}
// Lightning Rod effect while cramorant-gorging
if (target !== source && move.type === 'Electric' && target.species.id === 'cramorantgorging') {
if (!this.boost({ spa: 1 })) {
this.add('-immune', target, '[from] ability: Gulp Missile');
}
return null;
}
return;
},
},
asoneglastrier: {
inherit: true,
// removing these flags allows Frozen Armor to correctly set Caly-Ice ability as As One
flags: {},
},
protean: {
inherit: true,
onPrepareHit(source, target, move) {
if (move.hasBounced || move.flags['futuremove'] || move.sourceEffect === 'snatch' || move.callsMove) return;
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');
}
},
rating: 4.5,
shortDesc: "Gen 8 Protean.",
},
berserk: {
onUpdate(pokemon) {
if (pokemon.species.id !== 'infernape' || !pokemon.hp || pokemon.m.triggeredBerserk) return;
if (pokemon.hp < pokemon.maxhp / 2) {
this.boost({ spa: 1 }, pokemon, pokemon);
pokemon.m.triggeredBerserk = true;
}
},
flags: {},
name: "Berserk",
rating: 2,
num: 201,
},
bloodsoakedcrescent: {
// modifies atk
onStart(pokemon) {
this.add('-ability', pokemon, 'Blood-Soaked Crescent');
},
onModifyAtkPriority: 1,
onModifyAtk(atk, pokemon) {
if (pokemon.volatiles['dynamax']) return;
this.debug('bsc Attack boost');
return this.chainModify(1.5);
},
// ends move lock properly
onAfterMove(pokemon) {
if (pokemon.volatiles['bloodsoakedcrescent']?.duration === 1) {
pokemon.removeVolatile('bloodsoakedcrescent');
this.add('-end', pokemon, 'Blood-Soaked Rage');
}
},
// applies move lock
onAfterMoveSecondarySelf(pokemon, source, move) {
if (move.id === 'dragondance') return;
if (!pokemon.volatiles['bloodsoakedcrescent']) {
this.add('-start', pokemon, 'Blood-Soaked Rage');
}
pokemon.addVolatile('bloodsoakedcrescent');
},
// condition is just lockedmove with some changes
condition: {
// Outrage, Thrash, Petal Dance...
duration: 2,
onResidual(target) {
if (target.status === 'slp') {
// don't lock, and bypass confusion for calming
delete target.volatiles['bloodsoakedcrescent'];
}
this.effectState.trueDuration--;
},
onStart(target, source, effect) {
this.effectState.trueDuration = this.random(2, 4);
this.effectState.move = this.activeMove;
},
onRestart() {
if (this.effectState.trueDuration >= 2) {
this.effectState.duration = 2;
}
},
onEnd(target) {
if (this.effectState.trueDuration > 1) return;
target.addVolatile('confusion');
},
onLockMove(pokemon) {
if (pokemon.volatiles['dynamax']) return;
return this.effectState.move;
},
},
flags: {},
name: "Blood-Soaked Crescent",
rating: 5,
num: -111,
shortDesc: "1.5x Attack, but attacks have the Outrage effect.",
},
powerspot: {
onChargeMove(pokemon, target, move) {
this.debug('power spot - remove charge turn for ' + move.id);
this.attrLastMove('[still]');
this.addMove('-anim', pokemon, move.name, target);
return false; // skip charge turn
},
onAfterMoveSecondarySelf(pokemon, target, move) {
if (pokemon.getVolatile('mustrecharge')) {
pokemon.removeVolatile('mustrecharge');
this.add('-end', pokemon, 'mustrecharge');
}
},
flags: {},
name: "Power Spot",
rating: 5,
num: 249,
shortDesc: "Moves ignore charge/recharge turns.",
},
biogenesis: {
onSwitchInPriority: -1,
onBeforeSwitchIn(pokemon) {
if (pokemon.m.didRandomMoves) return;
const moves = this.dex.moves.all();
const newMoves = [];
while (newMoves.length < 8) {
const newMove = this.sample(moves);
if (newMove.basePower === 1) continue;
if (newMove.isMax === true) continue;
if (newMove.isNonstandard === "Gigantamax") continue;
if (newMoves.map(x => x.id).includes(newMove.id)) continue;
newMoves.push(newMove);
}
// Update move slots
pokemon.moveSlots = newMoves.map(move => {
const moveData = this.dex.moves.get(move);
return {
move: moveData.name,
id: moveData.id,
pp: moveData.pp,
maxpp: moveData.pp,
target: moveData.target,
disabled: false,
used: false,
};
});
// this forces the UI to update move slots visually
(pokemon as any).baseMoveSlots = pokemon.moveSlots.slice();
pokemon.m.didRandomMoves = true;
},
onSwitchIn(pokemon) {
if (!pokemon) return; // Chat command
if (!pokemon.m.hasTypeChanged) {
this.add('-ability', pokemon, 'Biogenesis');
this.add('-anim', pokemon, 'Growth', pokemon);
this.add('-message', `Mew evolves into a new form with its Biogenesis!`);
}
const attackingMoves = pokemon.baseMoveSlots
.map(slot => this.dex.moves.get(slot.id))
.filter(move => move.category !== "Status");
// pick types of first 2 attacking moves (failsafe if there are none)
const types = attackingMoves.length ?
[...new Set(attackingMoves.slice(0, 2).map(move => move.type))] :
pokemon.types;
pokemon.setType(types);
pokemon.baseTypes = pokemon.types;
pokemon.m.hasTypeChanged = true;
this.add('-start', pokemon, 'typechange', (pokemon.illusion || pokemon).getTypes(true).join('/'), '[silent]');
},
flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1,
breakable: 1, notransform: 1, cantsuppress: 1 },
name: "Biogenesis",
rating: 5,
num: -112,
shortDesc: "This Pokemon receives 8 completely random moves at the start of the game.",
},
orichalcumpulse: {
onStart(pokemon) {
pokemon.updateMaxHp();
if (this.field.setWeather('sunnyday')) {
this.add('-activate', pokemon, 'Orichalcum Pulse', '[source]');
} else if (this.field.isWeather('sunnyday')) {
this.add('-activate', pokemon, 'ability: Orichalcum Pulse');
}
},
onModifyAtkPriority: 5,
onModifyAtk(atk, pokemon) {
if (['sunnyday', 'desolateland'].includes(pokemon.effectiveWeather())) {
this.debug('Orichalcum boost');
return this.chainModify([5461, 4096]);
}
},
flags: {},
name: "Orichalcum Pulse",
rating: 4.5,
num: 288,
},
hailmary: {
onStart(pokemon) {
this.add('-activate', pokemon, 'ability: Hail Mary');
},
onModifySpe(spe, pokemon) {
if (this.field.isWeather(['hail', 'snowscape'])) {
this.debug('hail mary spe boost');
return this.chainModify(2);
}
},
onModifyAtkPriority: 5,
onModifyAtk(atk, pokemon) {
if (this.field.isWeather(['hail', 'snowscape'])) {
this.debug('hail mary atk boost');
return this.chainModify(1.5);
}
},
onSourceModifyAccuracyPriority: -1,
onSourceModifyAccuracy(accuracy, target, source, move) {
if (this.field.isWeather(['hail', 'snowscape'])) {
if (move.category === 'Physical' && typeof accuracy === 'number') {
return this.chainModify([3277, 4096]);
}
}
},
flags: {},
name: "Hail Mary",
rating: 5,
num: -113,
shortDesc: "In Snowscape: 2x Speed, 1.5x Attack, 0.8x accuracy.",
},
brainfreeze: {
onModifyCritRatio(critRatio, source, target) {
if (target && (target.status === 'frostbite' || this.field.isWeather('snowscape'))) return 5;
},
flags: {},
name: "Brain Freeze",
rating: 5,
num: -114,
shortDesc: "If Snowscape or target is Frostbitten, attacks auto Crit.",
},
neutralizinggas: {
inherit: true,
onStart(pokemon) {
// this makes Neutralizing Gas properly show as activated in the client when Typhlosion Mega evolves
this.add('-ability', pokemon, 'Neutralizing Gas');
},
},
terawheel: {
onStart(pokemon) {
pokemon.canTerastallize = null;
},
// copied from SSB High Performance Computing
onResidualOrder: 6,
onResidual(source) {
const type = this.sample(this.dex.types.names().filter(i => i !== source.getTypes()[0]));
if (source.setType(type)) {
this.add('-start', source, 'typechange', type, '[from] ability: Tera Wheel');
}
},
flags: {},
name: "Tera Wheel",
rating: 5,
num: -115,
shortDesc: "End of turn: this Pokemon switches to a random type (including Stellar).",
},
download: {
inherit: true,
onUpdate(pokemon) {
if (pokemon.species.name === 'Genesect-Burn' && pokemon.terastallized) {
pokemon.setAbility('Drought', null, null, true);
pokemon.baseAbility = pokemon.ability;
this.add('-ability', pokemon, 'Drought');
}
if (pokemon.species.name === 'Genesect-Chill' && pokemon.terastallized) {
pokemon.setAbility('Snow Warning', null, null, true);
pokemon.baseAbility = pokemon.ability;
this.add('-ability', pokemon, 'Snow Warning');
}
if (pokemon.species.name === 'Genesect-Douse' && pokemon.terastallized) {
pokemon.setAbility('Drizzle', null, null, true);
pokemon.baseAbility = pokemon.ability;
this.add('-ability', pokemon, 'Drizzle');
}
if (pokemon.species.name === 'Genesect-Shock' && pokemon.terastallized) {
pokemon.setAbility('Electric Surge', null, null, true);
pokemon.baseAbility = pokemon.ability;
this.add('-ability', pokemon, 'Electric Surge');
}
},
shortDesc: "Download + Gets weather setting move when Tera.",
},
battlerage: {
onDamagingHit(damage, target, source, effect) {
this.boost({ atk: 1 });
},
flags: {},
name: "Battle Rage",
rating: 5,
num: -116,
shortDesc: "+1 Atk when hit by an attack.",
},
terrainshift: {
onStart(source) {
if (source.hp >= source.maxhp) {
source.setType("Electric");
this.field.setTerrain('electricterrain');
this.add('-start', source, 'typechange', 'Electric', '[silent]');
} else if (source.hp >= (2 * source.maxhp) / 3) {
source.setType("Fairy");
this.field.setTerrain('mistyterrain');
this.add('-start', source, 'typechange', 'Fairy', '[silent]');
} else if (source.hp >= source.maxhp / 3) {
source.setType("Grass");
this.field.setTerrain('grassyterrain');
this.add('-start', source, 'typechange', 'Grass', '[silent]');
} else {
source.setType("Psychic");
this.field.setTerrain('psychicterrain');
this.add('-start', source, 'typechange', 'Psychic', '[silent]');
}
},
flags: {},
name: "Terrain Shift",
rating: 5,
num: -117,
shortDesc: "Sets terrain depending on HP value.",
},
dragonsjaw: {
onBasePower(basePower, attacker, defender, move) {
if (defender.hasType('Dragon') && defender.hasType('Steel')) {
return this.chainModify(1.5);
} else if (defender.hasType('Dragon')) {
return this.chainModify(2.25);
} else if (defender.hasType('Steel')) {
return;
} else return this.chainModify(1.5);
},
onTryHit(target, source, move) {
if (target.hasType('Fairy')) {
return null;
}
},
onModifyMovePriority: -2,
onModifyMove(move) {
if (move.secondaries) {
this.debug('doubling secondary chance');
for (const secondary of move.secondaries) {
if (secondary.chance) secondary.chance *= 2;
}
}
if (move.self?.chance) move.self.chance *= 2;
},
flags: {},
name: "Dragon's Jaw",
rating: 5,
num: -118,
shortDesc: "Serene Grace + Bite attacks are Dragon type.",
},
corrosivesoul: {
onStart(source) {
this.field.setTerrain('corrosivesoul');
},
condition: {
effectType: 'Terrain',
duration: 5,
durationCallback(source, effect) {
if (source?.hasItem('terrainextender')) {
return 8;
}
return 5;
},
onFieldStart(field, source, effect) {
if (effect?.effectType === 'Ability') {
this.add('-fieldstart', 'move: Corrosive Soul', '[from] ability: ' + effect.name, `[of] ${source}`);
} else {
this.add('-fieldstart', 'move: Corrosive Soul');
}
},
onResidualOrder: 5,
onResidualSubOrder: 2,
onResidual(pokemon) {
const move = this.dex.getActiveMove('smog');
move.accuracy = 100;
const target = pokemon.foes()[0];
if (target && !target.fainted) {
this.actions.useMove(move, pokemon, { target });
}
},
onFieldResidualOrder: 27,
onFieldResidualSubOrder: 7,
onFieldEnd() {
this.add('-fieldend', 'move: Corrosive Soul');
},
},
flags: {},
name: "Corrosive Soul",
rating: 5,
num: -119,
shortDesc: "Sets Corrosive Terrian: active Pokemon hit each other with Smog.",
},
oceanicblessing: {
onSwitchInPriority: -2,
onStart(pokemon) {
this.singleEvent('WeatherChange', this.effect, this.effectState, pokemon);
},
onWeatherChange(pokemon) {
if (!pokemon.isActive || pokemon.baseSpecies.baseSpecies !== 'Kyogre' || pokemon.transformed) return;
if (!pokemon.hp) return;
if (['raindance', 'primordialsea'].includes(pokemon.effectiveWeather())) {
if (pokemon.species.id !== 'kyogreprimal') {
pokemon.formeChange('Kyogre-Primal', this.effect, false);
}
} else {
if (pokemon.species.id === 'kyogreprimal') {
pokemon.formeChange('kyogre', this.effect, false);
}
}
},
onAllyModifyAtkPriority: 3,
onAllyModifyAtk(atk, pokemon) {
if (this.effectState.target.baseSpecies.baseSpecies !== 'Kyogre') return;
if (['raindance', 'primordialsea'].includes(pokemon.effectiveWeather())) {
return this.chainModify(1.5);
}
},
onAllyModifySpDPriority: 4,
onAllyModifySpD(spd, pokemon) {
if (this.effectState.target.baseSpecies.baseSpecies !== 'Kyogre') return;
if (['raindance', 'primordialsea'].includes(pokemon.effectiveWeather())) {
return this.chainModify(1.5);
}
},
flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, breakable: 1 },
name: "Oceanic Blessing",
rating: 5,
num: -120,
shortDesc: "Flower Gift but Kyogre",
},
autospin: {
onResidual(pokemon, s, effect) {
const move = this.dex.getActiveMove('metronome');
const target = pokemon.foes()[0];
if (target && !target.fainted && (pokemon.hp >= pokemon.maxhp / 2)) {
this.actions.useMove(move, pokemon, { target, sourceEffect: effect });
} else if (target && !target.fainted && (pokemon.hp <= pokemon.maxhp / 10)) {
this.actions.useMove(move, pokemon, { target, sourceEffect: effect });
this.actions.useMove(move, pokemon, { target, sourceEffect: effect });
this.actions.useMove(move, pokemon, { target, sourceEffect: effect });
} else if (target && !target.fainted) {
this.actions.useMove(move, pokemon, { target, sourceEffect: effect });
this.actions.useMove(move, pokemon, { target, sourceEffect: effect });
}
},
flags: {},
name: "Auto Spin",
rating: 5,
num: -121,
shortDesc: "Use Metronome at end of turn.",
},
corrosion: {
inherit: true,
onModifyMovePriority: -5,
onModifyMove(move) {
if (!move.ignoreImmunity) move.ignoreImmunity = {};
if (move.ignoreImmunity !== true) {
move.ignoreImmunity['Poison'] = true;
}
},
shortDesc: "This Pokemon can poison a Pokemon regardless of its typing and hit them with Poison moves.",
},
};

View File

@ -0,0 +1,20 @@
export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDataTable = {
frostbite: {
name: 'frostbite',
effectType: 'Status',
onStart(target) {
this.add('-start', target, 'Frostbite', '[silent]');
this.add('-message', `${target.species.name} is inflicted with frostbite!`);
},
onSwitchIn(pokemon) {
this.add('-start', pokemon, 'Frostbite', '[silent]');
},
onResidualOrder: 10,
onResidual(pokemon) {
this.damage(pokemon.baseMaxhp / 16);
},
onBasePower(basePower, source, target) {
return basePower / 2;
},
},
};

140
data/mods/chatbats/items.ts Normal file
View File

@ -0,0 +1,140 @@
export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
bigroot: {
inherit: true,
onTryHealPriority: 1,
onTryHeal(damage, target, source, effect) {
const heals = ['drain', 'leechseed', 'ingrain', 'aquaring', 'strengthsap'];
if (heals.includes(effect.id)) {
return this.chainModify([6144, 4096]);
}
},
shortDesc: "Holder gains 1.5x HP from draining, Aqua Ring, Ingrain, Leech Seed, Strength Sap.",
},
masquerainite: {
name: "Masquerainite",
spritenum: 1,
megaStone: "Masquerain-Mega",
megaEvolves: "Masquerain",
itemUser: ["Masquerain"],
onTakeItem(item, source) {
if (item.megaEvolves === source.baseSpecies.baseSpecies) return false;
return true;
},
num: -1,
gen: 9,
desc: "If held by a Masquerain, this item allows it to Mega Evolve in battle.",
},
starfberry: {
name: "Starf Berry",
spritenum: 472,
isBerry: true,
naturalGift: {
basePower: 100,
type: "Psychic",
},
onUpdate(pokemon) {
if (pokemon.hp <= pokemon.maxhp / 2 ||
((pokemon.hp <= pokemon.maxhp / 2 && pokemon.hasAbility('gluttony') && pokemon.abilityState.gluttony))) {
pokemon.eatItem();
}
},
onEat(pokemon) {
const stats: BoostID[] = [];
let stat: BoostID;
for (stat in pokemon.boosts) {
if (stat !== 'accuracy' && stat !== 'evasion' && pokemon.boosts[stat] < 6) {
stats.push(stat);
}
}
if (stats.length) {
const randomStat = this.sample(stats);
const boost: SparseBoostsTable = {};
boost[randomStat] = 2;
this.boost(boost);
}
},
num: 207,
gen: 3,
},
typhlosionite: {
name: "Typhlosionite",
spritenum: 1,
megaStone: "Typhlosion-Mega",
megaEvolves: "Typhlosion",
itemUser: ["Typhlosion"],
onTakeItem(item, source) {
if (item.megaEvolves === source.baseSpecies.baseSpecies) return false;
return true;
},
num: -2,
gen: 9,
desc: "If held by a Typhlosion, this item allows it to Mega Evolve in battle.",
},
tartapple: {
name: "Tart Apple",
spritenum: 712,
isBerry: true,
fling: {
basePower: 30,
},
onBasePowerPriority: 15,
onBasePower(basePower, user, target, move) {
if (
move && (user.baseSpecies.num === 841) &&
(move.type === 'Grass' || move.type === 'Ground')
) {
return this.chainModify([4915, 4096]);
}
},
onUpdate(pokemon) {
if (pokemon.hp <= pokemon.maxhp / 2) {
pokemon.eatItem();
}
},
onTryEatItem(item, pokemon) {
if (!this.runEvent('TryHeal', pokemon, null, this.effect, pokemon.baseMaxhp / 4)) return false;
},
onEat(pokemon) {
this.heal(pokemon.baseMaxhp / 4);
},
itemUser: ["Flapple"],
num: 1117,
gen: 8,
desc: "Grass- and Ground-type moves have 1.2x power. Restores 1/4 max HP when at 1/2 max HP or less.",
},
thickclub: {
name: "Thick Club",
spritenum: 491,
fling: {
basePower: 130,
},
onModifyAtkPriority: 1,
onModifyAtk(atk, pokemon) {
if (pokemon.baseSpecies.baseSpecies === 'Mandibuzz' || pokemon.baseSpecies.baseSpecies === 'Mew') {
return this.chainModify(2);
}
},
itemUser: ["Marowak", "Marowak-Alola", "Marowak-Alola-Totem", "Cubone", "Mandibuzz", "Mew"],
num: 258,
gen: 2,
desc: "Doubles Attack.",
},
focusband: {
name: "Focus Band",
spritenum: 150,
fling: {
basePower: 10,
},
onDamagePriority: -40,
onDamage(damage, target, source, effect) {
const chance = Math.max(Math.floor(target.hp / target.maxhp), 10);
if (this.randomChance(chance, 100) && damage >= target.hp && effect && effect.effectType === 'Move') {
this.add("-activate", target, "item: Focus Band");
return target.hp - 1;
}
},
num: 230,
gen: 2,
desc: "Chance to survive attack equal to percentage of remaining HP, minimum 10%.",
},
};

1720
data/mods/chatbats/moves.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,491 @@
export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable = {
volcarona: {
inherit: true,
abilities: { 0: "Fluffy" },
},
golemalola: {
inherit: true,
},
lurantis: {
inherit: true,
baseStats: { hp: 85, atk: 105, def: 90, spa: 95, spd: 90, spe: 75 },
},
ironcrown: {
inherit: true,
abilities: { 0: "Queenly Majesty", H: "Battle Armor" },
},
mamoswine: {
inherit: true,
},
ceruledge: {
inherit: true,
},
carbink: {
inherit: true,
abilities: { 0: "Magic Bounce" },
},
moltres: {
inherit: true,
abilities: { 0: "Magic Guard" },
},
kommoo: {
inherit: true,
baseStats: { hp: 75, atk: 100, def: 125, spa: 110, spd: 105, spe: 85 },
abilities: { 0: "Punk Rock" },
},
illumise: {
inherit: true,
abilities: { 0: "Call Volbeat" },
},
volbeat: {
inherit: true,
abilities: { 0: "Call Illumise" },
},
abomasnow: {
inherit: true,
},
abomasnowmega: {
inherit: true,
baseStats: { hp: 90, atk: 132, def: 105, spa: 92, spd: 105, spe: 70 },
abilities: { 0: "Slush Rush" },
},
dugtrio: {
inherit: true,
},
altaria: {
inherit: true,
abilities: { 0: "Fluffy" },
},
altariamega: {
inherit: true,
},
tyranitar: {
inherit: true,
abilities: { 0: "Sand Stream", H: "Sharpness" },
},
tyranitarmega: {
inherit: true,
baseStats: { hp: 100, atk: 114, def: 150, spa: 155, spd: 110, spe: 71 },
types: ["Rock", "Dragon"],
},
mimikyu: {
inherit: true,
baseStats: { hp: 65, atk: 110, def: 80, spa: 50, spd: 105, spe: 96 },
},
mimikyubusted: {
inherit: true,
abilities: { 0: "Perish Body" },
baseStats: { hp: 65, atk: 90, def: 80, spa: 50, spd: 105, spe: 116 },
},
mesprit: {
inherit: true,
abilities: { 0: "Liquid Voice" },
types: ["Psychic", "Water"],
},
electrode: {
inherit: true,
abilities: { 0: "Short Fuse" },
types: ["Electric", "Normal"],
},
taurospaldeacombat: {
inherit: true,
abilities: { 0: "Adaptability" },
},
chiyu: {
inherit: true,
abilities: { 0: "Water Absorb" },
baseStats: { hp: 55, atk: 135, def: 80, spa: 80, spd: 120, spe: 100 },
},
wochien: {
inherit: true,
abilities: { 0: "Liquid Ooze" },
types: ["Grass", "Water"],
},
staraptor: {
inherit: true,
types: ["Flying"],
},
archaludon: {
inherit: true,
abilities: { 0: "Hydroelectric Dam", 1: "Stamina" },
},
malamar: {
inherit: true,
abilities: { 0: "Flip Flop" },
baseStats: { hp: 86, atk: 92, def: 88, spa: 88, spd: 75, spe: 73 },
},
empoleon: {
inherit: true,
abilities: { 0: "Sharpness" },
types: ["Water", "Steel", "Flying"],
},
glastrier: {
inherit: true,
abilities: { 0: "Frozen Armor" },
},
calyrexice: {
inherit: true,
baseStats: { hp: 100, atk: 165, def: 130, spa: 85, spd: 110, spe: 90 },
},
regieleki: {
inherit: true,
abilities: { 0: "Galvanize" },
},
lycanrocmidnight: {
inherit: true,
abilities: { 0: "Technician" },
},
lycanroc: {
inherit: true,
abilities: { 0: "Drought" },
},
lycanrocdusk: {
inherit: true,
abilities: { 0: "Strong Jaw" },
},
dodrio: {
inherit: true,
abilities: { 0: "Speed Boost" },
types: ["Flying", "Fighting"],
},
whiscash: {
inherit: true,
abilities: { 0: "Regenerator" },
baseStats: { hp: 110, atk: 78, def: 88, spa: 76, spd: 86, spe: 60 },
},
hippowdon: {
inherit: true,
abilities: { 0: "Earth Eater" },
},
cramorant: {
inherit: true,
baseStats: { hp: 90, atk: 85, def: 75, spa: 85, spd: 95, spe: 85 },
},
cramorantgulping: {
inherit: true,
baseStats: { hp: 90, atk: 85, def: 75, spa: 85, spd: 95, spe: 85 },
abilities: { 0: "Storm Drain" },
},
cramorantgorging: {
inherit: true,
baseStats: { hp: 90, atk: 85, def: 75, spa: 85, spd: 95, spe: 85 },
abilities: { 0: "Lightning Rod" },
},
grafaiai: {
inherit: true,
baseStats: { hp: 83, atk: 95, def: 65, spa: 80, spd: 72, spe: 110 },
},
tatsugiri: {
inherit: true,
abilities: { 0: "Regenerator" },
baseStats: { hp: 78, atk: 50, def: 70, spa: 120, spd: 95, spe: 82 },
},
kyurem: {
inherit: true,
abilities: { 0: "Skill Link" },
},
roaringmoon: {
inherit: true,
abilities: { 0: "Shadow Shield" },
},
milotic: {
inherit: true,
abilities: { 0: "Aqua Veil" },
types: ["Water", "Fairy"],
},
gogoat: {
inherit: true,
types: ["Grass", "Rock"],
},
clodsire: {
inherit: true,
abilities: { 0: "Still Water" },
},
masquerain: {
inherit: true,
abilities: { 0: "Intimidate" },
},
masquerainmega: {
num: -999,
name: "Masquerain-Mega",
baseSpecies: "Masquerain",
forme: "Mega",
types: ["Bug", "Dark"],
genderRatio: { M: 0.5, F: 0.5 },
baseStats: { hp: 70, atk: 60, def: 82, spa: 140, spd: 82, spe: 120 },
abilities: { 0: "Primordial Sea" },
heightm: 0.8,
weightkg: 3.6,
color: "Blue",
eggGroups: ["Water 1", "Bug"],
requiredItem: "Masquerainite",
},
kyuremblack: {
inherit: true,
abilities: { 0: "Teravolt" },
types: ["Dragon", "Ice", "Electric"],
},
ironthorns: {
inherit: true,
abilities: { 0: "Iron Barbs" },
},
dudunsparce: {
inherit: true,
abilities: { 0: "Earth Eater" },
types: ["Normal", "Ground"],
},
dudunsparcethreesegment: {
inherit: true,
abilities: { 0: "Earth Eater" },
types: ["Normal", "Ground"],
},
chienpao: {
inherit: true,
abilities: { 0: "Tablets of Ruin" },
},
pelipper: {
inherit: true,
},
kleavor: {
inherit: true,
abilities: { 0: "King of the Hill" },
baseStats: { hp: 120, atk: 135, def: 95, spa: 45, spd: 75, spe: 85 },
},
araquanid: {
inherit: true,
},
avalugghisui: {
inherit: true,
abilities: { 0: "Multiscale" },
baseStats: { hp: 95, atk: 127, def: 184, spa: 68, spd: 72, spe: 76 },
},
swalot: {
inherit: true,
abilities: { 0: "Omnivore" },
},
zapdosgalar: {
inherit: true,
types: ["Electric", "Fighting"],
},
phione: {
inherit: true,
},
sudowoodo: {
inherit: true,
abilities: { 0: "Pseudowoodo" },
types: ["Grass"],
baseForme: "Grass",
otherFormes: ["Sudowoodo-Rock"],
formeOrder: ["Sudowoodo", "Sudowoodo-Rock"],
},
sudowoodorock: {
num: 185,
name: "Sudowoodo-Rock",
baseSpecies: "Sudowoodo",
forme: "Rock",
types: ["Rock"],
baseStats: { hp: 70, atk: 100, def: 110, spa: 30, spd: 65, spe: 30 },
abilities: { 0: "Pseudowoodo" },
heightm: 1.7,
weightkg: 38,
color: "Brown",
eggGroups: ["Mineral"],
requiredAbility: "Pseudowoodo",
battleOnly: "Sudowoodo",
},
dondozo: {
inherit: true,
},
golurk: {
inherit: true,
},
meowscarada: {
inherit: true,
},
infernape: {
inherit: true,
abilities: { 0: "Berserk" },
},
salamence: {
inherit: true,
abilities: { 0: "Aerilate" },
},
salamencemega: {
num: 373,
name: "Salamence-Mega",
baseSpecies: "Salamence",
forme: "Mega",
types: ["Dragon", "Flying"],
baseStats: { hp: 95, atk: 145, def: 130, spa: 120, spd: 90, spe: 120 },
abilities: { 0: "Blood-Soaked Crescent" },
heightm: 1.8,
weightkg: 112.6,
color: "Blue",
eggGroups: ["Dragon"],
requiredItem: "Salamencite",
},
urshifu: {
inherit: true,
abilities: { 0: "Sniper" },
},
urshifurapidstrike: {
inherit: true,
abilities: { 0: "Sniper" },
},
stonjourner: {
inherit: true,
},
veluza: {
inherit: true,
types: ["Water", "Ghost"],
},
ogerponhearthflame: {
inherit: true,
abilities: { 0: "Intimidate" },
},
dachsbun: {
inherit: true,
},
koraidon: {
inherit: true,
},
mew: {
inherit: true,
abilities: { 0: "Biogenesis" },
},
magneton: {
inherit: true,
},
delibird: {
inherit: true,
abilities: { 0: "Hail Mary" },
baseStats: { hp: 45, atk: 90, def: 45, spa: 65, spd: 45, spe: 136 },
},
articunogalar: {
inherit: true,
abilities: { 0: "Brain Freeze" },
},
vaporeon: {
inherit: true,
abilities: { 0: "Marvel Scale" },
},
jolteon: {
inherit: true,
abilities: { 0: "Quick Feet" },
},
flareon: {
inherit: true,
abilities: { 0: "Guts" },
baseStats: { hp: 65, atk: 130, def: 65, spa: 60, spd: 110, spe: 95 },
},
garganacl: {
inherit: true,
},
swanna: {
inherit: true,
abilities: { 0: "Serene Grace" },
baseStats: { hp: 75, atk: 117, def: 93, spa: 117, spd: 93, spe: 128 },
},
typhlosion: {
inherit: true,
abilities: { 0: "Magic Guard" },
},
typhlosionmega: {
num: -998,
name: "Typhlosion-Mega",
baseSpecies: "Typhlosion",
forme: "Mega",
types: ["Fire", "Water"],
genderRatio: { M: 0.5, F: 0.5 },
baseStats: { hp: 78, atk: 103, def: 98, spa: 140, spd: 115, spe: 100 },
abilities: { 0: "Neutralizing Gas" },
heightm: 1.7,
weightkg: 84.5,
color: "Blue",
eggGroups: ["Field"],
requiredItem: "Typhlosionite",
},
terapagos: {
inherit: true,
},
terapagosterastal: {
inherit: true,
abilities: { 0: "Tera Wheel" },
},
terapagosstellar: {
inherit: true,
types: ["Stellar"],
},
flapple: {
inherit: true,
abilities: { 0: "Ripen" },
types: ["Grass", "Ground"],
},
genesect: {
inherit: true,
abilities: { 0: "Download" },
},
honchkrow: {
inherit: true,
abilities: { 0: "Supreme Overlord" },
baseStats: { hp: 100, atk: 125, def: 52, spa: 125, spd: 52, spe: 71 },
},
primeape: {
inherit: true,
abilities: { 0: "Battle Rage" },
},
rillaboom: {
inherit: true,
abilities: { 0: "Terrain Shift" },
},
mandibuzz: {
inherit: true,
abilities: { 0: "Weak Armor" },
types: ["Dark", "Ground"],
},
feraligatr: {
inherit: true,
},
feraligatrmega: {
num: -988,
name: "Feraligatr-Mega",
baseSpecies: "Feraligatr",
forme: "Mega",
types: ["Dragon"],
genderRatio: { M: 0.875, F: 0.125 },
baseStats: { hp: 85, atk: 145, def: 120, spa: 99, spd: 103, spe: 78 },
abilities: { 0: "Dragon's Jaw" },
heightm: 2.3,
weightkg: 108.8,
color: "Blue",
eggGroups: ["Monster", "Water 1"],
requiredItem: "Feraligite",
gen: 9,
},
salazzle: {
inherit: true,
abilities: { 0: "Corrosive Soul" },
},
kyogre: {
inherit: true,
abilities: { 0: "Oceanic Blessing" },
},
azelf: {
inherit: true,
abilities: { 0: "Auto Spin" },
types: ["Psychic", "Normal"],
},
decidueye: {
inherit: true,
abilities: { 0: "Overgrow", 1: "Sniper" },
},
ogerponcornerstone: {
inherit: true,
abilities: { 0: "Solid Rock" },
types: ["Psychic", "Normal"],
},
glimmora: {
inherit: true,
abilities: { 0: "Corrosion" },
},
};

View File

@ -0,0 +1,298 @@
export const Scripts: ModdedBattleScriptsData = {
gen: 9,
init() {
this.modData('Learnsets', 'lurantis').learnset.icehammer = ['9L1'];
this.modData('Learnsets', 'ironcrown').learnset.kingsshield = ['9L1'];
this.modData('Learnsets', 'ironcrown').learnset.bodypress = ['9L1'];
this.modData('Learnsets', 'carbink').learnset.moonlight = ['9L1'];
this.modData('Learnsets', 'carbink').learnset.voltswitch = ['9L1'];
this.modData('Learnsets', 'carbink').learnset.spikes = ['9L1'];
this.modData('Learnsets', 'moltres').learnset.woodhammer = ['9L1'];
this.modData('Learnsets', 'moltres').learnset.wavecrash = ['9L1'];
this.modData('Learnsets', 'moltres').learnset.defog = ['9L1'];
this.modData('Learnsets', 'kommoo').learnset.aurasphere = ['9L1'];
this.modData('Learnsets', 'illumise').learnset.quiverdance = ['9L1'];
this.modData('Learnsets', 'illumise').learnset.thunderbolt = ['9L1'];
this.modData('Learnsets', 'illumise').learnset.icebeam = ['9L1'];
this.modData('Learnsets', 'volbeat').learnset.victorydance = ['9L1'];
this.modData('Learnsets', 'volbeat').learnset.mightycleave = ['9L1'];
this.modData('Learnsets', 'volbeat').learnset.earthquake = ['9L1'];
this.modData('Learnsets', 'abomasnow').learnset.glaciallance = ['9L1'];
this.modData('Learnsets', 'abomasnow').learnset.appleacid = ['9L1'];
this.modData('Learnsets', 'abomasnow').learnset.partingshot = ['9L1'];
this.modData('Learnsets', 'dugtrio').learnset.mightycleave = ['9L1'];
this.modData('Learnsets', 'dugtrio').learnset.saltcure = ['9L1'];
this.modData('Learnsets', 'dugtrio').learnset.acrobatics = ['9L1'];
this.modData('Learnsets', 'altaria').learnset.beakblast = ['9L1'];
this.modData('Learnsets', 'altaria').learnset.return = ['9L1'];
this.modData('Learnsets', 'altaria').learnset.explosion = ['9L1'];
this.modData('Learnsets', 'tyranitar').learnset.stoneaxe = ['9L1'];
this.modData('Learnsets', 'tyranitar').learnset.ceaselessedge = ['9L1'];
this.modData('Learnsets', 'tyranitar').learnset.kowtowcleave = ['9L1'];
this.modData('Learnsets', 'tyranitar').learnset.pursuit = ['9L1'];
this.modData('Learnsets', 'tyranitar').learnset.switcheroo = ['9L1'];
this.modData('Learnsets', 'tyranitar').learnset.accelerock = ['9L1'];
this.modData('Learnsets', 'tyranitar').learnset.dracometeor = ['9L1'];
this.modData('Learnsets', 'tyranitar').learnset.mysticalpower = ['9L1'];
this.modData('Learnsets', 'tyranitar').learnset.sandsearstorm = ['9L1'];
this.modData('Learnsets', 'mimikyu').learnset.spiritshackle = ['9L1'];
this.modData('Learnsets', 'mimikyu').learnset.uturn = ['9L1'];
this.modData('Learnsets', 'mimikyu').learnset.obstruct = ['9L1'];
this.modData('Learnsets', 'mesprit').learnset.agility = ['9L1'];
this.modData('Learnsets', 'mesprit').learnset.storedpower = ['9L1'];
this.modData('Learnsets', 'mesprit').learnset.torchsong = ['9L1'];
this.modData('Learnsets', 'mesprit').learnset.cosmicpower = ['9L1'];
this.modData('Learnsets', 'mesprit').learnset.aquaring = ['9L1'];
this.modData('Learnsets', 'mesprit').learnset.freezedry = ['9L1'];
this.modData('Learnsets', 'electrode').learnset.encore = ['9L1'];
this.modData('Learnsets', 'electrode').learnset.rapidspin = ['9L1'];
this.modData('Learnsets', 'taurospaldeacombat').learnset.extremespeed = ['9L1'];
this.modData('Learnsets', 'taurospaldeacombat').learnset.uturn = ['9L1'];
this.modData('Learnsets', 'taurospaldeacombat').learnset.knockoff = ['9L1'];
this.modData('Learnsets', 'chiyu').learnset.splash = ['9L1'];
this.modData('Learnsets', 'chiyu').learnset.tripledive = ['9L1'];
this.modData('Learnsets', 'chiyu').learnset.pyroball = ['9L1'];
this.modData('Learnsets', 'chiyu').learnset.knockoff = ['9L1'];
this.modData('Learnsets', 'chiyu').learnset.suckerpunch = ['9L1'];
this.modData('Learnsets', 'wochien').learnset.partingshot = ['9L1'];
this.modData('Learnsets', 'wochien').learnset.strengthsap = ['9L1'];
this.modData('Learnsets', 'wochien').learnset.bouncingbubble = ['9L1'];
this.modData('Learnsets', 'staraptor').learnset.jumpkick = ['9L1'];
this.modData('Learnsets', 'staraptor').learnset.flareblitz = ['9L1'];
this.modData('Learnsets', 'staraptor').learnset.wavecrash = ['9L1'];
this.modData('Learnsets', 'staraptor').learnset.headsmash = ['9L1'];
this.modData('Learnsets', 'archaludon').learnset.scald = ['9L1'];
this.modData('Learnsets', 'archaludon').learnset.hydropump = ['9L1'];
this.modData('Learnsets', 'malamar').learnset.willowisp = ['9L1'];
this.modData('Learnsets', 'malamar').learnset.recover = ['9L1'];
this.modData('Learnsets', 'malamar').learnset.eeriespell = ['9L1'];
this.modData('Learnsets', 'malamar').learnset.sweetkiss = ['9L1'];
this.modData('Learnsets', 'malamar').learnset.spiritbreak = ['9L1'];
this.modData('Learnsets', 'empoleon').learnset.nastyplot = ['9L1'];
this.modData('Learnsets', 'empoleon').learnset.watershuriken = ['9L1'];
this.modData('Learnsets', 'empoleon').learnset.tachyoncutter = ['9L1'];
this.modData('Learnsets', 'empoleon').learnset.secretsword = ['9L1'];
this.modData('Learnsets', 'regieleki').learnset.blazingtorque = ['9L1'];
this.modData('Learnsets', 'regieleki').learnset.soak = ['9L1'];
this.modData('Learnsets', 'lycanrocmidnight').learnset.accelerock = ['9L1'];
this.modData('Learnsets', 'lycanrocmidnight').learnset.bonerush = ['9L1'];
this.modData('Learnsets', 'lycanrocmidnight').learnset.stormthrow = ['9L1'];
this.modData('Learnsets', 'lycanroc').learnset.firelash = ['9L1'];
this.modData('Learnsets', 'lycanroc').learnset.uturn = ['9L1'];
this.modData('Learnsets', 'lycanroc').learnset.spikes = ['9L1'];
this.modData('Learnsets', 'lycanrocdusk').learnset.mountainmaw = ['9L1'];
this.modData('Learnsets', 'lycanrocdusk').learnset.icefang = ['9L1'];
this.modData('Learnsets', 'dodrio').learnset.triplearrows = ['9L1'];
this.modData('Learnsets', 'dodrio').learnset.obstruct = ['9L1'];
this.modData('Learnsets', 'whiscash').learnset.toxic = ['9L1'];
this.modData('Learnsets', 'whiscash').learnset.flipturn = ['9L1'];
this.modData('Learnsets', 'whiscash').learnset.scald = ['9L1'];
this.modData('Learnsets', 'hippowdon').learnset.saltcure = ['9L1'];
this.modData('Learnsets', 'cramorant').learnset.beakblast = ['9L1'];
this.modData('Learnsets', 'grafaiai').learnset.bulkup = ['9L1'];
this.modData('Learnsets', 'grafaiai').learnset.scavenge = ['9L1'];
this.modData('Learnsets', 'grafaiai').learnset.drainpunch = ['9L1'];
this.modData('Learnsets', 'kyurem').learnset.earthquake = ['9L1'];
this.modData('Learnsets', 'roaringmoon').learnset.firelash = ['9L1'];
this.modData('Learnsets', 'roaringmoon').learnset.glaiverush = ['9L1'];
this.modData('Learnsets', 'milotic').learnset.bouncybubble = ['9L1'];
this.modData('Learnsets', 'milotic').learnset.moonblast = ['9L1'];
this.modData('Learnsets', 'gogoat').learnset.headsmash = ['9L1'];
this.modData('Learnsets', 'clodsire').learnset.barbbarrage = ['9L1'];
this.modData('Learnsets', 'masquerain').learnset.roost = ['9L1'];
this.modData('Learnsets', 'masquerain').learnset.darkpulse = ['9L1'];
this.modData('Learnsets', 'kyuremblack').learnset.icehammer = ['9L1'];
this.modData('Learnsets', 'kyuremblack').learnset.dragonhammer = ['9L1'];
this.modData('Learnsets', 'kyuremblack').learnset.roost = ['9L1'];
this.modData('Learnsets', 'kyuremblack').learnset.earthquake = ['9L1'];
this.modData('Learnsets', 'ironthorns').learnset.ironstrike = ['9L1'];
this.modData('Learnsets', 'ironthorns').learnset.knockoff = ['9L1'];
this.modData('Learnsets', 'chienpao').learnset.bulkup = ['9L1'];
this.modData('Learnsets', 'chienpao').learnset.iciclestorm = ['9L1'];
this.modData('Learnsets', 'pelipper').learnset.bleakwindstorm = ['9L1'];
this.modData('Learnsets', 'pelipper').learnset.sandsearstorm = ['9L1'];
this.modData('Learnsets', 'pelipper').learnset.wildboltstorm = ['9L1'];
this.modData('Learnsets', 'pelipper').learnset.springtidestorm = ['9L1'];
this.modData('Learnsets', 'araquanid').learnset.surgingstrikes = ['9L1'];
this.modData('Learnsets', 'araquanid').learnset.flipturn = ['9L1'];
this.modData('Learnsets', 'araquanid').learnset.silktrap = ['9L1'];
this.modData('Learnsets', 'araquanid').learnset.firstimpression = ['9L1'];
this.modData('Learnsets', 'avalugghisui').learnset.mountainmaw = ['9L1'];
this.modData('Learnsets', 'swalot').learnset.earthpower = ['9L1'];
this.modData('Learnsets', 'zapdosgalar').learnset.wildcharge = ['9L1'];
this.modData('Learnsets', 'phione').learnset.workup = ['9L1'];
this.modData('Learnsets', 'phione').learnset.tidalsurge = ['9L1'];
this.modData('Learnsets', 'phione').learnset.geyser = ['9L1'];
this.modData('Learnsets', 'sudowoodo').learnset.bonsaibounce = ['9L1'];
this.modData('Learnsets', 'sudowoodo').learnset.synthesis = ['9L1'];
this.modData('Learnsets', 'dondozo').learnset.hornleech = ['9L1'];
this.modData('Learnsets', 'dondozo').learnset.fishiousrend = ['9L1'];
this.modData('Learnsets', 'dondozo').learnset.recover = ['9L1'];
this.modData('Learnsets', 'dondozo').learnset.flipturn = ['9L1'];
this.modData('Learnsets', 'golurk').learnset.shatteredseal = ['9L1'];
this.modData('Learnsets', 'golurk').learnset.trickroom = ['9L1'];
this.modData('Learnsets', 'golurk').learnset.headlongrush = ['9L1'];
this.modData('Learnsets', 'meowscarada').learnset.encore = ['9L1'];
this.modData('Learnsets', 'meowscarada').learnset.spectralthief = ['9L1'];
this.modData('Learnsets', 'infernape').learnset.alloutassault = ['9L1'];
this.modData('Learnsets', 'infernape').learnset.mindblown = ['9L1'];
this.modData('Learnsets', 'infernape').learnset.bitterblade = ['9L1'];
this.modData('Learnsets', 'salamence').learnset.uturn = ['9L1'];
this.modData('Learnsets', 'salamence').learnset.dragonascent = ['9L1'];
this.modData('Learnsets', 'salamence').learnset.bloodmoon = ['9L1'];
this.modData('Learnsets', 'urshifu').learnset.agility = ['9L1'];
this.modData('Learnsets', 'urshifu').learnset.aquajet = ['9L1'];
this.modData('Learnsets', 'urshifurapidstrike').learnset.agility = ['9L1'];
this.modData('Learnsets', 'urshifurapidstrike').learnset.suckerpunch = ['9L1'];
this.modData('Learnsets', 'stonjourner').learnset.rockwrecker = ['9L1'];
this.modData('Learnsets', 'stonjourner').learnset.meteorassault = ['9L1'];
this.modData('Learnsets', 'stonjourner').learnset.skyattack = ['9L1'];
this.modData('Learnsets', 'stonjourner').learnset.solarblade = ['9L1'];
this.modData('Learnsets', 'veluza').learnset.ragefist = ['9L1'];
this.modData('Learnsets', 'ogerpon').learnset.leafblade = ['9L1'];
this.modData('Learnsets', 'ogerpon').learnset.crabhammer = ['9L1'];
this.modData('Learnsets', 'ogerpon').learnset.stoneedge = ['9L1'];
this.modData('Learnsets', 'dachsbun').learnset.nuzzle = ['9L1'];
this.modData('Learnsets', 'dachsbun').learnset.spiritbreak = ['9L1'];
this.modData('Learnsets', 'dachsbun').learnset.morningsun = ['9L1'];
this.modData('Learnsets', 'magneton').learnset.magnetbomb = ['9L1'];
this.modData('Learnsets', 'delibird').learnset.iciclestorm = ['9L1'];
this.modData('Learnsets', 'hitmontop').learnset.bulletseed = ['9L1'];
this.modData('Learnsets', 'articunogalar').learnset.aeroblast = ['9L1'];
this.modData('Learnsets', 'articunogalar').learnset.oblivionwing = ['9L1'];
this.modData('Learnsets', 'articunogalar').learnset.aurasphere = ['9L1'];
this.modData('Learnsets', 'vaporeon').learnset.voltswitch = ['9L1'];
this.modData('Learnsets', 'vaporeon').learnset.burnout = ['9L1'];
this.modData('Learnsets', 'garganacl').learnset.thunderwave = ['9L1'];
this.modData('Learnsets', 'garganacl').learnset.saltcurse = ['9L1'];
this.modData('Learnsets', 'garganacl').learnset.purify = ['9L1'];
this.modData('Learnsets', 'swanna').learnset.bleakwindstorm = ['9L1'];
this.modData('Learnsets', 'swanna').learnset.steameruption = ['9L1'];
this.modData('Learnsets', 'swanna').learnset.flyby = ['9L1'];
this.modData('Learnsets', 'typhlosion').learnset.heatsink = ['9L1'];
this.modData('Learnsets', 'typhlosion').learnset.steameruption = ['9L1'];
this.modData('Learnsets', 'typhlosion').learnset.matchagotcha = ['9L1'];
this.modData('Learnsets', 'typhlosion').learnset.calmmind = ['9L1'];
this.modData('Learnsets', 'typhlosion').learnset.morningsun = ['9L1'];
this.modData('Learnsets', 'terapagos').learnset.nastyplot = ['9L1'];
this.modData('Learnsets', 'tatsugiri').learnset.switcheroo = ['9L1'];
this.modData('Learnsets', 'tatsugiri').learnset.sashimishuffle = ['9L1'];
this.modData('Learnsets', 'tatsugiri').learnset.twister = ['9L1'];
this.modData('Learnsets', 'tatsugiri').learnset.waterspout = ['9L1'];
this.modData('Learnsets', 'flapple').learnset.earthquake = ['9L1'];
this.modData('Learnsets', 'flapple').learnset.grabapple = ['9L1'];
this.modData('Learnsets', 'flapple').learnset.flareblitz = ['9L1'];
this.modData('Learnsets', 'genesect').learnset.earthquake = ['9L1'];
this.modData('Learnsets', 'genesect').learnset.sunsteelstrike = ['9L1'];
this.modData('Learnsets', 'genesect').learnset.behemothblade = ['9L1'];
this.modData('Learnsets', 'genesect').learnset.makeitrain = ['9L1'];
this.modData('Learnsets', 'genesect').learnset.tachyoncutter = ['9L1'];
this.modData('Learnsets', 'honchkrow').learnset.crowverload = ['9L1'];
this.modData('Learnsets', 'honchkrow').learnset.oblivionwing = ['9L1'];
this.modData('Learnsets', 'honchkrow').learnset.closecombat = ['9L1'];
this.modData('Learnsets', 'primeape').learnset.knockoff = ['9L1'];
this.modData('Learnsets', 'primeape').learnset.ironhead = ['9L1'];
this.modData('Learnsets', 'rillaboom').learnset.naturesfury = ['9L1'];
this.modData('Learnsets', 'rillaboom').learnset.landswrath = ['9L1'];
this.modData('Learnsets', 'mandibuzz').learnset.fling = ['9L1'];
this.modData('Learnsets', 'mandibuzz').learnset.scavenge = ['9L1'];
this.modData('Learnsets', 'mandibuzz').learnset.bonemerang = ['9L1'];
this.modData('Learnsets', 'feraligatr').learnset.firefang = ['9L1'];
this.modData('Learnsets', 'feraligatr').learnset.thunderfang = ['9L1'];
this.modData('Learnsets', 'feraligatr').learnset.poisonfang = ['9L1'];
this.modData('Learnsets', 'salazzle').learnset.magmastorm = ['9L1'];
this.modData('Learnsets', 'salazzle').learnset.malignantchain = ['9L1'];
this.modData('Learnsets', 'salazzle').learnset.psychicnoise = ['9L1'];
this.modData('Learnsets', 'salazzle').learnset.banefulbunker = ['9L1'];
this.modData('Learnsets', 'kyogre').learnset.hurricane = ['9L1'];
this.modData('Learnsets', 'kyogre').learnset.tidalsurge = ['9L1'];
this.modData('Learnsets', 'azelf').learnset.rapidspin = ['9L1'];
this.modData('Learnsets', 'azelf').learnset.lootbox = ['9L1'];
this.modData('Learnsets', 'azelf').learnset.acupressure = ['9L1'];
this.modData('Learnsets', 'decidueye').learnset.sinisterarrows = ['9L1'];
this.modData('Learnsets', 'ogerpon').learnset.sappyseed = ['9L1'];
this.modData('Learnsets', 'ogerpon').learnset.thousandwaves = ['9L1'];
this.modData('Learnsets', 'glimmora').learnset.icebeam = ['9L1'];
this.modData('Learnsets', 'glimmora').learnset.malignantchain = ['9L1'];
},
};

View File

@ -191,7 +191,7 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
duration: 2,
// defender still takes PSN damage, etc
// TODO: research exact mechanics
onBeforeMovePriority: 0,
onBeforeMovePriority: 9,
onBeforeMove(pokemon) {
this.add('cant', pokemon, 'partiallytrapped');
return false;
@ -199,6 +199,9 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
onRestart() {
this.effectState.duration = 2;
},
onAccuracy(accuracy, target, source, move) {
if (source === this.effectState.source) return true;
},
onLockMove() {
// exact move doesn't matter, no move is ever actually used
return 'struggle';
@ -227,34 +230,33 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
this.effectState.move = effect.id;
this.effectState.totalDuration = this.effectState.duration!;
this.effectState.damage = target.lastDamage;
this.effectState.trapTarget = foe;
this.effectState.damage = this.lastDamage;
this.effectState.locked = foe;
foe.addVolatile('partiallytrapped', target, effect);
},
onOverrideAction(pokemon, target, move) {
return this.effectState.move;
},
// attacker still takes PSN damage, etc
onBeforeMovePriority: 0,
onBeforeMove(pokemon, target, move) {
const foe = pokemon.foes()[0];
if (!foe || foe !== this.effectState.trapTarget) {
if (target !== this.effectState.locked) {
pokemon.removeVolatile('partialtrappinglock');
}
},
onAfterMove(pokemon, target, move) {
if (target && target.hp <= 0) {
delete pokemon.volatiles['partialtrappinglock'];
return;
}
const moveName = this.dex.moves.get(this.effectState.move).name;
this.add('move', pokemon, moveName, foe, `[from] ${moveName}`);
this.damage(this.effectState.damage, foe, pokemon, move);
if (this.effectState.duration === 1) {
if (this.effectState.totalDuration !== 5) {
pokemon.addVolatile('fakepartiallytrapped');
foe.addVolatile('fakepartiallytrapped');
pokemon.volatiles['fakepartiallytrapped'].counterpart = target;
target.addVolatile('fakepartiallytrapped');
target.volatiles['fakepartiallytrapped'].counterpart = pokemon;
}
} else {
foe.addVolatile('partiallytrapped', pokemon, move);
target.addVolatile('partiallytrapped', pokemon, move);
}
return false;
},
onLockMove() {
return this.effectState.move;
@ -268,6 +270,13 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
duration: 0,
onBeforeMovePriority: 7,
onStart() {},
onAfterMove(pokemon, target, move) {
if (target && target.hp <= 0) {
delete pokemon.volatiles['mustrecharge'];
return;
}
this.add('-mustrecharge', pokemon);
},
},
lockedmove: {
// Thrash and Petal Dance.
@ -287,6 +296,11 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
this.queue.changeAction(pokemon, { choice: 'move', moveid: move.id });
}
},
onAfterMove(pokemon) {
if (pokemon.volatiles['lockedmove'].time <= 0) {
pokemon.removeVolatile('lockedmove');
}
},
},
twoturnmove: {
// Skull Bash, Solar Beam, ...

View File

@ -51,7 +51,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
pidgeot: {
tier: "ZU",
tier: "PU",
},
rattata: {
tier: "LC",
@ -90,7 +90,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
nidoqueen: {
tier: "PU",
tier: "ZU",
},
nidoranm: {
tier: "LC",
@ -117,7 +117,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
wigglytuff: {
tier: "PU",
tier: "ZU",
},
zubat: {
tier: "LC",
@ -132,7 +132,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
vileplume: {
tier: "PU",
tier: "ZU",
},
paras: {
tier: "LC",
@ -168,7 +168,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
primeape: {
tier: "ZU",
tier: "PU",
},
growlithe: {
tier: "LC",
@ -189,7 +189,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "PU",
},
kadabra: {
tier: "UU",
tier: "NU",
},
alakazam: {
tier: "OU",
@ -201,7 +201,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
machamp: {
tier: "PU",
tier: "ZU",
},
bellsprout: {
tier: "LC",
@ -210,13 +210,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
victreebel: {
tier: "OU",
tier: "NU",
},
tentacool: {
tier: "ZU",
},
tentacruel: {
tier: "UU",
tier: "NU",
},
geodude: {
tier: "LC",
@ -225,19 +225,19 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "PU",
},
golem: {
tier: "UU",
tier: "NU",
},
ponyta: {
tier: "LC",
},
rapidash: {
tier: "PUBL",
tier: "UU",
},
slowpoke: {
tier: "ZU",
},
slowbro: {
tier: "OU",
tier: "UU",
},
magnemite: {
tier: "LC",
@ -258,7 +258,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
dewgong: {
tier: "UU",
tier: "NU",
},
grimer: {
tier: "LC",
@ -294,13 +294,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
kingler: {
tier: "PU",
tier: "ZU",
},
voltorb: {
tier: "LC",
},
electrode: {
tier: "UU",
tier: "NU",
},
exeggcute: {
tier: "PU",
@ -339,7 +339,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
tangela: {
tier: "UU",
tier: "NU",
},
kangaskhan: {
tier: "UU",
@ -366,7 +366,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
scyther: {
tier: "ZU",
tier: "PU",
},
jynx: {
tier: "OU",
@ -375,10 +375,10 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
magmar: {
tier: "ZU",
tier: "PU",
},
pinsir: {
tier: "PU",
tier: "ZU",
},
tauros: {
tier: "OU",
@ -399,7 +399,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
vaporeon: {
tier: "UU",
tier: "NU",
},
jolteon: {
tier: "OU",
@ -414,7 +414,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "ZU",
},
omastar: {
tier: "UU",
tier: "NU",
},
kabuto: {
tier: "LC",
@ -435,7 +435,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
moltres: {
tier: "NU",
tier: "UU",
},
dratini: {
tier: "LC",

View File

@ -129,6 +129,12 @@ export const Scripts: ModdedBattleScriptsData = {
let sourceEffect = options?.sourceEffect;
const target = this.battle.getTarget(pokemon, moveOrMoveName, targetLoc);
let move = this.battle.dex.getActiveMove(moveOrMoveName);
if (move.id !== 'struggle') {
const changedMove = this.battle.runEvent('OverrideAction', pokemon, target, move);
if (changedMove && changedMove !== true) {
move = this.battle.dex.getActiveMove(changedMove);
}
}
// If a faster partial trapping move misses against a user of Hyper Beam during a recharge turn,
// the user of Hyper Beam will automatically use Hyper Beam during that turn.
@ -153,21 +159,13 @@ export const Scripts: ModdedBattleScriptsData = {
return;
}
let ppMove: ID = pokemon.volatiles['twoturnmove']?.ppMove || '';
if (pokemon.getLockedMove()) {
// locked moves don't deduct PP
sourceEffect = move;
} else {
ppMove ||= move.id;
}
const lockedMove = pokemon.getLockedMove();
if (lockedMove) sourceEffect = move;
this.useMove(move, pokemon, { target, sourceEffect });
if (pokemon.volatiles['twoturnmove']) {
// Deduct PP on the second turn, not first
// If called from e.g. Metronome, remember to deduct Metronome PP
pokemon.volatiles['twoturnmove'].ppMove = move.id;
} else if (ppMove) {
// Locked moves don't deduct PP
// Two-turn moves like Sky Attack deduct PP on their second turn.
if (!lockedMove || pokemon.volatiles['twoturnmove']) {
const ppMove = pokemon.volatiles['twoturnmove']?.ppMove || move.id;
pokemon.deductPP(ppMove, null, target);
const moveSlot = pokemon.getMoveData(ppMove);
if (moveSlot && moveSlot.pp < 0) {
@ -175,6 +173,13 @@ export const Scripts: ModdedBattleScriptsData = {
this.battle.hint("In Gen 1, if a pokemon is forced to use a move with 0 PP, the move will underflow to have 63 PP.");
}
}
this.useMove(move, pokemon, { target, sourceEffect });
if (pokemon.volatiles['twoturnmove']) {
pokemon.deductPP(move, -1, target);
pokemon.volatiles['twoturnmove'].ppMove = move.id;
}
},
// This function deals with AfterMoveSelf events.
// This leads with partial trapping moves shenanigans after the move has been used.
@ -211,37 +216,9 @@ export const Scripts: ModdedBattleScriptsData = {
// The move is our 'final' move (a failed Mirror Move, or any move that isn't Metronome or Mirror Move).
pokemon.side.lastMove = move;
if (pokemon.volatiles['lockedmove']?.time <= 0) pokemon.removeVolatile('lockedmove');
// If target fainted
if (target && target.hp <= 0) {
// We remove recharge
if (pokemon.volatiles['mustrecharge']) pokemon.removeVolatile('mustrecharge');
delete pokemon.volatiles['partialtrappinglock'];
} else {
if (pokemon.volatiles['mustrecharge']) this.battle.add('-mustrecharge', pokemon);
if (pokemon.hp) this.battle.runEvent('AfterMoveSelf', pokemon, target, move);
}
// For partial trapping moves, we are saving the target
if (move.volatileStatus === 'partiallytrapped' && target && target.hp > 0) {
// Let's check if the lock exists
if (pokemon.volatiles['partialtrappinglock'] && target.volatiles['partiallytrapped']) {
// Here the partialtrappinglock volatile has been already applied
const sourceVolatile = pokemon.volatiles['partialtrappinglock'];
const targetVolatile = target.volatiles['partiallytrapped'];
if (!sourceVolatile.locked) {
// If it's the first hit, we save the target
sourceVolatile.locked = target;
} else if (target !== pokemon && target !== sourceVolatile.locked) {
// Our target switched out! Re-roll the duration, damage, and accuracy.
const duration = this.battle.sample([2, 2, 2, 3, 3, 3, 4, 5]);
sourceVolatile.duration = duration;
sourceVolatile.locked = target;
// Duration reset thus partially trapped at 2 always.
targetVolatile.duration = 2;
}
} // If we move to here, the move failed and there's no partial trapping lock.
this.battle.runEvent('AfterMove', pokemon, target, move);
if (!target || target.hp > 0) {
this.battle.runEvent('AfterMoveSelf', pokemon, target, move);
}
}
}
@ -281,7 +258,7 @@ export const Scripts: ModdedBattleScriptsData = {
return false;
}
if (sourceEffect) attrs += `|[from]${this.battle.dex.conditions.get(sourceEffect)}`;
if (sourceEffect) attrs += `|[from] ${this.battle.dex.conditions.get(sourceEffect).name}`;
this.battle.addMove('move', pokemon, move.name, `${target}${attrs}`);
if (!this.battle.singleEvent('Try', move, null, pokemon, target, move)) {
@ -371,11 +348,6 @@ export const Scripts: ModdedBattleScriptsData = {
// Now, let's calculate the accuracy.
let accuracy = move.accuracy;
// Partial trapping moves: true accuracy while it lasts
if (move.volatileStatus === 'partiallytrapped' && target === pokemon.volatiles['partialtrappinglock']?.locked) {
accuracy = true;
}
// If a sleep inducing move is used while the user is recharging, the accuracy is true.
if (move.status === 'slp' && target?.volatiles['mustrecharge']) {
accuracy = true;
@ -508,14 +480,6 @@ export const Scripts: ModdedBattleScriptsData = {
if (target) {
hitResult = this.battle.singleEvent('TryHit', moveData, {}, target, pokemon, move);
// Handle here the applying of partial trapping moves to Pokémon with Substitute
if (targetSub && moveData.volatileStatus && moveData.volatileStatus === 'partiallytrapped') {
target.addVolatile(moveData.volatileStatus, pokemon, move);
if (!pokemon.volatiles['partialtrappinglock'] || pokemon.volatiles['partialtrappinglock'].duration! > 1) {
target.volatiles[moveData.volatileStatus].duration = 2;
}
}
if (!hitResult) {
if (hitResult === false) this.battle.add('-fail', target);
return false;
@ -590,7 +554,7 @@ export const Scripts: ModdedBattleScriptsData = {
didSomething = true;
// Check the status of the Pokémon whose turn is not.
// When a move that affects stat levels is used, if the Pokémon whose turn it is not right now is paralyzed or
// burned, the correspoding stat penalties will be applied again to that Pokémon.
// burned, the corresponding stat penalties will be applied again to that Pokémon.
if (pokemon.side.foe.active[0].status) {
// If it's paralysed, quarter its speed.
if (pokemon.side.foe.active[0].status === 'par') {
@ -681,11 +645,6 @@ export const Scripts: ModdedBattleScriptsData = {
this.moveHit(pokemon, pokemon, move, moveData.self, isSecondary, true);
}
// Now we can save the partial trapping damage.
if (pokemon.volatiles['partialtrappinglock']) {
pokemon.volatiles['partialtrappinglock'].damage = this.battle.lastDamage;
}
// Apply move secondaries.
if (moveData.secondaries && target && target.hp > 0) {
for (const secondary of moveData.secondaries) {
@ -757,7 +716,7 @@ export const Scripts: ModdedBattleScriptsData = {
}
// If it's the first hit on a Normal-type partially trap move, it hits Ghosts anyways but damage is 0.
if (move.volatileStatus === 'partiallytrapped' && move.type === 'Normal' && target.hasType('Ghost')) {
if (move.self?.volatileStatus === 'partialtrappinglock' && move.type === 'Normal' && target.hasType('Ghost')) {
return 0;
}

View File

@ -225,7 +225,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
golem: {
tier: "OU",
tier: "UU",
},
ponyta: {
tier: "LC",
@ -270,7 +270,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
cloyster: {
tier: "OU",
tier: "UU",
},
gastly: {
tier: "LC",
@ -279,7 +279,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
gengar: {
tier: "OU",
tier: "UU",
},
onix: {
tier: "UU",
@ -342,7 +342,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
kangaskhan: {
tier: "OU",
tier: "UU",
},
horsea: {
tier: "LC",
@ -369,7 +369,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
jynx: {
tier: "OU",
tier: "UU",
},
electabuzz: {
tier: "UU",
@ -402,7 +402,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
jolteon: {
tier: "OU",
tier: "UU",
},
flareon: {
tier: "UU",

View File

@ -199,7 +199,7 @@ export const Scripts: ModdedBattleScriptsData = {
return false;
}
if (sourceEffect) attrs += `|[from]${this.battle.dex.conditions.get(sourceEffect)}`;
if (sourceEffect) attrs += `|[from] ${this.battle.dex.conditions.get(sourceEffect).name}`;
this.battle.addMove('move', pokemon, move.name, `${target}${attrs}`);
if (!this.battle.singleEvent('Try', move, null, pokemon, target, move)) {

View File

@ -177,6 +177,11 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
onStart(target, source, effect) {
this.effectState.move = effect.id;
},
onAfterMove(pokemon) {
if (this.effectState.duration === 1) {
pokemon.removeVolatile('lockedmove');
}
},
onEnd(target) {
// Confusion begins even if already confused
delete target.volatiles['confusion'];

View File

@ -162,7 +162,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "PU",
},
diglett: {
tier: "ZU",
tier: "LC",
},
dugtrio: {
tier: "NU",
@ -195,7 +195,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "ZUBL",
},
poliwhirl: {
tier: "PU",
tier: "PUBL",
},
poliwrath: {
tier: "NUBL",
@ -252,7 +252,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
slowpoke: {
tier: "ZU",
tier: "LC",
},
slowbro: {
tier: "UU",
@ -534,7 +534,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "ZU",
},
feraligatr: {
tier: "NUBL",
tier: "UU",
},
sentret: {
tier: "LC",
@ -699,7 +699,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "PU",
},
mantine: {
tier: "ZU",
tier: "ZUBL",
},
skarmory: {
tier: "OU",

View File

@ -252,11 +252,11 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
}
},
},
berserkgene: {
berry: {
inherit: true,
isNonstandard: null,
},
berry: {
berserkgene: {
inherit: true,
isNonstandard: null,
},

View File

@ -527,11 +527,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
onMoveFail(target, source, move) {
source.addVolatile('lockedmove');
},
onAfterMove(pokemon) {
if (pokemon.volatiles['lockedmove'] && pokemon.volatiles['lockedmove'].duration === 1) {
pokemon.removeVolatile('lockedmove');
}
},
},
painsplit: {
inherit: true,
@ -557,11 +552,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
onMoveFail(target, source, move) {
source.addVolatile('lockedmove');
},
onAfterMove(pokemon) {
if (pokemon.volatiles['lockedmove'] && pokemon.volatiles['lockedmove'].duration === 1) {
pokemon.removeVolatile('lockedmove');
}
},
},
poisongas: {
inherit: true,
@ -914,11 +904,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
onMoveFail(target, source, move) {
source.addVolatile('lockedmove');
},
onAfterMove(pokemon) {
if (pokemon.volatiles['lockedmove'] && pokemon.volatiles['lockedmove'].duration === 1) {
pokemon.removeVolatile('lockedmove');
}
},
},
toxic: {
inherit: true,

View File

@ -198,9 +198,7 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
const target = pokemon.side.randomFoe();
if (!target || target.fainted) return;
const ability = target.getAbility();
if (pokemon.setAbility(ability)) {
this.add('-ability', pokemon, ability, '[from] ability: Trace', `[of] ${target}`);
}
pokemon.setAbility(ability, target);
},
flags: {},
},

View File

@ -3,7 +3,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
ivysaur: {
tier: "PU",
tier: "ZU",
},
venusaur: {
tier: "UUBL",
@ -21,7 +21,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
wartortle: {
tier: "ZU",
tier: "PU",
},
blastoise: {
tier: "UU",
@ -762,7 +762,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
grovyle: {
tier: "PU",
tier: "ZU",
},
sceptile: {
tier: "UUBL",
@ -879,7 +879,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "RUBL",
},
shedinja: {
tier: "ZUBL",
tier: "PU",
},
whismur: {
tier: "LC",
@ -1056,13 +1056,10 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "ZU",
},
castformsunny: {
tier: "ZU",
},
castformrainy: {
tier: "ZU",
},
castformsnowy: {
tier: "ZU",
},
kecleon: {
tier: "NU",
@ -1092,7 +1089,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
glalie: {
tier: "NU",
tier: "NUBL",
},
spheal: {
tier: "LC",
@ -1104,7 +1101,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
clamperl: {
tier: "ZU",
tier: "PU",
},
huntail: {
tier: "NU",

View File

@ -138,8 +138,8 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
inherit: true,
onModifyMove(move) {
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',
];
'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', 'triplekick', 'twister', 'uproar', 'visegrip', 'vinewhip', 'vitalthrow', 'volttackle', 'watergun', 'waterpulse', 'waterfall', 'weatherball', 'whirlpool', 'wingattack', 'wrap',
]; // Tickle also has the move flag, but can never flinch because King's Rock requires damage to trigger
if (affectedByKingsRock.includes(move.id)) {
if (!move.secondaries) move.secondaries = [];
move.secondaries.push({

View File

@ -154,6 +154,30 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
this.add('-start', target, 'typechange', type);
},
},
conversion2: {
inherit: true,
onHit(target, source) {
if (!target.lastMoveUsed) {
return false;
}
const possibleTypes = [];
const lastMoveUsed = target.lastMoveUsed;
const attackType = lastMoveUsed.id === 'struggle' ? 'Normal' : lastMoveUsed.type;
for (const typeName of this.dex.types.names()) {
const typeCheck = this.dex.types.get(typeName).damageTaken[attackType];
if (typeCheck === 2 || typeCheck === 3) {
possibleTypes.push(typeName);
}
}
if (!possibleTypes.length) {
return false;
}
const randomType = this.sample(possibleTypes);
if (!source.setType(randomType)) return false;
this.add('-start', source, 'typechange', randomType);
},
},
counter: {
inherit: true,
condition: {

View File

@ -162,7 +162,7 @@ export const Scripts: ModdedBattleScriptsData = {
let movename = move.name;
if (move.id === 'hiddenpower') movename = 'Hidden Power';
if (sourceEffect) attrs += `|[from]${this.dex.conditions.get(sourceEffect)}`;
if (sourceEffect) attrs += `|[from] ${this.dex.conditions.get(sourceEffect).name}`;
this.battle.addMove('move', pokemon, movename, `${target}${attrs}`);
if (!target) {

View File

@ -1240,13 +1240,10 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "ZU",
},
castformsunny: {
tier: "ZU",
},
castformrainy: {
tier: "ZU",
},
castformsnowy: {
tier: "ZU",
},
kecleon: {
tier: "NU",

View File

@ -190,7 +190,7 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
inherit: true,
onStart(pokemon) {
const target = pokemon.side.randomFoe();
if (target?.item && !target.itemState.knockedOff) {
if (target?.item) {
this.add('-item', '', target.getItem().name, '[from] ability: Frisk', `[of] ${pokemon}`);
}
},
@ -534,22 +534,10 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
if (bannedAbilities.includes(target.ability)) {
return;
}
if (pokemon.setAbility(ability)) {
this.add('-ability', pokemon, ability, '[from] ability: Trace', `[of] ${target}`);
}
pokemon.setAbility(ability, target);
},
flags: { notrace: 1 },
},
unburden: {
inherit: true,
condition: {
onModifySpe(spe, pokemon) {
if ((!pokemon.item || pokemon.itemState.knockedOff) && !pokemon.ignoringAbility()) {
return this.chainModify(2);
}
},
},
},
vitalspirit: {
inherit: true,
rating: 2.5,
@ -561,7 +549,7 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
this.hint("In Gen 4, Fire Fang is always able to hit through Wonder Guard.", true, target.side);
return;
}
if (target === source || move.category === 'Status' || move.type === '???' || move.id === 'struggle') return;
if (target === source || move.category === 'Status' || move.type === '???') return;
this.debug('Wonder Guard immunity: ' + move.id);
if (target.runEffectiveness(move) <= 0 || !target.runImmunity(move)) {
this.add('-immune', target, '[from] ability: Wonder Guard');
@ -569,4 +557,8 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
}
},
},
rebound: {
inherit: true,
onTryHitSide() {},
},
};

View File

@ -116,6 +116,10 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
onResidualOrder: 10,
onResidualSubOrder: 9,
},
lockedmove: {
inherit: true,
onAfterMove() {},
},
choicelock: {
inherit: true,
onStart(pokemon) {

View File

@ -1137,9 +1137,12 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
castform: {
tier: "NU",
},
castformsunny: {},
castformrainy: {},
castformsnowy: {},
castformsunny: {
},
castformrainy: {
},
castformsnowy: {
},
kecleon: {
tier: "NU",
},

View File

@ -225,6 +225,31 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
this.add('-start', target, 'typechange', type);
},
},
conversion2: {
inherit: true,
onHit(target, source) {
if (!target.lastMoveUsed) {
return false;
}
const possibleTypes = [];
const lastMoveUsed = target.lastMoveUsed;
const attackType = lastMoveUsed.id === 'struggle' ? 'Normal' : lastMoveUsed.type;
for (const typeName of this.dex.types.names()) {
if (source.hasType(typeName)) continue;
const typeCheck = this.dex.types.get(typeName).damageTaken[attackType];
if (typeCheck === 2 || typeCheck === 3) {
possibleTypes.push(typeName);
}
}
if (!possibleTypes.length) {
return false;
}
const randomType = this.sample(possibleTypes);
if (!source.setType(randomType)) return false;
this.add('-start', source, 'typechange', randomType);
},
},
copycat: {
inherit: true,
onHit(pokemon) {
@ -550,8 +575,8 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
this.debug(`BP: ${move.basePower}`);
if (item.isBerry) {
move.onHit = function (foe) {
if (this.singleEvent('Eat', item, null, foe, null, null)) {
this.runEvent('EatItem', foe, null, null, item);
if (this.singleEvent('Eat', item, source.itemState, foe, source, move)) {
this.runEvent('EatItem', foe, source, move, item);
if (item.id === 'leppaberry') foe.staleness = 'external';
}
if (item.onEat) foe.ateBerry = true;
@ -880,11 +905,12 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
knockoff: {
inherit: true,
onAfterHit(target, source, move) {
if (!target.item || target.itemState.knockedOff) return;
if (!target.item) return;
if (target.ability === 'multitype') return;
const item = target.getItem();
if (this.runEvent('TakeItem', target, source, move, item)) {
target.itemState.knockedOff = true;
target.item = '';
target.itemKnockedOff = true;
this.add('-enditem', target, item.name, '[from] move: Knock Off', `[of] ${source}`);
this.hint("In Gens 3-4, Knock Off only makes the target's item unusable; it cannot obtain a new item.", true);
}
@ -1231,7 +1257,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
outrage: {
inherit: true,
pp: 15,
onAfterMove() {},
},
payback: {
inherit: true,
@ -1268,7 +1293,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
inherit: true,
basePower: 90,
pp: 20,
onAfterMove() {},
},
poisongas: {
inherit: true,
@ -1656,10 +1680,10 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
target.removeVolatile('substitute');
target.addVolatile('substitutebroken');
if (target.volatiles['substitutebroken']) target.volatiles['substitutebroken'].move = move.id;
if (move.ohko) this.add('-ohko');
} else {
this.add('-activate', target, 'Substitute', '[damage]');
}
if (move.ohko) this.add('-ohko');
if (move.recoil && damage) {
this.damage(this.actions.calcRecoilDamage(damage, move, source), source, target, 'recoil');
}
@ -1693,6 +1717,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
switcheroo: {
inherit: true,
onTryHit(target, source, move) {
if (target.itemKnockedOff || source.itemKnockedOff) return false;
if (target.hasAbility('multitype') || source.hasAbility('multitype')) return false;
},
},
@ -1781,7 +1806,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
inherit: true,
basePower: 90,
pp: 20,
onAfterMove() {},
},
torment: {
inherit: true,
@ -1810,7 +1834,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
if (pokemon.hasType('Poison')) {
this.add('-sideend', pokemon.side, 'move: Toxic Spikes', `[of] ${pokemon}`);
pokemon.side.removeSideCondition('toxicspikes');
} else if (pokemon.volatiles['substitute'] || pokemon.hasType('Steel')) {
} else if (pokemon.volatiles['substitute'] || pokemon.hasType('Steel') || pokemon.hasAbility('magicguard')) {
// do nothing
} else if (this.effectState.layers >= 2) {
pokemon.trySetStatus('tox', pokemon.side.foe.active[0]);
@ -1827,6 +1851,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
trick: {
inherit: true,
onTryHit(target, source, move) {
if (target.itemKnockedOff || source.itemKnockedOff) return false;
if (target.hasAbility('multitype') || source.hasAbility('multitype')) return false;
},
},

View File

@ -65,6 +65,9 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
},
overcoat: {
inherit: true,
onImmunity(type, pokemon) {
if (type === 'sandstorm' || type === 'hail') return false;
},
onTryHit() {},
flags: {},
rating: 0.5,
@ -88,4 +91,20 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
inherit: true,
onAllyTryHitSide() {},
},
rebound: {
inherit: true,
onAllyTryHitSide(target, source, move) {
if (this.effectState.target.activeTurns) return;
if (target.isAlly(source) || move.hasBounced || !move.flags['reflectable']) {
return;
}
const newMove = this.dex.getActiveMove(move.id);
newMove.hasBounced = true;
newMove.pranksterBoosted = false;
this.actions.useMove(newMove, this.effectState.target, { target: source });
move.hasBounced = true; // only bounce once in free-for-all battles
return null;
},
},
};

View File

@ -1347,7 +1347,8 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
},
castformrainy: {
},
castformsnowy: {},
castformsnowy: {
},
kecleon: {
tier: "PU",
doublesTier: "DUU",

View File

@ -553,6 +553,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
newMove.hasBounced = true;
newMove.pranksterBoosted = false;
this.actions.useMove(newMove, this.effectState.target, { target: source });
move.hasBounced = true; // only bounce once in free-for-all battles
return null;
},
},
@ -598,6 +599,10 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
inherit: true,
type: "Normal",
},
muddywater: {
inherit: true,
basePower: 95,
},
mudsport: {
inherit: true,
pseudoWeather: undefined,
@ -616,10 +621,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
},
},
muddywater: {
inherit: true,
basePower: 95,
},
naturepower: {
inherit: true,
onTryHit() {},
@ -660,6 +661,10 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
inherit: true,
basePower: 70,
},
psychoshift: {
inherit: true,
accuracy: 90,
},
psychup: {
inherit: true,
onHit(target, source) {
@ -670,10 +675,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
this.add('-copyboost', source, target, '[from] move: Psych Up');
},
},
psychoshift: {
inherit: true,
accuracy: 90,
},
psywave: {
inherit: true,
accuracy: 80,

View File

@ -0,0 +1,117 @@
export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTable = {
mummy: {
inherit: true,
onDamagingHit(damage, target, source, move) {
if (target.ability === 'mummy') {
const sourceAbility = source.getAbility();
if (sourceAbility.flags['cantsuppress'] || sourceAbility.id === 'mummy') {
return;
}
if (this.checkMoveMakesContact(move, source, target, !source.isAlly(target))) {
const oldAbility = source.setAbility('mummy', target);
if (oldAbility) {
this.add('-activate', target, 'ability: Mummy', this.dex.abilities.get(oldAbility).name, `[of] ${source}`);
}
}
} else {
const possibleAbilities = [source.ability, ...(source.m.innates || [])]
.filter(val => !this.dex.abilities.get(val).flags['cantsuppress'] && val !== 'mummy');
if (!possibleAbilities.length) return;
if (this.checkMoveMakesContact(move, source, target, !source.isAlly(target))) {
const abil = this.sample(possibleAbilities);
if (abil === source.ability) {
const oldAbility = source.setAbility('mummy', target);
if (oldAbility) {
this.add('-activate', target, 'ability: Mummy', this.dex.abilities.get(oldAbility).name, `[of] ${source}`);
}
} else {
source.removeVolatile('ability:' + abil);
source.addVolatile('ability:mummy', source);
if (abil) {
this.add('-activate', target, 'ability: Mummy', this.dex.abilities.get(abil).name, `[of] ${source}`);
}
}
}
}
},
},
powerofalchemy: {
inherit: true,
onAllyFaint(ally) {
const pokemon = this.effectState.target;
if (!pokemon.hp) return;
const isAbility = pokemon.ability === 'powerofalchemy';
let possibleAbilities = [ally.ability];
if (ally.m.innates) possibleAbilities.push(...ally.m.innates);
const additionalBannedAbilities = [pokemon.ability, ...(pokemon.m.innates || [])];
possibleAbilities = possibleAbilities
.filter(val => !this.dex.abilities.get(val).flags['noreceiver'] && !additionalBannedAbilities.includes(val));
if (!possibleAbilities.length) return;
const ability = this.dex.abilities.get(possibleAbilities[this.random(possibleAbilities.length)]);
this.add('-ability', pokemon, ability, '[from] ability: Power of Alchemy', `[of] ${ally}`);
if (isAbility) {
pokemon.setAbility(ability);
} else {
pokemon.removeVolatile("ability:powerofalchemy");
pokemon.addVolatile(`ability:${ability}`, pokemon);
}
},
},
receiver: {
inherit: true,
onAllyFaint(ally) {
const pokemon = this.effectState.target;
if (!pokemon.hp) return;
const isAbility = pokemon.ability === 'receiver';
let possibleAbilities = [ally.ability];
if (ally.m.innates) possibleAbilities.push(...ally.m.innates);
const additionalBannedAbilities = [pokemon.ability, ...(pokemon.m.innates || [])];
possibleAbilities = possibleAbilities
.filter(val => !this.dex.abilities.get(val).flags['noreceiver'] && !additionalBannedAbilities.includes(val));
if (!possibleAbilities.length) return;
const ability = this.dex.abilities.get(possibleAbilities[this.random(possibleAbilities.length)]);
this.add('-ability', pokemon, ability, '[from] ability: Receiver', `[of] ${ally}`);
if (isAbility) {
pokemon.setAbility(ability);
} else {
pokemon.removeVolatile("ability:receiver");
pokemon.addVolatile(`ability:${ability}`, pokemon);
}
},
},
trace: {
inherit: true,
onUpdate(pokemon) {
if (!this.effectState.seek) return;
const isAbility = pokemon.ability === 'trace';
const possibleTargets: Pokemon[] = [];
for (const target of pokemon.side.foe.active) {
if (target && !target.fainted) {
possibleTargets.push(target);
}
}
while (possibleTargets.length) {
const rand = this.random(possibleTargets.length);
const target = possibleTargets[rand];
let possibleAbilities = [target.ability];
if (target.m.innates) possibleAbilities.push(...target.m.innates);
const additionalBannedAbilities = [pokemon.ability, ...(pokemon.m.innates || [])];
possibleAbilities = possibleAbilities
.filter(val => !this.dex.abilities.get(val).flags['notrace'] && !additionalBannedAbilities.includes(val));
if (!possibleAbilities.length) {
possibleTargets.splice(rand, 1);
continue;
}
const ability = this.dex.abilities.get(this.sample(possibleAbilities));
this.add('-ability', pokemon, ability, '[from] ability: Trace', `[of] ${target}`);
if (isAbility) {
pokemon.setAbility(ability);
} else {
pokemon.removeVolatile("ability:trace");
pokemon.addVolatile(`ability:${ability}`, pokemon);
}
return;
}
},
},
};

View File

@ -0,0 +1,17 @@
export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
gastroacid: {
inherit: true,
condition: {
// Ability suppression implemented in Pokemon.ignoringAbility() within sim/pokemon.js
onStart(pokemon) {
this.add('-endability', pokemon);
this.singleEvent('End', pokemon.getAbility(), pokemon.abilityState, pokemon, pokemon, 'gastroacid');
if (pokemon.m.innates) {
for (const innate of pokemon.m.innates) {
pokemon.removeVolatile("ability" + innate);
}
}
},
},
},
};

View File

@ -0,0 +1,215 @@
export const Scripts: ModdedBattleScriptsData = {
inherit: 'gen5',
field: {
suppressingWeather() {
for (const pokemon of this.battle.getAllActive()) {
if (pokemon && !pokemon.fainted && !pokemon.ignoringAbility() &&
(pokemon.getAbility().suppressWeather ||
pokemon.m.innates?.some((k: string) => this.battle.dex.abilities.get(k).suppressWeather))) {
return true;
}
}
return false;
},
},
pokemon: {
ignoringAbility() {
// Check if any active pokemon have the ability Neutralizing Gas
let neutralizinggas = false;
for (const pokemon of this.battle.getAllActive()) {
// can't use hasAbility because it would lead to infinite recursion
if (
(pokemon.ability === ('neutralizinggas' as ID) || pokemon.m.innates?.some((k: string) => k === 'neutralizinggas')) &&
!pokemon.volatiles['gastroacid'] && !pokemon.abilityState.ending
) {
neutralizinggas = true;
break;
}
}
return !!(
(this.battle.gen >= 5 && !this.isActive) ||
((this.volatiles['gastroacid'] ||
(neutralizinggas && (this.ability !== ('neutralizinggas' as ID) ||
this.m.innates?.some((k: string) => k === 'neutralizinggas'))
)) && !this.getAbility().flags['cantsuppress']
)
);
},
hasAbility(ability) {
if (this.ignoringAbility()) return false;
if (Array.isArray(ability)) return ability.some(abil => this.hasAbility(abil));
ability = this.battle.toID(ability);
return this.ability === ability || !!this.volatiles['ability:' + ability];
},
transformInto(pokemon, effect) {
const species = pokemon.species;
if (pokemon.fainted || this.illusion || pokemon.illusion || (pokemon.volatiles['substitute'] && this.battle.gen >= 5) ||
(pokemon.transformed && this.battle.gen >= 2) || (this.transformed && this.battle.gen >= 5) ||
species.name === 'Eternatus-Eternamax') {
return false;
}
if (!this.setSpecies(species, effect, true)) return false;
this.transformed = true;
this.weighthg = pokemon.weighthg;
const types = pokemon.getTypes(true, true);
this.setType(pokemon.volatiles['roost'] ? pokemon.volatiles['roost'].typeWas : types, true);
this.addedType = pokemon.addedType;
this.knownType = this.isAlly(pokemon) && pokemon.knownType;
this.apparentType = pokemon.apparentType;
let statName: StatIDExceptHP;
for (statName in this.storedStats) {
this.storedStats[statName] = pokemon.storedStats[statName];
if (this.modifiedStats) this.modifiedStats[statName] = pokemon.modifiedStats![statName]; // Gen 1: Copy modified stats.
}
this.moveSlots = [];
this.set.ivs = (this.battle.gen >= 5 ? this.set.ivs : pokemon.set.ivs);
this.hpType = (this.battle.gen >= 5 ? this.hpType : pokemon.hpType);
this.hpPower = (this.battle.gen >= 5 ? this.hpPower : pokemon.hpPower);
this.timesAttacked = pokemon.timesAttacked;
for (const moveSlot of pokemon.moveSlots) {
let moveName = moveSlot.move;
if (moveSlot.id === 'hiddenpower') {
moveName = 'Hidden Power ' + this.hpType;
}
this.moveSlots.push({
move: moveName,
id: moveSlot.id,
pp: moveSlot.maxpp === 1 ? 1 : 5,
maxpp: this.battle.gen >= 5 ? (moveSlot.maxpp === 1 ? 1 : 5) : moveSlot.maxpp,
target: moveSlot.target,
disabled: false,
used: false,
virtual: true,
});
}
let boostName: BoostID;
for (boostName in pokemon.boosts) {
this.boosts[boostName] = pokemon.boosts[boostName];
}
if (this.battle.gen >= 6) {
// we need to be sure to remove all the overlapping crit volatiles before trying to add any of them
const volatilesToCopy = ['dragoncheer', 'focusenergy', 'gmaxchistrike', 'laserfocus'];
for (const volatile of volatilesToCopy) this.removeVolatile(volatile);
for (const volatile of volatilesToCopy) {
if (pokemon.volatiles[volatile]) {
this.addVolatile(volatile);
if (volatile === 'gmaxchistrike') this.volatiles[volatile].layers = pokemon.volatiles[volatile].layers;
if (volatile === 'dragoncheer') this.volatiles[volatile].hasDragonType = pokemon.volatiles[volatile].hasDragonType;
}
}
}
if (effect) {
this.battle.add('-transform', this, pokemon, '[from] ' + effect.fullname);
} else {
this.battle.add('-transform', this, pokemon);
}
if (this.terastallized && this.terastallized !== this.apparentType) {
this.battle.add('-start', this, 'typechange', this.terastallized, '[silent]');
this.apparentType = this.terastallized;
}
if (this.battle.gen > 2) {
this.setAbility(pokemon.ability, this, null, true);
if (this.m.innates) {
for (const innate of this.m.innates) {
this.removeVolatile('ability:' + innate);
}
}
if (pokemon.m.innates) {
for (const innate of pokemon.m.innates) {
this.addVolatile('ability:' + innate, this);
}
}
}
// Change formes based on held items (for Transform)
// Only ever relevant in Generation 4 since Generation 3 didn't have item-based forme changes
if (this.battle.gen === 4) {
if (this.species.num === 487) {
// Giratina formes
if (this.species.name === 'Giratina' && this.item === 'griseousorb') {
this.formeChange('Giratina-Origin');
} else if (this.species.name === 'Giratina-Origin' && this.item !== 'griseousorb') {
this.formeChange('Giratina');
}
}
if (this.species.num === 493) {
// Arceus formes
const item = this.getItem();
const targetForme = (item?.onPlate ? 'Arceus-' + item.onPlate : 'Arceus');
if (this.species.name !== targetForme) {
this.formeChange(targetForme);
}
}
}
return true;
},
/**
* Changes this Pokemon's forme to match the given speciesId (or species).
* This function handles all changes to stats, ability, type, species, etc.
* as well as sending all relevant messages sent to the client.
*/
formeChange(speciesId, source, isPermanent, message) {
if (!source) source = this.battle.effect;
const rawSpecies = this.battle.dex.species.get(speciesId);
const species = this.setSpecies(rawSpecies, source);
if (!species) return false;
if (this.battle.gen <= 2) return true;
// The species the opponent sees
const apparentSpecies =
this.illusion ? this.illusion.species.name : species.baseSpecies;
if (isPermanent) {
this.baseSpecies = rawSpecies;
this.details = this.getUpdatedDetails();
this.battle.add('detailschange', this, (this.illusion || this).details);
if (source.effectType === 'Item') {
this.canTerastallize = null; // National Dex behavior
if (source.zMove) {
this.battle.add('-burst', this, apparentSpecies, species.requiredItem);
this.moveThisTurnResult = true; // Ultra Burst counts as an action for Truant
} else if (source.isPrimalOrb) {
if (this.illusion) {
this.ability = '';
this.battle.add('-primal', this.illusion);
} else {
this.battle.add('-primal', this);
}
} else {
this.battle.add('-mega', this, apparentSpecies, species.requiredItem);
this.moveThisTurnResult = true; // Mega Evolution counts as an action for Truant
}
} else if (source.effectType === 'Status') {
// Shaymin-Sky -> Shaymin
this.battle.add('-formechange', this, species.name, message);
}
} else {
if (source.effectType === 'Ability') {
this.battle.add('-formechange', this, species.name, message, `[from] ability: ${source.name}`);
} else {
this.battle.add('-formechange', this, this.illusion ? this.illusion.species.name : species.name, message);
}
}
if (isPermanent && !['disguise', 'iceface', 'ability:disguise', 'ability:iceface'].includes(source.id)) {
if (this.illusion) {
this.ability = ''; // Don't allow Illusion to wear off
}
this.setAbility(species.abilities['0'], null, null, true);
this.baseAbility = this.ability;
}
if (this.terastallized && this.terastallized !== this.apparentType) {
this.battle.add('-start', this, 'typechange', this.terastallized, '[silent]');
this.apparentType = this.terastallized;
}
return true;
},
},
};

View File

@ -148,6 +148,14 @@ export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable
inherit: true,
color: "Gray",
},
burmysandy: {
inherit: true,
color: "Gray",
},
burmytrash: {
inherit: true,
color: "Gray",
},
wormadam: {
inherit: true,
color: "Gray",
@ -164,6 +172,14 @@ export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable
inherit: true,
color: "Pink",
},
shelloseast: {
inherit: true,
color: "Purple",
},
gastrodoneast: {
inherit: true,
color: "Purple",
},
arceus: {
inherit: true,
color: "Gray",
@ -297,6 +313,18 @@ export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable
inherit: true,
color: "Yellow",
},
deerlingsummer: {
inherit: true,
color: "Yellow",
},
deerlingautumn: {
inherit: true,
color: "Yellow",
},
deerlingwinter: {
inherit: true,
color: "Yellow",
},
cubchoo: {
inherit: true,
abilities: { 0: "Snow Cloak", H: "Rattled" },
@ -318,6 +346,82 @@ export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable
inherit: true,
color: "Black",
},
vivillonicysnow: {
inherit: true,
color: "Black",
},
vivillonpolar: {
inherit: true,
color: "Black",
},
vivillontundra: {
inherit: true,
color: "Black",
},
vivilloncontinental: {
inherit: true,
color: "Black",
},
vivillongarden: {
inherit: true,
color: "Black",
},
vivillonelegant: {
inherit: true,
color: "Black",
},
vivillonmodern: {
inherit: true,
color: "Black",
},
vivillonmarine: {
inherit: true,
color: "Black",
},
vivillonarchipelago: {
inherit: true,
color: "Black",
},
vivillonhighplains: {
inherit: true,
color: "Black",
},
vivillonsandstorm: {
inherit: true,
color: "Black",
},
vivillonriver: {
inherit: true,
color: "Black",
},
vivillonmonsoon: {
inherit: true,
color: "Black",
},
vivillonsavanna: {
inherit: true,
color: "Black",
},
vivillonsun: {
inherit: true,
color: "Black",
},
vivillonocean: {
inherit: true,
color: "Black",
},
vivillonjungle: {
inherit: true,
color: "Black",
},
vivillonfancy: {
inherit: true,
color: "Black",
},
vivillonpokeball: {
inherit: true,
color: "Black",
},
meowstic: {
inherit: true,
color: "White",

View File

@ -87,33 +87,18 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
},
slowstart: {
inherit: true,
condition: {
duration: 5,
onResidualOrder: 28,
onResidualSubOrder: 2,
onStart(target) {
this.add('-start', target, 'ability: Slow Start');
},
onModifyAtkPriority: 5,
onModifyAtk(atk, pokemon, target, move) {
// This is because the game checks the move's category in data, rather than what it is currently, unlike e.g. Huge Power
if (this.dex.moves.get(move.id).category === 'Physical') {
return this.chainModify(0.5);
}
},
onModifySpAPriority: 5,
onModifySpA(spa, pokemon, target, move) {
// Ordinary Z-moves like Breakneck Blitz will halve the user's Special Attack as well
if (this.dex.moves.get(move.id).category === 'Physical') {
return this.chainModify(0.5);
}
},
onModifySpe(spe, pokemon) {
onModifyAtk(atk, pokemon, target, move) {
// This is because the game checks the move's category in data, rather than what it is currently, unlike e.g. Huge Power
if (this.effectState.counter && this.dex.moves.get(move.id).category === 'Physical') {
return this.chainModify(0.5);
},
onEnd(target) {
this.add('-end', target, 'Slow Start');
},
}
},
onModifySpAPriority: 5,
onModifySpA(spa, pokemon, target, move) {
// Ordinary Z-moves like Breakneck Blitz will halve the user's Special Attack as well
if (this.effectState.counter && this.dex.moves.get(move.id).category === 'Physical') {
return this.chainModify(0.5);
}
},
},
soundproof: {

View File

@ -207,7 +207,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
vulpixalola: {
tier: "LC",
tier: "NFE",
},
ninetales: {
tier: "RU",

View File

@ -386,7 +386,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
inherit: true,
condition: {
duration: 2,
onSwitchInPriority: 1,
onSwitchIn(target) {
if (!target.fainted) {
target.heal(target.maxhp);
@ -613,7 +612,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
inherit: true,
condition: {
duration: 2,
onSwitchInPriority: 1,
onSwitchIn(target) {
if (!target.fainted) {
target.heal(target.maxhp);
@ -1155,39 +1153,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
inherit: true,
isNonstandard: null,
},
trick: {
inherit: true,
onHit(target, source, move) {
const yourItem = target.takeItem(source);
const myItem = source.takeItem();
if (target.item || source.item || (!yourItem && !myItem)) {
if (yourItem) target.item = yourItem.id;
if (myItem) source.item = myItem.id;
return false;
}
if (
(myItem && !this.singleEvent('TakeItem', myItem, source.itemState, target, source, move, myItem)) ||
(yourItem && !this.singleEvent('TakeItem', yourItem, target.itemState, source, target, move, yourItem))
) {
if (yourItem) target.item = yourItem.id;
if (myItem) source.item = myItem.id;
return false;
}
this.add('-activate', source, 'move: Trick', `[of] ${target}`);
if (myItem) {
target.setItem(myItem);
this.add('-item', target, myItem, '[from] move: Trick');
} else {
this.add('-enditem', target, yourItem, '[silent]', '[from] move: Trick');
}
if (yourItem) {
source.setItem(yourItem);
this.add('-item', source, yourItem, '[from] move: Trick');
} else {
this.add('-enditem', source, myItem, '[silent]', '[from] move: Trick');
}
},
},
trumpcard: {
inherit: true,
isNonstandard: null,

View File

@ -59,6 +59,14 @@ export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable
inherit: true,
eggGroups: ["Bug"],
},
burmysandy: {
inherit: true,
color: "Green",
},
burmytrash: {
inherit: true,
color: "Green",
},
magnezone: {
inherit: true,
evoType: "levelExtra",
@ -83,6 +91,94 @@ export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable
abilities: { 0: "Flash Fire", H: "Flame Body" },
unreleasedHidden: true,
},
deerlingsummer: {
inherit: true,
color: "Pink",
},
deerlingautumn: {
inherit: true,
color: "Pink",
},
deerlingwinter: {
inherit: true,
color: "Pink",
},
vivillonicysnow: {
inherit: true,
color: "White",
},
vivillonpolar: {
inherit: true,
color: "White",
},
vivillontundra: {
inherit: true,
color: "White",
},
vivilloncontinental: {
inherit: true,
color: "White",
},
vivillongarden: {
inherit: true,
color: "White",
},
vivillonelegant: {
inherit: true,
color: "White",
},
vivillonmodern: {
inherit: true,
color: "White",
},
vivillonmarine: {
inherit: true,
color: "White",
},
vivillonarchipelago: {
inherit: true,
color: "White",
},
vivillonhighplains: {
inherit: true,
color: "White",
},
vivillonsandstorm: {
inherit: true,
color: "White",
},
vivillonriver: {
inherit: true,
color: "White",
},
vivillonmonsoon: {
inherit: true,
color: "White",
},
vivillonsavanna: {
inherit: true,
color: "White",
},
vivillonsun: {
inherit: true,
color: "White",
},
vivillonocean: {
inherit: true,
color: "White",
},
vivillonjungle: {
inherit: true,
color: "White",
},
vivillonfancy: {
inherit: true,
color: "White",
},
vivillonpokeball: {
inherit: true,
color: "White",
},
aegislash: {
inherit: true,
baseStats: { hp: 60, atk: 50, def: 150, spa: 50, spd: 150, spe: 60 },

View File

@ -116,7 +116,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
},
pikachustarter: {
isNonstandard: null,
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
raichu: {
@ -138,7 +138,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
doublesTier: "DOU",
},
sandslashalola: {
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
nidoranf: {
@ -186,7 +186,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
wigglytuff: {
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
zubat: {
@ -203,7 +203,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
vileplume: {
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
paras: {
@ -227,7 +227,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
dugtrio: {
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
dugtrioalola: {
@ -245,7 +245,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
doublesTier: "DOU",
},
persianalola: {
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
psyduck: {
@ -266,7 +266,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
arcanine: {
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
poliwag: {
@ -317,7 +317,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
tentacruel: {
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
geodude: {
@ -401,7 +401,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
cloyster: {
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
gastly: {
@ -433,7 +433,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
kingler: {
tier: "RU",
tier: "UU",
doublesTier: "DOU",
},
voltorb: {
@ -492,7 +492,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
doublesTier: "DOU",
},
chansey: {
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
tangela: {
@ -572,7 +572,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
doublesTier: "DOU",
},
lapras: {
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
ditto: {
@ -584,7 +584,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
},
eeveestarter: {
isNonstandard: null,
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
vaporeon: {
@ -607,14 +607,14 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
omastar: {
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
kabuto: {
tier: "LC",
},
kabutops: {
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
aerodactyl: {
@ -638,7 +638,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
doublesTier: "DOU",
},
moltres: {
tier: "UU",
tier: "RU",
doublesTier: "DOU",
},
dratini: {

View File

@ -1,10 +1,10 @@
export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable = {
standard: {
inherit: true,
ruleset: ['Adjust Level = 50', 'Obtainable', 'Team Preview', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'HP Percentage Mod', 'Cancel Mod', 'Sleep Clause Mod'],
ruleset: ['LGPE Normal Rules', 'Obtainable', 'Team Preview', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'HP Percentage Mod', 'Cancel Mod', 'Sleep Clause Mod'],
},
standarddoubles: {
inherit: true,
ruleset: ['Adjust Level = 50', 'Obtainable', 'Team Preview', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'HP Percentage Mod', 'Cancel Mod'],
ruleset: ['LGPE Normal Rules', 'Obtainable', 'Team Preview', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'HP Percentage Mod', 'Cancel Mod'],
},
};

View File

@ -1,8 +1,7 @@
function checkMegaForme(species: Species, forme: string, battle: Battle) {
const baseSpecies = battle.dex.species.get(species.baseSpecies);
const altForme = battle.dex.species.get(`${baseSpecies.name}-${forme}`);
if (
altForme.exists && !battle.ruleTable.isBannedSpecies(altForme) &&
if (altForme.exists && altForme.gen <= 7 && !battle.ruleTable.isBannedSpecies(altForme) &&
!battle.ruleTable.isBanned('pokemontag:mega')
) {
return altForme.name;
@ -40,9 +39,9 @@ export const Scripts: ModdedBattleScriptsData = {
// Limit one mega evolution
for (const ally of pokemon.side.pokemon) {
ally.canMegaEvo = null;
ally.canMegaEvoX = null;
ally.canMegaEvoY = null;
ally.canMegaEvo = false;
ally.canMegaEvoX = false;
ally.canMegaEvoY = false;
}
this.battle.runEvent('AfterMega', pokemon);
@ -50,50 +49,34 @@ export const Scripts: ModdedBattleScriptsData = {
},
runMegaEvoX(pokemon) {
if (!pokemon.canMegaEvoX) return false;
pokemon.canMegaEvoY = null;
pokemon.canMegaEvoY = false;
return this.runMegaEvo(pokemon);
},
runMegaEvoY(pokemon) {
if (!pokemon.canMegaEvoY) return false;
pokemon.canMegaEvoX = null;
pokemon.canMegaEvoX = false;
return this.runMegaEvo(pokemon);
},
},
/**
* Given a table of base stats and a pokemon set, return the actual stats.
*/
spreadModify(baseStats, set) {
const modStats: StatsTable = { hp: 10, atk: 10, def: 10, spa: 10, spd: 10, spe: 10 };
let statName: StatID;
for (statName in modStats) {
const stat = baseStats[statName];
modStats[statName] = Math.floor((Math.floor(2 * stat + set.ivs[statName]) * set.level / 100 + 5));
statModify(baseStats, set, statName) {
const tr = this.trunc;
let stat = baseStats[statName];
if (statName === 'hp') {
return tr(tr(2 * stat + set.ivs[statName] + 100) * set.level / 100 + 10) + set.evs[statName];
}
if ('hp' in baseStats) {
const stat = baseStats['hp'];
modStats['hp'] = Math.floor(Math.floor(2 * stat + set.ivs['hp'] + 100) * set.level / 100 + 10);
}
return this.natureModify(modStats, set);
},
/**
* @param {StatsTable} stats
* @param {PokemonSet} set
* @return {StatsTable}
*/
natureModify(stats, set) {
stat = tr((tr(2 * stat + set.ivs[statName]) * set.level / 100 + 5));
const nature = this.dex.natures.get(set.nature);
if (nature.plus) stats[nature.plus] = Math.floor(stats[nature.plus] * 1.1);
if (nature.minus) stats[nature.minus] = Math.floor(stats[nature.minus] * 0.9);
set.happiness = 70;
const friendshipValue = Math.floor((set.happiness / 255 / 10 + 1) * 100);
let stat: StatID;
for (stat in stats) {
if (stat !== 'hp') {
stats[stat] = Math.floor(stats[stat] * friendshipValue / 100);
}
stats[stat] += set.evs[stat];
if (nature.plus === statName) {
stat = tr(tr(stat * 110, 16) / 100);
} else if (nature.minus === statName) {
stat = tr(tr(stat * 90, 16) / 100);
}
return stats;
set.happiness = 70;
const friendshipValue = tr((set.happiness / 255 / 10 + 1) * 100);
stat = tr(stat * friendshipValue / 100);
return stat;
},
};

View File

@ -113,7 +113,7 @@ export const Scripts: ModdedBattleScriptsData = {
this.apparentType = this.terastallized;
}
if (this.battle.gen > 2) {
this.setAbility(pokemon.ability, this, true);
this.setAbility(pokemon.ability, this, null, true);
if (this.m.innates) {
for (const innate of this.m.innates) {
this.removeVolatile('ability:' + innate);
@ -202,7 +202,7 @@ export const Scripts: ModdedBattleScriptsData = {
if (this.illusion) {
this.ability = ''; // Don't allow Illusion to wear off
}
this.setAbility(species.abilities['0'], null, true);
this.setAbility(species.abilities['0'], null, null, true);
this.baseAbility = this.ability;
}
if (this.terastallized && this.terastallized !== this.apparentType) {

View File

@ -283,9 +283,6 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
this.damage(pokemon.baseMaxhp / 8, pokemon, pokemon, this.dex.species.get(speciesid));
}
},
onFaint(target) {
delete this.effectState.busted;
},
rating: 3.5,
},
download: {

View File

@ -169,7 +169,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
}
// In SwSh, Fly's animation leaks the initial target through a camera focus
// The animation leak target itself isn't "accurate"; the target it reveals is as if Fly weren't a charge movee
// The animation leak target itself isn't "accurate"; the target it reveals is as if Fly weren't a charge move
// (Fly, like all other charge moves, will actually target slots on its charging turn, relevant for things like Follow Me)
// We use a generic single-target move to represent this
if (this.sides.length > 2) {

View File

@ -95,7 +95,7 @@ export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable
if (set.item && this.dex.items.get(set.item).megaStone) {
const item = this.dex.items.get(set.item);
if (item.megaEvolves === species.baseSpecies) {
species = this.dex.species.get(item.megaStone);
species = this.dex.species.get(Array.isArray(item.megaStone) ? item.megaStone[0] : item.megaStone);
}
}
if (
@ -123,7 +123,9 @@ export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable
}
if (set.item) {
const item = this.dex.items.get(set.item);
if (item.megaEvolves === set.species) godSpecies = this.dex.species.get(item.megaStone);
if (item.megaEvolves === set.species) {
godSpecies = this.dex.species.get(Array.isArray(item.megaStone) ? item.megaStone[0] : item.megaStone);
}
if (["Zacian", "Zamazenta"].includes(godSpecies.baseSpecies) && item.id.startsWith('rusted')) {
godSpecies = this.dex.species.get(set.species + "-Crowned");
}

View File

@ -1341,6 +1341,12 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "PU",
doublesTier: "DUU",
},
castformsunny: {
},
castformrainy: {
},
castformsnowy: {
},
kecleon: {
tier: "PU",
doublesTier: "DUU",
@ -1374,7 +1380,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
doublesTier: "DUU",
},
absol: {
tier: "RU",
tier: "RUBL",
doublesTier: "DUU",
},
snorunt: {
@ -1622,7 +1628,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
drifblim: {
tier: "PU",
tier: "PUBL",
doublesTier: "DUU",
},
buneary: {
@ -1654,7 +1660,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
doublesTier: "DOU",
},
chatot: {
tier: "PU",
tier: "PUBL",
doublesTier: "DUU",
},
spiritomb: {

View File

@ -102,18 +102,6 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
ejectbutton: {
inherit: true,
isNonstandard: "Unobtainable",
onAfterMoveSecondary(target, source, move) {
if (source && source !== target && target.hp && move && move.category !== 'Status' && !move.flags['futuremove']) {
if (!this.canSwitch(target.side) || target.forceSwitchFlag || target.beingCalledBack || target.isSkyDropped()) return;
for (const pokemon of this.getAllActive()) {
if (pokemon.switchFlag === true) return;
}
// TODO: Confirm mechanics
this.add("-activate", target, "item: Eject Button");
target.switchFlag = true;
source.switchFlag = false;
}
},
},
ejectpack: {
inherit: true,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable = {
pikachu: {
inherit: true,
evos: ["Raichu"],
},
quilava: {
inherit: true,
evos: ["Typhlosion-Hisui"],
},
cherrimsunshine: {
inherit: true,
baseStats: { hp: 70, atk: 90, def: 70, spa: 87, spd: 117, spe: 85 },
},
mimejr: {
inherit: true,
evos: ["Mr. Mime"],
},
dewott: {
inherit: true,
evos: ["Samurott-Hisui"],
},
petilil: {
inherit: true,
evos: ["Lilligant-Hisui"],
},
rufflet: {
inherit: true,
evos: ["Braviary-Hisui"],
},
goomy: {
inherit: true,
evos: ["Sliggoo-Hisui"],
},
bergmite: {
inherit: true,
evos: ["Avalugg-Hisui"],
},
dartrix: {
inherit: true,
evos: ["Decidueye-Hisui"],
},
};

View File

@ -0,0 +1,4 @@
export const Scripts: ModdedBattleScriptsData = {
gen: 8,
inherit: 'gen8',
};

View File

@ -83,7 +83,7 @@ export const Scripts: ModdedBattleScriptsData = {
if (!species) continue;
pokemon.baseSpecies = rawSpecies;
pokemon.details = pokemon.getUpdatedDetails();
// pokemon.setAbility(species.abilities['0'], null, true);
// pokemon.setAbility(species.abilities['0'], null, null, true);
// pokemon.baseAbility = pokemon.ability;
const behemothMove: { [k: string]: string } = {

View File

@ -35,13 +35,6 @@ export const Scripts: ModdedBattleScriptsData = {
}
if (pokemon.species.name === 'Terapagos-Terastal') {
pokemon.formeChange('Terapagos-Stellar', null, 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);
const newMaxHP = pokemon.baseMaxhp;
pokemon.hp = newMaxHP - (pokemon.maxhp - pokemon.hp);
pokemon.maxhp = newMaxHP;
this.battle.add('-heal', pokemon, pokemon.getHealth, '[silent]');
}
if (pokemon.species.baseSpecies === 'Morpeko' && !pokemon.transformed &&
pokemon.baseSpecies.id !== pokemon.species.id
@ -135,7 +128,7 @@ export const Scripts: ModdedBattleScriptsData = {
this.knownType = true;
this.apparentType = this.terastallized;
}
if (this.battle.gen > 2) this.setAbility(pokemon.ability, this, true, true);
if (this.battle.gen > 2) this.setAbility(pokemon.ability, this, null, true, true);
// Change formes based on held items (for Transform)
// Only ever relevant in Generation 4 since Generation 3 didn't have item-based forme changes

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable = {
starmiemega: {
inherit: true,
baseStats: { hp: 60, atk: 140, def: 105, spa: 130, spd: 105, spe: 120 },
},
mawilemega: {
inherit: true,
baseStats: { hp: 50, atk: 147, def: 125, spa: 55, spd: 95, spe: 50 },
},
medichammega: {
inherit: true,
baseStats: { hp: 60, atk: 140, def: 85, spa: 80, spd: 85, spe: 100 },
},
};

View File

@ -0,0 +1,4 @@
export const Scripts: ModdedBattleScriptsData = {
gen: 9,
inherit: 'gen9',
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
slowbronite: {
inherit: true,
onTakeItem(item, source) {
if (item.megaEvolves === source.baseSpecies.name || item.megaStone === source.baseSpecies.name) return false;
return true;
},
},
greninjite: {
inherit: true,
onTakeItem(item, source) {
if (item.megaEvolves === source.baseSpecies.name || item.megaStone === source.baseSpecies.name) return false;
return true;
},
},
zygardite: {
inherit: true,
onTakeItem(item, source) {
if ((source.baseSpecies.baseSpecies === 'Zygarde' && source.baseAbility === 'powerconstruct') ||
source.baseSpecies.name === 'Zygarde-Mega') return false;
return true;
},
},
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
volttackle: {
inherit: true,
onModifyMove(move, pokemon, target) {
if (pokemon.baseSpecies.name === "Raichu-Mega-X") {
move.self = { boosts: { atk: 1 } };
}
},
},
};

View File

@ -0,0 +1,219 @@
export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable = {
clefablemega: {
inherit: true,
abilities: { 0: "Prankster" },
},
victreebelmega: {
inherit: true,
abilities: { 0: "Triage" },
},
raichumegax: {
inherit: true,
abilities: { 0: "Levitate" },
},
raichumegay: {
inherit: true,
abilities: { 0: "Transistor" },
},
starmiemega: {
inherit: true,
baseStats: { hp: 60, atk: 100, def: 105, spa: 130, spd: 105, spe: 120 },
abilities: { 0: "Pure Power" },
},
dragonitemega: {
inherit: true,
abilities: { 0: "Sheer Force" },
},
meganiummega: {
inherit: true,
abilities: { 0: "Flower Veil" },
},
feraligatrmega: {
inherit: true,
abilities: { 0: "Dragon's Maw" },
},
ampharosmega: {
inherit: true,
abilities: { 0: "Fluffy" },
},
absolmegaz: {
inherit: true,
abilities: { 0: "Technician" },
},
chimechomega: {
inherit: true,
abilities: { 0: "Levitate" },
},
skarmorymega: {
inherit: true,
abilities: { 0: "Tough Claws" },
},
mawilemega: {
inherit: true,
baseStats: { hp: 50, atk: 105, def: 125, spa: 55, spd: 95, spe: 50 },
},
medichammega: {
inherit: true,
baseStats: { hp: 60, atk: 100, def: 85, spa: 80, spd: 85, spe: 100 },
},
staraptormega: {
inherit: true,
abilities: { 0: "Tough Claws" },
},
gallademega: {
inherit: true,
abilities: { 0: "Sharpness" },
},
froslassmega: {
inherit: true,
abilities: { 0: "Snow Warning" },
},
garchompmegaz: {
inherit: true,
abilities: { 0: "Rough Skin" },
},
lucariomegaz: {
inherit: true,
abilities: { 0: "Mind's Eye" },
},
heatranmega: {
inherit: true,
abilities: { 0: "Filter" },
},
darkraimega: {
inherit: true,
abilities: { 0: "Dark Aura" },
},
emboarmega: {
inherit: true,
abilities: { 0: "Supreme Overlord" },
},
excadrillmega: {
inherit: true,
abilities: { 0: "Sand Rush" },
},
golurkmega: {
inherit: true,
abilities: { 0: "Adaptability" },
},
audinomega: {
inherit: true,
abilities: { 0: "Regenerator" },
},
scolipedemega: {
inherit: true,
abilities: { 0: "Tinted Lens" },
},
scraftymega: {
inherit: true,
abilities: { 0: "Shed Skin" },
},
eelektrossmega: {
inherit: true,
abilities: { 0: "Hadron Engine" },
},
chandeluremega: {
inherit: true,
abilities: { 0: "Magic Guard" },
},
chesnaughtmega: {
inherit: true,
abilities: { 0: "Grassy Surge" },
},
delphoxmega: {
inherit: true,
abilities: { 0: "Levitate" },
},
greninjamega: {
inherit: true,
abilities: { 0: "Protean" },
},
meowsticmmega: {
inherit: true,
abilities: { 0: "Psychic Surge" },
},
meowsticfmega: {
inherit: true,
abilities: { 0: "Psychic Surge" },
},
pyroarmega: {
inherit: true,
abilities: { 0: "Drought" },
},
dragalgemega: {
inherit: true,
abilities: { 0: "Regenerator" },
},
floettemega: {
inherit: true,
abilities: { 0: "Regenerator" },
},
malamarmega: {
inherit: true,
abilities: { 0: "Contrary" },
},
barbaraclemega: {
inherit: true,
abilities: { 0: "Tough Claws" },
},
hawluchamega: {
inherit: true,
abilities: { 0: "Stamina" },
},
zygardemega: {
inherit: true,
abilities: { 0: "Aura Break" },
},
crabominablemega: {
inherit: true,
abilities: { 0: "Ice Scales" },
},
golisopodmega: {
inherit: true,
abilities: { 0: "Heatproof" },
},
drampamega: {
inherit: true,
abilities: { 0: "Adaptability" },
},
magearnamega: {
inherit: true,
abilities: { 0: "Soul-Heart" },
},
magearnaoriginalmega: {
inherit: true,
abilities: { 0: "Soul-Heart" },
},
zeraoramega: {
inherit: true,
abilities: { 0: "Volt Absorb" },
},
falinksmega: {
inherit: true,
abilities: { 0: "Dauntless Shield" },
},
scovillainmega: {
inherit: true,
abilities: { 0: "Contrary" },
},
glimmoramega: {
inherit: true,
abilities: { 0: "Levitate" },
},
tatsugiricurlymega: {
inherit: true,
abilities: { 0: "Drizzle" },
},
tatsugiridroopymega: {
inherit: true,
abilities: { 0: "Drizzle" },
},
tatsugiristretchymega: {
inherit: true,
abilities: { 0: "Drizzle" },
},
baxcaliburmega: {
inherit: true,
abilities: { 0: "Thermal Exchange" },
},
};

View File

@ -0,0 +1,114 @@
export const Scripts: ModdedBattleScriptsData = {
gen: 9,
inherit: 'gen9legends',
init() {
const legalItems = [
'assaultvest', 'bigroot', 'blackbelt', 'blackglasses', 'charcoal', 'dragonfang', 'eviolite', 'expertbelt', 'fairyfeather',
'focusband', 'focussash', 'hardstone', 'kingsrock', 'leftovers', 'lifeorb', 'lightball', 'magnet', 'metalcoat', 'miracleseed',
'muscleband', 'mysticwater', 'nevermeltice', 'normalgem', 'poisonbarb', 'poweranklet', 'powerband', 'powerbelt', 'powerbracer',
'powerlens', 'powerweight', 'quickclaw', 'rockyhelmet', 'scopelens', 'sharpbeak', 'shellbell', 'silkscarf', 'silverpowder',
'softsand', 'spelltag', 'twistedspoon', 'weaknesspolicy', 'whiteherb', 'wiseglasses', 'bottlecap', 'goldbottlecap', 'dawnstone',
'duskstone', 'firestone', 'galaricacuff', 'galaricawreath', 'icestone', 'leafstone', 'moonstone', 'sachet', 'shinystone',
'sunstone', 'thunderstone', 'waterstone', 'whippeddream', 'bignugget', 'redorb', 'blueorb', 'leek', 'thickclub', 'upgrade',
'dubiousdisc', 'prismscale', 'maliciousarmor', 'auspiciousarmor', 'poweranklet', 'powerband', 'powerbelt', 'powerbracer',
'powerlens', 'powerweight', 'bignugget', 'bottlecap', 'goldbottlecap', 'prismscale', 'sachet', 'whippeddream',
];
const legalBerries = [
'aspearberry', 'babiriberry', 'chartiberry', 'cheriberry', 'chestoberry', 'chilanberry', 'chopleberry', 'cobaberry', 'colburberry',
'grepaberry', 'habanberry', 'hondewberry', 'kasibberry', 'kebiaberry', 'kelpsyberry', 'lumberry', 'occaberry', 'oranberry', 'passhoberry',
'payapaberry', 'pechaberry', 'persimberry', 'pomegberry', 'qualotberry', 'rawstberry', 'rindoberry', 'roseliberry', 'shucaberry',
'sitrusberry', 'tamatoberry', 'tangaberry', 'wacanberry', 'yacheberry',
];
const votedLegalitems = [
'heavydutyboots', 'choiceband', 'choicescarf', 'choicespecs', 'airballoon', 'loadeddice', 'mentalherb', 'powerherb', 'mirrorherb',
'aguavberry', 'apicotberry', 'custapberry', 'enigmaberry', 'figyberry', 'ganlonberry', 'iapapaberry', 'jabocaberry', 'keeberry',
'lansatberry', 'leppaberry', 'liechiberry', 'magoberry', 'marangaberry', 'micleberry', 'petayaberry', 'rowapberry', 'salacberry',
'starfberry', 'wikiberry', 'abilityshield', 'blunderpolicy', 'blacksludge', 'lightclay', 'brightpowder', 'adrenalineorb', 'absorbbulb',
'clearamulet', 'covertcloak', 'damprock', 'heatrock', 'icyrock', 'smoothrock', 'electricseed', 'mistyseed', 'psychicseed', 'grassyseed',
'flameorb', 'toxicorb', 'gripclaw', 'laggingtail', 'metronome', 'protectivepads', 'punchingglove', 'razorclaw', 'razorfang', 'roomservice',
'safetygoggles', 'shellbell', 'shedshell', 'stickybarb', 'terrainextender', 'throatspray', 'utilityumbrella', 'zoomlens', 'bindingband',
'destinyknot', 'floatstone', 'ironball', 'machobrace', 'ringtarget', 'redcard', 'ejectpack', 'ejectbutton', 'souldew', 'cellbattery',
'luminousmoss', 'oddincense', 'roseincense', 'seaincense', 'waveincense', 'snowball',
];
for (const i in this.data.Items) {
if (this.data.Items[i].isNonstandard === 'CAP' || this.data.Items[i].isNonstandard === 'Custom') continue;
if ([...legalItems, ...votedLegalitems, ...legalBerries].includes(i) ||
this.data.Items[i].megaStone || this.data.Items[i].onDrive ||
(this.data.Items[i].onPlate && !this.data.Items[i].zMove)) {
this.modData('Items', i).isNonstandard = null;
} else {
this.modData('Items', i).isNonstandard = 'Past';
}
}
for (const i in this.data.Moves) {
if (this.data.Moves[i].isNonstandard !== 'Past') continue;
this.modData('Moves', i).isNonstandard = null;
}
},
actions: {
canMegaEvo(pokemon) {
const species = pokemon.baseSpecies;
const altForme = species.otherFormes && this.dex.species.get(species.otherFormes[0]);
const item = pokemon.getItem();
// Mega Rayquaza
if ((this.battle.gen <= 7 || this.battle.ruleTable.has('+pokemontag:past') ||
this.battle.ruleTable.has('+pokemontag:future')) &&
altForme?.isMega && altForme?.requiredMove &&
pokemon.baseMoves.includes(this.battle.toID(altForme.requiredMove)) && !item.zMove) {
return altForme.name;
}
if (Array.isArray(item.megaEvolves)) {
if (!Array.isArray(item.megaStone)) {
throw new Error(`${item.name}#megaEvolves and ${item.name}#megaStone type mismatch`);
}
if (item.megaEvolves.length !== item.megaStone.length) {
throw new Error(`${item.name}#megaEvolves and ${item.name}#megaStone length mismatch`);
}
const index = item.megaEvolves.indexOf(species.name);
if (index < 0) return null;
return item.megaStone[index];
} else {
if (item.megaEvolves === species.name) {
if (Array.isArray(item.megaStone)) throw new Error(`${item.name}#megaEvolves and ${item.name}#megaStone type mismatch`);
return item.megaStone;
}
}
return null;
},
runMegaEvo(pokemon) {
const speciesid = pokemon.canMegaEvo || pokemon.canUltraBurst;
if (!speciesid) return false;
pokemon.formeChange(speciesid, pokemon.getItem(), true);
// Limit one mega evolution
const wasMega = pokemon.canMegaEvo;
for (const ally of pokemon.side.pokemon) {
if (wasMega) {
ally.canMegaEvo = false;
} else {
ally.canUltraBurst = null;
}
}
// will finish coding this later, not important since zygarde is banned
if (speciesid === 'Zygarde-Mega') {
const coreEnforcer = pokemon.moveSlots.findIndex(x => x.id === 'coreenforcer');
if (coreEnforcer >= 0) {
const nihilLight = this.battle.dex.moves.get('nihillight');
pokemon.moveSlots[coreEnforcer] = pokemon.baseMoveSlots[coreEnforcer] = {
id: nihilLight.id,
move: nihilLight.name,
pp: pokemon.moveSlots[coreEnforcer].pp,
maxpp: pokemon.moveSlots[coreEnforcer].maxpp,
disabled: false,
used: false,
};
}
}
this.battle.runEvent('AfterMega', pokemon);
return true;
},
},
};

View File

@ -499,6 +499,54 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
flags: {},
},
// Cassiopeia
hacking: {
name: "Hacking",
shortDesc: "Hacks into PS and finds out if the enemy has any super effective moves.",
onStart(pokemon) {
const name = (pokemon.illusion || pokemon).name;
this.add(`c:|${getName(name)}|One moment, please. One does not simply go into battle blind.`);
const side = pokemon.side.id === 'p1' ? 'p2' : 'p1';
this.add(
`message`,
(
`ssh sim@pokemonshowdown.com && nc -U logs/repl/sim <<< ` +
`"Users.get('${this.toID(name)}').popup(battle.sides.get('${side}').pokemon.map(m => Teams.exportSet(m)))"`
)
);
let warnMoves: (Move | Pokemon)[][] = [];
let warnBp = 1;
for (const target of pokemon.foes()) {
for (const moveSlot of target.moveSlots) {
const move = this.dex.moves.get(moveSlot.move);
let bp = move.basePower;
if (move.ohko) bp = 150;
if (move.id === 'counter' || move.id === 'metalburst' || move.id === 'mirrorcoat') bp = 120;
if (bp === 1) bp = 80;
if (!bp && move.category !== 'Status') bp = 80;
if (bp > warnBp) {
warnMoves = [[move, target]];
warnBp = bp;
} else if (bp === warnBp) {
warnMoves.push([move, target]);
}
}
}
if (!warnMoves.length) {
this.add(`c:|${getName(name)}|Fascinating. None of your sets have any moves of interest.`);
return;
}
const [warnMoveName, warnTarget] = this.sample(warnMoves);
this.add(
'message',
`${name} hacked into PS and looked at ${name === 'Cassiopeia' ? 'her' : 'their'} opponent's sets. ` +
`${warnTarget.name}'s move ${warnMoveName} drew ${name === 'Cassiopeia' ? 'her' : 'their'} eye.`
);
this.add(`c:|${getName(name)}|Interesting. With that in mind, bring it!`);
},
flags: {},
},
// Chloe
acetosa: {
shortDesc: "This Pokemon's moves are changed to be Grass type and have 1.2x power.",
@ -936,54 +984,6 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
flags: {},
},
// Hecate
hacking: {
name: "Hacking",
shortDesc: "Hacks into PS and finds out if the enemy has any super effective moves.",
onStart(pokemon) {
const name = (pokemon.illusion || pokemon).name;
this.add(`c:|${getName(name)}|One moment, please. One does not simply go into battle blind.`);
const side = pokemon.side.id === 'p1' ? 'p2' : 'p1';
this.add(
`message`,
(
`ssh sim@pokemonshowdown.com && nc -U logs/repl/sim <<< ` +
`"Users.get('${this.toID(name)}').popup(battle.sides.get('${side}').pokemon.map(m => Teams.exportSet(m)))"`
)
);
let warnMoves: (Move | Pokemon)[][] = [];
let warnBp = 1;
for (const target of pokemon.foes()) {
for (const moveSlot of target.moveSlots) {
const move = this.dex.moves.get(moveSlot.move);
let bp = move.basePower;
if (move.ohko) bp = 150;
if (move.id === 'counter' || move.id === 'metalburst' || move.id === 'mirrorcoat') bp = 120;
if (bp === 1) bp = 80;
if (!bp && move.category !== 'Status') bp = 80;
if (bp > warnBp) {
warnMoves = [[move, target]];
warnBp = bp;
} else if (bp === warnBp) {
warnMoves.push([move, target]);
}
}
}
if (!warnMoves.length) {
this.add(`c:|${getName(name)}|Fascinating. None of your sets have any moves of interest.`);
return;
}
const [warnMoveName, warnTarget] = this.sample(warnMoves);
this.add(
'message',
`${name} hacked into PS and looked at ${name === 'Hecate' ? 'her' : 'their'} opponent's sets. ` +
`${warnTarget.name}'s move ${warnMoveName} drew ${name === 'Hecate' ? 'her' : 'their'} eye.`
);
this.add(`c:|${getName(name)}|Interesting. With that in mind, bring it!`);
},
flags: {},
},
// HoeenHero
misspelled: {
shortDesc: "Swift Swim + Special Attack 1.5x, Accuracy 0.8x. Never misses, only misspells.",
@ -1935,7 +1935,7 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
if (target.getMoveHitData(move).typeMod > 0) {
this.effectState.superHit = true;
target.removeVolatile('ultramystik');
target.setAbility('Healer', null, true);
target.setAbility('Healer', null, null, true);
target.baseAbility = target.ability;
}
},

View File

@ -168,28 +168,28 @@ export const Conditions: { [id: IDEntry]: ModdedConditionData & { innateName?: s
april: {
noCopy: true,
onStart(pokemon) {
if (this.ruleTable.has('zmovesclause')) {
if (this.ruleTable.has('zmoveclause')) {
this.add(`c:|${getName('April')}|Fool's Day`);
} else {
this.add(`c:|${getName('April')}|I said, "Do you have something against dogs?"`);
}
},
onSwitchOut() {
if (this.ruleTable.has('zmovesclause')) {
if (this.ruleTable.has('zmoveclause')) {
this.add(`c:|${getName('April')}|Fool's Day`);
} else {
this.add(`c:|${getName('April')}|(Is it the chorus yet?)`);
}
},
onFaint() {
if (this.ruleTable.has('zmovesclause')) {
if (this.ruleTable.has('zmoveclause')) {
this.add(`c:|${getName('April')}|Fool's Day`);
} else {
this.add(`c:|${getName('April')}|Don't get too impressed, you might lose your breath...`);
}
},
onTryHit() {
if (this.ruleTable.has('zmovesclause')) {
if (this.ruleTable.has('zmoveclause')) {
this.add(`c:|${getName('April')}|Fool's Day`);
}
},
@ -356,9 +356,6 @@ export const Conditions: { [id: IDEntry]: ModdedConditionData & { innateName?: s
case 'rumia':
this.add(`c:|${getName('ausma')}|oh no... it's poomia....`);
break;
case 'lily':
this.add(`c:|${getName('ausma')}|togedemaru`);
break;
case 'lumari':
this.add(`c:|${getName('ausma')}|we should watch the next ladybug ep after this tbh`);
break;
@ -591,6 +588,18 @@ export const Conditions: { [id: IDEntry]: ModdedConditionData & { innateName?: s
this.add(`c:|${getName('calmvibes ♫')}|The vibes are off... :(`);
},
},
cassiopeia: {
noCopy: true,
onStart() {
this.add(`c:|${getName('Cassiopeia')}|git pull ps cassiopeia`);
},
onSwitchOut() {
this.add(`c:|${getName('Cassiopeia')}|git switch`);
},
onFaint() {
this.add(`c:|${getName('Cassiopeia')}|git checkout --detach HEAD && git commit -m "war crimes"`);
},
},
chaos: {
noCopy: true,
},
@ -978,18 +987,6 @@ export const Conditions: { [id: IDEntry]: ModdedConditionData & { innateName?: s
this.add(`c:|${getName('havi')}|the nightmare swirls and churns unending n_n`);
},
},
hecate: {
noCopy: true,
onStart() {
this.add(`c:|${getName('Hecate')}|git pull ps hecate`);
},
onSwitchOut() {
this.add(`c:|${getName('Hecate')}|git switch`);
},
onFaint() {
this.add(`c:|${getName('Hecate')}|git checkout --detach HEAD && git commit -m "war crimes"`);
},
},
hizo: {
noCopy: true,
onStart() {
@ -1361,18 +1358,6 @@ export const Conditions: { [id: IDEntry]: ModdedConditionData & { innateName?: s
this.add(`c:|${getName('Lets go shuckles')}|He who lives by the Shuckle shall die by the Shuckle.`);
},
},
lily: {
noCopy: true,
onStart() {
this.add(`c:|${getName('Lily')}|buying gf`);
},
onSwitchOut() {
this.add(`c:|${getName('Lily')}|accidentally burnt the shrimps`);
},
onFaint() {
this.add(`c:|${getName('Lily')}|oh dear, i am dead`);
},
},
loethalion: {
noCopy: true,
onStart(pokemon) {
@ -3099,7 +3084,7 @@ export const Conditions: { [id: IDEntry]: ModdedConditionData & { innateName?: s
},
},
// Effects needed to be overriden for things to happen
// Effects needed to be overridden for things to happen
attract: {
onStart(pokemon, source, effect) {
if (!(pokemon.gender === 'M' && source.gender === 'F') && !(pokemon.gender === 'F' && source.gender === 'M')) {

View File

@ -1402,6 +1402,85 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
type: "Fairy",
},
// Cassiopeia
testinginproduction: {
accuracy: true,
basePower: 0,
category: "Status",
shortDesc: "+2, -2 to random stats, small chance of harm.",
desc: "The user boosts a random stat by 2 stages, and the user lowers a random stat by 2 stages. These can be the same stat, and cannot include Accuracy or Evasion. Independently, there is a 10% chance for the user to lose 10% of their maximum HP, and there is a 5% chance for the user to gain a random non-volatile status condition.",
name: "Testing in Production",
gen: 9,
pp: 5,
priority: 0,
flags: {},
onPrepareHit() {
this.attrLastMove('[anim] Curse');
},
onHit(pokemon) {
this.add(`c:|${getName((pokemon.illusion || pokemon).name)}|Please don't break...`);
let stats: BoostID[] = [];
const boost: SparseBoostsTable = {};
let statPlus: BoostID;
for (statPlus in pokemon.boosts) {
if (statPlus === 'accuracy' || statPlus === 'evasion') continue;
if (pokemon.boosts[statPlus] < 6) {
stats.push(statPlus);
}
}
let randomStat: BoostID | undefined = stats.length ? this.sample(stats) : undefined;
if (randomStat) boost[randomStat] = 2;
stats = [];
let statMinus: BoostID;
for (statMinus in pokemon.boosts) {
if (statMinus === 'accuracy' || statMinus === 'evasion') continue;
if (pokemon.boosts[statMinus] > -6) {
stats.push(statMinus);
}
}
randomStat = stats.length ? this.sample(stats) : undefined;
if (randomStat) {
if (boost[randomStat]) {
boost[randomStat] = 0;
this.add(`c:|${getName((pokemon.illusion || pokemon).name)}|Well. Guess that broke. Time to roll back.`);
return;
} else {
boost[randomStat] = -2;
}
}
this.boost(boost, pokemon, pokemon);
},
onAfterMove(pokemon) {
if (this.randomChance(1, 10)) {
this.add(`c:|${getName((pokemon.illusion || pokemon).name)}|Ouch! That crash is really getting on my nerves...`);
this.damage(pokemon.baseMaxhp / 10);
if (pokemon.hp <= 0) return;
}
if (this.randomChance(1, 20)) {
const status = this.sample(['frz', 'brn', 'psn', 'par']);
let statusText = status;
if (status === 'frz') {
statusText = 'froze';
} else if (status === 'brn') {
statusText = 'burned';
} else if (status === 'par') {
statusText = 'paralyzed';
} else if (status === 'psn') {
statusText = 'poisoned';
}
this.add(`c:|${getName((pokemon.illusion || pokemon).name)}|Darn. A bug ${statusText} me. Guess I should have tested this first.`);
pokemon.setStatus(status);
}
},
secondary: null,
target: "self",
type: "Electric",
},
// chaos
outage: {
accuracy: 95,
@ -2377,85 +2456,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
type: "Ghost",
},
// Hecate
testinginproduction: {
accuracy: true,
basePower: 0,
category: "Status",
shortDesc: "+2, -2 to random stats, small chance of harm.",
desc: "The user boosts a random stat by 2 stages, and the user lowers a random stat by 2 stages. These can be the same stat, and cannot include Accuracy or Evasion. Independently, there is a 10% chance for the user to lose 10% of their maximum HP, and there is a 5% chance for the user to gain a random non-volatile status condition.",
name: "Testing in Production",
gen: 9,
pp: 5,
priority: 0,
flags: {},
onPrepareHit() {
this.attrLastMove('[anim] Curse');
},
onHit(pokemon) {
this.add(`c:|${getName((pokemon.illusion || pokemon).name)}|Please don't break...`);
let stats: BoostID[] = [];
const boost: SparseBoostsTable = {};
let statPlus: BoostID;
for (statPlus in pokemon.boosts) {
if (statPlus === 'accuracy' || statPlus === 'evasion') continue;
if (pokemon.boosts[statPlus] < 6) {
stats.push(statPlus);
}
}
let randomStat: BoostID | undefined = stats.length ? this.sample(stats) : undefined;
if (randomStat) boost[randomStat] = 2;
stats = [];
let statMinus: BoostID;
for (statMinus in pokemon.boosts) {
if (statMinus === 'accuracy' || statMinus === 'evasion') continue;
if (pokemon.boosts[statMinus] > -6) {
stats.push(statMinus);
}
}
randomStat = stats.length ? this.sample(stats) : undefined;
if (randomStat) {
if (boost[randomStat]) {
boost[randomStat] = 0;
this.add(`c:|${getName((pokemon.illusion || pokemon).name)}|Well. Guess that broke. Time to roll back.`);
return;
} else {
boost[randomStat] = -2;
}
}
this.boost(boost, pokemon, pokemon);
},
onAfterMove(pokemon) {
if (this.randomChance(1, 10)) {
this.add(`c:|${getName((pokemon.illusion || pokemon).name)}|Ouch! That crash is really getting on my nerves...`);
this.damage(pokemon.baseMaxhp / 10);
if (pokemon.hp <= 0) return;
}
if (this.randomChance(1, 20)) {
const status = this.sample(['frz', 'brn', 'psn', 'par']);
let statusText = status;
if (status === 'frz') {
statusText = 'froze';
} else if (status === 'brn') {
statusText = 'burned';
} else if (status === 'par') {
statusText = 'paralyzed';
} else if (status === 'psn') {
statusText = 'poisoned';
}
this.add(`c:|${getName((pokemon.illusion || pokemon).name)}|Darn. A bug ${statusText} me. Guess I should have tested this first.`);
pokemon.setStatus(status);
}
},
secondary: null,
target: "self",
type: "Electric",
},
// HiZo
scapegoat: {
accuracy: true,
@ -2696,7 +2696,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
basePower: 0,
category: "Status",
shortDesc: "Changes target to a Randbats set.",
desc: "Z-Move requiring Irpatuzinium Z. Nearly always moves first. Permanently transforms the target into a randomized Pokemon that would be generated in one of the following formats: Gen 9 Random Battle, Gen 9 Hackmons Cup, Gen 9 Challenge Cup, or Computer-Generated Teams. In the vast majority of circumstances, this also prevents the target from acting this turn.",
desc: "Z-Move requiring Irpatuzinium Z. Nearly always moves first. Permanently transforms the target into a randomized Pokemon that would be generated in one of the following formats: Gen 9 Random Battle, Gen 9 Hackmons Cup, or Gen 9 Challenge Cup. In the vast majority of circumstances, this also prevents the target from acting this turn.",
name: "Bibbidi-Bobbidi-Rands",
gen: 9,
pp: 1,
@ -2706,7 +2706,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
this.attrLastMove('[anim] Doom Desire');
},
onHit(target, source) {
const formats = ['gen9randombattle', 'gen9hackmonscup', 'gen9challengecup1v1', 'gen9computergeneratedteams'];
const formats = ['gen9randombattle', 'gen9hackmonscup', 'gen9challengecup1v1'];
const randFormat = this.sample(formats);
let msg;
switch (randFormat) {
@ -2719,9 +2719,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
case 'gen9challengecup1v1':
msg = "The only difference between a Challenge Cup Pokémon and my in-game one is that the former actually surpassed lvl. 60, enjoy n.n";
break;
case 'gen9computergeneratedteams':
msg = "We asked an AI to make a randbats set. YOU WON'T BELIEVE WHAT IT CAME UP WITH N.N";
break;
}
let team = [] as PokemonSet[];
const unModdedDex = Dex.mod('base');
@ -3412,35 +3409,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
type: "Psychic",
},
// Lily
powerup: {
accuracy: true,
basePower: 0,
category: "Status",
name: "Power Up",
shortDesc: "Heals 50% HP. Heals 3% more per fainted ally.",
desc: "Heals the user for 50% of their maximum HP. Heals an additional 3% of the user's maximum HP for each team member on the user's side that has fainted.",
pp: 5,
priority: 0,
flags: { heal: 1 },
onModifyMove(move, source, target) {
const fntAllies = source.side.pokemon.filter(ally => ally !== source && ally.fainted);
if (move.heal) move.heal[0] = 50 + (3 * fntAllies.length);
},
onTryMove() {
this.attrLastMove('[still]');
},
onPrepareHit(pokemon) {
this.add('-anim', pokemon, 'Shore Up', pokemon);
this.add('-anim', pokemon, 'Charge', pokemon);
this.add('-anim', pokemon, 'Moonlight', pokemon);
},
heal: [50, 100],
secondary: null,
target: "self",
type: "Electric",
},
// Loethalion
darkmooncackle: {
accuracy: 100,

View File

@ -220,6 +220,16 @@ export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable
abilities: { 0: "Huge Power" },
},
// Cassiopeia
mewtwo: {
inherit: true,
abilities: { 0: "Hacking" },
},
mewtwomegax: {
inherit: true,
abilities: { 0: "Hacking" },
},
// chaos
ironjugulis: {
inherit: true,
@ -396,16 +406,6 @@ export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable
abilities: { 0: "Mensis Cage" },
},
// Hecate
mewtwo: {
inherit: true,
abilities: { 0: "Hacking" },
},
mewtwomegax: {
inherit: true,
abilities: { 0: "Hacking" },
},
// HiZo
zoroarkhisui: {
inherit: true,
@ -559,12 +559,6 @@ export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable
abilities: { 0: "Persistent" },
},
// Lily
togedemaru: {
inherit: true,
abilities: { 0: "Unaware" },
},
// Loethalion
ralts: {
inherit: true,

View File

@ -27,9 +27,9 @@ export const ssbSets: SSBSets = {
signatureMove: 'Move Name',
evs: {stat: number}, ivs: {stat: number}, nature: 'Nature', teraType: 'Type',
},
// Species, ability, and item need to be captialized properly ex: Ludicolo, Swift Swim, Life Orb
// Species, ability, and item need to be capitalized properly ex: Ludicolo, Swift Swim, Life Orb
// Gender can be M, F, N, or left as an empty string
// each slot in moves needs to be a string (the move name, captialized properly ex: Hydro Pump), or an array of strings (also move names)
// each slot in moves needs to be a string (the move name, capitalized properly ex: Hydro Pump), or an array of strings (also move names)
// signatureMove also needs to be capitalized properly ex: Scripting
// You can skip Evs (defaults to 84 all) and/or Ivs (defaults to 31 all), or just skip part of the Evs (skipped evs are 0) and/or Ivs (skipped Ivs are 31)
// You can also skip shiny, defaults to false. Level can be skipped (defaults to 100).
@ -264,6 +264,12 @@ export const ssbSets: SSBSets = {
signatureMove: 'Good Vibes Only',
evs: { hp: 4, atk: 252, spe: 252 }, nature: 'Adamant', teraType: 'Water', shiny: true,
},
Cassiopeia: {
species: 'Mewtwo', ability: 'Hacking', item: 'Mewtwonite X', gender: 'F',
moves: ['Photon Geyser', 'Drain Punch', 'Iron Head'],
signatureMove: 'Testing in Production',
evs: { atk: 252, spa: 4, spe: 252 }, nature: 'Jolly',
},
chaos: {
species: 'Iron Jugulis', ability: 'Transistor', item: 'Heavy-Duty Boots', gender: 'N',
moves: [['Oblivion Wing', 'Hurricane'], ['Thunderclap', 'Volt Switch'], ['Defog', 'Roost']],
@ -433,12 +439,6 @@ export const ssbSets: SSBSets = {
signatureMove: 'Augur of Ebrietas',
evs: { spa: 252, spd: 4, spe: 252 }, nature: 'Timid', teraType: 'Ghost',
},
Hecate: {
species: 'Mewtwo', ability: 'Hacking', item: 'Mewtwonite X', gender: 'F',
moves: ['Photon Geyser', 'Drain Punch', 'Iron Head'],
signatureMove: 'Testing in Production',
evs: { atk: 252, spa: 4, spe: 252 }, nature: 'Jolly',
},
HiZo: {
species: 'Zoroark-Hisui', ability: 'Justified', item: 'Heavy-Duty Boots', gender: 'M',
moves: ['Last Respects', 'Blood Moon', 'Spirit Break'],
@ -571,12 +571,6 @@ export const ssbSets: SSBSets = {
signatureMove: 'Shuckle Power',
evs: { hp: 252, def: 252, spd: 4 }, ivs: { spe: 0 }, nature: 'Relaxed', teraType: 'Ground', shiny: 213,
},
Lily: {
species: 'Togedemaru', ability: 'Unaware', item: 'Leftovers', gender: 'F',
moves: ['Victory Dance', 'Plasma Fists', 'Meteor Mash'],
signatureMove: 'Power Up',
evs: { hp: 252, def: 4, spd: 252 }, nature: 'Careful', teraType: 'Fairy', shiny: 1734,
},
Loethalion: {
species: 'Ralts', ability: 'Psychic Surge', item: 'Gardevoirite', gender: '',
moves: ['Esper Wing', 'Calm Mind', 'Lunar Blessing'],
@ -1111,7 +1105,7 @@ export class RandomStaffBrosTeams extends RandomTeams {
const debug: string[] = []; // Set this to a list of SSB sets to override the normal pool for debugging.
const ruleTable = this.dex.formats.getRuleTable(this.format);
const meme = ruleTable.has('dynamaxclause') && !debug.length;
const afd = !ruleTable.has('dynamaxclause') && ruleTable.has('zmovesclause') && debug.length;
const afd = !ruleTable.has('dynamaxclause') && ruleTable.has('zmoveclause') && !debug.length;
const monotype = this.forceMonotype || (ruleTable.has('sametypeclause') ?
this.sample(this.dex.types.names().filter(x => x !== 'Stellar')) : false);
@ -1124,7 +1118,7 @@ export class RandomStaffBrosTeams extends RandomTeams {
}
pool = debug;
}
if (monotype && !debug.length) {
if (monotype && !debug.length && !afd && !meme) {
pool = pool.filter(x => this.dex.species.get(ssbSets[x].species).types.includes(monotype));
}
if (global.Config?.disabledssbsets?.length) {

View File

@ -92,7 +92,7 @@ export function changeSet(context: Battle, pokemon: Pokemon, newSet: SSBSet, cha
}
const details = pokemon.getUpdatedDetails();
if (oldShiny !== pokemon.set.shiny || oldGender !== pokemon.gender) context.add('replace', pokemon, details);
if (changeAbility) pokemon.setAbility(newSet.ability as string, undefined, true);
if (changeAbility) pokemon.setAbility(newSet.ability as string, undefined, undefined, true);
pokemon.baseMaxhp = pokemon.species.name === 'Shedinja' ? 1 : Math.floor(Math.floor(
2 * pokemon.species.baseStats.hp + pokemon.set.ivs.hp + Math.floor(pokemon.set.evs.hp / 4) + 100
@ -387,7 +387,7 @@ export const Scripts: ModdedBattleScriptsData = {
if (!species) continue;
pokemon.baseSpecies = rawSpecies;
pokemon.details = pokemon.getUpdatedDetails();
// pokemon.setAbility(species.abilities['0'], null, true);
// pokemon.setAbility(species.abilities['0'], null, null, true);
// pokemon.baseAbility = pokemon.ability;
const behemothMove: { [k: string]: string } = {
@ -696,13 +696,6 @@ export const Scripts: ModdedBattleScriptsData = {
}
if (pokemon.species.name === 'Terapagos-Terastal' && type === 'Stellar') {
pokemon.formeChange('Terapagos-Stellar', null, 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);
const newMaxHP = pokemon.baseMaxhp;
pokemon.hp = newMaxHP - (pokemon.maxhp - pokemon.hp);
pokemon.maxhp = newMaxHP;
this.battle.add('-heal', pokemon, pokemon.getHealth, '[silent]');
}
if (!pokemon.illusion && pokemon.name === 'Neko') {
this.battle.add(`c:|${getName('Neko')}|Possible thermal failure if operation continues (Meow on fire ?)`);
@ -908,8 +901,8 @@ export const Scripts: ModdedBattleScriptsData = {
} else {
this.battle.add(isDrag ? 'drag' : 'switch', pokemon, pokemon.getFullDetails);
}
pokemon.abilityState.effectOrder = this.battle.effectOrder++;
pokemon.itemState.effectOrder = this.battle.effectOrder++;
pokemon.abilityState = this.battle.initEffectState({ id: pokemon.ability, target: pokemon });
pokemon.itemState = this.battle.initEffectState({ id: pokemon.item, target: pokemon });
if (isDrag && this.battle.gen === 2) pokemon.draggedIn = this.battle.turn;
pokemon.previouslySwitchedIn++;
@ -943,7 +936,7 @@ export const Scripts: ModdedBattleScriptsData = {
pokemon.formeChange(speciesid, pokemon.getItem(), true);
if (pokemon.canMegaEvo) {
pokemon.canMegaEvo = null;
pokemon.canMegaEvo = false;
} else {
pokemon.canUltraBurst = null;
}
@ -971,8 +964,23 @@ export const Scripts: ModdedBattleScriptsData = {
return altForme.name;
}
// a hacked-in Megazard X can mega evolve into Megazard Y, but not into Megazard X
if (item.megaEvolves === species.baseSpecies && item.megaStone !== species.name) {
return item.megaStone;
if (Array.isArray(item.megaEvolves)) {
if (!Array.isArray(item.megaStone)) {
throw new Error(`${item.name}#megaEvolves and ${item.name}#megaStone type mismatch`);
}
if (item.megaEvolves.length !== item.megaStone.length) {
throw new Error(`${item.name}#megaEvolves and ${item.name}#megaStone length mismatch`);
}
// FIXME: Change to species.name when champions comes
const index = item.megaEvolves.indexOf(species.baseSpecies);
if (index < 0) return null;
return item.megaStone[index];
// FIXME: Change to species.name when champions comes
} else {
if (item.megaEvolves === species.baseSpecies) {
if (Array.isArray(item.megaStone)) throw new Error(`${item.name}#megaEvolves and ${item.name}#megaStone type mismatch`);
return item.megaStone;
}
}
return null;
},
@ -1313,7 +1321,7 @@ export const Scripts: ModdedBattleScriptsData = {
let movename = move.name;
if (move.id === 'hiddenpower') movename = 'Hidden Power';
if (sourceEffect) attrs += `|[from]${sourceEffect.fullname}`;
if (sourceEffect) attrs += `|[from] ${sourceEffect.fullname}`;
if (zMove && move.isZ === true) {
attrs = '|[anim]' + movename + attrs;
movename = 'Z-' + movename;

View File

@ -5,10 +5,10 @@ export const Scripts: ModdedBattleScriptsData = {
const item = this.data.Items[i];
if (!item.megaStone && !item.onDrive && !(item.onPlate && !item.zMove) && !item.onMemory) continue;
this.modData('Items', i).onTakeItem = false;
if (item.isNonstandard === "Past") this.modData('Items', i).isNonstandard = null;
if (item.megaStone) {
if (item.isNonstandard === "Past" || item.isNonstandard === "Future") this.modData('Items', i).isNonstandard = null;
/* if (item.megaStone) {
this.modData('FormatsData', this.toID(item.megaStone)).isNonstandard = null;
}
} */
}
},
start() {
@ -83,27 +83,7 @@ export const Scripts: ModdedBattleScriptsData = {
this.add(`raw|<div class="infobox"><details class="readmore"${open}><summary><strong>${format.customRules.length} custom rule${plural}:</strong></summary> ${format.customRules.join(', ')}</details></div>`);
}
format.onTeamPreview?.call(this);
for (const rule of this.ruleTable.keys()) {
if ('+*-!'.includes(rule.charAt(0))) continue;
const subFormat = this.dex.formats.get(rule);
subFormat.onTeamPreview?.call(this);
}
if (this.requestState !== 'teampreview' && this.ruleTable.pickedTeamSize) {
this.add('clearpoke');
for (const side of this.sides) {
for (const pokemon of side.pokemon) {
// Still need to hide these formes since they change on battle start
const details = pokemon.details.replace(', shiny', '')
.replace(/(Zacian|Zamazenta)(?!-Crowned)/g, '$1-*')
.replace(/(Xerneas)(-[a-zA-Z?-]+)?/g, '$1-*');
this.addSplit(side.id, ['poke', pokemon.side.id, details, '']);
}
}
this.makeRequest('teampreview');
}
this.runPickTeam();
this.queue.addChoice({ choice: 'start' });
this.midTurn = true;
if (!this.requestState) this.turnLoop();
@ -406,8 +386,8 @@ export const Scripts: ModdedBattleScriptsData = {
const item = pokemon.getItem();
if (item.megaStone) {
if (item.megaStone === pokemon.baseSpecies.name) return null;
return item.megaStone;
if (item.megaStone.includes(pokemon.baseSpecies.name)) return null;
return Array.isArray(item.megaStone) ? item.megaStone[0] : item.megaStone;
} else {
return null;
}
@ -425,12 +405,12 @@ export const Scripts: ModdedBattleScriptsData = {
const oMegaSpecies = this.dex.species.get((species as any).originalSpecies);
pokemon.formeChange(species, pokemon.getItem(), true);
this.battle.add('-start', pokemon, oMegaSpecies.requiredItem, '[silent]');
if (oSpecies.types.length !== pokemon.species.types.length || oSpecies.types[1] !== pokemon.species.types[1]) {
if (oSpecies.types.join('/') !== pokemon.species.types.join('/')) {
this.battle.add('-start', pokemon, 'typechange', pokemon.species.types.join('/'), '[silent]');
}
// }
pokemon.canMegaEvo = null;
pokemon.canMegaEvo = false;
return true;
},
terastallize(pokemon) {
@ -486,7 +466,12 @@ export const Scripts: ModdedBattleScriptsData = {
return species;
},
getFormeChangeDeltas(formeChangeSpecies, pokemon) {
const baseSpecies = this.dex.species.get(formeChangeSpecies.baseSpecies);
// Should be fine as long as Necrozma-U doesn't get added or Game Freak makes me sad with some convoluted forme change
let baseSpecies = this.dex.species.get(formeChangeSpecies.isMega ?
formeChangeSpecies.battleOnly as string : formeChangeSpecies.baseSpecies);
if (formeChangeSpecies.name === 'Zygarde-Mega') {
baseSpecies = this.dex.species.get('Zygarde-Complete');
}
const deltas: {
ability: string,
baseStats: SparseStatsTable,
@ -496,6 +481,7 @@ export const Scripts: ModdedBattleScriptsData = {
requiredItem: string | undefined,
type?: string,
formeType?: string,
isMega?: boolean,
} = {
ability: formeChangeSpecies.abilities['0'],
baseStats: {},
@ -511,15 +497,19 @@ export const Scripts: ModdedBattleScriptsData = {
let formeType: string | null = null;
if (['Arceus', 'Silvally'].includes(baseSpecies.name)) {
deltas.type = formeChangeSpecies.types[0];
formeType = 'Arceus';
formeType = 'Primary';
} else if (formeChangeSpecies.types.length > baseSpecies.types.length) {
deltas.type = formeChangeSpecies.types[1];
} else if (formeChangeSpecies.types.length < baseSpecies.types.length) {
deltas.type = this.battle.ruleTable.has('mixandmegaoldaggronite') ? 'mono' : baseSpecies.types[0];
} else if (formeChangeSpecies.types[1] !== baseSpecies.types[1]) {
deltas.type = formeChangeSpecies.types[1];
} else if (formeChangeSpecies.types[0] !== baseSpecies.types[0]) {
deltas.type = formeChangeSpecies.types[0];
formeType = 'Primary';
deltas.isMega = true;
}
if (formeChangeSpecies.isMega) formeType = 'Mega';
if (formeChangeSpecies.isMega && !formeType) formeType = 'Mega';
if (formeChangeSpecies.isPrimal) formeType = 'Primal';
if (formeChangeSpecies.name.endsWith('Crowned')) formeType = 'Crowned';
if (formeType) deltas.formeType = formeType;
@ -533,7 +523,7 @@ export const Scripts: ModdedBattleScriptsData = {
if (!deltas) throw new TypeError("Must specify deltas!");
const species = this.dex.deepClone(this.dex.species.get(speciesOrForme));
species.abilities = { '0': deltas.ability };
if (deltas.formeType === 'Arceus') {
if (deltas.formeType === 'Primary') {
const secondType = species.types[1];
species.types = [deltas.type];
if (secondType && secondType !== deltas.type) species.types.push(secondType);
@ -552,7 +542,7 @@ export const Scripts: ModdedBattleScriptsData = {
species.heightm = Math.max(0.1, ((species.heightm * 10) + (deltas.heightm * 10)) / 10);
species.originalSpecies = deltas.originalSpecies;
species.requiredItem = deltas.requiredItem;
if (deltas.formeType === 'Mega') species.isMega = true;
if (deltas.formeType === 'Mega' || deltas.isMega) species.isMega = true;
if (deltas.formeType === 'Primal') species.isPrimal = true;
return species;
},

View File

@ -206,7 +206,7 @@ export const Scripts: ModdedBattleScriptsData = {
if (!species) continue;
pokemon.baseSpecies = rawSpecies;
pokemon.details = pokemon.getUpdatedDetails();
pokemon.setAbility(species.abilities['0'], null, true);
pokemon.setAbility(species.abilities['0'], null, null, true);
pokemon.baseAbility = pokemon.ability;
const behemothMove: { [k: string]: string } = {

View File

@ -311,28 +311,36 @@ export const Scripts: ModdedBattleScriptsData = {
this.makeRequest('move');
},
pokemon: {
setAbility(ability, source, isFromFormeChange) {
setAbility(ability, source, sourceEffect, isFromFormeChange, isTransform) {
if (!this.hp) return false;
const BAD_ABILITIES = ['trace', 'imposter', 'neutralizinggas', 'illusion', 'wanderingspirit'];
if (typeof ability === 'string') ability = this.battle.dex.abilities.get(ability);
const oldAbility = this.ability;
if (!sourceEffect && this.battle.effect) sourceEffect = this.battle.effect;
const oldAbility = this.battle.dex.abilities.get(this.ability);
if (!isFromFormeChange) {
if (ability.flags['cantsuppress'] || this.getAbility().flags['cantsuppress']) return false;
}
if (!this.battle.runEvent('SetAbility', this, source, this.battle.effect, ability)) return false;
this.battle.singleEvent('End', this.battle.dex.abilities.get(oldAbility), this.abilityState, this, source);
if (!isFromFormeChange && !isTransform) {
const setAbilityEvent: boolean | null = this.battle.runEvent('SetAbility', this, source, sourceEffect, ability);
if (!setAbilityEvent) return setAbilityEvent;
}
this.battle.singleEvent('End', oldAbility, this.abilityState, this, source);
const ally = this.side.active.find(mon => mon && mon !== this && !mon.fainted);
if (ally?.m.innate) {
ally.removeVolatile(ally.m.innate);
delete ally.m.innate;
}
if (this.battle.effect && this.battle.effect.effectType === 'Move' && !isFromFormeChange) {
this.battle.add('-endability', this, this.battle.dex.abilities.get(oldAbility),
`[from] move: ${this.battle.dex.moves.get(this.battle.effect.id)}`);
}
this.ability = ability.id;
this.abilityState = this.battle.initEffectState({ id: ability.id, target: this });
if (ability.id && this.battle.gen > 3) {
if (sourceEffect && !isFromFormeChange && !isTransform) {
if (source) {
this.battle.add('-ability', this, ability.name, oldAbility.name, `[from] ${sourceEffect.fullname}`, `[of] ${source}`);
} else {
this.battle.add('-ability', this, ability.name, oldAbility.name, `[from] ${sourceEffect.fullname}`);
}
}
if (ability.id && this.battle.gen > 3 &&
(!isTransform || oldAbility.id !== ability.id || this.battle.gen <= 4)) {
this.battle.singleEvent('Start', ability, this.abilityState, this, source);
if (ally && ally.ability !== this.ability) {
if (!this.m.innate) {
@ -345,12 +353,7 @@ export const Scripts: ModdedBattleScriptsData = {
}
}
}
// Entrainment
if (this.m.innate?.endsWith(ability.id)) {
this.removeVolatile(this.m.innate);
delete this.m.innate;
}
return oldAbility;
return oldAbility.id;
},
hasAbility(ability) {
if (this.ignoringAbility()) return false;
@ -446,7 +449,7 @@ export const Scripts: ModdedBattleScriptsData = {
this.knownType = true;
this.apparentType = this.terastallized;
}
if (this.battle.gen > 2) this.setAbility(pokemon.ability, this, true, true);
if (this.battle.gen > 2) this.setAbility(pokemon.ability, this, null, true, true);
// Change formes based on held items (for Transform)
// Only ever relevant in Generation 4 since Generation 3 didn't have item-based forme changes

View File

@ -113,7 +113,7 @@ export const Scripts: ModdedBattleScriptsData = {
this.apparentType = this.terastallized;
}
if (this.battle.gen > 2) {
this.setAbility(pokemon.ability, this, true);
this.setAbility(pokemon.ability, this, null, true);
if (this.m.innates) {
for (const innate of this.m.innates) {
this.removeVolatile('ability:' + innate);
@ -202,7 +202,7 @@ export const Scripts: ModdedBattleScriptsData = {
if (this.illusion) {
this.ability = ''; // Don't allow Illusion to wear off
}
this.setAbility(species.abilities['0'], null, true);
this.setAbility(species.abilities['0'], null, null, true);
this.baseAbility = this.ability;
}
if (this.terastallized && this.terastallized !== this.apparentType) {

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