Compare commits

..

No commits in common. "master" and "v0.11.9" have entirely different histories.

955 changed files with 113281 additions and 561262 deletions

359
.eslintrc-no-types.json Normal file
View File

@ -0,0 +1,359 @@
{
"root": true,
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "script",
"ecmaFeatures": {
"globalReturn": true
}
},
"ignorePatterns": [
"logs/",
"node_modules/",
"dist/",
"data/**/learnsets.ts",
"tools/set-import/importer.js",
"tools/set-import/sets",
"tools/modlog/converter.js",
"server/global-variables.d.ts"
],
"env": {
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"rules": {
// TODO: add/revisit
// "spaced-comment": ["error", "always", {"exceptions": ["*"]}],
// "no-use-before-define": "off",
// test only (should never be committed, but useful when testing)
"no-debugger": "warn",
"no-unused-vars": ["warn", {"args": "none"}],
"no-warning-comments": "off",
"prefer-const": ["warn", {"destructuring": "all"}],
// PS code (code specific to PS)
"consistent-return": "off", // sim event handlers mix them for ergonomics
"func-style": "off", // used to type event handlers
"no-console": "off",
"no-control-regex": "off", // used to find invalid data in chat messages
"no-invalid-this": "off", // `this` is used to pass context to sim event handlers, chat commands, etc
"no-loop-func": "off", // synchronous
"no-restricted-modules": ["error", "moment", "request", "sugar"],
"no-sync": "off",
"no-void": "off", // used for spawning Promises only
"strict": ["error", "global"],
// bad code, modern (new code patterns we don't like because they're less readable or performant)
"no-restricted-globals": ["error", "Proxy", "Reflect", "Symbol", "WeakSet"],
// bad code, deprecated (deprecated/bad patterns that should be written a different way)
"eqeqeq": "error",
"func-names": "off", // has minor advantages but way too verbose, hurting readability
"guard-for-in": "off", // guarding is a deprecated pattern, we just use no-extend-native instead
"init-declarations": "off", // TypeScript lets us delay initialization safely
"no-caller": "error",
"no-eval": "error",
"no-extend-native": "error",
"no-implied-eval": "error",
"no-inner-declarations": ["error", "functions"],
"no-iterator": "error",
"no-labels": ["error", {"allowLoop": true, "allowSwitch": true}],
"no-multi-str": "error",
"no-new-func": "error",
"no-new-wrappers": "error",
"no-path-concat": "off", // Windows supports `/` as a path separator; concat is more readable
"no-proto": "error",
"no-restricted-syntax": ["error", "WithStatement"],
"no-sparse-arrays": "error",
"no-var": "error",
"no-with": "error",
// probably bugs (code with no reason to exist, probably typoes)
"array-callback-return": "error",
"block-scoped-var": "error", // not actually used; precluded by no-var
"callback-return": [2, ["callback", "cb", "done"]],
"consistent-this": "off", // we use arrow functions instead
"constructor-super": "error",
"default-case": "off", // hopefully TypeScript will let us skip `default` for things that are exhaustive
"no-bitwise": "off", // used in Dashycode
"no-case-declarations": "off", // meh, we have no-shadow
"no-duplicate-case": "error",
"no-empty": ["error", {"allowEmptyCatch": true}],
"no-extra-bind": "error",
"no-extra-label": "error",
"no-fallthrough": "error",
"no-label-var": "error",
"no-new-require": "error",
"no-new": "error",
"no-redeclare": "error",
"no-return-await": "error",
"no-self-compare": "error",
"no-sequences": "error",
"no-shadow-restricted-names": "error",
"no-shadow": "off",
"no-template-curly-in-string": "error",
"no-throw-literal": "error",
"no-undef": "off",
"no-unmodified-loop-condition": "error",
"no-unused-expressions": "error",
"no-unsafe-finally": "error",
"no-unused-labels": "error",
"use-isnan": "error",
"valid-typeof": "error",
// style choices
"no-constant-condition": ["error", {"checkLoops": false}],
"no-lonely-if": "off",
"radix": ["error", "as-needed"],
// naming style
"camelcase": "off", // mostly only so we can import `child_process`
"id-length": "off",
"id-match": "off",
"new-cap": ["error", {"newIsCap": true, "capIsNew": false}],
"no-underscore-dangle": "off",
// syntax style (local syntactical, usually autofixable formatting decisions)
"arrow-parens": "off",
"arrow-body-style": "error",
"brace-style": ["error", "1tbs", {"allowSingleLine": true}],
"comma-dangle": ["error", {"arrays": "always-multiline", "objects": "always-multiline", "imports": "always-multiline", "exports": "always-multiline", "functions": "ignore"}],
"comma-style": ["error", "last"],
"curly": ["error", "multi-line", "consistent"],
"dot-notation": "off",
"new-parens": "error",
"no-array-constructor": "error",
"no-div-regex": "error",
"no-duplicate-imports": "error",
"no-extra-parens": "off",
"no-floating-decimal": "error",
"no-mixed-requires": "error",
"no-multi-spaces": "error",
"no-new-object": "error",
"no-octal-escape": "error",
"no-return-assign": ["error", "except-parens"],
"no-undef-init": "off",
"no-unneeded-ternary": "error",
"no-useless-call": "error",
"no-useless-computed-key": "error",
"no-useless-concat": "off",
"no-useless-rename": "error",
"object-shorthand": ["error", "methods"],
"one-var": "off",
"operator-assignment": "off",
"prefer-arrow-callback": "off",
"quote-props": "off",
"quotes": "off",
"semi": ["error", "always"],
"sort-vars": "off",
"vars-on-top": "off",
"wrap-iife": ["error", "inside"],
"wrap-regex": "off",
"yoda": ["error", "never", { "exceptRange": true }],
// whitespace
"array-bracket-spacing": ["error", "never"],
"arrow-spacing": ["error", {"before": true, "after": true}],
"block-spacing": ["error", "always"],
"comma-spacing": ["error", {"before": false, "after": true}],
"computed-property-spacing": ["error", "never"],
"dot-location": ["error", "property"],
"eol-last": ["error", "always"],
"func-call-spacing": "error",
"function-paren-newline": ["error", "consistent"],
"indent": ["error", "tab", {"flatTernaryExpressions": true}],
"key-spacing": "error",
"keyword-spacing": ["error", {"before": true, "after": true}],
"lines-around-comment": "off",
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
"no-multiple-empty-lines": ["error", {"max": 2, "maxEOF": 1}],
"no-trailing-spaces": ["error", {"ignoreComments": false}],
"object-curly-spacing": ["error", "never"],
"operator-linebreak": ["error", "after"],
"padded-blocks": ["error", "never"],
"padding-line-between-statements": "off",
"rest-spread-spacing": ["error", "never"],
"semi-spacing": ["error", {"before": false, "after": true}],
"space-before-blocks": ["error", "always"],
"space-before-function-paren": ["error", {"anonymous": "always", "named": "never"}],
"space-in-parens": ["error", "never"],
"space-infix-ops": "error",
"space-unary-ops": ["error", {"words": true, "nonwords": false}],
"template-curly-spacing": ["error", "never"]
},
"overrides": [
{
"files": [
"./config/*.ts", "./data/**/*.ts", "./lib/*.ts", "./server/**/*.ts", "./server/**/*.tsx", "./sim/**/*.ts",
"./tools/set-import/*.ts", "./tools/modlog/*.ts", "./translations/**/*.ts"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module",
"tsconfigRootDir": ".",
"project": ["./tsconfig.json"]
},
"extends": [
"plugin:@typescript-eslint/recommended"
],
"rules": {
// TODO revisit
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/member-ordering": "off",
// "@typescript-eslint/no-extraneous-class": "error",
// "@typescript-eslint/no-type-alias": "error",
"@typescript-eslint/prefer-optional-chain": "off",
// "@typescript-eslint/consistent-type-imports": "error", // TODO after no-duplicate-imports fix
// test only (should never be committed, but useful when testing)
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["warn", {"args": "none"}],
"max-len": ["warn", {
"code": 120, "tabWidth": 0,
// see bottom of file for source
"ignorePattern": "^\\s*(?:\\/\\/ \\s*)?(?:(?:export )?(?:let |const |readonly )?[a-zA-Z0-9_$.]+(?: \\+?=>? )|[a-zA-Z0-9$]+: \\[?|(?:return |throw )?(?:new )?(?:[a-zA-Z0-9$.]+\\()?)?(?:Utils\\.html|(?:this\\.)?(?:room\\.)?tr|\\$\\()?['\"`/]"
}],
"prefer-const": ["warn", {"destructuring": "all"}], // typescript-eslint/recommended forces this so we need to re-override
// PS code (code specific to PS)
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/unbound-method": "off", // used for sim event handlers, command handlers, etc
"new-parens": "off", // used for the `new class {...}` pattern
"no-prototype-builtins": "off",
"no-shadow": "off",
"@typescript-eslint/no-shadow": "error",
"@typescript-eslint/no-var-requires": "off",
// typescript-eslint defaults too strict
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-unsafe-argument": "off",
// probably bugs
"@typescript-eslint/ban-types": ["error", {
"extendDefaults": true,
"types": {
"object": false
}
}],
"@typescript-eslint/no-dupe-class-members": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-extra-non-null-assertion": "error",
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-non-null-asserted-optional-chain": "error",
"no-dupe-class-members": "off",
"no-unused-expressions": "off", // ternary is used to convert callbacks to Promises
"@typescript-eslint/no-unused-expressions": ["error", {"allowTernary": true}], // ternary is used to convert callbacks to Promises
// naming style
"@typescript-eslint/naming-convention": ["error", {
"selector": ["class", "interface", "typeAlias"],
"format": ["PascalCase"]
}],
// syntax style (local syntactical, usually autofixable formatting decisions)
"@typescript-eslint/adjacent-overload-signatures": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/consistent-type-assertions": ["error", {"assertionStyle": "as"}],
"@typescript-eslint/consistent-type-definitions": ["error", "interface"],
"@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
"@typescript-eslint/member-delimiter-style": ["error", {"overrides": {"typeLiteral": {
"multiline": {"delimiter": "comma", "requireLast": true},
"singleline": {"delimiter": "comma", "requireLast": false}
}}}],
"@typescript-eslint/no-parameter-properties": "error",
// `source` and `target` are frequently used as variables that may point to `this`
// or to another `Pokemon` object, depending on how the given method is invoked
"@typescript-eslint/no-this-alias": ["error", {"allowedNames": ["source", "target"]}],
"@typescript-eslint/prefer-as-const": "error",
"@typescript-eslint/prefer-for-of": "error",
"@typescript-eslint/prefer-function-type": "error",
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-namespace-keyword": "error",
"prefer-object-spread": "error",
"@typescript-eslint/triple-slash-reference": "error",
"@typescript-eslint/unified-signatures": "error",
// syntax style, overriding base
"quotes": "off",
"@typescript-eslint/quotes": "off",
"semi": "off",
"@typescript-eslint/semi": ["error", "always"],
"func-call-spacing": "off",
"@typescript-eslint/func-call-spacing": "error",
// whitespace
"@typescript-eslint/type-annotation-spacing": "error",
"spaced-comment": ["error", "always", {"exceptions": ["*", "/"]}],
// whitespace, overriding base
"indent": "off",
"@typescript-eslint/indent": ["error", "tab", {"flatTernaryExpressions": true}]
}
},
{
"files": ["./translations/**/*.ts"],
"rules": {
"no-template-curly-in-string": "off"
}
},
{
"env": {
"mocha": true
},
"files": ["test/**/*.js"]
},
{
"files": ["build"],
"rules": {
"no-var": "off"
}
},
{
"files": ["server/chat-plugins/private/*"],
"parserOptions": {
"ecmaVersion": 2021
}
}
]
}
/*
REGEXFREE SOURCE FOR IGNOREPATTERN: https://zarel.github.io/regexfree/
# indentation
^\s*
# possibly commented out
(\/\/\ \s*)?
(
# define a variable, append to a variable, or define a single-arg arrow function
(export\ )? (let\ |const\ |readonly\ )? [a-zA-Z0-9_$.]+ (\ \+?=>?\ )
|
# define a property (oversize arrays are only allowed in properties)
[a-zA-Z0-9$]+:\ \[?
|
# optionally return or throw
(return\ |throw\ )?
# call a function or constructor
(new\ )?([a-zA-Z0-9$.]+\()?
)?
(
Utils\.html
|
(this\.)?(room\.)?tr
|
\$\(
)?
# start of string or regex
['"`\/]
*/

51
.eslintrc.json Normal file
View File

@ -0,0 +1,51 @@
{
"extends": "./.eslintrc-no-types.json",
"overrides": [
{
"files": ["./config/*.ts", "./data/**/*.ts", "./lib/*.ts", "./server/**/*.ts", "./server/**/*.tsx", "./sim/**/*.ts", "./tools/set-import/*.ts"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module",
"tsconfigRootDir": ".",
"project": ["./tsconfig.json"]
},
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"./.eslintrc-no-types.json"
],
"rules": {
// TODO investigate
"@typescript-eslint/restrict-plus-operands": "off",
// "@typescript-eslint/restrict-plus-operands": ["error", {"checkCompoundAssignments": true}],
// "@typescript-eslint/switch-exhaustiveness-check": "error",
// typescript-eslint defaults too strict
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/restrict-template-expressions": "off",
// probably bugs
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-misused-promises": "error",
"@typescript-eslint/no-throw-literal": "error",
"@typescript-eslint/no-unnecessary-condition": "off", // sadly, we use not-null assertions so commonly that these are often necessary
// syntax style (local syntactical, usually autofixable formatting decisions)
"@typescript-eslint/no-unnecessary-qualifier": "off",
// Disabled because of a bug in typescript-eslint.
// See https://github.com/typescript-eslint/typescript-eslint/issues/4554
"@typescript-eslint/no-unnecessary-type-arguments": "off",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/prefer-regexp-exec": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error"
}
}
]
}

View File

@ -15,23 +15,23 @@ jobs:
last_version: ${{ steps.last_version.outputs.version }}
token_exists: ${{ steps.check_token.outputs.token }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v3
with:
fetch-depth: 50
# Check if the package.json version field has changed since the last push
- name: Get current version from package.json
id: current_version
run: |
echo "version=$(jq -r .version package.json)" >> "$GITHUB_OUTPUT"
echo "version=$(jq -r .version package.json)" >> $GITHUB_OUTPUT
- name: Get the version from the last push
id: last_version
run: |
git checkout ${{ github.event.before }}
echo "version=$(jq -r .version package.json)" >> "$GITHUB_OUTPUT"
echo "version=$(jq -r .version package.json)" >> $GITHUB_OUTPUT
- name: Check if NPM_TOKEN exists
id: check_token
run: |
echo "token=$(if [ -n "${{ secrets.NPM_TOKEN }}" ]; then echo true; else echo false; fi)" >> "$GITHUB_OUTPUT"
echo "token=$(if [ -n "${{ secrets.NPM_TOKEN }}" ]; then echo true; else echo false; fi)" >> $GITHUB_OUTPUT
npm-publish:
needs:
- test
@ -40,14 +40,14 @@ jobs:
# We only want to publish if the package.json version field has changed
if: needs.get-version.outputs.current_version != needs.get-version.outputs.last_version && needs.get-version.outputs.token_exists == 'true'
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'npm'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm run build-npm
- run: npm run build
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -17,72 +17,16 @@ jobs:
strategy:
matrix:
# 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']
node-version: [16.x]
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 100 # assumes PR/push to master is no larger than 100 commits. Other solutions are needlessly complex.
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- name: Determine which files to lint (if pull request)
if: ${{ github.event_name == 'pull_request' }}
id: changed-files
uses: tj-actions/changed-files@v47
with:
files: |
./config/*.ts
./data/**/*.ts
./lib/*.ts
./server/**/*.ts
./server/**/*.tsx
./sim/**/*.ts
./tools/set-import/*.ts
files_ignore: |
./logs/
./node_modules/
./dist/
./data/**/learnsets.ts
./tools/set-import/importer.js
./tools/set-import/sets
./tools/modlog/converter.js
./server/global-variables.d.ts
- name: Determine whether test/sim or test/random-battles need to run (if pull request)
if: ${{ github.event_name == 'pull_request' }}
id: changed-directories
uses: tj-actions/changed-files@v47
with:
files: |
config/formats.ts
data/**
sim/**
- name: Run selective lint & necessary tests (if pull request)
if: ${{ github.event_name == 'pull_request' }}
run: npm run full-test-ci
env:
CI: true
FILES: ${{ steps.changed-files.outputs.all_changed_and_modified_files }}
SKIPSIMTESTS: ${{ steps.changed-directories.outputs.all_changed_and_modified_files == '' }}
- name: Run full lint & test (if push to master)
run: npm run full-test
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
- run: npm run full-test
env:
CI: true

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,30 +16,27 @@ on:
- preminor
- prepatch
- prerelease
jobs:
update_version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'npm'
- id: bump_version
run: |
echo "old_version=$(jq -r .version package.json)" >> "$GITHUB_OUTPUT"
echo "old_version=$(jq -r .version package.json)" >> $GITHUB_OUTPUT
npm version ${{ github.event.inputs.version }} --no-git-tag-version
echo "new_version=$(jq -r .version package.json)" >> "$GITHUB_OUTPUT"
echo "new_version=$(jq -r .version package.json)" >> $GITHUB_OUTPUT
- uses: peter-evans/create-pull-request@v8
- uses: peter-evans/create-pull-request@v4
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

2
.gitignore vendored
View File

@ -1,6 +1,6 @@
/config/*
!/config/formats.ts
/logs/**/*
/logs/*
/test/modlogs
/test/replays/*.html
/node_modules

View File

@ -1,6 +1,10 @@
{
"editor.insertSpaces": false,
"editor.formatOnSave": false,
"typescript.tsdk": "node_modules/typescript/lib",
"javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false,
"typescript.format.semicolons": "insert"
"typescript.format.semicolons": "insert",
"eslint.options": {
"configFile": ".eslintrc.json"
}
}

View File

@ -3,19 +3,11 @@ Pokemon Showdown architecture
At the highest level, PS is split into three parts:
- Game server (**[smogon/pokemon-showdown](https://github.com/smogon/pokemon-showdown)**)
- Client (**[smogon/pokemon-showdown-client](https://github.com/smogon/pokemon-showdown-client)**)
- Login server (**[smogon/pokemon-showdown-loginserver](https://github.com/smogon/pokemon-showdown-loginserver)**)
- Client
- Login server
- Game server
All three communicate directly with each other.
A user starts by visiting `https://play.pokemonshowdown.com/`. This is handled by an Apache server (in the Client), which serves mostly static files but uses some PHP (legacy, intended to be migrated to Loginserver).
The user's web browser (running Client code) will then communicate with the Login server (mounted at `https://play.pokemonshowdown.com/api/` to handle logins mostly, or otherwise interface with the Client databases one way or another).
The user's web browser will also connect to the Game server, through SockJS. The Game server handles the chat rooms, matchmaking, and actual battle simulation.
The Game server also communicates with the Login server, to handle replay uploads (and, for the main server, ladder updates).
The game server is in this repository, **[smogon/pokemon-showdown](https://github.com/smogon/pokemon-showdown)**, while the client and login server are in **[smogon/pokemon-showdown-client](https://github.com/smogon/pokemon-showdown-client)**. All three communicate directly with each other.
Game server
@ -33,7 +25,7 @@ Its entry point is [server/index.ts](./server/index.ts), which launches several
- [server/chat.ts](./server/chat.ts) sets up `Chat`, which handles chat commands and messages coming in from users (all client-to-server commands are routed through there)
`Rooms` also includes support for battle rooms, which is where the server connects to the game simulator itself. Game simulation code is in [sim/](./sim/).
`Rooms` also includes support for battle rooms, which is where the game simulation itself is done. Game simulation code is in [sim/](./sim/).
Client
@ -41,16 +33,14 @@ Client
The client is built in a mix of TypeScript and JavaScript, with a mostly hand-rolled framework built on Backbone. Theres a rewrite to migrate it to Preact but its very stalled.
Its entry point is [index.template.html](https://github.com/smogon/pokemon-showdown-client/blob/master/play.pokemonshowdown.com/index.template.html)
Its entry point is [index.template.html](https://github.com/smogon/pokemon-showdown-client/blob/master/index.template.html).
It was written long ago, so instead of a single JS entry point, it includes a lot of JS files. Everything important is launched from [js/client.js](https://github.com/smogon/pokemon-showdown-client/blob/master/play.pokemonshowdown.com/js/client.js)
It was written long ago, so instead of a single JS entry point, it includes a lot of JS files. Everything important is launched from [js/client.js](https://github.com/smogon/pokemon-showdown-client/blob/master/js/client.js).
Login server
------------
The clients login server, which handles logins and most database interaction, is written in TypeScript. The backend is currently split between a MySQL InnoDB database (for users, ladder, and most other things) and a Postgres (technically Cockroach) database (for Replays).
The clients login server, which handles logins and most database interaction, is written in PHP, with a rewrite to TypeScript in progress. The backend is split between a MySQL InnoDB database and a Percona database, with a migration to Postgres planned.
Its entry point is [server.ts](https://github.com/smogon/pokemon-showdown-loginserver/blob/master/src/server.ts).
It's intended to replace all of the old PHP code in the Client, but that migration is only halfway done at the moment.
Its entry point is [action.php](https://github.com/smogon/pokemon-showdown-client/blob/master/action.php).

View File

@ -1,31 +1,27 @@
config/formats.ts @KrisXV @Marty-D
data/mods/gen9ssb/ @HoeenCoder @HisuianZoroark @KrisXV
data/random-battles/ @KrisXV @MathyFurret @ACakeWearingAHat @livid-washed @adrivrie
data/mods/*/random-teams.ts @AnnikaCodes
data/mods/ssb/ @HoeenCoder @KrisXV
data/random-sets.json @MathyFurret @ACakeWearingAHat @livid-washed @adrivrie
data/random-teams.ts @AnnikaCodes @KrisXV @MathyFurret @ACakeWearingAHat @livid-washed @adrivrie
data/text/ @Marty-D
databases/ @monsanto
lib/sql.ts @mia-pi-git
server/artemis/* @mia-pi-git
server/chat-plugins/random-battles/ @KrisXV @AnnikaCodes
server/chat-plugins/abuse-monitor.ts @mia-pi-git
server/chat-plugins/auction.ts @Karthik99999
server/chat-plugins/datasearch.ts @KrisXV
server/chat-plugins/friends.ts @mia-pi-git
server/chat-plugins/github.ts @mia-pi-git
server/chat-plugins/hosts.ts @AnnikaCodes
server/chat-plugins/helptickets*.ts @mia-pi-git
server/chat-plugins/laddertours.ts @mia-pi-git
server/chat-plugins/mafia.ts @HoeenCoder
server/chat-plugins/othermetas.ts @KrisXV
server/chat-plugins/permalocks.ts @mia-pi-git
server/chat-plugins/quotes.ts @mia-pi-git @KrisXV
server/chat-plugins/random-battles.ts @KrisXV @AnnikaCodes
server/chat-plugins/repeats.ts @AnnikaCodes
server/chat-plugins/responder.ts @mia-pi-git
server/chat-plugins/rock-paper-scissors.ts @mia-pi-git
server/chat-plugins/sample-teams.ts @KrisXV
server/chat-plugins/scavenger*.ts @xfix @sparkychildcharlie @PartMan7
sever/chat-plugins/teams.ts @mia-pi-git
server/chat-plugins/the-studio.ts @KrisXV
server/friends.ts @mia-pi-git
server/private-messages/* @mia-pi-git
server/chat-plugins/trivia/ @AnnikaCodes
server/chat-plugins/username-prefixes.ts @AnnikaCodes
server/chat-plugins/wifi.ts @KrisXV
server/friends.ts @mia-pi-git

View File

@ -47,7 +47,7 @@ Note: Commands that ask for a team want the team in [packed team format](./sim/T
- Simulates a battle, taking input to stdin and writing output to stdout
Using Pokémon Showdown as a command-line simulator is documented at:
[sim/README.md](./sim/README.md)
[sim/README.md](./README.md)
`./pokemon-showdown json-team`

View File

@ -128,14 +128,14 @@ BAD:
```ts
// if ten seconds have passed and the user is staff
if (now > then + 10_000 && '~@%'.includes(user.tempGroup)) {
if (now > then + 10_000 && '&@%'.includes(user.tempGroup)) {
```
GOOD:
```ts
const tenSecondsPassed = now > then + 10_000;
const userIsStaff = '~@%'.includes(user.tempGroup);
const userIsStaff = '&@%'.includes(user.tempGroup);
if (tenSecondsPassed && userIsStaff) {
```
@ -247,7 +247,7 @@ Our current quote convention is to use:
- `'` as in `'fireblast'` for any string not meant to be displayed to the user; i.e. IDs
- `"` as in `"Fire Blast"` for any string meant to be displayed verbatim to the user; i.e. names (i.e. usernames, move names, etc), most English text, and help entries of chat commands
As far as I know, we don't use strings for anything else, but if you need to use strings in a way that doesn't conform to the above three, ask Zarel on Discord to decide (and default to `` ` `` in lieu of a decision).
As far as I know, we don't use strings for anything else, but if you need to use strings in a way that doesn't conform to the above three, ask Zarel in the Development chatroom to decide (and default to `` ` `` in lieu of a decision).
Unfortunately, since this is not a convention the linter can test for (and also because our older string standards predate PS), a lot of existing code is wrong on this, so you can't look at surrounding code to get an idea of what the convention should be. Refer to the above paragraph as the definitive rule.
@ -279,14 +279,14 @@ If Water Absorb doesn't absorb Thunder Wave, Water Absorb's TryHit handler retur
We prefer using `||` instead of `??` for fallback, for a few reasons:
- `sucrase` (our TypeScript to JavaScript compiler) makes `??` rather more complicated than ideal.
- We rarely treat `0` or `''` differently from `null` (the same reason we use `!foo` instead of `foo == null` for null checks)
- TypeScript does not actually allow us to have "non-empty strings" or "positive integers" as a type, so we have to deal with those cases no matter what.
If, at a future point, TypeScript does allow us to constrain types better, we might consider using `??` for clarity. But for now, I see no reason to use `??` except in very niche situations where the difference matters.
It is, of course, completely fine to use `??` in the cases where we don't want `0` or `''` or `false` to fall back to something else.
Modern JavaScript/TypeScript syntax convention
------------------------------------------------------------------------
@ -299,15 +299,15 @@ In general, we prefer modern ways of writing things as long as they're supported
- `.reduce`: we usually prefer `for`...`of` for readability, but you can use it in code that you code-own if you really want to
- 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.
- Multiline template strings: A frequent source of bugs, 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 either 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 any async implementation (although we might insist on `async`/`await` if the reability 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: Use.
- 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.
Dependencies
@ -317,9 +317,7 @@ We oppose the usual JavaScript culture of casually adding dependencies from NPM.
There are, of course, a lot of libraries like SockJS doing valuable things that we shouldn't reimplement ourselves. However, most libraries on NPM have very different priorities than we do (not caring about performance or bugs in subdependencies).
A common reason given for preferring dependencies is because someone else is more likely to have encountered and fixed bugs. But in practice many dependencies are worse-maintained than Showdown. And many bugs come from misusing a dependency.
In practice, for any dependency we could reimplement in around 30 lines of code, we'll write it ourselves and maintain it in `lib/`. Such maintenance is usually worth avoiding a `left-pad` situation, and also is generally better for performance, and also helps us easily craft the API to be most convenient for our own use-case.
But in practice, for any dependency we could reimplement in around 30 lines of code, we'll write it ourselves and maintain it in `lib/`. Such maintenance is usually worth avoiding a `left-pad` situation, and also is generally better for performance, and also helps us easily craft the API to be most convenient for our own use-case.
To be clear, we're not _opposed_ to new dependencies and will accept them where they make sense. But we try to avoid them more most than other Node projects do.
@ -327,4 +325,18 @@ To be clear, we're not _opposed_ to new dependencies and will accept them where
`package-lock.json`
------------------------------------------------------------------------
In the past, we didn't use `package-lock`. The reasons are historical. We do now. If you see a project without package-lock, feel free to add it.
We don't use `package-lock`. This is against NPM's (and most others') official advice that we should.
: First, what's `package-lock` and why is it recommended? `package-lock.json` is basically a snapshot of the `node_modules/` directory. You can think of it like `node_modules.zip`, except more human-readable, and requires an internet connection to unzip.
: The main advantage of adding it to Git is that it lets you know exactly the state of `node_modules/` at the time the programmer commits it. So if a dependency breaks, it's easier to trace exactly when it broke.
: It also makes sure `node_modules/` is exactly the same between different development environments, so differences don't cause bugs to appear for some developers but not others.
This comes with a number of disadvantages. The biggest one is that it causes package-lock changes to appear in random commits, which can outright lead to merge conflicts. It also makes diffs in general significantly less readable. It also [introduces security vulnerabilities](https://snyk.io/blog/why-npm-lockfiles-can-be-a-security-blindspot-for-injecting-malicious-modules/).
The biggest supposed advantage (ensure everyone's on the same version) isn't even an advantage! We'd specify the versions as `4.15.4` instead of `^4.15.4` if we wanted everyone on the same version, rather than the latest version. Writing `^4.15.4` is an explicit choice to opt into automatic updating.
We can still have everyone on the same version if we all re-run `npm install`, which we would STILL have to do if we were using a package-lock file. The package-lock file does not improve this situation.
(The last time we polled our developers, most supported not having a `package-lock` file.)

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2011-2026 Guangcong Luo and other contributors http://pokemonshowdown.com/
Copyright (c) 2011-2022 Guangcong Luo and other contributors http://pokemonshowdown.com/
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View File

@ -40,7 +40,7 @@ Messages from the user to the server are in the form:
`ROOMID` can optionally be left blank if unneeded (commands like `/join lobby`
can be sent anywhere). Responses will be sent to a PM box with no username
(so `|/command` is equivalent to `|/pm ~, /command`).
(so `|/command` is equivalent to `|/pm &, /command`).
`TEXT` can contain newlines, in which case it'll be treated the same
way as if each line were sent to the room separately.
@ -144,7 +144,7 @@ represented by a space), and the rest of the string being their username.
`|uhtml|NAME|HTML`
> We received an HTML message (NAME) that can change what it's displaying,
> We recieved an HTML message (NAME) that can change what it's displaying,
> this is used in things like our Polls system, for example.
`|uhtmlchange|NAME|HTML`
@ -305,7 +305,7 @@ represented by a space), and the rest of the string being their username.
`|tournament|update|JSON`
> `JSON` is a JSON object representing the changes in the tournament
> since the last update you received or the start of the tournament.
> since the last update you recieved or the start of the tournament.
> These include:
>
format: the tournament's custom name or the format being used
@ -478,9 +478,7 @@ If the challenge is accepted, you will receive a room initialization message.
`JSON.searching` will be an array of format IDs you're currently searching for
games in.
`JSON.games` will be a `{roomid: title}` table of games you're currently in,
or `null` if you're in no games.
`JSON.games` will be a `{roomid: title}` table of games you're currently in.
Note that this includes ALL games, so `|updatesearch|` will be sent when you
start/end challenge battles, and even non-Pokémon games like Mafia.

View File

@ -36,7 +36,7 @@ Pokémon Showdown is many things:
- [server/README.md](./server/README.md)
Pokémon Showdown simulates singles, doubles and triples battles in all the games out so far (Generations 1 through 9).
Pokémon Showdown simulates singles, doubles and triples battles in all the games out so far (Generations 1 through 8).
Documentation quick links
@ -89,10 +89,8 @@ Staff
- Andrew Werner [HoeenHero] - Development
- Annika L. [Annika] - Development
- Chris Monsanto [chaos] - Development, Sysadmin
- Kris Johnson [dhelmise] - Development
- Leonard Craft III [DaWoblefet] - Research (game mechanics)
- Leonard Craft III - Research (game mechanics)
- Mathieu Dias-Martins [Marty-D] - Research (game mechanics), Development
- Mia A [Mia] - Development
Contributors

17
build
View File

@ -2,12 +2,10 @@
"use strict";
try {
// 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;
// technically this was introduced in Node 15, but we'll ask for the most recent LTS with it to be safe
Promise.any([null]);
} catch (e) {
console.error("We require Node.js version 22 or later; you're using " + process.version);
console.log("We require Node.js version 16 or later; you're using " + process.version);
process.exit(1);
}
@ -24,10 +22,13 @@ function shell(cmd) {
// Check to make sure the most recently added or updated dependency is installed at the correct version
try {
require.resolve('ts-chacha20');
var version = require('esbuild').version.split('.');
if (parseInt(version[1]) < 16) {
throw new Error("esbuild version too old");
}
} catch (e) {
console.log('Installing dependencies...');
shell('npm ci');
shell('npm install');
}
// Make sure config.js exists. If not, copy it over synchronously from
@ -47,5 +48,7 @@ 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

@ -7,7 +7,7 @@ Pokémon Showdown supports custom rules in three ways:
- Tournaments, using the command `/tour rules RULES` (see the [Tournament command help][tour-help])
- Custom formats on your side server, by editing `config/formats.ts`
- Custom formats on your side server, by editing `config/formats.js`
[tour-help]: https://www.smogon.com/forums/threads/pok%C3%A9mon-showdown-forum-rules-resources-read-here-first.3570628/#post-6777489
@ -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` - (DEPRECATED) ban miscellaneous other things
`- Custom` - ban made-up things other than CAP (such as Magikarp's Revenge, or Staff Bros moves)
`- Nonexistent` - catch-all to ban all nonexistent Pokémon, items, etc. Includes: `- CAP, - Past, - Future, - LGPE`
@ -83,20 +83,10 @@ Syntax is identical to bans, just replace `-` with `+`, like:
More specific always trumps less specific:
`- all pokemon, + Uber, - Giratina, + Giratina-Altered` - allow only Ubers other than Giratina-Origin
`- all pokemon, + Giratina-Altered, - Giratina, + Uber` - allow only Ubers other than Giratina-Origin
`- all Pokemon, + Uber, - Giratina, + Giratina-Altered` - allow only Ubers other than Giratina-Origin
`- Nonexistent, + Necturna` - don't allow anything from outside the game, except the CAP Necturna
Except `all pokemon`, which removes all bans/unbans of pokemon before it:
`- all pokemon, + Pikachu, + Raichu` - allow Pikachu and Raichu
`+ Pikachu, - all pokemon, + Raichu` - allow only Raichu
(Note that `all pokemon` does not affect obtainability rules. `+ all pokemon` will not allow CAPs or anything like that.)
For equally specific rules, the last rule wins:
`- Pikachu, - Pikachu, + Pikachu` - allow Pikachu
@ -138,7 +128,7 @@ Whitelisting
Instead of a banlist, you can have a list of allowed things:
`- all pokemon, + Charmander, + Squirtle, + Bulbasaur` - allow only Kanto starters
`- all Pokemon, + Charmander, + Squirtle, + Bulbasaur` - allow only Kanto starters
`- all moves, + move: Metronome` - allow only the move Metronome

View File

@ -15,6 +15,23 @@ 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.
@ -25,15 +42,6 @@ exports.bindaddress = '0.0.0.0';
*/
exports.wsdeflate = null;
/**
* lazysockets - disables eager initialization of network services
* Turn this on if you'd prefer to manually connect Showdown to the network,
* or you intend to run it offline.
*
* @type {boolean}
*/
exports.lazysockets = false;
/*
// example:
exports.wsdeflate = {
@ -84,50 +92,6 @@ 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
*
@ -281,7 +245,6 @@ exports.reportjoinsperiod = 0;
* report battles - shows messages like "OU battle started" in the lobby
* This feature can lag larger servers - turn this off if your server is
* getting more than 160 or so users.
* @type {boolean | string[] | string}
*/
exports.reportbattles = true;
@ -437,6 +400,15 @@ 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.
@ -527,7 +499,7 @@ exports.lastfmkey = '';
exports.chatlogreader = 'fs';
/**
* permissions and groups:
* Each entry in `grouplist` is a separate group. Some of the members are "special"
* Each entry in `grouplist` is a seperate group. Some of the members are "special"
* while the rest is just a normal permission.
* The order of the groups determines their ranking.
* The special members are as follows:
@ -582,7 +554,7 @@ exports.chatlogreader = 'fs';
*/
exports.grouplist = [
{
symbol: '~',
symbol: '&',
id: "admin",
name: "Administrator",
inherit: '@',
@ -592,7 +564,7 @@ exports.grouplist = [
console: true,
bypassall: true,
lockdown: true,
promote: '~u',
promote: '&u',
roomowner: true,
roombot: true,
roommod: true,
@ -678,7 +650,7 @@ exports.grouplist = [
timer: true,
modlog: true,
alts: '%u',
bypassblocks: 'u%@~',
bypassblocks: 'u%@&~',
receiveauthmessages: true,
gamemoderation: true,
jeopardy: true,
@ -687,6 +659,13 @@ exports.grouplist = [
modchat: true,
hiderank: true,
},
{
symbol: '\u00a7',
id: "sectionleader",
name: "Section Leader",
inherit: '+',
jurisdiction: 'u',
},
{
// Bots are ranked below Driver/Mod so that Global Bots can be kept out
// of modjoin % rooms (namely, Staff).

File diff suppressed because it is too large Load Diff

View File

@ -113,7 +113,7 @@ Some Pokémon change forme in the middle of a battle. These forme changes do res
List of all in-battle forme changes:
- Ash-Greninja (Battle Bond)
- Ash Greninja (Battle Bond)
- Mimikyu (Disguise)
- Cherrim (Flower Gift)
- Castform (Forecast)
@ -127,7 +127,6 @@ 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

70
data/cg-team-data.ts Normal file
View File

@ -0,0 +1,70 @@
// Data for computer-generated teams
export const MOVE_PAIRINGS: {[moveID: string]: string} = {
rest: 'sleeptalk',
sleeptalk: 'rest',
};
// Bonuses to move ratings by ability
export const ABILITY_MOVE_BONUSES: {[abilityID: string]: {[moveID: string]: number}} = {
drought: {sunnyday: 0.2, solarbeam: 2},
};
// Bonuses to move ratings by move type
export const ABILITY_MOVE_TYPE_BONUSES: {[abilityID: string]: {[typeID: string]: number}} = {
darkaura: {Dark: 1.33},
fairyaura: {Fairy: 1.33},
// -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: string]: 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 within our control
acrobatics: 1.75, // not 2 because of the opportunity cost of forgoing an item
facade: 1.5, // not 2 because we forgo an item AND get badly poisoned
// Power increases in conditions out of our control that may occur
avalanche: 1.2,
hex: 1.2,
// screens
lightscreen: 3, reflect: 3, auroraveil: 3, // TODO: make sure AVeil always gets Snow?
// hazard removal
defog: 2, rapidspin: 1.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 SPEED_BASED_MOVES = ['gyroball', 'electroball'];

668
data/cg-teams.ts Normal file
View File

@ -0,0 +1,668 @@
/**
* Computer-Generated Teams
*
* Generates teams based on heuristics, most of which carry over across generations.
* Teams generated will not always be competitively great, but they will have variety
* and be fun to play (i.e., tries to avoid awful sets).
*
* The [Gen 9] Computer-Generated Teams format is personally maintained by Annika,
* and is not part of any official Smogon or PS format selection. If you enjoy playing
* with teams you didn't make yourself, you may want to check out Random Battles, Battle Factory,
* and/or the sample teams for usage-based formats like OU.
*
* The core of the generator is the weightedRandomPick function, which chooses from an array
* of options based on a weight associated with each option. This way, better/stronger/more useful options
* are more likely to be chosen, but there's still an opportunity for weaker, more situational,
* or higher-risk/higher-reward options to be chosen. However, for moves, the 'worst' moves are excluded
* altogether, both to reduce the likelihood of a bad moveset and improve generator performance.
*
* Certain less-relevant aspects of the set are not randomized at all, such as:
* - IVs (all 31s, with 0 Attack IV if the Pokémon has no Physical moves in case of Confusion)
* - EVs (84 per stat, for +21 to each)
* - Nature (always Quirky, which has no effect)
* - Happiness (there are no Happiness-based moves in Gen IX)
*
* Currently, leveling is based on a Pokémon's position within Smogon's usage-based tiers,
* but an automatic leveling system is planned for the future. This would involve storing win and loss
* data by Pokémon species in a database, and increasing and decreasing the levels of Pokémon species
* each day based on their win/loss ratio. For example, if 60% of matches with a Pokémon species are wins,
* the species is probably overleveled!
*
* Other aspects of the team generator that may be worth implementing in the future include:
* - Explicit support for weather-oriented teams (boosting moves and typings that synergize with that weather)
* - Tracking type coverage to make it more likely that a moveset can hit every type
*/
import {Dex, PRNG, SQL} from '../sim';
import {
ABILITY_MOVE_BONUSES,
ABILITY_MOVE_TYPE_BONUSES,
HARDCODED_MOVE_WEIGHTS,
MOVE_PAIRINGS,
SPEED_BASED_MOVES,
WEIGHT_BASED_MOVES,
} from './cg-team-data';
interface TeamStats {
hazardSetters: {[moveid: string]: number};
typeWeaknesses: {[type: string]: number};
}
// We put a limit on the number of Pokémon on a team that can be weak to a given type.
const MAX_WEAK_TO_SAME_TYPE = 3;
const levelOverride: {[speciesID: string]: number} = {};
export let levelUpdateInterval: NodeJS.Timeout | null = null;
async function updateLevels(database: SQL.DatabaseManager) {
const updateSpecies = await database.prepare(
'UPDATE gen9computergeneratedteams SET wins = 0, losses = 0, level = ? WHERE species_id = ?'
);
const updateHistory = await database.prepare(
`INSERT INTO gen9_historical_levels (level, species_id, timestamp) VALUES (?, ?, ${Date.now()})`
);
const data = await database.all('SELECT species_id, wins, losses, level FROM gen9computergeneratedteams');
for (let {species_id, wins, losses, level} of data) {
const total = wins + losses;
if (total > 10) {
if (wins / total >= 0.55) level--;
if (wins / total <= 0.45) level++;
level = Math.max(1, Math.min(100, level));
await updateSpecies?.run([level, species_id]);
await updateHistory?.run([level, species_id]);
}
levelOverride[species_id] = level;
}
}
if (global.Config && Config.usesqlite && Config.usesqliteleveling) {
const database = SQL(module, {file: './databases/battlestats.db'});
// update every 2 hours
void updateLevels(database);
levelUpdateInterval = setInterval(() => void updateLevels(database), 1000 * 60 * 60 * 2);
}
export default class TeamGenerator {
dex: ModdedDex;
format: Format;
teamSize: number;
forceLevel?: number;
prng: PRNG;
itemPool: Item[];
constructor(format: Format | string, seed: PRNG | PRNGSeed | null) {
this.dex = Dex.forFormat(format);
this.format = Dex.formats.get(format);
this.teamSize = this.format.ruleTable?.maxTeamSize || 6;
this.prng = seed instanceof PRNG ? seed : new PRNG(seed);
this.itemPool = this.dex.items.all().filter(i => i.exists && i.isNonstandard !== 'Past' && !i.isPokeball);
const rules = Dex.formats.getRuleTable(this.format);
if (rules.adjustLevel) this.forceLevel = rules.adjustLevel;
}
getTeam(): PokemonSet[] {
let speciesPool = this.dex.species.all().filter(s => {
if (!s.exists) return false;
if (s.isNonstandard || s.isNonstandard === 'Unobtainable') return false;
if (s.nfe) return false;
if (s.battleOnly && !s.requiredItems?.length) return false;
return true;
});
const teamStats: TeamStats = {
hazardSetters: {},
typeWeaknesses: {},
};
const team: PokemonSet[] = [];
while (team.length < this.teamSize && speciesPool.length) {
const species = this.prng.sample(speciesPool);
const haveRoomToReject = speciesPool.length >= (this.teamSize - team.length);
const isGoodFit = this.speciesIsGoodFit(species, teamStats);
if (haveRoomToReject && !isGoodFit) continue;
speciesPool = speciesPool.filter(s => s.baseSpecies !== species.baseSpecies);
team.push(this.makeSet(species, teamStats));
}
return team;
}
protected makeSet(species: Species, teamStats: TeamStats): PokemonSet {
const abilityPool = Object.values(species.abilities);
const abilityWeights = abilityPool.map(a => this.getAbilityWeight(this.dex.abilities.get(a)));
const ability = this.weightedRandomPick(abilityPool, abilityWeights);
const moves: Move[] = [];
let learnset = this.dex.species.getLearnset(species.id);
let movePool: string[] = [];
let learnsetSpecies = species;
if (!learnset || species.id === 'gastrodoneast') {
learnsetSpecies = this.dex.species.get(species.baseSpecies);
learnset = this.dex.species.getLearnset(learnsetSpecies.id);
}
if (learnset) {
movePool = Object.keys(learnset).filter(
moveid => learnset![moveid].find(learned => learned.startsWith('9'))
);
}
if (learnset && learnsetSpecies === species && species.changesFrom) {
const changesFrom = this.dex.species.get(species.changesFrom);
learnset = this.dex.species.getLearnset(changesFrom.id);
for (const moveid in learnset) {
if (!movePool.includes(moveid) && learnset[moveid].some(source => source.startsWith('9'))) {
movePool.push(moveid);
}
}
}
const evoRegion = learnsetSpecies.evoRegion;
while (learnsetSpecies.prevo) {
learnsetSpecies = this.dex.species.get(learnsetSpecies.prevo);
for (const moveid in learnset) {
if (!movePool.includes(moveid) &&
learnset[moveid].some(source => source.startsWith('9') && !evoRegion)) {
movePool.push(moveid);
}
}
}
if (!movePool.length) throw new Error(`No moves for ${species.id}`);
// Consider either the top 15 moves or top 30% of moves, whichever is greater.
const numberOfMovesToConsider = Math.min(movePool.length, Math.max(15, Math.trunc(movePool.length * 0.3)));
let movePoolIsTrimmed = false;
while (moves.length < 4 && movePool.length) {
let weights;
if (!movePoolIsTrimmed) {
const interimMovePool = [];
for (const move of movePool) {
const weight = this.getMoveWeight(this.dex.moves.get(move), teamStats, species, moves, ability);
interimMovePool.push({move, weight});
}
interimMovePool.sort((a, b) => b.weight - a.weight);
movePool = [];
weights = [];
for (let i = 0; i < numberOfMovesToConsider; i++) {
movePool.push(interimMovePool[i].move);
weights.push(interimMovePool[i].weight);
}
movePoolIsTrimmed = true;
} else {
weights = movePool.map(m => this.getMoveWeight(this.dex.moves.get(m), teamStats, species, moves, ability));
}
const moveID = this.weightedRandomPick(movePool, weights, {remove: true});
// add paired moves, like RestTalk
const pairedMove = MOVE_PAIRINGS[moveID];
const alreadyHavePairedMove = moves.some(m => m.id === pairedMove);
if (
moves.length < 3 &&
pairedMove &&
!alreadyHavePairedMove &&
// We don't check movePool because sometimes paired moves are bad.
this.dex.species.getLearnset(species.id)?.[pairedMove]
) {
moves.push(this.dex.moves.get(pairedMove));
movePool.splice(movePool.indexOf(pairedMove), 1);
}
moves.push(this.dex.moves.get(moveID));
}
let item = '';
if (species.requiredItem) {
item = species.requiredItem;
} else if (species.requiredItems) {
item = this.prng.sample(species.requiredItems.filter(i => !this.dex.items.get(i).isNonstandard));
} else if (moves.every(m => m.id !== 'acrobatics')) { // Don't assign an item if the set includes Acrobatics...
const weights = [];
const items = [];
for (const i of this.itemPool) {
// If the species has a special item, we should use it.
if (i.itemUser?.includes(species.name)) {
item = i.name;
break;
}
const weight = this.getItemWeight(i, teamStats, species, moves, ability);
if (weight !== 0) {
weights.push(weight);
items.push(i.name);
}
}
if (!item) item = this.weightedRandomPick(items, weights);
} else if (['Quark Drive', 'Protosynthesis'].includes(ability)) {
// ...unless the Pokemon can use Booster Energy
item = 'Booster Energy';
}
const ivs: PokemonSet['ivs'] = {
hp: 31,
atk: moves.some(move => this.dex.moves.get(move).category === 'Physical') ? 31 : 0,
def: 31,
spa: 31,
spd: 31,
spe: 31,
};
const level = this.forceLevel || TeamGenerator.getLevel(species);
// For Tera Type, we just pick a random type if it's got Tera Blast or no attacking moves,
// and the type of one of its attacking moves otherwise (so it can take advantage of the boosts).
let teraType;
const nonStatusMoves = moves.filter(move => this.dex.moves.get(move).category !== 'Status');
if (!moves.some(m => m.id === 'terablast') && nonStatusMoves.length) {
teraType = this.prng.sample(nonStatusMoves.map(move => this.dex.moves.get(move).type));
} else {
teraType = this.prng.sample([...this.dex.types.all()]).name;
}
return {
name: species.name,
species: species.name,
item,
ability,
moves: moves.map(m => m.name),
nature: 'Quirky',
gender: species.gender,
evs: {hp: 84, atk: 84, def: 84, spa: 84, spd: 84, spe: 84},
ivs,
level,
teraType,
shiny: this.prng.randomChance(1, 1024),
happiness: 255,
};
}
/**
* @returns true if the Pokémon is a good fit for the team so far, and no otherwise
*/
protected speciesIsGoodFit(species: Species, stats: TeamStats): boolean {
// type check
for (const type of this.dex.types.all()) {
const effectiveness = this.dex.getEffectiveness(type.name, species.types);
if (effectiveness === 1) { // WEAKNESS!
if (stats.typeWeaknesses[type.name] === undefined) {
stats.typeWeaknesses[type.name] = 0;
}
if (stats.typeWeaknesses[type.name] >= MAX_WEAK_TO_SAME_TYPE) {
// too many weaknesses to this type
return false;
}
}
}
// species passes; increment counters
for (const type of this.dex.types.all()) {
const effectiveness = this.dex.getEffectiveness(type.name, species.types);
if (effectiveness === 1) {
stats.typeWeaknesses[type.name]++;
}
}
return true;
}
/**
* @returns A weighting for the Pokémon's ability.
*/
protected getAbilityWeight(ability: Ability): number {
return ability.rating + 1; // Some ability ratings are -1
}
/**
* @returns A weight for a given move on a given Pokémon.
*/
protected getMoveWeight(
move: Move,
teamStats: TeamStats,
species: Species,
movesSoFar: Move[],
ability: string
): number {
if (!move.exists) return 0;
// this is NOT doubles, so there will be no adjacent ally
if (move.target === 'adjacentAlly') return 0;
if (move.category === 'Status') {
// The initial value of this weight determines how valuable status moves are vs. attacking moves.
// You can raise it to make random status moves more valuable or lower it and increase multipliers
// to make only CERTAIN status moves valuable.
let weight = 2500;
// inflicts status
if (move.status) weight *= TeamGenerator.statusWeight(move.status) * 2;
// hazard setters: very important, but we don't need 2 pokemon to set the same hazard on a team
const isHazard = (m: Move) => m.sideCondition && m.target === 'foeSide';
if (isHazard(move) && (teamStats.hazardSetters[move.id] || 0) < 1) {
weight *= move.id === 'spikes' ? 12 : 16;
// if we are ALREADY setting hazards, setting MORE is really good
if (movesSoFar.some(m => isHazard(m))) weight *= 2;
teamStats.hazardSetters[move.id]++;
}
// boosts
weight *= this.boostWeight(move, movesSoFar, species) * 2;
weight *= this.opponentDebuffWeight(move) * 2;
// protection moves - useful for bulky/stally pokemon
if (species.baseStats.def >= 100 || species.baseStats.spd >= 100 || species.baseStats.hp >= 100) {
switch (move.volatileStatus) {
case 'endure':
weight *= 3;
break;
case 'protect': case 'kingsshield': case 'silktrap':
weight *= 4;
break;
case 'banefulbunker': case 'spikyshield':
weight *= 5;
break;
default:
break;
}
}
// Hardcoded boosts
if (move.id in HARDCODED_MOVE_WEIGHTS) weight *= HARDCODED_MOVE_WEIGHTS[move.id];
// Pokémon with high Attack and Special Attack stats shouldn't have too many status moves,
// but on bulkier Pokémon it's more likely to be worth it.
const goodAttacker = species.baseStats.atk > 80 || species.baseStats.spa > 80;
if (goodAttacker && movesSoFar.filter(m => m.category !== 'Status').length < 2) {
weight *= 0.3;
}
return weight;
}
// For Grass Knot and friends, let's just assume they average out to around 60 base power.
const isWeirdPowerMove = WEIGHT_BASED_MOVES.includes(move.id);
let basePower = isWeirdPowerMove ? 60 : move.basePower;
// not how this calc works but it should be close enough
if (SPEED_BASED_MOVES.includes(move.id)) basePower = species.baseStats.spe / 2;
const baseStat = move.category === 'Physical' ? species.baseStats.atk : species.baseStats.spa;
// 10% bonus for never-miss moves
const accuracy = move.accuracy === true ? 1.1 : move.accuracy / 100;
let powerEstimate = basePower * baseStat * accuracy;
// STAB
if (species.types.includes(move.type)) powerEstimate *= ability === 'Adaptability' ? 2 : 1.5;
if (ability === 'Technician' && move.basePower <= 60) powerEstimate *= 1.5;
if (ability === 'Steely Spirit' && move.type === 'Steel') powerEstimate *= 1.5;
if (move.multihit) {
const numberOfHits = Array.isArray(move.multihit) ?
(ability === 'Skill Link' ? move.multihit[1] : (move.multihit[0] + move.multihit[1]) / 2) :
move.multihit;
powerEstimate *= numberOfHits;
}
// If it uses the attacking stat that we don't boost, it's less useful!
const hasSpecialSetup = movesSoFar.some(m => m.boosts?.spa || m.self?.boosts?.spa || m.selfBoost?.boosts?.spa);
const hasPhysicalSetup = movesSoFar.some(m => m.boosts?.atk || m.self?.boosts?.atk || m.selfBoost?.boosts?.atk);
if (move.category === 'Physical' && hasSpecialSetup) powerEstimate *= 0.7;
if (move.category === 'Special' && hasPhysicalSetup) powerEstimate *= 0.7;
const abilityBonus = (
((ABILITY_MOVE_BONUSES[ability] || {})[move.id] || 1) *
((ABILITY_MOVE_TYPE_BONUSES[ability] || {})[move.type] || 1)
);
let weight = powerEstimate * abilityBonus;
if (move.id in HARDCODED_MOVE_WEIGHTS) weight *= HARDCODED_MOVE_WEIGHTS[move.id];
// priority is more useful when you're slower
if (move.priority > 0) weight *= (Math.max(130 - species.baseStats.spe, 0) / 130) * 0.5 + 1;
if (move.priority < 0) weight *= Math.min((1 / species.baseStats.spe) * 30, 1);
// flags
if (move.flags.charge || (move.flags.recharge && ability !== 'Truant')) weight *= 0.5;
if (move.flags.contact) {
if (ability === 'Tough Claws') weight *= 1.3;
if (ability === 'Unseen Fist') weight *= 1.1;
}
if (move.flags.bite && ability === 'Strong Jaw') weight *= 1.5;
// 10% boost for ability to break subs
if (move.flags.bypasssub) weight *= 1.1;
if (move.flags.pulse && ability === 'Mega Launcher') weight *= 1.5;
if (move.flags.punch && ability === 'Iron Fist') weight *= 1.2;
if (!move.flags.protect) weight *= 1.1;
if (move.flags.slicing && ability === 'Sharpness') weight *= 1.5;
// boosts/secondaries
// TODO: consider more possible secondaries
weight *= this.boostWeight(move, movesSoFar, species);
if (move.secondary?.status) {
weight *= TeamGenerator.statusWeight(move.secondary.status, (move.secondary.chance || 100) / 100);
}
// self-inflicted confusion or locking yourself in
if (move.self?.volatileStatus) weight *= 0.8;
// downweight moves if we already have an attacking move of the same type
if (movesSoFar.some(m => m.category !== 'Status' && m.type === move.type && m.basePower >= 60)) weight *= 0.3;
if (move.selfdestruct) weight *= 0.3;
if (move.recoil) weight *= 1 - (move.recoil[0] / move.recoil[1]);
if (move.mindBlownRecoil) weight *= 0.25;
if (move.flags['futuremove']) weight *= 0.3;
// TODO: account for normal higher-crit-chance moves
if (move.willCrit) weight *= 1.45;
if (move.drain) {
const drainedFraction = move.drain[0] / move.drain[1];
weight *= 1 + (drainedFraction * 0.5);
}
// don't need 2 healing moves
if (move.heal && movesSoFar.some(m => m.heal)) weight *= 0.5;
return weight;
}
/**
* @returns A multiplier to a move weighting based on the status it inflicts.
*/
protected static statusWeight(status: string, chance = 1): number {
if (chance !== 1) return 1 + (TeamGenerator.statusWeight(status) - 1) * chance;
switch (status) {
case 'brn': return 1.5;
case 'frz': return 5;
case 'par': return 1.5;
case 'psn': return 1.5;
case 'tox': return 4;
case 'slp': return 4;
}
return 1;
}
/**
* @returns A multiplier to a move weighting based on the boosts it produces for the user.
*/
protected boostWeight(move: Move, movesSoFar: Move[], species: Species): number {
const physicalIsRelevant = (
move.category === 'Physical' ||
movesSoFar.some(m => m.category === 'Physical')
);
const specialIsRelevant = (
move.category === 'Special' ||
movesSoFar.some(m => m.category === 'Special')
);
let weight = 1;
for (const {chance, boosts} of [
{chance: 1, boosts: move.boosts},
{chance: 1, boosts: move.self?.boosts},
{chance: 1, boosts: move.selfBoost?.boosts},
{
chance: move.secondary ? ((move.secondary.chance || 100) / 100) : 0,
boosts: move.target === 'self' ? move.secondary?.boosts : move.secondary?.self?.boosts,
},
]) {
if (!boosts || chance === 0) continue;
if (boosts.atk && physicalIsRelevant) weight += (chance || 1) * 0.5 * boosts.atk;
if (boosts.spa && specialIsRelevant) weight += (chance || 1) * 0.5 * boosts.spa;
// TODO: should these scale by base stat magnitude instead of using ternaries?
// defense/special defense boost is less useful if we have some bulk to start with
if (boosts.def) weight += (chance || 1) * 0.5 * boosts.def * (species.baseStats.def > 75 ? 1 : 0.5);
if (boosts.spd) weight += (chance || 1) * 0.5 * boosts.spd * (species.baseStats.spd > 75 ? 1 : 0.5);
// speed boost is less useful for fast pokemon
if (boosts.spe) weight += (chance || 1) * 0.5 * boosts.spe * (species.baseStats.spe > 120 ? 0.5 : 1);
}
return weight;
}
/**
* @returns A weight for a move based on how much it will reduce the opponent's stats.
*/
protected opponentDebuffWeight(move: Move): number {
if (!['allAdjacentFoes', 'allAdjacent', 'foeSide', 'normal'].includes(move.target)) return 1;
let averageNumberOfDebuffs = 0;
for (const {chance, boosts} of [
{chance: 1, boosts: move.boosts},
{
chance: move.secondary ? ((move.secondary.chance || 100) / 100) : 0,
boosts: move.secondary?.boosts,
},
]) {
if (!boosts || chance === 0) continue;
const numBoosts = Object.values(boosts).filter(x => x < 0).length;
averageNumberOfDebuffs += chance * numBoosts;
}
return 1 + (0.25 * averageNumberOfDebuffs);
}
/**
* @returns A weight for an item.
*/
protected getItemWeight(item: Item, teamStats: TeamStats, species: Species, moves: Move[], ability: string): number {
let weight;
switch (item.id) {
// Choice Items
case 'choiceband':
return moves.every(x => x.category === 'Physical') ? 50 : 0;
case 'choicespecs':
return moves.every(x => x.category === 'Special') ? 50 : 0;
case 'choicescarf':
if (moves.some(x => x.category === 'Status')) return 0;
if (species.baseStats.spe > 65 && species.baseStats.spe < 120) return 50;
return 10;
// Generally Decent Items
case 'lifeorb':
return moves.filter(x => x.category !== 'Status').length * 8;
case 'focussash':
if (ability === 'Sturdy') return 0;
// frail
if (species.baseStats.hp < 80 && species.baseStats.def < 80 && species.baseStats.spd < 80) return 35;
return 10;
case 'heavydutyboots':
switch (this.dex.getEffectiveness('Rock', species)) {
case 1: return 30; // super effective
case 0: return 10; // neutral
}
return 5; // not very effective/other
case 'assaultvest':
if (moves.some(x => x.category === 'Status')) return 0;
return 30;
// status
case 'flameorb':
weight = ability === 'Guts' && !species.types.includes('Fire') ? 30 : 0;
if (moves.some(m => m.id === 'facade')) weight *= 2;
return weight;
case 'toxicorb':
if (species.types.includes('Poison')) return 0;
weight = 0;
if (ability === 'Poison Heal') weight += 25;
if (moves.some(m => m.id === 'facade')) weight += 25;
return weight;
// Healing
case 'leftovers':
return 20;
case 'blacksludge':
return species.types.includes('Poison') ? 40 : 0;
// berries
case 'sitrusberry': case 'magoberry':
return 20;
case 'throatspray':
if (moves.some(m => m.flags.sound) && moves.some(m => m.category === 'Special')) return 30;
return 0;
default:
// probably not a very good item
return 0;
}
}
/**
* @returns The level a Pokémon should be.
*/
protected static getLevel(species: Species): number {
if (levelOverride[species.id]) return levelOverride[species.id];
switch (species.tier) {
case 'Uber': return 70;
case 'OU': case 'Unreleased': return 80;
case 'UU': return 90;
case 'LC': case 'NFE': return 100;
}
return 100;
}
/**
* Picks a choice from `choices` based on the weights in `weights`.
* `weights` must be the same length as `choices`.
*/
weightedRandomPick<T>(
choices: T[],
weights: number[],
options?: {remove?: boolean}
) {
if (!choices.length) throw new Error(`Can't pick from an empty list`);
if (choices.length !== weights.length) throw new Error(`Choices and weights must be the same length`);
const totalWeight = weights.reduce((a, b) => a + b, 0);
let randomWeight = this.prng.next(0, totalWeight);
for (let i = 0; i < choices.length; i++) {
randomWeight -= weights[i];
if (randomWeight < 0) {
const choice = choices[i];
if (options?.remove) choices.splice(i, 1);
return choice;
}
}
if (options?.remove && choices.length) return choices.pop()!;
return choices[choices.length - 1];
}
setSeed(seed: PRNGSeed) {
this.prng.seed = seed;
}
}

View File

@ -1,4 +1,4 @@
export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
export const Conditions: {[k: string]: ConditionData} = {
brn: {
name: 'brn',
effectType: 'Status',
@ -6,7 +6,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
if (sourceEffect && sourceEffect.id === 'flameorb') {
this.add('-status', target, 'brn', '[from] item: Flame Orb');
} else if (sourceEffect && sourceEffect.effectType === 'Ability') {
this.add('-status', target, 'brn', '[from] ability: ' + sourceEffect.name, `[of] ${source}`);
this.add('-status', target, 'brn', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
} else {
this.add('-status', target, 'brn');
}
@ -22,12 +22,11 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
effectType: 'Status',
onStart(target, source, sourceEffect) {
if (sourceEffect && sourceEffect.effectType === 'Ability') {
this.add('-status', target, 'par', '[from] ability: ' + sourceEffect.name, `[of] ${source}`);
this.add('-status', target, 'par', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
} else {
this.add('-status', target, 'par');
}
},
onModifySpePriority: -101,
onModifySpe(spe, pokemon) {
// Paralysis occurs after all other Speed modifiers, so evaluate all modifiers up to this point first
spe = this.finalModify(spe);
@ -49,9 +48,9 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
effectType: 'Status',
onStart(target, source, sourceEffect) {
if (sourceEffect && sourceEffect.effectType === 'Ability') {
this.add('-status', target, 'slp', '[from] ability: ' + sourceEffect.name, `[of] ${source}`);
this.add('-status', target, 'slp', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
} else if (sourceEffect && sourceEffect.effectType === 'Move') {
this.add('-status', target, 'slp', `[from] move: ${sourceEffect.name}`);
this.add('-status', target, 'slp', '[from] move: ' + sourceEffect.name);
} else {
this.add('-status', target, 'slp');
}
@ -85,7 +84,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
effectType: 'Status',
onStart(target, source, sourceEffect) {
if (sourceEffect && sourceEffect.effectType === 'Ability') {
this.add('-status', target, 'frz', '[from] ability: ' + sourceEffect.name, `[of] ${source}`);
this.add('-status', target, 'frz', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
} else {
this.add('-status', target, 'frz');
}
@ -95,7 +94,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
},
onBeforeMovePriority: 10,
onBeforeMove(pokemon, target, move) {
if (move.flags['defrost'] && !(move.id === 'burnup' && !pokemon.hasType('Fire'))) return;
if (move.flags['defrost']) return;
if (this.randomChance(1, 5)) {
pokemon.cureStatus();
return;
@ -105,7 +104,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
},
onModifyMove(move, pokemon) {
if (move.flags['defrost']) {
this.add('-curestatus', pokemon, 'frz', `[from] move: ${move}`);
this.add('-curestatus', pokemon, 'frz', '[from] move: ' + move);
pokemon.clearStatus();
}
},
@ -115,7 +114,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
}
},
onDamagingHit(damage, target, source, move) {
if (move.type === 'Fire' && move.category !== 'Status' && move.id !== 'polarflare') {
if (move.type === 'Fire' && move.category !== 'Status') {
target.cureStatus();
}
},
@ -125,7 +124,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
effectType: 'Status',
onStart(target, source, sourceEffect) {
if (sourceEffect && sourceEffect.effectType === 'Ability') {
this.add('-status', target, 'psn', '[from] ability: ' + sourceEffect.name, `[of] ${source}`);
this.add('-status', target, 'psn', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
} else {
this.add('-status', target, 'psn');
}
@ -143,7 +142,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
if (sourceEffect && sourceEffect.id === 'toxicorb') {
this.add('-status', target, 'tox', '[from] item: Toxic Orb');
} else if (sourceEffect && sourceEffect.effectType === 'Ability') {
this.add('-status', target, 'tox', '[from] ability: ' + sourceEffect.name, `[of] ${source}`);
this.add('-status', target, 'tox', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
} else {
this.add('-status', target, 'tox');
}
@ -165,8 +164,6 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
onStart(target, source, sourceEffect) {
if (sourceEffect?.id === 'lockedmove') {
this.add('-start', target, 'confusion', '[fatigue]');
} else if (sourceEffect?.effectType === 'Ability') {
this.add('-start', target, 'confusion', '[from] ability: ' + sourceEffect.name, `[of] ${source}`);
} else {
this.add('-start', target, 'confusion');
}
@ -190,7 +187,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
this.activeTarget = pokemon;
const damage = this.actions.getConfusionDamage(pokemon, 40);
if (typeof damage !== 'number') throw new Error("Confusion damage not dealt");
const activeMove = { id: this.toID('confused'), effectType: 'Move', type: '???' };
const activeMove = {id: this.toID('confused'), effectType: 'Move', type: '???'};
this.damage(damage, pokemon, pokemon, activeMove as ActiveMove);
return false;
},
@ -227,7 +224,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
return this.random(5, 7);
},
onStart(pokemon, source) {
this.add('-activate', pokemon, 'move: ' + this.effectState.sourceEffect, `[of] ${source}`);
this.add('-activate', pokemon, 'move: ' + this.effectState.sourceEffect, '[of] ' + source);
this.effectState.boundDivisor = source.hasItem('bindingband') ? 6 : 8;
},
onResidualOrder: 13,
@ -270,11 +267,6 @@ 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');
@ -379,18 +371,8 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
futuremove: {
// this is a slot condition
name: 'futuremove',
onStart(target) {
this.effectState.targetSlot = target.getSlot();
this.effectState.endingTurn = (this.turn - 1) + 2;
if (this.effectState.endingTurn >= 254) {
this.hint(`In Gen 8+, Future attacks will never resolve when used on the 255th turn or later.`);
}
},
duration: 3,
onResidualOrder: 3,
onResidual(target: Pokemon) {
if (this.getOverflowedTurnCount() < this.effectState.endingTurn) return;
target.side.removeSlotCondition(this.getAtSlot(this.effectState.targetSlot), 'futuremove');
},
onEnd(target) {
const data = this.effectState;
// time's up; time to hit! :D
@ -410,6 +392,9 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
if (data.source.hasAbility('normalize') && this.gen >= 6) {
data.moveData.type = 'Normal';
}
if (data.source.hasAbility('adaptability') && this.gen >= 6) {
data.moveData.stab = 2;
}
const hitMove = new this.dex.Move(data.moveData) as ActiveMove;
this.actions.trySpreadMoveHit([target], data.source, hitMove, true);
@ -428,6 +413,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
this.effectState.sourceEffect = sourceEffect;
this.add('-activate', source, 'healreplacement');
},
onSwitchInPriority: 1,
onSwitchIn(target) {
if (!target.fainted) {
target.heal(target.maxhp);
@ -448,7 +434,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
// this.effectState.counter should never be undefined here.
// However, just in case, use 1 if it is undefined.
const counter = this.effectState.counter || 1;
this.debug(`Success chance: ${Math.round(100 / counter)}%`);
this.debug("Success chance: " + Math.round(100 / counter) + "%");
const success = this.randomChance(1, counter);
if (!success) delete pokemon.volatiles['stall'];
return success;
@ -497,7 +483,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
onFieldStart(field, source, effect) {
if (effect?.effectType === 'Ability') {
if (this.gen <= 5) this.effectState.duration = 0;
this.add('-weather', 'RainDance', '[from] ability: ' + effect.name, `[of] ${source}`);
this.add('-weather', 'RainDance', '[from] ability: ' + effect.name, '[of] ' + source);
} else {
this.add('-weather', 'RainDance');
}
@ -532,7 +518,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
}
},
onFieldStart(field, source, effect) {
this.add('-weather', 'PrimordialSea', '[from] ability: ' + effect.name, `[of] ${source}`);
this.add('-weather', 'PrimordialSea', '[from] ability: ' + effect.name, '[of] ' + source);
},
onFieldResidualOrder: 1,
onFieldResidual() {
@ -571,7 +557,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
onFieldStart(battle, source, effect) {
if (effect?.effectType === 'Ability') {
if (this.gen <= 5) this.effectState.duration = 0;
this.add('-weather', 'SunnyDay', '[from] ability: ' + effect.name, `[of] ${source}`);
this.add('-weather', 'SunnyDay', '[from] ability: ' + effect.name, '[of] ' + source);
} else {
this.add('-weather', 'SunnyDay');
}
@ -610,7 +596,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
}
},
onFieldStart(field, source, effect) {
this.add('-weather', 'DesolateLand', '[from] ability: ' + effect.name, `[of] ${source}`);
this.add('-weather', 'DesolateLand', '[from] ability: ' + effect.name, '[of] ' + source);
},
onImmunity(type, pokemon) {
if (pokemon.hasItem('utilityumbrella')) return;
@ -646,7 +632,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
onFieldStart(field, source, effect) {
if (effect?.effectType === 'Ability') {
if (this.gen <= 5) this.effectState.duration = 0;
this.add('-weather', 'Sandstorm', '[from] ability: ' + effect.name, `[of] ${source}`);
this.add('-weather', 'Sandstorm', '[from] ability: ' + effect.name, '[of] ' + source);
} else {
this.add('-weather', 'Sandstorm');
}
@ -676,7 +662,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
onFieldStart(field, source, effect) {
if (effect?.effectType === 'Ability') {
if (this.gen <= 5) this.effectState.duration = 0;
this.add('-weather', 'Hail', '[from] ability: ' + effect.name, `[of] ${source}`);
this.add('-weather', 'Hail', '[from] ability: ' + effect.name, '[of] ' + source);
} else {
this.add('-weather', 'Hail');
}
@ -693,8 +679,8 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
this.add('-weather', 'none');
},
},
snowscape: {
name: 'Snowscape',
snow: {
name: 'Snow',
effectType: 'Weather',
duration: 5,
durationCallback(source, effect) {
@ -705,22 +691,22 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
},
onModifyDefPriority: 10,
onModifyDef(def, pokemon) {
if (pokemon.hasType('Ice') && this.field.isWeather('snowscape')) {
if (pokemon.hasType('Ice') && this.field.isWeather('snow')) {
return this.modify(def, 1.5);
}
},
onFieldStart(field, source, effect) {
if (effect?.effectType === 'Ability') {
if (this.gen <= 5) this.effectState.duration = 0;
this.add('-weather', 'Snowscape', '[from] ability: ' + effect.name, `[of] ${source}`);
this.add('-weather', 'Snow', '[from] ability: ' + effect.name, '[of] ' + source);
} else {
this.add('-weather', 'Snowscape');
this.add('-weather', 'Snow');
}
},
onFieldResidualOrder: 1,
onFieldResidual() {
this.add('-weather', 'Snowscape', '[upkeep]');
if (this.field.isWeather('snowscape')) this.eachEvent('Weather');
this.add('-weather', 'Snow', '[upkeep]');
if (this.field.isWeather('snow')) this.eachEvent('Weather');
},
onFieldEnd() {
this.add('-weather', 'none');
@ -738,7 +724,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
}
},
onFieldStart(field, source, effect) {
this.add('-weather', 'DeltaStream', '[from] ability: ' + effect.name, `[of] ${source}`);
this.add('-weather', 'DeltaStream', '[from] ability: ' + effect.name, '[of] ' + source);
},
onFieldResidualOrder: 1,
onFieldResidual() {
@ -810,7 +796,7 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
name: "Commanded",
noCopy: true,
onStart(pokemon) {
this.boost({ atk: 2, spa: 2, spe: 2, def: 2, spd: 2 }, pokemon);
this.boost({atk: 2, spa: 2, spe: 2, def: 2, spd: 2}, pokemon);
},
onDragOutPriority: 2,
onDragOut() {
@ -835,9 +821,11 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
onTrapPokemon(pokemon) {
pokemon.trapped = true;
},
// Dodging moves is handled in BattleActions#hitStepInvulnerabilityEvent
// This is here for moves that manually call this event like Perish Song
onInvulnerability: false,
// Override No Guard
onInvulnerabilityPriority: 2,
onInvulnerability(target, source, move) {
return false;
},
onBeforeTurn(pokemon) {
this.queue.cancelAction(pokemon);
},
@ -879,71 +867,12 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
return [type];
},
},
zacian: {
name: 'Zacian',
onBattleStart(pokemon) {
if (pokemon.item !== 'rustedsword') return;
const rawSpecies = this.dex.species.get('Zacian-Crowned');
const species = pokemon.setSpecies(rawSpecies);
if (!species) return;
pokemon.baseSpecies = rawSpecies;
pokemon.details = pokemon.getUpdatedDetails();
pokemon.setAbility(species.abilities['0'], null, null, true);
pokemon.baseAbility = pokemon.ability;
const ironHeadIndex = pokemon.baseMoves.indexOf('ironhead');
if (ironHeadIndex >= 0) {
const move = this.dex.moves.get('behemothblade');
pokemon.baseMoveSlots[ironHeadIndex] = {
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,
disabledSource: '',
used: false,
};
pokemon.moveSlots = pokemon.baseMoveSlots.slice();
}
},
},
zamazenta: {
name: 'Zamazenta',
onBattleStart(pokemon) {
if (pokemon.item !== 'rustedshield') return;
const rawSpecies = this.dex.species.get('Zamazenta-Crowned');
const species = pokemon.setSpecies(rawSpecies);
if (!species) return;
pokemon.baseSpecies = rawSpecies;
pokemon.details = pokemon.getUpdatedDetails();
pokemon.setAbility(species.abilities['0'], null, null, true);
pokemon.baseAbility = pokemon.ability;
const ironHeadIndex = pokemon.baseMoves.indexOf('ironhead');
if (ironHeadIndex >= 0) {
const move = this.dex.moves.get('behemothbash');
pokemon.baseMoveSlots[ironHeadIndex] = {
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,
disabledSource: '',
used: false,
};
pokemon.moveSlots = pokemon.baseMoveSlots.slice();
}
},
},
rolloutstorage: {
name: 'rolloutstorage',
duration: 2,
onBasePower(relayVar, source, target, move) {
let bp = Math.max(1, move.basePower);
bp *= 2 ** source.volatiles['rolloutstorage'].contactHitCount;
bp *= Math.pow(2, source.volatiles['rolloutstorage'].contactHitCount);
if (source.volatiles['defensecurl']) {
bp *= 2;
}

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

@ -1,206 +0,0 @@
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

@ -1,44 +0,0 @@
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

@ -1,41 +0,0 @@
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

@ -1,419 +0,0 @@
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,
onTryHit(target, source) {
const targetAbility = target.getAbility();
const sourceAbility = source.getAbility();
if (sourceAbility.flags['failskillswap'] || targetAbility.flags['failskillswap'] || target.volatiles['dynamax']) {
return false;
}
let sourceCanBeSet = this.runEvent('SetAbility', source, source, this.effect, targetAbility);
if (!this.dex.abilities.get(sourceAbility).exists && this.dex.items.get(sourceAbility.id).exists) {
sourceCanBeSet = this.runEvent('TakeItem', source, source, this.effect, this.dex.items.get(sourceAbility.id));
}
if (!sourceCanBeSet) return sourceCanBeSet;
let targetCanBeSet = this.runEvent('SetAbility', target, source, this.effect, sourceAbility);
if (!this.dex.abilities.get(targetAbility).exists && this.dex.items.get(targetAbility.id).exists) {
targetCanBeSet = this.runEvent('TakeItem', target, source, this.effect, this.dex.items.get(targetAbility.id));
}
if (!targetCanBeSet) return targetCanBeSet;
},
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

@ -1,584 +0,0 @@
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) {
const allThings = new Set([
...(this.m.scrambled.abilities as { thing: string }[]).map(e => e.thing),
...(this.m.scrambled.items as { thing: string }[]).map(e => e.thing),
...(this.m.scrambled.moves as { thing: string }[]).map(e => e.thing),
this.ability, ...this.moveSlots.map(e => e.move), this.item,
].map(this.battle.toID));
let isBMMAbil = false;
let isOldBMMAbil = false;
if (!this.hp) return false;
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 (ability.name.length && !this.battle.dex.abilities.get(ability).exists) isBMMAbil = true;
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);
} else {
if (!this.battle.runEvent('TakeItem', this, source, null, abil as Item)) return false;
}
oldAbility = {
id: this.ability,
name: abil.name || this.ability,
flags: {},
effectType: "Ability",
toString() {
return abil.name || this.id;
},
} as Ability;
isOldBMMAbil = true;
}
if (allThings.has(ability.id)) return false;
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' || (this.m.scrambled.abilities as { thing: string }[])
.findIndex(e => this.battle.toID(e.thing) === 'multitype') >= 0) {
return false;
}
if (this.battle.toID(source.ability) === 'multitype' || (source.m.scrambled.abilities as { thing: string }[])
.findIndex(e => this.battle.toID(e.thing) === 'multitype') >= 0) {
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) {
const dexAbil = this.battle.dex.abilities.get(this.m.scrambled.abilities[wrongSlot].thing);
if (dexAbil.flags['failskillswap']) return false;
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) {
const allThings = new Set([
...(this.m.scrambled.abilities as { thing: string }[]).map(e => e.thing),
...(this.m.scrambled.items as { thing: string }[]).map(e => e.thing),
...(this.m.scrambled.moves as { thing: string }[]).map(e => e.thing),
this.ability, ...this.moveSlots.map(e => e.move), this.item,
].map(this.battle.toID));
let isBMMItem = false;
let isOldBMMItem = false;
if (!this.hp || !this.isActive) return false;
if (typeof item === 'string') {
if (!item.length || 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);
} else {
if ((newData as Ability).flags['failskillswap']) return false;
}
item = {
id: newData.id || itemString,
name: newData.name || itemString,
effectType: "Item",
toString() {
return newData.name || itemString;
},
} as Item;
}
}
if (item.name.length && !this.battle.dex.items.get(item).exists) isBMMItem = true;
if (allThings.has(item.id)) return false;
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) {
const dexItem = this.battle.dex.items.get(item.name);
this.removeVolatile(item.id);
const itemIndex = (this.m.scrambled.items as { thing: string, inSlot: string }[]).findIndex(e =>
this.battle.toID(e.thing) === dexItem.id && e.inSlot === isBMM);
if (itemIndex >= 0) this.m.scrambled.items.splice(itemIndex, 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) {
const dexItem = this.battle.dex.items.get(item.name);
this.removeVolatile(item.id);
const itemIndex = (this.m.scrambled.items as { thing: string, inSlot: string }[]).findIndex(e =>
this.battle.toID(e.thing) === dexItem.id && e.inSlot === isBMM);
if (itemIndex >= 0) this.m.scrambled.items.splice(itemIndex, 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,993 +0,0 @@
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.",
},
jellobody: {
onTryHit(pokemon, target, move) {
if (move.selfSwitch) {
this.add('-immune', pokemon, '[from] ability: Jello Body');
this.heal(target.baseMaxhp / 2);
return null;
}
},
onModifyMove(move, source, target) {
move.drain = [1, 2];
},
flags: { breakable: 1 },
name: "Jello Body",
rating: 5,
num: -122,
shortDesc: "Immune to pivot moves, heals 50% HP when hit by one. All moves drain 50%.",
},
nibblenibble: {
onPrepareHit(source, target, move) {
if (move.category === 'Status' || move.multihit || move.flags['noparentalbond'] || move.flags['charge'] ||
move.flags['futuremove'] || move.spreadHit || move.isZ || move.isMax || !move.flags['bite']) return;
move.multihit = 2;
move.multihitType = 'parentalbond';
},
// Damage modifier implemented in BattleActions#modifyDamage()
onSourceModifySecondaries(secondaries, target, source, move) {
if (move.multihitType === 'parentalbond' && move.id === 'secretpower' && move.hit < 2) {
// hack to prevent accidentally suppressing King's Rock/Razor Fang
return secondaries.filter(effect => effect.volatileStatus === 'flinch');
}
},
flags: {},
name: "Nibble Nibble",
rating: 5,
num: -123,
shortDesc: "Parental Bond but for Bite moves.",
},
};

View File

@ -1,20 +0,0 @@
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;
},
},
};

View File

@ -1,150 +0,0 @@
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": "Masquerain-Mega" },
itemUser: ["Masquerain"],
onTakeItem(item, source) {
return !item.megaStone?.[source.baseSpecies.baseSpecies];
},
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": "Typhlosion-Mega" },
itemUser: ["Typhlosion"],
onTakeItem(item, source) {
return !item.megaStone?.[source.baseSpecies.baseSpecies];
},
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(100 - (target.maxhp - target.hp)), 10);
if (this.randomChance(chance, 100) && damage >= target.hp && effect && effect.effectType === 'Move') {
this.add("-activate", target, "item: Focus Band");
return target.hp - 1;
} else {
return damage;
}
},
num: 230,
gen: 2,
desc: "Chance to survive attack equal to percentage of remaining HP, minimum 10%.",
},
raticite: {
name: "Raticite",
spritenum: 1,
megaStone: { "Raticate": "Raticate-Mega" },
itemUser: ["Raticate"],
onTakeItem(item, source) {
return !item.megaStone?.[source.baseSpecies.baseSpecies];
},
num: -3,
gen: 9,
desc: "If held by a Raticate, this item allows it to Mega Evolve in battle.",
},
};

File diff suppressed because it is too large Load Diff

View File

@ -1,514 +0,0 @@
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" },
},
glimmora: {
inherit: true,
abilities: { 0: "Corrosion" },
},
wobbuffet: {
inherit: true,
abilities: { 0: "Jello Body" },
},
raticate: {
inherit: true,
abilities: { 0: "Hustle" },
baseStats: { hp: 90, atk: 81, def: 60, spa: 50, spd: 70, spe: 97 },
},
raticatemega: {
num: -977,
name: "Raticate-Mega",
baseSpecies: "Raticate",
forme: "Mega",
types: ["Normal", "Ghost"],
genderRatio: { M: 0.5, F: 0.5 },
baseStats: { hp: 90, atk: 105, def: 60, spa: 50, spd: 70, spe: 173 },
abilities: { 0: "Nibble Nibble" },
heightm: 0.7,
weightkg: 5,
color: "Black",
eggGroups: ["Field"],
requiredItem: "Raticite",
},
};

View File

@ -1,304 +0,0 @@
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'];
this.modData('Learnsets', 'wobbuffet').learnset.nightshade = ['9L1'];
this.modData('Learnsets', 'wobbuffet').learnset.guillotine = ['9L1'];
this.modData('Learnsets', 'wobbuffet').learnset.shedtail = ['9L1'];
this.modData('Learnsets', 'raticate').learnset.lastbreakfast = ['9L1'];
},
};

View File

@ -1,4 +1,4 @@
export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTable = {
export const Abilities: {[k: string]: ModdedAbilityData} = {
unaware: {
inherit: true,
onAnyModifyBoost(boosts, pokemon) {

View File

@ -15,8 +15,10 @@ export const Scripts: ModdedBattleScriptsData = {
move.hit = 0;
}
if (!target.runImmunity(move, !suppressMessages)) {
return false;
if (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) {
if (!target.runImmunity(move.type, !suppressMessages)) {
return false;
}
}
if (move.ohko) return target.maxhp;
@ -78,7 +80,7 @@ export const Scripts: ModdedBattleScriptsData = {
const isPhysical = move.category === 'Physical';
const defenseStat: StatIDExceptHP = move.overrideDefensiveStat || (isPhysical ? 'def' : 'spd');
const statTable: { [k in StatIDExceptHP]: string } = { atk: 'Atk', def: 'Def', spa: 'SpA', spd: 'SpD', spe: 'Spe' };
const statTable: {[k in StatIDExceptHP]: string} = {atk: 'Atk', def: 'Def', spa: 'SpA', spd: 'SpD', spe: 'Spe'};
let maxAttack = 0;

View File

@ -10,7 +10,7 @@ There were only 151 Pokémon plus MissingNo, just a handful of moves, no abiliti
EVd to the max and we had some kind of different IVs, which maxed at 15 and every point gave 2 to the stat, so in
a similar fashion, Pokes used to have 30 IVs on each stat.
The following sources have been used and extremely useful when developing this mod:
The following sources have been used and extremly useful when developing this mod:
https://raw.github.com/po-devs/pokemon-online/master/bin/database/rby-stuff.txt
https://www.smogon.com/rb/articles/differences
https://www.smogon.com/forums/threads/past-gens-research-thread.3506992/#post-5878612

View File

@ -8,7 +8,7 @@
* under certain conditions and re-applied under other conditions.
*/
export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDataTable = {
export const Conditions: {[id: string]: ModdedConditionData} = {
brn: {
name: 'brn',
effectType: 'Status',
@ -41,7 +41,7 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
if (pokemon.removeVolatile('twoturnmove')) {
if (pokemon.volatiles['invulnerability']) {
this.hint(`In Gen 1, when a Dig/Fly user is fully paralyzed while semi-invulnerable, ` +
`it will remain semi-invulnerable until it switches out or fully executes Dig/Fly`, true);
`it will remain semi-invulnerable until it switches out or fully executes Dig/Fly`, true);
}
}
pokemon.removeVolatile('partialtrappinglock');
@ -55,7 +55,7 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
effectType: 'Status',
onStart(target, source, sourceEffect) {
if (sourceEffect && sourceEffect.effectType === 'Move') {
this.add('-status', target, 'slp', `[from] move: ${sourceEffect.name}`);
this.add('-status', target, 'slp', '[from] move: ' + sourceEffect.name);
} else {
this.add('-status', target, 'slp');
}
@ -155,6 +155,7 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
pokemon.removeVolatile('lockedmove');
return false;
}
return;
},
},
flinch: {
@ -163,7 +164,7 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
onStart(target) {
target.removeVolatile('mustrecharge');
},
onBeforeMovePriority: 8,
onBeforeMovePriority: 4,
onBeforeMove(pokemon) {
if (!this.runEvent('Flinch', pokemon)) {
return;
@ -185,94 +186,37 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
},
partiallytrapped: {
name: 'partiallytrapped',
// this is the duration of Wrap if it doesn't continue.
// (i.e. if the attacker switches out.)
// the full duration is tracked in partialtrappinglock
duration: 2,
// defender still takes PSN damage, etc
// TODO: research exact mechanics
onBeforeMovePriority: 9,
onBeforeMovePriority: 4,
onBeforeMove(pokemon) {
this.add('cant', pokemon, 'partiallytrapped');
return false;
},
onRestart() {
this.effectState.duration = 2;
},
onAccuracy(accuracy, target, source, move) {
if (source === this.effectState.source) return true;
},
onDisableMove(target) {
target.maybeLocked = true;
},
},
fakepartiallytrapped: {
name: 'fakepartiallytrapped',
// Wrap ended this turn, but you don't know that
// until you try to use an attack
duration: 2,
onDisableMove(target) {
target.maybeLocked = true;
},
},
partialtrappinglock: {
name: 'partialtrappinglock',
durationCallback() {
return this.sample([2, 2, 2, 3, 3, 3, 4, 5]);
const duration = this.sample([2, 2, 2, 3, 3, 3, 4, 5]);
return duration;
},
onStart(target, source, effect) {
const foe = target.foes()[0];
if (!foe) return false;
this.effectState.move = effect.id;
this.effectState.totalDuration = this.effectState.duration!;
this.effectState.damage = this.lastDamage;
this.effectState.locked = foe;
foe.addVolatile('partiallytrapped', target, effect);
},
onOverrideAction(pokemon, target, move) {
return this.effectState.move;
},
onBeforeMove(pokemon, target, move) {
if (target !== this.effectState.locked) {
pokemon.removeVolatile('partialtrappinglock');
}
},
onAfterMove(pokemon, target, move) {
if (target && target.hp <= 0) {
delete pokemon.volatiles['partialtrappinglock'];
return;
}
if (this.effectState.duration === 1) {
if (this.effectState.totalDuration !== 5) {
pokemon.addVolatile('fakepartiallytrapped');
pokemon.volatiles['fakepartiallytrapped'].counterpart = target;
target.addVolatile('fakepartiallytrapped');
target.volatiles['fakepartiallytrapped'].counterpart = pokemon;
}
} else {
target.addVolatile('partiallytrapped', pokemon, move);
}
},
onLockMove() {
return this.effectState.move;
},
onDisableMove(pokemon) {
pokemon.maybeLocked = true;
if (!pokemon.hasMove(this.effectState.move)) {
return;
}
for (const moveSlot of pokemon.moveSlots) {
if (moveSlot.id !== this.effectState.move) {
pokemon.disableMove(moveSlot.id);
}
}
},
},
mustrecharge: {
inherit: true,
duration: 0,
onBeforeMovePriority: 7,
onStart: undefined, // no inherit
onAfterMove(pokemon, target, move) {
if (target && target.hp <= 0) {
delete pokemon.volatiles['mustrecharge'];
return;
}
this.add('-mustrecharge', pokemon);
},
onStart() {},
},
lockedmove: {
// Thrash and Petal Dance.
@ -289,12 +233,7 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
const move = this.dex.moves.get(this.effectState.move);
if (move.id) {
this.debug('Forcing into ' + move.id);
this.queue.changeAction(pokemon, { choice: 'move', moveid: move.id });
}
},
onAfterMove(pokemon) {
if (pokemon.volatiles['lockedmove'].time <= 0) {
pokemon.removeVolatile('lockedmove');
this.queue.changeAction(pokemon, {choice: 'move', moveid: move.id});
}
},
},

View File

@ -1,4 +1,4 @@
export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormatsDataTable = {
export const FormatsData: {[k: string]: ModdedSpeciesFormatsData} = {
bulbasaur: {
tier: "LC",
},
@ -6,7 +6,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
venusaur: {
tier: "PU",
tier: "UU",
},
charmander: {
tier: "LC",
@ -33,7 +33,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
butterfree: {
tier: "ZU",
tier: "PU",
},
weedle: {
tier: "LC",
@ -42,7 +42,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
beedrill: {
tier: "ZU",
tier: "PU",
},
pidgey: {
tier: "LC",
@ -63,25 +63,25 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
fearow: {
tier: "NU",
tier: "PU",
},
ekans: {
tier: "LC",
},
arbok: {
tier: "ZU",
tier: "PU",
},
pikachu: {
tier: "LC",
},
raichu: {
tier: "UU",
tier: "NUBL",
},
sandshrew: {
tier: "LC",
},
sandslash: {
tier: "ZU",
tier: "PU",
},
nidoranf: {
tier: "LC",
@ -90,7 +90,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
nidoqueen: {
tier: "ZU",
tier: "PU",
},
nidoranm: {
tier: "LC",
@ -99,31 +99,31 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
nidoking: {
tier: "PU",
tier: "NU",
},
clefairy: {
tier: "LC",
},
clefable: {
tier: "UU",
tier: "NU",
},
vulpix: {
tier: "LC",
},
ninetales: {
tier: "UU",
tier: "NU",
},
jigglypuff: {
tier: "LC",
},
wigglytuff: {
tier: "ZU",
tier: "NU",
},
zubat: {
tier: "LC",
},
golbat: {
tier: "ZU",
tier: "PU",
},
oddish: {
tier: "LC",
@ -132,13 +132,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
vileplume: {
tier: "ZU",
tier: "PU",
},
paras: {
tier: "LC",
},
parasect: {
tier: "ZU",
tier: "PU",
},
venonat: {
tier: "LC",
@ -162,7 +162,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
golduck: {
tier: "PU",
tier: "NUBL",
},
mankey: {
tier: "LC",
@ -174,22 +174,22 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
arcanine: {
tier: "NU",
},
poliwag: {
tier: "ZU",
},
poliwhirl: {
tier: "NU",
},
poliwrath: {
tier: "NU",
},
abra: {
tier: "PU",
},
poliwag: {
tier: "LC",
},
poliwhirl: {
tier: "NUBL",
},
poliwrath: {
tier: "NUBL",
},
abra: {
tier: "LC",
},
kadabra: {
tier: "NU",
tier: "UU",
},
alakazam: {
tier: "OU",
@ -201,7 +201,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
machamp: {
tier: "ZU",
tier: "PU",
},
bellsprout: {
tier: "LC",
@ -210,19 +210,19 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
victreebel: {
tier: "NU",
tier: "UU",
},
tentacool: {
tier: "ZU",
tier: "LC",
},
tentacruel: {
tier: "NU",
tier: "UU",
},
geodude: {
tier: "LC",
},
graveler: {
tier: "PU",
tier: "NFE",
},
golem: {
tier: "NU",
@ -231,22 +231,22 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
rapidash: {
tier: "UU",
tier: "PU",
},
slowpoke: {
tier: "ZU",
tier: "LC",
},
slowbro: {
tier: "UU",
tier: "OU",
},
magnemite: {
tier: "LC",
},
magneton: {
tier: "NU",
tier: "PU",
},
farfetchd: {
tier: "ZU",
tier: "PU",
},
doduo: {
tier: "LC",
@ -258,13 +258,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
dewgong: {
tier: "NU",
tier: "UU",
},
grimer: {
tier: "LC",
},
muk: {
tier: "ZU",
tier: "PU",
},
shellder: {
tier: "LC",
@ -273,7 +273,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
gastly: {
tier: "PU",
tier: "LC",
},
haunter: {
tier: "UU",
@ -282,10 +282,10 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
onix: {
tier: "ZU",
tier: "PU",
},
drowzee: {
tier: "ZU",
tier: "LC",
},
hypno: {
tier: "UU",
@ -294,7 +294,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
kingler: {
tier: "ZU",
tier: "NU",
},
voltorb: {
tier: "LC",
@ -303,7 +303,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
exeggcute: {
tier: "PU",
tier: "NU",
},
exeggutor: {
tier: "OU",
@ -312,22 +312,22 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
marowak: {
tier: "ZU",
tier: "PU",
},
hitmonlee: {
tier: "ZU",
tier: "PU",
},
hitmonchan: {
tier: "ZU",
tier: "PU",
},
lickitung: {
tier: "ZU",
tier: "PU",
},
koffing: {
tier: "LC",
},
weezing: {
tier: "ZU",
tier: "PU",
},
rhyhorn: {
tier: "LC",
@ -339,7 +339,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
tangela: {
tier: "NU",
tier: "UU",
},
kangaskhan: {
tier: "UU",
@ -357,7 +357,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "PU",
},
staryu: {
tier: "PU",
tier: "LC",
},
starmie: {
tier: "OU",
@ -378,7 +378,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "PU",
},
pinsir: {
tier: "ZU",
tier: "PU",
},
tauros: {
tier: "OU",
@ -393,28 +393,28 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
ditto: {
tier: "ZU",
tier: "PU",
},
eevee: {
tier: "LC",
},
vaporeon: {
tier: "NU",
tier: "UU",
},
jolteon: {
tier: "OU",
},
flareon: {
tier: "ZU",
tier: "PU",
},
porygon: {
tier: "PU",
},
omanyte: {
tier: "ZU",
tier: "LC",
},
omastar: {
tier: "NU",
tier: "UU",
},
kabuto: {
tier: "LC",
@ -423,7 +423,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
aerodactyl: {
tier: "NU",
tier: "UU",
},
snorlax: {
tier: "OU",
@ -435,13 +435,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
moltres: {
tier: "UU",
tier: "NU",
},
dratini: {
tier: "LC",
},
dragonair: {
tier: "ZU",
tier: "NFE",
},
dragonite: {
tier: "UU",

View File

@ -3,7 +3,7 @@
* Some moves have had major changes, such as Bite's typing.
*/
export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
export const Moves: {[k: string]: ModdedMoveData} = {
acid: {
inherit: true,
secondary: {
@ -53,7 +53,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
return false;
}
const target = this.getRandomTarget(pokemon, 'Pound');
this.actions.moveHit(target, pokemon, currentMove, { damage: this.effectState.damage * 2 } as ActiveMove);
this.actions.moveHit(target, pokemon, currentMove, {damage: this.effectState.damage * 2} as ActiveMove);
pokemon.removeVolatile('bide');
return false;
}
@ -76,13 +76,22 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
bind: {
inherit: true,
ignoreImmunity: true,
volatileStatus: 'partiallytrapped',
self: {
volatileStatus: 'partialtrappinglock',
},
onTryMove(source, target) {
if (target.volatiles['mustrecharge']) {
target.removeVolatile('mustrecharge');
this.hint("In Gen 1, partial trapping moves negate the recharge turn of Hyper Beam, even if they miss.", true);
// FIXME: onBeforeMove(pokemon, target) {target.removeVolatile('mustrecharge')}
onHit(target, source) {
/**
* The duration of the partially trapped must be always renewed to 2
* so target doesn't move on trapper switch out as happens in gen 1.
* However, this won't happen if there's no switch and the trapper is
* about to end its partial trapping.
**/
if (target.volatiles['partiallytrapped']) {
if (source.volatiles['partialtrappinglock'] && source.volatiles['partialtrappinglock'].duration > 1) {
target.volatiles['partiallytrapped'].duration = 2;
}
}
},
},
@ -123,13 +132,22 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
inherit: true,
accuracy: 75,
pp: 10,
volatileStatus: 'partiallytrapped',
self: {
volatileStatus: 'partialtrappinglock',
},
onTryMove(source, target) {
if (target.volatiles['mustrecharge']) {
target.removeVolatile('mustrecharge');
this.hint("In Gen 1, partial trapping moves negate the recharge turn of Hyper Beam, even if they miss.", true);
// FIXME: onBeforeMove(pokemon, target) {target.removeVolatile('mustrecharge')}
onHit(target, source) {
/**
* The duration of the partially trapped must be always renewed to 2
* so target doesn't move on trapper switch out as happens in gen 1.
* However, this won't happen if there's no switch and the trapper is
* about to end its partial trapping.
**/
if (target.volatiles['partiallytrapped']) {
if (source.volatiles['partialtrappinglock'] && source.volatiles['partialtrappinglock'].duration > 1) {
target.volatiles['partiallytrapped'].duration = 2;
}
}
},
},
@ -147,7 +165,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
target: "normal",
onHit(target, source) {
source.setType(target.getTypes(true));
this.add('-start', source, 'typechange', source.types.join('/'), '[from] move: Conversion', `[of] ${target}`);
this.add('-start', source, 'typechange', source.types.join('/'), '[from] move: Conversion', '[of] ' + target);
},
},
counter: {
@ -162,14 +180,14 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
// - if Counter is used by the opponent, it will succeed if the player's last selected move is Counterable
// - (Counter will thus desync if the target's last used move is not as counterable as the target's last selected move)
// - if Counter succeeds it will deal twice the last move damage dealt in battle (even if it's from a different pokemon because of a switch)
const isCounterable = (move: Move | null) => move && move.basePower > 0 &&
['Normal', 'Fighting'].includes(move.type) && move.id !== 'counter';
const lastMove = target.side.lastMove && this.dex.moves.get(target.side.lastMove.id);
const lastMoveIsCounterable = isCounterable(lastMove);
const lastMoveIsCounterable = lastMove && lastMove.basePower > 0 &&
['Normal', 'Fighting'].includes(lastMove.type) && lastMove.id !== 'counter';
const lastSelectedMove = target.side.lastSelectedMove && this.dex.moves.get(target.side.lastSelectedMove);
const lastSelectedMoveIsCounterable = isCounterable(lastSelectedMove || null);
const lastSelectedMoveIsCounterable = lastSelectedMove && lastSelectedMove.basePower > 0 &&
['Normal', 'Fighting'].includes(lastSelectedMove.type) && lastSelectedMove.id !== 'counter';
if (!lastMoveIsCounterable && !lastSelectedMoveIsCounterable) {
this.debug("Gen 1 Counter: last move was not Counterable");
@ -189,7 +207,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
return 2 * this.lastDamage;
},
flags: { contact: 1, protect: 1, metronome: 1 },
},
crabhammer: {
inherit: true,
@ -211,24 +228,34 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
},
disable: {
inherit: true,
num: 50,
accuracy: 55,
basePower: 0,
category: "Status",
name: "Disable",
pp: 20,
priority: 0,
flags: {protect: 1, mirror: 1, bypasssub: 1},
volatileStatus: 'disable',
onTryHit(target) {
return target.moveSlots.some(ms => ms.pp > 0);
// This function should not return if the checks are met. Adding && undefined ensures this happens.
return target.moveSlots.some(ms => ms.pp > 0) &&
!('disable' in target.volatiles) &&
undefined;
},
condition: {
inherit: true,
durationCallback: undefined, // no inherit
onStart(pokemon) {
// disable can only select moves that have pp > 0, hence the onTryHit modification
const [slotIndex, moveSlot] = this.sample(Array.from(pokemon.moveSlots.entries()).filter(([i, ms]) => ms.pp > 0));
this.debug(`Disable: disabling move ${moveSlot.move} in slot ${slotIndex}`);
const moveSlot = this.sample(pokemon.moveSlots.filter(ms => ms.pp > 0));
this.add('-start', pokemon, 'Disable', moveSlot.move);
this.effectState.move = moveSlot.id;
this.effectState.slotIndex = slotIndex;
// 1-8 turns (which will in effect translate to 0-7 missed turns for the target)
this.effectState.time = this.random(1, 9);
},
onBeforeMovePriority: 6,
onEnd(pokemon) {
this.add('-end', pokemon, 'Disable');
},
onBeforeMovePriority: 7,
onBeforeMove(pokemon, target, move) {
pokemon.volatiles['disable'].time--;
if (!pokemon.volatiles['disable'].time) {
@ -243,17 +270,20 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
}
},
onDisableMove(pokemon) {
// disable the move slot
if (pokemon.moveSlots.length > this.effectState.slotIndex) {
pokemon.moveSlots[this.effectState.slotIndex].disabled = true;
pokemon.moveSlots[this.effectState.slotIndex].disabledSource = this.effect.name;
for (const moveSlot of pokemon.moveSlots) {
if (moveSlot.id === this.effectState.move) {
pokemon.disableMove(moveSlot.id);
}
}
},
},
secondary: null,
target: "normal",
type: "Normal",
},
dizzypunch: {
inherit: true,
secondary: undefined, // no inherit
secondary: null,
},
doubleedge: {
inherit: true,
@ -279,13 +309,22 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
inherit: true,
accuracy: 70,
basePower: 15,
volatileStatus: 'partiallytrapped',
self: {
volatileStatus: 'partialtrappinglock',
},
onTryMove(source, target) {
if (target.volatiles['mustrecharge']) {
target.removeVolatile('mustrecharge');
this.hint("In Gen 1, partial trapping moves negate the recharge turn of Hyper Beam, even if they miss.", true);
// FIXME: onBeforeMove(pokemon, target) {target.removeVolatile('mustrecharge')}
onHit(target, source) {
/**
* The duration of the partially trapped must be always renewed to 2
* so target doesn't move on trapper switch out as happens in gen 1.
* However, this won't happen if there's no switch and the trapper is
* about to end its partial trapping.
**/
if (target.volatiles['partiallytrapped']) {
if (source.volatiles['partialtrappinglock'] && source.volatiles['partialtrappinglock'].duration > 1) {
target.volatiles['partiallytrapped'].duration = 2;
}
}
},
},
@ -306,9 +345,11 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
focusenergy: {
inherit: true,
condition: {
inherit: true,
onStart(pokemon) {
this.add('-start', pokemon, 'move: Focus Energy');
},
// This does nothing as it's dealt with on critical hit calculation.
onModifyCritRatio: undefined, // no inherit
onModifyMove() {},
},
},
glare: {
@ -345,7 +386,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
// in-game, so it is equivalent to just clear it.
const silentHack = '|[silent]';
const silentHackVolatiles = ['disable', 'confusion'];
const hazeVolatiles: { [key: string]: string } = {
const hazeVolatiles: {[key: string]: string} = {
'disable': '',
'confusion': '',
'mist': 'Mist',
@ -391,9 +432,11 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
leechseed: {
inherit: true,
onHit: undefined, // no inherit
onHit() {},
condition: {
inherit: true,
onStart(target) {
this.add('-start', target, 'move: Leech Seed');
},
onAfterMoveSelfPriority: 1,
onAfterMoveSelf(pokemon) {
const leecher = this.getAtSlot(pokemon.volatiles['leechseed'].sourceSlot);
@ -419,9 +462,15 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
},
lightscreen: {
inherit: true,
num: 113,
accuracy: true,
basePower: 0,
category: "Status",
name: "Light Screen",
pp: 30,
priority: 0,
flags: {},
volatileStatus: 'lightscreen',
sideCondition: undefined, // no inherit
onTryHit(pokemon) {
if (pokemon.volatiles['lightscreen']) {
return false;
@ -433,28 +482,17 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
},
target: "self",
type: "Psychic",
},
metronome: {
inherit: true,
onHit(pokemon) {
const moves = this.dex.moves.all().filter(move => (
(!move.isNonstandard || move.isNonstandard === 'Unobtainable') && move.flags['metronome']
));
let randomMove = '';
if (moves.length) {
moves.sort((a, b) => a.num - b.num);
randomMove = this.sample(moves).id;
}
if (!randomMove) return false;
pokemon.side.lastSelectedMove = this.toID(randomMove);
this.actions.useMove(randomMove, pokemon);
},
noMetronome: ["Metronome", "Struggle"],
},
mimic: {
inherit: true,
flags: { protect: 1, bypasssub: 1, metronome: 1 },
onHit(target, source) {
const moveslot = source.side.lastSelectedMoveSlot;
const moveslot = source.moves.indexOf('mimic');
if (moveslot < 0) return false;
const moves = target.moves;
const moveid = this.sample(moves);
if (!moveid) return false;
@ -472,13 +510,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
this.add('-start', source, 'Mimic', move.name);
},
},
minimize: {
inherit: true,
condition: {
inherit: true,
onSourceModifyDamage: undefined, // no inherit
},
},
mirrormove: {
inherit: true,
onHit(pokemon) {
@ -493,7 +524,9 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
mist: {
inherit: true,
condition: {
inherit: true,
onStart(pokemon) {
this.add('-start', pokemon, 'Mist');
},
onTryBoost(boost, target, source, effect) {
if (effect.effectType === 'Move' && effect.category !== 'Status') return;
if (source && target !== source) {
@ -519,7 +552,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
petaldance: {
inherit: true,
onMoveFail: undefined, // no inherit
onMoveFail() {},
},
poisonsting: {
inherit: true,
@ -542,15 +575,9 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
inherit: true,
basePower: 1,
damageCallback(pokemon) {
if ([0, 1, 171].includes(pokemon.level)) {
this.hint("Desync Clause Mod activated!");
this.hint("In Gen 1, if a Pokémon at level 0, 1 or 171 uses Psywave, the game softlocks.");
return false;
}
const psywaveDamage = (this.random(0, this.trunc(1.5 * pokemon.level)));
if (psywaveDamage <= 0) {
this.hint("Desync Clause Mod activated!");
this.hint("In Gen 1, Psywave can roll 0 damage.");
return false;
}
return psywaveDamage;
@ -571,7 +598,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
onHit(target, source, move) {
// Disable and exploding moves boost Rage even if they miss/fail, so they are dealt with separately.
if (target.boosts.atk < 6 && (move.category !== 'Status' && !move.selfdestruct)) {
this.boost({ atk: 1 });
this.boost({atk: 1});
}
},
},
@ -597,27 +624,27 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
recover: {
inherit: true,
heal: undefined, // no inherit
heal: null,
onHit(target) {
if (target.hp === target.maxhp) return false;
// Fail when health is 255 or 511 less than max, unless it is divisible by 256
if (
target.hp === target.maxhp ||
((target.hp === (target.maxhp - 255) || target.hp === (target.maxhp - 511)) && target.hp % 256 !== 0)
) {
this.hint(
"In Gen 1, recovery moves fail if (user's maximum HP - user's current HP + 1) is divisible by 256, " +
"unless the current hp is also divisible by 256."
);
// Fail when health is 255 or 511 less than max
if (target.hp === (target.maxhp - 255) || target.hp === (target.maxhp - 511) || target.hp === target.maxhp) {
this.hint("In Gen 1, recovery moves fail if (user's maximum HP - user's current HP + 1) is divisible by 256.");
return false;
}
this.heal(Math.floor(target.maxhp / 2), target, target);
},
},
reflect: {
inherit: true,
num: 115,
accuracy: true,
basePower: 0,
category: "Status",
name: "Reflect",
pp: 20,
priority: 0,
flags: {},
volatileStatus: 'reflect',
sideCondition: undefined, // no inherit
onTryHit(pokemon) {
if (pokemon.volatiles['reflect']) {
return false;
@ -628,22 +655,18 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
this.add('-start', pokemon, 'Reflect');
},
},
secondary: null,
target: "self",
type: "Psychic",
},
rest: {
inherit: true,
onTry: undefined, // no inherit
onTry() {},
onHit(target, source, move) {
if (target.hp === target.maxhp) return false;
// Fail when health is 255 or 511 less than max, unless it is divisible by 256
if (
target.hp === target.maxhp ||
((target.hp === (target.maxhp - 255) || target.hp === (target.maxhp - 511)) && target.hp % 256 !== 0)
) {
this.hint(
"In Gen 1, recovery moves fail if (user's maximum HP - user's current HP + 1) is divisible by 256, " +
"unless the current hp is also divisible by 256."
);
// Fail when health is 255 or 511 less than max
if (target.hp === (target.maxhp - 255) || target.hp === (target.maxhp - 511)) {
this.hint("In Gen 1, recovery moves fail if (user's maximum HP - user's current HP + 1) is divisible by 256.");
return false;
}
if (!target.setStatus('slp', source, move)) return false;
@ -655,12 +678,12 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
roar: {
inherit: true,
forceSwitch: false,
onTryHit: undefined, // no inherit
onTryHit() {},
priority: 0,
},
rockslide: {
inherit: true,
secondary: undefined, // no inherit
secondary: null,
target: "normal",
},
rockthrow: {
@ -736,18 +759,12 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
softboiled: {
inherit: true,
heal: undefined, // no inherit
heal: null,
onHit(target) {
if (target.hp === target.maxhp) return false;
// Fail when health is 255 or 511 less than max, unless it is divisible by 256
if (
target.hp === target.maxhp ||
((target.hp === (target.maxhp - 255) || target.hp === (target.maxhp - 511)) && target.hp % 256 !== 0)
) {
this.hint(
"In Gen 1, recovery moves fail if (user's maximum HP - user's current HP + 1) is divisible by 256, " +
"unless the current hp is also divisible by 256."
);
// Fail when health is 255 or 511 less than max
if (target.hp === (target.maxhp - 255) || target.hp === (target.maxhp - 511) || target.hp === target.maxhp) {
this.hint("In Gen 1, recovery moves fail if (user's maximum HP - user's current HP + 1) is divisible by 256.");
return false;
}
this.heal(Math.floor(target.maxhp / 2), target, target);
@ -757,10 +774,17 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
inherit: true,
pp: 10,
recoil: [1, 2],
onModifyMove: undefined, // no inherit
onModifyMove() {},
},
substitute: {
inherit: true,
num: 164,
accuracy: true,
basePower: 0,
category: "Status",
name: "Substitute",
pp: 10,
priority: 0,
volatileStatus: 'substitute',
onTryHit(target) {
if (target.volatiles['substitute']) {
this.add('-fail', target, 'move: Substitute');
@ -780,16 +804,11 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
}
},
condition: {
inherit: true,
onStart(target) {
this.add('-start', target, 'Substitute');
this.effectState.hp = Math.floor(target.maxhp / 4) + 1;
if (target.volatiles['partiallytrapped']) {
this.add('-end', target, target.volatiles['partiallytrapped'].sourceEffect, '[partiallytrapped]', '[silent]');
delete target.volatiles['partiallytrapped'];
}
delete target.volatiles['partiallytrapped'];
},
onTryPrimaryHit: undefined, // no inherit
onTryHitPriority: -1,
onTryHit(target, source, move) {
if (move.category === 'Status') {
@ -811,6 +830,8 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
let uncappedDamage = move.hit > 1 ? this.lastDamage : this.actions.getDamage(source, target, move);
if (move.id === 'bide') uncappedDamage = source.volatiles['bide'].damage * 2;
if (!uncappedDamage && uncappedDamage !== 0) return null;
uncappedDamage = this.runEvent('SubDamage', target, source, move, uncappedDamage);
if (!uncappedDamage && uncappedDamage !== 0) return uncappedDamage;
this.lastDamage = uncappedDamage;
target.volatiles['substitute'].hp -= uncappedDamage > target.volatiles['substitute'].hp ?
target.volatiles['substitute'].hp : uncappedDamage;
@ -823,8 +844,8 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
// Drain/recoil/secondary effect confusion do not happen if the substitute breaks
if (target.volatiles['substitute']) {
if (move.recoil) {
this.damage(this.clampIntRange(Math.floor(uncappedDamage * move.recoil[0] / move.recoil[1]), 1),
source, target, 'recoil');
this.damage(this.clampIntRange(Math.floor(uncappedDamage * move.recoil[0] / move.recoil[1]), 1)
, source, target, 'recoil');
}
if (move.drain) {
const amount = this.clampIntRange(Math.floor(uncappedDamage * move.drain[0] / move.drain[1]), 1);
@ -846,14 +867,21 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
// Add here counter damage
const lastAttackedBy = target.getLastAttackedBy();
if (!lastAttackedBy) {
target.attackedBy.push({ source, move: move.id, damage: uncappedDamage, slot: source.getSlot(), thisTurn: true });
target.attackedBy.push({source: source, move: move.id, damage: uncappedDamage, slot: source.getSlot(), thisTurn: true});
} else {
lastAttackedBy.move = move.id;
lastAttackedBy.damage = uncappedDamage;
}
return 0;
},
onEnd(target) {
this.add('-end', target, 'Substitute');
},
},
secondary: null,
target: "self",
type: "Normal",
flags: {},
},
superfang: {
inherit: true,
@ -862,7 +890,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
thrash: {
inherit: true,
onMoveFail: undefined, // no inherit
onMoveFail() {},
},
thunder: {
inherit: true,
@ -873,14 +901,14 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
triattack: {
inherit: true,
onHit: undefined, // no inherit
secondary: undefined, // no inherit
onHit() {},
secondary: null,
},
whirlwind: {
inherit: true,
accuracy: 85,
forceSwitch: false,
onTryHit: undefined, // no inherit
onTryHit() {},
priority: 0,
},
wingattack: {
@ -891,13 +919,22 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
inherit: true,
accuracy: 85,
ignoreImmunity: true,
volatileStatus: 'partiallytrapped',
self: {
volatileStatus: 'partialtrappinglock',
},
onTryMove(source, target) {
if (target.volatiles['mustrecharge']) {
target.removeVolatile('mustrecharge');
this.hint("In Gen 1, partial trapping moves negate the recharge turn of Hyper Beam, even if they miss.", true);
// FIXME: onBeforeMove(pokemon, target) {target.removeVolatile('mustrecharge')}
onHit(target, source) {
/**
* The duration of the partially trapped must be always renewed to 2
* so target doesn't move on trapper switch out as happens in gen 1.
* However, this won't happen if there's no switch and the trapper is
* about to end its partial trapping.
**/
if (target.volatiles['partiallytrapped']) {
if (source.volatiles['partialtrappinglock'] && source.volatiles['partialtrappinglock'].duration > 1) {
target.volatiles['partiallytrapped'].duration = 2;
}
}
},
},

View File

@ -1,612 +1,612 @@
export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable = {
export const Pokedex: {[k: string]: ModdedSpeciesData} = {
missingno: {
inherit: true,
baseStats: { hp: 33, atk: 136, def: 0, spa: 6, spd: 6, spe: 29 },
baseStats: {hp: 33, atk: 136, def: 0, spa: 6, spd: 6, spe: 29},
},
bulbasaur: {
inherit: true,
baseStats: { hp: 45, atk: 49, def: 49, spa: 65, spd: 65, spe: 45 },
baseStats: {hp: 45, atk: 49, def: 49, spa: 65, spd: 65, spe: 45},
},
ivysaur: {
inherit: true,
baseStats: { hp: 60, atk: 62, def: 63, spa: 80, spd: 80, spe: 60 },
baseStats: {hp: 60, atk: 62, def: 63, spa: 80, spd: 80, spe: 60},
},
venusaur: {
inherit: true,
baseStats: { hp: 80, atk: 82, def: 83, spa: 100, spd: 100, spe: 80 },
baseStats: {hp: 80, atk: 82, def: 83, spa: 100, spd: 100, spe: 80},
},
charmander: {
inherit: true,
baseStats: { hp: 39, atk: 52, def: 43, spa: 50, spd: 50, spe: 65 },
baseStats: {hp: 39, atk: 52, def: 43, spa: 50, spd: 50, spe: 65},
},
charmeleon: {
inherit: true,
baseStats: { hp: 58, atk: 64, def: 58, spa: 65, spd: 65, spe: 80 },
baseStats: {hp: 58, atk: 64, def: 58, spa: 65, spd: 65, spe: 80},
},
charizard: {
inherit: true,
baseStats: { hp: 78, atk: 84, def: 78, spa: 85, spd: 85, spe: 100 },
baseStats: {hp: 78, atk: 84, def: 78, spa: 85, spd: 85, spe: 100},
},
squirtle: {
inherit: true,
baseStats: { hp: 44, atk: 48, def: 65, spa: 50, spd: 50, spe: 43 },
baseStats: {hp: 44, atk: 48, def: 65, spa: 50, spd: 50, spe: 43},
},
wartortle: {
inherit: true,
baseStats: { hp: 59, atk: 63, def: 80, spa: 65, spd: 65, spe: 58 },
baseStats: {hp: 59, atk: 63, def: 80, spa: 65, spd: 65, spe: 58},
},
blastoise: {
inherit: true,
baseStats: { hp: 79, atk: 83, def: 100, spa: 85, spd: 85, spe: 78 },
baseStats: {hp: 79, atk: 83, def: 100, spa: 85, spd: 85, spe: 78},
},
caterpie: {
inherit: true,
baseStats: { hp: 45, atk: 30, def: 35, spa: 20, spd: 20, spe: 45 },
baseStats: {hp: 45, atk: 30, def: 35, spa: 20, spd: 20, spe: 45},
},
metapod: {
inherit: true,
baseStats: { hp: 50, atk: 20, def: 55, spa: 25, spd: 25, spe: 30 },
baseStats: {hp: 50, atk: 20, def: 55, spa: 25, spd: 25, spe: 30},
},
butterfree: {
inherit: true,
baseStats: { hp: 60, atk: 45, def: 50, spa: 80, spd: 80, spe: 70 },
baseStats: {hp: 60, atk: 45, def: 50, spa: 80, spd: 80, spe: 70},
},
weedle: {
inherit: true,
baseStats: { hp: 40, atk: 35, def: 30, spa: 20, spd: 20, spe: 50 },
baseStats: {hp: 40, atk: 35, def: 30, spa: 20, spd: 20, spe: 50},
},
kakuna: {
inherit: true,
baseStats: { hp: 45, atk: 25, def: 50, spa: 25, spd: 25, spe: 35 },
baseStats: {hp: 45, atk: 25, def: 50, spa: 25, spd: 25, spe: 35},
},
beedrill: {
inherit: true,
baseStats: { hp: 65, atk: 80, def: 40, spa: 45, spd: 45, spe: 75 },
baseStats: {hp: 65, atk: 80, def: 40, spa: 45, spd: 45, spe: 75},
},
pidgey: {
inherit: true,
baseStats: { hp: 40, atk: 45, def: 40, spa: 35, spd: 35, spe: 56 },
baseStats: {hp: 40, atk: 45, def: 40, spa: 35, spd: 35, spe: 56},
},
pidgeotto: {
inherit: true,
baseStats: { hp: 63, atk: 60, def: 55, spa: 50, spd: 50, spe: 71 },
baseStats: {hp: 63, atk: 60, def: 55, spa: 50, spd: 50, spe: 71},
},
pidgeot: {
inherit: true,
baseStats: { hp: 83, atk: 80, def: 75, spa: 70, spd: 70, spe: 91 },
baseStats: {hp: 83, atk: 80, def: 75, spa: 70, spd: 70, spe: 91},
},
rattata: {
inherit: true,
baseStats: { hp: 30, atk: 56, def: 35, spa: 25, spd: 25, spe: 72 },
baseStats: {hp: 30, atk: 56, def: 35, spa: 25, spd: 25, spe: 72},
},
raticate: {
inherit: true,
baseStats: { hp: 55, atk: 81, def: 60, spa: 50, spd: 50, spe: 97 },
baseStats: {hp: 55, atk: 81, def: 60, spa: 50, spd: 50, spe: 97},
},
spearow: {
inherit: true,
baseStats: { hp: 40, atk: 60, def: 30, spa: 31, spd: 31, spe: 70 },
baseStats: {hp: 40, atk: 60, def: 30, spa: 31, spd: 31, spe: 70},
},
fearow: {
inherit: true,
baseStats: { hp: 65, atk: 90, def: 65, spa: 61, spd: 61, spe: 100 },
baseStats: {hp: 65, atk: 90, def: 65, spa: 61, spd: 61, spe: 100},
},
ekans: {
inherit: true,
baseStats: { hp: 35, atk: 60, def: 44, spa: 40, spd: 40, spe: 55 },
baseStats: {hp: 35, atk: 60, def: 44, spa: 40, spd: 40, spe: 55},
},
arbok: {
inherit: true,
baseStats: { hp: 60, atk: 85, def: 69, spa: 65, spd: 65, spe: 80 },
baseStats: {hp: 60, atk: 85, def: 69, spa: 65, spd: 65, spe: 80},
},
pikachu: {
inherit: true,
baseStats: { hp: 35, atk: 55, def: 30, spa: 50, spd: 50, spe: 90 },
baseStats: {hp: 35, atk: 55, def: 30, spa: 50, spd: 50, spe: 90},
},
raichu: {
inherit: true,
baseStats: { hp: 60, atk: 90, def: 55, spa: 90, spd: 90, spe: 100 },
baseStats: {hp: 60, atk: 90, def: 55, spa: 90, spd: 90, spe: 100},
},
sandshrew: {
inherit: true,
baseStats: { hp: 50, atk: 75, def: 85, spa: 30, spd: 30, spe: 40 },
baseStats: {hp: 50, atk: 75, def: 85, spa: 30, spd: 30, spe: 40},
},
sandslash: {
inherit: true,
baseStats: { hp: 75, atk: 100, def: 110, spa: 55, spd: 55, spe: 65 },
baseStats: {hp: 75, atk: 100, def: 110, spa: 55, spd: 55, spe: 65},
},
nidoranf: {
inherit: true,
baseStats: { hp: 55, atk: 47, def: 52, spa: 40, spd: 40, spe: 41 },
baseStats: {hp: 55, atk: 47, def: 52, spa: 40, spd: 40, spe: 41},
},
nidorina: {
inherit: true,
baseStats: { hp: 70, atk: 62, def: 67, spa: 55, spd: 55, spe: 56 },
baseStats: {hp: 70, atk: 62, def: 67, spa: 55, spd: 55, spe: 56},
},
nidoqueen: {
inherit: true,
baseStats: { hp: 90, atk: 82, def: 87, spa: 75, spd: 75, spe: 76 },
baseStats: {hp: 90, atk: 82, def: 87, spa: 75, spd: 75, spe: 76},
},
nidoranm: {
inherit: true,
baseStats: { hp: 46, atk: 57, def: 40, spa: 40, spd: 40, spe: 50 },
baseStats: {hp: 46, atk: 57, def: 40, spa: 40, spd: 40, spe: 50},
},
nidorino: {
inherit: true,
baseStats: { hp: 61, atk: 72, def: 57, spa: 55, spd: 55, spe: 65 },
baseStats: {hp: 61, atk: 72, def: 57, spa: 55, spd: 55, spe: 65},
},
nidoking: {
inherit: true,
baseStats: { hp: 81, atk: 92, def: 77, spa: 75, spd: 75, spe: 85 },
baseStats: {hp: 81, atk: 92, def: 77, spa: 75, spd: 75, spe: 85},
},
clefairy: {
inherit: true,
baseStats: { hp: 70, atk: 45, def: 48, spa: 60, spd: 60, spe: 35 },
baseStats: {hp: 70, atk: 45, def: 48, spa: 60, spd: 60, spe: 35},
},
clefable: {
inherit: true,
baseStats: { hp: 95, atk: 70, def: 73, spa: 85, spd: 85, spe: 60 },
baseStats: {hp: 95, atk: 70, def: 73, spa: 85, spd: 85, spe: 60},
},
vulpix: {
inherit: true,
baseStats: { hp: 38, atk: 41, def: 40, spa: 65, spd: 65, spe: 65 },
baseStats: {hp: 38, atk: 41, def: 40, spa: 65, spd: 65, spe: 65},
},
ninetales: {
inherit: true,
baseStats: { hp: 73, atk: 76, def: 75, spa: 100, spd: 100, spe: 100 },
baseStats: {hp: 73, atk: 76, def: 75, spa: 100, spd: 100, spe: 100},
},
jigglypuff: {
inherit: true,
baseStats: { hp: 115, atk: 45, def: 20, spa: 25, spd: 25, spe: 20 },
baseStats: {hp: 115, atk: 45, def: 20, spa: 25, spd: 25, spe: 20},
},
wigglytuff: {
inherit: true,
baseStats: { hp: 140, atk: 70, def: 45, spa: 50, spd: 50, spe: 45 },
baseStats: {hp: 140, atk: 70, def: 45, spa: 50, spd: 50, spe: 45},
},
zubat: {
inherit: true,
baseStats: { hp: 40, atk: 45, def: 35, spa: 40, spd: 40, spe: 55 },
baseStats: {hp: 40, atk: 45, def: 35, spa: 40, spd: 40, spe: 55},
},
golbat: {
inherit: true,
baseStats: { hp: 75, atk: 80, def: 70, spa: 75, spd: 75, spe: 90 },
baseStats: {hp: 75, atk: 80, def: 70, spa: 75, spd: 75, spe: 90},
},
oddish: {
inherit: true,
baseStats: { hp: 45, atk: 50, def: 55, spa: 75, spd: 75, spe: 30 },
baseStats: {hp: 45, atk: 50, def: 55, spa: 75, spd: 75, spe: 30},
},
gloom: {
inherit: true,
baseStats: { hp: 60, atk: 65, def: 70, spa: 85, spd: 85, spe: 40 },
baseStats: {hp: 60, atk: 65, def: 70, spa: 85, spd: 85, spe: 40},
},
vileplume: {
inherit: true,
baseStats: { hp: 75, atk: 80, def: 85, spa: 100, spd: 100, spe: 50 },
baseStats: {hp: 75, atk: 80, def: 85, spa: 100, spd: 100, spe: 50},
},
paras: {
inherit: true,
baseStats: { hp: 35, atk: 70, def: 55, spa: 55, spd: 55, spe: 25 },
baseStats: {hp: 35, atk: 70, def: 55, spa: 55, spd: 55, spe: 25},
},
parasect: {
inherit: true,
baseStats: { hp: 60, atk: 95, def: 80, spa: 80, spd: 80, spe: 30 },
baseStats: {hp: 60, atk: 95, def: 80, spa: 80, spd: 80, spe: 30},
},
venonat: {
inherit: true,
baseStats: { hp: 60, atk: 55, def: 50, spa: 40, spd: 40, spe: 45 },
baseStats: {hp: 60, atk: 55, def: 50, spa: 40, spd: 40, spe: 45},
},
venomoth: {
inherit: true,
baseStats: { hp: 70, atk: 65, def: 60, spa: 90, spd: 90, spe: 90 },
baseStats: {hp: 70, atk: 65, def: 60, spa: 90, spd: 90, spe: 90},
},
diglett: {
inherit: true,
baseStats: { hp: 10, atk: 55, def: 25, spa: 45, spd: 45, spe: 95 },
baseStats: {hp: 10, atk: 55, def: 25, spa: 45, spd: 45, spe: 95},
},
dugtrio: {
inherit: true,
baseStats: { hp: 35, atk: 80, def: 50, spa: 70, spd: 70, spe: 120 },
baseStats: {hp: 35, atk: 80, def: 50, spa: 70, spd: 70, spe: 120},
},
meowth: {
inherit: true,
baseStats: { hp: 40, atk: 45, def: 35, spa: 40, spd: 40, spe: 90 },
baseStats: {hp: 40, atk: 45, def: 35, spa: 40, spd: 40, spe: 90},
},
persian: {
inherit: true,
baseStats: { hp: 65, atk: 70, def: 60, spa: 65, spd: 65, spe: 115 },
baseStats: {hp: 65, atk: 70, def: 60, spa: 65, spd: 65, spe: 115},
},
psyduck: {
inherit: true,
baseStats: { hp: 50, atk: 52, def: 48, spa: 50, spd: 50, spe: 55 },
baseStats: {hp: 50, atk: 52, def: 48, spa: 50, spd: 50, spe: 55},
},
golduck: {
inherit: true,
baseStats: { hp: 80, atk: 82, def: 78, spa: 80, spd: 80, spe: 85 },
baseStats: {hp: 80, atk: 82, def: 78, spa: 80, spd: 80, spe: 85},
},
mankey: {
inherit: true,
baseStats: { hp: 40, atk: 80, def: 35, spa: 35, spd: 35, spe: 70 },
baseStats: {hp: 40, atk: 80, def: 35, spa: 35, spd: 35, spe: 70},
},
primeape: {
inherit: true,
baseStats: { hp: 65, atk: 105, def: 60, spa: 60, spd: 60, spe: 95 },
baseStats: {hp: 65, atk: 105, def: 60, spa: 60, spd: 60, spe: 95},
},
growlithe: {
inherit: true,
baseStats: { hp: 55, atk: 70, def: 45, spa: 50, spd: 50, spe: 60 },
baseStats: {hp: 55, atk: 70, def: 45, spa: 50, spd: 50, spe: 60},
},
arcanine: {
inherit: true,
baseStats: { hp: 90, atk: 110, def: 80, spa: 80, spd: 80, spe: 95 },
baseStats: {hp: 90, atk: 110, def: 80, spa: 80, spd: 80, spe: 95},
},
poliwag: {
inherit: true,
baseStats: { hp: 40, atk: 50, def: 40, spa: 40, spd: 40, spe: 90 },
baseStats: {hp: 40, atk: 50, def: 40, spa: 40, spd: 40, spe: 90},
},
poliwhirl: {
inherit: true,
baseStats: { hp: 65, atk: 65, def: 65, spa: 50, spd: 50, spe: 90 },
baseStats: {hp: 65, atk: 65, def: 65, spa: 50, spd: 50, spe: 90},
},
poliwrath: {
inherit: true,
baseStats: { hp: 90, atk: 85, def: 95, spa: 70, spd: 70, spe: 70 },
baseStats: {hp: 90, atk: 85, def: 95, spa: 70, spd: 70, spe: 70},
},
abra: {
inherit: true,
baseStats: { hp: 25, atk: 20, def: 15, spa: 105, spd: 105, spe: 90 },
baseStats: {hp: 25, atk: 20, def: 15, spa: 105, spd: 105, spe: 90},
},
kadabra: {
inherit: true,
baseStats: { hp: 40, atk: 35, def: 30, spa: 120, spd: 120, spe: 105 },
baseStats: {hp: 40, atk: 35, def: 30, spa: 120, spd: 120, spe: 105},
},
alakazam: {
inherit: true,
baseStats: { hp: 55, atk: 50, def: 45, spa: 135, spd: 135, spe: 120 },
baseStats: {hp: 55, atk: 50, def: 45, spa: 135, spd: 135, spe: 120},
},
machop: {
inherit: true,
baseStats: { hp: 70, atk: 80, def: 50, spa: 35, spd: 35, spe: 35 },
baseStats: {hp: 70, atk: 80, def: 50, spa: 35, spd: 35, spe: 35},
},
machoke: {
inherit: true,
baseStats: { hp: 80, atk: 100, def: 70, spa: 50, spd: 50, spe: 45 },
baseStats: {hp: 80, atk: 100, def: 70, spa: 50, spd: 50, spe: 45},
},
machamp: {
inherit: true,
baseStats: { hp: 90, atk: 130, def: 80, spa: 65, spd: 65, spe: 55 },
baseStats: {hp: 90, atk: 130, def: 80, spa: 65, spd: 65, spe: 55},
},
bellsprout: {
inherit: true,
baseStats: { hp: 50, atk: 75, def: 35, spa: 70, spd: 70, spe: 40 },
baseStats: {hp: 50, atk: 75, def: 35, spa: 70, spd: 70, spe: 40},
},
weepinbell: {
inherit: true,
baseStats: { hp: 65, atk: 90, def: 50, spa: 85, spd: 85, spe: 55 },
baseStats: {hp: 65, atk: 90, def: 50, spa: 85, spd: 85, spe: 55},
},
victreebel: {
inherit: true,
baseStats: { hp: 80, atk: 105, def: 65, spa: 100, spd: 100, spe: 70 },
baseStats: {hp: 80, atk: 105, def: 65, spa: 100, spd: 100, spe: 70},
},
tentacool: {
inherit: true,
baseStats: { hp: 40, atk: 40, def: 35, spa: 100, spd: 100, spe: 70 },
baseStats: {hp: 40, atk: 40, def: 35, spa: 100, spd: 100, spe: 70},
},
tentacruel: {
inherit: true,
baseStats: { hp: 80, atk: 70, def: 65, spa: 120, spd: 120, spe: 100 },
baseStats: {hp: 80, atk: 70, def: 65, spa: 120, spd: 120, spe: 100},
},
geodude: {
inherit: true,
baseStats: { hp: 40, atk: 80, def: 100, spa: 30, spd: 30, spe: 20 },
baseStats: {hp: 40, atk: 80, def: 100, spa: 30, spd: 30, spe: 20},
},
graveler: {
inherit: true,
baseStats: { hp: 55, atk: 95, def: 115, spa: 45, spd: 45, spe: 35 },
baseStats: {hp: 55, atk: 95, def: 115, spa: 45, spd: 45, spe: 35},
},
golem: {
inherit: true,
baseStats: { hp: 80, atk: 110, def: 130, spa: 55, spd: 55, spe: 45 },
baseStats: {hp: 80, atk: 110, def: 130, spa: 55, spd: 55, spe: 45},
},
ponyta: {
inherit: true,
baseStats: { hp: 50, atk: 85, def: 55, spa: 65, spd: 65, spe: 90 },
baseStats: {hp: 50, atk: 85, def: 55, spa: 65, spd: 65, spe: 90},
},
rapidash: {
inherit: true,
baseStats: { hp: 65, atk: 100, def: 70, spa: 80, spd: 80, spe: 105 },
baseStats: {hp: 65, atk: 100, def: 70, spa: 80, spd: 80, spe: 105},
},
slowpoke: {
inherit: true,
baseStats: { hp: 90, atk: 65, def: 65, spa: 40, spd: 40, spe: 15 },
baseStats: {hp: 90, atk: 65, def: 65, spa: 40, spd: 40, spe: 15},
},
slowbro: {
inherit: true,
baseStats: { hp: 95, atk: 75, def: 110, spa: 80, spd: 80, spe: 30 },
baseStats: {hp: 95, atk: 75, def: 110, spa: 80, spd: 80, spe: 30},
},
magnemite: {
inherit: true,
types: ["Electric"],
baseStats: { hp: 25, atk: 35, def: 70, spa: 95, spd: 95, spe: 45 },
baseStats: {hp: 25, atk: 35, def: 70, spa: 95, spd: 95, spe: 45},
},
magneton: {
inherit: true,
types: ["Electric"],
baseStats: { hp: 50, atk: 60, def: 95, spa: 120, spd: 120, spe: 70 },
baseStats: {hp: 50, atk: 60, def: 95, spa: 120, spd: 120, spe: 70},
},
farfetchd: {
inherit: true,
baseStats: { hp: 52, atk: 65, def: 55, spa: 58, spd: 58, spe: 60 },
baseStats: {hp: 52, atk: 65, def: 55, spa: 58, spd: 58, spe: 60},
},
doduo: {
inherit: true,
baseStats: { hp: 35, atk: 85, def: 45, spa: 35, spd: 35, spe: 75 },
baseStats: {hp: 35, atk: 85, def: 45, spa: 35, spd: 35, spe: 75},
},
dodrio: {
inherit: true,
baseStats: { hp: 60, atk: 110, def: 70, spa: 60, spd: 60, spe: 100 },
baseStats: {hp: 60, atk: 110, def: 70, spa: 60, spd: 60, spe: 100},
},
seel: {
inherit: true,
baseStats: { hp: 65, atk: 45, def: 55, spa: 70, spd: 70, spe: 45 },
baseStats: {hp: 65, atk: 45, def: 55, spa: 70, spd: 70, spe: 45},
},
dewgong: {
inherit: true,
baseStats: { hp: 90, atk: 70, def: 80, spa: 95, spd: 95, spe: 70 },
baseStats: {hp: 90, atk: 70, def: 80, spa: 95, spd: 95, spe: 70},
},
grimer: {
inherit: true,
baseStats: { hp: 80, atk: 80, def: 50, spa: 40, spd: 40, spe: 25 },
baseStats: {hp: 80, atk: 80, def: 50, spa: 40, spd: 40, spe: 25},
},
muk: {
inherit: true,
baseStats: { hp: 105, atk: 105, def: 75, spa: 65, spd: 65, spe: 50 },
baseStats: {hp: 105, atk: 105, def: 75, spa: 65, spd: 65, spe: 50},
},
shellder: {
inherit: true,
baseStats: { hp: 30, atk: 65, def: 100, spa: 45, spd: 45, spe: 40 },
baseStats: {hp: 30, atk: 65, def: 100, spa: 45, spd: 45, spe: 40},
},
cloyster: {
inherit: true,
baseStats: { hp: 50, atk: 95, def: 180, spa: 85, spd: 85, spe: 70 },
baseStats: {hp: 50, atk: 95, def: 180, spa: 85, spd: 85, spe: 70},
},
gastly: {
inherit: true,
baseStats: { hp: 30, atk: 35, def: 30, spa: 100, spd: 100, spe: 80 },
baseStats: {hp: 30, atk: 35, def: 30, spa: 100, spd: 100, spe: 80},
},
haunter: {
inherit: true,
baseStats: { hp: 45, atk: 50, def: 45, spa: 115, spd: 115, spe: 95 },
baseStats: {hp: 45, atk: 50, def: 45, spa: 115, spd: 115, spe: 95},
},
gengar: {
inherit: true,
baseStats: { hp: 60, atk: 65, def: 60, spa: 130, spd: 130, spe: 110 },
baseStats: {hp: 60, atk: 65, def: 60, spa: 130, spd: 130, spe: 110},
},
onix: {
inherit: true,
baseStats: { hp: 35, atk: 45, def: 160, spa: 30, spd: 30, spe: 70 },
baseStats: {hp: 35, atk: 45, def: 160, spa: 30, spd: 30, spe: 70},
},
drowzee: {
inherit: true,
baseStats: { hp: 60, atk: 48, def: 45, spa: 90, spd: 90, spe: 42 },
baseStats: {hp: 60, atk: 48, def: 45, spa: 90, spd: 90, spe: 42},
},
hypno: {
inherit: true,
baseStats: { hp: 85, atk: 73, def: 70, spa: 115, spd: 115, spe: 67 },
baseStats: {hp: 85, atk: 73, def: 70, spa: 115, spd: 115, spe: 67},
},
krabby: {
inherit: true,
baseStats: { hp: 30, atk: 105, def: 90, spa: 25, spd: 25, spe: 50 },
baseStats: {hp: 30, atk: 105, def: 90, spa: 25, spd: 25, spe: 50},
},
kingler: {
inherit: true,
baseStats: { hp: 55, atk: 130, def: 115, spa: 50, spd: 50, spe: 75 },
baseStats: {hp: 55, atk: 130, def: 115, spa: 50, spd: 50, spe: 75},
},
voltorb: {
inherit: true,
baseStats: { hp: 40, atk: 30, def: 50, spa: 55, spd: 55, spe: 100 },
baseStats: {hp: 40, atk: 30, def: 50, spa: 55, spd: 55, spe: 100},
},
electrode: {
inherit: true,
baseStats: { hp: 60, atk: 50, def: 70, spa: 80, spd: 80, spe: 140 },
baseStats: {hp: 60, atk: 50, def: 70, spa: 80, spd: 80, spe: 140},
},
exeggcute: {
inherit: true,
baseStats: { hp: 60, atk: 40, def: 80, spa: 60, spd: 60, spe: 40 },
baseStats: {hp: 60, atk: 40, def: 80, spa: 60, spd: 60, spe: 40},
},
exeggutor: {
inherit: true,
baseStats: { hp: 95, atk: 95, def: 85, spa: 125, spd: 125, spe: 55 },
baseStats: {hp: 95, atk: 95, def: 85, spa: 125, spd: 125, spe: 55},
},
cubone: {
inherit: true,
baseStats: { hp: 50, atk: 50, def: 95, spa: 40, spd: 40, spe: 35 },
baseStats: {hp: 50, atk: 50, def: 95, spa: 40, spd: 40, spe: 35},
},
marowak: {
inherit: true,
baseStats: { hp: 60, atk: 80, def: 110, spa: 50, spd: 50, spe: 45 },
baseStats: {hp: 60, atk: 80, def: 110, spa: 50, spd: 50, spe: 45},
},
hitmonlee: {
inherit: true,
baseStats: { hp: 50, atk: 120, def: 53, spa: 35, spd: 35, spe: 87 },
baseStats: {hp: 50, atk: 120, def: 53, spa: 35, spd: 35, spe: 87},
},
hitmonchan: {
inherit: true,
baseStats: { hp: 50, atk: 105, def: 79, spa: 35, spd: 35, spe: 76 },
baseStats: {hp: 50, atk: 105, def: 79, spa: 35, spd: 35, spe: 76},
},
lickitung: {
inherit: true,
baseStats: { hp: 90, atk: 55, def: 75, spa: 60, spd: 60, spe: 30 },
baseStats: {hp: 90, atk: 55, def: 75, spa: 60, spd: 60, spe: 30},
},
koffing: {
inherit: true,
baseStats: { hp: 40, atk: 65, def: 95, spa: 60, spd: 60, spe: 35 },
baseStats: {hp: 40, atk: 65, def: 95, spa: 60, spd: 60, spe: 35},
},
weezing: {
inherit: true,
baseStats: { hp: 65, atk: 90, def: 120, spa: 85, spd: 85, spe: 60 },
baseStats: {hp: 65, atk: 90, def: 120, spa: 85, spd: 85, spe: 60},
},
rhyhorn: {
inherit: true,
baseStats: { hp: 80, atk: 85, def: 95, spa: 30, spd: 30, spe: 25 },
baseStats: {hp: 80, atk: 85, def: 95, spa: 30, spd: 30, spe: 25},
},
rhydon: {
inherit: true,
baseStats: { hp: 105, atk: 130, def: 120, spa: 45, spd: 45, spe: 40 },
baseStats: {hp: 105, atk: 130, def: 120, spa: 45, spd: 45, spe: 40},
},
chansey: {
inherit: true,
baseStats: { hp: 250, atk: 5, def: 5, spa: 105, spd: 105, spe: 50 },
baseStats: {hp: 250, atk: 5, def: 5, spa: 105, spd: 105, spe: 50},
},
tangela: {
inherit: true,
baseStats: { hp: 65, atk: 55, def: 115, spa: 100, spd: 100, spe: 60 },
baseStats: {hp: 65, atk: 55, def: 115, spa: 100, spd: 100, spe: 60},
},
kangaskhan: {
inherit: true,
baseStats: { hp: 105, atk: 95, def: 80, spa: 40, spd: 40, spe: 90 },
baseStats: {hp: 105, atk: 95, def: 80, spa: 40, spd: 40, spe: 90},
},
horsea: {
inherit: true,
baseStats: { hp: 30, atk: 40, def: 70, spa: 70, spd: 70, spe: 60 },
baseStats: {hp: 30, atk: 40, def: 70, spa: 70, spd: 70, spe: 60},
},
seadra: {
inherit: true,
baseStats: { hp: 55, atk: 65, def: 95, spa: 95, spd: 95, spe: 85 },
baseStats: {hp: 55, atk: 65, def: 95, spa: 95, spd: 95, spe: 85},
},
goldeen: {
inherit: true,
baseStats: { hp: 45, atk: 67, def: 60, spa: 50, spd: 50, spe: 63 },
baseStats: {hp: 45, atk: 67, def: 60, spa: 50, spd: 50, spe: 63},
},
seaking: {
inherit: true,
baseStats: { hp: 80, atk: 92, def: 65, spa: 80, spd: 80, spe: 68 },
baseStats: {hp: 80, atk: 92, def: 65, spa: 80, spd: 80, spe: 68},
},
staryu: {
inherit: true,
baseStats: { hp: 30, atk: 45, def: 55, spa: 70, spd: 70, spe: 85 },
baseStats: {hp: 30, atk: 45, def: 55, spa: 70, spd: 70, spe: 85},
},
starmie: {
inherit: true,
baseStats: { hp: 60, atk: 75, def: 85, spa: 100, spd: 100, spe: 115 },
baseStats: {hp: 60, atk: 75, def: 85, spa: 100, spd: 100, spe: 115},
},
mrmime: {
inherit: true,
baseStats: { hp: 40, atk: 45, def: 65, spa: 100, spd: 100, spe: 90 },
baseStats: {hp: 40, atk: 45, def: 65, spa: 100, spd: 100, spe: 90},
},
scyther: {
inherit: true,
baseStats: { hp: 70, atk: 110, def: 80, spa: 55, spd: 55, spe: 105 },
baseStats: {hp: 70, atk: 110, def: 80, spa: 55, spd: 55, spe: 105},
},
jynx: {
inherit: true,
baseStats: { hp: 65, atk: 50, def: 35, spa: 95, spd: 95, spe: 95 },
baseStats: {hp: 65, atk: 50, def: 35, spa: 95, spd: 95, spe: 95},
},
electabuzz: {
inherit: true,
baseStats: { hp: 65, atk: 83, def: 57, spa: 85, spd: 85, spe: 105 },
baseStats: {hp: 65, atk: 83, def: 57, spa: 85, spd: 85, spe: 105},
},
magmar: {
inherit: true,
baseStats: { hp: 65, atk: 95, def: 57, spa: 85, spd: 85, spe: 93 },
baseStats: {hp: 65, atk: 95, def: 57, spa: 85, spd: 85, spe: 93},
},
pinsir: {
inherit: true,
baseStats: { hp: 65, atk: 125, def: 100, spa: 55, spd: 55, spe: 85 },
baseStats: {hp: 65, atk: 125, def: 100, spa: 55, spd: 55, spe: 85},
},
tauros: {
inherit: true,
baseStats: { hp: 75, atk: 100, def: 95, spa: 70, spd: 70, spe: 110 },
baseStats: {hp: 75, atk: 100, def: 95, spa: 70, spd: 70, spe: 110},
},
magikarp: {
inherit: true,
baseStats: { hp: 20, atk: 10, def: 55, spa: 20, spd: 20, spe: 80 },
baseStats: {hp: 20, atk: 10, def: 55, spa: 20, spd: 20, spe: 80},
},
gyarados: {
inherit: true,
baseStats: { hp: 95, atk: 125, def: 79, spa: 100, spd: 100, spe: 81 },
baseStats: {hp: 95, atk: 125, def: 79, spa: 100, spd: 100, spe: 81},
},
lapras: {
inherit: true,
baseStats: { hp: 130, atk: 85, def: 80, spa: 95, spd: 95, spe: 60 },
baseStats: {hp: 130, atk: 85, def: 80, spa: 95, spd: 95, spe: 60},
},
ditto: {
inherit: true,
baseStats: { hp: 48, atk: 48, def: 48, spa: 48, spd: 48, spe: 48 },
baseStats: {hp: 48, atk: 48, def: 48, spa: 48, spd: 48, spe: 48},
},
eevee: {
inherit: true,
baseStats: { hp: 55, atk: 55, def: 50, spa: 65, spd: 65, spe: 55 },
baseStats: {hp: 55, atk: 55, def: 50, spa: 65, spd: 65, spe: 55},
},
vaporeon: {
inherit: true,
baseStats: { hp: 130, atk: 65, def: 60, spa: 110, spd: 110, spe: 65 },
baseStats: {hp: 130, atk: 65, def: 60, spa: 110, spd: 110, spe: 65},
},
jolteon: {
inherit: true,
baseStats: { hp: 65, atk: 65, def: 60, spa: 110, spd: 110, spe: 130 },
baseStats: {hp: 65, atk: 65, def: 60, spa: 110, spd: 110, spe: 130},
},
flareon: {
inherit: true,
baseStats: { hp: 65, atk: 130, def: 60, spa: 110, spd: 110, spe: 65 },
baseStats: {hp: 65, atk: 130, def: 60, spa: 110, spd: 110, spe: 65},
},
porygon: {
inherit: true,
baseStats: { hp: 65, atk: 60, def: 70, spa: 75, spd: 75, spe: 40 },
baseStats: {hp: 65, atk: 60, def: 70, spa: 75, spd: 75, spe: 40},
},
omanyte: {
inherit: true,
baseStats: { hp: 35, atk: 40, def: 100, spa: 90, spd: 90, spe: 35 },
baseStats: {hp: 35, atk: 40, def: 100, spa: 90, spd: 90, spe: 35},
},
omastar: {
inherit: true,
baseStats: { hp: 70, atk: 60, def: 125, spa: 115, spd: 115, spe: 55 },
baseStats: {hp: 70, atk: 60, def: 125, spa: 115, spd: 115, spe: 55},
},
kabuto: {
inherit: true,
baseStats: { hp: 30, atk: 80, def: 90, spa: 45, spd: 45, spe: 55 },
baseStats: {hp: 30, atk: 80, def: 90, spa: 45, spd: 45, spe: 55},
},
kabutops: {
inherit: true,
baseStats: { hp: 60, atk: 115, def: 105, spa: 70, spd: 70, spe: 80 },
baseStats: {hp: 60, atk: 115, def: 105, spa: 70, spd: 70, spe: 80},
},
aerodactyl: {
inherit: true,
baseStats: { hp: 80, atk: 105, def: 65, spa: 60, spd: 60, spe: 130 },
baseStats: {hp: 80, atk: 105, def: 65, spa: 60, spd: 60, spe: 130},
},
snorlax: {
inherit: true,
baseStats: { hp: 160, atk: 110, def: 65, spa: 65, spd: 65, spe: 30 },
baseStats: {hp: 160, atk: 110, def: 65, spa: 65, spd: 65, spe: 30},
},
articuno: {
inherit: true,
baseStats: { hp: 90, atk: 85, def: 100, spa: 125, spd: 125, spe: 85 },
baseStats: {hp: 90, atk: 85, def: 100, spa: 125, spd: 125, spe: 85},
},
zapdos: {
inherit: true,
baseStats: { hp: 90, atk: 90, def: 85, spa: 125, spd: 125, spe: 100 },
baseStats: {hp: 90, atk: 90, def: 85, spa: 125, spd: 125, spe: 100},
},
moltres: {
inherit: true,
baseStats: { hp: 90, atk: 100, def: 90, spa: 125, spd: 125, spe: 90 },
baseStats: {hp: 90, atk: 100, def: 90, spa: 125, spd: 125, spe: 90},
},
dratini: {
inherit: true,
baseStats: { hp: 41, atk: 64, def: 45, spa: 50, spd: 50, spe: 50 },
baseStats: {hp: 41, atk: 64, def: 45, spa: 50, spd: 50, spe: 50},
},
dragonair: {
inherit: true,
baseStats: { hp: 61, atk: 84, def: 65, spa: 70, spd: 70, spe: 70 },
baseStats: {hp: 61, atk: 84, def: 65, spa: 70, spd: 70, spe: 70},
},
dragonite: {
inherit: true,
baseStats: { hp: 91, atk: 134, def: 95, spa: 100, spd: 100, spe: 80 },
baseStats: {hp: 91, atk: 134, def: 95, spa: 100, spd: 100, spe: 80},
},
mewtwo: {
inherit: true,
baseStats: { hp: 106, atk: 110, def: 90, spa: 154, spd: 154, spe: 130 },
baseStats: {hp: 106, atk: 110, def: 90, spa: 154, spd: 154, spe: 130},
},
mew: {
inherit: true,
baseStats: { hp: 100, atk: 100, def: 100, spa: 100, spd: 100, spe: 100 },
baseStats: {hp: 100, atk: 100, def: 100, spa: 100, spd: 100, spe: 100},
},
};

View File

@ -0,0 +1,756 @@
{
"bulbasaur": {
"level": 88,
"moves": ["bodyslam", "sleeppowder"],
"essentialMove": "razorleaf",
"exclusiveMoves": ["megadrain", "swordsdance", "swordsdance"]
},
"ivysaur": {
"level": 80,
"moves": ["bodyslam", "sleeppowder", "swordsdance"],
"essentialMove": "razorleaf"
},
"venusaur": {
"level": 74,
"moves": ["bodyslam", "hyperbeam", "sleeppowder", "swordsdance"],
"essentialMove": "razorleaf"
},
"charmander": {
"level": 88,
"moves": ["bodyslam", "slash"],
"essentialMove": "fireblast",
"exclusiveMoves": ["counter", "seismictoss"],
"comboMoves": ["bodyslam", "fireblast", "submission", "swordsdance"]
},
"charmeleon": {
"level": 80,
"moves": ["bodyslam", "slash"],
"essentialMove": "fireblast",
"exclusiveMoves": ["counter", "swordsdance"],
"comboMoves": ["bodyslam", "fireblast", "submission", "swordsdance"]
},
"charizard": {
"level": 77,
"moves": ["bodyslam", "earthquake", "slash"],
"essentialMove": "fireblast",
"comboMoves": ["hyperbeam", "swordsdance"]
},
"squirtle": {
"level": 88,
"moves": ["blizzard", "hydropump", "seismictoss", "surf"],
"exclusiveMoves": ["bodyslam", "counter"]
},
"wartortle": {
"level": 80,
"moves": ["blizzard", "bodyslam", "hydropump", "surf"],
"exclusiveMoves": ["counter", "rest", "seismictoss"]
},
"blastoise": {
"level": 77,
"moves": ["blizzard", "bodyslam", "hydropump", "surf"],
"exclusiveMoves": ["earthquake", "rest"]
},
"butterfree": {
"level": 77,
"moves": ["psychic", "sleeppowder", "stunspore"],
"exclusiveMoves": ["megadrain", "psywave"]
},
"beedrill": {
"level": 77,
"moves": ["megadrain", "swordsdance", "twineedle"],
"exclusiveMoves": ["doubleedge", "doubleedge", "hyperbeam"],
"comboMoves": ["agility", "hyperbeam", "swordsdance", "twineedle"]
},
"pidgey": {
"level": 88,
"moves": ["agility", "doubleedge", "skyattack"],
"exclusiveMoves": ["mimic", "mirrormove", "reflect", "sandattack", "substitute", "quickattack", "toxic"]
},
"pidgeotto": {
"level": 80,
"moves": ["agility", "doubleedge", "skyattack"],
"exclusiveMoves": ["mimic", "mirrormove", "reflect", "sandattack", "substitute", "quickattack", "toxic"]
},
"pidgeot": {
"level": 77,
"moves": ["agility", "doubleedge", "hyperbeam"],
"exclusiveMoves": ["mimic", "mirrormove", "reflect", "sandattack", "skyattack", "skyattack", "substitute", "quickattack", "toxic"]
},
"rattata": {
"level": 88,
"moves": ["blizzard", "bodyslam"],
"essentialMove": "superfang",
"exclusiveMoves": ["thunderbolt", "thunderbolt", "quickattack"]
},
"raticate": {
"level": 77,
"moves": ["blizzard", "bodyslam", "hyperbeam"],
"essentialMove": "superfang"
},
"spearow": {
"level": 88,
"moves": ["agility", "doubleedge", "drillpeck"],
"exclusiveMoves": ["leer", "mimic", "mirrormove", "substitute", "toxic"]
},
"fearow": {
"level": 77,
"moves": ["agility", "doubleedge", "drillpeck", "hyperbeam"]
},
"ekans": {
"level": 88,
"moves": ["bodyslam", "earthquake", "glare", "rockslide"]
},
"arbok": {
"level": 77,
"moves": ["earthquake", "glare", "hyperbeam"],
"exclusiveMoves": ["bodyslam", "rockslide"]
},
"pikachu": {
"level": 88,
"moves": ["surf", "thunderwave"],
"essentialMove": "thunderbolt",
"exclusiveMoves": ["agility", "bodyslam", "seismictoss", "thunder"]
},
"raichu": {
"level": 76,
"moves": ["surf", "thunderwave"],
"essentialMove": "thunderbolt",
"exclusiveMoves": ["agility", "bodyslam", "hyperbeam", "seismictoss", "thunder"]
},
"sandshrew": {
"level": 88,
"moves": ["bodyslam", "rockslide", "swordsdance"],
"essentialMove": "earthquake"
},
"sandslash": {
"level": 77,
"moves": ["bodyslam", "rockslide", "swordsdance"],
"essentialMove": "earthquake"
},
"nidoranf": {
"level": 88,
"moves": ["blizzard", "bodyslam", "thunderbolt"],
"exclusiveMoves": ["doubleedge", "doublekick"]
},
"nidorina": {
"level": 80,
"moves": ["blizzard", "bodyslam", "thunderbolt"],
"exclusiveMoves": ["bubblebeam", "doubleedge", "doublekick"]
},
"nidoqueen": {
"level": 77,
"moves": ["blizzard", "bodyslam", "thunderbolt"],
"essentialMove": "earthquake"
},
"nidoranm": {
"level": 88,
"moves": ["blizzard", "bodyslam", "thunderbolt"],
"exclusiveMoves": ["doubleedge", "doublekick"]
},
"nidorino": {
"level": 80,
"moves": ["blizzard", "bodyslam", "thunderbolt"],
"exclusiveMoves": ["bubblebeam", "doubleedge", "doublekick"]
},
"nidoking": {
"level": 77,
"moves": ["blizzard", "bodyslam"],
"essentialMove": "earthquake",
"exclusiveMoves": ["rockslide", "thunder", "thunderbolt"]
},
"clefairy": {
"level": 88,
"moves": ["bodyslam", "thunderbolt", "thunderwave"],
"essentialMove": "blizzard",
"exclusiveMoves": ["counter", "psychic", "seismictoss", "sing", "sing"]
},
"clefable": {
"level": 77,
"moves": ["bodyslam", "thunderbolt", "thunderwave"],
"essentialMove": "blizzard",
"exclusiveMoves": ["counter", "hyperbeam", "psychic", "sing", "sing"]
},
"vulpix": {
"level": 88,
"moves": ["bodyslam", "confuseray", "fireblast"],
"exclusiveMoves": ["flamethrower", "reflect", "substitute"]
},
"ninetales": {
"level": 77,
"moves": ["bodyslam", "confuseray", "fireblast"],
"exclusiveMoves": ["flamethrower", "hyperbeam", "reflect", "substitute"]
},
"jigglypuff": {
"level": 88,
"moves": ["blizzard", "bodyslam", "seismictoss", "thunderwave"],
"exclusiveMoves": ["counter", "sing"]
},
"wigglytuff": {
"level": 77,
"moves": ["blizzard", "bodyslam", "thunderwave"],
"exclusiveMoves": ["counter", "hyperbeam", "sing"]
},
"zubat": {
"level": 88,
"moves": ["confuseray", "doubleedge", "megadrain", "toxic"]
},
"golbat": {
"level": 77,
"moves": ["confuseray", "doubleedge", "hyperbeam", "megadrain"]
},
"oddish": {
"level": 88,
"moves": ["doubleedge", "sleeppowder"],
"essentialMove": "megadrain",
"exclusiveMoves": ["stunspore", "stunspore", "swordsdance"]
},
"gloom": {
"level": 80,
"moves": ["doubleedge", "sleeppowder"],
"essentialMove": "megadrain",
"exclusiveMoves": ["stunspore", "stunspore", "swordsdance"]
},
"vileplume": {
"level": 77,
"moves": ["bodyslam", "sleeppowder", "stunspore", "swordsdance"],
"essentialMove": "megadrain"
},
"paras": {
"level": 88,
"moves": ["bodyslam", "megadrain"],
"essentialMove": "spore",
"exclusiveMoves": ["growth", "slash", "stunspore", "stunspore", "swordsdance"]
},
"parasect": {
"level": 77,
"moves": ["bodyslam", "megadrain"],
"essentialMove": "spore",
"exclusiveMoves": ["growth", "hyperbeam", "slash", "stunspore", "stunspore", "swordsdance"]
},
"venonat": {
"level": 88,
"moves": ["psychic", "sleeppowder", "stunspore"],
"exclusiveMoves": ["doubleedge", "megadrain", "psywave"]
},
"venomoth": {
"level": 77,
"moves": ["psychic", "sleeppowder", "stunspore"],
"exclusiveMoves": ["doubleedge", "megadrain", "megadrain"]
},
"diglett": {
"level": 88,
"moves": ["bodyslam", "rockslide", "slash"],
"essentialMove": "earthquake"
},
"dugtrio": {
"level": 74,
"moves": ["bodyslam", "rockslide", "slash"],
"essentialMove": "earthquake"
},
"meowth": {
"level": 88,
"moves": ["bodyslam", "bubblebeam"],
"essentialMove": "slash",
"exclusiveMoves": ["thunder", "thunderbolt"]
},
"persian": {
"level": 74,
"moves": ["bodyslam", "bubblebeam"],
"essentialMove": "slash",
"exclusiveMoves": ["hyperbeam", "hyperbeam", "thunder", "thunderbolt"]
},
"psyduck": {
"level": 88,
"moves": ["amnesia", "blizzard"],
"essentialMove": "surf",
"exclusiveMoves": ["bodyslam", "hydropump", "rest", "seismictoss"]
},
"golduck": {
"level": 76,
"moves": ["amnesia", "blizzard"],
"essentialMove": "surf",
"exclusiveMoves": ["bodyslam", "hydropump", "rest", "seismictoss"]
},
"mankey": {
"level": 88,
"moves": ["bodyslam", "rockslide", "submission"],
"exclusiveMoves": ["counter", "megakick"]
},
"primeape": {
"level": 77,
"moves": ["bodyslam", "rockslide", "submission"],
"exclusiveMoves": ["counter", "hyperbeam", "hyperbeam"]
},
"growlithe": {
"level": 88,
"moves": ["bodyslam", "fireblast", "flamethrower", "reflect"]
},
"arcanine": {
"level": 77,
"moves": ["bodyslam", "fireblast", "hyperbeam"],
"exclusiveMoves": ["flamethrower", "reflect"]
},
"poliwag": {
"level": 88,
"moves": ["blizzard", "surf"],
"essentialMove": "amnesia",
"exclusiveMoves": ["hypnosis", "hypnosis", "psychic"]
},
"poliwhirl": {
"level": 76,
"moves": ["blizzard", "surf"],
"essentialMove": "amnesia",
"exclusiveMoves": ["counter", "hypnosis", "hypnosis", "psychic"]
},
"poliwrath": {
"level": 76,
"moves": ["blizzard", "bodyslam", "earthquake", "submission"],
"essentialMove": "surf",
"exclusiveMoves": ["hypnosis", "hypnosis", "psychic"],
"comboMoves": ["amnesia", "blizzard"]
},
"abra": {
"level": 88,
"moves": ["psychic", "seismictoss", "thunderwave"],
"exclusiveMoves": ["counter", "reflect"]
},
"kadabra": {
"level": 74,
"moves": ["psychic", "recover", "thunderwave"],
"exclusiveMoves": ["counter", "reflect", "reflect", "seismictoss", "seismictoss"]
},
"alakazam": {
"level": 68,
"moves": ["psychic", "recover", "thunderwave"],
"exclusiveMoves": ["counter", "reflect", "reflect", "seismictoss", "seismictoss"]
},
"machop": {
"level": 88,
"moves": ["bodyslam", "earthquake", "submission"],
"exclusiveMoves": ["counter", "rockslide", "rockslide"]
},
"machoke": {
"level": 80,
"moves": ["bodyslam", "earthquake", "submission"],
"exclusiveMoves": ["counter", "rockslide", "rockslide"]
},
"machamp": {
"level": 77,
"moves": ["bodyslam", "earthquake", "submission"],
"exclusiveMoves": ["counter", "hyperbeam", "rockslide", "rockslide"]
},
"bellsprout": {
"level": 88,
"moves": ["doubleedge", "sleeppowder", "stunspore", "swordsdance"],
"essentialMove": "razorleaf"
},
"weepinbell": {
"level": 80,
"moves": ["doubleedge", "sleeppowder", "stunspore", "swordsdance"],
"essentialMove": "razorleaf"
},
"victreebel": {
"level": 74,
"moves": ["bodyslam", "sleeppowder", "stunspore"],
"essentialMove": "razorleaf",
"comboMoves": ["hyperbeam", "swordsdance"]
},
"tentacool": {
"level": 88,
"moves": ["barrier", "hydropump", "surf"],
"essentialMove": "blizzard",
"exclusiveMoves": ["megadrain", "megadrain"],
"comboMoves": ["hydropump", "surf"]
},
"tentacruel": {
"level": 74,
"moves": ["blizzard", "hydropump", "hyperbeam", "surf"],
"essentialMove": "swordsdance"
},
"geodude": {
"level": 88,
"moves": ["bodyslam", "earthquake", "explosion", "rockslide"]
},
"graveler": {
"level": 80,
"moves": ["bodyslam", "earthquake", "explosion", "rockslide"]
},
"golem": {
"level": 77,
"moves": ["bodyslam", "earthquake", "explosion", "rockslide"]
},
"ponyta": {
"level": 88,
"moves": ["agility", "bodyslam", "fireblast", "reflect"]
},
"rapidash": {
"level": 77,
"moves": ["agility", "bodyslam", "fireblast", "hyperbeam"]
},
"slowpoke": {
"level": 88,
"moves": ["earthquake", "surf"],
"essentialMove": "thunderwave",
"exclusiveMoves": ["blizzard", "psychic", "rest"],
"comboMoves": ["amnesia", "surf"]
},
"slowbro": {
"level": 68,
"moves": ["amnesia", "surf", "thunderwave"],
"exclusiveMoves": ["blizzard", "psychic", "rest", "rest"]
},
"magnemite": {
"level": 88,
"moves": ["thunder", "thunderbolt", "thunderwave"],
"exclusiveMoves": ["doubleedge", "mimic", "substitute", "toxic"]
},
"magneton": {
"level": 77,
"moves": ["thunder", "thunderbolt", "thunderwave"],
"exclusiveMoves": ["doubleedge", "hyperbeam", "hyperbeam", "mimic", "substitute", "toxic"]
},
"farfetchd": {
"level": 77,
"moves": ["agility", "bodyslam", "swordsdance"],
"essentialMove": "slash"
},
"doduo": {
"level": 88,
"moves": ["agility", "bodyslam", "doubleedge", "drillpeck"]
},
"dodrio": {
"level": 74,
"moves": ["agility", "bodyslam", "drillpeck", "hyperbeam"]
},
"seel": {
"level": 88,
"moves": ["blizzard", "bodyslam", "surf"],
"exclusiveMoves": ["mimic", "rest"]
},
"dewgong": {
"level": 74,
"moves": ["blizzard", "bodyslam", "surf"],
"exclusiveMoves": ["hyperbeam", "mimic", "rest", "rest"]
},
"grimer": {
"level": 88,
"moves": ["bodyslam", "sludge"],
"essentialMove": "explosion",
"exclusiveMoves": ["fireblast", "megadrain", "megadrain", "screech"]
},
"muk": {
"level": 77,
"moves": ["bodyslam", "sludge"],
"essentialMove": "explosion",
"exclusiveMoves": ["fireblast", "hyperbeam", "megadrain", "megadrain"]
},
"shellder": {
"level": 88,
"moves": ["blizzard", "doubleedge", "explosion", "surf"]
},
"cloyster": {
"level": 68,
"moves": ["blizzard", "explosion", "surf"],
"exclusiveMoves": ["doubleedge", "hyperbeam", "hyperbeam"]
},
"gastly": {
"level": 88,
"moves": ["explosion", "megadrain", "nightshade", "psychic"],
"essentialMove": "thunderbolt",
"exclusiveMoves": ["confuseray", "hypnosis", "hypnosis"]
},
"haunter": {
"level": 74,
"moves": ["explosion", "megadrain", "nightshade", "psychic"],
"essentialMove": "thunderbolt",
"exclusiveMoves": ["confuseray", "hypnosis", "hypnosis"]
},
"gengar": {
"level": 68,
"moves": ["explosion", "megadrain", "nightshade", "psychic"],
"essentialMove": "thunderbolt",
"exclusiveMoves": ["confuseray", "hypnosis", "hypnosis"]
},
"onix": {
"level": 77,
"moves": ["bodyslam", "earthquake", "explosion", "rockslide"]
},
"drowzee": {
"level": 88,
"moves": ["hypnosis", "psychic", "thunderwave"],
"exclusiveMoves": ["counter", "reflect", "rest", "seismictoss", "seismictoss"]
},
"hypno": {
"level": 74,
"moves": ["hypnosis", "psychic", "thunderwave"],
"exclusiveMoves": ["counter", "reflect", "rest", "rest", "seismictoss", "seismictoss"]
},
"krabby": {
"level": 88,
"moves": ["blizzard", "bodyslam", "crabhammer", "swordsdance"]
},
"kingler": {
"level": 77,
"moves": ["bodyslam", "crabhammer", "hyperbeam", "swordsdance"]
},
"voltorb": {
"level": 88,
"moves": ["explosion", "thunderbolt", "thunderwave"],
"exclusiveMoves": ["screech", "thunder", "toxic"]
},
"electrode": {
"level": 77,
"moves": ["explosion", "thunderbolt", "thunderwave"],
"exclusiveMoves": ["hyperbeam", "screech", "thunder", "toxic"]
},
"exeggcute": {
"level": 77,
"moves": ["sleeppowder", "stunspore"],
"essentialMove": "psychic",
"exclusiveMoves": ["doubleedge", "explosion", "explosion"]
},
"exeggutor": {
"level": 68,
"moves": ["explosion", "psychic", "sleeppowder"],
"exclusiveMoves": ["doubleedge", "eggbomb", "hyperbeam", "megadrain", "megadrain", "stunspore", "stunspore", "stunspore"]
},
"cubone": {
"level": 88,
"moves": ["blizzard", "bodyslam", "earthquake", "seismictoss"]
},
"marowak": {
"level": 77,
"moves": ["blizzard", "bodyslam", "earthquake", "seismictoss"]
},
"hitmonlee": {
"level": 77,
"moves": ["bodyslam", "highjumpkick", "seismictoss"],
"exclusiveMoves": ["counter", "counter", "meditate"]
},
"hitmonchan": {
"level": 77,
"moves": ["bodyslam", "seismictoss", "submission"],
"exclusiveMoves": ["agility", "counter", "counter"]
},
"lickitung": {
"level": 77,
"moves": ["hyperbeam", "swordsdance"],
"essentialMove": "bodyslam",
"exclusiveMoves": ["blizzard", "earthquake", "earthquake", "earthquake"]
},
"koffing": {
"level": 88,
"moves": ["explosion", "fireblast", "sludge", "thunderbolt"]
},
"weezing": {
"level": 77,
"moves": ["explosion", "fireblast", "sludge", "thunderbolt"]
},
"rhyhorn": {
"level": 88,
"moves": ["bodyslam", "earthquake", "rockslide", "substitute"]
},
"rhydon": {
"level": 68,
"moves": ["bodyslam", "earthquake", "rockslide"],
"exclusiveMoves": ["hyperbeam", "substitute", "substitute"]
},
"chansey": {
"level": 68,
"moves": ["icebeam", "thunderwave"],
"essentialMove": "softboiled",
"exclusiveMoves": ["counter", "reflect", "seismictoss", "sing", "thunderbolt", "thunderbolt", "thunderbolt"]
},
"tangela": {
"level": 74,
"moves": ["bodyslam", "sleeppowder"],
"essentialMove": "megadrain",
"exclusiveMoves": ["growth", "stunspore", "stunspore", "stunspore", "swordsdance", "swordsdance"]
},
"kangaskhan": {
"level": 74,
"moves": ["bodyslam", "earthquake", "hyperbeam"],
"exclusiveMoves": ["counter", "rockslide", "rockslide", "surf"]
},
"horsea": {
"level": 88,
"moves": ["agility", "blizzard"],
"essentialMove": "surf",
"exclusiveMoves": ["doubleedge", "hydropump", "smokescreen"]
},
"seadra": {
"level": 77,
"moves": ["agility", "blizzard"],
"essentialMove": "surf",
"exclusiveMoves": ["doubleedge", "hydropump", "hyperbeam", "smokescreen"]
},
"goldeen": {
"level": 88,
"moves": ["agility", "blizzard", "doubleedge", "surf"]
},
"seaking": {
"level": 77,
"moves": ["blizzard", "doubleedge", "surf"],
"exclusiveMoves": ["agility", "agility", "hyperbeam"]
},
"staryu": {
"level": 88,
"moves": ["blizzard", "thunderbolt", "thunderwave"],
"essentialMove": "recover",
"exclusiveMoves": ["hydropump", "surf", "surf"]
},
"starmie": {
"level": 68,
"moves": ["blizzard", "thunderbolt", "thunderwave"],
"essentialMove": "recover",
"exclusiveMoves": ["hydropump", "psychic", "surf", "surf"]
},
"mrmime": {
"level": 77,
"moves": ["psychic", "seismictoss", "thunderbolt", "thunderwave"]
},
"scyther": {
"level": 77,
"moves": ["agility", "hyperbeam", "slash", "swordsdance"]
},
"jynx": {
"level": 68,
"moves": ["blizzard", "lovelykiss", "psychic"],
"exclusiveMoves": ["bodyslam", "counter", "counter", "mimic", "seismictoss"]
},
"electabuzz": {
"level": 74,
"moves": ["psychic", "seismictoss", "thunderbolt", "thunderwave"]
},
"magmar": {
"level": 77,
"moves": ["bodyslam", "confuseray", "fireblast"],
"exclusiveMoves": ["hyperbeam", "psychic"]
},
"pinsir": {
"level": 77,
"moves": ["bodyslam", "hyperbeam", "swordsdance"],
"exclusiveMoves": ["seismictoss", "submission", "submission"]
},
"tauros": {
"level": 68,
"moves": ["bodyslam", "earthquake", "hyperbeam"],
"exclusiveMoves": ["blizzard", "blizzard", "blizzard", "thunderbolt"]
},
"gyarados": {
"level": 74,
"moves": ["blizzard", "bodyslam", "hyperbeam", "thunderbolt"],
"exclusiveMoves": ["hydropump", "surf"]
},
"lapras": {
"level": 74,
"moves": ["bodyslam", "confuseray", "rest", "sing", "surf"],
"essentialMove": "blizzard",
"exclusiveMoves": ["thunderbolt", "thunderbolt"]
},
"ditto": {
"level": 100,
"moves": ["transform"]
},
"eevee": {
"level": 88,
"moves": ["doubleedge", "quickattack", "reflect"],
"essentialMove": "bodyslam",
"exclusiveMoves": ["bide", "mimic", "sandattack", "tailwhip"]
},
"vaporeon": {
"level": 74,
"moves": ["blizzard", "rest"],
"essentialMove": "surf",
"exclusiveMoves": ["bodyslam", "hydropump", "mimic"]
},
"jolteon": {
"level": 68,
"moves": ["bodyslam", "thunderbolt", "thunderwave"],
"exclusiveMoves": ["agility", "agility", "doublekick", "pinmissile", "pinmissile"]
},
"flareon": {
"level": 77,
"moves": ["bodyslam", "fireblast", "hyperbeam", "quickattack"]
},
"porygon": {
"level": 77,
"moves": ["blizzard", "thunderwave"],
"essentialMove": "recover",
"exclusiveMoves": ["doubleedge", "psychic", "thunderbolt", "triattack"]
},
"omanyte": {
"level": 88,
"moves": ["bodyslam", "hydropump", "rest", "surf"],
"essentialMove": "blizzard"
},
"omastar": {
"level": 74,
"moves": ["blizzard", "hydropump", "seismictoss", "surf"],
"exclusiveMoves": ["bodyslam", "rest"]
},
"kabuto": {
"level": 88,
"moves": ["blizzard", "bodyslam", "slash", "surf"]
},
"kabutops": {
"level": 77,
"moves": ["hyperbeam", "surf", "swordsdance"],
"exclusiveMoves": ["bodyslam", "slash"]
},
"aerodactyl": {
"level": 74,
"moves": ["doubleedge", "fireblast", "hyperbeam", "skyattack"]
},
"snorlax": {
"level": 68,
"moves": ["bodyslam", "rest", "selfdestruct", "thunderbolt"],
"essentialMove": "amnesia",
"exclusiveMoves": ["blizzard", "blizzard"],
"comboMoves": ["bodyslam", "earthquake", "hyperbeam", "selfdestruct"]
},
"articuno": {
"level": 74,
"moves": ["agility", "hyperbeam", "icebeam", "mimic", "reflect"],
"essentialMove": "blizzard",
"comboMoves": ["icebeam", "reflect", "rest"]
},
"zapdos": {
"level": 68,
"moves": ["agility", "drillpeck", "thunderbolt", "thunderwave"]
},
"moltres": {
"level": 77,
"moves": ["agility", "fireblast", "hyperbeam"],
"exclusiveMoves": ["doubleedge", "reflect", "skyattack"]
},
"dratini": {
"level": 88,
"moves": ["bodyslam", "hyperbeam", "thunderbolt", "thunderwave"],
"essentialMove": "blizzard"
},
"dragonair": {
"level": 80,
"moves": ["bodyslam", "hyperbeam", "thunderbolt", "thunderwave"],
"essentialMove": "blizzard"
},
"dragonite": {
"level": 74,
"moves": ["bodyslam", "hyperbeam", "thunderbolt", "thunderwave"],
"essentialMove": "blizzard"
},
"mewtwo": {
"level": 62,
"moves": ["blizzard", "recover", "thunderbolt"],
"essentialMove": "amnesia",
"exclusiveMoves": ["psychic", "psychic"],
"comboMoves": ["barrier", "rest"]
},
"mew": {
"level": 65,
"moves": ["blizzard", "earthquake", "thunderbolt", "thunderwave"],
"essentialMove": "psychic",
"exclusiveMoves": ["explosion", "softboiled", "softboiled"],
"comboMoves": ["earthquake", "hyperbeam", "swordsdance"]
}
}

View File

@ -1,5 +1,6 @@
import RandomGen2Teams from '../gen2/teams';
import { Utils } from '../../../lib';
import RandomGen2Teams from '../gen2/random-teams';
import {Utils} from '../../../lib';
import {MoveCounter} from '../gen8/random-teams';
interface HackmonsCupEntry {
types: string[];
@ -9,16 +10,16 @@ interface HackmonsCupEntry {
interface Gen1RandomBattleSpecies {
level?: number;
moves?: ID[];
essentialMoves?: ID[];
essentialMove?: ID;
exclusiveMoves?: ID[];
comboMoves?: ID[];
}
export class RandomGen1Teams extends RandomGen2Teams {
override randomData: { [species: IDEntry]: Gen1RandomBattleSpecies } = require('./data.json');
randomData: {[species: string]: Gen1RandomBattleSpecies} = require('./random-data.json');
// Challenge Cup or CC teams are basically fully random teams.
override randomCCTeam() {
randomCCTeam() {
this.enforceNoDirectCustomBanlistChanges();
const team = [];
@ -27,6 +28,7 @@ export class RandomGen1Teams extends RandomGen2Teams {
for (const pokemon of randomN) {
const species = this.dex.species.get(pokemon);
const learnset = this.dex.species.getLearnset(species.id);
// Level balance: calculate directly from stats rather than using some silly lookup table.
const mbstmin = 1307;
@ -77,11 +79,19 @@ export class RandomGen1Teams extends RandomGen2Teams {
ivs["spe"] *= 2;
// Maxed EVs.
const evs = { hp: 255, atk: 255, def: 255, spa: 255, spd: 255, spe: 255 };
const evs = {hp: 255, atk: 255, def: 255, spa: 255, spd: 255, spe: 255};
// Four random unique moves from movepool. don't worry about "attacking" or "viable".
// Since Gens 1 and 2 learnsets are shared, we need to weed out Gen 2 moves.
const pool = [...this.dex.species.getMovePool(species.id)];
const pool: string[] = [];
if (learnset) {
for (const move in learnset) {
if (this.dex.moves.get(move).gen !== 1) continue;
if (learnset[move].some(learned => learned.startsWith('1'))) {
pool.push(move);
}
}
}
team.push({
name: species.baseSpecies,
@ -89,8 +99,8 @@ export class RandomGen1Teams extends RandomGen2Teams {
moves: this.multipleSamplesNoReplace(pool, 4),
gender: false,
ability: 'No Ability',
evs,
ivs,
evs: evs,
ivs: ivs,
item: '',
level,
happiness: 0,
@ -103,11 +113,11 @@ export class RandomGen1Teams extends RandomGen2Teams {
}
// Random team generation for Gen 1 Random Battles.
override randomTeam() {
randomTeam() {
this.enforceNoDirectCustomBanlistChanges();
// Get what we need ready.
const seed = this.prng.getSeed();
const seed = this.prng.seed;
const ruleTable = this.dex.formats.getRuleTable(this.format);
const pokemon: RandomTeamsTypes.RandomSet[] = [];
@ -118,17 +128,19 @@ export class RandomGen1Teams extends RandomGen2Teams {
/** Pokémon that are not wholly incompatible with the team, but still pretty bad */
const rejectedButNotInvalidPool: string[] = [];
const nuTiers = ['UU', 'UUBL', 'NFE', 'LC', 'NU'];
const uuTiers = ['NFE', 'UU', 'UUBL', 'NU'];
// Now let's store what we are getting.
const typeCount: { [k: string]: number } = {};
const weaknessCount: { [k: string]: number } = { Electric: 0, Psychic: 0, Water: 0, Ice: 0, Ground: 0, Fire: 0 };
let numMaxLevelPokemon = 0;
const typeCount: {[k: string]: number} = {};
const weaknessCount: {[k: string]: number} = {Electric: 0, Psychic: 0, Water: 0, Ice: 0, Ground: 0, Fire: 0};
let uberCount = 0;
let nuCount = 0;
const pokemonPool = Object.keys(this.getPokemonPool(type, pokemon, isMonotype, Object.keys(this.randomData))[0]);
const pokemonPool = this.getPokemonPool(type, pokemon, isMonotype);
while (pokemonPool.length && pokemon.length < this.maxTeamSize) {
const species = this.dex.species.get(this.sampleNoReplace(pokemonPool));
if (!species.exists) continue;
if (!species.exists || !this.randomData[species.id]?.moves) continue;
// Only one Ditto is allowed per battle in Generation 1,
// as it can cause an endless battle if two Dittos are forced
// to face each other.
@ -137,12 +149,33 @@ export class RandomGen1Teams extends RandomGen2Teams {
// Dynamically scale limits for different team sizes. The default and minimum value is 1.
const limitFactor = Math.round(this.maxTeamSize / 6) || 1;
const tier = species.tier;
switch (tier) {
case 'LC':
case 'NFE':
// Don't add pre-evo mon if already 4 or more non-OUs
// Regardless, pre-evo mons are slightly less common.
if (nuCount >= 4 * limitFactor || this.randomChance(1, 3)) continue;
break;
case 'Uber':
// Only allow a single Uber.
if (uberCount >= 1 * limitFactor) continue;
break;
default:
// OUs are fine. Otherwise 50% chance to skip mon if already 4 or more non-OUs.
if (uuTiers.includes(tier) && pokemonPool.length > 1 && (nuCount >= 4 * limitFactor && this.randomChance(1, 2))) {
continue;
}
}
let skip = false;
if (!isMonotype && !this.forceMonotype) {
// Limit two of any type
// Limit 2 of any type as well. Diversity and minor weakness count.
// The second of a same type has halved chance of being added.
for (const typeName of species.types) {
if (typeCount[typeName] >= 2 * limitFactor) {
if (typeCount[typeName] >= 2 * limitFactor ||
(typeCount[typeName] >= 1 * limitFactor && this.randomChance(1, 2) && pokemonPool.length > 1)) {
skip = true;
break;
}
@ -155,7 +188,7 @@ export class RandomGen1Teams extends RandomGen2Teams {
}
// We need a weakness count of spammable attacks to avoid being swept by those.
// Spammable attacks are: Thunderbolt, Psychic, Surf, Blizzard, Earthquake, Fire Blast.
// Spammable attacks are: Thunderbolt, Psychic, Surf, Blizzard, Earthquake.
const pokemonWeaknesses = [];
for (const typeName in weaknessCount) {
const increaseCount = this.dex.getImmunity(typeName, species) && this.dex.getEffectiveness(typeName, species) > 0;
@ -172,12 +205,6 @@ export class RandomGen1Teams extends RandomGen2Teams {
continue;
}
// Limit one level 100 Pokemon
if (!this.adjustLevel && (this.getLevel(species) === 100) && numMaxLevelPokemon >= limitFactor) {
rejectedButNotInvalidPool.push(species.id);
continue;
}
// The set passes the limitations.
pokemon.push(this.randomSet(species));
@ -196,8 +223,12 @@ export class RandomGen1Teams extends RandomGen2Teams {
weaknessCount[weakness]++;
}
// Increment level 100 counter
if (this.getLevel(species) === 100) numMaxLevelPokemon++;
// Increment tier bias counters.
if (tier === 'Uber') {
uberCount++;
} else if (nuTiers.includes(tier)) {
nuCount++;
}
// Ditto check
if (species.id === 'ditto') this.battleHasDitto = true;
@ -216,18 +247,47 @@ export class RandomGen1Teams extends RandomGen2Teams {
return pokemon;
}
shouldCullMove(move: Move, types: Set<string>, moves: Set<string>, counter: MoveCounter): {cull: boolean} {
switch (move.id) {
// bit redundant to have both, but neither particularly better than the other
case 'hydropump':
return {cull: moves.has('surf')};
case 'surf':
return {cull: moves.has('hydropump')};
// other redundancies that aren't handled within the movesets themselves
case 'selfdestruct':
return {cull: moves.has('rest')};
case 'rest':
return {cull: moves.has('selfdestruct')};
case 'sharpen': case 'swordsdance':
return {cull: counter.get('Special') > counter.get('Physical') || !counter.get('Physical') || moves.has('growth')};
case 'growth':
return {cull: counter.get('Special') < counter.get('Physical') || !counter.get('Special') || moves.has('swordsdance')};
case 'poisonpowder': case 'stunspore': case 'sleeppowder': case 'toxic':
return {cull: counter.get('Status') > 1};
}
return {cull: false};
}
/**
* Random set generation for Gen 1 Random Battles.
*/
override randomSet(species: string | Species): RandomTeamsTypes.RandomSet {
const ruleTable = this.dex.formats.getRuleTable(this.format);
randomSet(species: string | Species): RandomTeamsTypes.RandomSet {
species = this.dex.species.get(species);
if (!species.exists) species = this.dex.species.get('pikachu'); // Because Gen 1.
const data = this.randomData[species.id];
const movePool = data.moves?.slice() || [];
const moves = new Set<string>();
const types = new Set(species.types);
const counter = new MoveCounter();
// Moves that boost Attack:
const PhysicalSetup = ['swordsdance', 'sharpen'];
// Moves which boost Special Attack:
const SpecialSetup = ['amnesia', 'growth'];
// Either add all moves or add none
if (data.comboMoves && data.comboMoves.length <= this.maxMoveCount && this.randomChance(1, 2)) {
@ -236,17 +296,13 @@ export class RandomGen1Teams extends RandomGen2Teams {
// Add one of the semi-mandatory moves
// Often, these are used so that the Pokemon only gets one of the less useful moves
// This is added before the essential moves so that combos containing three moves can roll an exclusive move
if (moves.size < this.maxMoveCount && data.exclusiveMoves) {
moves.add(this.sample(data.exclusiveMoves));
}
// Add the mandatory moves.
if (moves.size < this.maxMoveCount && data.essentialMoves) {
for (const moveid of data.essentialMoves) {
moves.add(moveid);
if (moves.size === this.maxMoveCount) break;
}
// Add the mandatory move. SD Mew and Amnesia Snorlax are exceptions.
if (moves.size < this.maxMoveCount && data.essentialMove) {
moves.add(data.essentialMove);
}
while (moves.size < this.maxMoveCount && movePool.length) {
@ -255,12 +311,36 @@ export class RandomGen1Teams extends RandomGen2Teams {
const moveid = this.sampleNoReplace(movePool);
moves.add(moveid);
}
// Only do move choosing if we have backup moves in the pool...
if (movePool.length) {
for (const setMoveid of moves) {
const move = this.dex.moves.get(setMoveid);
const moveid = move.id;
if (!move.damage && !move.damageCallback) counter.add(move.category);
if (PhysicalSetup.includes(moveid)) counter.add('physicalsetup');
if (SpecialSetup.includes(moveid)) counter.add('specialsetup');
}
for (const moveid of moves) {
if (moveid === data.essentialMove) continue;
const move = this.dex.moves.get(moveid);
if (
(!data.essentialMove || moveid !== data.essentialMove) &&
this.shouldCullMove(move, types, moves, counter).cull
) {
moves.delete(moveid);
break;
}
counter.add(move.category);
}
} // End of the check for more than 4 moves on moveset.
}
const level = this.getLevel(species);
const level = this.adjustLevel || data.level || 80;
const evs = { hp: 255, atk: 255, def: 255, spa: 255, spd: 255, spe: 255 };
const ivs = { hp: 30, atk: 30, def: 30, spa: 30, spd: 30, spe: 30 };
const evs = {hp: 255, atk: 255, def: 255, spa: 255, spd: 255, spe: 255};
const ivs = {hp: 30, atk: 30, def: 30, spa: 30, spd: 30, spe: 30};
// Should be able to use Substitute four times from full HP without fainting
if (moves.has('substitute')) {
@ -277,20 +357,16 @@ export class RandomGen1Teams extends RandomGen2Teams {
if (move.damageCallback || move.damage) return true;
return move.category !== 'Physical';
});
if (noAttackStatMoves && !moves.has('mimic') && !moves.has('transform') && !ruleTable.has('forceofthefallenmod')) {
if (noAttackStatMoves && !moves.has('mimic') && !moves.has('transform')) {
evs.atk = 0;
// We don't want to lower the HP DV/IV
ivs.atk = 2;
}
// shuffle moves to add more randomness to camomons
const shuffledMoves = Array.from(moves);
this.prng.shuffle(shuffledMoves);
return {
name: species.name,
species: species.name,
moves: shuffledMoves,
moves: Array.from(moves),
ability: 'No Ability',
evs,
ivs,
@ -301,7 +377,7 @@ export class RandomGen1Teams extends RandomGen2Teams {
};
}
override randomHCTeam(): PokemonSet[] {
randomHCTeam(): PokemonSet[] {
this.enforceNoDirectCustomBanlistChanges();
const team = [];
@ -310,7 +386,7 @@ export class RandomGen1Teams extends RandomGen2Teams {
const typesPool = ['Bird', ...this.dex.types.names()];
const randomN = this.randomNPokemon(this.maxTeamSize);
const hackmonsCup: { [k: string]: HackmonsCupEntry } = {};
const hackmonsCup: {[k: string]: HackmonsCupEntry} = {};
for (const forme of randomN) {
// Choose forme

View File

@ -1,17 +1,8 @@
export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable = {
standardag: {
inherit: true,
ruleset: [
'Obtainable', 'Desync Clause Mod', 'HP Percentage Mod', 'Cancel Mod', 'Endless Battle Clause',
],
},
export const Rulesets: {[k: string]: ModdedFormatData} = {
standard: {
effectType: 'ValidatorRule',
name: 'Standard',
ruleset: [
'Standard AG',
'Sleep Clause Mod', 'Freeze Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause',
],
ruleset: ['Obtainable', 'Desync Clause Mod', 'Sleep Clause Mod', 'Freeze Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod'],
banlist: ['Dig', 'Fly'],
},
'350cupmod': {
@ -45,7 +36,7 @@ export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable
},
onModifySpecies(species) {
const newSpecies = this.dex.deepClone(species);
const stats: { [k: string]: number } = {
const stats: {[k: string]: number} = {
hp: newSpecies.baseStats.spe,
atk: newSpecies.baseStats.spa,
def: newSpecies.baseStats.def,

View File

@ -17,26 +17,14 @@ export const Scripts: ModdedBattleScriptsData = {
gen: 1,
init() {
for (const i in this.data.Pokedex) {
const poke = this.modData('Pokedex', i);
poke.gender = 'N';
poke.eggGroups = null;
(this.data.Pokedex[i] as any).gender = 'N';
(this.data.Pokedex[i] as any).eggGroups = null;
}
},
// BattlePokemon scripts.
pokemon: {
inherit: true,
deductPP(move, amount) {
// deduct PP based on side.lastSelectedMoveSlot
const ppData = this.getMoveSlot(this.side.lastSelectedMoveSlot);
if (!ppData) return 0;
ppData.used = true;
if (!amount) amount = 1;
ppData.pp -= amount;
return amount;
},
getStat(statName, unmodified) {
// @ts-expect-error type checking prevents 'hp' from being passed, but we're paranoid
// @ts-ignore - type checking prevents 'hp' from being passed, but we're paranoid
if (statName === 'hp') throw new Error("Please read `maxhp` directly");
if (unmodified) return this.baseStoredStats[statName];
return this.modifiedStats![statName];
@ -131,76 +119,59 @@ export const Scripts: ModdedBattleScriptsData = {
},
},
actions: {
inherit: true,
// This function is the main one when running a move.
// It deals with the beforeMove event.
// It also deals with how PP reduction works on gen 1.
runMove(moveOrMoveName, pokemon, targetLoc, options) {
let sourceEffect = options?.sourceEffect;
runMove(moveOrMoveName, pokemon, targetLoc, 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.
if (move.id === 'recharge' && !pokemon.volatiles['mustrecharge'] && !pokemon.volatiles['partiallytrapped']) {
move = this.battle.dex.getActiveMove('hyperbeam');
this.battle.hint(`In Gen 1, partial trapping moves like Wrap remove Hyper Beam recharges. ` +
`If the target would have recharged, it will automatically use Hyper Beam instead.`, true);
}
const move = this.battle.dex.getActiveMove(moveOrMoveName);
if (target?.subFainted) target.subFainted = null;
this.battle.setActiveMove(move, pokemon, target);
if (pokemon.moveThisTurn || move.id === 'cantmove' || !this.battle.runEvent('BeforeMove', pokemon, target, move)) {
if (pokemon.moveThisTurn || !this.battle.runEvent('BeforeMove', pokemon, target, move)) {
this.battle.clearActiveMove(true);
// This is only run for sleep.
this.battle.runEvent('AfterMoveSelf', pokemon, target, move);
return;
}
if (move.beforeMoveCallback?.call(this.battle, pokemon, target, move)) {
this.battle.clearActiveMove(true);
return;
}
if (move.id !== 'struggle') {
const lockedMove = pokemon.getLockedMove();
if (lockedMove) sourceEffect = move;
// Locked moves don't deduct PP
// Two-turn moves like Sky Attack deduct PP on their second turn.
if ((!lockedMove && !TWO_TURN_MOVES.includes(move.id)) || pokemon.volatiles['twoturnmove']) {
const moveSlot = pokemon.getMoveSlot(pokemon.side.lastSelectedMoveSlot);
if (moveSlot && pokemon.deductPP(moveSlot.id, null, target) && moveSlot.pp < 0) {
moveSlot.pp += 64;
this.battle.hint("In Gen 1, if a Pokémon is forced to use a move with 0 PP, the move will underflow to have 63 PP.");
}
}
if (move.id !== pokemon.getMoveSlot(pokemon.side.lastSelectedMoveSlot)?.id) {
this.battle.hint("Desync Clause Mod activated!");
this.battle.hint(
"In Gen 1, a Pokémon that thaws out might try to use a move that doesn't match the move " +
"of the slot it last selected (switches reset to the first slot).",
);
if (move.beforeMoveCallback) {
if (move.beforeMoveCallback.call(this.battle, pokemon, target, move)) {
this.battle.clearActiveMove(true);
return;
}
}
this.useMove(move, pokemon, { target, sourceEffect });
let lockedMove = this.battle.runEvent('LockMove', pokemon);
if (lockedMove === true) lockedMove = false;
if (
(!lockedMove &&
(!pokemon.volatiles['partialtrappinglock'] || pokemon.volatiles['partialtrappinglock'].locked !== target))
) {
pokemon.deductPP(move, null, target);
} else {
sourceEffect = move;
if (pokemon.volatiles['twoturnmove']) {
// Two-turn moves like Sky Attack deduct PP on their second turn.
pokemon.deductPP(pokemon.volatiles['twoturnmove'].originalMove, null, target);
}
}
if (pokemon.volatiles['partialtrappinglock'] && target !== pokemon.volatiles['partialtrappinglock'].locked) {
const moveSlot = pokemon.moveSlots.find(ms => ms.id === move.id);
if (moveSlot && moveSlot.pp < 0) {
moveSlot.pp = 63;
this.battle.hint("In Gen 1, if a player is forced to use a move with 0 PP, the move will underflow to have 63 PP.");
}
}
this.useMove(move, pokemon, target, sourceEffect);
// Restore PP if the move is the first turn of a charging move. Save the move from which PP should be deducted if the move succeeds.
if (pokemon.volatiles['twoturnmove']) {
pokemon.deductPP(move, -1, target);
pokemon.volatiles['twoturnmove'].originalMove = move.id;
}
},
// This function deals with AfterMoveSelf events.
// This leads with partial trapping moves shenanigans after the move has been used.
useMove(moveOrMoveName, pokemon, options) {
let sourceEffect = options?.sourceEffect;
let target = options?.target;
useMove(moveOrMoveName, pokemon, target, sourceEffect) {
if (!sourceEffect && this.battle.effect.id) sourceEffect = this.battle.effect;
const baseMove = this.battle.dex.moves.get(moveOrMoveName);
let move = this.battle.dex.getActiveMove(baseMove);
@ -210,11 +181,6 @@ export const Scripts: ModdedBattleScriptsData = {
}
if (sourceEffect) move.sourceEffect = sourceEffect.id;
if (sourceEffect?.id === 'metronome' || sourceEffect?.id === 'mirrormove') {
const moveSlot = pokemon.getMoveSlot(pokemon.side.lastSelectedMoveSlot);
if (moveSlot) pokemon.deductPP(moveSlot.id, -1, target);
}
this.battle.singleEvent('ModifyMove', move, null, pokemon, target, move, move);
if (baseMove.target !== move.target) {
// Target changed in ModifyMove, so we must adjust it here
@ -228,7 +194,7 @@ export const Scripts: ModdedBattleScriptsData = {
// The charging turn of a two-turn move does not update pokemon.lastMove
if (!TWO_TURN_MOVES.includes(move.id) || pokemon.volatiles['twoturnmove']) pokemon.lastMove = move;
const moveResult = this.useMoveInner(moveOrMoveName, pokemon, { target, sourceEffect });
const moveResult = this.useMoveInner(moveOrMoveName, pokemon, target, sourceEffect);
if (move.id !== 'metronome') {
if (move.id !== 'mirrormove' ||
@ -236,9 +202,37 @@ 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;
this.battle.runEvent('AfterMove', pokemon, target, move);
if (!target || target.hp > 0) {
this.battle.runEvent('AfterMoveSelf', pokemon, target, 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.
}
}
}
@ -246,9 +240,7 @@ export const Scripts: ModdedBattleScriptsData = {
},
// This is the function that actually uses the move, running ModifyMove events.
// It uses the move and then deals with the effects after the move.
useMoveInner(moveOrMoveName, pokemon, options) {
let sourceEffect = options?.sourceEffect;
let target = options?.target;
useMoveInner(moveOrMoveName, pokemon, target, sourceEffect) {
if (!sourceEffect && this.battle.effect.id) sourceEffect = this.battle.effect;
const baseMove = this.battle.dex.moves.get(moveOrMoveName);
let move = this.battle.dex.getActiveMove(baseMove);
@ -278,8 +270,8 @@ export const Scripts: ModdedBattleScriptsData = {
return false;
}
if (sourceEffect) attrs += `|[from] ${this.battle.dex.conditions.get(sourceEffect).name}`;
this.battle.addMove('move', pokemon, move.name, `${target}${attrs}`);
if (sourceEffect) attrs += '|[from]' + this.battle.dex.conditions.get(sourceEffect);
this.battle.addMove('move', pokemon, move.name, target + attrs);
if (!this.battle.singleEvent('Try', move, null, pokemon, target, move)) {
return true;
@ -306,9 +298,9 @@ export const Scripts: ModdedBattleScriptsData = {
// Disable and Selfdestruct/Explosion boost rage, regardless of whether they miss/fail.
if (target.boosts.atk < 6 && (move.selfdestruct || move.id === 'disable') && target.volatiles['rage']) {
this.battle.boost({ atk: 1 }, target, pokemon, this.dex.conditions.get('rage'));
this.battle.boost({atk: 1}, target, pokemon, this.dex.conditions.get('rage'));
this.battle.hint(`In Gen 1, using ${move.name} causes the target to build Rage, ` +
`even if it misses or fails`, true);
`even if it misses or fails`, true);
}
// Go ahead with results of the used move.
@ -317,8 +309,10 @@ export const Scripts: ModdedBattleScriptsData = {
return true;
}
this.battle.singleEvent('AfterMoveSecondarySelf', move, null, pokemon, target, move);
this.battle.runEvent('AfterMoveSecondarySelf', pokemon, target, move);
if (!move.negateSecondary) {
this.battle.singleEvent('AfterMoveSecondarySelf', move, null, pokemon, target, move);
this.battle.runEvent('AfterMoveSecondarySelf', pokemon, target, move);
}
return true;
},
// This function attempts a move hit and returns the attempt result before the actual hit happens.
@ -353,7 +347,10 @@ export const Scripts: ModdedBattleScriptsData = {
}
// Then, check if the Pokémon is immune to this move.
if (!target.runImmunity(move, true)) {
if (
(!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) &&
!target.runImmunity(move.type, true)
) {
if (move.selfdestruct) {
this.battle.faint(pokemon, pokemon, move);
}
@ -368,8 +365,13 @@ 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']) {
if (move.status === 'slp' && target && target.volatiles['mustrecharge']) {
accuracy = true;
}
@ -474,8 +476,10 @@ export const Scripts: ModdedBattleScriptsData = {
if (move.ohko) this.battle.add('-ohko');
this.battle.singleEvent('AfterMoveSecondary', move, null, target, pokemon, move);
this.battle.runEvent('AfterMoveSecondary', target, pokemon, move);
if (!move.negateSecondary) {
this.battle.singleEvent('AfterMoveSecondary', move, null, target, pokemon, move);
this.battle.runEvent('AfterMoveSecondary', target, pokemon, move);
}
return damage;
},
@ -500,6 +504,14 @@ 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;
@ -574,7 +586,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 corresponding stat penalties will be applied again to that Pokémon.
// burned, the correspoding 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') {
@ -665,6 +677,11 @@ 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) {
@ -711,8 +728,10 @@ export const Scripts: ModdedBattleScriptsData = {
}
// Let's see if the target is immune to the move.
if (!target.runImmunity(move, true)) {
return false;
if (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) {
if (!target.runImmunity(move.type, true)) {
return false;
}
}
// Is it an OHKO move?
@ -736,7 +755,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.self?.volatileStatus === 'partialtrappinglock' && move.type === 'Normal' && target.hasType('Ghost')) {
if (move.volatileStatus === 'partiallytrapped' && move.type === 'Normal' && target.hasType('Ghost')) {
return 0;
}
@ -884,8 +903,7 @@ export const Scripts: ModdedBattleScriptsData = {
// Type effectiveness.
// In Gen 1, type effectiveness is applied against each of the target's types.
for (const targetType of target.types) {
let typeMod = this.battle.dex.getEffectiveness(type, targetType);
typeMod = this.battle.runEvent('Effectiveness', this.battle, targetType, move, typeMod);
const typeMod = this.battle.dex.getEffectiveness(type, targetType);
if (typeMod > 0) {
// Super effective against targetType
damage *= 20;
@ -929,7 +947,7 @@ export const Scripts: ModdedBattleScriptsData = {
if (typeof effect === 'string') effect = this.dex.conditions.get(effect);
if (!target?.hp) return 0;
let success = null;
boost = this.runEvent('TryBoost', target, source, effect, { ...boost });
boost = this.runEvent('TryBoost', target, source, effect, {...boost});
let i: BoostID;
for (i in boost) {
const currentBoost: SparseBoostsTable = {};

View File

@ -6,7 +6,7 @@
* Psychic was immune to ghost
*/
export const TypeChart: import('../../../sim/dex-data').ModdedTypeDataTable = {
export const TypeChart: {[k: string]: ModdedTypeData | null} = {
bug: {
damageTaken: {
Bug: 0,

View File

@ -1,4 +1,4 @@
export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDataTable = {
export const Conditions: {[k: string]: ModdedConditionData} = {
invulnerability: {
// Dig/Fly
name: 'invulnerability',

View File

@ -2,7 +2,7 @@
* The japanese version of Blizzard in Gen 1 had a 30% chance to freeze
*/
export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
export const Moves: {[k: string]: ModdedMoveData} = {
blizzard: {
inherit: true,
secondary: {
@ -13,7 +13,12 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
substitute: {
inherit: true,
condition: {
inherit: true,
onStart(target) {
this.add('-start', target, 'Substitute');
this.effectState.hp = Math.floor(target.maxhp / 4) + 1;
delete target.volatiles['partiallytrapped'];
},
onTryHitPriority: -1,
onTryHit(target, source, move) {
if (move.drain) {
this.add('-miss', source);
@ -34,8 +39,10 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
// NOTE: In future generations the damage is capped to the remaining HP of the
// Substitute, here we deliberately use the uncapped damage when tracking lastDamage etc.
// Also, multi-hit moves must always deal the same damage as the first hit for any subsequent hits
const uncappedDamage = move.hit > 1 ? this.lastDamage : this.actions.getDamage(source, target, move);
let uncappedDamage = move.hit > 1 ? this.lastDamage : this.actions.getDamage(source, target, move);
if (!uncappedDamage && uncappedDamage !== 0) return null;
uncappedDamage = this.runEvent('SubDamage', target, source, move, uncappedDamage);
if (!uncappedDamage && uncappedDamage !== 0) return uncappedDamage;
this.lastDamage = uncappedDamage;
target.volatiles['substitute'].hp -= uncappedDamage > target.volatiles['substitute'].hp ?
target.volatiles['substitute'].hp : uncappedDamage;
@ -58,7 +65,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
// Add here counter damage
const lastAttackedBy = target.getLastAttackedBy();
if (!lastAttackedBy) {
target.attackedBy.push({ source, move: move.id, damage: uncappedDamage, thisTurn: true, slot: source.getSlot() });
target.attackedBy.push({source: source, move: move.id, damage: uncappedDamage, thisTurn: true, slot: source.getSlot()});
} else {
lastAttackedBy.move = move.id;
lastAttackedBy.damage = uncappedDamage;
@ -71,6 +78,9 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
}
return accuracy;
},
onEnd(target) {
this.add('-end', target, 'Substitute');
},
},
},
swift: {

View File

@ -1,16 +1,13 @@
export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable = {
export const Rulesets: {[k: string]: ModdedFormatData} = {
standard: {
effectType: 'ValidatorRule',
name: 'Standard',
ruleset: [
'Standard AG',
'Sleep Clause Mod', 'Freeze Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause',
],
ruleset: ['Obtainable', 'Desync Clause Mod', 'Sleep Clause Mod', 'Freeze Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod'],
banlist: ['Dig', 'Fly'],
},
nc1997movelegality: {
nintendocup1997movelegality: {
effectType: 'ValidatorRule',
name: 'NC 1997 Move Legality',
name: 'Nintendo Cup 1997 Move Legality',
desc: "Bans move combinations on Pok\u00e9mon that would only be obtainable in Pok\u00e9mon Yellow.",
banlist: [
// https://www.smogon.com/forums/threads/rby-and-gsc-illegal-movesets.78638/
@ -25,40 +22,40 @@ export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable
'Flareon + Tackle + Growl', 'Flareon + Focus Energy + Ember',
],
onValidateSet(set) {
const rgb97Legality: { [speciesid: string]: { [moveid: string]: 'illegal' | number } } = {
charizard: { fly: 'illegal' },
const rgb97Legality: {[speciesid: string]: {[moveid: string]: 'illegal' | number}} = {
charizard: {fly: 'illegal'},
butterfree: {
confusion: 12, poisonpowder: 15, stunspore: 16, sleeppowder: 17, supersonic: 21,
psybeam: 34, flash: 'illegal', gust: 'illegal',
},
fearow: { payday: 'illegal' },
pikachu: { quickattack: 16, tailwhip: 'illegal', slam: 'illegal', lightscreen: 'illegal' },
raichu: { quickattack: 16, tailwhip: 'illegal', slam: 'illegal', lightscreen: 'illegal' },
nidoranf: { doublekick: 43 },
nidorina: { doublekick: 43 },
nidoqueen: { doublekick: 43 },
nidoranm: { doublekick: 43 },
nidorino: { doublekick: 43 },
nidoking: { doublekick: 43 },
venonat: { poisonpowder: 24, supersonic: 'illegal', confusion: 'illegal' },
venomoth: { poisonpowder: 24, supersonic: 'illegal' },
diglett: { cut: 'illegal' },
dugtrio: { cut: 'illegal' },
psyduck: { amnesia: 'illegal' },
golduck: { amnesia: 'illegal' },
mankey: { lowkick: 'illegal', screech: 'illegal' },
primeape: { lowkick: 'illegal', screech: 'illegal' },
kadabra: { kinesis: 'illegal' },
alakazam: { kinesis: 'illegal' },
rapidash: { payday: 'illegal' },
cubone: { tailwhip: 'illegal', headbutt: 'illegal' },
marowak: { tailwhip: 'illegal', headbutt: 'illegal' },
chansey: { tailwhip: 'illegal' },
tangela: { absorb: 29, growth: 49, vinewhip: 'illegal' },
scyther: { wingattack: 'illegal' },
pinsir: { bind: 'illegal' },
magikarp: { dragonrage: 'illegal' },
eevee: { quickattack: 27, tailwhip: 31, bite: 37, growl: 'illegal', focusenergy: 'illegal' },
fearow: {payday: 'illegal'},
pikachu: {quickattack: 16, tailwhip: 'illegal', slam: 'illegal', lightscreen: 'illegal'},
raichu: {quickattack: 16, tailwhip: 'illegal', slam: 'illegal', lightscreen: 'illegal'},
nidoranf: {doublekick: 43},
nidorina: {doublekick: 43},
nidoqueen: {doublekick: 43},
nidoranm: {doublekick: 43},
nidorino: {doublekick: 43},
nidoking: {doublekick: 43},
venonat: {poisonpowder: 24, supersonic: 'illegal', confusion: 'illegal'},
venomoth: {poisonpowder: 24, supersonic: 'illegal'},
diglett: {cut: 'illegal'},
dugtrio: {cut: 'illegal'},
psyduck: {amnesia: 'illegal'},
golduck: {amnesia: 'illegal'},
mankey: {lowkick: 'illegal', screech: 'illegal'},
primeape: {lowkick: 'illegal', screech: 'illegal'},
kadabra: {kinesis: 'illegal'},
alakazam: {kinesis: 'illegal'},
rapidash: {payday: 'illegal'},
cubone: {tailwhip: 'illegal', headbutt: 'illegal'},
marowak: {tailwhip: 'illegal', headbutt: 'illegal'},
chansey: {tailwhip: 'illegal'},
tangela: {absorb: 29, growth: 49, vinewhip: 'illegal'},
scyther: {wingattack: 'illegal'},
pinsir: {bind: 'illegal'},
magikarp: {dragonrage: 'illegal'},
eevee: {quickattack: 27, tailwhip: 31, bite: 37, growl: 'illegal', focusenergy: 'illegal'},
vaporeon: {
quickattack: 27, tailwhip: 31, watergun: 31, bite: 37, acidarmor: 42, haze: 44, mist: 48, hydropump: 54,
growl: 'illegal', focusenergy: 'illegal', aurorabeam: 'illegal',

View File

@ -1,4 +1,4 @@
export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDataTable = {
export const Conditions: {[k: string]: ModdedConditionData} = {
brn: {
name: 'brn',
effectType: 'Status',
@ -37,7 +37,7 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
effectType: 'Status',
onStart(target, source, sourceEffect) {
if (sourceEffect && sourceEffect.effectType === 'Move') {
this.add('-status', target, 'slp', `[from] move: ${sourceEffect.name}`);
this.add('-status', target, 'slp', '[from] move: ' + sourceEffect.name);
} else {
this.add('-status', target, 'slp');
}
@ -101,14 +101,14 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
},
flinch: {
inherit: true,
onStart: undefined, // no inherit
onStart() {},
},
partiallytrapped: {
name: 'partiallytrapped',
duration: 2,
onBeforeMovePriority: 1,
onStart(target, source, effect) {
this.add('-activate', target, `move: ${effect}`, `[of] ${source}`);
this.add('-activate', target, 'move: ' + effect, '[of] ' + source);
},
onBeforeMove(pokemon) {
if (this.effectState.source && (!this.effectState.source.isActive || this.effectState.source.hp <= 0)) {

View File

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

View File

@ -1,4 +1,4 @@
export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
export const Moves: {[k: string]: ModdedMoveData} = {
bide: {
inherit: true,
priority: 0,
@ -33,14 +33,14 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
onAfterSetStatus(status, pokemon) {
// Sleep, freeze, and partial trap will just pause duration.
if (pokemon.volatiles['flinch']) {
this.effectState.duration!++;
this.effectState.duration++;
} else if (pokemon.volatiles['partiallytrapped']) {
this.effectState.duration!++;
this.effectState.duration++;
} else {
switch (status.id) {
case 'slp':
case 'frz':
this.effectState.duration!++;
this.effectState.duration++;
break;
}
}
@ -59,7 +59,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
pokemon.removeVolatile('bide');
return false;
}
this.actions.moveHit(target, pokemon, move, { damage: this.effectState.totalDamage * 2 } as ActiveMove);
this.actions.moveHit(target, pokemon, move, {damage: this.effectState.totalDamage * 2} as ActiveMove);
pokemon.removeVolatile('bide');
return false;
}
@ -160,20 +160,21 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
volatileStatus: 'rage',
},
condition: {
inherit: true,
// Rage lock
onStart(target, source, effect) {
this.effectState.move = 'rage';
},
onLockMove: 'rage',
onHit(target, source, move) {
if (target.boosts.atk < 6 && (move.category !== 'Status' || move.id === 'disable')) {
this.boost({ atk: 1 });
this.boost({atk: 1});
}
},
},
},
recover: {
inherit: true,
heal: null,
onHit(target) {
if (target.hp === target.maxhp) {
return false;
@ -196,6 +197,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
softboiled: {
inherit: true,
heal: null,
onHit(target) {
// Fail when health is 255 or 511 less than max
if (target.hp === target.maxhp) {
@ -218,7 +220,12 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
}
},
condition: {
inherit: true,
onStart(target) {
this.add('-start', target, 'Substitute');
this.effectState.hp = Math.floor(target.maxhp / 4);
delete target.volatiles['partiallytrapped'];
},
onTryHitPriority: -1,
onTryHit(target, source, move) {
if (target === source) {
this.debug('sub bypass: self hit');
@ -242,6 +249,8 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
damage = target.volatiles['substitute'].hp;
}
if (!damage && damage !== 0) return null;
damage = this.runEvent('SubDamage', target, source, move, damage);
if (!damage && damage !== 0) return damage;
target.volatiles['substitute'].hp -= damage;
this.lastDamage = damage;
if (target.volatiles['substitute'].hp <= 0) {
@ -261,20 +270,24 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
// Add here counter damage
const lastAttackedBy = target.getLastAttackedBy();
if (!lastAttackedBy) {
target.attackedBy.push({ source, move: move.id, damage, slot: source.getSlot(), thisTurn: true });
target.attackedBy.push({source: source, move: move.id, damage: damage, slot: source.getSlot(), thisTurn: true});
} else {
lastAttackedBy.move = move.id;
lastAttackedBy.damage = damage;
}
return 0;
},
onEnd(target) {
this.add('-end', target, 'Substitute');
},
},
secondary: null,
target: "self",
type: "Normal",
},
struggle: {
inherit: true,
ignoreImmunity: { 'Normal': true },
ignoreImmunity: {'Normal': true},
},
wrap: {
inherit: true,

View File

@ -1,763 +1,7 @@
export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable = {
standardag: {
inherit: true,
ruleset: [
'Obtainable', 'Exact HP Mod', 'Cancel Mod',
],
},
export const Rulesets: {[k: string]: ModdedFormatData} = {
standard: {
effectType: 'ValidatorRule',
name: 'Standard',
ruleset: [
'Standard AG',
'Stadium Sleep Clause', 'Freeze Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause',
],
},
stadiumpokecuprentals: {
inherit: true,
onChangeSet(set, format, setHas, teamHas) {
set.level = 50;
switch (this.dex.species.get(set.species).name) {
case 'Bulbasaur':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Leech Seed', 'Toxic', 'Body Slam', 'Razor Leaf'];
break;
case 'Ivysaur':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Razor Leaf', 'Sleep Powder', 'Growth', 'Double-Edge'];
break;
case 'Venusaur':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Psychic', 'Seismic Toss', 'Reflect', 'Thunder Wave'];
break;
case 'Charmander':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Flamethrower', 'Slash', 'Dig', 'Fire Spin'];
break;
case 'Charmeleon':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Flamethrower', 'Counter', 'Seismic Toss', 'Strength'];
break;
case 'Charizard':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Fly', 'Swords Dance', 'Fire Spin', 'Fire Blast'];
break;
case 'Squirtle':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Surf', 'Blizzard', 'Body Slam', 'Dig'];
break;
case 'Wartortle':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Surf', 'Strength', 'Rest', 'Ice Beam'];
break;
case 'Blastoise':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Hydro Pump', 'Skull Bash', 'Withdraw', 'Seismic Toss'];
break;
case 'Caterpie':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['String Shot', 'Tackle'];
break;
case 'Metapod':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['String Shot', 'Tackle'];
break;
case 'Butterfree':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Psychic', 'Supersonic', 'Mega Drain', 'Stun Spore'];
break;
case 'Weedle':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['String Shot', 'Poison Sting'];
break;
case 'Kakuna':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['String Shot', 'Poison Sting'];
break;
case 'Beedrill':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Twineedle', 'Hyper Beam', 'Toxic', 'Focus Energy'];
break;
case 'Pidgey':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Fly', 'Toxic', 'Double-Edge', 'Double Team'];
break;
case 'Pidgeotto':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Fly', 'Quick Attack', 'Sand Attack', 'Take Down'];
break;
case 'Pidgeot':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Mirror Move', 'Fly', 'Quick Attack', 'Sand Attack'];
break;
case 'Rattata':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Super Fang', 'Blizzard', 'Quick Attack', 'Hyper Fang'];
break;
case 'Raticate':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Hyper Fang', 'Hyper Beam', 'Focus Energy', 'Thunder'];
break;
case 'Spearow':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Drill Peck', 'Mirror Move', 'Double Team', 'Double-Edge'];
break;
case 'Fearow':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Drill Peck', 'Mirror Move', 'Fury Attack', 'Swift'];
break;
case 'Ekans':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Earthquake', 'Acid', 'Screech', 'Body Slam'];
break;
case 'Arbok':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Glare', 'Wrap', 'Dig', 'Strength'];
break;
case 'Pikachu':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Thunderbolt', 'Slam', 'Thunder Wave', 'Seismic Toss'];
break;
case 'Raichu':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Thunder', 'Thunder Wave', 'Flash', 'Mega Kick'];
break;
case 'Sandshrew':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Earthquake', 'Slash', 'Seismic Toss', 'Sand Attack'];
break;
case 'Sandslash':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Dig', 'Swift', 'Seismic Toss', 'Sand Attack'];
break;
case 'Nidoran-F':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Toxic', 'Thunderbolt', 'Body Slam', 'Blizzard'];
break;
case 'Nidorina':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Toxic', 'Thunder', 'Double-Edge', 'Ice Beam'];
break;
case 'Nidoqueen':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Toxic', 'Double Kick', 'Bite', 'Earthquake'];
break;
case 'Nidoran-M':
set.evs = { hp: 177, atk: 176, def: 176, spa: 176, spd: 176, spe: 176 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Blizzard', 'Body Slam', 'Thunderbolt', 'Focus Energy'];
break;
case 'Nidorino':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Double-Edge', 'Horn Drill', 'Focus Energy', 'Thunder'];
break;
case 'Nidoking':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Earthquake', 'Horn Drill', 'Rage', 'Substitute'];
break;
case 'Clefairy':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Thunderbolt', 'Psychic', 'Body Slam', 'Blizzard'];
break;
case 'Clefable':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Sing', 'Tri Attack', 'Minimize', 'Ice Beam'];
break;
case 'Vulpix':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Flamethrower', 'Dig', 'Confuse Ray', 'Double-Edge'];
break;
case 'Ninetales':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Fire Blast', 'Skull Bash', 'Confuse Ray', 'Tail Whip'];
break;
case 'Jigglypuff':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Sing', 'Body Slam', 'Seismic Toss', 'Psychic'];
break;
case 'Wigglytuff':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Sing', 'Double-Edge', 'Submission', 'Thunderbolt'];
break;
case 'Zubat':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Confuse Ray', 'Mega Drain', 'Toxic', 'Double-Edge'];
break;
case 'Golbat':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Confuse Ray', 'Mega Drain', 'Bite', 'Haze'];
break;
case 'Oddish':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Petal Dance', 'Toxic', 'Mega Drain', 'Double-Edge'];
break;
case 'Gloom':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Petal Dance', 'Take Down', 'Mega Drain', 'Stun Spore'];
break;
case 'Vileplume':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Petal Dance', 'Sleep Powder', 'Acid', 'Cut'];
break;
case 'Paras':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Spore', 'Slash', 'Dig', 'Mega Drain'];
break;
case 'Parasect':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Spore', 'Take Down', 'Dig', 'Solar Beam'];
break;
case 'Venonat':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Psychic', 'Mega Drain', 'Double-Edge', 'Stun Spore'];
break;
case 'Venomoth':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Psychic', 'Supersonic', 'Solar Beam', 'Swift'];
break;
case 'Diglett':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Earthquake', 'Slash', 'Sand Attack', 'Rock Slide'];
break;
case 'Dugtrio':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Dig', 'Sand Attack', 'Toxic', 'Hyper Beam'];
break;
case 'Meowth':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Slash', 'Thunderbolt', 'Swift', 'Double Team'];
break;
case 'Persian':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Slash', 'Bubble Beam', 'Mimic', 'Growl'];
break;
case 'Psyduck':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Surf', 'Confusion', 'Dig', 'Blizzard'];
break;
case 'Golduck':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Ice Beam', 'Surf', 'Toxic', 'Disable'];
break;
case 'Mankey':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Submission', 'Rock Slide', 'Seismic Toss', 'Screech'];
break;
case 'Primeape':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Fury Swipes', 'Rock Slide', 'Low Kick', 'Screech'];
break;
case 'Growlithe':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Flamethrower', 'Body Slam', 'Reflect', 'Dig'];
break;
case 'Arcanine':
set.evs = { hp: 105, atk: 104, def: 104, spa: 104, spd: 104, spe: 104 };
set.ivs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
set.moves = ['Fire Blast', 'Take Down', 'Dragon Rage', 'Substitute'];
break;
case 'Poliwag':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Body Slam', 'Blizzard', 'Surf', 'Amnesia'];
break;
case 'Poliwhirl':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Hypnosis', 'Surf', 'Ice Beam', 'Earthquake'];
break;
case 'Poliwrath':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Hypnosis', 'Submission', 'Counter', 'Hydro Pump'];
break;
case 'Abra':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Psychic', 'Seismic Toss', 'Reflect', 'Thunder Wave'];
break;
case 'Kadabra':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Psychic', 'Counter', 'Recover', 'Dig'];
break;
case 'Alakazam':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Psybeam', 'Metronome', 'Disable', 'Tri Attack'];
break;
case 'Machop':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Submission', 'Rock Slide', 'Earthquake', 'Focus Energy'];
break;
case 'Machoke':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Submission', 'Strength', 'Rock Slide', 'Focus Energy'];
break;
case 'Machamp':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Low Kick', 'Strength', 'Counter', 'Focus Energy'];
break;
case 'Bellsprout':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Razor Leaf', 'Growth', 'Mega Drain', 'Stun Spore'];
break;
case 'Weepinbell':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Razor Leaf', 'Acid', 'Wrap', 'Toxic'];
break;
case 'Victreebel':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Solar Beam', 'Acid', 'Reflect', 'Slam'];
break;
case 'Tentacool':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Surf', 'Supersonic', 'Mega Drain', 'Blizzard'];
break;
case 'Tentacruel':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Acid', 'Supersonic', 'Hydro Pump', 'Cut'];
break;
case 'Geodude':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Earthquake', 'Seismic Toss', 'Rock Slide', 'Explosion'];
break;
case 'Graveler':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Earthquake', 'Seismic Toss', 'Strength', 'Self-Destruct'];
break;
case 'Golem':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Dig', 'Seismic Toss', 'Fire Blast', 'Metronome'];
break;
case 'Ponyta':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Fire Blast', 'Agility', 'Horn Drill', 'Body Slam'];
break;
case 'Rapidash':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Fire Blast', 'Stomp', 'Toxic', 'Fire Spin'];
break;
case 'Slowpoke':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Surf', 'Psychic', 'Thunder Wave', 'Amnesia'];
break;
case 'Slowbro':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Surf', 'Psychic', 'Disable', 'Withdraw'];
break;
case 'Magnemite':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Thunderbolt', 'Thunder Wave', 'Supersonic', 'Double-Edge'];
break;
case 'Magneton':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Thunderbolt', 'Screech', 'Supersonic', 'Swift'];
break;
case 'Farfetch\u2019d':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Slash', 'Sand Attack', 'Toxic', 'Fly'];
break;
case 'Doduo':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Drill Peck', 'Tri Attack', 'Double Team', 'Reflect'];
break;
case 'Dodrio':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Fly', 'Tri Attack', 'Agility', 'Reflect'];
break;
case 'Seel':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Ice Beam', 'Body Slam', 'Horn Drill', 'Surf'];
break;
case 'Dewgong':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Aurora Beam', 'Heabutt', 'Rest', 'Surf'];
break;
case 'Grimer':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Sludge', 'Body Slam', 'Explosion', 'Screech'];
break;
case 'Muk':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Sludge', 'Thunderbolt', 'Hyper Beam', 'Self-Destruct'];
break;
case 'Shellder':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Surf', 'Explosion', 'Blizzard', 'Tri Attack'];
break;
case 'Cloyster':
set.evs = { hp: 105, atk: 104, def: 104, spa: 104, spd: 104, spe: 104 };
set.ivs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
set.moves = ['Clamp', 'Spike Cannon', 'Ice Beam', 'Supersonic'];
break;
case 'Gastly':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Hypnosis', 'Dream Eater', 'Psychic', 'Confuse Ray'];
break;
case 'Haunter':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Mega Drain', 'Psychic', 'Explosion', 'Confuse Ray'];
break;
case 'Gengar':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Thunderbolt', 'Night Shade', 'Hypnosis', 'Confuse Ray'];
break;
case 'Onix':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Earthquake', 'Rock Slide', 'Strength', 'Explosion'];
break;
case 'Drowzee':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Hypnois', 'Dream Eater', 'Psychic', 'Tri Attack'];
break;
case 'Hypno':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Hypnosis', 'Headbutt', 'Dream Eater', 'Meditate'];
break;
case 'Krabby':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Crabhammer', 'Guillotine', 'Double-Edge', 'Blizzard'];
break;
case 'Kingler':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Crabhammer', 'Guillotine', 'Stomp', 'Substitute'];
break;
case 'Voltorb':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Thunderbolt', 'Thunder Wave', 'Swift', 'Explosion'];
break;
case 'Electrode':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Thunder', 'Thunder Wave', 'Swift', 'Self-Destruct'];
break;
case 'Exeggcute':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Psychic', 'Explosion', 'Leech Seed', 'Toxic'];
break;
case 'Exeggutor':
set.evs = { hp: 105, atk: 104, def: 104, spa: 104, spd: 104, spe: 104 };
set.ivs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
set.moves = ['Mega Drain', 'Stun Spore', 'Leech Seed', 'Egg Bomb'];
break;
case 'Cubone':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Earthquake', 'Submission', 'Blizzard', 'Strength'];
break;
case 'Marowak':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Bonemerang', 'Thrash', 'Fire Blast', 'Focus Energy'];
break;
case 'Hitmonlee':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['High Jump Kick', 'Mega Kick', 'Metronome', 'Seismic Toss'];
break;
case 'Hitmonchan':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Submission', 'Thunder Punch', 'Ice Punch', 'Strength'];
break;
case 'Lickitung':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Strength', 'Blizzard', 'Thunder', 'Fire Blast'];
break;
case 'Koffing':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Sludge', 'Toxic', 'Thunderbolt', 'Explosion'];
break;
case 'Weezing':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Sludge', 'Hyper Beam', 'Fire Blast', 'Self-Destruct'];
break;
case 'Rhyhorn':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Earthquake', 'Body Slam', 'Rock Slide', 'Fire Blast'];
break;
case 'Rhydon':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Dig', 'Strength', 'Thunder', 'Surf'];
break;
case 'Chansey':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Thunder', 'Fire Blast', 'Minimize', 'Rest'];
break;
case 'Tangela':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Mega Drain', 'Growth', 'Toxic', 'Double-Edge'];
break;
case 'Kangaskhan':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Dizzy Punch', 'Rock Slide', 'Surf', 'Thunderbolt'];
break;
case 'Horsea':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Hydro Pump', 'Toxic', 'Smokescreen', 'Ice Beam'];
break;
case 'Seadra':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Surf', 'Toxic', 'Smokescreen', 'Swift'];
break;
case 'Goldeen':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Surf', 'Supersonic', 'Horn Drill', 'Blizzard'];
break;
case 'Seaking':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Waterfall', 'Supersonic', 'Horn Attack', 'Ice Beam'];
break;
case 'Staryu':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Hydro Pump', 'Recover', 'Thunderbolt', 'Psychic'];
break;
case 'Starmie':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Surf', 'Thunder', 'Swift', 'Harden'];
break;
case 'Mr. Mime':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Barrier', 'Psychic', 'Metronome', 'Seismic Toss'];
break;
case 'Scyther':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Slash', 'Wing Attack', 'Leer', 'Double Team'];
break;
case 'Jynx':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Ice Punch', 'Mega Punch', 'Psychic', 'Lovely Kiss'];
break;
case 'Electabuzz':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Thunder Punch', 'Mega Punch', 'Psychic', 'Thunder Wave'];
break;
case 'Magmar':
set.evs = { hp: 121, atk: 120, def: 120, spa: 120, spd: 120, spe: 120 };
set.ivs = { hp: 6, atk: 8, def: 8, spa: 6, spd: 6, spe: 6 };
set.moves = ['Fire Punch', 'Mega Punch', 'Psychic', 'Smokescreen'];
break;
case 'Pinsir':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Strength', 'Harden', 'Seismic Toss', 'Guillotine'];
break;
case 'Tauros':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Double-Edge', 'Fire Blast', 'Tail Whip', 'Bide'];
break;
case 'Magikarp':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Splash', 'Tackle'];
break;
case 'Gyarados':
set.evs = { hp: 105, atk: 104, def: 104, spa: 104, spd: 104, spe: 104 };
set.ivs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
set.moves = ['Surf', 'Dragon Rage', 'Bite', 'Fire Blast'];
break;
case 'Lapras':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Ice Beam', 'Solar Beam', 'Body Slam', 'Sing'];
break;
case 'Ditto':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Transform'];
break;
case 'Eevee':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Body Slam', 'Swift', 'Sand Attack', 'Toxic'];
break;
case 'Vaporeon':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Surf', 'Quick Attack', 'Sand Attack', 'Acid Armor'];
break;
case 'Jolteon':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Thunderbolt', 'Pin Missile', 'Toxic', 'Sand Attack'];
break;
case 'Flareon':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Fire Blast', 'Take Down', 'Smog', 'Sand Attack'];
break;
case 'Omanyte':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Surf', 'Ice Beam', 'Double Edge', 'Double Team'];
break;
case 'Omastar':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Hydro Pump', 'Submission', 'Spike Cannon', 'Withdraw'];
break;
case 'Kabuto':
set.evs = { hp: 145, atk: 144, def: 144, spa: 144, spd: 144, spe: 144 };
set.ivs = { hp: 12, atk: 12, def: 10, spa: 12, spd: 12, spe: 10 };
set.moves = ['Hydro Pump', 'Blizzard', 'Slash', 'Double Team'];
break;
case 'Kabutops':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Surf', 'Swords Dance', 'Mega Kick', 'Submission'];
break;
case 'Aerodactyl':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Fly', 'Hyper Beam', 'Supersonic', 'Dragon Rage'];
break;
case 'Snorlax':
set.evs = { hp: 113, atk: 112, def: 112, spa: 112, spd: 112, spe: 112 };
set.ivs = { hp: 4, atk: 4, def: 4, spa: 4, spd: 4, spe: 6 };
set.moves = ['Mega Kick', 'Rock Slide', 'Metronome', 'Rest'];
break;
case 'Articuno':
set.evs = { hp: 97, atk: 96, def: 96, spa: 96, spd: 96, spe: 96 };
set.ivs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
set.moves = ['Ice Beam', 'Sky Attack', 'Razor Wind', 'Substitute'];
break;
case 'Zapdos':
set.evs = { hp: 97, atk: 96, def: 96, spa: 96, spd: 96, spe: 96 };
set.ivs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
set.moves = ['Thunderbolt', 'Sky Attack', 'Thunder Wave', 'Flash'];
break;
case 'Moltres':
set.evs = { hp: 97, atk: 96, def: 96, spa: 96, spd: 96, spe: 96 };
set.ivs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
set.moves = ['Fire Blast', 'Fly', 'Swift', 'Substitute'];
break;
case 'Dratini':
set.evs = { hp: 161, atk: 160, def: 160, spa: 160, spd: 160, spe: 160 };
set.ivs = { hp: 14, atk: 12, def: 14, spa: 14, spd: 14, spe: 14 };
set.moves = ['Hyper Beam', 'Body Slam', 'Thunderbolt', 'Thunder Wave'];
break;
case 'Dragonair':
set.evs = { hp: 129, atk: 128, def: 128, spa: 128, spd: 128, spe: 128 };
set.ivs = { hp: 10, atk: 8, def: 10, spa: 10, spd: 10, spe: 8 };
set.moves = ['Hyper Beam', 'Swift', 'Ice Beam', 'Thunder Wave'];
break;
case 'Dragonite':
set.evs = { hp: 97, atk: 96, def: 96, spa: 96, spd: 96, spe: 96 };
set.ivs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
set.moves = ['Slam', 'Dragon Rage', 'Thunder', 'Agility'];
}
},
ruleset: ['Obtainable', 'Stadium Sleep Clause', 'Freeze Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Exact HP Mod', 'Cancel Mod'],
},
};

View File

@ -36,9 +36,6 @@ export const Scripts: ModdedBattleScriptsData = {
this.modifyStat!(statName, [100, 66, 50, 40, 33, 28, 25][-this.boosts[statName]] / 100);
}
}
if (this.modifiedStats![statName] > 999) {
this.modifiedStats![statName] = 999;
}
}
},
// Stadium's fixed boosting function.
@ -72,8 +69,7 @@ export const Scripts: ModdedBattleScriptsData = {
},
actions: {
inherit: true,
runMove(moveOrMoveName, pokemon, targetLoc, options) {
let sourceEffect = options?.sourceEffect;
runMove(moveOrMoveName, pokemon, targetLoc, sourceEffect) {
const move = this.dex.getActiveMove(moveOrMoveName);
const target = this.battle.getTarget(pokemon, move, targetLoc);
if (target?.subFainted) target.subFainted = null;
@ -81,7 +77,7 @@ export const Scripts: ModdedBattleScriptsData = {
this.battle.setActiveMove(move, pokemon, target);
if (pokemon.moveThisTurn || !this.battle.runEvent('BeforeMove', pokemon, target, move)) {
this.battle.debug(`${pokemon.fullname} move interrupted; movedThisTurn: ${pokemon.moveThisTurn}`);
this.battle.debug('' + pokemon.fullname + ' move interrupted; movedThisTurn: ' + pokemon.moveThisTurn);
this.battle.clearActiveMove(true);
// This is only run for sleep
this.battle.runEvent('AfterMoveSelf', pokemon, target, move);
@ -103,14 +99,12 @@ export const Scripts: ModdedBattleScriptsData = {
} else {
sourceEffect = move;
}
this.battle.actions.useMove(move, pokemon, { target, sourceEffect });
this.battle.actions.useMove(move, pokemon, target, sourceEffect);
},
// This function deals with AfterMoveSelf events.
// This leads with partial trapping moves shenanigans after the move has been used.
useMove(moveOrMoveName, pokemon, options) {
let sourceEffect = options?.sourceEffect;
let target = options?.target;
const moveResult = this.useMoveInner(moveOrMoveName, pokemon, { target, sourceEffect });
useMove(moveOrMoveName, pokemon, target, sourceEffect) {
const moveResult = this.useMoveInner(moveOrMoveName, pokemon, target, sourceEffect);
if (!sourceEffect && this.battle.effect.id) sourceEffect = this.battle.effect;
const baseMove = this.battle.dex.moves.get(moveOrMoveName);
@ -167,9 +161,7 @@ export const Scripts: ModdedBattleScriptsData = {
},
// This is the function that actually uses the move, running ModifyMove events.
// It uses the move and then deals with the effects after the move.
useMoveInner(moveOrMoveName, pokemon, options) {
let sourceEffect = options?.sourceEffect;
let target = options?.target;
useMoveInner(moveOrMoveName, pokemon, target, sourceEffect) {
if (!sourceEffect && this.battle.effect.id) sourceEffect = this.battle.effect;
const baseMove = this.battle.dex.moves.get(moveOrMoveName);
let move = this.battle.dex.getActiveMove(baseMove);
@ -199,8 +191,8 @@ export const Scripts: ModdedBattleScriptsData = {
return false;
}
if (sourceEffect) attrs += `|[from] ${this.battle.dex.conditions.get(sourceEffect).name}`;
this.battle.addMove('move', pokemon, move.name, `${target}${attrs}`);
if (sourceEffect) attrs += '|[from]' + this.battle.dex.conditions.get(sourceEffect);
this.battle.addMove('move', pokemon, move.name, target + attrs);
if (!this.battle.singleEvent('Try', move, null, pokemon, target, move)) {
return true;
@ -231,8 +223,10 @@ export const Scripts: ModdedBattleScriptsData = {
return true;
}
this.battle.singleEvent('AfterMoveSecondarySelf', move, null, pokemon, target, move);
this.battle.runEvent('AfterMoveSecondarySelf', pokemon, target, move);
if (!move.negateSecondary) {
this.battle.singleEvent('AfterMoveSecondarySelf', move, null, pokemon, target, move);
this.battle.runEvent('AfterMoveSecondarySelf', pokemon, target, move);
}
return true;
},
tryMoveHit(target, pokemon, move) {
@ -250,7 +244,10 @@ export const Scripts: ModdedBattleScriptsData = {
}
// Then, check if the Pokemon is immune to this move.
if (!target.runImmunity(move, true)) {
if (
(!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) &&
!target.runImmunity(move.type, true)
) {
if (move.selfdestruct) {
this.battle.faint(pokemon, pokemon, move);
}
@ -300,16 +297,10 @@ export const Scripts: ModdedBattleScriptsData = {
}
accuracy = this.battle.runEvent('Accuracy', target, pokemon, move, accuracy);
// Stadium attempts to fix the 1/256 miss by rerolling if the first value
// would trigger the 1/256 miss.
let randomValue = this.battle.random(256);
if (randomValue === 256) randomValue = this.battle.random(256);
if (accuracy !== true && randomValue > accuracy) {
// Stadium fixes the 1/256 accuracy bug.
if (accuracy !== true && !this.battle.randomChance(accuracy + 1, 256)) {
this.battle.attrLastMove('[miss]');
this.battle.add('-miss', pokemon);
if (accuracy === 255) {
this.battle.hint("In Pokemon Stadium, moves with 100% accuracy can still miss 1/65536 of the time.");
}
damage = false;
this.battle.lastDamage = 0;
}
@ -366,8 +357,10 @@ export const Scripts: ModdedBattleScriptsData = {
if (move.ohko) this.battle.add('-ohko');
this.battle.singleEvent('AfterMoveSecondary', move, null, target, pokemon, move);
this.battle.runEvent('AfterMoveSecondary', target, pokemon, move);
if (!move.negateSecondary) {
this.battle.singleEvent('AfterMoveSecondary', move, null, target, pokemon, move);
this.battle.runEvent('AfterMoveSecondary', target, pokemon, move);
}
return damage;
},
@ -390,7 +383,7 @@ export const Scripts: ModdedBattleScriptsData = {
const targetHadSub = !!target.volatiles['substitute'];
if (targetHadSub && moveData.volatileStatus && moveData.volatileStatus === 'partiallytrapped') {
target.addVolatile(moveData.volatileStatus, pokemon, move);
if (!pokemon.volatiles['partialtrappinglock'] || pokemon.volatiles['partialtrappinglock'].duration! > 1) {
if (!pokemon.volatiles['partialtrappinglock'] || pokemon.volatiles['partialtrappinglock'].duration > 1) {
target.volatiles[moveData.volatileStatus].duration = 2;
}
}
@ -548,8 +541,10 @@ export const Scripts: ModdedBattleScriptsData = {
}
// Let's see if the target is immune to the move.
if (!target.runImmunity(move, true)) {
return false;
if (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) {
if (!target.runImmunity(move.type, true)) {
return false;
}
}
// Is it an OHKO move?
@ -606,26 +601,26 @@ export const Scripts: ModdedBattleScriptsData = {
let critChance = source.species.baseStats['spe'] + 76;
// Now we right logical shift it two places, essentially dividing by 4 and flooring it.
critChance >>= 2;
critChance = critChance >> 2;
// Now we check for focus energy volatile.
if (source.volatiles['focusenergy']) {
// If it exists, crit chance is multiplied by 4 and floored with a logical left shift.
critChance <<= 2;
critChance = critChance << 2;
// Then we add 160.
critChance += 160;
} else {
// If it is not active, we left shift it by 1.
critChance <<= 1;
critChance = critChance << 1;
}
// Now we check for the move's critical hit ratio.
if (move.critRatio === 2) {
// High crit ratio, we multiply the result so far by 4.
critChance <<= 2;
critChance = critChance << 2;
} else if (move.critRatio === 1) {
// Normal hit ratio, we divide the crit chance by 2 and floor the result again.
critChance >>= 1;
critChance = critChance >> 1;
}
// Now we make sure it's a number between 1 and 255.
@ -693,7 +688,7 @@ export const Scripts: ModdedBattleScriptsData = {
}
// When either attack or defense are higher than 256, they are both divided by 4 and moded by 256.
// This is what causes the rollover bugs.
// This is what cuases the roll over bugs.
if (attack >= 256 || defense >= 256) {
attack = this.battle.clampIntRange(Math.floor(attack / 4) % 256, 1);
// Defense isn't checked on the cartridge, but we don't want those / 0 bugs on the sim.

View File

@ -1,4 +1,4 @@
export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDataTable = {
export const Conditions: {[k: string]: ModdedConditionData} = {
brn: {
name: 'brn',
effectType: 'Status',
@ -18,7 +18,7 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
inherit: true,
onBeforeMovePriority: 2,
onBeforeMove(pokemon) {
if (this.randomChance(63, 256)) {
if (this.randomChance(1, 4)) {
this.add('cant', pokemon, 'par');
return false;
}
@ -29,7 +29,7 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
effectType: 'Status',
onStart(target, source, sourceEffect) {
if (sourceEffect && sourceEffect.effectType === 'Move') {
this.add('-status', target, 'slp', `[from] move: ${sourceEffect.name}`);
this.add('-status', target, 'slp', '[from] move: ' + sourceEffect.name);
} else {
this.add('-status', target, 'slp');
}
@ -62,8 +62,8 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
this.add('cant', pokemon, 'frz');
return false;
},
onModifyMove: undefined, // no inherit
onDamagingHit: undefined, // no inherit
onModifyMove() {},
onDamagingHit() {},
onAfterMoveSecondary(target, source, move) {
if ((move.secondary && move.secondary.status === 'brn') || move.statusRoll === 'brn') {
target.cureStatus();
@ -177,17 +177,12 @@ 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'];
if (!target.side.getSideCondition('safeguard')) target.addVolatile('confusion');
},
onLockMove() {
onLockMove(pokemon) {
return this.effectState.move;
},
onMoveAborted(pokemon) {
@ -197,7 +192,7 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
const move = this.dex.moves.get(this.effectState.move);
if (move.id) {
this.debug('Forcing into ' + move.id);
this.queue.changeAction(pokemon, { choice: 'move', moveid: move.id });
this.queue.changeAction(pokemon, {choice: 'move', moveid: move.id});
}
},
},
@ -228,7 +223,7 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
},
onStallMove() {
const counter = Math.floor(this.effectState.counter) || 127;
this.debug(`Success chance: ${Math.round(counter * 1000 / 255) / 10}% (${counter}/255)`);
this.debug("Success chance: " + Math.round(counter * 1000 / 255) / 10 + "% (" + counter + "/255)");
return this.randomChance(counter, 255);
},
onRestart() {

View File

@ -1,9 +1,9 @@
export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormatsDataTable = {
export const FormatsData: {[k: string]: ModdedSpeciesFormatsData} = {
bulbasaur: {
tier: "LC",
},
ivysaur: {
tier: "ZU",
tier: "NFE",
},
venusaur: {
tier: "UUBL",
@ -12,7 +12,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
charmeleon: {
tier: "ZUBL",
tier: "NFE",
},
charizard: {
tier: "UUBL",
@ -21,7 +21,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
wartortle: {
tier: "PU",
tier: "NFE",
},
blastoise: {
tier: "UU",
@ -33,7 +33,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
butterfree: {
tier: "ZU",
tier: "NU",
},
weedle: {
tier: "LC",
@ -42,7 +42,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
beedrill: {
tier: "ZUBL",
tier: "NU",
},
pidgey: {
tier: "LC",
@ -57,7 +57,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
raticate: {
tier: "PU",
tier: "NU",
},
spearow: {
tier: "LC",
@ -69,7 +69,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
arbok: {
tier: "PU",
tier: "NU",
},
pichu: {
tier: "LC",
@ -90,7 +90,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
nidorina: {
tier: "ZU",
tier: "NFE",
},
nidoqueen: {
tier: "UU",
@ -108,7 +108,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
clefairy: {
tier: "PU",
tier: "NFE",
},
clefable: {
tier: "UUBL",
@ -132,7 +132,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
golbat: {
tier: "PU",
tier: "NFE",
},
crobat: {
tier: "UU",
@ -153,13 +153,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
parasect: {
tier: "ZU",
tier: "NU",
},
venonat: {
tier: "LC",
},
venomoth: {
tier: "PU",
tier: "NU",
},
diglett: {
tier: "LC",
@ -168,7 +168,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
meowth: {
tier: "ZU",
tier: "LC",
},
persian: {
tier: "NU",
@ -192,10 +192,10 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
poliwag: {
tier: "ZUBL",
tier: "LC",
},
poliwhirl: {
tier: "PUBL",
tier: "NU",
},
poliwrath: {
tier: "NUBL",
@ -204,19 +204,19 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
abra: {
tier: "PU",
tier: "LC",
},
kadabra: {
tier: "UU",
},
alakazam: {
tier: "OU",
tier: "UUBL",
},
machop: {
tier: "LC",
},
machoke: {
tier: "PU",
tier: "NU",
},
machamp: {
tier: "OU",
@ -225,19 +225,19 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
weepinbell: {
tier: "ZU",
tier: "NFE",
},
victreebel: {
tier: "UU",
},
tentacool: {
tier: "ZU",
tier: "NU",
},
tentacruel: {
tier: "UUBL",
},
geodude: {
tier: "PU",
tier: "LC",
},
graveler: {
tier: "NU",
@ -246,13 +246,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
ponyta: {
tier: "ZU",
tier: "LC",
},
rapidash: {
tier: "NU",
},
slowpoke: {
tier: "LC",
tier: "NU",
},
slowbro: {
tier: "UU",
@ -267,10 +267,10 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
farfetchd: {
tier: "PU",
tier: "NU",
},
doduo: {
tier: "ZU",
tier: "LC",
},
dodrio: {
tier: "UU",
@ -282,10 +282,10 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
grimer: {
tier: "ZU",
tier: "LC",
},
muk: {
tier: "UU",
tier: "UUBL",
},
shellder: {
tier: "LC",
@ -294,7 +294,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
gastly: {
tier: "PU",
tier: "NU",
},
haunter: {
tier: "UU",
@ -303,13 +303,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
onix: {
tier: "ZU",
tier: "LC",
},
steelix: {
tier: "OU",
},
drowzee: {
tier: "PU",
tier: "LC",
},
hypno: {
tier: "UU",
@ -321,7 +321,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
voltorb: {
tier: "PU",
tier: "NU",
},
electrode: {
tier: "UU",
@ -333,7 +333,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
cubone: {
tier: "PU",
tier: "NU",
},
marowak: {
tier: "OU",
@ -345,7 +345,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
hitmonchan: {
tier: "PU",
tier: "NU",
},
hitmontop: {
tier: "NU",
@ -354,13 +354,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
koffing: {
tier: "ZU",
tier: "LC",
},
weezing: {
tier: "NU",
},
rhyhorn: {
tier: "PU",
tier: "LC",
},
rhydon: {
tier: "OU",
@ -372,7 +372,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
tangela: {
tier: "PU",
tier: "NU",
},
kangaskhan: {
tier: "UUBL",
@ -381,7 +381,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
seadra: {
tier: "PU",
tier: "NU",
},
kingdra: {
tier: "UUBL",
@ -390,10 +390,10 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
seaking: {
tier: "PU",
tier: "NU",
},
staryu: {
tier: "ZUBL",
tier: "LC",
},
starmie: {
tier: "OU",
@ -414,13 +414,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
elekid: {
tier: "PU",
tier: "NU",
},
electabuzz: {
tier: "UU",
},
magby: {
tier: "ZU",
tier: "LC",
},
magmar: {
tier: "NU",
@ -441,16 +441,16 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UUBL",
},
ditto: {
tier: "ZU",
tier: "NU",
},
eevee: {
tier: "ZUBL",
tier: "LC",
},
vaporeon: {
tier: "OU",
},
jolteon: {
tier: "OU",
tier: "UUBL",
},
flareon: {
tier: "NU",
@ -468,7 +468,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UUBL",
},
omanyte: {
tier: "ZU",
tier: "LC",
},
omastar: {
tier: "UU",
@ -480,7 +480,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
aerodactyl: {
tier: "UU",
tier: "UUBL",
},
snorlax: {
tier: "OU",
@ -513,7 +513,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
bayleef: {
tier: "PU",
tier: "NU",
},
meganium: {
tier: "UUBL",
@ -522,7 +522,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
quilava: {
tier: "ZU",
tier: "NFE",
},
typhlosion: {
tier: "UUBL",
@ -531,22 +531,22 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
croconaw: {
tier: "ZU",
tier: "NFE",
},
feraligatr: {
tier: "UU",
tier: "NUBL",
},
sentret: {
tier: "LC",
},
furret: {
tier: "PU",
tier: "NU",
},
hoothoot: {
tier: "LC",
},
noctowl: {
tier: "PU",
tier: "NU",
},
ledyba: {
tier: "LC",
@ -558,7 +558,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
ariados: {
tier: "ZUBL",
tier: "NU",
},
chinchou: {
tier: "NU",
@ -570,7 +570,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
togetic: {
tier: "ZU",
tier: "NU",
},
natu: {
tier: "LC",
@ -582,7 +582,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
flaaffy: {
tier: "PU",
tier: "NU",
},
ampharos: {
tier: "UU",
@ -591,7 +591,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
azumarill: {
tier: "PU",
tier: "NU",
},
sudowoodo: {
tier: "NU",
@ -606,16 +606,16 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
aipom: {
tier: "ZU",
tier: "NU",
},
sunkern: {
tier: "LC",
},
sunflora: {
tier: "PU",
tier: "NU",
},
yanma: {
tier: "ZU",
tier: "NU",
},
wooper: {
tier: "LC",
@ -624,16 +624,16 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
murkrow: {
tier: "PU",
tier: "NU",
},
misdreavus: {
tier: "OU",
},
unown: {
tier: "ZU",
tier: "NU",
},
wobbuffet: {
tier: "ZU",
tier: "NU",
},
girafarig: {
tier: "UU",
@ -645,7 +645,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
dunsparce: {
tier: "PU",
tier: "NU",
},
gligar: {
tier: "UU",
@ -666,7 +666,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
sneasel: {
tier: "PU",
tier: "NU",
},
teddiursa: {
tier: "LC",
@ -678,7 +678,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
magcargo: {
tier: "PU",
tier: "NU",
},
swinub: {
tier: "LC",
@ -687,7 +687,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
corsola: {
tier: "PU",
tier: "NU",
},
remoraid: {
tier: "LC",
@ -696,16 +696,16 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
delibird: {
tier: "PU",
tier: "NU",
},
mantine: {
tier: "ZUBL",
tier: "NU",
},
skarmory: {
tier: "OU",
},
houndour: {
tier: "PU",
tier: "NU",
},
houndoom: {
tier: "UUBL",

View File

@ -1,11 +1,11 @@
export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
export const Items: {[k: string]: ModdedItemData} = {
berryjuice: {
inherit: true,
isNonstandard: null,
},
blackbelt: {
inherit: true,
onModifyAtk: undefined, // no inherit
onModifyAtk() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Fighting') {
return damage * 1.1;
@ -14,7 +14,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
blackglasses: {
inherit: true,
onModifySpA: undefined, // no inherit
onModifySpA() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Dark') {
return damage * 1.1;
@ -31,7 +31,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
charcoal: {
inherit: true,
onModifySpA: undefined, // no inherit
onModifySpA() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Fire') {
return damage * 1.1;
@ -40,7 +40,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
dragonfang: {
inherit: true,
onModifySpA: undefined, // no inherit
onModifySpA() {},
},
dragonscale: {
inherit: true,
@ -65,7 +65,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
hardstone: {
inherit: true,
onModifyAtk: undefined, // no inherit
onModifyAtk() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Rock') {
return damage * 1.1;
@ -106,7 +106,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
lightball: {
inherit: true,
// In Gen 2 this happens in stat calculation directly.
onModifySpA: undefined, // no inherit
onModifySpA() {},
},
loveball: {
inherit: true,
@ -127,7 +127,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
magnet: {
inherit: true,
onModifySpA: undefined, // no inherit
onModifySpA() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Electric') {
return damage * 1.1;
@ -136,7 +136,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
metalcoat: {
inherit: true,
onModifyAtk: undefined, // no inherit
onModifyAtk() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Steel') {
return damage * 1.1;
@ -146,12 +146,12 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
metalpowder: {
inherit: true,
// In Gen 2 this happens in stat calculation directly.
onModifyDef: undefined, // no inherit
onModifySpD: undefined, // no inherit
onModifyDef() {},
onModifySpD() {},
},
miracleseed: {
inherit: true,
onModifySpA: undefined, // no inherit
onModifySpA() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Grass') {
return damage * 1.1;
@ -164,7 +164,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
mysticwater: {
inherit: true,
onModifySpA: undefined, // no inherit
onModifySpA() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Water') {
return damage * 1.1;
@ -173,7 +173,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
nevermeltice: {
inherit: true,
onModifySpA: undefined, // no inherit
onModifySpA() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Ice') {
return damage * 1.1;
@ -182,7 +182,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
poisonbarb: {
inherit: true,
onModifyAtk: undefined, // no inherit
onModifyAtk() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Poison') {
return damage * 1.1;
@ -191,7 +191,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
sharpbeak: {
inherit: true,
onModifyAtk: undefined, // no inherit
onModifyAtk() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Flying') {
return damage * 1.1;
@ -200,7 +200,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
silverpowder: {
inherit: true,
onModifyAtk: undefined, // no inherit
onModifyAtk() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Bug') {
return damage * 1.1;
@ -209,7 +209,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
softsand: {
inherit: true,
onModifyAtk: undefined, // no inherit
onModifyAtk() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Ground') {
return damage * 1.1;
@ -218,7 +218,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
spelltag: {
inherit: true,
onModifyAtk: undefined, // no inherit
onModifyAtk() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Ghost') {
return damage * 1.1;
@ -241,22 +241,22 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
thickclub: {
inherit: true,
// In Gen 2 this happens in stat calculation directly.
onModifyAtk: undefined, // no inherit
onModifyAtk() {},
},
twistedspoon: {
inherit: true,
onModifySpA: undefined, // no inherit
onModifySpA() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Psychic') {
return damage * 1.1;
}
},
},
berry: {
berserkgene: {
inherit: true,
isNonstandard: null,
},
berserkgene: {
berry: {
inherit: true,
isNonstandard: null,
},
@ -290,7 +290,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
pinkbow: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Normal') {
return damage * 1.1;
@ -300,7 +300,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
polkadotbow: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifyDamage(damage, source, target, move) {
if (move?.type === 'Normal') {
return damage * 1.1;

View File

@ -1,4 +1,4 @@
export const Learnsets: import('../../../sim/dex-species').ModdedLearnsetDataTable = {
export const Learnsets: {[k: string]: ModdedLearnsetData} = {
missingno: {
learnset: {
blizzard: ["1M"],

View File

@ -2,7 +2,7 @@
* Gen 2 moves
*/
export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
export const Moves: {[k: string]: ModdedMoveData} = {
aeroblast: {
inherit: true,
critRatio: 3,
@ -23,7 +23,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
return false;
}
if (target.hp <= target.maxhp / 2) {
this.boost({ atk: 2 }, null, null, this.dex.conditions.get('bellydrum2'));
this.boost({atk: 2}, null, null, this.dex.conditions.get('bellydrum2'));
return false;
}
this.directDamage(target.maxhp / 2);
@ -45,16 +45,70 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
}
boosts = target.boosts.atk - originalStage;
target.boosts.atk = originalStage;
this.boost({ atk: boosts });
this.boost({atk: boosts});
},
},
bide: {
inherit: true,
condition: {
inherit: true,
duration: 3,
durationCallback(target, source, effect) {
return this.random(3, 5);
},
onLockMove: 'bide',
onStart(pokemon) {
this.effectState.totalDamage = 0;
this.add('-start', pokemon, 'move: Bide');
},
onDamagePriority: -101,
onDamage(damage, target, source, move) {
if (!move || move.effectType !== 'Move' || !source) return;
this.effectState.totalDamage += damage;
this.effectState.lastDamageSource = source;
},
onBeforeMove(pokemon, target, move) {
if (this.effectState.duration === 1) {
this.add('-end', pokemon, 'move: Bide');
if (!this.effectState.totalDamage) {
this.add('-fail', pokemon);
return false;
}
target = this.effectState.lastDamageSource;
if (!target) {
this.add('-fail', pokemon);
return false;
}
if (!target.isActive) {
const possibleTarget = this.getRandomTarget(pokemon, this.dex.moves.get('pound'));
if (!possibleTarget) {
this.add('-miss', pokemon);
return false;
}
target = possibleTarget;
}
const moveData = {
id: 'bide',
name: "Bide",
accuracy: 100,
damage: this.effectState.totalDamage * 2,
category: "Physical",
priority: 0,
flags: {contact: 1, protect: 1},
effectType: 'Move',
type: 'Normal',
} as unknown as ActiveMove;
this.actions.tryMoveHit(target, pokemon, moveData);
pokemon.removeVolatile('bide');
return false;
}
this.add('-activate', pokemon, 'move: Bide');
},
onMoveAborted(pokemon) {
pokemon.removeVolatile('bide');
},
onEnd(pokemon) {
this.add('-end', pokemon, 'move: Bide', '[silent]');
},
},
},
counter: {
@ -69,8 +123,8 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
}
return false;
},
beforeTurnCallback: undefined, // no inherit
onTry: undefined, // no inherit
beforeTurnCallback() {},
onTry() {},
condition: {},
priority: -1,
},
@ -85,12 +139,12 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
curse: {
inherit: true,
condition: {
inherit: true,
onAfterMoveSelfPriority: 0, // explicit
onStart(pokemon, source) {
this.add('-start', pokemon, 'Curse', '[of] ' + source);
},
onAfterMoveSelf(pokemon) {
this.damage(pokemon.baseMaxhp / 4);
},
onResidual: undefined, // no inherit
},
},
detect: {
@ -103,7 +157,10 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
return source.status !== 'slp';
},
condition: {
inherit: true,
duration: 2,
onImmunity(type, pokemon) {
if (type === 'sandstorm') return false;
},
onInvulnerability(target, source, move) {
if (move.id === 'earthquake' || move.id === 'magnitude' || move.id === 'fissure') {
return;
@ -115,7 +172,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
if (source.volatiles['lockon'] && target === source.volatiles['lockon'].source) return;
return false;
},
onSourceModifyDamage: undefined, // no inherit
onSourceBasePower(basePower, target, source, move) {
if (move.id === 'earthquake' || move.id === 'magnitude') {
return this.chainModify(2);
@ -130,19 +186,43 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
encore: {
inherit: true,
condition: {
inherit: true,
durationCallback() {
return this.random(3, 7);
},
onStart(target) {
const lockedMove = target.lastMoveEncore?.id || '';
const moveSlot = lockedMove ? target.getMoveData(lockedMove) : null;
if (!moveSlot || target.lastMoveEncore?.flags['failencore'] || moveSlot.pp <= 0) {
const moveIndex = lockedMove ? target.moves.indexOf(lockedMove) : -1;
if (moveIndex < 0 || target.lastMoveEncore?.flags['failencore'] || target.moveSlots[moveIndex].pp <= 0) {
// it failed
return false;
}
this.effectState.move = lockedMove;
this.add('-start', target, 'Encore');
},
onOverrideAction(pokemon) {
return this.effectState.move;
},
onResidualOrder: 13,
onResidualSubOrder: undefined, // no inherit
onResidual(target) {
const lockedMoveIndex = target.moves.indexOf(this.effectState.move);
if (lockedMoveIndex >= 0 && target.moveSlots[lockedMoveIndex].pp <= 0) {
// early termination if you run out of PP
target.removeVolatile('encore');
}
},
onEnd(target) {
this.add('-end', target, 'Encore');
},
onDisableMove(pokemon) {
if (!this.effectState.move || !pokemon.hasMove(this.effectState.move)) {
return;
}
for (const moveSlot of pokemon.moveSlots) {
if (moveSlot.id !== this.effectState.move) {
pokemon.disableMove(moveSlot.id);
}
}
},
},
},
endure: {
@ -151,7 +231,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
explosion: {
inherit: true,
flags: { protect: 1, mirror: 1, metronome: 1, noparentalbond: 1, nosketch: 1 },
noSketch: true,
},
flail: {
inherit: true,
@ -164,7 +244,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
return source.status !== 'slp';
},
condition: {
inherit: true,
duration: 2,
onInvulnerability(target, source, move) {
if (move.id === 'gust' || move.id === 'twister' || move.id === 'thunder' || move.id === 'whirlwind') {
return;
@ -180,7 +260,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
if (source.volatiles['lockon'] && target === source.volatiles['lockon'].source) return;
return false;
},
onSourceModifyDamage: undefined, // no inherit
onSourceBasePower(basePower, target, source, move) {
if (move.id === 'gust' || move.id === 'twister') {
return this.chainModify(2);
@ -191,7 +270,9 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
focusenergy: {
inherit: true,
condition: {
inherit: true,
onStart(pokemon) {
this.add('-start', pokemon, 'move: Focus Energy');
},
onModifyCritRatio(critRatio) {
return critRatio + 1;
},
@ -203,8 +284,17 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
if (target.volatiles['foresight']) return false;
},
condition: {
inherit: true,
noCopy: false,
onStart(pokemon) {
this.add('-start', pokemon, 'Foresight');
},
onNegateImmunity(pokemon, type) {
if (pokemon.hasType('Ghost') && ['Normal', 'Fighting'].includes(type)) return false;
},
onModifyBoost(boosts) {
if (boosts.evasion && boosts.evasion > 0) {
boosts.evasion = 0;
}
},
},
},
frustration: {
@ -248,10 +338,11 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
leechseed: {
inherit: true,
onHit: undefined, // no inherit
onHit() {},
condition: {
inherit: true,
onResidual: undefined, // no inherit
onStart(target) {
this.add('-start', target, 'move: Leech Seed');
},
onAfterMoveSelfPriority: 2,
onAfterMoveSelf(pokemon) {
if (!pokemon.hp) return;
@ -287,15 +378,19 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
if (target.volatiles['foresight'] || target.volatiles['lockon']) return false;
},
condition: {
inherit: true,
onSourceInvulnerability: undefined, // no inherit
duration: 2,
onSourceAccuracy(accuracy, target, source, move) {
if (move && source === this.effectState.target && target === this.effectState.source) return true;
},
},
},
lowkick: {
inherit: true,
accuracy: 90,
basePower: 50,
basePowerCallback: undefined, // no inherit
basePowerCallback() {
return 50;
},
secondary: {
chance: 30,
volatileStatus: 'flinch',
@ -303,16 +398,21 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
meanlook: {
inherit: true,
flags: { reflectable: 1, mirror: 1, metronome: 1 },
flags: {reflectable: 1, mirror: 1},
},
metronome: {
inherit: true,
flags: { failencore: 1, nosketch: 1 },
flags: {failencore: 1},
noMetronome: [
"Counter", "Destiny Bond", "Detect", "Endure", "Metronome", "Mimic", "Mirror Coat", "Protect", "Sketch", "Sleep Talk", "Struggle", "Thief",
],
noSketch: true,
},
mimic: {
inherit: true,
accuracy: 100,
flags: { protect: 1, bypasssub: 1, allyanim: 1, failencore: 1, noassist: 1, nosketch: 1 },
noSketch: true,
flags: {protect: 1, bypasssub: 1, allyanim: 1, failencore: 1, noassist: 1},
},
mindreader: {
inherit: true,
@ -332,14 +432,14 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
}
return false;
},
beforeTurnCallback: undefined, // no inherit
onTry: undefined, // no inherit
beforeTurnCallback() {},
onTry() {},
condition: {},
priority: -1,
},
mirrormove: {
inherit: true,
flags: { metronome: 1, failencore: 1, nosketch: 1 },
flags: {failencore: 1},
onHit(pokemon) {
const noMirror = ['metronome', 'mimic', 'mirrormove', 'sketch', 'sleeptalk', 'transform'];
const target = pokemon.side.foe.active[0];
@ -352,11 +452,18 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
}
this.actions.useMove(lastMove, pokemon);
},
noSketch: true,
},
mist: {
inherit: true,
num: 54,
accuracy: true,
basePower: 0,
category: "Status",
name: "Mist",
pp: 30,
priority: 0,
flags: {},
volatileStatus: 'mist',
sideCondition: undefined, // no inherit
condition: {
onStart(pokemon) {
this.add('-start', pokemon, 'Mist');
@ -377,7 +484,9 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
}
},
},
secondary: null,
target: "self",
type: "Ice",
},
moonlight: {
inherit: true,
@ -406,8 +515,13 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
nightmare: {
inherit: true,
condition: {
inherit: true,
onResidual: undefined, // no inherit
noCopy: true,
onStart(pokemon) {
if (pokemon.status !== 'slp') {
return false;
}
this.add('-start', pokemon, 'Nightmare');
},
onAfterMoveSelfPriority: 1,
onAfterMoveSelf(pokemon) {
if (pokemon.status === 'slp') this.damage(pokemon.baseMaxhp / 4);
@ -419,6 +533,11 @@ 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,
@ -427,8 +546,16 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
perishsong: {
inherit: true,
condition: {
inherit: true,
duration: 4,
onEnd(target) {
this.add('-start', target, 'perish0');
target.faint();
},
onResidualOrder: 4,
onResidual(pokemon) {
const duration = pokemon.volatiles['perishsong'].duration;
this.add('-start', pokemon, 'perish' + duration);
},
},
},
petaldance: {
@ -436,6 +563,11 @@ 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,
@ -457,23 +589,14 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
pursuit: {
inherit: true,
beforeTurnCallback(pokemon, target) {
if (pokemon.isAlly(target)) return;
target.addVolatile('pursuit');
const data = target.volatiles['pursuit'];
if (!data.sources) {
data.sources = [];
}
data.sources.push(pokemon);
},
onModifyMove: undefined, // no inherit
onModifyMove() {},
condition: {
inherit: true,
duration: 1,
onBeforeSwitchOut(pokemon) {
this.debug('Pursuit start');
let alreadyAdded = false;
for (const source of this.effectState.sources) {
if (source.speed < pokemon.speed || (source.speed === pokemon.speed && this.randomChance(1, 2))) {
if (source.speed < pokemon.speed || (source.speed === pokemon.speed && this.random(2) === 0)) {
// Destiny Bond ends if the switch action "outspeeds" the attacker, regardless of host
pokemon.removeVolatile('destinybond');
}
@ -542,6 +665,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
target.statusState.source = target;
this.heal(target.maxhp);
},
secondary: null,
},
return: {
inherit: true,
@ -567,17 +691,50 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
safeguard: {
inherit: true,
condition: {
inherit: true,
duration: 5,
durationCallback(target, source, effect) {
if (source?.hasAbility('persistent')) {
this.add('-activate', source, 'ability: Persistent', effect);
return 7;
}
return 5;
},
onSetStatus(status, target, source, effect) {
if (!effect || !source) return;
if (effect.id === 'yawn') return;
if (effect.effectType === 'Move' && effect.infiltrates && !target.isAlly(source)) return;
if (target !== source) {
this.debug('interrupting setStatus');
if (effect.id === 'synchronize' || (effect.effectType === 'Move' && !effect.secondaries)) {
this.add('-activate', target, 'move: Safeguard');
}
return null;
}
},
onTryAddVolatile(status, target, source, effect) {
if (!effect || !source) return;
if (effect.effectType === 'Move' && effect.infiltrates && !target.isAlly(source)) return;
if ((status.id === 'confusion' || status.id === 'yawn') && target !== source) {
if (effect.effectType === 'Move' && !effect.secondaries) this.add('-activate', target, 'move: Safeguard');
return null;
}
},
onSideStart(side) {
this.add('-sidestart', side, 'Safeguard');
},
onSideResidualOrder: 8,
onSideEnd(side) {
this.add('-sideend', side, 'Safeguard');
},
},
},
selfdestruct: {
inherit: true,
flags: { protect: 1, mirror: 1, metronome: 1, noparentalbond: 1, nosketch: 1 },
noSketch: true,
},
sketch: {
inherit: true,
flags: { bypasssub: 1, failencore: 1, noassist: 1, nosketch: 1 },
flags: {bypasssub: 1, failencore: 1, noassist: 1},
onHit() {
// Sketch always fails in Link Battles
this.add('-nothing');
@ -595,7 +752,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
onPrepareHit(target, source) {
return source.status !== 'slp';
},
secondary: undefined, // no inherit
secondary: null,
},
slash: {
inherit: true,
@ -603,7 +760,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
sleeptalk: {
inherit: true,
flags: { failencore: 1, nosleeptalk: 1, nosketch: 1 },
flags: {failencore: 1, nosleeptalk: 1},
onHit(pokemon) {
const moves = [];
for (const moveSlot of pokemon.moveSlots) {
@ -618,6 +775,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
if (!randomMove) return false;
this.actions.useMove(randomMove, pokemon);
},
noSketch: true,
},
solarbeam: {
inherit: true,
@ -625,23 +783,40 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
return source.status !== 'slp';
},
// Rain weakening done directly in the damage formula
onBasePower: undefined, // no inherit
onBasePower() {},
},
spiderweb: {
inherit: true,
flags: { reflectable: 1, mirror: 1, metronome: 1 },
flags: {reflectable: 1, mirror: 1},
},
spikes: {
inherit: true,
condition: {
inherit: true,
onSideRestart: undefined, // no inherit
// this is a side condition
onSideStart(side) {
if (!this.effectState.layers || this.effectState.layers === 0) {
this.add('-sidestart', side, 'Spikes');
this.effectState.layers = 1;
} else {
return false;
}
},
onSwitchIn(pokemon) {
if (!pokemon.runImmunity('Ground')) return;
const damageAmounts = [0, 3];
this.damage(damageAmounts[this.effectState.layers] * pokemon.maxhp / 24);
},
},
},
substitute: {
inherit: true,
condition: {
inherit: true,
onStart(target) {
this.add('-start', target, 'Substitute');
this.effectState.hp = Math.floor(target.maxhp / 4);
delete target.volatiles['partiallytrapped'];
},
onTryPrimaryHitPriority: -1,
onTryPrimaryHit(target, source, move) {
if (move.stallingMove) {
this.add('-fail', source);
@ -678,6 +853,10 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
if (!damage) {
return null;
}
damage = this.runEvent('SubDamage', target, source, move, damage);
if (!damage) {
return damage;
}
if (damage > target.volatiles['substitute'].hp) {
damage = target.volatiles['substitute'].hp as number;
}
@ -694,6 +873,9 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
this.runEvent('AfterSubDamage', target, source, move, damage);
return this.HIT_SUBSTITUTE;
},
onEnd(target) {
this.add('-end', target, 'Substitute');
},
},
},
swagger: {
@ -719,7 +901,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
thief: {
inherit: true,
onAfterHit: undefined, // no inherit
onAfterHit() {},
secondary: {
chance: 100,
onHit(target, source) {
@ -734,7 +916,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
target.item = yourItem.id; // bypass setItem so we don't break choicelock or anything
return;
}
this.add('-item', source, yourItem, '[from] move: Thief', `[of] ${target}`);
this.add('-item', source, yourItem, '[from] move: Thief', '[of] ' + target);
},
},
},
@ -743,6 +925,11 @@ 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,
@ -750,7 +937,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
transform: {
inherit: true,
flags: { bypasssub: 1, metronome: 1, failencore: 1, nosketch: 1 },
noSketch: true,
},
triattack: {
inherit: true,

View File

@ -1,4 +1,4 @@
export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable = {
export const Pokedex: {[k: string]: ModdedSpeciesData} = {
unown: {
inherit: true,
cosmeticFormes: ["Unown-B", "Unown-C", "Unown-D", "Unown-E", "Unown-F", "Unown-G", "Unown-H", "Unown-I", "Unown-J", "Unown-K", "Unown-L", "Unown-M", "Unown-N", "Unown-O", "Unown-P", "Unown-Q", "Unown-R", "Unown-S", "Unown-T", "Unown-U", "Unown-V", "Unown-W", "Unown-X", "Unown-Y", "Unown-Z"],

View File

@ -0,0 +1,425 @@
{
"venusaur": {
"moves": ["growth", "hiddenpowerfire", "hiddenpowerice", "leechseed", "razorleaf", "sleeppowder", "synthesis"]
},
"charizard": {
"moves": ["bellydrum", "earthquake", "fireblast", "hyperbeam", "rockslide"]
},
"blastoise": {
"moves": ["icebeam", "rapidspin", "rest", "sleeptalk", "surf", "toxic", "zapcannon"]
},
"butterfree": {
"moves": ["nightmare", "psychic", "sleeppowder", "stunspore", "substitute"]
},
"beedrill": {
"moves": ["agility", "hiddenpowerground", "sludgebomb", "substitute", "swordsdance"]
},
"pidgeot": {
"moves": ["curse", "hiddenpowerground", "reflect", "rest", "return", "sleeptalk", "toxic", "whirlwind"]
},
"raticate": {
"moves": ["irontail", "rest", "return", "sleeptalk", "superfang"]
},
"fearow": {
"moves": ["doubleedge", "drillpeck", "hiddenpowerground", "rest", "sleeptalk", "substitute"]
},
"arbok": {
"moves": ["curse", "earthquake", "glare", "haze", "rest", "sludgebomb"]
},
"pikachu": {
"moves": ["encore", "hiddenpowerice", "substitute", "surf", "thunderbolt"]
},
"raichu": {
"moves": ["encore", "hiddenpowerice", "rest", "sleeptalk", "surf", "thunder", "thunderbolt"]
},
"sandslash": {
"moves": ["earthquake", "hiddenpowerbug", "rockslide", "substitute", "swordsdance"]
},
"nidoqueen": {
"moves": ["earthquake", "fireblast", "icebeam", "lovelykiss", "moonlight", "thunder"]
},
"nidoking": {
"moves": ["earthquake", "fireblast", "icebeam", "lovelykiss", "morningsun", "thunder"]
},
"clefable": {
"moves": ["bellydrum", "encore", "fireblast", "moonlight", "return", "shadowball"]
},
"ninetales": {
"moves": ["confuseray", "fireblast", "hiddenpowergrass", "hypnosis", "sunnyday"]
},
"wigglytuff": {
"moves": ["bodyslam", "charm", "curse", "doubleedge", "fireblast", "rest", "sleeptalk", "thunderwave"]
},
"vileplume": {
"moves": ["hiddenpowergrass", "moonlight", "sleeppowder", "sludgebomb", "stunspore", "swordsdance"]
},
"parasect": {
"moves": ["bodyslam", "gigadrain", "hiddenpowerbug", "spore", "swordsdance", "synthesis"]
},
"venomoth": {
"moves": ["disable", "gigadrain", "psychic", "sleeppowder", "sludgebomb", "stunspore"]
},
"dugtrio": {
"moves": ["earthquake", "rockslide", "sludgebomb", "substitute", "swagger"]
},
"persian": {
"moves": ["bodyslam", "hypnosis", "irontail", "rest", "return", "thunder"]
},
"golduck": {
"moves": ["crosschop", "hiddenpowerelectric", "hydropump", "hypnosis", "icebeam", "psychic", "surf"]
},
"primeape": {
"moves": ["crosschop", "doubleedge", "hiddenpowerghost", "meditate", "rest", "rockslide", "substitute"]
},
"arcanine": {
"moves": ["bodyslam", "crunch", "extremespeed", "fireblast", "hiddenpowergrass", "rest", "sleeptalk"]
},
"poliwrath": {
"moves": ["bellydrum", "bodyslam", "earthquake", "lovelykiss"]
},
"alakazam": {
"moves": ["encore", "firepunch", "icepunch", "psychic", "recover", "thunderpunch", "thunderwave"]
},
"machamp": {
"moves": ["bodyslam", "crosschop", "curse", "earthquake", "hiddenpowerghost", "rest", "rockslide", "sleeptalk"]
},
"victreebel": {
"moves": ["hiddenpowerground", "razorleaf", "sleeppowder", "sludgebomb", "swordsdance", "synthesis"]
},
"tentacruel": {
"moves": ["hydropump", "sludgebomb", "substitute", "swordsdance"]
},
"golem": {
"moves": ["curse", "earthquake", "explosion", "fireblast", "rapidspin", "rockslide"]
},
"rapidash": {
"moves": ["bodyslam", "fireblast", "hiddenpowergrass", "hypnosis", "sunnyday"]
},
"slowbro": {
"moves": ["fireblast", "psychic", "rest", "sleeptalk", "surf", "thunderwave", "toxic"]
},
"magneton": {
"moves": ["hiddenpowerice", "substitute", "thunderbolt", "thunderwave"]
},
"farfetchd": {
"moves": ["agility", "batonpass", "return", "swordsdance"]
},
"dodrio": {
"moves": ["doubleedge", "drillpeck", "hiddenpowerground", "rest", "substitute"]
},
"dewgong": {
"moves": ["bodyslam", "encore", "icebeam", "rest", "sleeptalk", "surf", "toxic"]
},
"muk": {
"moves": ["curse", "explosion", "fireblast", "hiddenpowerground", "sludgebomb"]
},
"cloyster": {
"moves": ["explosion", "icebeam", "spikes", "surf", "toxic"]
},
"gengar": {
"moves": ["destinybond", "explosion", "firepunch", "hypnosis", "icepunch", "thunderbolt"]
},
"hypno": {
"moves": ["hypnosis", "lightscreen", "psychic", "reflect", "seismictoss", "thunderwave"]
},
"kingler": {
"moves": ["hiddenpowerground", "rest", "return", "surf", "swordsdance"]
},
"electrode": {
"moves": ["explosion", "hiddenpowerice", "lightscreen", "reflect", "thunderbolt", "thunderwave"]
},
"exeggutor": {
"moves": ["explosion", "gigadrain", "hiddenpowerfire", "psychic", "sleeppowder", "stunspore", "synthesis"]
},
"marowak": {
"moves": ["earthquake", "hiddenpowerbug", "rockslide", "swordsdance"]
},
"hitmonlee": {
"moves": ["bodyslam", "hiddenpowerghost", "highjumpkick", "meditate", "rest"]
},
"hitmonchan": {
"moves": ["bodyslam", "counter", "curse", "hiddenpowerghost", "highjumpkick"]
},
"lickitung": {
"moves": ["bodyslam", "earthquake", "fireblast", "rest", "sleeptalk", "swordsdance"]
},
"weezing": {
"moves": ["explosion", "fireblast", "hiddenpowergrass", "painsplit", "sludgebomb", "thunder"]
},
"rhydon": {
"moves": ["curse", "earthquake", "fireblast", "rest", "roar", "rockslide", "sleeptalk", "zapcannon"]
},
"tangela": {
"moves": ["gigadrain", "growth", "hiddenpowerice", "sleeppowder", "synthesis"]
},
"kangaskhan": {
"moves": ["bodyslam", "curse", "earthquake", "rest", "return", "roar", "sleeptalk"]
},
"seaking": {
"moves": ["agility", "return", "substitute", "surf", "swordsdance"]
},
"starmie": {
"moves": ["psychic", "rapidspin", "recover", "surf", "thunderbolt", "thunderwave"]
},
"mrmime": {
"moves": ["encore", "firepunch", "hypnosis", "icepunch", "psychic", "thief", "thunderbolt", "thunderwave"]
},
"scyther": {
"moves": ["batonpass", "hiddenpowerbug", "hiddenpowerground", "swordsdance", "wingattack"]
},
"jynx": {
"moves": ["icebeam", "lovelykiss", "nightmare", "psychic", "substitute", "thief"]
},
"electabuzz": {
"moves": ["crosschop", "icepunch", "psychic", "pursuit", "thunder", "thunderbolt"]
},
"magmar": {
"moves": ["crosschop", "fireblast", "hiddenpowerground", "sunnyday", "thief", "thunderpunch"]
},
"pinsir": {
"moves": ["bodyslam", "doubleedge", "hiddenpowerbug", "rest", "submission", "swordsdance"]
},
"tauros": {
"moves": ["curse", "doubleedge", "earthquake", "rest", "return", "sleeptalk"]
},
"gyarados": {
"moves": ["bodyslam", "doubleedge", "hiddenpowerflying", "hydropump", "rest", "sleeptalk", "thunder"]
},
"lapras": {
"moves": ["icebeam", "rest", "sing", "sleeptalk", "surf", "thunder", "thunderbolt", "toxic"]
},
"ditto": {
"level": 83,
"moves": ["transform"]
},
"vaporeon": {
"moves": ["growth", "hiddenpowerelectric", "icebeam", "rest", "sleeptalk", "surf"]
},
"jolteon": {
"moves": ["batonpass", "growth", "hiddenpowerice", "hiddenpowerwater", "substitute", "thunderbolt", "thunderwave"]
},
"flareon": {
"moves": ["batonpass", "doubleedge", "fireblast", "growth", "hiddenpowergrass", "zapcannon"]
},
"omastar": {
"moves": ["hiddenpowerelectric", "icebeam", "rest", "sleeptalk", "surf"]
},
"kabutops": {
"moves": ["ancientpower", "hiddenpowerground", "return", "submission", "surf", "swordsdance"]
},
"aerodactyl": {
"moves": ["ancientpower", "curse", "earthquake", "hiddenpowerflying", "reflect", "whirlwind"]
},
"snorlax": {
"moves": ["curse", "doubleedge", "earthquake", "fireblast", "lovelykiss", "rest", "return", "sleeptalk"]
},
"articuno": {
"moves": ["hiddenpowerelectric", "icebeam", "rest", "sleeptalk", "toxic"]
},
"zapdos": {
"moves": ["hiddenpowerice", "rest", "sleeptalk", "thunder"]
},
"moltres": {
"moves": ["fireblast", "hiddenpowergrass", "rest", "sleeptalk", "sunnyday"]
},
"dragonite": {
"moves": ["haze", "hiddenpowerflying", "icebeam", "lightscreen", "reflect", "rest", "thunder"]
},
"mewtwo": {
"moves": ["fireblast", "icebeam", "psychic", "recover", "thunderbolt", "thunderwave"]
},
"mew": {
"moves": ["earthquake", "explosion", "psychic", "shadowball", "softboiled", "submission", "swordsdance"]
},
"meganium": {
"moves": ["hiddenpowerfire", "leechseed", "lightscreen", "razorleaf", "reflect", "synthesis"]
},
"typhlosion": {
"moves": ["earthquake", "fireblast", "rest", "sleeptalk", "thunderpunch"]
},
"feraligatr": {
"moves": ["crunch", "earthquake", "icebeam", "rest", "rockslide", "sleeptalk", "surf"]
},
"furret": {
"moves": ["curse", "doubleedge", "rest", "shadowball", "sleeptalk", "surf"]
},
"noctowl": {
"moves": ["hypnosis", "nightshade", "reflect", "thief", "toxic", "whirlwind"]
},
"ledian": {
"moves": ["agility", "barrier", "batonpass", "lightscreen"]
},
"ariados": {
"moves": ["batonpass", "curse", "sludgebomb", "spiderweb"]
},
"crobat": {
"moves": ["gigadrain", "haze", "hiddenpowerground", "rest", "whirlwind", "wingattack"]
},
"lanturn": {
"moves": ["icebeam", "raindance", "rest", "sleeptalk", "surf", "thunder"]
},
"togetic": {
"moves": ["fireblast", "solarbeam", "sunnyday", "zapcannon"]
},
"xatu": {
"moves": ["drillpeck", "haze", "hiddenpowergrass", "hiddenpowerwater", "psychic", "rest", "sleeptalk", "thief"]
},
"ampharos": {
"moves": ["firepunch", "hiddenpowerice", "lightscreen", "reflect", "rest", "sleeptalk", "thunderbolt", "thunderwave"]
},
"bellossom": {
"moves": ["doubleedge", "hiddenpowerfire", "leechseed", "moonlight", "razorleaf", "sleeppowder", "stunspore"]
},
"azumarill": {
"moves": ["perishsong", "rest", "surf", "whirlpool"]
},
"sudowoodo": {
"moves": ["curse", "earthquake", "rest", "rockslide", "selfdestruct", "sleeptalk"]
},
"politoed": {
"moves": ["growth", "hiddenpowerelectric", "icebeam", "lovelykiss", "rest", "sleeptalk", "surf"]
},
"jumpluff": {
"moves": ["gigadrain", "hiddenpowerice", "hiddenpowerwater", "leechseed", "sleeppowder", "stunspore"]
},
"aipom": {
"moves": ["agility", "batonpass", "curse", "return", "shadowball"]
},
"sunflora": {
"moves": ["growth", "hiddenpowerfire", "hiddenpowerwater", "razorleaf", "synthesis"]
},
"yanma": {
"moves": ["gigadrain", "hiddenpowerbug", "hiddenpowerflying", "return", "screech", "thief"]
},
"quagsire": {
"moves": ["bellydrum", "bodyslam", "earthquake", "rest", "surf"]
},
"espeon": {
"moves": ["batonpass", "bite", "growth", "hiddenpowerfire", "morningsun", "psychic", "substitute"]
},
"umbreon": {
"moves": ["batonpass", "growth", "hiddenpowerdark", "meanlook", "moonlight"]
},
"murkrow": {
"moves": ["drillpeck", "haze", "hiddenpowerdark", "hiddenpowerfire", "pursuit", "thief", "toxic"]
},
"slowking": {
"moves": ["fireblast", "psychic", "rest", "sleeptalk", "surf", "thunderwave", "toxic"]
},
"misdreavus": {
"moves": ["meanlook", "painsplit", "perishsong", "psychic", "shadowball", "thief", "thunderbolt"]
},
"unown": {
"level": 87,
"moves": ["hiddenpowerpsychic"]
},
"wobbuffet": {
"level": 83,
"moves": ["counter", "mimic", "mirrorcoat", "safeguard"]
},
"girafarig": {
"moves": ["crunch", "curse", "earthquake", "psychic", "rest", "return", "thunderbolt"]
},
"forretress": {
"moves": ["doubleedge", "explosion", "hiddenpowerbug", "rapidspin", "reflect", "spikes", "toxic"]
},
"dunsparce": {
"moves": ["curse", "flamethrower", "rest", "return", "sleeptalk", "thunder", "thunderbolt"]
},
"gligar": {
"moves": ["counter", "earthquake", "hiddenpowerflying", "screech", "thief"]
},
"steelix": {
"moves": ["curse", "earthquake", "explosion", "irontail", "rest", "roar", "sleeptalk", "toxic"]
},
"granbull": {
"moves": ["curse", "healbell", "hiddenpowerground", "lovelykiss", "rest", "return", "sleeptalk"]
},
"qwilfish": {
"moves": ["curse", "haze", "hydropump", "sludgebomb", "spikes"]
},
"scizor": {
"moves": ["agility", "batonpass", "hiddenpowerbug", "return", "swordsdance"]
},
"shuckle": {
"moves": ["defensecurl", "rest", "rollout", "toxic"]
},
"heracross": {
"moves": ["curse", "earthquake", "megahorn", "rest", "seismictoss", "sleeptalk"]
},
"sneasel": {
"moves": ["icebeam", "moonlight", "return", "screech", "shadowball", "thief"]
},
"ursaring": {
"moves": ["curse", "earthquake", "rest", "return", "roar", "sleeptalk"]
},
"magcargo": {
"moves": ["curse", "earthquake", "fireblast", "rest", "rockslide", "sleeptalk"]
},
"piloswine": {
"moves": ["ancientpower", "bodyslam", "curse", "earthquake", "icebeam", "rest", "sleeptalk"]
},
"corsola": {
"moves": ["curse", "recover", "rockslide", "surf", "toxic"]
},
"octillery": {
"moves": ["flamethrower", "hiddenpowerelectric", "icebeam", "rest", "sleeptalk", "surf"]
},
"delibird": {
"moves": ["hiddenpowerflying", "icebeam", "rapidspin", "spikes", "thief"]
},
"mantine": {
"moves": ["haze", "hiddenpowerelectric", "icebeam", "rest", "sleeptalk", "surf", "toxic"]
},
"skarmory": {
"moves": ["curse", "drillpeck", "rest", "sleeptalk", "toxic"]
},
"houndoom": {
"moves": ["crunch", "fireblast", "pursuit", "solarbeam", "sunnyday"]
},
"kingdra": {
"moves": ["dragonbreath", "hiddenpowerelectric", "icebeam", "rest", "sleeptalk", "surf"]
},
"donphan": {
"moves": ["ancientpower", "curse", "earthquake", "hiddenpowerbug", "rapidspin", "rest", "roar", "sleeptalk"]
},
"porygon2": {
"moves": ["curse", "doubleedge", "icebeam", "recover", "return", "thunderbolt", "thunderwave"]
},
"stantler": {
"moves": ["confuseray", "curse", "earthquake", "rest", "return", "sleeptalk"]
},
"smeargle": {
"moves": ["agility", "batonpass", "spikes", "spore"]
},
"hitmontop": {
"moves": ["curse", "hiddenpowerghost", "highjumpkick", "rest", "sleeptalk"]
},
"miltank": {
"moves": ["bodyslam", "curse", "earthquake", "healbell", "milkdrink"]
},
"blissey": {
"moves": ["flamethrower", "healbell", "icebeam", "present", "sing", "softboiled", "toxic"]
},
"raikou": {
"moves": ["crunch", "hiddenpowerice", "hiddenpowerwater", "reflect", "rest", "roar", "sleeptalk", "thunder", "thunderbolt"]
},
"entei": {
"moves": ["fireblast", "hiddenpowerrock", "return", "solarbeam", "sunnyday"]
},
"suicune": {
"moves": ["hiddenpowerelectric", "icebeam", "rest", "roar", "sleeptalk", "surf", "toxic"]
},
"tyranitar": {
"moves": ["crunch", "curse", "earthquake", "fireblast", "pursuit", "rest", "roar", "rockslide", "surf"]
},
"lugia": {
"moves": ["aeroblast", "curse", "earthquake", "icebeam", "recover", "whirlwind"]
},
"hooh": {
"moves": ["curse", "earthquake", "hiddenpowerflying", "recover", "sacredfire", "thunder", "thunderbolt"]
},
"celebi": {
"moves": ["hiddenpowergrass", "healbell", "leechseed", "psychic", "recover", "toxic"]
}
}

View File

@ -0,0 +1,313 @@
import RandomGen3Teams from '../gen3/random-teams';
import {PRNG, PRNGSeed} from '../../../sim/prng';
import type {MoveCounter, OldRandomBattleSpecies} from '../gen8/random-teams';
export class RandomGen2Teams extends RandomGen3Teams {
randomData: {[species: string]: OldRandomBattleSpecies} = require('./random-data.json');
constructor(format: string | Format, prng: PRNG | PRNGSeed | null) {
super(format, prng);
this.moveEnforcementCheckers = {
Electric: (movePool, moves, abilities, types, counter) => !counter.get('Electric'),
Fire: (movePool, moves, abilities, types, counter) => !counter.get('Fire'),
Flying: (movePool, moves, abilities, types, counter) => !counter.get('Flying') && types.has('Ground'),
Ground: (movePool, moves, abilities, types, counter) => !counter.get('Ground'),
Ice: (movePool, moves, abilities, types, counter) => !counter.get('Ice'),
Normal: (movePool, moves, abilities, types, counter) => !counter.get('Normal') && counter.setupType === 'Physical',
Psychic: (movePool, moves, abilities, types, counter) => !counter.get('Psychic') && types.has('Grass'),
Rock: (movePool, moves, abilities, types, counter, species) => !counter.get('Rock') && species.baseStats.atk > 60,
Water: (movePool, moves, abilities, types, counter) => !counter.get('Water'),
};
}
shouldCullMove(
move: Move,
types: Set<string>,
moves: Set<string>,
abilities = {},
counter: MoveCounter,
movePool: string[],
teamDetails: RandomTeamsTypes.TeamDetails,
): {cull: boolean, isSetup?: boolean} {
const restTalk = moves.has('rest') && moves.has('sleeptalk');
switch (move.id) {
// Set up once and only if we have the moves for it
case 'bellydrum': case 'curse': case 'meditate': case 'screech': case 'swordsdance':
return {
cull: (
(counter.setupType !== 'Physical' || counter.get('physicalsetup') > 1) ||
(!counter.get('Physical') || counter.damagingMoves.size < 2 && !moves.has('batonpass') && !moves.has('sleeptalk'))
),
isSetup: true,
};
// Not very useful without their supporting moves
case 'batonpass':
return {cull: !counter.setupType && !counter.get('speedsetup') && !moves.has('meanlook')};
case 'meanlook':
return {cull: movePool.includes('perishsong')};
case 'nightmare':
return {cull: !moves.has('lovelykiss') && !moves.has('sleeppowder')};
case 'swagger':
return {cull: !moves.has('substitute')};
// Bad after setup
case 'charm': case 'counter':
return {cull: !!counter.setupType};
case 'haze':
return {cull: !!counter.setupType || restTalk};
case 'reflect': case 'lightscreen':
return {cull: !!counter.setupType || moves.has('rest')};
// Ineffective to have both
case 'doubleedge':
return {cull: moves.has('bodyslam') || moves.has('return')};
case 'explosion': case 'selfdestruct':
return {cull: moves.has('softboiled') || restTalk};
case 'extremespeed':
return {cull: moves.has('bodyslam') || restTalk};
case 'hyperbeam':
return {cull: moves.has('rockslide')};
case 'rapidspin':
return {cull: !!teamDetails.rapidSpin || !!counter.setupType || moves.has('sleeptalk')};
case 'return':
return {cull: moves.has('bodyslam')};
case 'surf':
return {cull: moves.has('hydropump')};
case 'thunder':
return {cull: moves.has('thunderbolt')};
case 'razorleaf':
return {cull: moves.has('swordsdance') && movePool.includes('sludgebomb')};
case 'icebeam':
return {cull: moves.has('dragonbreath')};
case 'seismictoss':
return {cull: moves.has('rest') || moves.has('sleeptalk')};
case 'destinybond':
return {cull: moves.has('explosion')};
case 'pursuit':
return {cull: moves.has('crunch') && moves.has('solarbeam')};
case 'thief':
return {cull: moves.has('rest') || moves.has('substitute')};
case 'irontail':
return {cull: types.has('Ground') && movePool.includes('earthquake')};
// Status and illegal move rejections
case 'confuseray': case 'encore': case 'roar': case 'whirlwind':
return {cull: restTalk};
case 'lovelykiss':
return {cull: ['healbell', 'moonlight', 'morningsun', 'sleeptalk'].some(m => moves.has(m))};
case 'sleeptalk':
return {cull: moves.has('curse') && counter.get('stab') >= 2};
case 'softboiled':
return {cull: movePool.includes('swordsdance')};
case 'spikes':
return {cull: !!teamDetails.spikes};
case 'substitute':
return {cull: moves.has('agility') || moves.has('rest')};
case 'synthesis':
return {cull: moves.has('explosion')};
case 'thunderwave':
return {cull: moves.has('thunder') || moves.has('toxic')};
}
return {cull: false};
}
getItem(
ability: string,
types: Set<string>,
moves: Set<string>,
counter: MoveCounter,
species: Species,
) {
// First, the high-priority items
if (species.name === 'Ditto') return 'Metal Powder';
if (species.name === 'Farfetch\u2019d') return 'Stick';
if (species.name === 'Marowak') return 'Thick Club';
if (species.name === 'Pikachu') return 'Light Ball';
if (species.name === 'Unown') return 'Twisted Spoon';
if (moves.has('thief')) return '';
// Medium priority
if (moves.has('rest') && !moves.has('sleeptalk')) return 'Mint Berry';
if (
(moves.has('bellydrum') || moves.has('swordsdance')) &&
species.baseStats.spe >= 60 && !types.has('Ground') &&
!moves.has('sleeptalk') && !moves.has('substitute') &&
this.randomChance(1, 2)
) {
return 'Miracle Berry';
}
// Default to Leftovers
return 'Leftovers';
}
randomSet(species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}): RandomTeamsTypes.RandomSet {
species = this.dex.species.get(species);
const data = this.randomData[species.id];
const movePool = (data.moves || Object.keys(this.dex.species.getLearnset(species.id)!)).slice();
const rejectedPool: string[] = [];
const moves = new Set<string>();
let ivs = {hp: 30, atk: 30, def: 30, spa: 30, spd: 30, spe: 30};
let availableHP = 0;
for (const setMoveid of movePool) {
if (setMoveid.startsWith('hiddenpower')) availableHP++;
}
const types = new Set(species.types);
let counter;
// We use a special variable to track Hidden Power
// so that we can check for all Hidden Powers at once
let hasHiddenPower = false;
do {
// Choose next 4 moves from learnset/viable moves and add them to moves list:
while (moves.size < this.maxMoveCount && movePool.length) {
const moveid = this.sampleNoReplace(movePool);
if (moveid.startsWith('hiddenpower')) {
availableHP--;
if (hasHiddenPower) continue;
hasHiddenPower = true;
}
moves.add(moveid);
}
while (moves.size < this.maxMoveCount && rejectedPool.length) {
const moveid = this.sampleNoReplace(rejectedPool);
if (moveid.startsWith('hiddenpower')) {
if (hasHiddenPower) continue;
hasHiddenPower = true;
}
moves.add(moveid);
}
counter = this.queryMoves(moves, species.types, new Set(), movePool);
// Iterate through the moves again, this time to cull them:
for (const moveid of moves) {
const move = this.dex.moves.get(moveid);
let {cull, isSetup} = this.shouldCullMove(move, types, moves, {}, counter, movePool, teamDetails);
// This move doesn't satisfy our setup requirements:
if (counter.setupType === 'Physical' && move.category === 'Special' && !counter.get('Physical')) {
cull = true;
}
// Reject Status, non-STAB, or low basepower moves
const moveIsRejectable = (
(move.category !== 'Status' || !move.flags.heal) &&
// These moves cannot be rejected in favor of a forced move
!['batonpass', 'sleeptalk', 'spikes', 'sunnyday'].includes(move.id) &&
(move.category === 'Status' || !types.has(move.type) || (move.basePower && move.basePower < 40))
);
if (!cull && !isSetup && moveIsRejectable && (counter.setupType || !move.stallingMove)) {
// There may be more important moves that this Pokemon needs
if (
// Pokemon should usually have at least one STAB move
(
!counter.get('stab') &&
!counter.get('damage') &&
!types.has('Ghost') &&
counter.get('physicalpool') + counter.get('specialpool') > 0
) || (movePool.includes('megahorn') || (movePool.includes('softboiled') && moves.has('present'))) ||
// Rest + Sleep Talk should be selected together
((moves.has('rest') && movePool.includes('sleeptalk')) || (moves.has('sleeptalk') && movePool.includes('rest'))) ||
// Sunny Day + Solar Beam should be selected together
(moves.has('sunnyday') && movePool.includes('solarbeam') ||
(moves.has('solarbeam') && movePool.includes('sunnyday'))) ||
['milkdrink', 'recover', 'spore'].some(m => movePool.includes(m))
) {
cull = true;
} else {
// Pokemon should have moves that benefit their typing
for (const type of types) {
if (this.moveEnforcementCheckers[type]?.(movePool, moves, new Set(), types, counter, species, teamDetails)) cull = true;
}
}
}
// Remove rejected moves from the move list
if (
cull &&
(movePool.length - availableHP || availableHP && (move.id === 'hiddenpower' || !hasHiddenPower))
) {
if (move.category !== 'Status' && !move.damage && (move.id !== 'hiddenpower' || !availableHP)) {
rejectedPool.push(moveid);
}
moves.delete(moveid);
if (moveid.startsWith('hiddenpower')) hasHiddenPower = false;
break;
}
if (cull && rejectedPool.length) {
moves.delete(moveid);
if (moveid.startsWith('hiddenpower')) hasHiddenPower = false;
break;
}
}
} while (moves.size < this.maxMoveCount && (movePool.length || rejectedPool.length));
// Adjust IVs for Hidden Power
for (const setMoveid of moves) {
if (!setMoveid.startsWith('hiddenpower')) continue;
const hpType = setMoveid.substr(11, setMoveid.length);
const hpIVs: {[k: string]: Partial<typeof ivs>} = {
dragon: {def: 28},
ice: {def: 26},
psychic: {def: 24},
electric: {atk: 28},
grass: {atk: 28, def: 28},
water: {atk: 28, def: 26},
fire: {atk: 28, def: 24},
steel: {atk: 26},
ghost: {atk: 26, def: 28},
bug: {atk: 26, def: 26},
rock: {atk: 26, def: 24},
ground: {atk: 24},
poison: {atk: 24, def: 28},
flying: {atk: 24, def: 26},
fighting: {atk: 24, def: 24},
};
if (hpIVs[hpType]) {
ivs = {...ivs, ...hpIVs[hpType]};
}
if (ivs.atk === 28 || ivs.atk === 24) ivs.hp = 14;
if (ivs.def === 28 || ivs.def === 24) ivs.hp -= 8;
}
const levelScale: {[k: string]: number} = {
NU: 73,
NUBL: 71,
UU: 69,
UUBL: 67,
OU: 65,
Uber: 61,
};
const level = this.adjustLevel || data.level || levelScale[species.tier] || 80;
return {
name: species.name,
species: species.name,
moves: Array.from(moves),
ability: 'No Ability',
evs: {hp: 255, atk: 255, def: 255, spa: 255, spd: 255, spe: 255},
ivs,
item: this.getItem('None', types, moves, counter, species),
level,
// No shiny chance because Gen 2 shinies have bad IVs
shiny: false,
gender: species.gender ? species.gender : 'M',
};
}
}
export default RandomGen2Teams;

View File

@ -1,6 +1,4 @@
import type { Learnset } from "../../../sim/dex-species";
export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable = {
export const Rulesets: {[k: string]: ModdedFormatData} = {
obtainablemoves: {
inherit: true,
banlist: [
@ -28,10 +26,7 @@ export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable
standard: {
effectType: 'ValidatorRule',
name: 'Standard',
ruleset: [
'Standard AG',
'Sleep Clause Mod', 'Freeze Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Items Clause', 'Evasion Moves Clause',
],
ruleset: ['Obtainable', 'Sleep Clause Mod', 'Freeze Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Items Clause', 'Evasion Moves Clause', 'Endless battle Clause', 'HP Percentage Mod', 'Cancel Mod'],
banlist: [
'Hypnosis + Mean Look',
'Hypnosis + Spider Web',
@ -45,65 +40,68 @@ export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable
'Spore + Spider Web',
],
},
nc2000movelegality: {
nintendocup2000movelegality: {
effectType: 'ValidatorRule',
name: 'NC 2000 Move Legality',
name: 'Nintendo Cup 2000 Move Legality',
desc: "Prevents Pok\u00e9mon from having moves that would only be obtainable in Pok\u00e9mon Crystal.",
onValidateSet(set) {
const illegalCombos: { [speciesid: string]: { [moveid: string]: 'E' | 'L' | 'S' } } = {
arbok: { crunch: 'E' },
sandslash: { metalclaw: 'E' },
golduck: { crosschop: 'E' },
marowak: { swordsdance: 'E' },
electabuzz: { crosschop: 'E' },
magmar: { crosschop: 'E' },
jolteon: { batonpass: 'L' },
vaporeon: { batonpass: 'L' },
flareon: { batonpass: 'L' },
espeon: { batonpass: 'L' },
umbreon: { batonpass: 'L' },
dragonite: { extremespeed: 'S' },
meganium: { swordsdance: 'E' },
typhlosion: { submission: 'E' },
ariados: { agility: 'L' },
yanma: { wingattack: 'L' },
murkrow: { skyattack: 'E' },
qwilfish: { spikes: 'L' },
sneasel: { metalclaw: 'L' },
ursaring: { metalclaw: 'E' },
piloswine: { amnesia: 'L' },
skarmory: { skyattack: 'E' },
donphan: { watergun: 'E' },
suicune: { aurorabeam: 'L' },
dugtrio: { triattack: 'L' },
magneton: { triattack: 'L' },
cloyster: { spikes: 'L' },
const illegalCombos: {[speciesid: string]: {[moveid: string]: 'E' | 'L' | 'S'}} = {
arbok: {crunch: 'E'},
sandslash: {metalclaw: 'E'},
golduck: {crosschop: 'E'},
marowak: {swordsdance: 'E'},
electabuzz: {crosschop: 'E'},
magmar: {crosschop: 'E'},
jolteon: {batonpass: 'L'},
vaporeon: {batonpass: 'L'},
flareon: {batonpass: 'L'},
espeon: {batonpass: 'L'},
umbreon: {batonpass: 'L'},
dragonite: {extremespeed: 'S'},
meganium: {swordsdance: 'E'},
typhlosion: {submission: 'E'},
ariados: {agility: 'L'},
yanma: {wingattack: 'L'},
murkrow: {skyattack: 'E'},
qwilfish: {spikes: 'L'},
sneasel: {metalclaw: 'L'},
ursaring: {metalclaw: 'E'},
piloswine: {amnesia: 'L'},
skarmory: {skyattack: 'E'},
donphan: {watergun: 'E'},
suicune: {aurorabeam: 'L'},
dugtrio: {triattack: 'L'},
magneton: {triattack: 'L'},
cloyster: {spikes: 'L'},
};
const moveSources: NonNullable<Learnset['learnset']> = Object.fromEntries(
set.moves.map(move => [this.toID(move), []])
);
const species = this.dex.species.get(set.species);
for (const { learnset } of this.dex.species.getFullLearnset(species.id)) {
for (const moveid in moveSources) {
moveSources[moveid].push(...(learnset[moveid] || []));
}
}
const notUsableAsTM = ['icebeam', 'flamethrower', 'thunderbolt'];
const species = this.dex.species.get(set.species || set.name);
const learnsetData = {...(this.dex.data.Learnsets[species.id]?.learnset || {})};
const legalityList = illegalCombos[species.id];
const problems = [];
let prevo = species.prevo;
while (prevo) {
const prevoSpecies = this.dex.species.get(prevo);
const prevoLsetData = this.dex.data.Learnsets[prevoSpecies.id]?.learnset || {};
for (const moveid in prevoLsetData) {
if (!(moveid in learnsetData)) {
learnsetData[moveid] = prevoLsetData[moveid];
} else {
learnsetData[moveid].push(...prevoLsetData[moveid]);
}
}
prevo = prevoSpecies.prevo;
}
for (const moveid of set.moves.map(this.toID)) {
// Diglett Magnemite Shellder
if (!moveSources[moveid]) continue;
if (!learnsetData[moveid]) continue;
if (legalityList) {
const list = moveSources[moveid].filter(x => !x.includes(legalityList[moveid]));
const list = learnsetData[moveid].filter(x => !x.includes(legalityList[moveid]));
if (!list.length) {
switch (legalityList[moveid]) {
case 'L':
// Converted to a set to remove duplicate entries
const levels = new Set(moveSources[moveid].filter(x => x.includes(legalityList[moveid])).map(x => x.slice(2)));
const levels = new Set(learnsetData[moveid].filter(x => x.includes(legalityList[moveid])).map(x => x.slice(2)));
problems.push(
`${species.name} can't learn ${this.dex.moves.get(moveid).name}.`,
`(It learns ${this.dex.moves.get(moveid).name} in Pok\u00e9mon Crystal at the following levels: ${[...levels].join(', ')})`
@ -125,7 +123,7 @@ export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable
}
}
for (const id of notUsableAsTM) {
if (moveid === id && moveSources[id] && !moveSources[id].filter(x => !x.includes('2T')).length) {
if (moveid === id && learnsetData[id] && !learnsetData[id].filter(x => !x.includes('2T')).length) {
problems.push(`${species.name} can't learn ${this.dex.moves.get(id).name}.`);
}
}

View File

@ -8,7 +8,7 @@ export const Scripts: ModdedBattleScriptsData = {
pokemon: {
inherit: true,
getStat(statName, unboosted, unmodified, fastReturn) {
// @ts-expect-error type checking prevents 'hp' from being passed, but we're paranoid
// @ts-ignore - type checking prevents 'hp' from being passed, but we're paranoid
if (statName === 'hp') throw new Error("Please read `maxhp` directly");
// base stat
@ -89,10 +89,10 @@ export const Scripts: ModdedBattleScriptsData = {
},
actions: {
inherit: true,
runMove(moveOrMoveName, pokemon, targetLoc, options) {
runMove(moveOrMoveName, pokemon, targetLoc, sourceEffect) {
let move = this.dex.getActiveMove(moveOrMoveName);
let target = this.battle.getTarget(pokemon, move, targetLoc);
if (!options?.sourceEffect && move.id !== 'struggle') {
if (!sourceEffect && move.id !== 'struggle') {
const changedMove = this.battle.runEvent('OverrideAction', pokemon, target, move);
if (changedMove && changedMove !== true) {
move = this.dex.getActiveMove(changedMove);
@ -107,7 +107,7 @@ export const Scripts: ModdedBattleScriptsData = {
// THIS IS PURELY A SANITY CHECK
// DO NOT TAKE ADVANTAGE OF THIS TO PREVENT A POKEMON FROM MOVING;
// USE this.battle.queue.cancelMove INSTEAD
this.battle.debug(`${pokemon.fullname} INCONSISTENT STATE, ALREADY MOVED: ${pokemon.moveThisTurn}`);
this.battle.debug('' + pokemon.fullname + ' INCONSISTENT STATE, ALREADY MOVED: ' + pokemon.moveThisTurn);
this.battle.clearActiveMove(true);
return;
}
@ -135,7 +135,7 @@ export const Scripts: ModdedBattleScriptsData = {
}
}
pokemon.moveUsed(move);
this.battle.actions.useMove(move, pokemon, { target, sourceEffect: options?.sourceEffect });
this.battle.actions.useMove(move, pokemon, target, sourceEffect);
this.battle.singleEvent('AfterMove', move, null, pokemon, target, move);
if (!move.selfSwitch && pokemon.side.foe.active[0].hp) this.battle.runEvent('AfterMoveSelf', pokemon, target, move);
},
@ -187,7 +187,10 @@ export const Scripts: ModdedBattleScriptsData = {
move.ignoreImmunity = (move.category === 'Status');
}
if (!target.runImmunity(move, true)) {
if (
(!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) &&
!target.runImmunity(move.type, true)
) {
return false;
}
@ -203,11 +206,6 @@ export const Scripts: ModdedBattleScriptsData = {
return false;
}
if (move.ohko && pokemon.level < target.level) {
this.battle.add('-immune', target, '[ohko]');
return false;
}
let accuracy = move.accuracy;
if (move.alwaysHit) {
accuracy = true;
@ -218,8 +216,13 @@ export const Scripts: ModdedBattleScriptsData = {
if (accuracy !== true) {
accuracy = Math.floor(accuracy * 255 / 100);
if (move.ohko) {
accuracy += (pokemon.level - target.level) * 2;
accuracy = Math.min(accuracy, 255);
if (pokemon.level >= target.level) {
accuracy += (pokemon.level - target.level) * 2;
accuracy = Math.min(accuracy, 255);
} else {
this.battle.add('-immune', target, '[ohko]');
return false;
}
}
if (!move.ignoreAccuracy) {
if (pokemon.boosts.accuracy > 0) {
@ -293,11 +296,13 @@ export const Scripts: ModdedBattleScriptsData = {
}
if (move.ohko) this.battle.add('-ohko');
this.battle.singleEvent('AfterMoveSecondary', move, null, target, pokemon, move);
this.battle.runEvent('AfterMoveSecondary', target, pokemon, move);
if (!move.negateSecondary) {
this.battle.singleEvent('AfterMoveSecondary', move, null, target, pokemon, move);
this.battle.runEvent('AfterMoveSecondary', target, pokemon, move);
}
if (move.recoil && move.totalDamage) {
this.battle.damage(this.calcRecoilDamage(move.totalDamage, move, pokemon), pokemon, target, 'recoil');
this.battle.damage(this.calcRecoilDamage(move.totalDamage, move), pokemon, target, 'recoil');
}
return damage;
},
@ -492,8 +497,10 @@ export const Scripts: ModdedBattleScriptsData = {
}
// Let's test for immunities.
if (!target.runImmunity(move, true)) {
return false;
if (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) {
if (!target.runImmunity(move.type, true)) {
return false;
}
}
// Is it an OHKO move?
@ -538,11 +545,11 @@ export const Scripts: ModdedBattleScriptsData = {
// Checking for the move's Critical Hit ratio
let critRatio = this.battle.runEvent('ModifyCritRatio', source, target, move, move.critRatio || 0);
critRatio = this.battle.clampIntRange(critRatio, 0, 5);
const critMult = [0, 17, 32, 64, 85, 128];
const critMult = [0, 16, 8, 4, 3, 2];
let isCrit = move.willCrit || false;
if (typeof move.willCrit === 'undefined') {
if (critRatio) {
isCrit = this.battle.random(256) < critMult[critRatio];
isCrit = this.battle.randomChance(1, critMult[critRatio]);
}
}
@ -619,7 +626,7 @@ export const Scripts: ModdedBattleScriptsData = {
}
if (move.id === 'present') {
const typeIndexes: { [k: string]: number } = {
const typeIndexes: {[k: string]: number} = {
Normal: 0, Fighting: 1, Flying: 2, Poison: 3, Ground: 4, Rock: 5, Bug: 7, Ghost: 8, Steel: 9,
Fire: 20, Water: 21, Grass: 22, Electric: 23, Psychic: 24, Ice: 25, Dragon: 26, Dark: 27,
};

View File

@ -1,4 +1,4 @@
export const TypeChart: import('../../../sim/dex-data').ModdedTypeDataTable = {
export const TypeChart: {[k: string]: ModdedTypeData} = {
fire: {
inherit: true,
damageTaken: {

View File

@ -4,7 +4,7 @@
* a volatile along with them to keep track of if their respective stat changes should be factored
* in during stat calculations or not.
*/
export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDataTable = {
export const Conditions: {[k: string]: ModdedConditionData} = {
brn: {
name: 'brn',
effectType: 'Status',
@ -46,7 +46,7 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
effectType: 'Status',
onStart(target, source, sourceEffect) {
if (sourceEffect && sourceEffect.effectType === 'Move') {
this.add('-status', target, 'slp', `[from] move: ${sourceEffect.name}`);
this.add('-status', target, 'slp', '[from] move: ' + sourceEffect.name);
} else {
this.add('-status', target, 'slp');
}

View File

@ -1,5 +1,5 @@
// Gen 2 Stadium fixes Dragon Fang and Dragon Scale having the wrong effects.
export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
export const Items: {[k: string]: ModdedItemData} = {
dragonfang: {
inherit: true,
onModifyDamage(damage, source, target, move) {
@ -10,6 +10,6 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
dragonscale: {
inherit: true,
onModifyDamage: undefined, // no inherit
onModifyDamage() {},
},
};

View File

@ -1,4 +1,4 @@
export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
export const Moves: {[k: string]: ModdedMoveData} = {
// Belly Drum no longer boosts attack by 2 stages if under 50% health.
bellydrum: {
inherit: true,
@ -25,7 +25,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
}
boosts = target.boosts.atk - originalStage;
target.boosts.atk = originalStage;
this.boost({ atk: boosts });
this.boost({atk: boosts});
},
},
destinybond: {

View File

@ -1,17 +1,8 @@
export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable = {
standardag: {
inherit: true,
ruleset: [
'Obtainable', 'Team Preview', 'Exact HP Mod', 'Cancel Mod', 'Beat Up Nicknames Mod',
],
},
export const Rulesets: {[k: string]: ModdedFormatData} = {
standard: {
effectType: 'ValidatorRule',
name: 'Standard',
ruleset: [
'Standard AG',
'Stadium Sleep Clause', 'Freeze Clause Mod', 'Self-KO Clause', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Stadium Items Clause',
],
ruleset: ['Obtainable', 'Team Preview', 'Stadium Sleep Clause', 'Freeze Clause Mod', 'Self-KO Clause', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Exact HP Mod', 'Cancel Mod', 'Stadium Items Clause'],
},
selfkoclause: {
effectType: 'Rule',

View File

@ -7,7 +7,7 @@ export const Scripts: ModdedBattleScriptsData = {
pokemon: {
inherit: true,
getStat(statName, unboosted, unmodified, fastReturn) {
// @ts-expect-error type checking prevents 'hp' from being passed, but we're paranoid
// @ts-ignore - type checking prevents 'hp' from being passed, but we're paranoid
if (statName === 'hp') throw new Error("Please read `maxhp` directly");
// base stat
@ -122,7 +122,10 @@ export const Scripts: ModdedBattleScriptsData = {
move.ignoreImmunity = (move.category === 'Status');
}
if (!target.runImmunity(move, true)) {
if (
(!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) &&
!target.runImmunity(move.type, true)
) {
return false;
}
@ -228,13 +231,15 @@ export const Scripts: ModdedBattleScriptsData = {
}
if (move.ohko) this.battle.add('-ohko');
this.battle.singleEvent('AfterMoveSecondary', move, null, target, pokemon, move);
this.battle.runEvent('AfterMoveSecondary', target, pokemon, move);
if (!move.negateSecondary) {
this.battle.singleEvent('AfterMoveSecondary', move, null, target, pokemon, move);
this.battle.runEvent('AfterMoveSecondary', target, pokemon, move);
}
// Implementing Recoil mechanics from Stadium 2.
// If a pokemon caused the other to faint with a recoil move and only one pokemon remains on both sides,
// recoil damage will not be taken.
if (move.recoil && move.totalDamage && (pokemon.side.pokemonLeft > 1 || target.side.pokemonLeft > 1 || target.hp)) {
this.battle.damage(this.calcRecoilDamage(move.totalDamage, move, pokemon), pokemon, target, 'recoil');
this.battle.damage(this.calcRecoilDamage(move.totalDamage, move), pokemon, target, 'recoil');
}
return damage;
},
@ -253,8 +258,10 @@ export const Scripts: ModdedBattleScriptsData = {
}
// Let's test for immunities.
if (!target.runImmunity(move, true)) {
return false;
if (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) {
if (!target.runImmunity(move.type, true)) {
return false;
}
}
// Is it an OHKO move?
@ -471,7 +478,7 @@ export const Scripts: ModdedBattleScriptsData = {
if (typeof effect === 'string') effect = this.dex.conditions.get(effect);
if (!target?.hp) return 0;
let success = null;
boost = this.runEvent('TryBoost', target, source, effect, { ...boost });
boost = this.runEvent('TryBoost', target, source, effect, {...boost});
let i: BoostID;
for (i in boost) {
const currentBoost: SparseBoostsTable = {};
@ -540,7 +547,7 @@ export const Scripts: ModdedBattleScriptsData = {
this.queue.clear();
// Fainting clears accumulated Bide damage
for (const pokemon of this.getAllActive()) {
if (pokemon.volatiles['bide']?.damage) {
if (pokemon.volatiles['bide'] && pokemon.volatiles['bide'].damage) {
pokemon.volatiles['bide'].damage = 0;
this.hint("Desync Clause Mod activated!");
this.hint("In Gen 1, Bide's accumulated damage is reset to 0 when a Pokemon faints.");

View File

@ -1,4 +1,4 @@
export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTable = {
export const Abilities: {[k: string]: ModdedAbilityData} = {
cutecharm: {
inherit: true,
onDamagingHit(damage, target, source, move) {
@ -51,19 +51,6 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
}
},
},
forecast: {
inherit: true,
flags: {},
},
hustle: {
inherit: true,
onSourceModifyAccuracy(accuracy, target, source, move) {
const physicalTypes = ['Normal', 'Fighting', 'Flying', 'Poison', 'Ground', 'Rock', 'Bug', 'Ghost', 'Steel'];
if (physicalTypes.includes(move.type) && typeof accuracy === 'number') {
return this.chainModify([3277, 4096]);
}
},
},
intimidate: {
inherit: true,
onStart(pokemon) {
@ -85,38 +72,22 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
if (target.volatiles['substitute']) {
this.add('-immune', target);
} else {
this.boost({ atk: -1 }, target, pokemon, null, true);
this.boost({atk: -1}, target, pokemon, null, true);
}
}
},
},
lightningrod: {
inherit: true,
onAnyRedirectTarget: undefined, // no inherit
onFoeRedirectTarget(target, source, source2, move) {
// don't count Hidden Power as Electric-type
if (this.dex.moves.get(move.id).type !== 'Electric') return;
if (move.type !== 'Electric') return;
if (this.validTarget(this.effectState.target, source, move.target)) {
return this.effectState.target;
}
},
},
magnetpull: {
inherit: true,
onFoeTrapPokemon: undefined, // no inherit
onFoeMaybeTrapPokemon: undefined, // no inherit
onAnyTrapPokemon(pokemon) {
if (pokemon.hasType('Steel') && pokemon.isAdjacent(this.effectState.target)) {
pokemon.tryTrap(true);
}
},
onAnyMaybeTrapPokemon(pokemon, source) {
if (!source) source = this.effectState.target;
if (!source || !pokemon.isAdjacent(source)) return;
if (!pokemon.knownType || pokemon.hasType('Steel')) {
pokemon.maybeTrapped = true;
}
},
isBreakable: true,
name: "Lightning Rod",
rating: 0,
num: 32,
},
minus: {
inherit: true,
@ -156,7 +127,7 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
},
raindish: {
inherit: true,
onWeather: undefined, // no inherit
onWeather() {},
onResidualOrder: 10,
onResidualSubOrder: 3,
onResidual(pokemon) {
@ -191,18 +162,23 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
},
trace: {
inherit: true,
onUpdate: undefined, // no inherit
onStart(pokemon) {
onUpdate(pokemon) {
if (!pokemon.isStarted) return;
const target = pokemon.side.randomFoe();
if (!target || target.fainted) return;
const ability = target.getAbility();
pokemon.setAbility(ability, target);
const bannedAbilities = ['forecast', 'multitype', 'trace'];
if (bannedAbilities.includes(target.ability)) {
return;
}
if (pokemon.setAbility(ability)) {
this.add('-ability', pokemon, ability, '[from] ability: Trace', '[of] ' + target);
}
},
flags: {},
},
truant: {
inherit: true,
onStart: undefined, // no inherit
onStart() {},
onSwitchIn(pokemon) {
pokemon.truantTurn = this.turn !== 0;
},

View File

@ -1,10 +1,10 @@
export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDataTable = {
export const Conditions: {[k: string]: ModdedConditionData} = {
slp: {
name: 'slp',
effectType: 'Status',
onStart(target, source, sourceEffect) {
if (sourceEffect && sourceEffect.effectType === 'Move') {
this.add('-status', target, 'slp', `[from] move: ${sourceEffect.name}`);
this.add('-status', target, 'slp', '[from] move: ' + sourceEffect.name);
} else {
this.add('-status', target, 'slp');
}
@ -51,6 +51,6 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
},
sandstorm: {
inherit: true,
onModifySpD: undefined, // no inherit
onModifySpD() {},
},
};

View File

@ -1,9 +1,9 @@
export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormatsDataTable = {
export const FormatsData: {[k: string]: ModdedSpeciesFormatsData} = {
bulbasaur: {
tier: "LC",
},
ivysaur: {
tier: "ZU",
tier: "NFE",
},
venusaur: {
tier: "UUBL",
@ -12,7 +12,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
charmeleon: {
tier: "PU",
tier: "NFE",
},
charizard: {
tier: "OU",
@ -21,7 +21,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
wartortle: {
tier: "PU",
tier: "NFE",
},
blastoise: {
tier: "UU",
@ -33,7 +33,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
butterfree: {
tier: "ZU",
tier: "PU",
},
weedle: {
tier: "LC",
@ -42,7 +42,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
beedrill: {
tier: "ZU",
tier: "PU",
},
pidgey: {
tier: "LC",
@ -63,7 +63,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
fearow: {
tier: "RUBL",
tier: "UU",
},
ekans: {
tier: "LC",
@ -78,7 +78,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
raichu: {
tier: "RU",
tier: "UU",
},
sandshrew: {
tier: "LC",
@ -111,13 +111,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
clefable: {
tier: "RU",
tier: "UU",
},
vulpix: {
tier: "LC",
},
ninetales: {
tier: "RU",
tier: "UU",
},
igglybuff: {
tier: "LC",
@ -141,7 +141,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
gloom: {
tier: "ZU",
tier: "NFE",
},
vileplume: {
tier: "UU",
@ -153,7 +153,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
parasect: {
tier: "ZU",
tier: "PU",
},
venonat: {
tier: "LC",
@ -171,7 +171,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
persian: {
tier: "RU",
tier: "UU",
},
psyduck: {
tier: "LC",
@ -183,7 +183,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
primeape: {
tier: "RU",
tier: "UU",
},
growlithe: {
tier: "LC",
@ -195,16 +195,16 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
poliwhirl: {
tier: "ZUBL",
tier: "NFE",
},
poliwrath: {
tier: "RU",
tier: "UU",
},
politoed: {
tier: "RU",
tier: "UU",
},
abra: {
tier: "ZU",
tier: "LC",
},
kadabra: {
tier: "UUBL",
@ -228,10 +228,10 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
victreebel: {
tier: "RU",
tier: "UU",
},
tentacool: {
tier: "ZU",
tier: "LC",
},
tentacruel: {
tier: "UU",
@ -240,16 +240,16 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
graveler: {
tier: "PU",
tier: "NFE",
},
golem: {
tier: "UU",
},
ponyta: {
tier: "ZU",
tier: "LC",
},
rapidash: {
tier: "RU",
tier: "UU",
},
slowpoke: {
tier: "LC",
@ -261,16 +261,16 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
magnemite: {
tier: "ZU",
tier: "LC",
},
magneton: {
tier: "OU",
},
farfetchd: {
tier: "ZU",
tier: "PU",
},
doduo: {
tier: "ZU",
tier: "LC",
},
dodrio: {
tier: "UUBL",
@ -294,7 +294,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
gastly: {
tier: "PU",
tier: "LC",
},
haunter: {
tier: "NU",
@ -309,10 +309,10 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UUBL",
},
drowzee: {
tier: "ZU",
tier: "LC",
},
hypno: {
tier: "RU",
tier: "UU",
},
krabby: {
tier: "LC",
@ -321,7 +321,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "PU",
},
voltorb: {
tier: "ZU",
tier: "LC",
},
electrode: {
tier: "UU",
@ -333,7 +333,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UUBL",
},
cubone: {
tier: "ZU",
tier: "LC",
},
marowak: {
tier: "UUBL",
@ -354,13 +354,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "PU",
},
koffing: {
tier: "ZU",
tier: "LC",
},
weezing: {
tier: "UUBL",
},
rhyhorn: {
tier: "ZU",
tier: "LC",
},
rhydon: {
tier: "UUBL",
@ -381,7 +381,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
seadra: {
tier: "PU",
tier: "NFE",
},
kingdra: {
tier: "UUBL",
@ -390,7 +390,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
seaking: {
tier: "ZU",
tier: "PU",
},
staryu: {
tier: "LC",
@ -399,7 +399,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
mrmime: {
tier: "RU",
tier: "UU",
},
scyther: {
tier: "UU",
@ -414,7 +414,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UUBL",
},
elekid: {
tier: "ZU",
tier: "LC",
},
electabuzz: {
tier: "UU",
@ -423,7 +423,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
magmar: {
tier: "RU",
tier: "UU",
},
pinsir: {
tier: "UU",
@ -438,10 +438,10 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
lapras: {
tier: "UU",
tier: "UUBL",
},
ditto: {
tier: "ZU",
tier: "PU",
},
eevee: {
tier: "LC",
@ -462,13 +462,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UUBL",
},
porygon: {
tier: "ZU",
tier: "LC",
},
porygon2: {
tier: "UUBL",
},
omanyte: {
tier: "PU",
tier: "LC",
},
omastar: {
tier: "UU",
@ -477,7 +477,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
kabutops: {
tier: "RU",
tier: "UU",
},
aerodactyl: {
tier: "OU",
@ -498,7 +498,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
dragonair: {
tier: "PU",
tier: "NFE",
},
dragonite: {
tier: "UUBL",
@ -516,13 +516,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
meganium: {
tier: "RU",
tier: "UU",
},
cyndaquil: {
tier: "LC",
},
quilava: {
tier: "ZU",
tier: "NFE",
},
typhlosion: {
tier: "UUBL",
@ -546,22 +546,22 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
noctowl: {
tier: "ZU",
tier: "PU",
},
ledyba: {
tier: "LC",
},
ledian: {
tier: "ZU",
tier: "PU",
},
spinarak: {
tier: "LC",
},
ariados: {
tier: "ZU",
tier: "PU",
},
chinchou: {
tier: "ZU",
tier: "LC",
},
lanturn: {
tier: "UU",
@ -576,13 +576,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
xatu: {
tier: "RU",
tier: "UU",
},
mareep: {
tier: "LC",
},
flaaffy: {
tier: "ZU",
tier: "NFE",
},
ampharos: {
tier: "UU",
@ -594,7 +594,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
azumarill: {
tier: "RU",
tier: "UU",
},
sudowoodo: {
tier: "NU",
@ -606,19 +606,19 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
jumpluff: {
tier: "RUBL",
tier: "UUBL",
},
aipom: {
tier: "ZU",
tier: "PU",
},
sunkern: {
tier: "LC",
},
sunflora: {
tier: "ZU",
tier: "PU",
},
yanma: {
tier: "ZUBL",
tier: "PU",
},
wooper: {
tier: "LC",
@ -633,7 +633,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
unown: {
tier: "ZU",
tier: "PU",
},
wynaut: {
tier: "Uber",
@ -645,7 +645,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
pineco: {
tier: "PU",
tier: "LC",
},
forretress: {
tier: "OU",
@ -672,7 +672,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
sneasel: {
tier: "RU",
tier: "UU",
},
teddiursa: {
tier: "LC",
@ -690,10 +690,10 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
piloswine: {
tier: "PUBL",
tier: "PU",
},
corsola: {
tier: "ZU",
tier: "PU",
},
remoraid: {
tier: "LC",
@ -702,16 +702,16 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
delibird: {
tier: "ZU",
tier: "PU",
},
mantine: {
tier: "RU",
tier: "UU",
},
skarmory: {
tier: "OU",
},
houndour: {
tier: "PU",
tier: "LC",
},
houndoom: {
tier: "UUBL",
@ -723,7 +723,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UUBL",
},
stantler: {
tier: "RU",
tier: "UU",
},
smeargle: {
tier: "UUBL",
@ -762,7 +762,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
grovyle: {
tier: "ZU",
tier: "NFE",
},
sceptile: {
tier: "UUBL",
@ -771,7 +771,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
combusken: {
tier: "PU",
tier: "NFE",
},
blaziken: {
tier: "UUBL",
@ -780,7 +780,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
marshtomp: {
tier: "PU",
tier: "NFE",
},
swampert: {
tier: "OU",
@ -789,13 +789,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
mightyena: {
tier: "ZU",
tier: "PU",
},
zigzagoon: {
tier: "NFE",
tier: "LC",
},
linoone: {
tier: "UUBL",
tier: "UU",
},
wurmple: {
tier: "LC",
@ -804,13 +804,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
beautifly: {
tier: "ZU",
tier: "PU",
},
cascoon: {
tier: "NFE",
},
dustox: {
tier: "ZU",
tier: "PU",
},
lotad: {
tier: "LC",
@ -828,7 +828,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
shiftry: {
tier: "RU",
tier: "UU",
},
taillow: {
tier: "LC",
@ -855,7 +855,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
masquerain: {
tier: "ZU",
tier: "PU",
},
shroomish: {
tier: "LC",
@ -876,7 +876,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
ninjask: {
tier: "RUBL",
tier: "UU",
},
shedinja: {
tier: "PU",
@ -888,7 +888,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NFE",
},
exploud: {
tier: "RU",
tier: "UU",
},
makuhita: {
tier: "LC",
@ -897,13 +897,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UUBL",
},
nosepass: {
tier: "ZU",
tier: "PU",
},
skitty: {
tier: "LC",
},
delcatty: {
tier: "ZU",
tier: "PU",
},
sableye: {
tier: "NU",
@ -915,22 +915,22 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
lairon: {
tier: "ZUBL",
tier: "NFE",
},
aggron: {
tier: "RU",
tier: "UU",
},
meditite: {
tier: "ZU",
tier: "LC",
},
medicham: {
tier: "OU",
tier: "UUBL",
},
electrike: {
tier: "LC",
},
manectric: {
tier: "RU",
tier: "UU",
},
plusle: {
tier: "NU",
@ -939,10 +939,10 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "PU",
},
volbeat: {
tier: "ZU",
tier: "PU",
},
illumise: {
tier: "ZU",
tier: "PU",
},
roselia: {
tier: "NU",
@ -957,7 +957,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
sharpedo: {
tier: "RU",
tier: "UU",
},
wailmer: {
tier: "LC",
@ -969,7 +969,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
camerupt: {
tier: "RU",
tier: "UU",
},
torkoal: {
tier: "NU",
@ -981,13 +981,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "UU",
},
spinda: {
tier: "ZU",
tier: "PU",
},
trapinch: {
tier: "PU",
tier: "LC",
},
vibrava: {
tier: "PU",
tier: "NFE",
},
flygon: {
tier: "OU",
@ -1035,13 +1035,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
lileep: {
tier: "ZU",
tier: "LC",
},
cradily: {
tier: "UU",
},
anorith: {
tier: "ZU",
tier: "LC",
},
armaldo: {
tier: "UUBL",
@ -1053,13 +1053,16 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "OU",
},
castform: {
tier: "ZU",
tier: "PU",
},
castformsunny: {
tier: "PU",
},
castformrainy: {
tier: "PU",
},
castformsnowy: {
tier: "PU",
},
kecleon: {
tier: "NU",
@ -1068,40 +1071,40 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "LC",
},
banette: {
tier: "RU",
tier: "UU",
},
duskull: {
tier: "PU",
tier: "LC",
},
dusclops: {
tier: "UUBL",
},
tropius: {
tier: "ZU",
tier: "PU",
},
chimecho: {
tier: "NU",
},
absol: {
tier: "RU",
tier: "UU",
},
snorunt: {
tier: "LC",
},
glalie: {
tier: "NUBL",
tier: "NU",
},
spheal: {
tier: "LC",
},
sealeo: {
tier: "PU",
tier: "NFE",
},
walrein: {
tier: "UU",
},
clamperl: {
tier: "PU",
tier: "LC",
},
huntail: {
tier: "NU",
@ -1113,13 +1116,13 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
luvdisc: {
tier: "ZU",
tier: "PU",
},
bagon: {
tier: "LC",
},
shelgon: {
tier: "ZUBL",
tier: "NFE",
},
salamence: {
tier: "OU",

View File

@ -1,7 +1,7 @@
export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
export const Items: {[k: string]: ModdedItemData} = {
aguavberry: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {
@ -12,7 +12,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
apicotberry: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {
@ -23,12 +23,12 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
berryjuice: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {
if (pokemon.hp <= pokemon.maxhp / 2) {
if (this.runEvent('TryHeal', pokemon, null, this.effect, 20) && pokemon.useItem()) {
if (this.runEvent('TryHeal', pokemon) && pokemon.useItem()) {
this.heal(20);
}
}
@ -37,7 +37,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
blackbelt: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifyAtkPriority: 1,
onModifyAtk(atk, user, target, move) {
if (move?.type === 'Fighting') {
@ -47,7 +47,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
blackglasses: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifySpAPriority: 1,
onModifySpA(spa, user, target, move) {
if (move?.type === 'Dark') {
@ -57,7 +57,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
charcoal: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifySpAPriority: 1,
onModifySpA(spa, user, target, move) {
if (move?.type === 'Fire') {
@ -67,7 +67,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
dragonfang: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifySpAPriority: 1,
onModifySpA(spa, user, target, move) {
if (move?.type === 'Dragon') {
@ -82,7 +82,6 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
num: 208,
gen: 3,
isNonstandard: "Unobtainable",
// No competitive use
},
fastball: {
inherit: true,
@ -90,7 +89,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
figyberry: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {
@ -101,7 +100,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
ganlonberry: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {
@ -112,7 +111,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
hardstone: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifyAtkPriority: 1,
onModifyAtk(atk, user, target, move) {
if (move?.type === 'Rock') {
@ -126,7 +125,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
iapapaberry: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {
@ -139,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', '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
'aerialace', 'aeroblast', 'aircutter', 'armthrust', 'barrage', 'beatup', 'bide', 'bind', 'blastburn', 'bonerush', 'bonemerang', 'bounce', 'brickbreak', 'bulletseed', 'clamp', 'cometpunch', 'crabhammer', 'crosschop', 'cut', 'dig', 'dive', 'doublekick', 'doubleslap', 'doubleedge', 'dragonbreath', 'dragonclaw', 'dragonrage', 'drillpeck', 'earthquake', 'eggbomb', 'endeavor', 'eruption', 'explosion', 'extremespeed', 'falseswipe', 'feintattack', 'firespin', 'flail', 'fly', 'frenzyplant', 'frustration', 'furyattack', 'furycutter', 'furyswipes', 'gust', 'hiddenpower', 'highjumpkick', 'hornattack', 'hydrocannon', 'hydropump', 'hyperbeam', 'iceball', 'iciclespear', 'jumpkick', 'karatechop', 'leafblade', 'lowkick', 'machpunch', 'magicalleaf', 'magnitude', 'megakick', 'megapunch', 'megahorn', 'meteormash', 'mudshot', 'muddywater', 'nightshade', 'outrage', 'overheat', 'payday', 'peck', 'petaldance', 'pinmissile', 'poisontail', 'pound', 'psychoboost', 'psywave', 'quickattack', 'rage', 'rapidspin', 'razorleaf', 'razorwind', 'return', 'revenge', 'reversal', 'rockblast', 'rockthrow', 'rollingkick', 'rollout', 'sandtomb', 'scratch', 'seismictoss', 'selfdestruct', 'shadowpunch', 'shockwave', 'signalbeam', 'silverwind', 'skullbash', 'skyattack', 'skyuppercut', 'slam', 'slash', 'snore', 'solarbeam', 'sonicboom', 'spikecannon', 'spitup', 'steelwing', 'strength', 'struggle', 'submission', 'surf', 'swift', 'tackle', 'takedown', 'thrash', 'tickle', 'triplekick', 'twister', 'uproar', 'visegrip', 'vinewhip', 'vitalthrow', 'volttackle', 'watergun', 'waterpulse', 'waterfall', 'weatherball', 'whirlpool', 'wingattack', 'wrap',
];
if (affectedByKingsRock.includes(move.id)) {
if (!move.secondaries) move.secondaries = [];
move.secondaries.push({
@ -152,7 +151,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
lansatberry: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {
@ -175,7 +174,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
liechiberry: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {
@ -191,7 +190,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
return this.chainModify(2);
}
},
onBasePower: undefined, // no inherit
onBasePower() {},
},
loveball: {
inherit: true,
@ -203,7 +202,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
magnet: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifySpAPriority: 1,
onModifySpA(spa, user, target, move) {
if (move?.type === 'Electric') {
@ -213,7 +212,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
magoberry: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {
@ -224,7 +223,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
metalcoat: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifyAtkPriority: 1,
onModifyAtk(atk, user, target, move) {
if (move?.type === 'Steel') {
@ -234,7 +233,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
miracleseed: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifySpAPriority: 1,
onModifySpA(spa, user, target, move) {
if (move?.type === 'Grass') {
@ -248,7 +247,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
mysticwater: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifySpAPriority: 1,
onModifySpA(spa, user, target, move) {
if (move?.type === 'Water') {
@ -258,7 +257,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
nevermeltice: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifySpAPriority: 1,
onModifySpA(spa, user, target, move) {
if (move?.type === 'Ice') {
@ -268,7 +267,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
oranberry: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {
@ -279,7 +278,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
petayaberry: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {
@ -290,7 +289,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
poisonbarb: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifyAtkPriority: 1,
onModifyAtk(atk, user, target, move) {
if (move?.type === 'Poison') {
@ -300,12 +299,12 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
quickclaw: {
inherit: true,
onFractionalPriority: undefined, // no inherit
onFractionalPriority() {},
// implemented in Pokemon#getActionSpeed()
},
salacberry: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {
@ -316,7 +315,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
seaincense: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifySpAPriority: 1,
onModifySpA(spa, user, target, move) {
if (move?.type === 'Water') {
@ -326,7 +325,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
sharpbeak: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifyAtkPriority: 1,
onModifyAtk(atk, user, target, move) {
if (move?.type === 'Flying') {
@ -336,7 +335,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
silkscarf: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifyAtkPriority: 1,
onModifyAtk(atk, user, target, move) {
if (move?.type === 'Normal') {
@ -346,7 +345,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
silverpowder: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifyAtkPriority: 1,
onModifyAtk(atk, user, target, move) {
if (move?.type === 'Bug') {
@ -356,7 +355,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
sitrusberry: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {
@ -370,7 +369,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
softsand: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifyAtkPriority: 1,
onModifyAtk(atk, user, target, move) {
if (move?.type === 'Ground') {
@ -380,7 +379,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
spelltag: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifyAtkPriority: 1,
onModifyAtk(atk, user, target, move) {
if (move?.type === 'Ghost') {
@ -394,7 +393,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
starfberry: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {
@ -405,7 +404,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
twistedspoon: {
inherit: true,
onBasePower: undefined, // no inherit
onBasePower() {},
onModifySpAPriority: 1,
onModifySpA(spa, user, target, move) {
if (move?.type === 'Psychic') {
@ -415,7 +414,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
wikiberry: {
inherit: true,
onUpdate: undefined, // no inherit
onUpdate() {},
onResidualOrder: 10,
onResidualSubOrder: 4,
onResidual(pokemon) {

View File

@ -2,7 +2,7 @@
* Gen 3 moves
*/
export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
export const Moves: {[k: string]: ModdedMoveData} = {
absorb: {
inherit: true,
pp: 20,
@ -18,15 +18,14 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
ancientpower: {
inherit: true,
flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
},
assist: {
inherit: true,
flags: { metronome: 1, noassist: 1, nosleeptalk: 1 },
flags: {contact: 1, protect: 1, mirror: 1},
},
astonish: {
inherit: true,
flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, minimize: 1 },
basePowerCallback(pokemon, target) {
if (target.volatiles['minimize']) return 60;
return 30;
},
},
beatup: {
inherit: true,
@ -41,16 +40,15 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
duration: 1,
onModifySpAPriority: -101,
onModifySpA(atk, pokemon, defender, move) {
if (!this.ruleTable.has('beatupnicknamesmod')) {
this.add('-activate', pokemon, 'move: Beat Up', '[of] ' + move.allies![0].name);
}
// https://www.smogon.com/forums/posts/8992145/
// this.add('-activate', pokemon, 'move: Beat Up', '[of] ' + move.allies![0].name);
this.event.modifier = 1;
return this.dex.species.get(move.allies!.shift()!.set.species).baseStats.atk;
return move.allies!.shift()!.species.baseStats.atk;
},
onFoeModifySpDPriority: -101,
onFoeModifySpD(def, pokemon) {
this.event.modifier = 1;
return this.dex.species.get(pokemon.set.species).baseStats.def;
return pokemon.species.baseStats.def;
},
},
},
@ -59,8 +57,18 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
accuracy: 100,
priority: 0,
condition: {
inherit: true,
onAfterSetStatus: undefined, // no inherit
duration: 3,
onLockMove: 'bide',
onStart(pokemon) {
this.effectState.totalDamage = 0;
this.add('-start', pokemon, 'move: Bide');
},
onDamagePriority: -101,
onDamage(damage, target, source, move) {
if (!move || move.effectType !== 'Move' || !source) return;
this.effectState.totalDamage += damage;
this.effectState.lastDamageSource = source;
},
onBeforeMove(pokemon, target, move) {
if (this.effectState.duration === 1) {
this.add('-end', pokemon, 'move: Bide');
@ -88,7 +96,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
damage: this.effectState.totalDamage * 2,
category: "Physical",
priority: 0,
flags: { contact: 1, protect: 1 },
flags: {contact: 1, protect: 1},
effectType: 'Move',
type: 'Normal',
} as unknown as ActiveMove;
@ -98,11 +106,17 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
}
this.add('-activate', pokemon, 'move: Bide');
},
onMoveAborted(pokemon) {
pokemon.removeVolatile('bide');
},
onEnd(pokemon) {
this.add('-end', pokemon, 'move: Bide', '[silent]');
},
},
},
blizzard: {
inherit: true,
onModifyMove: undefined, // no inherit
onModifyMove() { },
},
brickbreak: {
inherit: true,
@ -115,7 +129,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
charge: {
inherit: true,
boosts: undefined, // no inherit
boosts: null,
},
conversion: {
inherit: true,
@ -136,39 +150,20 @@ 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: {
inherit: true,
duration: 1,
noCopy: true,
onStart(target, source, move) {
this.effectState.slot = null;
this.effectState.damage = 0;
},
onRedirectTargetPriority: -1,
onRedirectTarget(target, source, source2) {
if (source !== this.effectState.target || !this.effectState.slot) return;
return this.getAtSlot(this.effectState.slot);
},
onDamagingHit: undefined, // no inherit
onDamagePriority: -101,
onDamage(damage, target, source, effect) {
if (
@ -183,7 +178,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
covet: {
inherit: true,
flags: { protect: 1, mirror: 1, noassist: 1 },
flags: {protect: 1, mirror: 1, noassist: 1},
},
crunch: {
inherit: true,
@ -201,14 +196,49 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
disable: {
inherit: true,
accuracy: 55,
flags: { protect: 1, mirror: 1, bypasssub: 1, metronome: 1 },
flags: {protect: 1, mirror: 1, bypasssub: 1},
volatileStatus: 'disable',
condition: {
inherit: true,
durationCallback() {
return this.random(2, 6);
},
"onResidualOrder": undefined, // no inherit
"onResidualSubOrder": undefined, // no inherit
noCopy: true,
onStart(pokemon) {
if (!this.queue.willMove(pokemon)) {
this.effectState.duration++;
}
if (!pokemon.lastMove) {
return false;
}
for (const moveSlot of pokemon.moveSlots) {
if (moveSlot.id === pokemon.lastMove.id) {
if (!moveSlot.pp) {
return false;
} else {
this.add('-start', pokemon, 'Disable', moveSlot.move);
this.effectState.move = pokemon.lastMove.id;
return;
}
}
}
return false;
},
onEnd(pokemon) {
this.add('-end', pokemon, 'move: Disable');
},
onBeforeMove(attacker, defender, move) {
if (move.id === this.effectState.move) {
this.add('cant', attacker, 'Disable', move);
return false;
}
},
onDisableMove(pokemon) {
for (const moveSlot of pokemon.moveSlots) {
if (moveSlot.id === this.effectState.move) {
pokemon.disableMove(moveSlot.id);
}
}
},
},
},
dive: {
@ -223,7 +253,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
name: "Doom Desire",
basePower: 120,
category: "Physical",
flags: { metronome: 1, futuremove: 1 },
flags: {},
willCrit: false,
type: '???',
} as unknown as ActiveMove;
@ -231,15 +261,15 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
Object.assign(target.side.slotConditions[target.position]['futuremove'], {
duration: 3,
move: 'doomdesire',
source,
source: source,
moveData: {
id: 'doomdesire',
name: "Doom Desire",
accuracy: 85,
basePower: 0,
damage,
damage: damage,
category: "Physical",
flags: { metronome: 1, futuremove: 1 },
flags: {futuremove: 1},
effectType: 'Move',
type: '???',
},
@ -252,45 +282,64 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
inherit: true,
volatileStatus: 'encore',
condition: {
inherit: true,
durationCallback() {
return this.random(3, 7);
},
onStart(target, source) {
const moveIndex = target.lastMove ? target.moves.indexOf(target.lastMove.id) : -1;
if (
!target.lastMove || target.lastMove.flags['failencore'] ||
!target.moveSlots[moveIndex] || target.moveSlots[moveIndex].pp <= 0
) {
// it failed
return false;
}
this.effectState.move = target.lastMove.id;
this.add('-start', target, 'Encore');
},
onOverrideAction(pokemon) {
return this.effectState.move;
},
onResidualOrder: 10,
onResidualSubOrder: 14,
onResidual(target) {
if (
target.moves.includes(this.effectState.move) &&
target.moveSlots[target.moves.indexOf(this.effectState.move)].pp <= 0
) {
// early termination if you run out of PP
target.removeVolatile('encore');
}
},
onEnd(target) {
this.add('-end', target, 'Encore');
},
onDisableMove(pokemon) {
if (!this.effectState.move || !pokemon.hasMove(this.effectState.move)) {
return;
}
for (const moveSlot of pokemon.moveSlots) {
if (moveSlot.id !== this.effectState.move) {
pokemon.disableMove(moveSlot.id);
}
}
},
},
},
extrasensory: {
inherit: true,
flags: { protect: 1, mirror: 1, metronome: 1, minimize: 1 },
basePowerCallback(pokemon, target) {
if (target.volatiles['minimize']) return 160;
return 80;
},
},
fakeout: {
inherit: true,
flags: { protect: 1, mirror: 1, metronome: 1 },
flags: {protect: 1, mirror: 1},
},
feintattack: {
inherit: true,
flags: { protect: 1, mirror: 1, metronome: 1 },
},
flail: {
inherit: true,
basePowerCallback(pokemon) {
const ratio = Math.max(Math.floor(pokemon.hp * 48 / pokemon.maxhp), 1);
let bp;
if (ratio < 2) {
bp = 200;
} else if (ratio < 5) {
bp = 150;
} else if (ratio < 10) {
bp = 100;
} else if (ratio < 17) {
bp = 80;
} else if (ratio < 33) {
bp = 40;
} else {
bp = 20;
}
this.debug(`BP: ${bp}`);
return bp;
},
flags: {protect: 1, mirror: 1},
},
flash: {
inherit: true,
@ -302,14 +351,15 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
followme: {
inherit: true,
volatileStatus: undefined, // no inherit
volatileStatus: undefined,
slotCondition: 'followme',
condition: {
inherit: true,
duration: 1,
onStart(target, source, effect) {
this.add('-singleturn', target, 'move: Follow Me');
this.effectState.slot = target.getSlot();
},
onFoeRedirectTargetPriority: 1,
onFoeRedirectTarget(target, source, source2, move) {
const userSlot = this.getAtSlot(this.effectState.slot);
if (this.validTarget(userSlot, source, move.target)) {
@ -336,15 +386,6 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
inherit: true,
ignoreImmunity: false,
},
haze: {
inherit: true,
onHitField() {
this.add('-clearallboost');
for (const pokemon of this.getAllActive()) {
pokemon.clearBoosts();
}
},
},
hiddenpower: {
inherit: true,
category: "Physical",
@ -402,17 +443,22 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
mimic: {
inherit: true,
flags: { protect: 1, bypasssub: 1, allyanim: 1, failencore: 1, noassist: 1, failmimic: 1 },
flags: {protect: 1, bypasssub: 1, allyanim: 1, failencore: 1, noassist: 1, failmimic: 1},
},
mirrorcoat: {
inherit: true,
condition: {
inherit: true,
duration: 1,
noCopy: true,
onStart(target, source, move) {
this.effectState.slot = null;
this.effectState.damage = 0;
},
onRedirectTargetPriority: -1,
onRedirectTarget(target, source, source2) {
if (source !== this.effectState.target || !this.effectState.slot) return;
return this.getAtSlot(this.effectState.slot);
},
onDamagingHit: undefined, // no inherit
onDamagePriority: -101,
onDamage(damage, target, source, effect) {
if (
@ -427,8 +473,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
mirrormove: {
inherit: true,
flags: { metronome: 1, failencore: 1, nosleeptalk: 1, noassist: 1 },
onTryHit: undefined, // no inherit
onTryHit() { },
onHit(pokemon) {
const noMirror = [
'assist', 'curse', 'doomdesire', 'focuspunch', 'futuresight', 'magiccoat', 'metronome', 'mimic', 'mirrormove', 'naturepower', 'psychup', 'roleplay', 'sketch', 'sleeptalk', 'spikes', 'spitup', 'taunt', 'teeterdance', 'transform',
@ -453,7 +498,10 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
needlearm: {
inherit: true,
flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, minimize: 1 },
basePowerCallback(pokemon, target) {
if (target.volatiles['minimize']) return 120;
return 60;
},
},
nightmare: {
inherit: true,
@ -469,59 +517,23 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
overheat: {
inherit: true,
flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
flags: {contact: 1, protect: 1, mirror: 1},
},
petaldance: {
inherit: true,
basePower: 70,
},
pursuit: {
inherit: true,
beforeTurnCallback(pokemon, target) {
if (['frz', 'slp'].includes(pokemon.status) ||
(pokemon.hasAbility('truant') && pokemon.truantTurn)) return;
if (pokemon.isAlly(target)) return;
target.addVolatile('pursuit');
const data = target.volatiles['pursuit'];
if (!data.sources) {
data.sources = [];
}
data.sources.push(pokemon);
},
},
recover: {
inherit: true,
pp: 20,
},
reversal: {
inherit: true,
basePowerCallback(pokemon) {
const ratio = Math.max(Math.floor(pokemon.hp * 48 / pokemon.maxhp), 1);
let bp;
if (ratio < 2) {
bp = 200;
} else if (ratio < 5) {
bp = 150;
} else if (ratio < 10) {
bp = 100;
} else if (ratio < 17) {
bp = 80;
} else if (ratio < 33) {
bp = 40;
} else {
bp = 20;
}
this.debug(`BP: ${bp}`);
return bp;
},
},
rocksmash: {
inherit: true,
basePower: 20,
},
sketch: {
inherit: true,
flags: { bypasssub: 1, failencore: 1, noassist: 1, failmimic: 1, nosketch: 1 },
flags: {bypasssub: 1, failencore: 1, noassist: 1, failmimic: 1},
},
sleeptalk: {
inherit: true,
@ -532,7 +544,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
const pp = moveSlot.pp;
const move = this.dex.moves.get(moveid);
if (moveid && !move.flags['nosleeptalk'] && !move.flags['charge']) {
moves.push({ move: moveid, pp });
moves.push({move: moveid, pp: pp});
}
}
if (!moves.length) {
@ -579,7 +591,7 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
struggle: {
inherit: true,
flags: { contact: 1, protect: 1, noassist: 1, failencore: 1, failmimic: 1, nosketch: 1 },
flags: {contact: 1, protect: 1, noassist: 1, failencore: 1, failmimic: 1},
accuracy: 100,
recoil: [1, 4],
struggleRecoil: false,
@ -590,31 +602,74 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
taunt: {
inherit: true,
flags: { protect: 1, bypasssub: 1, metronome: 1 },
flags: {protect: 1, bypasssub: 1},
condition: {
inherit: true,
duration: 2,
durationCallback: undefined, // no inherit
onStart(target) {
this.add('-start', target, 'move: Taunt');
},
onResidualOrder: 10,
onResidualSubOrder: 15,
onEnd(target) {
this.add('-end', target, 'move: Taunt', '[silent]');
},
onBeforeMovePriority: undefined, // no inherit
onDisableMove(pokemon) {
for (const moveSlot of pokemon.moveSlots) {
if (this.dex.moves.get(moveSlot.move).category === 'Status') {
pokemon.disableMove(moveSlot.id);
}
}
},
onBeforeMove(attacker, defender, move) {
if (move.category === 'Status') {
this.add('cant', attacker, 'move: Taunt', move);
return false;
}
},
},
},
teeterdance: {
inherit: true,
flags: { protect: 1, metronome: 1 },
flags: {protect: 1},
},
tickle: {
inherit: true,
flags: { protect: 1, reflectable: 1, mirror: 1, bypasssub: 1, metronome: 1 },
flags: {protect: 1, reflectable: 1, mirror: 1, bypasssub: 1},
},
uproar: {
inherit: true,
condition: {
inherit: true,
durationCallback() {
return this.random(2, 6);
onStart(target) {
this.add('-start', target, 'Uproar');
// 2-5 turns
this.effectState.duration = this.random(2, 6);
},
onResidual(target) {
if (target.volatiles['throatchop']) {
target.removeVolatile('uproar');
return;
}
if (target.lastMove && target.lastMove.id === 'struggle') {
// don't lock
delete target.volatiles['uproar'];
}
this.add('-start', target, 'Uproar', '[upkeep]');
},
onResidualOrder: 10,
onResidualSubOrder: 11,
onEnd(target) {
this.add('-end', target, 'Uproar');
},
onLockMove: 'uproar',
onAnySetStatus(status, pokemon) {
if (status.id === 'slp') {
if (pokemon === this.effectState.target) {
this.add('-fail', pokemon, 'slp', '[from] Uproar', '[msg]');
} else {
this.add('-fail', pokemon, 'slp', '[from] Uproar');
}
return null;
}
},
},
},
@ -624,11 +679,11 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
},
volttackle: {
inherit: true,
secondary: undefined, // no inherit
secondary: null,
},
waterfall: {
inherit: true,
secondary: undefined, // no inherit
secondary: null,
},
weatherball: {
inherit: true,

View File

@ -0,0 +1,882 @@
{
"venusaur": {
"level": 82,
"moves": ["curse", "earthquake", "hiddenpowerrock", "leechseed", "sleeppowder", "sludgebomb", "swordsdance", "synthesis"]
},
"charizard": {
"level": 80,
"moves": ["bellydrum", "dragondance", "earthquake", "fireblast", "hiddenpowerflying", "substitute"]
},
"blastoise": {
"level": 84,
"moves": ["earthquake", "icebeam", "mirrorcoat", "rest", "roar", "sleeptalk", "surf", "toxic"]
},
"butterfree": {
"level": 91,
"moves": ["gigadrain", "hiddenpowerfire", "morningsun", "psychic", "sleeppowder", "stunspore", "toxic"]
},
"beedrill": {
"level": 90,
"moves": ["brickbreak", "doubleedge", "endure", "hiddenpowerbug", "sludgebomb", "swordsdance"]
},
"pidgeot": {
"level": 88,
"moves": ["aerialace", "hiddenpowerground", "quickattack", "return", "substitute", "toxic"]
},
"raticate": {
"level": 88,
"moves": ["endeavor", "hiddenpowerground", "quickattack", "return", "reversal", "shadowball", "substitute"]
},
"fearow": {
"level": 84,
"moves": ["agility", "batonpass", "drillpeck", "hiddenpowerground", "quickattack", "return", "substitute"]
},
"arbok": {
"level": 90,
"moves": ["doubleedge", "earthquake", "hiddenpowerfire", "rest", "rockslide", "sleeptalk", "sludgebomb"]
},
"pikachu": {
"level": 88,
"moves": ["hiddenpowerice", "substitute", "surf", "thunderbolt"]
},
"raichu": {
"level": 84,
"moves": ["encore", "focuspunch", "hiddenpowergrass", "hiddenpowerice", "substitute", "surf", "thunderbolt", "thunderwave"]
},
"sandslash": {
"level": 84,
"moves": ["earthquake", "hiddenpowerbug", "rapidspin", "rockslide", "swordsdance", "toxic"]
},
"nidoqueen": {
"level": 84,
"moves": ["earthquake", "fireblast", "icebeam", "shadowball", "sludgebomb", "superpower"]
},
"nidoking": {
"level": 84,
"moves": ["earthquake", "fireblast", "icebeam", "megahorn", "sludgebomb", "substitute", "thunderbolt"]
},
"clefable": {
"level": 84,
"moves": ["calmmind", "counter", "icebeam", "return", "shadowball", "softboiled", "thunderbolt", "thunderwave"]
},
"ninetales": {
"level": 83,
"moves": ["fireblast", "flamethrower", "hiddenpowergrass", "hypnosis", "substitute", "toxic", "willowisp"]
},
"wigglytuff": {
"level": 90,
"moves": ["fireblast", "icebeam", "protect", "return", "thunderbolt", "toxic", "wish"]
},
"vileplume": {
"level": 85,
"moves": ["aromatherapy", "hiddenpowerfire", "sleeppowder", "sludgebomb", "solarbeam", "sunnyday", "synthesis"]
},
"parasect": {
"level": 91,
"moves": ["aromatherapy", "gigadrain", "hiddenpowerbug", "return", "spore", "stunspore", "swordsdance"]
},
"venomoth": {
"level": 88,
"moves": ["batonpass", "hiddenpowerground", "signalbeam", "sleeppowder", "sludgebomb", "substitute"]
},
"dugtrio": {
"level": 81,
"moves": ["aerialace", "earthquake", "hiddenpowerbug", "rockslide", "substitute"]
},
"persian": {
"level": 84,
"moves": ["fakeout", "hiddenpowerground", "hypnosis", "irontail", "return", "shadowball", "substitute"]
},
"golduck": {
"level": 83,
"moves": ["calmmind", "hiddenpowergrass", "hydropump", "hypnosis", "icebeam", "substitute", "surf"]
},
"primeape": {
"level": 85,
"moves": ["bulkup", "crosschop", "earthquake", "hiddenpowerghost", "rockslide", "substitute"]
},
"arcanine": {
"level": 83,
"moves": ["extremespeed", "fireblast", "flamethrower", "hiddenpowergrass", "rest", "sleeptalk", "toxic"]
},
"poliwrath": {
"level": 84,
"moves": ["brickbreak", "bulkup", "hiddenpowerghost", "hydropump", "hypnosis", "icebeam", "substitute"]
},
"alakazam": {
"level": 81,
"moves": ["calmmind", "encore", "firepunch", "icepunch", "psychic", "recover", "substitute"]
},
"machamp": {
"level": 83,
"moves": ["bulkup", "crosschop", "earthquake", "hiddenpowerghost", "rest", "rockslide", "sleeptalk"]
},
"victreebel": {
"level": 84,
"moves": ["hiddenpowerfire", "sleeppowder", "sludgebomb", "solarbeam", "sunnyday"]
},
"tentacruel": {
"level": 83,
"moves": ["gigadrain", "haze", "hydropump", "icebeam", "rapidspin", "surf", "toxic"]
},
"golem": {
"level": 84,
"moves": ["doubleedge", "earthquake", "explosion", "hiddenpowerbug", "rockslide", "toxic"]
},
"rapidash": {
"level": 84,
"moves": ["fireblast", "hiddenpowergrass", "hiddenpowerrock", "substitute", "toxic"]
},
"slowbro": {
"level": 82,
"moves": ["calmmind", "fireblast", "icebeam", "psychic", "rest", "sleeptalk", "surf", "thunderwave"]
},
"magneton": {
"level": 81,
"moves": ["hiddenpowergrass", "hiddenpowerice", "rest", "sleeptalk", "thunderbolt", "toxic"]
},
"farfetchd": {
"level": 91,
"moves": ["agility", "batonpass", "hiddenpowerflying", "return", "swordsdance"]
},
"dodrio": {
"level": 82,
"moves": ["drillpeck", "flail", "hiddenpowerground", "quickattack", "return", "substitute"]
},
"dewgong": {
"level": 88,
"moves": ["encore", "hiddenpowergrass", "icebeam", "rest", "sleeptalk", "surf", "toxic"]
},
"muk": {
"level": 84,
"moves": ["brickbreak", "curse", "explosion", "fireblast", "hiddenpowerghost", "rest", "sludgebomb"]
},
"cloyster": {
"level": 80,
"moves": ["explosion", "icebeam", "rapidspin", "spikes", "surf", "toxic"]
},
"gengar": {
"level": 79,
"moves": ["destinybond", "explosion", "firepunch", "hypnosis", "icepunch", "substitute", "thunderbolt", "willowisp"]
},
"hypno": {
"level": 84,
"moves": ["batonpass", "calmmind", "firepunch", "hypnosis", "protect", "psychic", "toxic", "wish"]
},
"kingler": {
"level": 90,
"moves": ["doubleedge", "hiddenpowerghost", "hiddenpowerground", "surf", "swordsdance"]
},
"electrode": {
"level": 84,
"moves": ["explosion", "hiddenpowergrass", "hiddenpowerice", "substitute", "thunderbolt", "thunderwave", "toxic"]
},
"exeggutor": {
"level": 82,
"moves": ["explosion", "gigadrain", "hiddenpowerfire", "hiddenpowerice", "leechseed", "psychic", "sleeppowder", "solarbeam", "sunnyday"]
},
"marowak": {
"level": 82,
"moves": ["bonemerang", "doubleedge", "earthquake", "rockslide", "swordsdance"]
},
"hitmonlee": {
"level": 85,
"moves": ["bulkup", "earthquake", "hiddenpowerghost", "highjumpkick", "machpunch", "rockslide", "substitute"]
},
"hitmonchan": {
"level": 88,
"moves": ["bulkup", "earthquake", "hiddenpowerghost", "machpunch", "rapidspin", "skyuppercut", "toxic"]
},
"lickitung": {
"level": 90,
"moves": ["counter", "healbell", "protect", "return", "seismictoss", "toxic", "wish"]
},
"weezing": {
"level": 82,
"moves": ["explosion", "fireblast", "haze", "painsplit", "sludgebomb", "toxic", "willowisp"]
},
"rhydon": {
"level": 83,
"moves": ["doubleedge", "earthquake", "megahorn", "rockslide", "substitute", "swordsdance"]
},
"tangela": {
"level": 90,
"moves": ["hiddenpowergrass", "leechseed", "morningsun", "sleeppowder", "stunspore"]
},
"kangaskhan": {
"level": 84,
"moves": ["earthquake", "fakeout", "focuspunch", "rest", "return", "shadowball", "substitute", "toxic"]
},
"seaking": {
"level": 90,
"moves": ["hiddenpowergrass", "hydropump", "icebeam", "megahorn", "raindance"]
},
"starmie": {
"level": 79,
"moves": ["hydropump", "icebeam", "psychic", "recover", "surf", "thunderbolt"]
},
"mrmime": {
"level": 84,
"moves": ["barrier", "batonpass", "calmmind", "encore", "firepunch", "hypnosis", "psychic", "substitute", "thunderbolt"]
},
"scyther": {
"level": 84,
"moves": ["aerialace", "batonpass", "hiddenpowerground", "hiddenpowerrock", "quickattack", "silverwind", "swordsdance"]
},
"jynx": {
"level": 82,
"moves": ["calmmind", "hiddenpowerfire", "icebeam", "lovelykiss", "psychic", "substitute"]
},
"electabuzz": {
"level": 84,
"moves": ["crosschop", "firepunch", "focuspunch", "hiddenpowergrass", "icepunch", "substitute", "thunderbolt"]
},
"magmar": {
"level": 84,
"moves": ["crosschop", "fireblast", "flamethrower", "hiddenpowergrass", "psychic", "substitute", "thunderpunch"]
},
"pinsir": {
"level": 83,
"moves": ["earthquake", "hiddenpowerbug", "return", "rockslide", "swordsdance"]
},
"tauros": {
"level": 79,
"moves": ["doubleedge", "earthquake", "hiddenpowerghost", "hiddenpowerrock", "return"]
},
"gyarados": {
"level": 79,
"moves": ["doubleedge", "dragondance", "earthquake", "hiddenpowerflying", "hydropump", "taunt"]
},
"lapras": {
"level": 82,
"moves": ["healbell", "icebeam", "rest", "sleeptalk", "surf", "thunderbolt", "toxic"]
},
"ditto": {
"level": 100,
"moves": ["transform"]
},
"vaporeon": {
"level": 82,
"moves": ["icebeam", "protect", "surf", "toxic", "wish"]
},
"jolteon": {
"level": 80,
"moves": ["batonpass", "hiddenpowerice", "substitute", "thunderbolt", "toxic", "wish"]
},
"flareon": {
"level": 88,
"moves": ["doubleedge", "fireblast", "hiddenpowergrass", "protect", "shadowball", "toxic", "wish"]
},
"omastar": {
"level": 84,
"moves": ["hiddenpowergrass", "hydropump", "icebeam", "raindance", "spikes", "surf"]
},
"kabutops": {
"level": 84,
"moves": ["brickbreak", "doubleedge", "hiddenpowerground", "rockslide", "surf", "swordsdance"]
},
"aerodactyl": {
"level": 79,
"moves": ["doubleedge", "earthquake", "hiddenpowerflying", "rockslide", "substitute"]
},
"snorlax": {
"level": 77,
"moves": ["bodyslam", "curse", "earthquake", "rest", "return", "selfdestruct", "shadowball", "sleeptalk"]
},
"articuno": {
"level": 82,
"moves": ["healbell", "hiddenpowerfire", "icebeam", "protect", "rest", "roar", "sleeptalk", "toxic"]
},
"zapdos": {
"level": 78,
"moves": ["agility", "batonpass", "hiddenpowerice", "substitute", "thunderbolt", "thunderwave", "toxic"]
},
"moltres": {
"level": 80,
"moves": ["fireblast", "flamethrower", "hiddenpowergrass", "morningsun", "substitute", "toxic", "willowisp"]
},
"dragonite": {
"level": 81,
"moves": ["doubleedge", "dragondance", "earthquake", "flamethrower", "healbell", "hiddenpowerflying", "icebeam", "substitute"]
},
"mewtwo": {
"level": 73,
"moves": ["calmmind", "flamethrower", "icebeam", "psychic", "recover", "substitute", "thunderbolt"]
},
"mew": {
"level": 76,
"moves": ["calmmind", "explosion", "flamethrower", "icebeam", "psychic", "softboiled", "thunderbolt", "thunderwave", "transform"]
},
"meganium": {
"level": 84,
"moves": ["bodyslam", "hiddenpowergrass", "leechseed", "synthesis", "toxic"]
},
"typhlosion": {
"level": 82,
"moves": ["fireblast", "flamethrower", "focuspunch", "hiddenpowergrass", "hiddenpowerice", "substitute", "thunderpunch"]
},
"feraligatr": {
"level": 84,
"moves": ["earthquake", "hiddenpowerflying", "hydropump", "rockslide", "swordsdance"]
},
"furret": {
"level": 90,
"moves": ["doubleedge", "quickattack", "return", "reversal", "shadowball", "substitute", "trick"]
},
"noctowl": {
"level": 90,
"moves": ["hypnosis", "psychic", "reflect", "toxic", "whirlwind"]
},
"ledian": {
"level": 92,
"moves": ["agility", "batonpass", "lightscreen", "reflect", "silverwind", "swordsdance", "toxic"]
},
"ariados": {
"level": 90,
"moves": ["agility", "batonpass", "signalbeam", "sludgebomb", "spiderweb", "toxic"]
},
"crobat": {
"level": 82,
"moves": ["aerialace", "haze", "hiddenpowerground", "shadowball", "sludgebomb", "taunt", "toxic"]
},
"lanturn": {
"level": 84,
"moves": ["confuseray", "icebeam", "rest", "sleeptalk", "surf", "thunderbolt", "thunderwave", "toxic"]
},
"togetic": {
"level": 90,
"moves": ["charm", "encore", "flamethrower", "seismictoss", "softboiled", "thunderwave", "toxic"]
},
"xatu": {
"level": 84,
"moves": ["batonpass", "calmmind", "hiddenpowerfire", "psychic", "reflect", "wish"]
},
"ampharos": {
"level": 84,
"moves": ["firepunch", "healbell", "hiddenpowergrass", "hiddenpowerice", "thunderbolt", "toxic"]
},
"bellossom": {
"level": 88,
"moves": ["hiddenpowergrass", "leechseed", "moonlight", "sleeppowder", "sludgebomb", "stunspore"]
},
"azumarill": {
"level": 85,
"moves": ["brickbreak", "encore", "hiddenpowerghost", "hydropump", "return"]
},
"sudowoodo": {
"level": 89,
"moves": ["brickbreak", "doubleedge", "earthquake", "explosion", "rockslide", "toxic"]
},
"politoed": {
"level": 84,
"moves": ["hiddenpowergrass", "hypnosis", "icebeam", "rest", "surf", "toxic"]
},
"jumpluff": {
"level": 83,
"moves": ["encore", "hiddenpowerflying", "leechseed", "sleeppowder", "substitute", "toxic"]
},
"aipom": {
"level": 90,
"moves": ["batonpass", "doubleedge", "focuspunch", "shadowball", "substitute", "thunderwave"]
},
"sunflora": {
"level": 91,
"moves": ["hiddenpowerfire", "leechseed", "razorleaf", "synthesis", "toxic"]
},
"yanma": {
"level": 90,
"moves": ["hiddenpowerflying", "hypnosis", "reversal", "shadowball", "substitute"]
},
"quagsire": {
"level": 84,
"moves": ["counter", "curse", "earthquake", "hiddenpowerrock", "icebeam", "rest", "surf", "toxic"]
},
"espeon": {
"level": 81,
"moves": ["batonpass", "calmmind", "hiddenpowerfire", "morningsun", "psychic", "reflect"]
},
"umbreon": {
"level": 82,
"moves": ["batonpass", "hiddenpowerdark", "protect", "toxic", "wish"]
},
"murkrow": {
"level": 89,
"moves": ["doubleedge", "drillpeck", "hiddenpowerfighting", "hiddenpowerground", "meanlook", "perishsong", "protect", "shadowball", "substitute"]
},
"slowking": {
"level": 84,
"moves": ["calmmind", "flamethrower", "icebeam", "psychic", "rest", "sleeptalk", "surf", "thunderwave"]
},
"misdreavus": {
"level": 84,
"moves": ["calmmind", "hiddenpowerice", "meanlook", "perishsong", "protect", "substitute", "thunderbolt", "toxic"]
},
"unown": {
"level": 100,
"moves": ["hiddenpowerpsychic"]
},
"wobbuffet": {
"level": 77,
"moves": ["counter", "destinybond", "encore", "mirrorcoat"]
},
"girafarig": {
"level": 84,
"moves": ["agility", "batonpass", "calmmind", "psychic", "substitute", "thunderbolt", "thunderwave", "wish"]
},
"forretress": {
"level": 80,
"moves": ["earthquake", "explosion", "hiddenpowerbug", "rapidspin", "spikes", "toxic"]
},
"dunsparce": {
"level": 88,
"moves": ["bodyslam", "curse", "headbutt", "rest", "rockslide", "shadowball", "thunderwave"]
},
"gligar": {
"level": 84,
"moves": ["earthquake", "hiddenpowerflying", "irontail", "quickattack", "rockslide", "substitute", "swordsdance"]
},
"steelix": {
"level": 82,
"moves": ["doubleedge", "earthquake", "explosion", "hiddenpowerrock", "irontail", "rest", "roar", "toxic"]
},
"granbull": {
"level": 84,
"moves": ["bulkup", "earthquake", "healbell", "overheat", "rest", "return", "shadowball", "thunderwave"]
},
"qwilfish": {
"level": 84,
"moves": ["destinybond", "hydropump", "selfdestruct", "shadowball", "sludgebomb", "spikes", "swordsdance"]
},
"scizor": {
"level": 82,
"moves": ["agility", "batonpass", "hiddenpowerground", "hiddenpowerrock", "morningsun", "silverwind", "steelwing", "swordsdance"]
},
"shuckle": {
"level": 91,
"moves": ["encore", "rest", "toxic", "wrap"]
},
"heracross": {
"level": 80,
"moves": ["brickbreak", "focuspunch", "megahorn", "rest", "rockslide", "sleeptalk", "substitute", "swordsdance"]
},
"sneasel": {
"level": 84,
"moves": ["brickbreak", "doubleedge", "hiddenpowerflying", "shadowball", "substitute", "swordsdance"]
},
"ursaring": {
"level": 82,
"moves": ["earthquake", "focuspunch", "hiddenpowerghost", "return", "swordsdance"]
},
"magcargo": {
"level": 90,
"moves": ["fireblast", "hiddenpowergrass", "rest", "sleeptalk", "toxic", "yawn"]
},
"piloswine": {
"level": 90,
"moves": ["doubleedge", "earthquake", "icebeam", "protect", "rockslide", "toxic"]
},
"corsola": {
"level": 91,
"moves": ["calmmind", "icebeam", "recover", "surf", "toxic"]
},
"octillery": {
"level": 88,
"moves": ["fireblast", "hiddenpowergrass", "icebeam", "rockblast", "surf", "thunderwave"]
},
"delibird": {
"level": 91,
"moves": ["aerialace", "focuspunch", "hiddenpowerground", "icebeam", "quickattack"]
},
"mantine": {
"level": 84,
"moves": ["haze", "hiddenpowergrass", "icebeam", "raindance", "rest", "sleeptalk", "surf", "toxic"]
},
"skarmory": {
"level": 80,
"moves": ["drillpeck", "hiddenpowerground", "protect", "rest", "sleeptalk", "spikes", "toxic", "whirlwind"]
},
"houndoom": {
"level": 82,
"moves": ["crunch", "fireblast", "flamethrower", "hiddenpowergrass", "pursuit", "willowisp"]
},
"kingdra": {
"level": 82,
"moves": ["hiddenpowergrass", "hydropump", "icebeam", "raindance", "substitute", "surf"]
},
"donphan": {
"level": 82,
"moves": ["earthquake", "rapidspin", "rest", "rockslide", "sleeptalk", "toxic"]
},
"porygon2": {
"level": 82,
"moves": ["icebeam", "recover", "return", "thunderbolt", "thunderwave", "toxic"]
},
"stantler": {
"level": 84,
"moves": ["earthquake", "hypnosis", "return", "shadowball", "thunderbolt"]
},
"smeargle": {
"level": 84,
"moves": ["encore", "explosion", "spikes", "spore"]
},
"hitmontop": {
"level": 84,
"moves": ["bulkup", "earthquake", "hiddenpowerghost", "highjumpkick", "machpunch", "rockslide", "toxic"]
},
"miltank": {
"level": 81,
"moves": ["bodyslam", "curse", "earthquake", "healbell", "milkdrink", "toxic"]
},
"blissey": {
"level": 80,
"moves": ["aromatherapy", "calmmind", "icebeam", "seismictoss", "softboiled", "thunderbolt", "thunderwave", "toxic"]
},
"raikou": {
"level": 79,
"moves": ["calmmind", "crunch", "hiddenpowergrass", "hiddenpowerice", "rest", "sleeptalk", "substitute", "thunderbolt"]
},
"entei": {
"level": 82,
"moves": ["bodyslam", "calmmind", "fireblast", "flamethrower", "hiddenpowergrass", "hiddenpowerice", "solarbeam", "substitute", "sunnyday"]
},
"suicune": {
"level": 77,
"moves": ["calmmind", "icebeam", "rest", "sleeptalk", "substitute", "surf", "toxic"]
},
"tyranitar": {
"level": 79,
"moves": ["dragondance", "earthquake", "fireblast", "focuspunch", "hiddenpowerbug", "icebeam", "pursuit", "rockslide", "substitute"]
},
"lugia": {
"level": 73,
"moves": ["aeroblast", "calmmind", "earthquake", "icebeam", "recover", "substitute", "thunderbolt", "toxic"]
},
"hooh": {
"level": 74,
"moves": ["calmmind", "earthquake", "recover", "sacredfire", "substitute", "thunderbolt", "toxic"]
},
"celebi": {
"level": 79,
"moves": ["batonpass", "calmmind", "healbell", "hiddenpowergrass", "leechseed", "psychic", "recover"]
},
"sceptile": {
"level": 82,
"moves": ["focuspunch", "hiddenpowerice", "leafblade", "leechseed", "substitute", "thunderpunch"]
},
"blaziken": {
"level": 82,
"moves": ["endure", "fireblast", "hiddenpowerice", "reversal", "rockslide", "skyuppercut", "swordsdance", "thunderpunch"]
},
"swampert": {
"level": 80,
"moves": ["earthquake", "hydropump", "icebeam", "protect", "rest", "rockslide", "sleeptalk", "surf", "toxic"]
},
"mightyena": {
"level": 90,
"moves": ["crunch", "doubleedge", "healbell", "hiddenpowerfighting", "protect", "shadowball", "toxic"]
},
"linoone": {
"level": 84,
"moves": ["bellydrum", "extremespeed", "flail", "hiddenpowerground", "shadowball", "substitute"]
},
"beautifly": {
"level": 91,
"moves": ["hiddenpowerbug", "hiddenpowerflying", "morningsun", "stunspore", "substitute", "toxic"]
},
"dustox": {
"level": 91,
"moves": ["hiddenpowerground", "lightscreen", "moonlight", "sludgebomb", "toxic", "whirlwind"]
},
"ludicolo": {
"level": 82,
"moves": ["hiddenpowergrass", "icebeam", "leechseed", "raindance", "substitute", "surf"]
},
"shiftry": {
"level": 85,
"moves": ["brickbreak", "explosion", "shadowball", "swordsdance"]
},
"swellow": {
"level": 82,
"moves": ["aerialace", "doubleedge", "hiddenpowerfighting", "hiddenpowerground", "quickattack", "return"]
},
"pelipper": {
"level": 88,
"moves": ["icebeam", "protect", "rest", "sleeptalk", "surf", "toxic"]
},
"gardevoir": {
"level": 82,
"moves": ["calmmind", "firepunch", "hypnosis", "psychic", "substitute", "thunderbolt", "willowisp"]
},
"masquerain": {
"level": 90,
"moves": ["hydropump", "icebeam", "stunspore", "substitute", "toxic"]
},
"breloom": {
"level": 81,
"moves": ["focuspunch", "hiddenpowerghost", "hiddenpowerrock", "leechseed", "machpunch", "skyuppercut", "spore", "substitute", "swordsdance"]
},
"vigoroth": {
"level": 87,
"moves": ["brickbreak", "bulkup", "earthquake", "return", "shadowball", "slackoff"]
},
"slaking": {
"level": 81,
"moves": ["doubleedge", "earthquake", "focuspunch", "return", "shadowball"]
},
"ninjask": {
"level": 84,
"moves": ["aerialace", "batonpass", "hiddenpowerrock", "protect", "silverwind", "substitute", "swordsdance"]
},
"shedinja": {
"level": 90,
"moves": ["agility", "batonpass", "hiddenpowerground", "shadowball", "silverwind", "toxic"]
},
"exploud": {
"level": 84,
"moves": ["earthquake", "flamethrower", "icebeam", "overheat", "return", "shadowball", "substitute"]
},
"hariyama": {
"level": 82,
"moves": ["bulkup", "crosschop", "fakeout", "hiddenpowerghost", "rest", "rockslide", "sleeptalk"]
},
"nosepass": {
"level": 91,
"moves": ["earthquake", "explosion", "rockslide", "thunderwave", "toxic"]
},
"delcatty": {
"level": 91,
"moves": ["batonpass", "doubleedge", "healbell", "thunderwave", "wish"]
},
"sableye": {
"level": 89,
"moves": ["knockoff", "recover", "seismictoss", "shadowball", "toxic"]
},
"mawile": {
"level": 91,
"moves": ["batonpass", "brickbreak", "focuspunch", "hiddenpowersteel", "rockslide", "substitute", "swordsdance", "toxic"]
},
"aggron": {
"level": 84,
"moves": ["doubleedge", "earthquake", "focuspunch", "irontail", "rockslide", "substitute", "thunderwave", "toxic"]
},
"medicham": {
"level": 82,
"moves": ["brickbreak", "bulkup", "recover", "rockslide", "shadowball", "substitute"]
},
"manectric": {
"level": 84,
"moves": ["crunch", "hiddenpowergrass", "hiddenpowerice", "substitute", "thunderbolt", "thunderwave"]
},
"plusle": {
"level": 88,
"moves": ["agility", "batonpass", "encore", "hiddenpowergrass", "substitute", "thunderbolt", "toxic"]
},
"minun": {
"level": 90,
"moves": ["batonpass", "encore", "hiddenpowerice", "lightscreen", "substitute", "thunderbolt", "wish"]
},
"volbeat": {
"level": 91,
"moves": ["batonpass", "icepunch", "tailglow", "thunderbolt"]
},
"illumise": {
"level": 90,
"moves": ["batonpass", "encore", "icepunch", "substitute", "thunderwave", "wish"]
},
"roselia": {
"level": 90,
"moves": ["aromatherapy", "gigadrain", "hiddenpowerfire", "spikes", "stunspore", "synthesis"]
},
"swalot": {
"level": 90,
"moves": ["encore", "explosion", "hiddenpowerground", "icebeam", "sludgebomb", "toxic", "yawn"]
},
"sharpedo": {
"level": 84,
"moves": ["crunch", "earthquake", "endure", "hiddenpowerflying", "hydropump", "icebeam", "return"]
},
"wailord": {
"level": 88,
"moves": ["hiddenpowergrass", "icebeam", "rest", "selfdestruct", "sleeptalk", "surf", "toxic"]
},
"camerupt": {
"level": 84,
"moves": ["earthquake", "explosion", "fireblast", "rest", "rockslide", "sleeptalk", "toxic"]
},
"torkoal": {
"level": 88,
"moves": ["explosion", "fireblast", "flamethrower", "hiddenpowergrass", "rest", "toxic", "yawn"]
},
"grumpig": {
"level": 84,
"moves": ["calmmind", "firepunch", "icywind", "psychic", "substitute", "taunt"]
},
"spinda": {
"level": 91,
"moves": ["bodyslam", "encore", "focuspunch", "shadowball", "substitute", "teeterdance", "toxic"]
},
"flygon": {
"level": 80,
"moves": ["dragonclaw", "earthquake", "fireblast", "hiddenpowerbug", "rockslide", "substitute", "toxic"]
},
"cacturne": {
"level": 89,
"moves": ["focuspunch", "hiddenpowerdark", "leechseed", "needlearm", "spikes", "substitute", "thunderpunch"]
},
"altaria": {
"level": 84,
"moves": ["dragonclaw", "dragondance", "earthquake", "fireblast", "flamethrower", "haze", "hiddenpowerflying", "rest", "toxic"]
},
"zangoose": {
"level": 82,
"moves": ["brickbreak", "quickattack", "return", "shadowball", "swordsdance"]
},
"seviper": {
"level": 90,
"moves": ["crunch", "doubleedge", "earthquake", "flamethrower", "hiddenpowergrass", "sludgebomb"]
},
"lunatone": {
"level": 84,
"moves": ["batonpass", "calmmind", "explosion", "hypnosis", "icebeam", "psychic"]
},
"solrock": {
"level": 84,
"moves": ["earthquake", "explosion", "overheat", "reflect", "rockslide", "shadowball"]
},
"whiscash": {
"level": 87,
"moves": ["earthquake", "hiddenpowerbug", "icebeam", "rest", "rockslide", "sleeptalk", "spark", "surf", "toxic"]
},
"crawdaunt": {
"level": 88,
"moves": ["brickbreak", "crunch", "doubleedge", "hiddenpowerghost", "icebeam", "surf"]
},
"claydol": {
"level": 80,
"moves": ["earthquake", "explosion", "icebeam", "psychic", "rapidspin", "toxic"]
},
"cradily": {
"level": 84,
"moves": ["barrier", "earthquake", "hiddenpowergrass", "mirrorcoat", "recover", "rockslide", "toxic"]
},
"armaldo": {
"level": 82,
"moves": ["doubleedge", "earthquake", "hiddenpowerbug", "rockslide", "swordsdance"]
},
"milotic": {
"level": 79,
"moves": ["icebeam", "mirrorcoat", "recover", "surf", "toxic"]
},
"castform": {
"level": 90,
"moves": ["flamethrower", "icebeam", "substitute", "thunderbolt", "thunderwave"]
},
"kecleon": {
"level": 88,
"moves": ["brickbreak", "return", "shadowball", "thunderwave", "trick"]
},
"banette": {
"level": 84,
"moves": ["destinybond", "endure", "hiddenpowerfighting", "knockoff", "shadowball", "willowisp"]
},
"dusclops": {
"level": 84,
"moves": ["focuspunch", "icebeam", "painsplit", "rest", "shadowball", "sleeptalk", "substitute", "willowisp"]
},
"tropius": {
"level": 90,
"moves": ["hiddenpowerfire", "solarbeam", "sunnyday", "synthesis"]
},
"chimecho": {
"level": 88,
"moves": ["calmmind", "healbell", "hiddenpowerfire", "lightscreen", "psychic", "reflect", "toxic", "yawn"]
},
"absol": {
"level": 85,
"moves": ["batonpass", "hiddenpowerfighting", "quickattack", "shadowball", "swordsdance"]
},
"glalie": {
"level": 87,
"moves": ["earthquake", "explosion", "icebeam", "spikes", "toxic"]
},
"walrein": {
"level": 84,
"moves": ["encore", "hiddenpowergrass", "icebeam", "rest", "sleeptalk", "surf", "toxic"]
},
"huntail": {
"level": 88,
"moves": ["doubleedge", "hiddenpowergrass", "hydropump", "icebeam", "raindance", "surf"]
},
"gorebyss": {
"level": 84,
"moves": ["hiddenpowerelectric", "hiddenpowergrass", "hydropump", "icebeam", "raindance", "surf"]
},
"relicanth": {
"level": 88,
"moves": ["doubleedge", "earthquake", "hiddenpowerflying", "rest", "rockslide", "sleeptalk", "toxic"]
},
"luvdisc": {
"level": 92,
"moves": ["icebeam", "protect", "substitute", "surf", "sweetkiss", "toxic"]
},
"salamence": {
"level": 78,
"moves": ["brickbreak", "dragondance", "earthquake", "fireblast", "hiddenpowerflying", "rockslide"]
},
"metagross": {
"level": 79,
"moves": ["agility", "earthquake", "explosion", "meteormash", "psychic", "rockslide"]
},
"regirock": {
"level": 82,
"moves": ["curse", "earthquake", "explosion", "rest", "rockslide", "superpower", "thunderwave"]
},
"regice": {
"level": 82,
"moves": ["explosion", "icebeam", "rest", "sleeptalk", "thunderbolt", "thunderwave", "toxic"]
},
"registeel": {
"level": 82,
"moves": ["rest", "seismictoss", "sleeptalk", "toxic"]
},
"latias": {
"level": 76,
"moves": ["calmmind", "dragonclaw", "hiddenpowerfire", "recover", "refresh", "toxic"]
},
"latios": {
"level": 75,
"moves": ["calmmind", "dragonclaw", "hiddenpowerfire", "psychic", "recover", "thunderbolt"]
},
"kyogre": {
"level": 73,
"moves": ["calmmind", "icebeam", "rest", "sleeptalk", "surf", "thunder"]
},
"groudon": {
"level": 73,
"moves": ["earthquake", "hiddenpowerbug", "overheat", "rockslide", "substitute", "swordsdance", "thunderwave"]
},
"rayquaza": {
"level": 74,
"moves": ["dragondance", "earthquake", "extremespeed", "hiddenpowerflying", "overheat", "rockslide"]
},
"jirachi": {
"level": 77,
"moves": ["bodyslam", "calmmind", "firepunch", "icepunch", "protect", "psychic", "substitute", "thunderbolt", "wish"]
},
"deoxys": {
"level": 76,
"moves": ["extremespeed", "firepunch", "icebeam", "psychoboost", "shadowball", "superpower"]
},
"deoxysattack": {
"level": 76,
"moves": ["extremespeed", "firepunch", "psychoboost", "shadowball", "superpower"]
},
"deoxysdefense": {
"level": 76,
"moves": ["nightshade", "recover", "spikes", "taunt", "toxic"]
},
"deoxysspeed": {
"level": 76,
"moves": ["calmmind", "icebeam", "psychic", "recover", "spikes", "taunt", "toxic"]
}
}

View File

@ -0,0 +1,695 @@
import RandomGen4Teams from '../gen4/random-teams';
import {Utils} from '../../../lib';
import {PRNG, PRNGSeed} from '../../../sim/prng';
import type {MoveCounter, OldRandomBattleSpecies} from '../gen8/random-teams';
export class RandomGen3Teams extends RandomGen4Teams {
battleHasDitto: boolean;
battleHasWobbuffet: boolean;
randomData: {[species: string]: OldRandomBattleSpecies} = require('./random-data.json');
constructor(format: string | Format, prng: PRNG | PRNGSeed | null) {
super(format, prng);
this.battleHasDitto = false;
this.battleHasWobbuffet = false;
this.moveEnforcementCheckers = {
Bug: (movePool, moves, abilities, types, counter, species) => (
movePool.includes('megahorn') || (!species.types[1] && movePool.includes('hiddenpowerbug'))
),
Electric: (movePool, moves, abilities, types, counter) => !counter.get('Electric'),
Fighting: (movePool, moves, abilities, types, counter) => !counter.get('Fighting'),
Fire: (movePool, moves, abilities, types, counter) => !counter.get('Fire'),
Ground: (movePool, moves, abilities, types, counter) => !counter.get('Ground'),
Normal: (movePool, moves, abilities, types, counter, species) => {
if (species.id === 'blissey' && movePool.includes('softboiled')) return true;
return !counter.get('Normal') && counter.setupType === 'Physical';
},
Psychic: (movePool, moves, abilities, types, counter, species) => (
types.has('Psychic') &&
(movePool.includes('psychic') || movePool.includes('psychoboost')) &&
species.baseStats.spa >= 100
),
Rock: (movePool, moves, abilities, types, counter, species) => !counter.get('Rock') && species.baseStats.atk >= 100,
Water: (movePool, moves, abilities, types, counter, species) => (
!counter.get('Water') && counter.setupType !== 'Physical' && species.baseStats.spa >= 60
),
// If the Pokémon has this move, the other move will be forced
protect: movePool => movePool.includes('wish'),
sunnyday: movePool => movePool.includes('solarbeam'),
sleeptalk: movePool => movePool.includes('rest'),
};
}
shouldCullMove(
move: Move,
types: Set<string>,
moves: Set<string>,
abilities: Set<string>,
counter: MoveCounter,
movePool: string[],
teamDetails: RandomTeamsTypes.TeamDetails,
species: Species,
): {cull: boolean, isSetup?: boolean} {
const restTalk = moves.has('rest') && moves.has('sleeptalk');
switch (move.id) {
// Set up once and only if we have the moves for it
case 'bulkup': case 'curse': case 'dragondance': case 'swordsdance':
return {
cull: (
(counter.setupType !== 'Physical' || counter.get('physicalsetup') > 1) ||
(counter.get('Physical') + counter.get('physicalpool') < 2 && !moves.has('batonpass') && !restTalk)
),
isSetup: true,
};
case 'calmmind':
return {
cull: (
counter.setupType !== 'Special' ||
(counter.get('Special') + counter.get('specialpool') < 2 && !moves.has('batonpass') &&
!moves.has('refresh') && !restTalk)
),
isSetup: true,
};
case 'agility':
return {
cull: (counter.damagingMoves.size < 2 && !moves.has('batonpass')) || moves.has('substitute') || restTalk,
isSetup: !counter.setupType,
};
// Not very useful without their supporting moves
case 'amnesia': case 'sleeptalk':
if (moves.has('roar') || moves.has('whirlwind')) return {cull: true};
if (!moves.has('rest')) return {cull: true};
if (movePool.length > 1) {
const rest = movePool.indexOf('rest');
if (rest >= 0) this.fastPop(movePool, rest);
}
break;
case 'barrier':
return {cull: !moves.has('calmmind') && !moves.has('batonpass') && !moves.has('mirrorcoat')};
case 'batonpass':
return {cull: (
(!counter.setupType && !counter.get('speedsetup')) &&
['meanlook', 'spiderweb', 'substitute', 'wish'].every(m => !moves.has(m))
)};
case 'endeavor': case 'flail': case 'reversal':
return {cull: restTalk || (!moves.has('endure') && !moves.has('substitute'))};
case 'endure':
return {cull: movePool.includes('destinybond')};
case 'extremespeed': case 'raindance': case 'sunnyday':
return {cull: counter.damagingMoves.size < 2 || moves.has('rest')};
case 'focuspunch':
return {cull: (
(counter.damagingMoves.size < 2 || moves.has('rest') || counter.setupType && !moves.has('spore')) ||
(!moves.has('substitute') && (counter.get('Physical') < 4 || moves.has('fakeout'))) ||
// Breloom likes to have coverage
(species.id === 'breloom' && (moves.has('machpunch') || moves.has('skyuppercut')))
)};
case 'moonlight':
return {cull: moves.has('wish') || moves.has('protect')};
case 'perishsong':
return {cull: !moves.has('meanlook') && !moves.has('spiderweb')};
case 'protect':
return {cull: !abilities.has('Speed Boost') && ['perishsong', 'toxic', 'wish'].every(m => !moves.has(m))};
case 'refresh':
return {cull: !counter.setupType};
case 'rest':
return {cull: (
movePool.includes('sleeptalk') ||
(!moves.has('sleeptalk') && (!!counter.get('recovery') || movePool.includes('curse')))
)};
case 'solarbeam':
if (movePool.length > 1) {
const sunnyday = movePool.indexOf('sunnyday');
if (sunnyday >= 0) this.fastPop(movePool, sunnyday);
}
return {cull: !moves.has('sunnyday')};
// Bad after setup
case 'aromatherapy': case 'healbell':
return {cull: moves.has('rest') || !!teamDetails.statusCure};
case 'confuseray':
return {cull: !!counter.setupType || restTalk};
case 'counter': case 'mirrorcoat':
return {cull: !!counter.setupType || ['rest', 'substitute', 'toxic'].some(m => moves.has(m))};
case 'destinybond':
return {cull: !!counter.setupType || moves.has('explosion') || moves.has('selfdestruct')};
case 'doubleedge': case 'facade': case 'fakeout': case 'waterspout':
return {cull: (
(!types.has(move.type) && counter.get('Status') >= 1) ||
(move.id === 'doubleedge' && moves.has('return'))
)};
case 'encore': case 'painsplit': case 'recover': case 'yawn':
return {cull: restTalk};
case 'explosion': case 'machpunch': case 'selfdestruct':
// Snorlax doesn't want to roll selfdestruct as its only STAB move
const snorlaxCase = species.id === 'snorlax' && !moves.has('return') && !moves.has('bodyslam');
return {cull: snorlaxCase || moves.has('rest') || moves.has('substitute') || !!counter.get('recovery')};
case 'haze':
return {cull: !!counter.setupType || moves.has('raindance') || restTalk};
case 'icywind': case 'pursuit': case 'superpower': case 'transform':
return {cull: !!counter.setupType || moves.has('rest')};
case 'leechseed':
return {cull: !!counter.setupType || moves.has('explosion')};
case 'stunspore':
return {cull: moves.has('sunnyday') || moves.has('toxic')};
case 'lightscreen':
return {cull: !!counter.setupType || !!counter.get('speedsetup')};
case 'meanlook': case 'spiderweb':
return {cull: !!counter.get('speedsetup') || (!moves.has('batonpass') && !moves.has('perishsong'))};
case 'morningsun':
return {cull: counter.get('speedsetup') >= 1};
case 'quickattack':
return {cull: (
!!counter.get('speedsetup') ||
moves.has('substitute') ||
(!types.has('Normal') && !!counter.get('Status'))
)};
case 'rapidspin':
return {cull: !!counter.setupType || moves.has('rest') || !!teamDetails.rapidSpin};
case 'reflect':
return {cull: !!counter.setupType || !!counter.get('speedsetup')};
case 'roar': case 'whirlwind':
return {cull: moves.has('sleeptalk') || moves.has('rest')};
case 'seismictoss':
return {cull: !!counter.setupType || moves.has('thunderbolt')};
case 'spikes':
return {cull: !!counter.setupType || moves.has('substitute') || restTalk || !!teamDetails.spikes};
case 'substitute':
const restOrDD = moves.has('rest') || (moves.has('dragondance') && !moves.has('bellydrum'));
// This cull condition otherwise causes mono-solarbeam Entei
return {cull: restOrDD || (species.id !== 'entei' && !moves.has('batonpass') && movePool.includes('calmmind'))};
case 'thunderwave':
return {cull: !!counter.setupType || moves.has('bodyslam') ||
moves.has('substitute') && movePool.includes('toxic') || restTalk};
case 'toxic':
return {cull: (
!!counter.setupType ||
!!counter.get('speedsetup') ||
['endure', 'focuspunch', 'raindance', 'yawn', 'hypnosis'].some(m => moves.has(m))
)};
case 'trick':
return {cull: counter.get('Status') > 1};
case 'willowisp':
return {cull: !!counter.setupType || moves.has('hypnosis') || moves.has('toxic')};
// Bit redundant to have both
case 'bodyslam':
return {cull: moves.has('return') && !!counter.get('Status')};
case 'headbutt':
return {cull: !moves.has('bodyslam') && !moves.has('thunderwave')};
case 'return':
return {cull: (
moves.has('endure') ||
(moves.has('substitute') && moves.has('flail')) ||
(moves.has('bodyslam') && !counter.get('Status'))
)};
case 'fireblast':
return {cull: moves.has('flamethrower') && !!counter.get('Status')};
case 'flamethrower':
return {cull: moves.has('fireblast') && !counter.get('Status')};
case 'overheat':
return {cull: moves.has('flamethrower') || moves.has('substitute')};
case 'hydropump':
return {cull: moves.has('surf') && !!counter.get('Status')};
case 'surf':
return {cull: moves.has('hydropump') && !counter.get('Status')};
case 'gigadrain':
return {cull: moves.has('morningsun') || moves.has('toxic')};
case 'hiddenpower':
const stabCondition = types.has(move.type) && counter.get(move.type) > 1 && (
(moves.has('substitute') && !counter.setupType && !moves.has('toxic')) ||
// This otherwise causes STABless meganium
(species.id !== 'meganium' && moves.has('toxic') && !moves.has('substitute')) ||
restTalk
);
return {cull: stabCondition || (move.type === 'Grass' && moves.has('sunnyday') && moves.has('solarbeam'))};
case 'brickbreak': case 'crosschop': case 'skyuppercut':
return {cull: moves.has('substitute') && (moves.has('focuspunch') || movePool.includes('focuspunch'))};
case 'earthquake':
return {cull: moves.has('bonemerang')};
}
return {cull: false};
}
getItem(
ability: string,
types: Set<string>,
moves: Set<string>,
counter: MoveCounter,
species: Species
) {
// First, the high-priority items
if (species.name === 'Ditto') return this.sample(['Metal Powder', 'Quick Claw']);
if (species.name === 'Farfetch\u2019d') return 'Stick';
if (species.name === 'Marowak') return 'Thick Club';
if (species.name === 'Pikachu') return 'Light Ball';
if (species.name === 'Shedinja') return 'Lum Berry';
if (species.name === 'Unown') return 'Twisted Spoon';
if (moves.has('trick')) return 'Choice Band';
if (moves.has('rest') && !moves.has('sleeptalk') && !['Early Bird', 'Natural Cure', 'Shed Skin'].includes(ability)) {
return 'Chesto Berry';
}
// Medium priority items
if (moves.has('dragondance') && ability !== 'Natural Cure') return 'Lum Berry';
if ((moves.has('bellydrum') && counter.get('Physical') - counter.get('priority') > 1) || (
((moves.has('swordsdance') && counter.get('Status') < 2) || (moves.has('bulkup') && moves.has('substitute'))) &&
!counter.get('priority') &&
species.baseStats.spe >= 60 && species.baseStats.spe <= 95
)) {
return 'Salac Berry';
}
if (moves.has('endure') || (
moves.has('substitute') &&
['bellydrum', 'endeavor', 'flail', 'reversal'].some(m => moves.has(m))
)) {
return (
species.baseStats.spe <= 100 && ability !== 'Speed Boost' && !counter.get('speedsetup') && !moves.has('focuspunch')
) ? 'Salac Berry' : 'Liechi Berry';
}
if (moves.has('substitute') && counter.get('Physical') >= 3 && species.baseStats.spe >= 120) return 'Liechi Berry';
if ((moves.has('substitute') || moves.has('raindance')) && counter.get('Special') >= 3) return 'Petaya Berry';
if (counter.get('Physical') >= 4 && !moves.has('fakeout')) return 'Choice Band';
if (counter.get('Physical') >= 3 && !moves.has('rapidspin') && (
['fireblast', 'icebeam', 'overheat'].some(m => moves.has(m)) ||
Array.from(moves).some(m => {
const moveData = this.dex.moves.get(m);
return moveData.category === 'Special' && types.has(moveData.type);
})
)) {
return 'Choice Band';
}
if (moves.has('psychoboost')) return 'White Herb';
// Default to Leftovers
return 'Leftovers';
}
shouldCullAbility(
ability: string,
types: Set<string>,
moves: Set<string>,
abilities: Set<string>,
counter: MoveCounter,
movePool: string[],
teamDetails: RandomTeamsTypes.TeamDetails,
species: Species,
) {
switch (ability) {
case 'Chlorophyll':
return !moves.has('sunnyday') && !teamDetails['sun'];
case 'Compound Eyes':
return !counter.get('inaccurate');
case 'Hustle':
return counter.get('Physical') < 2;
case 'Lightning Rod':
return species.types.includes('Ground');
case 'Overgrow':
return !counter.get('Grass');
case 'Rock Head':
return !counter.get('recoil');
case 'Sand Veil':
return !teamDetails['sand'];
case 'Serene Grace':
return species.id === 'blissey';
case 'Soundproof': case 'Sturdy':
// Electrode prefers Static, and Sturdy is bad.
return true;
case 'Swift Swim':
return !moves.has('raindance') && !teamDetails['rain'];
case 'Swarm':
return !counter.get('Bug');
case 'Torrent':
return !counter.get('Water');
case 'Water Absorb':
return abilities.has('Swift Swim');
}
return false;
}
randomSet(species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}): RandomTeamsTypes.RandomSet {
species = this.dex.species.get(species);
let forme = species.name;
const data = this.randomData[species.id];
if (typeof species.battleOnly === 'string') forme = species.battleOnly;
const movePool = (data.moves || Object.keys(this.dex.species.getLearnset(species.id)!)).slice();
const rejectedPool = [];
const moves = new Set<string>();
let ability = '';
const evs = {hp: 85, atk: 85, def: 85, spa: 85, spd: 85, spe: 85};
const ivs = {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31};
let availableHP = 0;
for (const setMoveid of movePool) {
if (setMoveid.startsWith('hiddenpower')) availableHP++;
}
const types = new Set(species.types);
const abilities = new Set(Object.values(species.abilities));
let counter: MoveCounter;
// We use a special variable to track Hidden Power
// so that we can check for all Hidden Powers at once
let hasHiddenPower = false;
do {
// Choose next 4 moves from learnset/viable moves and add them to moves list:
while (moves.size < this.maxMoveCount && movePool.length) {
const moveid = this.sampleNoReplace(movePool);
if (moveid.startsWith('hiddenpower')) {
availableHP--;
if (hasHiddenPower) continue;
hasHiddenPower = true;
}
moves.add(moveid);
}
while (moves.size < this.maxMoveCount && rejectedPool.length) {
const moveid = this.sampleNoReplace(rejectedPool);
if (moveid.startsWith('hiddenpower')) {
if (hasHiddenPower) continue;
hasHiddenPower = true;
}
moves.add(moveid);
}
counter = this.queryMoves(moves, species.types, abilities, movePool);
// Iterate through the moves again, this time to cull them:
for (const moveid of moves) {
const move = this.dex.moves.get(moveid);
let {cull, isSetup} = this.shouldCullMove(move, types, moves, abilities, counter, movePool, teamDetails, species);
// This move doesn't satisfy our setup requirements:
if (
(counter.setupType === 'Physical' && move.category === 'Special' && !types.has(move.type) && move.type !== 'Fire') ||
(counter.setupType === 'Special' && move.category === 'Physical' && moveid !== 'superpower')
) {
cull = true;
}
const moveIsRejectable = (
!move.weather &&
(move.category !== 'Status' || !move.flags.heal) &&
(counter.setupType || !move.stallingMove) &&
// These moves cannot be rejected in favor of a forced move
!['batonpass', 'sleeptalk', 'solarbeam', 'substitute', 'sunnyday'].includes(moveid) &&
(move.category === 'Status' || !types.has(move.type) || (move.basePower && move.basePower < 40 && !move.multihit))
);
// Pokemon should usually have at least one STAB move
const requiresStab = (
!counter.get('stab') &&
!moves.has('seismictoss') && !moves.has('nightshade') &&
species.id !== 'castform' && species.id !== 'umbreon' &&
// If a Flying-type has Psychic, it doesn't need STAB
!(moves.has('psychic') && types.has('Flying')) &&
!(types.has('Ghost') && species.baseStats.spa > species.baseStats.atk) &&
!(
// With Calm Mind, Lugia and pure Normal-types are fine without STAB
counter.setupType === 'Special' && (
species.id === 'lugia' ||
(types.has('Normal') && species.types.length < 2)
)
) &&
!(
// With Swords Dance, Dark-types and pure Water-types are fine without STAB
counter.setupType === 'Physical' &&
((types.has('Water') && species.types.length < 2) || types.has('Dark'))
) &&
counter.get('physicalpool') + counter.get('specialpool') > 0
);
const runEnforcementChecker = (checkerName: string) => {
if (!this.moveEnforcementCheckers[checkerName]) return false;
return this.moveEnforcementCheckers[checkerName](
movePool, moves, abilities, types, counter, species as Species, teamDetails
);
};
if (!cull && !isSetup && moveIsRejectable) {
// There may be more important moves that this Pokemon needs
if (
requiresStab ||
(counter.setupType && counter.get(counter.setupType) < 2 && !moves.has('refresh')) ||
(moves.has('substitute') && movePool.includes('morningsun')) ||
['meteormash', 'spore', 'recover'].some(m => movePool.includes(m))
) {
cull = true;
} else {
// Pokemon should have moves that benefit their typing and their other moves
for (const type of types) {
if (runEnforcementChecker(type)) {
cull = true;
}
}
for (const m of moves) {
if (runEnforcementChecker(m)) cull = true;
}
}
}
// Sleep Talk shouldn't be selected without Rest
if (moveid === 'rest' && cull) {
const sleeptalk = movePool.indexOf('sleeptalk');
if (sleeptalk >= 0) {
if (movePool.length < 2) {
cull = false;
} else {
this.fastPop(movePool, sleeptalk);
}
}
}
// Remove rejected moves from the move list
const moveIsHP = moveid.startsWith('hiddenpower');
if (
cull &&
(movePool.length - availableHP || availableHP && (moveIsHP || !hasHiddenPower))
) {
if (move.category !== 'Status' && !move.damage && (!moveIsHP || !availableHP)) {
rejectedPool.push(moveid);
}
if (moveIsHP) hasHiddenPower = false;
moves.delete(moveid);
break;
}
if (cull && rejectedPool.length) {
if (moveIsHP) hasHiddenPower = false;
moves.delete(moveid);
break;
}
}
} while (moves.size < this.maxMoveCount && (movePool.length || rejectedPool.length));
if (hasHiddenPower) {
let hpType;
for (const move of moves) {
if (move.startsWith('hiddenpower')) hpType = move.substr(11);
}
if (!hpType) throw new Error(`hasHiddenPower is true, but no Hidden Power move was found.`);
const HPivs = this.dex.types.get(hpType).HPivs;
let iv: StatID;
for (iv in HPivs) {
ivs[iv] = HPivs[iv]!;
}
}
const abilityData = Array.from(abilities).map(a => this.dex.abilities.get(a)).filter(a => a.gen === 3);
Utils.sortBy(abilityData, abil => -abil.rating);
let ability0 = abilityData[0];
let ability1 = abilityData[1];
if (abilityData[1]) {
if (ability0.rating <= ability1.rating && this.randomChance(1, 2)) {
[ability0, ability1] = [ability1, ability0];
} else if (ability0.rating - 0.6 <= ability1.rating && this.randomChance(2, 3)) {
[ability0, ability1] = [ability1, ability0];
}
ability = ability0.name;
while (this.shouldCullAbility(ability, types, moves, abilities, counter, movePool, teamDetails, species)) {
if (ability === ability0.name && ability1.rating > 1) {
ability = ability1.name;
} else {
// Default to the highest rated ability if all are rejected
ability = abilityData[0].name;
break;
}
}
} else {
ability = abilityData[0].name;
}
const item = this.getItem(ability, types, moves, counter, species);
const level = this.adjustLevel || data.level || (species.nfe ? 90 : 80);
// Prepare optimal HP
let hp = Math.floor(Math.floor(2 * species.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10);
if (moves.has('substitute') && ['endeavor', 'flail', 'reversal'].some(m => moves.has(m))) {
// Endeavor/Flail/Reversal users should be able to use four Substitutes
if (hp % 4 === 0) evs.hp -= 4;
} else if (moves.has('substitute') && (item === 'Salac Berry' || item === 'Petaya Berry' || item === 'Liechi Berry')) {
// Other pinch berry holders should have berries activate after three Substitutes
while (hp % 4 > 0) {
evs.hp -= 4;
hp = Math.floor(Math.floor(2 * species.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10);
}
}
// Minimize confusion damage
if (!counter.get('Physical') && !moves.has('transform')) {
evs.atk = 0;
ivs.atk = hasHiddenPower ? ivs.atk - 28 : 0;
}
return {
name: species.baseSpecies,
species: forme,
gender: species.gender,
moves: Array.from(moves),
ability: ability,
evs: evs,
ivs: ivs,
item: item,
level,
shiny: this.randomChance(1, 1024),
};
}
randomTeam() {
this.enforceNoDirectCustomBanlistChanges();
const seed = this.prng.seed;
const ruleTable = this.dex.formats.getRuleTable(this.format);
const pokemon: RandomTeamsTypes.RandomSet[] = [];
// For Monotype
const isMonotype = !!this.forceMonotype || ruleTable.has('sametypeclause');
const typePool = this.dex.types.names();
const type = this.forceMonotype || this.sample(typePool);
const baseFormes: {[k: string]: number} = {};
const tierCount: {[k: string]: number} = {};
const typeCount: {[k: string]: number} = {};
const typeComboCount: {[k: string]: number} = {};
const typeWeaknesses: {[k: string]: number} = {};
const teamDetails: RandomTeamsTypes.TeamDetails = {};
const pokemonPool = this.getPokemonPool(type, pokemon, isMonotype);
while (pokemonPool.length && pokemon.length < this.maxTeamSize) {
const species = this.dex.species.get(this.sampleNoReplace(pokemonPool));
if (!species.exists || !this.randomData[species.id]?.moves) continue;
// Limit to one of each species (Species Clause)
if (baseFormes[species.baseSpecies]) continue;
// Limit to one Wobbuffet per battle (not just per team)
if (species.name === 'Wobbuffet' && this.battleHasWobbuffet) continue;
// Limit to one Ditto per battle in Gen 2
if (this.dex.gen < 3 && species.name === 'Ditto' && this.battleHasDitto) continue;
const tier = species.tier;
const types = species.types;
const typeCombo = types.slice().sort().join();
if (!isMonotype && !this.forceMonotype) {
// Dynamically scale limits for different team sizes. The default and minimum value is 1.
const limitFactor = Math.round(this.maxTeamSize / 6) || 1;
// Limit two Pokemon per tier
if (tierCount[tier] >= 2 * limitFactor) continue;
// Limit two of any type
let skip = false;
for (const typeName of types) {
if (typeCount[typeName] >= 2 * limitFactor) {
skip = true;
break;
}
}
if (skip) continue;
// Limit three weak to any type
for (const typeName of this.dex.types.names()) {
// it's weak to the type
if (this.dex.getEffectiveness(typeName, species) > 0) {
if (!typeWeaknesses[typeName]) typeWeaknesses[typeName] = 0;
if (typeWeaknesses[typeName] >= 3 * limitFactor) {
skip = true;
break;
}
}
}
if (skip) continue;
// Limit one of any type combination
if (!this.forceMonotype && typeComboCount[typeCombo] >= 1 * limitFactor) continue;
}
// Okay, the set passes, add it to our team
const set = this.randomSet(species, teamDetails);
pokemon.push(set);
// Now that our Pokemon has passed all checks, we can increment our counters
baseFormes[species.baseSpecies] = 1;
// Increment tier counter
if (tierCount[tier]) {
tierCount[tier]++;
} else {
tierCount[tier] = 1;
}
// Increment type counters
for (const typeName of types) {
if (typeName in typeCount) {
typeCount[typeName]++;
} else {
typeCount[typeName] = 1;
}
}
if (typeCombo in typeComboCount) {
typeComboCount[typeCombo]++;
} else {
typeComboCount[typeCombo] = 1;
}
// Increment weakness counter
for (const typeName of this.dex.types.names()) {
// it's weak to the type
if (this.dex.getEffectiveness(typeName, species) > 0) {
typeWeaknesses[typeName]++;
}
}
// Updateeam details
if (set.ability === 'Drizzle' || set.moves.includes('raindance')) teamDetails.rain = 1;
if (set.ability === 'Sand Stream') teamDetails.sand = 1;
if (set.moves.includes('spikes')) teamDetails.spikes = 1;
if (set.moves.includes('rapidspin')) teamDetails.rapidSpin = 1;
if (set.moves.includes('aromatherapy') || set.moves.includes('healbell')) teamDetails.statusCure = 1;
// In Gen 3, Shadow Tag users can prevent each other from switching out, possibly causing and endless battle or at least causing a long stall war
// To prevent this, we prevent more than one Wobbuffet in a single battle.
if (set.ability === 'Shadow Tag') this.battleHasWobbuffet = true;
if (species.id === 'ditto') this.battleHasDitto = true;
}
if (pokemon.length < this.maxTeamSize && !isMonotype && !this.forceMonotype && pokemon.length < 12) {
throw new Error(`Could not build a random team for ${this.format} (seed=${seed})`);
}
return pokemon;
}
}
export default RandomGen3Teams;

View File

@ -1,24 +1,8 @@
export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable = {
export const Rulesets: {[k: string]: ModdedFormatData} = {
standard: {
effectType: 'ValidatorRule',
name: 'Standard',
desc: "The standard ruleset for all official Smogon singles tiers (Ubers, OU, etc.)",
ruleset: [
'Standard AG',
'Sleep Clause Mod', 'Switch Priority Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Items Clause', 'Evasion Moves Clause',
],
},
standarddraft: {
effectType: 'ValidatorRule',
name: 'Standard Draft',
desc: "The custom Draft League ruleset",
ruleset: [
'Obtainable', 'Nickname Clause', 'Beat Up Nicknames Mod', '+Unreleased', 'Sleep Clause Mod', 'OHKO Clause', 'Evasion Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod',
'One Boost Passer Clause', 'Freeze Clause Mod', 'Accuracy Moves Clause', 'Baton Pass Trap Clause',
],
banlist: [
'Uber', 'Smeargle + Ingrain', 'Swagger', 'Focus Band', 'King\'s Rock', 'Quick Claw', 'Baton Pass + Ancient Power', 'Baton Pass + Silver Wind',
],
// timer: {starting: 60 * 60, grace: 0, addPerTurn: 10, maxPerTurn: 100, timeoutAutoChoose: true},
desc: "The standard ruleset for all offical Smogon singles tiers (Ubers, OU, etc.)",
ruleset: ['Obtainable', 'Sleep Clause Mod', 'Switch Priority Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Items Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod'],
},
};

View File

@ -17,9 +17,7 @@ export const Scripts: ModdedBattleScriptsData = {
inherit: true,
getActionSpeed() {
let speed = this.getStat('spe', false, false);
const trickRoomCheck = this.battle.ruleTable.has('twisteddimensionmod') ?
!this.battle.field.getPseudoWeather('trickroom') : this.battle.field.getPseudoWeather('trickroom');
if (trickRoomCheck) {
if (this.battle.field.getPseudoWeather('trickroom')) {
speed = -speed;
}
if (this.battle.quickClawRoll && this.hasItem('quickclaw')) {
@ -49,8 +47,8 @@ export const Scripts: ModdedBattleScriptsData = {
// In Generation 3, the spread move modifier is 0.5x instead of 0.75x. Moves that hit both foes
// and the user's ally, like Earthquake and Explosion, don't get affected by spread modifiers
if (move.spreadHit && move.target === 'allAdjacentFoes') {
const spreadModifier = 0.5;
this.battle.debug(`Spread modifier: ${spreadModifier}`);
const spreadModifier = move.spreadModifier || 0.5;
this.battle.debug('Spread modifier: ' + spreadModifier);
baseDamage = this.battle.modify(baseDamage, spreadModifier);
}
@ -72,17 +70,12 @@ export const Scripts: ModdedBattleScriptsData = {
baseDamage = Math.floor(this.battle.runEvent('ModifyDamagePhase2', pokemon, target, move, baseDamage));
// STAB
// The "???" type never gets STAB
// Not even if you Roost in Gen 4 and somehow manage to use
// Struggle in the same turn.
// (On second thought, it might be easier to get a MissingNo.)
if (type !== '???') {
let stab: number | [number, number] = 1;
if (move.forceSTAB || pokemon.hasType(type)) {
stab = 1.5;
}
stab = this.battle.runEvent('ModifySTAB', pokemon, target, move, stab);
baseDamage = this.battle.modify(baseDamage, stab);
if (move.forceSTAB || type !== '???' && pokemon.hasType(type)) {
// The "???" type never gets STAB
// Not even if you Roost in Gen 4 and somehow manage to use
// Struggle in the same turn.
// (On second thought, it might be easier to get a MissingNo.)
baseDamage = this.battle.modify(baseDamage, move.stab || 1.5);
}
// types
let typeMod = target.runEffectiveness(move);
@ -117,9 +110,7 @@ export const Scripts: ModdedBattleScriptsData = {
return Math.floor(baseDamage);
},
useMoveInner(moveOrMoveName, pokemon, options) {
let sourceEffect = options?.sourceEffect;
let target = options?.target;
useMoveInner(moveOrMoveName, pokemon, target, sourceEffect, zMove) {
if (!sourceEffect && this.battle.effect.id) sourceEffect = this.battle.effect;
if (sourceEffect && sourceEffect.id === 'instruct') sourceEffect = null;
@ -162,8 +153,8 @@ export const Scripts: ModdedBattleScriptsData = {
let movename = move.name;
if (move.id === 'hiddenpower') movename = 'Hidden Power';
if (sourceEffect) attrs += `|[from] ${this.dex.conditions.get(sourceEffect).name}`;
this.battle.addMove('move', pokemon, movename, `${target}${attrs}`);
if (sourceEffect) attrs += `|[from]${this.dex.conditions.get(sourceEffect)}`;
this.battle.addMove('move', pokemon, movename, target + attrs);
if (!target) {
this.battle.attrLastMove('[notarget]');
@ -171,7 +162,7 @@ export const Scripts: ModdedBattleScriptsData = {
return false;
}
const { targets, pressureTargets } = pokemon.getMoveTargets(move, target);
const {targets, pressureTargets} = pokemon.getMoveTargets(move, target);
if (!sourceEffect || sourceEffect.id === 'pursuit') {
let extraPP = 0;
@ -256,7 +247,7 @@ export const Scripts: ModdedBattleScriptsData = {
return false;
}
if (!(move.hasSheerForce && pokemon.hasAbility('sheerforce'))) {
if (!move.negateSecondary && !(move.hasSheerForce && pokemon.hasAbility('sheerforce'))) {
this.battle.singleEvent('AfterMoveSecondarySelf', move, null, pokemon, target, move);
this.battle.runEvent('AfterMoveSecondarySelf', pokemon, target, move);
}
@ -308,7 +299,10 @@ export const Scripts: ModdedBattleScriptsData = {
move.ignoreImmunity = (move.category === 'Status');
}
if (!target.runImmunity(move)) {
if (
(!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) &&
!target.runImmunity(move.type)
) {
naturalImmunity = true;
} else {
hitResult = this.battle.singleEvent('TryImmunity', move, {}, target, pokemon, move);
@ -325,7 +319,7 @@ export const Scripts: ModdedBattleScriptsData = {
let boost: number;
if (accuracy !== true) {
if (!move.ignoreAccuracy) {
boosts = this.battle.runEvent('ModifyBoost', pokemon, null, null, { ...pokemon.boosts });
boosts = this.battle.runEvent('ModifyBoost', pokemon, null, null, {...pokemon.boosts});
boost = this.battle.clampIntRange(boosts['accuracy'], -6, 6);
if (boost > 0) {
accuracy *= boostTable[boost];
@ -334,7 +328,7 @@ export const Scripts: ModdedBattleScriptsData = {
}
}
if (!move.ignoreEvasion) {
boosts = this.battle.runEvent('ModifyBoost', target, null, null, { ...target.boosts });
boosts = this.battle.runEvent('ModifyBoost', target, null, null, {...target.boosts});
boost = this.battle.clampIntRange(boosts['evasion'], -6, 6);
if (boost > 0) {
accuracy /= boostTable[boost];
@ -414,7 +408,7 @@ export const Scripts: ModdedBattleScriptsData = {
accuracy = move.accuracy;
if (accuracy !== true) {
if (!move.ignoreAccuracy) {
boosts = this.battle.runEvent('ModifyBoost', pokemon, null, null, { ...pokemon.boosts });
boosts = this.battle.runEvent('ModifyBoost', pokemon, null, null, {...pokemon.boosts});
boost = this.battle.clampIntRange(boosts['accuracy'], -6, 6);
if (boost > 0) {
accuracy *= boostTable[boost];
@ -423,7 +417,7 @@ export const Scripts: ModdedBattleScriptsData = {
}
}
if (!move.ignoreEvasion) {
boosts = this.battle.runEvent('ModifyBoost', target, null, null, { ...target.boosts });
boosts = this.battle.runEvent('ModifyBoost', target, null, null, {...target.boosts});
boost = this.battle.clampIntRange(boosts['evasion'], -6, 6);
if (boost > 0) {
accuracy /= boostTable[boost];
@ -457,7 +451,7 @@ export const Scripts: ModdedBattleScriptsData = {
}
if (move.recoil && move.totalDamage) {
this.battle.damage(this.calcRecoilDamage(move.totalDamage, move, pokemon), pokemon, target, 'recoil');
this.battle.damage(this.calcRecoilDamage(move.totalDamage, move), pokemon, target, 'recoil');
}
if (target && pokemon !== target) target.gotAttacked(move, damage, pokemon);
@ -468,7 +462,7 @@ export const Scripts: ModdedBattleScriptsData = {
this.battle.eachEvent('Update');
if (target) {
if (target && !move.negateSecondary) {
this.battle.singleEvent('AfterMoveSecondary', move, null, target, pokemon, move);
this.battle.runEvent('AfterMoveSecondary', target, pokemon, move);
}

View File

@ -1,25 +0,0 @@
/**
* Gen 3 colosseum modifications (perish song should fail if it effects all remaining pokemon)
*/
export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
perishsong: {
inherit: true,
onTryMove(attacker, defender, move) {
if (attacker.side.pokemonLeft === 1) {
this.add('-fail', attacker, 'move: Perish Song');
this.hint("Self KO Clause: The last pokemon on a team cannot use moves that force fainting");
return false;
}
},
},
destinybond: {
inherit: true,
onTryMove(attacker, defender, move) {
if (attacker.side.pokemonLeft === 1) {
this.add('-fail', attacker, 'move: Perish Song');
this.hint("Self KO Clause: The last pokemon on a team cannot use moves that force fainting");
return false;
}
},
},
};

View File

@ -1,27 +0,0 @@
export const Scripts: ModdedBattleScriptsData = {
inherit: 'gen3',
gen: 3,
checkWin(faintData?: Battle['faintQueue'][0]) {
if (this.sides.every(side => !side.pokemonLeft)) {
let isSelfKo = false;
if (faintData?.effect) {
isSelfKo = isSelfKo || this.dex.moves.getByID(faintData?.effect?.id).selfdestruct !== undefined;
isSelfKo = isSelfKo || this.dex.moves.getByID(faintData?.effect?.id).recoil !== undefined;
}
if (isSelfKo) {
this.win(faintData ? faintData.target.side : null);
return true;
} else {
this.win(undefined);
return true;
}
}
for (const side of this.sides) {
if (!side.foePokemonLeft()) {
this.win(side);
return true;
}
}
},
};

File diff suppressed because it is too large Load Diff

View File

@ -1,58 +0,0 @@
export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
apicotberry: {
inherit: true,
isNonstandard: "Unobtainable",
},
berryjuice: {
inherit: true,
isNonstandard: "Unobtainable",
},
fastball: {
inherit: true,
isNonstandard: "Unobtainable",
},
ganlonberry: {
inherit: true,
isNonstandard: "Unobtainable",
},
heavyball: {
inherit: true,
isNonstandard: "Unobtainable",
},
lansatberry: {
inherit: true,
isNonstandard: "Unobtainable",
},
levelball: {
inherit: true,
isNonstandard: "Unobtainable",
},
loveball: {
inherit: true,
isNonstandard: "Unobtainable",
},
lureball: {
inherit: true,
isNonstandard: "Unobtainable",
},
moonball: {
inherit: true,
isNonstandard: "Unobtainable",
},
petayaberry: {
inherit: true,
isNonstandard: "Unobtainable",
},
salacberry: {
inherit: true,
isNonstandard: "Unobtainable",
},
sportball: {
inherit: true,
isNonstandard: "Unobtainable",
},
starfberry: {
inherit: true,
isNonstandard: "Unobtainable",
},
};

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +0,0 @@
export const Scripts: ModdedBattleScriptsData = {
inherit: 'gen3',
};

View File

@ -1,17 +1,15 @@
export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTable = {
export const Abilities: {[k: string]: ModdedAbilityData} = {
airlock: {
inherit: true,
onSwitchIn: undefined, // no inherit
onStart(pokemon) {
pokemon.abilityState.ending = false;
},
onSwitchIn() {},
onStart() {},
},
angerpoint: {
inherit: true,
onAfterSubDamage(damage, target, source, move) {
if (!target.hp) return;
if (move && move.effectType === 'Move' && target.getMoveHitData(move).crit) {
target.setBoost({ atk: 6 });
target.setBoost({atk: 6});
this.add('-setboost', target, 'atk', 12, '[from] ability: Anger Point');
}
},
@ -23,9 +21,6 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
onResidualSubOrder: 10,
},
blaze: {
inherit: true,
onModifyAtk: undefined, // no inherit
onModifySpA: undefined, // no inherit
onBasePowerPriority: 2,
onBasePower(basePower, attacker, defender, move) {
if (move.type === 'Fire' && attacker.hp <= attacker.maxhp / 3) {
@ -33,13 +28,14 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
return this.chainModify(1.5);
}
},
name: "Blaze",
rating: 2,
num: 66,
},
cloudnine: {
inherit: true,
onSwitchIn: undefined, // no inherit
onStart(pokemon) {
pokemon.abilityState.ending = false;
},
onSwitchIn() {},
onStart() {},
},
colorchange: {
inherit: true,
@ -51,14 +47,14 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
this.add('-start', target, 'typechange', type, '[from] ability: Color Change');
}
},
onAfterMoveSecondary: undefined, // no inherit
onAfterMoveSecondary() {},
},
compoundeyes: {
onSourceModifyAccuracyPriority: 9,
onSourceModifyAccuracy(accuracy) {
if (typeof accuracy !== 'number') return;
this.debug('compoundeyes - enhancing accuracy');
return this.chainModify(1.3);
return accuracy * 1.3;
},
inherit: true,
},
@ -72,23 +68,6 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
}
},
},
download: {
inherit: true,
onStart(pokemon) {
let totaldef = 0;
let totalspd = 0;
for (const target of pokemon.foes()) {
if (target.volatiles.substitute) continue;
totaldef += target.getStat('def', false, true);
totalspd += target.getStat('spd', false, true);
}
if (totaldef && totaldef >= totalspd) {
this.boost({ spa: 1 });
} else if (totalspd) {
this.boost({ atk: 1 });
}
},
},
effectspore: {
inherit: true,
onDamagingHit(damage, target, source, move) {
@ -128,15 +107,19 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
}
},
condition: {
inherit: true,
onModifyAtk: undefined, // no inherit
onModifySpA: undefined, // no inherit
noCopy: true, // doesn't get copied by Baton Pass
onStart(target) {
this.add('-start', target, 'ability: Flash Fire');
},
onModifyDamagePhase1(atk, attacker, defender, move) {
if (move.type === 'Fire') {
this.debug('Flash Fire boost');
return this.chainModify(1.5);
}
},
onEnd(target) {
this.add('-end', target, 'ability: Flash Fire', '[silent]');
},
},
},
flowergift: {
@ -151,11 +134,6 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
return this.chainModify(1.5);
}
},
flags: { breakable: 1 },
},
forecast: {
inherit: true,
flags: { notrace: 1 },
},
forewarn: {
inherit: true,
@ -185,9 +163,10 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
frisk: {
inherit: true,
onStart(pokemon) {
const target = pokemon.side.randomFoe();
if (target?.item) {
this.add('-item', '', target.getItem().name, '[from] ability: Frisk', `[of] ${pokemon}`);
for (const target of pokemon.foes()) {
if (target.item && !target.itemState.knockedOff) {
this.add('-item', target, target.getItem().name, '[from] ability: Frisk', '[of] ' + pokemon, '[identify]');
}
}
},
},
@ -196,19 +175,20 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
onSourceModifyAccuracyPriority: 7,
onSourceModifyAccuracy(accuracy, target, source, move) {
if (move.category === 'Physical' && typeof accuracy === 'number') {
return this.chainModify(0.8);
return accuracy * 0.8;
}
},
},
hydration: {
inherit: true,
onResidual: undefined, // no inherit
onWeather(target, source, effect) {
if (effect.id === 'raindance' && target.status) {
this.add('-activate', target, 'ability: Hydration');
target.cureStatus();
}
},
name: "Hydration",
rating: 1.5,
num: 93,
},
insomnia: {
inherit: true,
@ -233,7 +213,7 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
} else if (target.volatiles['substitutebroken']?.move === 'uturn') {
this.hint("In Gen 4, if U-turn breaks Substitute the incoming Intimidate does nothing.");
} else {
this.boost({ atk: -1 }, target, pokemon, null, true);
this.boost({atk: -1}, target, pokemon, null, true);
}
}
},
@ -242,7 +222,7 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
inherit: true,
onSetStatus(status, target, source, effect) {
if (effect && effect.id === 'rest') {
// do nothing
return;
} else if (this.field.isWeather('sunnyday')) {
return false;
}
@ -250,13 +230,13 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
},
lightningrod: {
inherit: true,
onTryHit: undefined, // no inherit
onTryHit() {},
rating: 0,
},
liquidooze: {
inherit: true,
onSourceTryHeal(damage, target, source, effect) {
this.debug(`Heal is occurring: ${target} <- ${source} :: ${effect.id}`);
this.debug("Heal is occurring: " + target + " <- " + source + " :: " + effect.id);
const canOoze = ['drain', 'leechseed'];
if (canOoze.includes(effect.id) && this.activeMove?.id !== 'dreameater') {
this.damage(damage, null, null, null, true);
@ -265,28 +245,35 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
},
},
magicguard: {
inherit: true,
onDamage(damage, target, source, effect) {
if (effect.effectType !== 'Move') {
return false;
}
},
onSetStatus(status, target, source, effect) {
if (effect && effect.id === 'toxicspikes') {
return false;
}
},
name: "Magic Guard",
rating: 4.5,
num: 98,
},
minus: {
inherit: true,
onModifySpAPriority: undefined, // no inherit
onModifySpA(spa, pokemon) {
for (const allyActive of pokemon.allies()) {
if (allyActive.hasAbility('plus')) {
return this.chainModify(1.5);
for (const ally of pokemon.allies()) {
if (ally.ability === 'plus') {
return spa * 1.5;
}
}
},
name: "Minus",
rating: 0,
num: 58,
},
naturalcure: {
inherit: true,
onCheckShow: undefined, // no inherit
onCheckShow(pokemon) {},
onSwitchOut(pokemon) {
if (!pokemon.status || pokemon.status === 'fnt') return;
@ -306,9 +293,6 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
},
},
overgrow: {
inherit: true,
onModifyAtk: undefined, // no inherit
onModifySpA: undefined, // no inherit
onBasePowerPriority: 2,
onBasePower(basePower, attacker, defender, move) {
if (move.type === 'Grass' && attacker.hp <= attacker.maxhp / 3) {
@ -316,23 +300,26 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
return this.chainModify(1.5);
}
},
name: "Overgrow",
rating: 2,
num: 65,
},
pickup: {
inherit: true,
onResidual: undefined, // no inherit
name: "Pickup",
rating: 0,
// No competitive use
num: 53,
},
plus: {
inherit: true,
onModifySpAPriority: undefined, // no inherit
onModifySpA(spa, pokemon) {
for (const allyActive of pokemon.allies()) {
if (allyActive.hasAbility('minus')) {
return this.chainModify(1.5);
for (const ally of pokemon.allies()) {
if (ally.ability === 'minus') {
return spa * 1.5;
}
}
},
name: "Plus",
rating: 0,
num: 57,
},
poisonpoint: {
inherit: true,
@ -345,12 +332,16 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
},
},
pressure: {
inherit: true,
onStart(pokemon) {
this.add('-ability', pokemon, 'Pressure');
},
onDeductPP(target, source) {
if (target === source) return;
return 1;
},
name: "Pressure",
rating: 1.5,
num: 46,
},
roughskin: {
inherit: true,
@ -367,14 +358,14 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
if (typeof accuracy !== 'number') return;
if (this.field.isWeather('sandstorm')) {
this.debug('Sand Veil - decreasing accuracy');
return this.chainModify(0.8);
return accuracy * 0.8;
}
},
},
serenegrace: {
inherit: true,
onModifyMove(move) {
if (move.secondaries && move.id !== 'chatter') {
if (move.secondaries) {
this.debug('doubling secondary chance');
for (const secondary of move.secondaries) {
if (secondary.chance) secondary.chance *= 2;
@ -388,14 +379,16 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
onResidualSubOrder: 3,
},
simple: {
inherit: true,
onChangeBoost: undefined, // no inherit
onModifyBoost(boosts) {
let key: BoostID;
for (key in boosts) {
boosts[key]! *= 2;
}
},
isBreakable: true,
name: "Simple",
rating: 4,
num: 86,
},
snowcloak: {
inherit: true,
@ -404,7 +397,7 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
if (typeof accuracy !== 'number') return;
if (this.field.isWeather('hail')) {
this.debug('Snow Cloak - decreasing accuracy');
return this.chainModify(0.8);
return accuracy * 0.8;
}
},
},
@ -424,10 +417,9 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
},
},
stench: {
inherit: true,
onModifyMove: undefined, // no inherit
name: "Stench",
rating: 0,
// No competitive use
num: 1,
},
stickyhold: {
inherit: true,
@ -440,18 +432,15 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
},
stormdrain: {
inherit: true,
onTryHit: undefined, // no inherit
onTryHit() {},
rating: 0,
},
sturdy: {
inherit: true,
onDamage: undefined, // no inherit
onDamage() {},
rating: 0,
},
swarm: {
inherit: true,
onModifyAtk: undefined, // no inherit
onModifySpA: undefined, // no inherit
onBasePowerPriority: 2,
onBasePower(basePower, attacker, defender, move) {
if (move.type === 'Bug' && attacker.hp <= attacker.maxhp / 3) {
@ -459,6 +448,9 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
return this.chainModify(1.5);
}
},
name: "Swarm",
rating: 2,
num: 68,
},
synchronize: {
inherit: true,
@ -478,25 +470,23 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
if (typeof accuracy !== 'number') return;
if (target?.volatiles['confusion']) {
this.debug('Tangled Feet - decreasing accuracy');
return this.chainModify(0.5);
return accuracy * 0.5;
}
},
},
thickfat: {
inherit: true,
onSourceModifyAtk: undefined, // no inherit
onSourceModifySpA: undefined, // no inherit
onSourceBasePowerPriority: 1,
onSourceBasePower(basePower, attacker, defender, move) {
if (move.type === 'Ice' || move.type === 'Fire') {
return this.chainModify(0.5);
}
},
isBreakable: true,
name: "Thick Fat",
rating: 3.5,
num: 47,
},
torrent: {
inherit: true,
onModifyAtk: undefined, // no inherit
onModifySpA: undefined, // no inherit
onBasePowerPriority: 2,
onBasePower(basePower, attacker, defender, move) {
if (move.type === 'Water' && attacker.hp <= attacker.maxhp / 3) {
@ -504,11 +494,14 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
return this.chainModify(1.5);
}
},
name: "Torrent",
rating: 2,
num: 67,
},
trace: {
inherit: true,
onUpdate(pokemon) {
if (!this.effectState.seek) return;
if (!pokemon.isStarted) return;
const target = pokemon.side.randomFoe();
if (!target || target.fainted) return;
const ability = target.getAbility();
@ -516,9 +509,20 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
if (bannedAbilities.includes(target.ability)) {
return;
}
pokemon.setAbility(ability, target);
if (pokemon.setAbility(ability)) {
this.add('-ability', pokemon, ability, '[from] ability: Trace', '[of] ' + target);
}
},
},
unburden: {
inherit: true,
condition: {
onModifySpe(spe, pokemon) {
if ((!pokemon.item || pokemon.itemState.knockedOff) && !pokemon.ignoringAbility()) {
return this.chainModify(2);
}
},
},
flags: { notrace: 1 },
},
vitalspirit: {
inherit: true,
@ -528,19 +532,15 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
inherit: true,
onTryHit(target, source, move) {
if (move.id === 'firefang') {
this.hint("In Gen 4, Fire Fang is always able to hit through Wonder Guard.", true, target.side);
this.hint("In Gen 4, Fire Fang is always able to hit through Wonder Guard.");
return;
}
if (target === source || move.category === 'Status' || move.type === '???') return;
if (target === source || move.category === 'Status' || move.type === '???' || move.id === 'struggle') return;
this.debug('Wonder Guard immunity: ' + move.id);
if (target.runEffectiveness(move) <= 0 || !target.runImmunity(move)) {
if (target.runEffectiveness(move) <= 0) {
this.add('-immune', target, '[from] ability: Wonder Guard');
return null;
}
},
},
rebound: {
inherit: true,
onTryHitSide: undefined, // no inherit
},
};

View File

@ -1,4 +1,4 @@
export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDataTable = {
export const Conditions: {[k: string]: ModdedConditionData} = {
brn: {
inherit: true,
onResidualOrder: 10,
@ -24,7 +24,7 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
effectType: 'Status',
onStart(target, source, sourceEffect) {
if (sourceEffect && sourceEffect.effectType === 'Move') {
this.add('-status', target, 'slp', `[from] move: ${sourceEffect.name}`);
this.add('-status', target, 'slp', '[from] move: ' + sourceEffect.name);
} else {
this.add('-status', target, 'slp');
}
@ -116,10 +116,6 @@ export const Conditions: import('../../../sim/dex-conditions').ModdedConditionDa
onResidualOrder: 10,
onResidualSubOrder: 9,
},
lockedmove: {
inherit: true,
onAfterMove: undefined, // no inherit
},
choicelock: {
inherit: true,
onStart(pokemon) {

View File

@ -1,4 +1,4 @@
export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormatsDataTable = {
export const FormatsData: {[k: string]: ModdedSpeciesFormatsData} = {
bulbasaur: {
tier: "LC",
},
@ -222,7 +222,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
machamp: {
tier: "Uber",
tier: "OU",
},
bellsprout: {
tier: "LC",
@ -736,7 +736,7 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
tier: "NU",
},
weavile: {
tier: "(OU)",
tier: "OU",
},
teddiursa: {
tier: "LC",
@ -1137,12 +1137,9 @@ export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormat
castform: {
tier: "NU",
},
castformsunny: {
},
castformrainy: {
},
castformsnowy: {
},
castformsunny: {},
castformrainy: {},
castformsnowy: {},
kecleon: {
tier: "NU",
},

View File

@ -1,4 +1,4 @@
export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
export const Items: {[k: string]: ModdedItemData} = {
adamantorb: {
inherit: true,
onBasePower(basePower, user, target, move) {
@ -32,24 +32,24 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
choiceband: {
inherit: true,
onStart: undefined, // no inherit
onModifyMove: undefined, // no inherit
onStart() {},
onModifyMove() {},
onAfterMove(pokemon) {
pokemon.addVolatile('choicelock');
},
},
choicescarf: {
inherit: true,
onStart: undefined, // no inherit
onModifyMove: undefined, // no inherit
onStart() {},
onModifyMove() {},
onAfterMove(pokemon) {
pokemon.addVolatile('choicelock');
},
},
choicespecs: {
inherit: true,
onStart: undefined, // no inherit
onModifyMove: undefined, // no inherit
onStart() {},
onModifyMove() {},
onAfterMove(pokemon) {
pokemon.addVolatile('choicelock');
},
@ -72,7 +72,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
custapberry: {
inherit: true,
onFractionalPriority: undefined, // no inherit
onFractionalPriority() {},
onBeforeTurn(pokemon) {
if (pokemon.hp <= pokemon.maxhp / 4 || (pokemon.hp <= pokemon.maxhp / 2 && pokemon.ability === 'gluttony')) {
const action = this.queue.willMove(pokemon);
@ -89,7 +89,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
onCustap(pokemon) {
const action = this.queue.willMove(pokemon);
this.debug(`custap action: ${action?.moveid}`);
this.debug('custap action: ' + action);
if (action && pokemon.eatItem()) {
this.queue.cancelAction(pokemon);
this.add('-message', "Custap Berry activated.");
@ -113,38 +113,18 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
}
},
},
dracoplate: {
inherit: true,
onTakeItem: true,
},
dreadplate: {
inherit: true,
onTakeItem: true,
},
earthplate: {
inherit: true,
onTakeItem: true,
},
fastball: {
inherit: true,
isNonstandard: null,
},
fistplate: {
inherit: true,
onTakeItem: true,
},
flameorb: {
inherit: true,
onResidualOrder: 10,
onResidualSubOrder: 20,
},
flameplate: {
inherit: true,
onTakeItem: true,
},
focussash: {
inherit: true,
onDamage: undefined, // no inherit
onDamage() { },
onTryHit(target, source, move) {
if (target !== source && target.hp === target.maxhp) {
target.addVolatile('focussash');
@ -176,21 +156,9 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
inherit: true,
isNonstandard: null,
},
icicleplate: {
inherit: true,
onTakeItem: true,
},
insectplate: {
inherit: true,
onTakeItem: true,
},
ironball: {
inherit: true,
onEffectiveness: undefined, // no inherit
},
ironplate: {
inherit: true,
onTakeItem: true,
onEffectiveness() {},
},
kingsrock: {
inherit: true,
@ -227,8 +195,8 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
lifeorb: {
inherit: true,
onModifyDamage: undefined, // no inherit
onAfterMoveSecondarySelf: undefined, // no inherit
onModifyDamage() {},
onAfterMoveSecondarySelf() {},
onBasePower(basePower, user, target) {
if (!target.volatiles['substitute']) {
user.addVolatile('lifeorb');
@ -241,7 +209,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
condition: {
duration: 1,
onAfterMoveSecondarySelf(source, target, move) {
if (move && move.effectType === 'Move' && source?.volatiles['lifeorb']) {
if (move && move.effectType === 'Move' && source && source.volatiles['lifeorb']) {
this.damage(source.baseMaxhp / 10, source, source, this.dex.items.get('lifeorb'));
source.removeVolatile('lifeorb');
}
@ -250,8 +218,8 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
lightball: {
inherit: true,
onModifyAtk: undefined, // no inherit
onModifySpA: undefined, // no inherit
onModifyAtk() {},
onModifySpA() {},
onBasePower(basePower, pokemon) {
if (pokemon.species.name === 'Pikachu') {
return this.chainModify(2);
@ -282,10 +250,6 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
}
},
},
meadowplate: {
inherit: true,
onTakeItem: true,
},
mentalherb: {
inherit: true,
fling: {
@ -306,7 +270,11 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
metronome: {
inherit: true,
condition: {
inherit: true,
onStart(pokemon) {
this.effectState.numConsecutive = 0;
this.effectState.lastMove = '';
},
onTryMovePriority: -2,
onTryMove(pokemon, target, move) {
if (!pokemon.hasItem('metronome')) {
pokemon.removeVolatile('metronome');
@ -319,7 +287,6 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
}
this.effectState.lastMove = move.id;
},
onModifyDamage: undefined, // no inherit
onModifyDamagePhase2(damage, source, target, move) {
return damage * (1 + (this.effectState.numConsecutive / 10));
},
@ -328,7 +295,7 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
micleberry: {
inherit: true,
condition: {
inherit: true,
duration: 2,
onSourceModifyAccuracyPriority: 3,
onSourceModifyAccuracy(accuracy, target, source) {
this.add('-enditem', source, 'Micle Berry');
@ -339,10 +306,6 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
},
},
},
mindplate: {
inherit: true,
onTakeItem: true,
},
moonball: {
inherit: true,
isNonstandard: null,
@ -362,18 +325,6 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
}
},
},
skyplate: {
inherit: true,
onTakeItem: true,
},
splashplate: {
inherit: true,
onTakeItem: true,
},
spookyplate: {
inherit: true,
onTakeItem: true,
},
sportball: {
inherit: true,
isNonstandard: null,
@ -391,10 +342,6 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
onResidualOrder: 10,
onResidualSubOrder: 20,
},
stoneplate: {
inherit: true,
onTakeItem: true,
},
thickclub: {
inherit: true,
onModifyAtk(atk, pokemon) {
@ -408,10 +355,6 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
onResidualOrder: 10,
onResidualSubOrder: 20,
},
toxicplate: {
inherit: true,
onTakeItem: true,
},
widelens: {
inherit: true,
onSourceModifyAccuracyPriority: 4,
@ -421,10 +364,6 @@ export const Items: import('../../../sim/dex-items').ModdedItemDataTable = {
}
},
},
zapplate: {
inherit: true,
onTakeItem: true,
},
zoomlens: {
inherit: true,
onSourceModifyAccuracyPriority: 4,

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
export const Pokedex: import('../../../sim/dex-species').ModdedSpeciesDataTable = {
export const Pokedex: {[k: string]: ModdedSpeciesData} = {
milotic: {
inherit: true,
evoType: 'levelExtra',

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,873 @@
import RandomGen5Teams from '../gen5/random-teams';
import {Utils} from '../../../lib';
import {toID} from '../../../sim/dex';
import {PRNG} from '../../../sim';
import type {MoveCounter, OldRandomBattleSpecies} from '../gen8/random-teams';
// These moves can be used even if we aren't setting up to use them:
const SetupException = ['dracometeor', 'overheat'];
// Give recovery moves priority over certain other defensive status moves
const recoveryMoves = [
'healorder', 'milkdrink', 'moonlight', 'morningsun', 'painsplit', 'recover', 'rest', 'roost',
'slackoff', 'softboiled', 'synthesis', 'wish',
];
const defensiveStatusMoves = ['aromatherapy', 'haze', 'healbell', 'roar', 'whirlwind', 'willowisp', 'yawn'];
export class RandomGen4Teams extends RandomGen5Teams {
randomData: {[species: string]: OldRandomBattleSpecies} = require('./random-data.json');
constructor(format: string | Format, prng: PRNG | PRNGSeed | null) {
super(format, prng);
this.moveEnforcementCheckers = {
Bug: (movePool, moves, abilities, types, counter) => (
(movePool.includes('bugbuzz') || movePool.includes('megahorn'))
),
Dark: (movePool, moves, abilities, types, counter) => (
!counter.get('damage') &&
(!counter.get('Dark') || (counter.get('Dark') < 2 && moves.has('pursuit') && movePool.includes('suckerpunch')))),
Dragon: (movePool, moves, abilities, types, counter) => !counter.get('Dragon'),
Electric: (movePool, moves, abilities, types, counter) => !counter.get('Electric'),
Fighting: (movePool, moves, abilities, types, counter) => (
!counter.get('Fighting') &&
(!!counter.setupType || !counter.get('Status') || movePool.includes('closecombat') || movePool.includes('highjumpkick'))
),
Fire: (movePool, moves, abilities, types, counter) => !counter.get('Fire'),
Flying: (movePool, moves, abilities, types, counter) => !counter.get('Flying') && (
(counter.setupType !== 'Special' && movePool.includes('bravebird')) ||
(abilities.has('Serene Grace') && movePool.includes('airslash'))
),
Grass: (movePool, moves, abilities, types, counter) => (
!counter.get('Grass') &&
['leafblade', 'leafstorm', 'seedflare', 'woodhammer'].some(m => movePool.includes(m))
),
Ground: (movePool, moves, abilities, types, counter) => !counter.get('Ground'),
Ice: (movePool, moves, abilities, types, counter) => (
!counter.get('Ice') && (!types.has('Water') || !counter.get('Water'))
),
Rock: (movePool, moves, abilities, types, counter) => (
!counter.get('Rock') && (movePool.includes('headsmash') || movePool.includes('stoneedge'))
),
Steel: (movePool, moves, abilities, types, counter) => !counter.get('Steel') && movePool.includes('meteormash'),
Water: (movePool, moves, abilities, types, counter) => (
!counter.get('Water') && (moves.has('raindance') || !types.has('Ice') || !counter.get('Ice'))
),
Adaptability: (movePool, moves, abilities, types, counter, species) => (
!counter.setupType &&
species.types.length > 1 &&
(!counter.get(species.types[0]) || !counter.get(species.types[1]))
),
Guts: (movePool, moves, abilities, types) => types.has('Normal') && movePool.includes('facade'),
'Slow Start': movePool => movePool.includes('substitute'),
protect: movePool => movePool.includes('wish'),
wish: movePool => movePool.includes('protect'),
};
}
shouldCullMove(
move: Move,
types: Set<string>,
moves: Set<string>,
abilities: Set<string>,
counter: MoveCounter,
movePool: string[],
teamDetails: RandomTeamsTypes.TeamDetails,
species: Species,
isLead: boolean,
): {cull: boolean, isSetup?: boolean} {
const restTalk = moves.has('rest') && moves.has('sleeptalk');
switch (move.id) {
// Not very useful without their supporting moves
case 'batonpass':
return {cull: !counter.setupType && !counter.get('speedsetup') && !moves.has('substitute')};
case 'eruption': case 'waterspout':
return {cull: counter.get('Physical') + counter.get('Special') < 4};
case 'focuspunch':
return {cull: !moves.has('substitute') || counter.damagingMoves.size < 2 || moves.has('hammerarm')};
case 'lightscreen':
if (movePool.length > 1) {
const screen = movePool.indexOf('reflect');
if (screen >= 0) {
this.fastPop(movePool, screen);
return {cull: true};
}
}
return {cull: false};
case 'raindance':
return {cull: abilities.has('Hydration') ? !moves.has('rest') : counter.get('Physical') + counter.get('Special') < 2};
case 'reflect':
if (movePool.length > 1) {
const screen = movePool.indexOf('lightscreen');
if (screen >= 0) {
this.fastPop(movePool, screen);
return {cull: true};
}
}
return {cull: false};
case 'refresh':
return {cull: !(moves.has('calmmind') && (moves.has('recover') || moves.has('roost')))};
case 'rest':
return {cull: movePool.includes('sleeptalk') || (abilities.has('Hydration') && !moves.has('raindance')) ||
moves.has('reflect') && moves.has('lightscreen')};
case 'sleeptalk':
if (movePool.length > 1) {
const rest = movePool.indexOf('rest');
if (rest >= 0) this.fastPop(movePool, rest);
}
return {cull: !moves.has('rest')};
case 'sunnyday':
return {cull: !moves.has('solarbeam')};
case 'weatherball':
return {cull: !moves.has('raindance') && !moves.has('sunnyday')};
// Set up once and only if we have the moves for it
case 'bellydrum': case 'bulkup': case 'curse': case 'dragondance': case 'swordsdance':
const notEnoughPhysicalMoves = (
counter.get('Physical') + counter.get('physicalpool') < 2 &&
!moves.has('batonpass') &&
(!moves.has('rest') || !moves.has('sleeptalk'))
);
const badPhysicalMoveset = counter.setupType !== 'Physical' || counter.get('physicalsetup') > 1;
return {cull: moves.has('sunnyday') || notEnoughPhysicalMoves || badPhysicalMoveset, isSetup: true};
case 'calmmind': case 'nastyplot': case 'tailglow':
const notEnoughSpecialMoves = (
counter.get('Special') + counter.get('specialpool') < 2 &&
!moves.has('batonpass') &&
(!moves.has('rest') || !moves.has('sleeptalk'))
);
const badSpecialMoveset = counter.setupType !== 'Special' || counter.get('specialsetup') > 1;
return {cull: notEnoughSpecialMoves || badSpecialMoveset, isSetup: true};
case 'agility': case 'rockpolish':
return {cull: restTalk || (counter.damagingMoves.size < 2 && !moves.has('batonpass')), isSetup: !counter.setupType};
// Bad after setup
case 'destinybond':
return {cull: !!counter.setupType || moves.has('explosion')};
case 'explosion': case 'selfdestruct':
return {cull: (
counter.setupType === 'Special' ||
Array.from(moves).some(id => recoveryMoves.includes(id) || defensiveStatusMoves.includes(id)) ||
['batonpass', 'protect', 'substitute'].some(m => moves.has(m))
)};
case 'foresight': case 'roar': case 'whirlwind':
return {cull: !!counter.setupType && !abilities.has('Speed Boost')};
case 'healingwish': case 'lunardance':
return {cull: !!counter.setupType || moves.has('rest') || moves.has('substitute')};
case 'protect':
return {cull: (
['rest', 'softboiled'].some(m => moves.has(m)) ||
!['Guts', 'Quick Feet', 'Speed Boost'].some(abil => abilities.has(abil)) &&
!['toxic', 'wish'].some(m => moves.has(m))
)};
case 'wish':
return {cull: (
!['batonpass', 'ironhead', 'moonlight', 'protect', 'softboiled', 'uturn'].some(m => moves.has(m)) &&
!movePool.includes('protect')
)};
case 'moonlight':
return {cull: (moves.has('wish') && (moves.has('protect') || movePool.includes('protect')))};
case 'rapidspin':
return {cull: !!teamDetails.rapidSpin || (!!counter.setupType && counter.get('Physical') + counter.get('Special') < 2)};
case 'fakeout':
return {cull: !!counter.setupType || !!counter.get('speedsetup') || moves.has('substitute')};
case 'spikes':
return {cull: !!counter.setupType || !!counter.get('speedsetup') || moves.has('substitute')};
case 'stealthrock':
return {cull: (
!!counter.setupType ||
!!counter.get('speedsetup') ||
moves.has('rest') || moves.has('substitute') ||
!!teamDetails.stealthRock
)};
case 'switcheroo': case 'trick':
return {cull: (
counter.get('Physical') + counter.get('Special') < 3 ||
!!counter.setupType ||
['fakeout', 'lightscreen', 'reflect', 'suckerpunch', 'trickroom'].some(m => moves.has(m))
)};
case 'toxic': case 'toxicspikes':
return {cull: (
!!counter.setupType || !!counter.get('speedsetup') || !!teamDetails.toxicSpikes || moves.has('willowisp')
)};
case 'trickroom':
return {cull: (
!!counter.setupType ||
!!counter.get('speedsetup') ||
counter.damagingMoves.size < 2 ||
moves.has('lightscreen') || moves.has('reflect') ||
restTalk
)};
case 'uturn':
return {cull: (
(abilities.has('Speed Boost') && moves.has('protect')) ||
!!counter.setupType ||
!!counter.get('speedsetup') ||
moves.has('batonpass') || moves.has('substitute')
)};
// Bit redundant to have both
// Attacks:
case 'bodyslam': case 'slash':
return {cull: moves.has('facade') || moves.has('return')};
case 'bugbite':
return {cull: moves.has('uturn')};
case 'doubleedge':
return {cull: ['bodyslam', 'facade', 'return'].some(m => moves.has(m))};
case 'endeavor':
return {cull: !isLead};
case 'facade':
return {cull: moves.has('substitute')};
case 'headbutt':
return {cull: !moves.has('bodyslam') && !moves.has('thunderwave')};
case 'swift':
return {cull: counter.setupType !== 'Special'};
case 'quickattack':
return {cull: moves.has('thunderwave')};
case 'firepunch': case 'flamethrower':
return {cull: moves.has('fireblast') || moves.has('overheat') && !counter.setupType};
case 'flareblitz':
return {cull: moves.has('superpower') && !!counter.get('speedsetup')};
case 'lavaplume': case 'fireblast':
if (move.id === 'fireblast' && moves.has('lavaplume') && !counter.get('speedsetup')) return {cull: true};
if (move.id === 'lavaplume' && moves.has('fireblast') && counter.get('speedsetup')) return {cull: true};
if (moves.has('flareblitz') && counter.setupType !== 'Special' &&
(!moves.has('superpower') || !counter.get('speedsetup'))) return {cull: true};
break;
case 'overheat':
return {cull: counter.setupType === 'Special' || ['batonpass', 'fireblast', 'flareblitz'].some(m => moves.has(m))};
case 'aquajet':
return {cull: moves.has('dragondance') || (moves.has('waterfall') && counter.get('Physical') < 3)};
case 'hydropump':
return {cull: moves.has('surf')};
case 'waterfall':
return {cull: (
moves.has('aquatail') ||
(counter.setupType !== 'Physical' && (moves.has('hydropump') || moves.has('surf')))
)};
case 'chargebeam':
return {cull: moves.has('thunderbolt') && counter.get('Special') < 3};
case 'discharge':
return {cull: moves.has('thunderbolt') || moves.has('shadowball')};
case 'energyball':
return {cull: (
moves.has('woodhammer') ||
(moves.has('sunnyday') && moves.has('solarbeam')) ||
(moves.has('leafstorm') && counter.get('Physical') + counter.get('Special') < 4)
)};
case 'grassknot': case 'leafblade': case 'seedbomb':
return {cull: moves.has('woodhammer') || (moves.has('sunnyday') && moves.has('solarbeam'))};
case 'leafstorm':
return {cull: (
!!counter.setupType ||
moves.has('batonpass') ||
moves.has('powerwhip') ||
moves.has('leafblade') ||
(moves.has('sunnyday') && moves.has('solarbeam'))
)};
case 'solarbeam':
return {cull: counter.setupType === 'Physical' || !moves.has('sunnyday')};
case 'icepunch':
return {cull: !counter.setupType && moves.has('icebeam')};
case 'aurasphere': case 'drainpunch': case 'focusblast':
return {cull: moves.has('closecombat') && counter.setupType !== 'Special'};
case 'brickbreak': case 'closecombat': case 'crosschop': case 'lowkick':
return {cull: moves.has('substitute') && moves.has('focuspunch')};
case 'machpunch':
return {cull: (counter.damagingMoves.size <= counter.get('Fighting'))};
case 'seismictoss':
return {cull: moves.has('nightshade') || counter.get('Physical') + counter.get('Special') >= 1};
case 'superpower':
return {cull: moves.has('dragondance') || !!counter.get('speedsetup') && !types.has('Fighting')};
case 'gunkshot':
return {cull: moves.has('poisonjab')};
case 'earthpower':
return {cull: moves.has('earthquake')};
case 'airslash':
return {cull: !counter.setupType && moves.has('bravebird')};
case 'zenheadbutt':
return {cull: moves.has('psychocut')};
case 'rockblast': case 'rockslide':
return {cull: moves.has('stoneedge')};
case 'shadowclaw': case 'shadowsneak':
return {cull: moves.has('suckerpunch') && !types.has('Ghost')};
case 'dracometeor':
return {cull: moves.has('calmmind') || restTalk || (!!counter.setupType && counter.get('stab') < 2)};
case 'dragonclaw':
return {cull: moves.has('outrage')};
case 'dragonpulse':
return {cull: moves.has('dracometeor') || moves.has('outrage')};
case 'crunch': case 'nightslash':
return {cull: moves.has('suckerpunch') && !types.has('Dark')};
case 'pursuit':
return {cull: !!counter.setupType || moves.has('payback')};
case 'flashcannon':
return {cull: (moves.has('ironhead') || movePool.includes('ironhead')) && counter.setupType !== 'Special'};
// Status:
case 'encore':
return {cull: ['roar', 'taunt', 'whirlwind'].some(m => moves.has(m)) || restTalk};
case 'haze': case 'taunt':
return {cull: restTalk};
case 'healbell':
// Ampharos doesn't want both
return {cull: moves.has('reflect') && moves.has('lightscreen')};
case 'leechseed': case 'painsplit':
return {cull: !!counter.setupType || !!counter.get('speedsetup') || moves.has('rest')};
case 'recover': case 'slackoff':
return {cull: restTalk};
case 'stunspore':
return {cull: (
!!counter.setupType ||
moves.has('toxic') ||
movePool.includes('sleeppowder') ||
movePool.includes('spore')
)};
case 'substitute':
return {cull: ['lightscreen', 'pursuit', 'rapidspin', 'reflect', 'rest', 'taunt'].some(m => moves.has(m))};
case 'thunderwave':
return {cull: (
!!counter.setupType ||
moves.has('toxic') ||
moves.has('trickroom') ||
(moves.has('bodyslam') && abilities.has('Serene Grace'))
)};
case 'yawn':
return {cull: moves.has('thunderwave') || moves.has('toxic')};
}
return {cull: false};
}
shouldCullAbility(
ability: string,
types: Set<string>,
moves: Set<string>,
abilities: Set<string>,
counter: MoveCounter,
movePool: string[],
teamDetails: RandomTeamsTypes.TeamDetails,
species: Species,
) {
switch (ability) {
case 'Anger Point': case 'Ice Body': case 'Steadfast': case 'Unaware':
return true;
case 'Blaze':
return !counter.get('Fire');
case 'Chlorophyll':
return !moves.has('sunnyday') && !teamDetails.sun;
case 'Compound Eyes': case 'No Guard':
return !counter.get('inaccurate');
case 'Early Bird':
return !moves.has('rest');
case 'Gluttony':
return !moves.has('bellydrum');
case 'Hustle':
return counter.get('Physical') < 2;
case 'Mold Breaker':
return !moves.has('earthquake');
case 'Overgrow':
return !counter.get('Grass');
case 'Reckless': case 'Rock Head':
return !counter.get('recoil');
case 'Sand Veil':
return !teamDetails.sand;
case 'Serene Grace':
return !counter.get('serenegrace') || species.id === 'blissey';
case 'Simple':
return !counter.setupType && !moves.has('cosmicpower');
case 'Skill Link':
return !counter.get('skilllink');
case 'Snow Cloak':
return !teamDetails.hail;
case 'Solar Power':
return !counter.get('Special') || !moves.has('sunnyday') && !teamDetails.sun;
case 'Speed Boost':
return moves.has('uturn');
case 'Swift Swim':
return !moves.has('raindance') && !teamDetails.rain;
case 'Swarm':
return !counter.get('Bug');
case 'Synchronize':
return counter.get('Status') < 2;
case 'Technician':
return !counter.get('technician') || moves.has('toxic');
case 'Thick Fat':
return (moves.has('facade') || moves.has('fakeout')) && abilities.has('Guts');
case 'Tinted Lens':
return moves.has('protect');
case 'Torrent':
return !counter.get('Water');
}
return false;
}
getHighPriorityItem(
ability: string,
types: Set<string>,
moves: Set<string>,
counter: MoveCounter,
teamDetails: RandomTeamsTypes.TeamDetails,
species: Species,
isLead: boolean,
): string | undefined {
if (species.requiredItem) return species.requiredItem;
if (species.requiredItems) return this.sample(species.requiredItems);
if (species.name === 'Ditto') return this.sample(['Salac Berry', 'Sitrus Berry']);
if (species.name === 'Farfetch\u2019d' && counter.get('Physical') < 4) return 'Stick';
if (species.name === 'Marowak') return 'Thick Club';
if (species.name === 'Pikachu') return 'Light Ball';
if (species.name === 'Shedinja' || species.name === 'Smeargle') return 'Focus Sash';
if (species.name === 'Unown') return 'Choice Specs';
if (species.name === 'Wobbuffet') {
return moves.has('destinybond') ? 'Custap Berry' : this.sample(['Leftovers', 'Sitrus Berry']);
}
if (moves.has('switcheroo') || moves.has('trick')) {
if (
species.baseStats.spe >= 60 && species.baseStats.spe <= 108 &&
!counter.get('priority') &&
this.randomChance(2, 3)
) {
return 'Choice Scarf';
} else {
return (counter.get('Physical') > counter.get('Special')) ? 'Choice Band' : 'Choice Specs';
}
}
if (moves.has('bellydrum')) return 'Sitrus Berry';
if (ability === 'Magic Guard' || ability === 'Speed Boost' && counter.get('Status') < 2) return 'Life Orb';
if (ability === 'Poison Heal' || ability === 'Toxic Boost') return 'Toxic Orb';
if (moves.has('rest') && !moves.has('sleeptalk') && ability !== 'Natural Cure' && ability !== 'Shed Skin') {
return (moves.has('raindance') && ability === 'Hydration') ? 'Damp Rock' : 'Chesto Berry';
}
if (moves.has('raindance') && ability === 'Swift Swim' && counter.get('Status') < 2) return 'Life Orb';
if (moves.has('sunnyday')) return (ability === 'Chlorophyll' && counter.get('Status') < 2) ? 'Life Orb' : 'Heat Rock';
if (moves.has('lightscreen') && moves.has('reflect')) return 'Light Clay';
if ((ability === 'Guts' || ability === 'Quick Feet') && moves.has('facade')) return 'Toxic Orb';
if (ability === 'Unburden') return 'Sitrus Berry';
if (species.baseStats.hp + species.baseStats.def + species.baseStats.spd <= 150) {
return isLead ? 'Focus Sash' : 'Life Orb';
}
if (moves.has('endeavor')) return 'Focus Sash';
}
getMediumPriorityItem(
ability: string,
moves: Set<string>,
counter: MoveCounter,
species: Species,
isDoubles: boolean,
isLead: boolean
): string | undefined {
if (
ability === 'Slow Start' ||
['curse', 'leechseed', 'protect', 'roar', 'sleeptalk', 'whirlwind'].some(m => moves.has(m)) ||
(ability === 'Serene Grace' && ['bodyslam', 'headbutt', 'ironhead'].some(m => moves.has(m)))
) {
return 'Leftovers';
}
if (counter.get('Physical') >= 4 && !moves.has('fakeout') && !moves.has('rapidspin') && !moves.has('suckerpunch')) {
return (
species.baseStats.spe >= 60 && species.baseStats.spe <= 108 &&
!counter.get('priority') && !moves.has('bodyslam') && this.randomChance(2, 3)
) ? 'Choice Scarf' : 'Choice Band';
}
if (
(counter.get('Special') >= 4 || (
counter.get('Special') >= 3 &&
['batonpass', 'uturn', 'waterspout', 'selfdestruct'].some(m => moves.has(m))
)) &&
!moves.has('chargebeam')
) {
return (
species.baseStats.spe >= 60 && species.baseStats.spe <= 108 &&
ability !== 'Speed Boost' && !counter.get('priority') && this.randomChance(2, 3)
) ? 'Choice Scarf' : 'Choice Specs';
}
if (moves.has('outrage') && counter.setupType) return 'Lum Berry';
if (moves.has('substitute')) {
return (counter.damagingMoves.size < 2 ||
!counter.get('drain') &&
(counter.damagingMoves.size < 3 || species.baseStats.hp >= 60 || species.baseStats.def + species.baseStats.spd >= 180)
) ? 'Leftovers' : 'Life Orb';
}
if (ability === 'Guts') return 'Toxic Orb';
if (
isLead &&
!counter.get('recoil') &&
!Array.from(moves).some(id => !!recoveryMoves.includes(id)) &&
species.baseStats.hp + species.baseStats.def + species.baseStats.spd < 225
) {
return 'Focus Sash';
}
if (counter.get('Dark') >= 3) return 'Black Glasses';
if (counter.damagingMoves.size >= 4) {
return (
counter.get('Normal') || counter.get('Dragon') > 1 || moves.has('chargebeam') || moves.has('suckerpunch')
) ? 'Life Orb' : 'Expert Belt';
}
if (counter.damagingMoves.size >= 3 && !moves.has('superfang') && !moves.has('metalburst')) {
const totalBulk = species.baseStats.hp + species.baseStats.def + species.baseStats.spd;
return (
counter.get('speedsetup') || counter.get('priority') ||
moves.has('dragondance') || moves.has('trickroom') ||
totalBulk < 235 ||
(species.baseStats.spe >= 70 && (totalBulk < 260 || (!!counter.get('recovery') && totalBulk < 285)))
) ? 'Life Orb' : 'Leftovers';
}
}
getLowPriorityItem(
ability: string,
types: Set<string>,
moves: Set<string>,
abilities: Set<string>,
counter: MoveCounter,
teamDetails: RandomTeamsTypes.TeamDetails,
species: Species,
) {
if (types.has('Poison')) return 'Black Sludge';
if (this.dex.getEffectiveness('Rock', species) >= 1 || moves.has('roar')) return 'Leftovers';
if (counter.get('Status') <= 1 && ['metalburst', 'rapidspin', 'superfang'].every(m => !moves.has(m))) return 'Life Orb';
return 'Leftovers';
}
randomSet(
species: string | Species,
teamDetails: RandomTeamsTypes.TeamDetails = {},
isLead = false
): RandomTeamsTypes.RandomSet {
species = this.dex.species.get(species);
let forme = species.name;
if (typeof species.battleOnly === 'string') forme = species.battleOnly;
if (species.cosmeticFormes) {
forme = this.sample([species.name].concat(species.cosmeticFormes));
}
const data = this.randomData[species.id];
const movePool = (data.moves || Object.keys(this.dex.species.getLearnset(species.id)!)).slice();
const rejectedPool: string[] = [];
const moves = new Set<string>();
let ability = '';
let item: string | undefined;
const evs = {hp: 85, atk: 85, def: 85, spa: 85, spd: 85, spe: 85};
const ivs: SparseStatsTable = {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31};
const types = new Set(species.types);
const abilities = new Set(Object.values(species.abilities));
let availableHP = 0;
for (const setMoveid of movePool) {
if (setMoveid.startsWith('hiddenpower')) availableHP++;
}
let counter: MoveCounter;
let hasHiddenPower = false;
do {
// Choose next 4 moves from learnset/viable moves and add them to moves list:
while (moves.size < this.maxMoveCount && movePool.length) {
const moveid = this.sampleNoReplace(movePool);
if (moveid.startsWith('hiddenpower')) {
availableHP--;
if (hasHiddenPower) continue;
hasHiddenPower = true;
}
moves.add(moveid);
}
while (moves.size < this.maxMoveCount && rejectedPool.length) {
const moveid = this.sampleNoReplace(rejectedPool);
if (moveid.startsWith('hiddenpower')) {
if (hasHiddenPower) continue;
hasHiddenPower = true;
}
moves.add(moveid);
}
counter = this.queryMoves(moves, species.types, abilities, movePool);
if (types.has('Dark') && moves.has('suckerpunch') && species.types.length === 1) {
counter.add('stab');
}
// Iterate through the moves again, this time to cull them:
for (const moveid of moves) {
const move = this.dex.moves.get(moveid);
let {cull, isSetup} = this.shouldCullMove(
move, types, moves, abilities, counter,
movePool, teamDetails, species, isLead
);
// Increased/decreased priority moves are unneeded with moves that boost only speed
if (move.priority !== 0 && !!counter.get('speedsetup')) cull = true;
// This move doesn't satisfy our setup requirements:
if (
(move.category === 'Physical' && counter.setupType === 'Special') ||
(move.category === 'Special' && counter.setupType === 'Physical')
) {
// Reject STABs last in case the setup type changes later on
if (
!SetupException.includes(moveid) &&
(!types.has(move.type) || counter.get('stab') > 1 || counter.get(move.category) < 2)
) {
cull = true;
}
}
if (
counter.setupType && !isSetup && move.category !== counter.setupType &&
counter.get(counter.setupType) < 2 && !moves.has('batonpass')
) {
// Mono-attacking with setup and RestTalk or recovery + status healing is allowed
if (
moveid !== 'rest' && moveid !== 'sleeptalk' &&
!(recoveryMoves.includes(moveid) && (moves.has('healbell') || moves.has('refresh'))) &&
!((moveid === 'healbell' || moveid === 'refresh') && Array.from(moves).some(id => recoveryMoves.includes(id))) && (
// Reject Status moves only if there is nothing else to reject
move.category !== 'Status' || (
counter.get(counter.setupType) + counter.get('Status') > 3 &&
counter.get('physicalsetup') + counter.get('specialsetup') < 2
)
)
) {
cull = true;
}
}
if (
moveid === 'hiddenpower' &&
counter.setupType === 'Special' &&
species.types.length > 1 &&
counter.get('Special') <= 2 &&
!types.has(move.type) &&
!counter.get('Physical') &&
counter.get('specialpool') &&
(!(types.has('Ghost') && move.type === 'Fighting' || types.has('Electric') && move.type === 'Ice'))
) {
// Hidden Power isn't good enough
cull = true;
}
// Reject defensive status moves if a reliable recovery move is available but not selected.
// Toxic is only defensive if used with another status move other than Protect (Toxic + 3 attacks and Toxic + Protect are ok).
if (
!Array.from(moves).some(id => recoveryMoves.includes(id)) &&
movePool.some(id => recoveryMoves.includes(id)) && (
defensiveStatusMoves.includes(moveid) ||
(moveid === 'toxic' && ((counter.get('Status') > 1 && !moves.has('protect')) || counter.get('Status') > 2))
)
) {
cull = true;
}
const runEnforcementChecker = (checkerName: string) => {
if (!this.moveEnforcementCheckers[checkerName]) return false;
return this.moveEnforcementCheckers[checkerName](
movePool, moves, abilities, types, counter, species as Species, teamDetails
);
};
const moveIsRejectable = (
!move.weather &&
!move.damage &&
(move.category !== 'Status' || !move.flags.heal) &&
(move.category === 'Status' || !types.has(move.type) || (move.basePower && move.basePower < 40 && !move.multihit)) &&
// These moves cannot be rejected in favor of a forced move
!['judgment', 'lightscreen', 'reflect', 'sleeptalk'].includes(moveid) &&
// Setup-supported moves should only be rejected under specific circumstances
(counter.get('physicalsetup') + counter.get('specialsetup') < 2 && (
!counter.setupType || counter.setupType === 'Mixed' ||
(move.category !== counter.setupType && move.category !== 'Status') ||
counter.get(counter.setupType) + counter.get('Status') > 3
))
);
if (!cull && !isSetup && moveIsRejectable) {
// There may be more important moves that this Pokemon needs
const canRollForcedMoves = (
// These moves should always be rolled
movePool.includes('spore') || (!Array.from(moves).some(id => recoveryMoves.includes(id)) && (
movePool.includes('softboiled') && !moves.has('explosion') ||
(species.baseSpecies === 'Arceus' && movePool.includes('recover'))
))
);
// Pokemon should usually have at least one STAB move
const requiresStab = (
!counter.get('stab') && !counter.get('damage') && (
species.types.length > 1 ||
(species.types[0] !== 'Normal' && species.types[0] !== 'Psychic') ||
!moves.has('icebeam') ||
species.baseStats.spa >= species.baseStats.spd
)
);
if (
canRollForcedMoves ||
requiresStab ||
(species.requiredMove && movePool.includes(toID(species.requiredMove))) ||
(counter.get('defensesetup') && !counter.get('recovery') && !moves.has('rest'))
) {
cull = true;
} else {
// Pokemon should have moves that benefit their typing or ability
for (const type of types) {
if (runEnforcementChecker(type)) cull = true;
}
for (const abil of abilities) {
if (runEnforcementChecker(abil)) cull = true;
}
for (const m of moves) {
if (runEnforcementChecker(m)) cull = true;
}
}
}
// Sleep Talk shouldn't be selected without Rest
if (moveid === 'rest' && cull) {
const sleeptalk = movePool.indexOf('sleeptalk');
if (sleeptalk >= 0) {
if (movePool.length < 2) {
cull = false;
} else {
this.fastPop(movePool, sleeptalk);
}
}
}
// Remove rejected moves from the move list
if (cull && (
movePool.length - availableHP || availableHP && (moveid.startsWith('hiddenpower') || !hasHiddenPower)
)) {
if (move.category !== 'Status' && (!moveid.startsWith('hiddenpower') || !availableHP)) rejectedPool.push(moveid);
moves.delete(moveid);
if (moveid.startsWith('hiddenpower')) hasHiddenPower = false;
break;
}
if (cull && rejectedPool.length) {
moves.delete(moveid);
if (moveid.startsWith('hiddenpower')) hasHiddenPower = false;
break;
}
}
} while (moves.size < this.maxMoveCount && (movePool.length || rejectedPool.length));
if (hasHiddenPower) {
let hpType;
for (const move of moves) {
if (move.startsWith('hiddenpower')) {
hpType = move.substr(11);
break;
}
}
if (!hpType) throw new Error(`hasHiddenPower is true, but no Hidden Power move was found.`);
const HPivs = this.dex.types.get(hpType).HPivs;
let iv: StatID;
for (iv in HPivs) {
ivs[iv] = HPivs[iv]!;
}
}
const abilityData = Array.from(abilities).map(a => this.dex.abilities.get(a));
Utils.sortBy(abilityData, abil => -abil.rating);
let ability0 = abilityData[0];
let ability1 = abilityData[1];
if (abilityData[1]) {
if (ability0.rating <= ability1.rating && this.randomChance(1, 2)) {
[ability0, ability1] = [ability1, ability0];
} else if (ability0.rating - 0.6 <= ability1.rating && this.randomChance(2, 3)) {
[ability0, ability1] = [ability1, ability0];
}
ability = ability0.name;
while (this.shouldCullAbility(ability, types, moves, abilities, counter, movePool, teamDetails, species)) {
if (ability === ability0.name && ability1.rating >= 1) {
ability = ability1.name;
} else {
// Default to the highest rated ability if all are rejected
ability = abilityData[0].name;
break;
}
}
if (abilities.has('Hydration') && moves.has('raindance') && moves.has('rest')) {
ability = 'Hydration';
} else if (abilities.has('Swift Swim') && moves.has('raindance')) {
ability = 'Swift Swim';
} else if (abilities.has('Technician') && moves.has('machpunch') && types.has('Fighting') && counter.get('stab') < 2) {
ability = 'Technician';
}
} else {
ability = ability0.name;
}
item = this.getHighPriorityItem(ability, types, moves, counter, teamDetails, species, isLead);
if (item === undefined) item = this.getMediumPriorityItem(ability, moves, counter, species, false, isLead);
if (item === undefined) {
item = this.getLowPriorityItem(ability, types, moves, abilities, counter, teamDetails, species);
}
// For Trick / Switcheroo
if (item === 'Leftovers' && types.has('Poison')) {
item = 'Black Sludge';
}
const level = this.adjustLevel || data.level || (species.nfe ? 90 : 80);
// Prepare optimal HP
let hp = Math.floor(
Math.floor(
2 * species.baseStats.hp + (ivs.hp || 31) + Math.floor(evs.hp / 4) + 100
) * level / 100 + 10
);
if (moves.has('substitute') && item === 'Sitrus Berry') {
// Two Substitutes should activate Sitrus Berry
while (hp % 4 > 0) {
evs.hp -= 4;
hp = Math.floor(
Math.floor(
2 * species.baseStats.hp + (ivs.hp || 31) + Math.floor(evs.hp / 4) + 100
) * level / 100 + 10
);
}
} else if (moves.has('bellydrum') && item === 'Sitrus Berry') {
// Belly Drum should activate Sitrus Berry
if (hp % 2 > 0) evs.hp -= 4;
} else {
// Maximize number of Stealth Rock switch-ins
const srWeakness = this.dex.getEffectiveness('Rock', species);
if (srWeakness > 0 && hp % (4 / srWeakness) === 0) evs.hp -= 4;
}
// Minimize confusion damage
if (!counter.get('Physical') && !moves.has('transform')) {
evs.atk = 0;
ivs.atk = hasHiddenPower ? (ivs.atk || 31) - 28 : 0;
}
if (['gyroball', 'metalburst', 'trickroom'].some(m => moves.has(m))) {
evs.spe = 0;
ivs.spe = hasHiddenPower ? (ivs.spe || 31) - 28 : 0;
}
return {
name: species.baseSpecies,
species: forme,
gender: species.gender,
shiny: this.randomChance(1, 1024),
moves: Array.from(moves),
ability,
evs,
ivs,
item,
level,
};
}
}
export default RandomGen4Teams;

View File

@ -1,20 +1,11 @@
export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable = {
standardag: {
inherit: true,
ruleset: [
'Obtainable', 'HP Percentage Mod', 'Cancel Mod', 'Beat Up Nicknames Mod', 'Endless Battle Clause',
],
},
export const Rulesets: {[k: string]: ModdedFormatData} = {
standard: {
inherit: true,
ruleset: [
'Standard AG',
'Sleep Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Items Clause', 'Evasion Moves Clause',
],
ruleset: ['Obtainable', 'Sleep Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Items Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod'],
},
flatrules: {
inherit: true,
ruleset: ['Obtainable', 'Species Clause', 'Nickname Clause', 'Beat Up Nicknames Mod', 'Item Clause = 1', 'Adjust Level Down = 50', 'Picked Team Size = Auto', 'Cancel Mod'],
ruleset: ['Obtainable', 'Species Clause', 'Nickname Clause', 'Item Clause', 'Adjust Level Down = 50', 'Cancel Mod'],
},
teampreview: {
inherit: true,
@ -22,9 +13,8 @@ export const Rulesets: import('../../../sim/dex-formats').ModdedFormatDataTable
this.add('clearpoke');
for (const pokemon of this.getAllPokemon()) {
const details = pokemon.details.replace(', shiny', '')
.replace(/(Arceus|Genesect|Gourgeist|Pumpkaboo|Xerneas|Silvally|Urshifu|Dudunsparce)(-[a-zA-Z?-]+)?/g, '$1-*')
.replace(/(Zacian|Zamazenta)(?!-Crowned)/g, '$1-*') // Hacked-in Crowned formes will be revealed
.replace(/(Greninja)(?!-Ash)/g, '$1-*'); // Hacked-in Greninja-Ash will be revealed
.replace(/(Arceus|Gourgeist|Pumpkaboo|Xerneas|Silvally|Urshifu|Dudunsparce)(-[a-zA-Z?-]+)?/g, '$1-*')
.replace(/(Zacian|Zamazenta)(?!-Crowned)/g, '$1-*'); // Hacked-in Crowned formes will be revealed
this.add('poke', pokemon.side.id, details, pokemon.item ? 'item' : '');
}
this.makeRequest('teampreview');

View File

@ -4,33 +4,6 @@ export const Scripts: ModdedBattleScriptsData = {
actions: {
inherit: true,
runSwitch(pokemon) {
this.battle.runEvent('EntryHazard', pokemon);
this.battle.runEvent('SwitchIn', pokemon);
if (this.battle.gen <= 2) {
// pokemon.lastMove is reset for all Pokemon on the field after a switch. This affects Mirror Move.
for (const poke of this.battle.getAllActive()) poke.lastMove = null;
if (this.battle.gen === 1) pokemon.side.lastSelectedMoveSlot = 0;
if (!pokemon.side.faintedThisTurn && pokemon.draggedIn !== this.battle.turn) {
this.battle.runEvent('AfterSwitchInSelf', pokemon);
}
}
if (!pokemon.hp) return false;
pokemon.isStarted = true;
if (!pokemon.fainted) {
this.battle.singleEvent('Start', pokemon.getAbility(), pokemon.abilityState, pokemon);
this.battle.singleEvent('Start', pokemon.getItem(), pokemon.itemState, pokemon);
}
if (this.battle.gen === 4) {
for (const foeActive of pokemon.foes()) {
foeActive.removeVolatile('substitutebroken');
}
}
pokemon.draggedIn = null;
return true;
},
modifyDamage(baseDamage, pokemon, target, move, suppressMessages = false) {
// DPP divides modifiers into several mathematically important stages
// The modifiers run earlier than other generations are called with ModifyDamagePhase1 and ModifyDamagePhase2
@ -49,13 +22,17 @@ export const Scripts: ModdedBattleScriptsData = {
// Double battle multi-hit
if (move.spreadHit) {
const spreadModifier = move.spreadModifier || (this.battle.gameType === 'freeforall' ? 0.5 : 0.75);
this.battle.debug(`Spread modifier: ${spreadModifier}`);
this.battle.debug('Spread modifier: ' + spreadModifier);
baseDamage = this.battle.modify(baseDamage, spreadModifier);
}
// Weather
baseDamage = this.battle.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage);
if (this.battle.gen === 3 && move.category === 'Physical' && !Math.floor(baseDamage)) {
baseDamage = 1;
}
baseDamage += 2;
const isCrit = target.getMoveHitData(move).crit;
@ -70,17 +47,12 @@ export const Scripts: ModdedBattleScriptsData = {
baseDamage = this.battle.randomizer(baseDamage);
// STAB
// The "???" type never gets STAB
// Not even if you Roost in Gen 4 and somehow manage to use
// Struggle in the same turn.
// (On second thought, it might be easier to get a MissingNo.)
if (type !== '???') {
let stab: number | [number, number] = 1;
if (move.forceSTAB || pokemon.hasType(type)) {
stab = 1.5;
}
stab = this.battle.runEvent('ModifySTAB', pokemon, target, move, stab);
baseDamage = this.battle.modify(baseDamage, stab);
if (move.forceSTAB || type !== '???' && pokemon.hasType(type)) {
// The "???" type never gets STAB
// Not even if you Roost in Gen 4 and somehow manage to use
// Struggle in the same turn.
// (On second thought, it might be easier to get a MissingNo.)
baseDamage = this.battle.modify(baseDamage, move.stab || 1.5);
}
// types
let typeMod = target.runEffectiveness(move);
@ -144,7 +116,7 @@ export const Scripts: ModdedBattleScriptsData = {
let boost!: number;
if (accuracy !== true) {
if (!move.ignoreAccuracy) {
boosts = this.battle.runEvent('ModifyBoost', pokemon, null, null, { ...pokemon.boosts });
boosts = this.battle.runEvent('ModifyBoost', pokemon, null, null, {...pokemon.boosts});
boost = this.battle.clampIntRange(boosts['accuracy'], -6, 6);
if (boost > 0) {
accuracy *= boostTable[boost];
@ -153,7 +125,7 @@ export const Scripts: ModdedBattleScriptsData = {
}
}
if (!move.ignoreEvasion) {
boosts = this.battle.runEvent('ModifyBoost', target, null, null, { ...target.boosts });
boosts = this.battle.runEvent('ModifyBoost', target, null, null, {...target.boosts});
boost = this.battle.clampIntRange(boosts['evasion'], -6, 6);
if (boost > 0) {
accuracy /= boostTable[boost];

View File

@ -1,4 +1,4 @@
export const FormatsData: import('../../../sim/dex-species').ModdedSpeciesFormatsDataTable = {
export const FormatsData: {[k: string]: ModdedSpeciesFormatsData} = {
pichuspikyeared: {
isNonstandard: "Future",
tier: "Illegal",

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