Compare commits

..

No commits in common. "9edeba321f595362e0f5ac133c72795839bc7d07" and "d06eb1bcc7ec0e3476745018cc53f332d8ec0a3c" have entirely different histories.

222 changed files with 28614 additions and 32831 deletions

View File

@ -5,7 +5,9 @@ module.exports = {
name: "no-non-type-@type-exports", name: "no-non-type-@type-exports",
severity: "error", severity: "error",
comment: comment:
"Files in @types should not export anything but types and interfaces. The folder is intended to house imports that are removed at runtime, and thus should not contain anything with a bearing on runtime code.", "Files in @types should not export anything but types and interfaces. " +
"The folder is intended to house imports that are removed at runtime, " +
"and thus should not contain anything with a bearing on runtime code.",
from: {}, from: {},
to: { to: {
path: "(^|/)src/@types", path: "(^|/)src/@types",
@ -27,7 +29,8 @@ module.exports = {
name: "no-circular-at-runtime", name: "no-circular-at-runtime",
severity: "error", severity: "error",
comment: comment:
"This dependency is part of a circular relationship. You might want to revise your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ", "This dependency is part of a circular relationship. You might want to revise " +
"your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ",
from: {}, from: {},
to: { to: {
circular: true, circular: true,
@ -39,7 +42,11 @@ module.exports = {
{ {
name: "no-orphans", name: "no-orphans",
comment: comment:
"This is an orphan module - it's likely not used (anymore?). Either use it or remove it. If it's logical this module is an orphan (i.e. it's a config file), add an exception for it in your dependency-cruiser configuration. By default this rule does not scrutinize dot-files (e.g. .eslintrc.js), TypeScript declaration files (.d.ts), tsconfig.json and some of the babel and webpack configs.", "This is an orphan module - it's likely not used (anymore?). Either use it or " +
"remove it. If it's logical this module is an orphan (i.e. it's a config file), " +
"add an exception for it in your dependency-cruiser configuration. By default " +
"this rule does not scrutinize dot-files (e.g. .eslintrc.js), TypeScript declaration " +
"files (.d.ts), tsconfig.json and some of the babel and webpack configs.",
severity: "error", severity: "error",
from: { from: {
orphan: true, orphan: true,
@ -56,7 +63,8 @@ module.exports = {
{ {
name: "no-deprecated-core", name: "no-deprecated-core",
comment: comment:
"A module depends on a node core module that has been deprecated. Find an alternative - these are bound to exist - node doesn't deprecate lightly.", "A module depends on a node core module that has been deprecated. Find an alternative - these are " +
"bound to exist - node doesn't deprecate lightly.",
severity: "error", severity: "error",
from: {}, from: {},
to: { to: {
@ -88,7 +96,8 @@ module.exports = {
{ {
name: "not-to-deprecated", name: "not-to-deprecated",
comment: comment:
"This module uses a (version of an) npm module that has been deprecated. Either upgrade to a later version of that module, or find an alternative. Deprecated modules are a security risk.", "This module uses a (version of an) npm module that has been deprecated. Either upgrade to a later " +
"version of that module, or find an alternative. Deprecated modules are a security risk.",
severity: "error", severity: "error",
from: {}, from: {},
to: { to: {
@ -99,7 +108,10 @@ module.exports = {
name: "no-non-package-json", name: "no-non-package-json",
severity: "error", severity: "error",
comment: comment:
"This module depends on an npm package that isn't in the 'dependencies' section of your package.json. That's problematic as the package either (1) won't be available on live (2 - worse) will be available on live with an non-guaranteed version. Fix it by adding the package to the dependencies in your package.json.", "This module depends on an npm package that isn't in the 'dependencies' section of your package.json. " +
"That's problematic as the package either (1) won't be available on live (2 - worse) will be " +
"available on live with an non-guaranteed version. Fix it by adding the package to the dependencies " +
"in your package.json.",
from: {}, from: {},
to: { to: {
dependencyTypes: ["npm-no-pkg", "npm-unknown"], dependencyTypes: ["npm-no-pkg", "npm-unknown"],
@ -108,7 +120,8 @@ module.exports = {
{ {
name: "not-to-unresolvable", name: "not-to-unresolvable",
comment: comment:
"This module depends on a module that cannot be found ('resolved to disk'). If it's an npm module: add it to your package.json. In all other cases you likely already know what to do.", "This module depends on a module that cannot be found ('resolved to disk'). If it's an npm " +
"module: add it to your package.json. In all other cases you likely already know what to do.",
severity: "error", severity: "error",
from: {}, from: {},
to: { to: {
@ -118,7 +131,9 @@ module.exports = {
{ {
name: "no-duplicate-dep-types", name: "no-duplicate-dep-types",
comment: comment:
"Likely this module depends on an external ('npm') package that occurs more than once in your package.json i.e. bot as a devDependencies and in dependencies. This will cause maintenance problems later on.", "Likely this module depends on an external ('npm') package that occurs more than once " +
"in your package.json i.e. bot as a devDependencies and in dependencies. This will cause " +
"maintenance problems later on.",
severity: "error", severity: "error",
from: {}, from: {},
to: { to: {
@ -135,7 +150,9 @@ module.exports = {
{ {
name: "not-to-spec", name: "not-to-spec",
comment: comment:
"This module depends on a spec (test) file. The sole responsibility of a spec file is to test code. If there's something in a spec that's of use to other modules, it doesn't have that single responsibility anymore. Factor it out into (e.g.) a separate utility/ helper or a mock.", "This module depends on a spec (test) file. The sole responsibility of a spec file is to test code. " +
"If there's something in a spec that's of use to other modules, it doesn't have that single " +
"responsibility anymore. Factor it out into (e.g.) a separate utility/ helper or a mock.",
severity: "error", severity: "error",
from: {}, from: {},
to: { to: {
@ -146,7 +163,11 @@ module.exports = {
name: "not-to-dev-dep", name: "not-to-dev-dep",
severity: "error", severity: "error",
comment: comment:
"This module depends on an npm package from the 'devDependencies' section of your package.json. It looks like something that ships to production, though. To prevent problems with npm packages that aren't there on production declare it (only!) in the 'dependencies'section of your package.json. If this module is development only - add it to the from.pathNot re of the not-to-dev-dep rule in the dependency-cruiser configuration", "This module depends on an npm package from the 'devDependencies' section of your " +
"package.json. It looks like something that ships to production, though. To prevent problems " +
"with npm packages that aren't there on production declare it (only!) in the 'dependencies'" +
"section of your package.json. If this module is development only - add it to the " +
"from.pathNot re of the not-to-dev-dep rule in the dependency-cruiser configuration",
from: { from: {
path: "^(src)", path: "^(src)",
pathNot: ["[.](?:spec|test|setup|script)[.](?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$", "./test"], pathNot: ["[.](?:spec|test|setup|script)[.](?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$", "./test"],
@ -163,7 +184,10 @@ module.exports = {
name: "optional-deps-used", name: "optional-deps-used",
severity: "info", severity: "info",
comment: comment:
"This module depends on an npm package that is declared as an optional dependency in your package.json. As this makes sense in limited situations only, it's flagged here. If you're using an optional dependency here by design - add an exception to yourdependency-cruiser configuration.", "This module depends on an npm package that is declared as an optional dependency " +
"in your package.json. As this makes sense in limited situations only, it's flagged here. " +
"If you're using an optional dependency here by design - add an exception to your" +
"dependency-cruiser configuration.",
from: {}, from: {},
to: { to: {
dependencyTypes: ["npm-optional"], dependencyTypes: ["npm-optional"],
@ -172,7 +196,10 @@ module.exports = {
{ {
name: "peer-deps-used", name: "peer-deps-used",
comment: comment:
"This module depends on an npm package that is declared as a peer dependency in your package.json. This makes sense if your package is e.g. a plugin, but in other cases - maybe not so much. If the use of a peer dependency is intentional add an exception to your dependency-cruiser configuration.", "This module depends on an npm package that is declared as a peer dependency " +
"in your package.json. This makes sense if your package is e.g. a plugin, but in " +
"other cases - maybe not so much. If the use of a peer dependency is intentional " +
"add an exception to your dependency-cruiser configuration.",
severity: "error", severity: "error",
from: {}, from: {},
to: { to: {

View File

@ -1,5 +1,6 @@
{ {
"$schema": "https://biomejs.dev/schemas/2.2.0/schema.json", // ! Just ignore the errors for now guys.
"$schema": "https://biomejs.dev/schemas/2.1.4/schema.json",
"vcs": { "vcs": {
"enabled": true, "enabled": true,
"clientKind": "git", "clientKind": "git",
@ -10,7 +11,7 @@
"enabled": true, "enabled": true,
"useEditorconfig": true, "useEditorconfig": true,
"indentStyle": "space", "indentStyle": "space",
"includes": ["**", "!**/src/enums", "!**/src/data/balance"], "includes": ["**", "!**/src/enums/**/*", "!**/src/data/balance/**/*"],
"lineWidth": 120 "lineWidth": 120
}, },
"files": { "files": {
@ -19,12 +20,12 @@
// and having to verify whether each individual file is ignored // and having to verify whether each individual file is ignored
"includes": [ "includes": [
"**", "**",
"!**/dist", "!**/dist/**/*",
"!**/coverage", "!**/coverage/**/*",
"!**/public", "!**/public/**/*",
"!**/.github", "!**/.github/**/*",
"!**/node_modules", "!**/node_modules/**/*",
"!**/typedoc", "!**/typedoc/**/*",
// TODO: lint css and html? // TODO: lint css and html?
"!**/*.css", "!**/*.css",
"!**/*.html", "!**/*.html",
@ -58,35 +59,23 @@
"noVoidTypeReturn": "error", "noVoidTypeReturn": "error",
"noUnusedImports": { "noUnusedImports": {
"level": "error", "level": "error",
"fix": "safe", "fix": "safe"
"options": {}
}, },
"noUnusedFunctionParameters": "error", "noUnusedFunctionParameters": "error",
"noUnusedLabels": "error", "noUnusedLabels": "error",
"noPrivateImports": "error", "noPrivateImports": "error"
"useSingleJsDocAsterisk": "error",
"useJsonImportAttributes": "off" // "Import attributes are only supported when the '--module' option is set to 'esnext', 'node18', 'nodenext', or 'preserve'. ts(2823)"
}, },
"style": { "style": {
"useReadonlyClassProperties": {
"level": "info", // TODO: Graduate to error eventually
"options": { "checkAllProperties": true }
},
"useConsistentObjectDefinitions": {
"level": "error",
"options": { "syntax": "shorthand" }
},
"useCollapsedIf": "error", "useCollapsedIf": "error",
"useCollapsedElseIf": "error", "useCollapsedElseIf": "error",
"noDoneCallback": "error", "noDoneCallback": "error",
"noSubstr": "error", "noSubstr": "error",
"noYodaExpression": "error", "noYodaExpression": "error",
"useForOf": "error", "useForOf": "error",
"useEnumInitializers": "off", // large enums like MoveId/SpeciesId would make this cumbersome "useEnumInitializers": "off", // large enums like Moves/Species would make this cumbersome
"useBlockStatements": { "useBlockStatements": {
"level": "error", "level": "error",
"fix": "safe", "fix": "safe"
"options": {}
}, },
"useConst": "error", "useConst": "error",
"useImportType": "error", "useImportType": "error",
@ -97,13 +86,11 @@
// TODO: Fix spots in the codebase where this flag would be triggered // TODO: Fix spots in the codebase where this flag would be triggered
// and then set to "error" and re-enable the fixer // and then set to "error" and re-enable the fixer
"level": "warn", "level": "warn",
"fix": "none", "fix": "none"
"options": {}
}, },
"useSingleVarDeclarator": { "useSingleVarDeclarator": {
"level": "error", "level": "error",
"fix": "safe", "fix": "safe"
"options": {}
}, },
"useNodejsImportProtocol": "off", "useNodejsImportProtocol": "off",
"useTemplate": "off", // string concatenation is faster: https://stackoverflow.com/questions/29055518/are-es6-template-literals-faster-than-string-concatenation "useTemplate": "off", // string concatenation is faster: https://stackoverflow.com/questions/29055518/are-es6-template-literals-faster-than-string-concatenation
@ -122,11 +109,7 @@
} }
} }
} }
}, }
"useUnifiedTypeSignatures": "error",
"useGroupedAccessorPairs": "error",
"useObjectSpread": "error",
"useNumericSeparators": "off" // TODO: Consider enabling?
}, },
"suspicious": { "suspicious": {
"noDoubleEquals": "error", "noDoubleEquals": "error",
@ -135,17 +118,14 @@
// This can likely be enabled for all non-utils files once these are eventually reworked, but until then we leave it off. // This can likely be enabled for all non-utils files once these are eventually reworked, but until then we leave it off.
"noExplicitAny": "off", "noExplicitAny": "off",
"noAssignInExpressions": "off", "noAssignInExpressions": "off",
"noPrototypeBuiltins": "off", // TODO: enable this "noPrototypeBuiltins": "off", // TOZO: consider enabling?
"noFallthroughSwitchClause": "error", // Prevents accidental automatic fallthroughs in switch cases (use disable comment if needed) "noFallthroughSwitchClause": "error", // Prevents accidental automatic fallthroughs in switch cases (use disable comment if needed)
"noImplicitAnyLet": "warn", // TODO: Refactor and make this an error "noImplicitAnyLet": "warn", // TODO: Refactor and make this an error
"noRedeclare": "info", // TODO: Refactor and make this an error "noRedeclare": "info", // TODO: Refactor and make this an error
"noGlobalIsNan": "error", "noGlobalIsNan": "error",
"noAsyncPromiseExecutor": "warn", // TODO: Refactor and make this an error "noAsyncPromiseExecutor": "warn", // TODO: Refactor and make this an error
"noVar": "error", "noVar": "error",
"noDocumentCookie": "off", // Firefox has minimal support for the "Cookie Store API" "noDocumentCookie": "off" // Firefox has minimal support for the "Cookie Store API"
"noConstantBinaryExpressions": "error",
"noTsIgnore": "error",
"useIterableCallbackReturn": "warn" // TODO: Refactor and change to error
}, },
"complexity": { "complexity": {
"noUselessStringConcat": "error", "noUselessStringConcat": "error",
@ -157,8 +137,7 @@
"noBannedTypes": "warn", // TODO: Refactor and make this an error "noBannedTypes": "warn", // TODO: Refactor and make this an error
"noThisInStatic": "error", "noThisInStatic": "error",
"noUselessThisAlias": "error", "noUselessThisAlias": "error",
"noUselessTernary": "error", "noUselessTernary": "error"
"useIndexOf": "error"
}, },
"performance": { "performance": {
"noNamespaceImport": "error", "noNamespaceImport": "error",
@ -166,10 +145,17 @@
"noBarrelFile": "error" "noBarrelFile": "error"
}, },
"nursery": { "nursery": {
"useMaxParams": { "useSingleJsDocAsterisk": "error",
"level": "warn", // TODO: Change to "error" eventually "useUnifiedTypeSignature": "error",
"options": { "max": 4 } // TODO: is this ok? "useAdjacentGetterSetter": "error",
}, "noConstantBinaryExpression": "error",
"noTsIgnore": "error",
"noAwaitInLoop": "off", // we do this a lot
"useJsonImportAttribute": "off", // "Import attributes are only supported when the '--module' option is set to 'esnext', 'node18', 'nodenext', or 'preserve'. ts(2823)"
"useIndexOf": "error",
"useObjectSpread": "error",
"useNumericSeparators": "off", // TODO: enable?
"useIterableCallbackReturn": "warn", // TODO: refactor and make "error"
"noShadow": "warn" // TODO: refactor and make "error" "noShadow": "warn" // TODO: refactor and make "error"
} }
} }
@ -177,8 +163,7 @@
"javascript": { "javascript": {
"formatter": { "formatter": {
"quoteStyle": "double", "quoteStyle": "double",
"arrowParentheses": "asNeeded", "arrowParentheses": "asNeeded"
"operatorLinebreak": "before"
}, },
"globals": ["Phaser"], "globals": ["Phaser"],
"parser": { "parser": {
@ -195,7 +180,7 @@
"noNamespaceImport": "off" // this is required for `vi.spyOn` to work in some tests "noNamespaceImport": "off" // this is required for `vi.spyOn` to work in some tests
}, },
"style": { "style": {
"noNonNullAssertion": "off" // tedious in some tests "noNonNullAssertion": "off"
}, },
"nursery": { "nursery": {
"noFloatingPromises": "error" "noFloatingPromises": "error"
@ -207,7 +192,7 @@
// Overrides to prevent unused import removal inside `overrides.ts` and enums files (for TSDoc linkcodes), // Overrides to prevent unused import removal inside `overrides.ts` and enums files (for TSDoc linkcodes),
// as well as in all TS files in `scripts/` (which are assumed to be boilerplate templates). // as well as in all TS files in `scripts/` (which are assumed to be boilerplate templates).
{ {
"includes": ["**/src/overrides.ts", "**/src/enums", "**/scripts/**/*.ts", "**/*.d.ts"], "includes": ["**/src/overrides.ts", "**/src/enums/**/*", "**/scripts/**/*.ts", "**/*.d.ts"],
"linter": { "linter": {
"rules": { "rules": {
"correctness": { "correctness": {

View File

@ -15,12 +15,12 @@ On the other hand, if Biome complains about a piece of code, **there's probably
Biome has integration with many popular code editors. See [these](https://biomejs.dev/guides/editors/first-party-extensions/) [pages](https://biomejs.dev/guides/editors/third-party-extensions/) for information about enabling Biome in your editor of choice. Biome has integration with many popular code editors. See [these](https://biomejs.dev/guides/editors/first-party-extensions/) [pages](https://biomejs.dev/guides/editors/third-party-extensions/) for information about enabling Biome in your editor of choice.
## Automated Runs ## Automated Runs
Generally speaking, most users shouldn't need to run Biome directly; in addition to editor integration, a [pre-commit hook](../lefthook.yml) will automatically format and lint all staged files before each commit. Generally speaking, most users shouldn't need to run Biome directly; in addition to editor integration, a [pre-commit hook](../lefthook.yml) will periodically run Biome before each commit on any changed files.
> ![WARNING] > ![WARNING]
> You will **not** be able to commit code if any staged files contain `error`-level linting problems. \ > You will **not** be able to commit code if any staged files contain `error`-level linting problems. \
> If you, for whatever reason, _absolutely need_ to bypass Lefthook for a given commit, > If you, for whatever reason, _absolutely need_ to bypass Lefthook for a given commit,
> pass the `--no-verify` flag to `git commit`. > `LEFTHOOK=0 git commit` will skip running all pre-commit hooks during the commit process.
We also have a [Github Action](../.github/workflows/linting.yml) to verify code quality each time a PR is updated, preventing bad code from inadvertently making its way upstream. \ We also have a [Github Action](../.github/workflows/linting.yml) to verify code quality each time a PR is updated, preventing bad code from inadvertently making its way upstream. \
These are effectively the same commands as run by Lefthook, merely on a project-wide scale. These are effectively the same commands as run by Lefthook, merely on a project-wide scale.
@ -52,6 +52,6 @@ Some things to consider:
- Some rules are currently marked as warnings (`warn`) to allow for gradual refactoring without blocking development. **Do not write new code that triggers these rules!** - Some rules are currently marked as warnings (`warn`) to allow for gradual refactoring without blocking development. **Do not write new code that triggers these rules!**
- The linter is configured to ignore specific files and folders (such as excessively large files or ones in need of refactoring) to improve performance and focus on actionable areas. - The linter is configured to ignore specific files and folders (such as excessively large files or ones in need of refactoring) to improve performance and focus on actionable areas.
Any questions about linting rules can be brought up in the `#dev-corner` channel in the community Discord. Any questions about linting rules can be brought up in the `#dev-corner` channel in the communityDiscord.
[^1]: A complete list of rules can be found in the [`biome.jsonc`](../biome.jsonc) file in the project root. Many rules are accompanied by comments explaining the reasons for their inclusion (or lack thereof). [^1]: A complete list of rules can be found in the [`biome.jsonc`](../biome.jsonc) file in the project root. Many rules are accompanied by comments explaining the reasons for their inclusion (or lack thereof).

View File

@ -1,6 +1,6 @@
# Localization 101 # Localization 101
PokéRogue's localization team puts immense effort into making the game accessible around the world, supporting over 12 different languages at the time of writing this document. \ PokéRogue's localization team puts immense effort into making the game accessible around the world, supporting over 12 different languages at the time of writing this document.
As a developer, it's important to help maintain global accessibility by effectively coordinating with the Translation Team on any new features or enhancements. As a developer, it's important to help maintain global accessibility by effectively coordinating with the Translation Team on any new features or enhancements.
This document aims to cover everything you need to know to help keep the integration process for localization smooth and simple. This document aims to cover everything you need to know to help keep the integration process for localization smooth and simple.
@ -33,14 +33,14 @@ The parent repo (the "superproject") houses a cloned version of the 2nd reposito
The following command will initialize your branch's locales repository and update its HEAD: The following command will initialize your branch's locales repository and update its HEAD:
```bash ```bash
pnpm update-locales pnpm update:locales
``` ```
> [!TIP] > [!TIP]
> This command is run _automatically_ after cloning, merging or changing branches, so you should rarely have to run it manually. > This command is run _automatically_ after cloning, merging or changing branches, so you should rarely have to run it manually.
> [!IMPORTANT] > [!IMPORTANT]
> If you EVER run into issues with the `locales` submodule, try deleting the `.git/modules/public` and `public/locales` folders before re-initializing it again. > If you EVER run into issues with the `locales` submodule, try deleting the `.git/modules/public` and `public/locales` folders before re-initializing the repository.
## How Are Translations Integrated? ## How Are Translations Integrated?
@ -80,14 +80,7 @@ Given `pokerogue-locales` is a full-fledged `git` repository _inside_ `pokerogue
> [!WARNING] > [!WARNING]
> Make sure to checkout or rebase onto `upstream/main` (`pnpm update-locales:remote`) **BEFORE** creating a locales PR! > Make sure to checkout or rebase onto `upstream/main` (`pnpm update-locales:remote`) **BEFORE** creating a locales PR!
> The checked-out commit is based on the superproject's SHA-1 by default, so hastily making changes may see you basing your commits on last week's `HEAD`. > The checked-out commit is based on the superproject's SHA-1 by default, so hastily making changes may see you basing your commits on last week 's `HEAD`.
### Locale Key Formatting Standards
Newly added locale keys should adhere to the following format:
- File names should be in `kebab-case`. Example: `trainer-names.json`
- Key names should be in `camelCase`. Example: `aceTrainer`
- If you make use of i18next's inbuilt [context support](https://www.i18next.com/translation-function/context), you will need to use `snake_case` for the context key part. Example: `aceTrainer_male`
## Requirements for Adding Translated Text ## Requirements for Adding Translated Text
When a new feature or enhancement requires adding a new locales key **without changing text in existing keys**, we have the following workflow with regards to localization: When a new feature or enhancement requires adding a new locales key **without changing text in existing keys**, we have the following workflow with regards to localization:
@ -142,7 +135,7 @@ The easiest way to do this is by **pinging the current Head of Translation** in
<!-- Remember to update this everytime the head of translation changes! --> <!-- Remember to update this everytime the head of translation changes! -->
> [!IMPORTANT] > [!IMPORTANT]
> The current Head of Translation is: \ > The current Head of Translation is:
> ** @lugiadrien ** > ** @lugiadrien **
# Closing Remarks # Closing Remarks

View File

@ -4,9 +4,8 @@ pre-commit:
- rebase - rebase
commands: commands:
biome-lint: biome-lint:
# Disable colors as certain IDEs don't support it in the output pane. # Disable colors as certain IDEs don't allow it
# Summary mode looks decent in plain ASCII anyhow run: pnpm exec biome check --write --colors="off" --staged --no-errors-on-unmatched
run: pnpm exec biome check --write --colors=off --reporter=summary --staged --no-errors-on-unmatched --diagnostic-level=error
stage_fixed: true stage_fixed: true
ls-lint: ls-lint:
run: pnpm exec ls-lint run: pnpm exec ls-lint

View File

@ -29,7 +29,7 @@
"update-locales:remote": "git submodule update --progress --init --recursive --force --remote" "update-locales:remote": "git submodule update --progress --init --recursive --force --remote"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.2.0", "@biomejs/biome": "2.1.4",
"@ls-lint/ls-lint": "2.3.1", "@ls-lint/ls-lint": "2.3.1",
"@types/jsdom": "^21.1.7", "@types/jsdom": "^21.1.7",
"@types/node": "^22.16.5", "@types/node": "^22.16.5",

View File

@ -43,8 +43,8 @@ importers:
version: 1.80.16(graphology-types@0.24.8) version: 1.80.16(graphology-types@0.24.8)
devDependencies: devDependencies:
'@biomejs/biome': '@biomejs/biome':
specifier: 2.2.0 specifier: 2.1.4
version: 2.2.0 version: 2.1.4
'@ls-lint/ls-lint': '@ls-lint/ls-lint':
specifier: 2.3.1 specifier: 2.3.1
version: 2.3.1 version: 2.3.1
@ -180,55 +180,55 @@ packages:
resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@biomejs/biome@2.2.0': '@biomejs/biome@2.1.4':
resolution: {integrity: sha512-3On3RSYLsX+n9KnoSgfoYlckYBoU6VRM22cw1gB4Y0OuUVSYd/O/2saOJMrA4HFfA1Ff0eacOvMN1yAAvHtzIw==} resolution: {integrity: sha512-QWlrqyxsU0FCebuMnkvBIkxvPqH89afiJzjMl+z67ybutse590jgeaFdDurE9XYtzpjRGTI1tlUZPGWmbKsElA==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
hasBin: true hasBin: true
'@biomejs/cli-darwin-arm64@2.2.0': '@biomejs/cli-darwin-arm64@2.1.4':
resolution: {integrity: sha512-zKbwUUh+9uFmWfS8IFxmVD6XwqFcENjZvEyfOxHs1epjdH3wyyMQG80FGDsmauPwS2r5kXdEM0v/+dTIA9FXAg==} resolution: {integrity: sha512-sCrNENE74I9MV090Wq/9Dg7EhPudx3+5OiSoQOkIe3DLPzFARuL1dOwCWhKCpA3I5RHmbrsbNSRfZwCabwd8Qg==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@biomejs/cli-darwin-x64@2.2.0': '@biomejs/cli-darwin-x64@2.1.4':
resolution: {integrity: sha512-+OmT4dsX2eTfhD5crUOPw3RPhaR+SKVspvGVmSdZ9y9O/AgL8pla6T4hOn1q+VAFBHuHhsdxDRJgFCSC7RaMOw==} resolution: {integrity: sha512-gOEICJbTCy6iruBywBDcG4X5rHMbqCPs3clh3UQ+hRKlgvJTk4NHWQAyHOXvaLe+AxD1/TNX1jbZeffBJzcrOw==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@biomejs/cli-linux-arm64-musl@2.2.0': '@biomejs/cli-linux-arm64-musl@2.1.4':
resolution: {integrity: sha512-egKpOa+4FL9YO+SMUMLUvf543cprjevNc3CAgDNFLcjknuNMcZ0GLJYa3EGTCR2xIkIUJDVneBV3O9OcIlCEZQ==} resolution: {integrity: sha512-nYr7H0CyAJPaLupFE2cH16KZmRC5Z9PEftiA2vWxk+CsFkPZQ6dBRdcC6RuS+zJlPc/JOd8xw3uCCt9Pv41WvQ==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@biomejs/cli-linux-arm64@2.2.0': '@biomejs/cli-linux-arm64@2.1.4':
resolution: {integrity: sha512-6eoRdF2yW5FnW9Lpeivh7Mayhq0KDdaDMYOJnH9aT02KuSIX5V1HmWJCQQPwIQbhDh68Zrcpl8inRlTEan0SXw==} resolution: {integrity: sha512-juhEkdkKR4nbUi5k/KRp1ocGPNWLgFRD4NrHZSveYrD6i98pyvuzmS9yFYgOZa5JhaVqo0HPnci0+YuzSwT2fw==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@biomejs/cli-linux-x64-musl@2.2.0': '@biomejs/cli-linux-x64-musl@2.1.4':
resolution: {integrity: sha512-I5J85yWwUWpgJyC1CcytNSGusu2p9HjDnOPAFG4Y515hwRD0jpR9sT9/T1cKHtuCvEQ/sBvx+6zhz9l9wEJGAg==} resolution: {integrity: sha512-lvwvb2SQQHctHUKvBKptR6PLFCM7JfRjpCCrDaTmvB7EeZ5/dQJPhTYBf36BE/B4CRWR2ZiBLRYhK7hhXBCZAg==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@biomejs/cli-linux-x64@2.2.0': '@biomejs/cli-linux-x64@2.1.4':
resolution: {integrity: sha512-5UmQx/OZAfJfi25zAnAGHUMuOd+LOsliIt119x2soA2gLggQYrVPA+2kMUxR6Mw5M1deUF/AWWP2qpxgH7Nyfw==} resolution: {integrity: sha512-Eoy9ycbhpJVYuR+LskV9s3uyaIkp89+qqgqhGQsWnp/I02Uqg2fXFblHJOpGZR8AxdB9ADy87oFVxn9MpFKUrw==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@biomejs/cli-win32-arm64@2.2.0': '@biomejs/cli-win32-arm64@2.1.4':
resolution: {integrity: sha512-n9a1/f2CwIDmNMNkFs+JI0ZjFnMO0jdOyGNtihgUNFnlmd84yIYY2KMTBmMV58ZlVHjgmY5Y6E1hVTnSRieggA==} resolution: {integrity: sha512-3WRYte7orvyi6TRfIZkDN9Jzoogbv+gSvR+b9VOXUg1We1XrjBg6WljADeVEaKTvOcpVdH0a90TwyOQ6ue4fGw==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@biomejs/cli-win32-x64@2.2.0': '@biomejs/cli-win32-x64@2.1.4':
resolution: {integrity: sha512-Nawu5nHjP/zPKTIryh2AavzTc/KEg4um/MxWdXW0A6P/RZOyIpa7+QSjeXwAwX/utJGaCoXRPWtF3m5U/bB3Ww==} resolution: {integrity: sha512-tBc+W7anBPSFXGAoQW+f/+svkpt8/uXfRwDzN1DvnatkRMt16KIYpEi/iw8u9GahJlFv98kgHcIrSsZHZTR0sw==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
@ -2119,39 +2119,39 @@ snapshots:
'@babel/helper-string-parser': 7.27.1 '@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1 '@babel/helper-validator-identifier': 7.27.1
'@biomejs/biome@2.2.0': '@biomejs/biome@2.1.4':
optionalDependencies: optionalDependencies:
'@biomejs/cli-darwin-arm64': 2.2.0 '@biomejs/cli-darwin-arm64': 2.1.4
'@biomejs/cli-darwin-x64': 2.2.0 '@biomejs/cli-darwin-x64': 2.1.4
'@biomejs/cli-linux-arm64': 2.2.0 '@biomejs/cli-linux-arm64': 2.1.4
'@biomejs/cli-linux-arm64-musl': 2.2.0 '@biomejs/cli-linux-arm64-musl': 2.1.4
'@biomejs/cli-linux-x64': 2.2.0 '@biomejs/cli-linux-x64': 2.1.4
'@biomejs/cli-linux-x64-musl': 2.2.0 '@biomejs/cli-linux-x64-musl': 2.1.4
'@biomejs/cli-win32-arm64': 2.2.0 '@biomejs/cli-win32-arm64': 2.1.4
'@biomejs/cli-win32-x64': 2.2.0 '@biomejs/cli-win32-x64': 2.1.4
'@biomejs/cli-darwin-arm64@2.2.0': '@biomejs/cli-darwin-arm64@2.1.4':
optional: true optional: true
'@biomejs/cli-darwin-x64@2.2.0': '@biomejs/cli-darwin-x64@2.1.4':
optional: true optional: true
'@biomejs/cli-linux-arm64-musl@2.2.0': '@biomejs/cli-linux-arm64-musl@2.1.4':
optional: true optional: true
'@biomejs/cli-linux-arm64@2.2.0': '@biomejs/cli-linux-arm64@2.1.4':
optional: true optional: true
'@biomejs/cli-linux-x64-musl@2.2.0': '@biomejs/cli-linux-x64-musl@2.1.4':
optional: true optional: true
'@biomejs/cli-linux-x64@2.2.0': '@biomejs/cli-linux-x64@2.1.4':
optional: true optional: true
'@biomejs/cli-win32-arm64@2.2.0': '@biomejs/cli-win32-arm64@2.1.4':
optional: true optional: true
'@biomejs/cli-win32-x64@2.2.0': '@biomejs/cli-win32-x64@2.1.4':
optional: true optional: true
'@bundled-es-modules/cookie@2.0.1': '@bundled-es-modules/cookie@2.0.1':

View File

@ -1,24 +1,13 @@
import type { Pokemon } from "#field/pokemon";
import type { import type {
AttackMove, AttackMove,
ChargingAttackMove, ChargingAttackMove,
ChargingSelfStatusMove, ChargingSelfStatusMove,
Move,
MoveAttr, MoveAttr,
MoveAttrConstructorMap, MoveAttrConstructorMap,
SelfStatusMove, SelfStatusMove,
StatusMove, StatusMove,
} from "#moves/move"; } from "#moves/move";
/**
* A generic function producing a message during a Move's execution.
* @param user - The {@linkcode Pokemon} using the move
* @param target - The {@linkcode Pokemon} targeted by the move
* @param move - The {@linkcode Move} being used
* @returns a string
*/
export type MoveMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => string;
export type MoveAttrFilter = (attr: MoveAttr) => boolean; export type MoveAttrFilter = (attr: MoveAttr) => boolean;
export type * from "#moves/move"; export type * from "#moves/move";

View File

@ -104,7 +104,6 @@ import {
getLuckString, getLuckString,
getLuckTextTint, getLuckTextTint,
getPartyLuckValue, getPartyLuckValue,
type ModifierType,
PokemonHeldItemModifierType, PokemonHeldItemModifierType,
} from "#modifiers/modifier-type"; } from "#modifiers/modifier-type";
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
@ -811,7 +810,7 @@ export class BattleScene extends SceneBase {
* @returns An array of {@linkcode Pokemon}, as described above. * @returns An array of {@linkcode Pokemon}, as described above.
*/ */
public getField(activeOnly = false): Pokemon[] { public getField(activeOnly = false): Pokemon[] {
const ret: Pokemon[] = new Array(4).fill(null); const ret = new Array(4).fill(null);
const playerField = this.getPlayerField(); const playerField = this.getPlayerField();
const enemyField = this.getEnemyField(); const enemyField = this.getEnemyField();
ret.splice(0, playerField.length, ...playerField); ret.splice(0, playerField.length, ...playerField);
@ -834,10 +833,10 @@ export class BattleScene extends SceneBase {
do { do {
targetingMovePhase = this.phaseManager.findPhase( targetingMovePhase = this.phaseManager.findPhase(
mp => mp =>
mp.is("MovePhase") mp.is("MovePhase") &&
&& mp.targets.length === 1 mp.targets.length === 1 &&
&& mp.targets[0] === removedPokemon.getBattlerIndex() mp.targets[0] === removedPokemon.getBattlerIndex() &&
&& mp.pokemon.isPlayer() !== allyPokemon.isPlayer(), mp.pokemon.isPlayer() !== allyPokemon.isPlayer(),
) as MovePhase; ) as MovePhase;
if (targetingMovePhase && targetingMovePhase.targets[0] !== allyPokemon.getBattlerIndex()) { if (targetingMovePhase && targetingMovePhase.targets[0] !== allyPokemon.getBattlerIndex()) {
targetingMovePhase.targets[0] = allyPokemon.getBattlerIndex(); targetingMovePhase.targets[0] = allyPokemon.getBattlerIndex();
@ -1204,9 +1203,7 @@ export class BattleScene extends SceneBase {
this.updateScoreText(); this.updateScoreText();
this.scoreText.setVisible(false); this.scoreText.setVisible(false);
[this.luckLabelText, this.luckText].forEach(t => { [this.luckLabelText, this.luckText].map(t => t.setVisible(false));
t.setVisible(false);
});
this.newArena(Overrides.STARTING_BIOME_OVERRIDE || BiomeId.TOWN); this.newArena(Overrides.STARTING_BIOME_OVERRIDE || BiomeId.TOWN);
@ -1240,7 +1237,8 @@ export class BattleScene extends SceneBase {
Object.values(mp) Object.values(mp)
.flat() .flat()
.map(mt => mt.modifierType) .map(mt => mt.modifierType)
.filter((mt): mt is ModifierType & Localizable => "localize" in mt && typeof mt.localize === "function"), .filter(mt => "localize" in mt)
.map(lpb => lpb as unknown as Localizable),
), ),
]; ];
for (const item of localizable) { for (const item of localizable) {
@ -1322,16 +1320,16 @@ export class BattleScene extends SceneBase {
} }
} else { } else {
if ( if (
!this.gameMode.hasTrainers !this.gameMode.hasTrainers ||
|| Overrides.BATTLE_TYPE_OVERRIDE === BattleType.WILD Overrides.BATTLE_TYPE_OVERRIDE === BattleType.WILD ||
|| (Overrides.DISABLE_STANDARD_TRAINERS_OVERRIDE && isNullOrUndefined(trainerData)) (Overrides.DISABLE_STANDARD_TRAINERS_OVERRIDE && isNullOrUndefined(trainerData))
) { ) {
newBattleType = BattleType.WILD; newBattleType = BattleType.WILD;
} else { } else {
newBattleType = newBattleType =
Overrides.BATTLE_TYPE_OVERRIDE Overrides.BATTLE_TYPE_OVERRIDE ??
?? battleType battleType ??
?? (this.gameMode.isWaveTrainer(newWaveIndex, this.arena) ? BattleType.TRAINER : BattleType.WILD); (this.gameMode.isWaveTrainer(newWaveIndex, this.arena) ? BattleType.TRAINER : BattleType.WILD);
} }
if (newBattleType === BattleType.TRAINER) { if (newBattleType === BattleType.TRAINER) {
@ -1342,12 +1340,12 @@ export class BattleScene extends SceneBase {
doubleTrainer = true; doubleTrainer = true;
} else if (trainerConfigs[trainerType].hasDouble) { } else if (trainerConfigs[trainerType].hasDouble) {
doubleTrainer = doubleTrainer =
Overrides.RANDOM_TRAINER_OVERRIDE?.alwaysDouble Overrides.RANDOM_TRAINER_OVERRIDE?.alwaysDouble ||
|| !randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField)); !randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField));
// Add a check that special trainers can't be double except for tate and liza - they should use the normal double chance // Add a check that special trainers can't be double except for tate and liza - they should use the normal double chance
if ( if (
trainerConfigs[trainerType].trainerTypeDouble trainerConfigs[trainerType].trainerTypeDouble &&
&& ![TrainerType.TATE, TrainerType.LIZA].includes(trainerType) ![TrainerType.TATE, TrainerType.LIZA].includes(trainerType)
) { ) {
doubleTrainer = false; doubleTrainer = false;
} }
@ -1364,8 +1362,8 @@ export class BattleScene extends SceneBase {
// Check for mystery encounter // Check for mystery encounter
// Can only occur in place of a standard (non-boss) wild battle, waves 10-180 // Can only occur in place of a standard (non-boss) wild battle, waves 10-180
if ( if (
!Overrides.BATTLE_TYPE_OVERRIDE !Overrides.BATTLE_TYPE_OVERRIDE &&
&& (this.isWaveMysteryEncounter(newBattleType, newWaveIndex) || newBattleType === BattleType.MYSTERY_ENCOUNTER) (this.isWaveMysteryEncounter(newBattleType, newWaveIndex) || newBattleType === BattleType.MYSTERY_ENCOUNTER)
) { ) {
newBattleType = BattleType.MYSTERY_ENCOUNTER; newBattleType = BattleType.MYSTERY_ENCOUNTER;
// Reset to base spawn weight // Reset to base spawn weight
@ -1455,9 +1453,9 @@ export class BattleScene extends SceneBase {
const isNewBiome = this.isNewBiome(lastBattle); const isNewBiome = this.isNewBiome(lastBattle);
/** Whether to reset and recall pokemon */ /** Whether to reset and recall pokemon */
const resetArenaState = const resetArenaState =
isNewBiome isNewBiome ||
|| [BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(this.currentBattle.battleType) [BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(this.currentBattle.battleType) ||
|| this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS; this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
for (const enemyPokemon of this.getEnemyParty()) { for (const enemyPokemon of this.getEnemyParty()) {
enemyPokemon.destroy(); enemyPokemon.destroy();
@ -1484,8 +1482,8 @@ export class BattleScene extends SceneBase {
pokemon.resetTera(); pokemon.resetTera();
applyAbAttrs("PostBattleInitAbAttr", { pokemon }); applyAbAttrs("PostBattleInitAbAttr", { pokemon });
if ( if (
pokemon.hasSpecies(SpeciesId.TERAPAGOS) pokemon.hasSpecies(SpeciesId.TERAPAGOS) ||
|| (this.gameMode.isClassic && this.currentBattle.waveIndex > 180 && this.currentBattle.waveIndex <= 190) (this.gameMode.isClassic && this.currentBattle.waveIndex > 180 && this.currentBattle.waveIndex <= 190)
) { ) {
this.arena.playerTerasUsed = 0; this.arena.playerTerasUsed = 0;
} }
@ -1515,8 +1513,8 @@ export class BattleScene extends SceneBase {
return this.currentBattle; return this.currentBattle;
} }
newArena(biome: BiomeId, playerFaints = 0): Arena { newArena(biome: BiomeId, playerFaints?: number): Arena {
this.arena = new Arena(biome, playerFaints); this.arena = new Arena(biome, BiomeId[biome].toLowerCase(), playerFaints);
this.eventTarget.dispatchEvent(new NewArenaEvent()); this.eventTarget.dispatchEvent(new NewArenaEvent());
this.arenaBg.pipelineData = { this.arenaBg.pipelineData = {
@ -1531,8 +1529,8 @@ export class BattleScene extends SceneBase {
const fieldScale = const fieldScale =
Math.floor( Math.floor(
Math.pow( Math.pow(
1 1 /
/ this.getField(true) this.getField(true)
.map(p => p.getSpriteScale()) .map(p => p.getSpriteScale())
.reduce((highestScale: number, scale: number) => (highestScale = Math.max(scale, highestScale)), 0), .reduce((highestScale: number, scale: number) => (highestScale = Math.max(scale, highestScale)), 0),
0.7, 0.7,
@ -1556,7 +1554,7 @@ export class BattleScene extends SceneBase {
this.tweens.add({ this.tweens.add({
targets: this.field, targets: this.field,
scale, scale: scale,
x: (defaultWidth - scaledWidth) / 2, x: (defaultWidth - scaledWidth) / 2,
y: defaultHeight - scaledHeight, y: defaultHeight - scaledHeight,
duration: !instant ? fixedInt(Math.abs(this.field.scale - scale) * 200) : 0, duration: !instant ? fixedInt(Math.abs(this.field.scale - scale) * 200) : 0,
@ -1577,10 +1575,10 @@ export class BattleScene extends SceneBase {
if ( if (
// Give trainers with specialty types an appropriately-typed form for Wormadam, Rotom, Arceus, Oricorio, Silvally, or Paldean Tauros. // Give trainers with specialty types an appropriately-typed form for Wormadam, Rotom, Arceus, Oricorio, Silvally, or Paldean Tauros.
!isEggPhase !isEggPhase &&
&& this.currentBattle?.battleType === BattleType.TRAINER this.currentBattle?.battleType === BattleType.TRAINER &&
&& !isNullOrUndefined(this.currentBattle.trainer) !isNullOrUndefined(this.currentBattle.trainer) &&
&& this.currentBattle.trainer.config.hasSpecialtyType() this.currentBattle.trainer.config.hasSpecialtyType()
) { ) {
if (species.speciesId === SpeciesId.WORMADAM) { if (species.speciesId === SpeciesId.WORMADAM) {
switch (this.currentBattle.trainer.config.specialtyType) { switch (this.currentBattle.trainer.config.specialtyType) {
@ -1672,9 +1670,9 @@ export class BattleScene extends SceneBase {
return randSeedInt(8); return randSeedInt(8);
case SpeciesId.EEVEE: case SpeciesId.EEVEE:
if ( if (
this.currentBattle?.battleType === BattleType.TRAINER this.currentBattle?.battleType === BattleType.TRAINER &&
&& this.currentBattle?.waveIndex < 30 this.currentBattle?.waveIndex < 30 &&
&& !isEggPhase !isEggPhase
) { ) {
return 0; // No Partner Eevee for Wave 12 Preschoolers return 0; // No Partner Eevee for Wave 12 Preschoolers
} }
@ -1784,9 +1782,9 @@ export class BattleScene extends SceneBase {
} else { } else {
this.executeWithSeedOffset(() => { this.executeWithSeedOffset(() => {
isBoss = isBoss =
waveIndex % 10 === 0 waveIndex % 10 === 0 ||
|| (this.gameMode.hasRandomBosses (this.gameMode.hasRandomBosses &&
&& randSeedInt(100) < Math.min(Math.max(Math.ceil((waveIndex - 250) / 50), 0) * 2, 30)); randSeedInt(100) < Math.min(Math.max(Math.ceil((waveIndex - 250) / 50), 0) * 2, 30));
}, waveIndex << 2); }, waveIndex << 2);
} }
if (!isBoss) { if (!isBoss) {
@ -1900,8 +1898,8 @@ export class BattleScene extends SceneBase {
): Phaser.GameObjects.Sprite { ): Phaser.GameObjects.Sprite {
sprite.setPipeline(this.spritePipeline, { sprite.setPipeline(this.spritePipeline, {
tone: [0.0, 0.0, 0.0, 0.0], tone: [0.0, 0.0, 0.0, 0.0],
hasShadow, hasShadow: hasShadow,
ignoreOverride, ignoreOverride: ignoreOverride,
teraColor: pokemon ? getTypeRgb(pokemon.getTeraType()) : undefined, teraColor: pokemon ? getTypeRgb(pokemon.getTeraType()) : undefined,
isTerastallized: pokemon ? pokemon.isTerastallized : false, isTerastallized: pokemon ? pokemon.isTerastallized : false,
}); });
@ -1922,7 +1920,7 @@ export class BattleScene extends SceneBase {
targets: this.fieldOverlay, targets: this.fieldOverlay,
alpha: 0.5, alpha: 0.5,
ease: "Sine.easeOut", ease: "Sine.easeOut",
duration, duration: duration,
onComplete: () => resolve(), onComplete: () => resolve(),
}); });
}); });
@ -1933,7 +1931,7 @@ export class BattleScene extends SceneBase {
this.tweens.add({ this.tweens.add({
targets: this.fieldOverlay, targets: this.fieldOverlay,
alpha: 0, alpha: 0,
duration, duration: duration,
ease: "Cubic.easeIn", ease: "Cubic.easeIn",
onComplete: () => resolve(), onComplete: () => resolve(),
}); });
@ -1967,7 +1965,7 @@ export class BattleScene extends SceneBase {
this.tweens.add({ this.tweens.add({
targets: this.shopOverlay, targets: this.shopOverlay,
alpha: 0, alpha: 0,
duration, duration: duration,
ease: "Cubic.easeIn", ease: "Cubic.easeIn",
onComplete: () => resolve(), onComplete: () => resolve(),
}); });
@ -2044,7 +2042,7 @@ export class BattleScene extends SceneBase {
this.luckLabelText.setX(this.scaledCanvas.width - 2 - (this.luckText.displayWidth + 2)); this.luckLabelText.setX(this.scaledCanvas.width - 2 - (this.luckText.displayWidth + 2));
this.tweens.add({ this.tweens.add({
targets: labels, targets: labels,
duration, duration: duration,
alpha: 1, alpha: 1,
onComplete: () => { onComplete: () => {
for (const label of labels) { for (const label of labels) {
@ -2061,7 +2059,7 @@ export class BattleScene extends SceneBase {
const labels = [this.luckLabelText, this.luckText]; const labels = [this.luckLabelText, this.luckText];
this.tweens.add({ this.tweens.add({
targets: labels, targets: labels,
duration, duration: duration,
alpha: 0, alpha: 0,
onComplete: () => { onComplete: () => {
for (const label of labels) { for (const label of labels) {
@ -2075,9 +2073,9 @@ export class BattleScene extends SceneBase {
const enemyModifierCount = this.enemyModifiers.filter(m => m.isIconVisible()).length; const enemyModifierCount = this.enemyModifiers.filter(m => m.isIconVisible()).length;
const biomeWaveTextHeight = this.biomeWaveText.getBottomLeft().y - this.biomeWaveText.getTopLeft().y; const biomeWaveTextHeight = this.biomeWaveText.getBottomLeft().y - this.biomeWaveText.getTopLeft().y;
this.biomeWaveText.setY( this.biomeWaveText.setY(
-this.scaledCanvas.height -this.scaledCanvas.height +
+ (enemyModifierCount ? (enemyModifierCount <= 12 ? 15 : 24) : 0) (enemyModifierCount ? (enemyModifierCount <= 12 ? 15 : 24) : 0) +
+ biomeWaveTextHeight / 2, biomeWaveTextHeight / 2,
); );
this.moneyText.setY(this.biomeWaveText.y + 10); this.moneyText.setY(this.biomeWaveText.y + 10);
this.scoreText.setY(this.moneyText.y + 10); this.scoreText.setY(this.moneyText.y + 10);
@ -2101,9 +2099,9 @@ export class BattleScene extends SceneBase {
addFaintedEnemyScore(enemy: EnemyPokemon): void { addFaintedEnemyScore(enemy: EnemyPokemon): void {
let scoreIncrease = let scoreIncrease =
enemy.getSpeciesForm().getBaseExp() enemy.getSpeciesForm().getBaseExp() *
* (enemy.level / this.getMaxExpLevel()) (enemy.level / this.getMaxExpLevel()) *
* ((enemy.ivs.reduce((iv: number, total: number) => (total += iv), 0) / 93) * 0.2 + 0.8); ((enemy.ivs.reduce((iv: number, total: number) => (total += iv), 0) / 93) * 0.2 + 0.8);
this.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemy.id, false).map( this.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemy.id, false).map(
m => (scoreIncrease *= (m as PokemonHeldItemModifier).getScoreMultiplier()), m => (scoreIncrease *= (m as PokemonHeldItemModifier).getScoreMultiplier()),
); );
@ -2621,8 +2619,8 @@ export class BattleScene extends SceneBase {
const waveIndex = this.currentBattle.waveIndex; const waveIndex = this.currentBattle.waveIndex;
const waveSetIndex = Math.ceil(waveIndex / 10) - 1; const waveSetIndex = Math.ceil(waveIndex / 10) - 1;
const moneyValue = const moneyValue =
Math.pow((waveSetIndex + 1 + (0.75 + (((waveIndex - 1) % 10) + 1) / 10)) * 100, 1 + 0.005 * waveSetIndex) Math.pow((waveSetIndex + 1 + (0.75 + (((waveIndex - 1) % 10) + 1) / 10)) * 100, 1 + 0.005 * waveSetIndex) *
* moneyMultiplier; moneyMultiplier;
return Math.floor(moneyValue / 10) * 10; return Math.floor(moneyValue / 10) * 10;
} }
@ -2706,9 +2704,7 @@ export class BattleScene extends SceneBase {
} }
} }
this.party.forEach(p => { this.party.map(p => p.updateInfo(instant));
p.updateInfo(instant);
});
} else { } else {
const args = [this]; const args = [this];
if (modifier.shouldApply(...args)) { if (modifier.shouldApply(...args)) {
@ -2936,8 +2932,8 @@ export class BattleScene extends SceneBase {
} }
} else { } else {
const isBoss = const isBoss =
enemyPokemon.isBoss() enemyPokemon.isBoss() ||
|| (this.currentBattle.battleType === BattleType.TRAINER && !!this.currentBattle.trainer?.config.isBoss); (this.currentBattle.battleType === BattleType.TRAINER && !!this.currentBattle.trainer?.config.isBoss);
let upgradeChance = 32; let upgradeChance = 32;
if (isBoss) { if (isBoss) {
upgradeChance /= 2; upgradeChance /= 2;
@ -3006,15 +3002,15 @@ export class BattleScene extends SceneBase {
for (let m = 0; m < modifiers.length; m++) { for (let m = 0; m < modifiers.length; m++) {
const modifier = modifiers[m]; const modifier = modifiers[m];
if ( if (
modifier instanceof PokemonHeldItemModifier modifier instanceof PokemonHeldItemModifier &&
&& !this.getPokemonById((modifier as PokemonHeldItemModifier).pokemonId) !this.getPokemonById((modifier as PokemonHeldItemModifier).pokemonId)
) { ) {
modifiers.splice(m--, 1); modifiers.splice(m--, 1);
} }
if ( if (
modifier instanceof PokemonHeldItemModifier modifier instanceof PokemonHeldItemModifier &&
&& !isNullOrUndefined(modifier.getSpecies()) !isNullOrUndefined(modifier.getSpecies()) &&
&& !this.getPokemonById(modifier.pokemonId)?.hasSpecies(modifier.getSpecies()!) !this.getPokemonById(modifier.pokemonId)?.hasSpecies(modifier.getSpecies()!)
) { ) {
modifiers.splice(m--, 1); modifiers.splice(m--, 1);
} }
@ -3278,8 +3274,8 @@ export class BattleScene extends SceneBase {
validateAchv(achv: Achv, args?: unknown[]): boolean { validateAchv(achv: Achv, args?: unknown[]): boolean {
if ( if (
(!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE) (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE) &&
&& achv.validate(args) achv.validate(args)
) { ) {
this.gameData.achvUnlocks[achv.id] = Date.now(); this.gameData.achvUnlocks[achv.id] = Date.now();
this.ui.achvBar.showAchv(achv); this.ui.achvBar.showAchv(achv);
@ -3417,8 +3413,8 @@ export class BattleScene extends SceneBase {
if (participantIds.size > 0) { if (participantIds.size > 0) {
if ( if (
this.currentBattle.battleType === BattleType.TRAINER this.currentBattle.battleType === BattleType.TRAINER ||
|| this.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE this.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE
) { ) {
expValue = Math.floor(expValue * 1.5); expValue = Math.floor(expValue * 1.5);
} else if (this.currentBattle.isBattleMysteryEncounter() && this.currentBattle.mysteryEncounter) { } else if (this.currentBattle.isBattleMysteryEncounter() && this.currentBattle.mysteryEncounter) {
@ -3514,12 +3510,12 @@ export class BattleScene extends SceneBase {
isMysteryEncounterValidForWave(battleType: BattleType, waveIndex: number): boolean { isMysteryEncounterValidForWave(battleType: BattleType, waveIndex: number): boolean {
const [lowestMysteryEncounterWave, highestMysteryEncounterWave] = this.gameMode.getMysteryEncounterLegalWaves(); const [lowestMysteryEncounterWave, highestMysteryEncounterWave] = this.gameMode.getMysteryEncounterLegalWaves();
return ( return (
this.gameMode.hasMysteryEncounters this.gameMode.hasMysteryEncounters &&
&& battleType === BattleType.WILD battleType === BattleType.WILD &&
&& !this.gameMode.isBoss(waveIndex) !this.gameMode.isBoss(waveIndex) &&
&& waveIndex % 10 !== 1 waveIndex % 10 !== 1 &&
&& waveIndex < highestMysteryEncounterWave waveIndex < highestMysteryEncounterWave &&
&& waveIndex > lowestMysteryEncounterWave waveIndex > lowestMysteryEncounterWave
); );
} }
@ -3541,12 +3537,12 @@ export class BattleScene extends SceneBase {
// Reduces occurrence of runs with total encounters significantly different from AVERAGE_ENCOUNTERS_PER_RUN_TARGET // Reduces occurrence of runs with total encounters significantly different from AVERAGE_ENCOUNTERS_PER_RUN_TARGET
// Favored rate changes can never exceed 50%. So if base rate is 15/256 and favored rate would add 200/256, result will be (15 + 128)/256 // Favored rate changes can never exceed 50%. So if base rate is 15/256 and favored rate would add 200/256, result will be (15 + 128)/256
const expectedEncountersByFloor = const expectedEncountersByFloor =
(AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave)) (AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave)) *
* (waveIndex - lowestMysteryEncounterWave); (waveIndex - lowestMysteryEncounterWave);
const currentRunDiffFromAvg = expectedEncountersByFloor - encounteredEvents.length; const currentRunDiffFromAvg = expectedEncountersByFloor - encounteredEvents.length;
const favoredEncounterRate = const favoredEncounterRate =
sessionEncounterRate sessionEncounterRate +
+ Math.min(currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT / 2); Math.min(currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT / 2);
const successRate = Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE ?? favoredEncounterRate; const successRate = Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE ?? favoredEncounterRate;
@ -3580,8 +3576,8 @@ export class BattleScene extends SceneBase {
// Loading override or session encounter // Loading override or session encounter
let encounter: MysteryEncounter | null; let encounter: MysteryEncounter | null;
if ( if (
!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) !isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) &&
&& allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)
) { ) {
encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE]; encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE];
if (canBypass) { if (canBypass) {
@ -3596,9 +3592,9 @@ export class BattleScene extends SceneBase {
// Check for queued encounters first // Check for queued encounters first
if ( if (
!encounter !encounter &&
&& this.mysteryEncounterSaveData?.queuedEncounters this.mysteryEncounterSaveData?.queuedEncounters &&
&& this.mysteryEncounterSaveData.queuedEncounters.length > 0 this.mysteryEncounterSaveData.queuedEncounters.length > 0
) { ) {
let i = 0; let i = 0;
while (i < this.mysteryEncounterSaveData.queuedEncounters.length && !!encounter) { while (i < this.mysteryEncounterSaveData.queuedEncounters.length && !!encounter) {
@ -3678,18 +3674,18 @@ export class BattleScene extends SceneBase {
} }
const disallowedGameModes = encounterCandidate.disallowedGameModes; const disallowedGameModes = encounterCandidate.disallowedGameModes;
if ( if (
disallowedGameModes disallowedGameModes &&
&& disallowedGameModes.length > 0 disallowedGameModes.length > 0 &&
&& disallowedGameModes.includes(this.gameMode.modeId) disallowedGameModes.includes(this.gameMode.modeId)
) { ) {
return false; return false;
} }
if (this.gameMode.modeId === GameModes.CHALLENGE) { if (this.gameMode.modeId === GameModes.CHALLENGE) {
const disallowedChallenges = encounterCandidate.disallowedChallenges; const disallowedChallenges = encounterCandidate.disallowedChallenges;
if ( if (
disallowedChallenges disallowedChallenges &&
&& disallowedChallenges.length > 0 disallowedChallenges.length > 0 &&
&& this.gameMode.challenges.some(challenge => disallowedChallenges.includes(challenge.id)) this.gameMode.challenges.some(challenge => disallowedChallenges.includes(challenge.id))
) { ) {
return false; return false;
} }
@ -3701,11 +3697,11 @@ export class BattleScene extends SceneBase {
return false; return false;
} }
return !( return !(
this.mysteryEncounterSaveData.encounteredEvents.length > 0 this.mysteryEncounterSaveData.encounteredEvents.length > 0 &&
&& encounterCandidate.maxAllowedEncounters encounterCandidate.maxAllowedEncounters &&
&& encounterCandidate.maxAllowedEncounters > 0 encounterCandidate.maxAllowedEncounters > 0 &&
&& this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length >=
>= encounterCandidate.maxAllowedEncounters encounterCandidate.maxAllowedEncounters
); );
}) })
.map(m => allMysteryEncounters[m]); .map(m => allMysteryEncounters[m]);

View File

@ -237,8 +237,8 @@ export class Battle {
return null; return null;
} }
if ( if (
this.battleType === BattleType.TRAINER this.battleType === BattleType.TRAINER ||
|| this.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE this.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE
) { ) {
if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) { if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) {
return `encounter_${this.trainer?.getEncounterBgm()}`; return `encounter_${this.trainer?.getEncounterBgm()}`;

View File

@ -116,8 +116,8 @@ export function getIconForLatestInput(configs, source, devices, settingName) {
export function assign(config, settingNameTarget, keycode): boolean { export function assign(config, settingNameTarget, keycode): boolean {
// first, we need to check if this keycode is already used on another settingName // first, we need to check if this keycode is already used on another settingName
if ( if (
!canIAssignThisKey(config, getKeyWithKeycode(config, keycode)) !canIAssignThisKey(config, getKeyWithKeycode(config, keycode)) ||
|| !canIOverrideThisSetting(config, settingNameTarget) !canIOverrideThisSetting(config, settingNameTarget)
) { ) {
return false; return false;
} }

View File

@ -74,7 +74,6 @@ import {
randSeedItem, randSeedItem,
toDmgValue, toDmgValue,
} from "#utils/common"; } from "#utils/common";
import { toCamelCase } from "#utils/strings";
import i18next from "i18next"; import i18next from "i18next";
export class Ability implements Localizable { export class Ability implements Localizable {
@ -110,9 +109,13 @@ export class Ability implements Localizable {
} }
localize(): void { localize(): void {
const i18nKey = toCamelCase(AbilityId[this.id]); const i18nKey = AbilityId[this.id]
.split("_")
.filter(f => f)
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
.join("") as string;
this.name = this.id ? `${i18next.t(`ability:${i18nKey}.name`)}${this.nameAppend}` : ""; this.name = this.id ? `${i18next.t(`ability:${i18nKey}.name`) as string}${this.nameAppend}` : "";
this.description = this.id ? (i18next.t(`ability:${i18nKey}.description`) as string) : ""; this.description = this.id ? (i18next.t(`ability:${i18nKey}.description`) as string) : "";
} }
@ -575,10 +578,13 @@ export abstract class PreDefendAbAttr extends AbAttr {
export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr { export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr {
override canApply({ pokemon, damage }: PreDefendModifyDamageAbAttrParams): boolean { override canApply({ pokemon, damage }: PreDefendModifyDamageAbAttrParams): boolean {
return ( return (
pokemon.isFullHp() // Checks if pokemon has wonder_guard (which forces 1hp) pokemon.isFullHp() &&
&& pokemon.getMaxHp() > 1 // Damage >= hp // Checks if pokemon has wonder_guard (which forces 1hp)
&& damage.value >= pokemon.hp // Cannot apply if the pokemon already has sturdy from some other source pokemon.getMaxHp() > 1 &&
&& !pokemon.getTag(BattlerTagType.STURDY) // Damage >= hp
damage.value >= pokemon.hp &&
// Cannot apply if the pokemon already has sturdy from some other source
!pokemon.getTag(BattlerTagType.STURDY)
); );
} }
@ -696,9 +702,9 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr {
override canApply({ move, opponent: attacker, pokemon }: TypeMultiplierAbAttrParams): boolean { override canApply({ move, opponent: attacker, pokemon }: TypeMultiplierAbAttrParams): boolean {
return ( return (
![MoveTarget.BOTH_SIDES, MoveTarget.ENEMY_SIDE, MoveTarget.USER_SIDE].includes(move.moveTarget) ![MoveTarget.BOTH_SIDES, MoveTarget.ENEMY_SIDE, MoveTarget.USER_SIDE].includes(move.moveTarget) &&
&& attacker !== pokemon attacker !== pokemon &&
&& attacker.getMoveType(move) === this.immuneType attacker.getMoveType(move) === this.immuneType
); );
} }
@ -724,9 +730,9 @@ export class AttackTypeImmunityAbAttr extends TypeImmunityAbAttr {
override canApply(params: TypeMultiplierAbAttrParams): boolean { override canApply(params: TypeMultiplierAbAttrParams): boolean {
const { move } = params; const { move } = params;
return ( return (
move.category !== MoveCategory.STATUS move.category !== MoveCategory.STATUS &&
&& !move.hasAttr("NeutralDamageAgainstFlyingTypeMultiplierAttr") !move.hasAttr("NeutralDamageAgainstFlyingTypeMultiplierAttr") &&
&& super.canApply(params) super.canApply(params)
); );
} }
} }
@ -838,10 +844,10 @@ export class FullHpResistTypeAbAttr extends PreDefendAbAttr {
*/ */
override canApply({ typeMultiplier, move, pokemon }: TypeMultiplierAbAttrParams): boolean { override canApply({ typeMultiplier, move, pokemon }: TypeMultiplierAbAttrParams): boolean {
return ( return (
typeMultiplier instanceof NumberHolder typeMultiplier instanceof NumberHolder &&
&& !move?.hasAttr("FixedDamageAttr") !move?.hasAttr("FixedDamageAttr") &&
&& pokemon.isFullHp() pokemon.isFullHp() &&
&& typeMultiplier.value > 0.5 typeMultiplier.value > 0.5
); );
} }
@ -868,9 +874,9 @@ export interface FieldPriorityMoveImmunityAbAttrParams extends AugmentMoveIntera
export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr {
override canApply({ move, opponent: attacker }: FieldPriorityMoveImmunityAbAttrParams): boolean { override canApply({ move, opponent: attacker }: FieldPriorityMoveImmunityAbAttrParams): boolean {
return ( return (
!(move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY) !(move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY) &&
&& move.getPriority(attacker) > 0 move.getPriority(attacker) > 0 &&
&& !move.isMultiTarget() !move.isMultiTarget()
); );
} }
@ -1112,8 +1118,8 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr {
override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean { override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean {
const tag = globalScene.arena.getTag(this.arenaTagType) as ArenaTrapTag; const tag = globalScene.arena.getTag(this.arenaTagType) as ArenaTrapTag;
return ( return (
this.condition(pokemon, attacker, move) this.condition(pokemon, attacker, move) &&
&& (!globalScene.arena.getTag(this.arenaTagType) || tag.layers < tag.maxLayers) (!globalScene.arena.getTag(this.arenaTagType) || tag.layers < tag.maxLayers)
); );
} }
@ -1221,10 +1227,10 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
const effect = const effect =
this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)];
return ( return (
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
&& !attacker.status !attacker.status &&
&& (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance) (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance) &&
&& attacker.canSetStatus(effect, true, false, pokemon) attacker.canSetStatus(effect, true, false, pokemon)
); );
} }
@ -1262,9 +1268,9 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
override canApply({ move, pokemon, opponent: attacker }: PostMoveInteractionAbAttrParams): boolean { override canApply({ move, pokemon, opponent: attacker }: PostMoveInteractionAbAttrParams): boolean {
return ( return (
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
&& pokemon.randBattleSeedInt(100) < this.chance pokemon.randBattleSeedInt(100) < this.chance &&
&& attacker.canAddTag(this.tagType) attacker.canAddTag(this.tagType)
); );
} }
@ -1319,9 +1325,9 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr {
override canApply({ simulated, move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { override canApply({ simulated, move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
return ( return (
!simulated !simulated &&
&& move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
&& !attacker.hasAbilityWithAttr("BlockNonDirectDamageAbAttr") !attacker.hasAbilityWithAttr("BlockNonDirectDamageAbAttr")
); );
} }
@ -1355,8 +1361,8 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr {
override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
return ( return (
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
&& !attacker.getTag(BattlerTagType.PERISH_SONG) !attacker.getTag(BattlerTagType.PERISH_SONG)
); );
} }
@ -1370,7 +1376,7 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr {
override getTriggerMessage({ pokemon }: PostMoveInteractionAbAttrParams, abilityName: string): string { override getTriggerMessage({ pokemon }: PostMoveInteractionAbAttrParams, abilityName: string): string {
return i18next.t("abilityTriggers:perishBody", { return i18next.t("abilityTriggers:perishBody", {
pokemonName: getPokemonNameWithAffix(pokemon), pokemonName: getPokemonNameWithAffix(pokemon),
abilityName, abilityName: abilityName,
}); });
} }
} }
@ -1388,9 +1394,9 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr {
override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean { override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean {
return ( return (
!(this.condition && !this.condition(pokemon, attacker, move)) !(this.condition && !this.condition(pokemon, attacker, move)) &&
&& !globalScene.arena.weather?.isImmutable() !globalScene.arena.weather?.isImmutable() &&
&& globalScene.arena.canSetWeather(this.weatherType) globalScene.arena.canSetWeather(this.weatherType)
); );
} }
@ -1404,8 +1410,8 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr {
export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr {
override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
return ( return (
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
&& attacker.getAbility().isSwappable attacker.getAbility().isSwappable
); );
} }
@ -1434,9 +1440,9 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
return ( return (
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
&& attacker.getAbility().isSuppressable attacker.getAbility().isSuppressable &&
&& !attacker.getAbility().hasAttr("PostDefendAbilityGiveAbAttr") !attacker.getAbility().hasAttr("PostDefendAbilityGiveAbAttr")
); );
} }
@ -1466,9 +1472,9 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
return ( return (
isNullOrUndefined(attacker.getTag(BattlerTagType.DISABLED)) isNullOrUndefined(attacker.getTag(BattlerTagType.DISABLED)) &&
&& move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
&& (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance) (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance)
); );
} }
@ -1633,10 +1639,10 @@ export class FieldMultiplyStatAbAttr extends AbAttr {
canApply({ hasApplied, target, stat }: FieldMultiplyStatAbAttrParams): boolean { canApply({ hasApplied, target, stat }: FieldMultiplyStatAbAttrParams): boolean {
return ( return (
this.canStack this.canStack ||
|| (!hasApplied.value (!hasApplied.value &&
&& this.stat === stat this.stat === stat &&
&& target.getAbilityAttrs("FieldMultiplyStatAbAttr").every(attr => attr.stat !== stat)) target.getAbilityAttrs("FieldMultiplyStatAbAttr").every(attr => attr.stat !== stat))
); );
} }
@ -1661,7 +1667,6 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr {
constructor( constructor(
private newType: PokemonType, private newType: PokemonType,
private powerMultiplier: number, private powerMultiplier: number,
// TODO: all moves with this attr solely check the move being used...
private condition?: PokemonAttackCondition, private condition?: PokemonAttackCondition,
) { ) {
super(false); super(false);
@ -1678,14 +1683,14 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr {
*/ */
override canApply({ pokemon, opponent: target, move }: MoveTypeChangeAbAttrParams): boolean { override canApply({ pokemon, opponent: target, move }: MoveTypeChangeAbAttrParams): boolean {
return ( return (
(!this.condition || this.condition(pokemon, target, move)) (!this.condition || this.condition(pokemon, target, move)) &&
&& !noAbilityTypeOverrideMoves.has(move.id) !noAbilityTypeOverrideMoves.has(move.id) &&
&& !( !(
pokemon.isTerastallized pokemon.isTerastallized &&
&& (move.id === MoveId.TERA_BLAST (move.id === MoveId.TERA_BLAST ||
|| (move.id === MoveId.TERA_STARSTORM (move.id === MoveId.TERA_STARSTORM &&
&& pokemon.getTeraType() === PokemonType.STELLAR pokemon.getTeraType() === PokemonType.STELLAR &&
&& pokemon.hasSpecies(SpeciesId.TERAPAGOS))) pokemon.hasSpecies(SpeciesId.TERAPAGOS)))
) )
); );
} }
@ -1708,13 +1713,14 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr {
override canApply({ move, pokemon }: AugmentMoveInteractionAbAttrParams): boolean { override canApply({ move, pokemon }: AugmentMoveInteractionAbAttrParams): boolean {
if ( if (
pokemon.isTerastallized pokemon.isTerastallized ||
|| move.id === MoveId.STRUGGLE /* move.id === MoveId.STRUGGLE ||
/*
* Skip moves that call other moves because these moves generate a following move that will trigger this ability attribute * Skip moves that call other moves because these moves generate a following move that will trigger this ability attribute
* See: https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_call_other_moves * See: https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_call_other_moves
*/ */
|| move.hasAttr("CallMoveAttr") move.hasAttr("CallMoveAttr") ||
|| move.hasAttr("NaturePowerAttr") // TODO: remove this line when nature power is made to extend from `CallMoveAttr` move.hasAttr("NaturePowerAttr") // TODO: remove this line when nature power is made to extend from `CallMoveAttr`
) { ) {
return false; return false;
} }
@ -2142,10 +2148,10 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
// The PostAttackAbAttr should should only be invoked in cases where the move successfully connected, // The PostAttackAbAttr should should only be invoked in cases where the move successfully connected,
// calling `super.canApply` already checks that the move was a damage move and not a status move. // calling `super.canApply` already checks that the move was a damage move and not a status move.
if ( if (
super.canApply(params) super.canApply(params) &&
&& !simulated !simulated &&
&& hitResult < HitResult.NO_EFFECT hitResult < HitResult.NO_EFFECT &&
&& (!this.stealCondition || this.stealCondition(pokemon, opponent, move)) (!this.stealCondition || this.stealCondition(pokemon, opponent, move))
) { ) {
const heldItems = this.getTargetHeldItems(opponent).filter(i => i.isTransferable); const heldItems = this.getTargetHeldItems(opponent).filter(i => i.isTransferable);
if (heldItems.length) { if (heldItems.length) {
@ -2201,14 +2207,14 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr {
override canApply(params: PostMoveInteractionAbAttrParams): boolean { override canApply(params: PostMoveInteractionAbAttrParams): boolean {
const { simulated, pokemon, move, opponent } = params; const { simulated, pokemon, move, opponent } = params;
if ( if (
super.canApply(params) super.canApply(params) &&
&& (simulated (simulated ||
|| (!opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") (!opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") &&
&& pokemon !== opponent pokemon !== opponent &&
&& (!this.contactRequired (!this.contactRequired ||
|| move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: pokemon, target: opponent })) move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: pokemon, target: opponent })) &&
&& pokemon.randBattleSeedInt(100) < this.chance pokemon.randBattleSeedInt(100) < this.chance &&
&& !pokemon.status)) !pokemon.status))
) { ) {
const effect = const effect =
this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)];
@ -2252,13 +2258,13 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr {
const { pokemon, move, opponent } = params; const { pokemon, move, opponent } = params;
/**Battler tags inflicted by abilities post attacking are also considered additional effects.*/ /**Battler tags inflicted by abilities post attacking are also considered additional effects.*/
return ( return (
super.canApply(params) super.canApply(params) &&
&& !opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") !opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") &&
&& pokemon !== opponent pokemon !== opponent &&
&& (!this.contactRequired (!this.contactRequired ||
|| move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: opponent, target: pokemon })) move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: opponent, target: pokemon })) &&
&& pokemon.randBattleSeedInt(100) < this.chance(opponent, pokemon, move) pokemon.randBattleSeedInt(100) < this.chance(opponent, pokemon, move) &&
&& !pokemon.status !pokemon.status
); );
} }
@ -2915,10 +2921,10 @@ export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr {
override canApply(_params: AbAttrBaseParams): boolean { override canApply(_params: AbAttrBaseParams): boolean {
const weatherReplaceable = const weatherReplaceable =
this.weatherType === WeatherType.HEAVY_RAIN this.weatherType === WeatherType.HEAVY_RAIN ||
|| this.weatherType === WeatherType.HARSH_SUN this.weatherType === WeatherType.HARSH_SUN ||
|| this.weatherType === WeatherType.STRONG_WINDS this.weatherType === WeatherType.STRONG_WINDS ||
|| !globalScene.arena.weather?.isImmutable(); !globalScene.arena.weather?.isImmutable();
return weatherReplaceable && globalScene.arena.canSetWeather(this.weatherType); return weatherReplaceable && globalScene.arena.canSetWeather(this.weatherType);
} }
@ -3026,8 +3032,9 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr {
} }
if ( if (
!target!.getAbility().isCopiable // Wonder Guard is normally uncopiable so has the attribute, but Trace specifically can copy it !target!.getAbility().isCopiable &&
&& !(pokemon.hasAbility(AbilityId.TRACE) && target!.getAbility().id === AbilityId.WONDER_GUARD) // Wonder Guard is normally uncopiable so has the attribute, but Trace specifically can copy it
!(pokemon.hasAbility(AbilityId.TRACE) && target!.getAbility().id === AbilityId.WONDER_GUARD)
) { ) {
return false; return false;
} }
@ -3243,10 +3250,10 @@ export class CommanderAbAttr extends AbAttr {
// TODO: Should this work with X + Dondozo fusions? // TODO: Should this work with X + Dondozo fusions?
const ally = pokemon.getAlly(); const ally = pokemon.getAlly();
return ( return (
globalScene.currentBattle?.double globalScene.currentBattle?.double &&
&& !isNullOrUndefined(ally) !isNullOrUndefined(ally) &&
&& ally.species.speciesId === SpeciesId.DONDOZO ally.species.speciesId === SpeciesId.DONDOZO &&
&& !(ally.isFainted() || ally.getTag(BattlerTagType.COMMANDED)) !(ally.isFainted() || ally.getTag(BattlerTagType.COMMANDED))
); );
} }
@ -3312,8 +3319,8 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr {
switch (weatherType) { switch (weatherType) {
case WeatherType.HARSH_SUN: case WeatherType.HARSH_SUN:
if ( if (
pokemon.hasAbility(AbilityId.DESOLATE_LAND) pokemon.hasAbility(AbilityId.DESOLATE_LAND) &&
&& globalScene globalScene
.getField(true) .getField(true)
.filter(p => p !== pokemon) .filter(p => p !== pokemon)
.filter(p => p.hasAbility(AbilityId.DESOLATE_LAND)).length === 0 .filter(p => p.hasAbility(AbilityId.DESOLATE_LAND)).length === 0
@ -3323,8 +3330,8 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr {
break; break;
case WeatherType.HEAVY_RAIN: case WeatherType.HEAVY_RAIN:
if ( if (
pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA) pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA) &&
&& globalScene globalScene
.getField(true) .getField(true)
.filter(p => p !== pokemon) .filter(p => p !== pokemon)
.filter(p => p.hasAbility(AbilityId.PRIMORDIAL_SEA)).length === 0 .filter(p => p.hasAbility(AbilityId.PRIMORDIAL_SEA)).length === 0
@ -3334,8 +3341,8 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr {
break; break;
case WeatherType.STRONG_WINDS: case WeatherType.STRONG_WINDS:
if ( if (
pokemon.hasAbility(AbilityId.DELTA_STREAM) pokemon.hasAbility(AbilityId.DELTA_STREAM) &&
&& globalScene globalScene
.getField(true) .getField(true)
.filter(p => p !== pokemon) .filter(p => p !== pokemon)
.filter(p => p.hasAbility(AbilityId.DELTA_STREAM)).length === 0 .filter(p => p.hasAbility(AbilityId.DELTA_STREAM)).length === 0
@ -3420,8 +3427,8 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr {
switch (weatherType) { switch (weatherType) {
case WeatherType.HARSH_SUN: case WeatherType.HARSH_SUN:
if ( if (
pokemon.hasAbility(AbilityId.DESOLATE_LAND) pokemon.hasAbility(AbilityId.DESOLATE_LAND) &&
&& globalScene globalScene
.getField(true) .getField(true)
.filter(p => p !== pokemon) .filter(p => p !== pokemon)
.filter(p => p.hasAbility(AbilityId.DESOLATE_LAND)).length === 0 .filter(p => p.hasAbility(AbilityId.DESOLATE_LAND)).length === 0
@ -3431,8 +3438,8 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr {
break; break;
case WeatherType.HEAVY_RAIN: case WeatherType.HEAVY_RAIN:
if ( if (
pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA) pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA) &&
&& globalScene globalScene
.getField(true) .getField(true)
.filter(p => p !== pokemon) .filter(p => p !== pokemon)
.filter(p => p.hasAbility(AbilityId.PRIMORDIAL_SEA)).length === 0 .filter(p => p.hasAbility(AbilityId.PRIMORDIAL_SEA)).length === 0
@ -3442,8 +3449,8 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr {
break; break;
case WeatherType.STRONG_WINDS: case WeatherType.STRONG_WINDS:
if ( if (
pokemon.hasAbility(AbilityId.DELTA_STREAM) pokemon.hasAbility(AbilityId.DELTA_STREAM) &&
&& globalScene globalScene
.getField(true) .getField(true)
.filter(p => p !== pokemon) .filter(p => p !== pokemon)
.filter(p => p.hasAbility(AbilityId.DELTA_STREAM)).length === 0 .filter(p => p.hasAbility(AbilityId.DELTA_STREAM)).length === 0
@ -3720,8 +3727,8 @@ export class UserFieldStatusEffectImmunityAbAttr extends AbAttr {
override canApply({ effect, cancelled }: UserFieldStatusEffectImmunityAbAttrParams): boolean { override canApply({ effect, cancelled }: UserFieldStatusEffectImmunityAbAttrParams): boolean {
return ( return (
(!cancelled.value && effect !== StatusEffect.FAINT && this.immuneEffects.length < 1) (!cancelled.value && effect !== StatusEffect.FAINT && this.immuneEffects.length < 1) ||
|| this.immuneEffects.includes(effect) this.immuneEffects.includes(effect)
); );
} }
@ -3803,9 +3810,9 @@ export class ConditionalUserFieldProtectStatAbAttr extends PreStatStageChangeAbA
return false; return false;
} }
return ( return (
!cancelled.value !cancelled.value &&
&& (isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) (isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) &&
&& this.condition(target) this.condition(target)
); );
} }
@ -4195,8 +4202,8 @@ function getAnticipationCondition(): AbAttrCondition {
} }
// the move's base type (not accounting for variable type changes) is super effective // the move's base type (not accounting for variable type changes) is super effective
if ( if (
move.getMove().is("AttackMove") move.getMove().is("AttackMove") &&
&& pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true, undefined, move.getMove()) >= 2 pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true, undefined, move.getMove()) >= 2
) { ) {
return true; return true;
} }
@ -4207,14 +4214,14 @@ function getAnticipationCondition(): AbAttrCondition {
// edge case for hidden power, type is computed // edge case for hidden power, type is computed
if (move.getMove().id === MoveId.HIDDEN_POWER) { if (move.getMove().id === MoveId.HIDDEN_POWER) {
const iv_val = Math.floor( const iv_val = Math.floor(
(((opponent.ivs[Stat.HP] & 1) (((opponent.ivs[Stat.HP] & 1) +
+ (opponent.ivs[Stat.ATK] & 1) * 2 (opponent.ivs[Stat.ATK] & 1) * 2 +
+ (opponent.ivs[Stat.DEF] & 1) * 4 (opponent.ivs[Stat.DEF] & 1) * 4 +
+ (opponent.ivs[Stat.SPD] & 1) * 8 (opponent.ivs[Stat.SPD] & 1) * 8 +
+ (opponent.ivs[Stat.SPATK] & 1) * 16 (opponent.ivs[Stat.SPATK] & 1) * 16 +
+ (opponent.ivs[Stat.SPDEF] & 1) * 32) (opponent.ivs[Stat.SPDEF] & 1) * 32) *
* 15) 15) /
/ 63, 63,
); );
const type = [ const type = [
@ -4281,9 +4288,9 @@ export class ForewarnAbAttr extends PostSummonAbAttr {
} else if (move?.getMove().hasAttr("OneHitKOAttr")) { } else if (move?.getMove().hasAttr("OneHitKOAttr")) {
movePower = 150; movePower = 150;
} else if ( } else if (
move?.getMove().id === MoveId.COUNTER move?.getMove().id === MoveId.COUNTER ||
|| move?.getMove().id === MoveId.MIRROR_COAT move?.getMove().id === MoveId.MIRROR_COAT ||
|| move?.getMove().id === MoveId.METAL_BURST move?.getMove().id === MoveId.METAL_BURST
) { ) {
movePower = 120; movePower = 120;
} else if (move?.getMove().power === -1) { } else if (move?.getMove().power === -1) {
@ -4874,9 +4881,9 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr {
.getOpponents() .getOpponents()
.some( .some(
opp => opp =>
(opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(AbilityId.COMATOSE)) (opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(AbilityId.COMATOSE)) &&
&& !opp.hasAbilityWithAttr("BlockNonDirectDamageAbAttr") !opp.hasAbilityWithAttr("BlockNonDirectDamageAbAttr") &&
&& !opp.switchOutStatus, !opp.switchOutStatus,
); );
} }
@ -5010,8 +5017,8 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
]; ];
// The move to replicate cannot come from the Dancer // The move to replicate cannot come from the Dancer
return ( return (
source.getBattlerIndex() !== pokemon.getBattlerIndex() source.getBattlerIndex() !== pokemon.getBattlerIndex() &&
&& !pokemon.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType)) !pokemon.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType))
); );
} }
@ -5271,12 +5278,12 @@ export interface CheckTrappedAbAttrParams extends AbAttrBaseParams {
export class ArenaTrapAbAttr extends CheckTrappedAbAttr { export class ArenaTrapAbAttr extends CheckTrappedAbAttr {
override canApply({ pokemon, opponent }: CheckTrappedAbAttrParams): boolean { override canApply({ pokemon, opponent }: CheckTrappedAbAttrParams): boolean {
return ( return (
this.arenaTrapCondition(pokemon, opponent) this.arenaTrapCondition(pokemon, opponent) &&
&& !( !(
opponent.getTypes(true).includes(PokemonType.GHOST) opponent.getTypes(true).includes(PokemonType.GHOST) ||
|| (opponent.getTypes(true).includes(PokemonType.STELLAR) && opponent.getTypes().includes(PokemonType.GHOST)) (opponent.getTypes(true).includes(PokemonType.STELLAR) && opponent.getTypes().includes(PokemonType.GHOST))
) ) &&
&& !opponent.hasAbility(AbilityId.RUN_AWAY) !opponent.hasAbility(AbilityId.RUN_AWAY)
); );
} }
@ -5436,9 +5443,9 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
override canApply({ pokemon, attacker, move, simulated }: PostFaintAbAttrParams): boolean { override canApply({ pokemon, attacker, move, simulated }: PostFaintAbAttrParams): boolean {
if ( if (
move === undefined move === undefined ||
|| attacker === undefined attacker === undefined ||
|| !move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) !move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
) { ) {
return false; return false;
} }
@ -6013,9 +6020,9 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr {
// If the last conscious Pokémon in the party is a Terastallized Ogerpon or Terapagos, Illusion will not activate. // If the last conscious Pokémon in the party is a Terastallized Ogerpon or Terapagos, Illusion will not activate.
// Illusion will also not activate if the Pokémon with Illusion is Terastallized and the last Pokémon in the party is Ogerpon or Terapagos. // Illusion will also not activate if the Pokémon with Illusion is Terastallized and the last Pokémon in the party is Ogerpon or Terapagos.
if ( if (
lastPokemon === pokemon lastPokemon === pokemon ||
|| ((speciesId === SpeciesId.OGERPON || speciesId === SpeciesId.TERAPAGOS) ((speciesId === SpeciesId.OGERPON || speciesId === SpeciesId.TERAPAGOS) &&
&& (lastPokemon.isTerastallized || pokemon.isTerastallized)) (lastPokemon.isTerastallized || pokemon.isTerastallized))
) { ) {
return false; return false;
} }
@ -6336,30 +6343,30 @@ class ForceSwitchOutHelper {
} }
if ( if (
!player !player &&
&& globalScene.currentBattle.battleType === BattleType.WILD globalScene.currentBattle.battleType === BattleType.WILD &&
&& !globalScene.currentBattle.waveIndex !globalScene.currentBattle.waveIndex &&
&& globalScene.currentBattle.waveIndex % 10 === 0 globalScene.currentBattle.waveIndex % 10 === 0
) { ) {
return false; return false;
} }
if ( if (
!player !player &&
&& globalScene.currentBattle.isBattleMysteryEncounter() globalScene.currentBattle.isBattleMysteryEncounter() &&
&& !globalScene.currentBattle.mysteryEncounter?.fleeAllowed !globalScene.currentBattle.mysteryEncounter?.fleeAllowed
) { ) {
return false; return false;
} }
const party = player ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); const party = player ? globalScene.getPlayerParty() : globalScene.getEnemyParty();
return ( return (
(!player && globalScene.currentBattle.battleType === BattleType.WILD) (!player && globalScene.currentBattle.battleType === BattleType.WILD) ||
|| party.filter( party.filter(
p => p =>
p.isAllowedInBattle() p.isAllowedInBattle() &&
&& !p.isOnField() !p.isOnField() &&
&& (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot), (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot),
).length > 0 ).length > 0
); );
} }
@ -6453,8 +6460,8 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr {
const enemyLastMoveUsed = enemyMoveHistory[enemyMoveHistory.length - 1]; const enemyLastMoveUsed = enemyMoveHistory[enemyMoveHistory.length - 1];
// Will not activate if the Pokémon's HP falls below half while it is in the air during Sky Drop. // Will not activate if the Pokémon's HP falls below half while it is in the air during Sky Drop.
if ( if (
forbiddenDefendingMoves.includes(enemyLastMoveUsed.move) forbiddenDefendingMoves.includes(enemyLastMoveUsed.move) ||
|| (enemyLastMoveUsed.move === MoveId.SKY_DROP && enemyLastMoveUsed.result === MoveResult.OTHER) (enemyLastMoveUsed.move === MoveId.SKY_DROP && enemyLastMoveUsed.result === MoveResult.OTHER)
) { ) {
return false; return false;
// Will not activate if the Pokémon's HP falls below half by a move affected by Sheer Force. // Will not activate if the Pokémon's HP falls below half by a move affected by Sheer Force.
@ -6743,8 +6750,8 @@ function getPokemonWithWeatherBasedForms() {
.getField(true) .getField(true)
.filter( .filter(
p => p =>
(p.hasAbility(AbilityId.FORECAST) && p.species.speciesId === SpeciesId.CASTFORM) (p.hasAbility(AbilityId.FORECAST) && p.species.speciesId === SpeciesId.CASTFORM) ||
|| (p.hasAbility(AbilityId.FLOWER_GIFT) && p.species.speciesId === SpeciesId.CHERRIM), (p.hasAbility(AbilityId.FLOWER_GIFT) && p.species.speciesId === SpeciesId.CHERRIM),
); );
} }
@ -7443,7 +7450,7 @@ export function initAbilities() {
.conditionalAttr(pokemon => pokemon.formIndex === 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.DISGUISE, 0, false) .conditionalAttr(pokemon => pokemon.formIndex === 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.DISGUISE, 0, false)
.attr(FormBlockDamageAbAttr, .attr(FormBlockDamageAbAttr,
(target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getMoveEffectiveness(user, move) > 0, 0, BattlerTagType.DISGUISE, (target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getMoveEffectiveness(user, move) > 0, 0, BattlerTagType.DISGUISE,
(pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), (pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }),
(pokemon) => toDmgValue(pokemon.getMaxHp() / 8)) (pokemon) => toDmgValue(pokemon.getMaxHp() / 8))
.attr(PostBattleInitFormChangeAbAttr, () => 0) .attr(PostBattleInitFormChangeAbAttr, () => 0)
.attr(PostFaintFormChangeAbAttr, () => 0) .attr(PostFaintFormChangeAbAttr, () => 0)
@ -7617,7 +7624,7 @@ export function initAbilities() {
.attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.ICE_FACE, 0, WeatherType.HAIL, WeatherType.SNOW) .attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.ICE_FACE, 0, WeatherType.HAIL, WeatherType.SNOW)
.attr(FormBlockDamageAbAttr, .attr(FormBlockDamageAbAttr,
(target, _user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE), 0, BattlerTagType.ICE_FACE, (target, _user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE), 0, BattlerTagType.ICE_FACE,
(pokemon, abilityName) => i18next.t("abilityTriggers:iceFaceAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName })) (pokemon, abilityName) => i18next.t("abilityTriggers:iceFaceAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }))
.attr(PostBattleInitFormChangeAbAttr, () => 0) .attr(PostBattleInitFormChangeAbAttr, () => 0)
.uncopiable() .uncopiable()
.unreplaceable() .unreplaceable()

View File

@ -570,10 +570,10 @@ class MatBlockTag extends ConditionalProtectTag {
const CraftyShieldConditionFunc: ProtectConditionFunc = (_arena, moveId) => { const CraftyShieldConditionFunc: ProtectConditionFunc = (_arena, moveId) => {
const move = allMoves[moveId]; const move = allMoves[moveId];
return ( return (
move.category === MoveCategory.STATUS move.category === MoveCategory.STATUS &&
&& move.moveTarget !== MoveTarget.ENEMY_SIDE move.moveTarget !== MoveTarget.ENEMY_SIDE &&
&& move.moveTarget !== MoveTarget.BOTH_SIDES move.moveTarget !== MoveTarget.BOTH_SIDES &&
&& move.moveTarget !== MoveTarget.ALL move.moveTarget !== MoveTarget.ALL
); );
}; };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,11 @@ import { AbilityId } from "#enums/ability-id";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
export interface PassiveAbilities { export interface PassiveAbilities {
[key: number]: AbilityId; [key: number]: AbilityId
} }
interface StarterPassiveAbilities { interface StarterPassiveAbilities {
[key: number]: PassiveAbilities; [key: number]: PassiveAbilities
} }
export const starterPassiveAbilities: StarterPassiveAbilities = { export const starterPassiveAbilities: StarterPassiveAbilities = {
@ -15,12 +15,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.VENUSAUR]: { 0: AbilityId.GRASSY_SURGE, 1: AbilityId.SEED_SOWER, 2: AbilityId.FLOWER_VEIL }, [SpeciesId.VENUSAUR]: { 0: AbilityId.GRASSY_SURGE, 1: AbilityId.SEED_SOWER, 2: AbilityId.FLOWER_VEIL },
[SpeciesId.CHARMANDER]: { 0: AbilityId.SHEER_FORCE }, [SpeciesId.CHARMANDER]: { 0: AbilityId.SHEER_FORCE },
[SpeciesId.CHARMELEON]: { 0: AbilityId.BEAST_BOOST }, [SpeciesId.CHARMELEON]: { 0: AbilityId.BEAST_BOOST },
[SpeciesId.CHARIZARD]: { [SpeciesId.CHARIZARD]: { 0: AbilityId.BEAST_BOOST, 1: AbilityId.LEVITATE, 2: AbilityId.TURBOBLAZE, 3: AbilityId.UNNERVE },
0: AbilityId.BEAST_BOOST,
1: AbilityId.LEVITATE,
2: AbilityId.TURBOBLAZE,
3: AbilityId.UNNERVE,
},
[SpeciesId.SQUIRTLE]: { 0: AbilityId.DAUNTLESS_SHIELD }, [SpeciesId.SQUIRTLE]: { 0: AbilityId.DAUNTLESS_SHIELD },
[SpeciesId.WARTORTLE]: { 0: AbilityId.DAUNTLESS_SHIELD }, [SpeciesId.WARTORTLE]: { 0: AbilityId.DAUNTLESS_SHIELD },
[SpeciesId.BLASTOISE]: { 0: AbilityId.DAUNTLESS_SHIELD, 1: AbilityId.BULLETPROOF, 2: AbilityId.BULLETPROOF }, [SpeciesId.BLASTOISE]: { 0: AbilityId.DAUNTLESS_SHIELD, 1: AbilityId.BULLETPROOF, 2: AbilityId.BULLETPROOF },
@ -197,17 +192,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.CHINCHOU]: { 0: AbilityId.REGENERATOR }, [SpeciesId.CHINCHOU]: { 0: AbilityId.REGENERATOR },
[SpeciesId.LANTURN]: { 0: AbilityId.REGENERATOR }, [SpeciesId.LANTURN]: { 0: AbilityId.REGENERATOR },
[SpeciesId.PICHU]: { 0: AbilityId.ELECTRIC_SURGE, 1: AbilityId.STURDY }, [SpeciesId.PICHU]: { 0: AbilityId.ELECTRIC_SURGE, 1: AbilityId.STURDY },
[SpeciesId.PIKACHU]: { [SpeciesId.PIKACHU]: { 0: AbilityId.ELECTRIC_SURGE, 1: AbilityId.STURDY, 2: AbilityId.COSTAR, 3: AbilityId.IRON_FIST, 4: AbilityId.QUEENLY_MAJESTY, 5: AbilityId.MISTY_SURGE, 6: AbilityId.TINTED_LENS, 7: AbilityId.LIBERO, 8: AbilityId.THICK_FAT },
0: AbilityId.ELECTRIC_SURGE,
1: AbilityId.STURDY,
2: AbilityId.COSTAR,
3: AbilityId.IRON_FIST,
4: AbilityId.QUEENLY_MAJESTY,
5: AbilityId.MISTY_SURGE,
6: AbilityId.TINTED_LENS,
7: AbilityId.LIBERO,
8: AbilityId.THICK_FAT,
},
[SpeciesId.RAICHU]: { 0: AbilityId.ELECTRIC_SURGE }, [SpeciesId.RAICHU]: { 0: AbilityId.ELECTRIC_SURGE },
[SpeciesId.ALOLA_RAICHU]: { 0: AbilityId.ELECTRIC_SURGE }, [SpeciesId.ALOLA_RAICHU]: { 0: AbilityId.ELECTRIC_SURGE },
[SpeciesId.CLEFFA]: { 0: AbilityId.PRANKSTER }, [SpeciesId.CLEFFA]: { 0: AbilityId.PRANKSTER },
@ -239,36 +224,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.HONCHKROW]: { 0: AbilityId.INTIMIDATE }, [SpeciesId.HONCHKROW]: { 0: AbilityId.INTIMIDATE },
[SpeciesId.MISDREAVUS]: { 0: AbilityId.BEADS_OF_RUIN }, [SpeciesId.MISDREAVUS]: { 0: AbilityId.BEADS_OF_RUIN },
[SpeciesId.MISMAGIUS]: { 0: AbilityId.BEADS_OF_RUIN }, [SpeciesId.MISMAGIUS]: { 0: AbilityId.BEADS_OF_RUIN },
[SpeciesId.UNOWN]: { [SpeciesId.UNOWN]: { 0: AbilityId.ADAPTABILITY, 1: AbilityId.BEAST_BOOST, 2: AbilityId.CONTRARY, 3: AbilityId.DAZZLING, 4: AbilityId.EMERGENCY_EXIT, 5: AbilityId.FRIEND_GUARD, 6: AbilityId.GOOD_AS_GOLD, 7: AbilityId.HONEY_GATHER, 8: AbilityId.IMPOSTER, 9: AbilityId.JUSTIFIED, 10: AbilityId.KLUTZ, 11: AbilityId.LIBERO, 12: AbilityId.MOODY, 13: AbilityId.NEUTRALIZING_GAS, 14: AbilityId.OPPORTUNIST, 15: AbilityId.PICKUP, 16: AbilityId.QUICK_DRAW, 17: AbilityId.RUN_AWAY, 18: AbilityId.SIMPLE, 19: AbilityId.TRACE, 20: AbilityId.UNNERVE, 21: AbilityId.VICTORY_STAR, 22: AbilityId.WANDERING_SPIRIT, 23: AbilityId.FAIRY_AURA, 24: AbilityId.DARK_AURA, 25: AbilityId.AURA_BREAK, 26: AbilityId.PURE_POWER, 27: AbilityId.UNAWARE },
0: AbilityId.ADAPTABILITY,
1: AbilityId.BEAST_BOOST,
2: AbilityId.CONTRARY,
3: AbilityId.DAZZLING,
4: AbilityId.EMERGENCY_EXIT,
5: AbilityId.FRIEND_GUARD,
6: AbilityId.GOOD_AS_GOLD,
7: AbilityId.HONEY_GATHER,
8: AbilityId.IMPOSTER,
9: AbilityId.JUSTIFIED,
10: AbilityId.KLUTZ,
11: AbilityId.LIBERO,
12: AbilityId.MOODY,
13: AbilityId.NEUTRALIZING_GAS,
14: AbilityId.OPPORTUNIST,
15: AbilityId.PICKUP,
16: AbilityId.QUICK_DRAW,
17: AbilityId.RUN_AWAY,
18: AbilityId.SIMPLE,
19: AbilityId.TRACE,
20: AbilityId.UNNERVE,
21: AbilityId.VICTORY_STAR,
22: AbilityId.WANDERING_SPIRIT,
23: AbilityId.FAIRY_AURA,
24: AbilityId.DARK_AURA,
25: AbilityId.AURA_BREAK,
26: AbilityId.PURE_POWER,
27: AbilityId.UNAWARE,
},
[SpeciesId.GIRAFARIG]: { 0: AbilityId.PARENTAL_BOND }, [SpeciesId.GIRAFARIG]: { 0: AbilityId.PARENTAL_BOND },
[SpeciesId.FARIGIRAF]: { 0: AbilityId.PARENTAL_BOND }, [SpeciesId.FARIGIRAF]: { 0: AbilityId.PARENTAL_BOND },
[SpeciesId.PINECO]: { 0: AbilityId.ROUGH_SKIN }, [SpeciesId.PINECO]: { 0: AbilityId.ROUGH_SKIN },
@ -429,12 +385,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.ARMALDO]: { 0: AbilityId.WATER_ABSORB }, [SpeciesId.ARMALDO]: { 0: AbilityId.WATER_ABSORB },
[SpeciesId.FEEBAS]: { 0: AbilityId.MULTISCALE }, [SpeciesId.FEEBAS]: { 0: AbilityId.MULTISCALE },
[SpeciesId.MILOTIC]: { 0: AbilityId.MAGIC_GUARD }, [SpeciesId.MILOTIC]: { 0: AbilityId.MAGIC_GUARD },
[SpeciesId.CASTFORM]: { [SpeciesId.CASTFORM]: { 0: AbilityId.ADAPTABILITY, 1: AbilityId.ADAPTABILITY, 2: AbilityId.ADAPTABILITY, 3: AbilityId.ADAPTABILITY },
0: AbilityId.ADAPTABILITY,
1: AbilityId.ADAPTABILITY,
2: AbilityId.ADAPTABILITY,
3: AbilityId.ADAPTABILITY,
},
[SpeciesId.KECLEON]: { 0: AbilityId.ADAPTABILITY }, [SpeciesId.KECLEON]: { 0: AbilityId.ADAPTABILITY },
[SpeciesId.SHUPPET]: { 0: AbilityId.SHADOW_SHIELD }, [SpeciesId.SHUPPET]: { 0: AbilityId.SHADOW_SHIELD },
[SpeciesId.BANETTE]: { 0: AbilityId.SHADOW_SHIELD, 1: AbilityId.SHADOW_SHIELD }, [SpeciesId.BANETTE]: { 0: AbilityId.SHADOW_SHIELD, 1: AbilityId.SHADOW_SHIELD },
@ -471,12 +422,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.GROUDON]: { 0: AbilityId.MOLD_BREAKER, 1: AbilityId.TURBOBLAZE }, [SpeciesId.GROUDON]: { 0: AbilityId.MOLD_BREAKER, 1: AbilityId.TURBOBLAZE },
[SpeciesId.RAYQUAZA]: { 0: AbilityId.UNNERVE, 1: AbilityId.UNNERVE }, [SpeciesId.RAYQUAZA]: { 0: AbilityId.UNNERVE, 1: AbilityId.UNNERVE },
[SpeciesId.JIRACHI]: { 0: AbilityId.PURIFYING_SALT }, [SpeciesId.JIRACHI]: { 0: AbilityId.PURIFYING_SALT },
[SpeciesId.DEOXYS]: { [SpeciesId.DEOXYS]: { 0: AbilityId.PROTEAN, 1: AbilityId.ADAPTABILITY, 2: AbilityId.REGENERATOR, 3: AbilityId.SHADOW_SHIELD },
0: AbilityId.PROTEAN,
1: AbilityId.ADAPTABILITY,
2: AbilityId.REGENERATOR,
3: AbilityId.SHADOW_SHIELD,
},
[SpeciesId.TURTWIG]: { 0: AbilityId.SOLID_ROCK }, [SpeciesId.TURTWIG]: { 0: AbilityId.SOLID_ROCK },
[SpeciesId.GROTLE]: { 0: AbilityId.SOLID_ROCK }, [SpeciesId.GROTLE]: { 0: AbilityId.SOLID_ROCK },
@ -559,14 +505,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.MANTINE]: { 0: AbilityId.UNAWARE }, [SpeciesId.MANTINE]: { 0: AbilityId.UNAWARE },
[SpeciesId.SNOVER]: { 0: AbilityId.SLUSH_RUSH }, [SpeciesId.SNOVER]: { 0: AbilityId.SLUSH_RUSH },
[SpeciesId.ABOMASNOW]: { 0: AbilityId.SLUSH_RUSH, 1: AbilityId.SEED_SOWER }, [SpeciesId.ABOMASNOW]: { 0: AbilityId.SLUSH_RUSH, 1: AbilityId.SEED_SOWER },
[SpeciesId.ROTOM]: { [SpeciesId.ROTOM]: { 0: AbilityId.HADRON_ENGINE, 1: AbilityId.HADRON_ENGINE, 2: AbilityId.HADRON_ENGINE, 3: AbilityId.HADRON_ENGINE, 4: AbilityId.HADRON_ENGINE, 5: AbilityId.HADRON_ENGINE },
0: AbilityId.HADRON_ENGINE,
1: AbilityId.HADRON_ENGINE,
2: AbilityId.HADRON_ENGINE,
3: AbilityId.HADRON_ENGINE,
4: AbilityId.HADRON_ENGINE,
5: AbilityId.HADRON_ENGINE,
},
[SpeciesId.UXIE]: { 0: AbilityId.ILLUSION }, [SpeciesId.UXIE]: { 0: AbilityId.ILLUSION },
[SpeciesId.MESPRIT]: { 0: AbilityId.MOODY }, [SpeciesId.MESPRIT]: { 0: AbilityId.MOODY },
[SpeciesId.AZELF]: { 0: AbilityId.NEUROFORCE }, [SpeciesId.AZELF]: { 0: AbilityId.NEUROFORCE },
@ -580,26 +519,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.MANAPHY]: { 0: AbilityId.PRIMORDIAL_SEA }, [SpeciesId.MANAPHY]: { 0: AbilityId.PRIMORDIAL_SEA },
[SpeciesId.DARKRAI]: { 0: AbilityId.UNNERVE }, [SpeciesId.DARKRAI]: { 0: AbilityId.UNNERVE },
[SpeciesId.SHAYMIN]: { 0: AbilityId.GRASSY_SURGE, 1: AbilityId.DELTA_STREAM }, [SpeciesId.SHAYMIN]: { 0: AbilityId.GRASSY_SURGE, 1: AbilityId.DELTA_STREAM },
[SpeciesId.ARCEUS]: { [SpeciesId.ARCEUS]: { 0: AbilityId.ADAPTABILITY, 1: AbilityId.ADAPTABILITY, 2: AbilityId.ADAPTABILITY, 3: AbilityId.ADAPTABILITY, 4: AbilityId.ADAPTABILITY, 5: AbilityId.ADAPTABILITY, 6: AbilityId.ADAPTABILITY, 7: AbilityId.ADAPTABILITY, 8: AbilityId.ADAPTABILITY, 9: AbilityId.ADAPTABILITY, 10: AbilityId.ADAPTABILITY, 11: AbilityId.ADAPTABILITY, 12: AbilityId.ADAPTABILITY, 13: AbilityId.ADAPTABILITY, 14: AbilityId.ADAPTABILITY, 15: AbilityId.ADAPTABILITY, 16: AbilityId.ADAPTABILITY, 17: AbilityId.ADAPTABILITY },
0: AbilityId.ADAPTABILITY,
1: AbilityId.ADAPTABILITY,
2: AbilityId.ADAPTABILITY,
3: AbilityId.ADAPTABILITY,
4: AbilityId.ADAPTABILITY,
5: AbilityId.ADAPTABILITY,
6: AbilityId.ADAPTABILITY,
7: AbilityId.ADAPTABILITY,
8: AbilityId.ADAPTABILITY,
9: AbilityId.ADAPTABILITY,
10: AbilityId.ADAPTABILITY,
11: AbilityId.ADAPTABILITY,
12: AbilityId.ADAPTABILITY,
13: AbilityId.ADAPTABILITY,
14: AbilityId.ADAPTABILITY,
15: AbilityId.ADAPTABILITY,
16: AbilityId.ADAPTABILITY,
17: AbilityId.ADAPTABILITY,
},
[SpeciesId.VICTINI]: { 0: AbilityId.SHEER_FORCE }, [SpeciesId.VICTINI]: { 0: AbilityId.SHEER_FORCE },
[SpeciesId.SNIVY]: { 0: AbilityId.MULTISCALE }, [SpeciesId.SNIVY]: { 0: AbilityId.MULTISCALE },
@ -695,18 +615,8 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.VANILLITE]: { 0: AbilityId.REFRIGERATE }, [SpeciesId.VANILLITE]: { 0: AbilityId.REFRIGERATE },
[SpeciesId.VANILLISH]: { 0: AbilityId.REFRIGERATE }, [SpeciesId.VANILLISH]: { 0: AbilityId.REFRIGERATE },
[SpeciesId.VANILLUXE]: { 0: AbilityId.SLUSH_RUSH }, [SpeciesId.VANILLUXE]: { 0: AbilityId.SLUSH_RUSH },
[SpeciesId.DEERLING]: { [SpeciesId.DEERLING]: { 0: AbilityId.FLOWER_VEIL, 1: AbilityId.CUD_CHEW, 2: AbilityId.HARVEST, 3: AbilityId.FUR_COAT },
0: AbilityId.FLOWER_VEIL, [SpeciesId.SAWSBUCK]: { 0: AbilityId.FLOWER_VEIL, 1: AbilityId.CUD_CHEW, 2: AbilityId.HARVEST, 3: AbilityId.FUR_COAT },
1: AbilityId.CUD_CHEW,
2: AbilityId.HARVEST,
3: AbilityId.FUR_COAT,
},
[SpeciesId.SAWSBUCK]: {
0: AbilityId.FLOWER_VEIL,
1: AbilityId.CUD_CHEW,
2: AbilityId.HARVEST,
3: AbilityId.FUR_COAT,
},
[SpeciesId.EMOLGA]: { 0: AbilityId.SERENE_GRACE }, [SpeciesId.EMOLGA]: { 0: AbilityId.SERENE_GRACE },
[SpeciesId.KARRABLAST]: { 0: AbilityId.QUICK_DRAW }, [SpeciesId.KARRABLAST]: { 0: AbilityId.QUICK_DRAW },
[SpeciesId.ESCAVALIER]: { 0: AbilityId.QUICK_DRAW }, [SpeciesId.ESCAVALIER]: { 0: AbilityId.QUICK_DRAW },
@ -771,13 +681,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.KYUREM]: { 0: AbilityId.SNOW_WARNING, 1: AbilityId.HADRON_ENGINE, 2: AbilityId.ORICHALCUM_PULSE }, [SpeciesId.KYUREM]: { 0: AbilityId.SNOW_WARNING, 1: AbilityId.HADRON_ENGINE, 2: AbilityId.ORICHALCUM_PULSE },
[SpeciesId.KELDEO]: { 0: AbilityId.GRIM_NEIGH, 1: AbilityId.GRIM_NEIGH }, [SpeciesId.KELDEO]: { 0: AbilityId.GRIM_NEIGH, 1: AbilityId.GRIM_NEIGH },
[SpeciesId.MELOETTA]: { 0: AbilityId.PUNK_ROCK, 1: AbilityId.SCRAPPY }, [SpeciesId.MELOETTA]: { 0: AbilityId.PUNK_ROCK, 1: AbilityId.SCRAPPY },
[SpeciesId.GENESECT]: { [SpeciesId.GENESECT]: { 0: AbilityId.PROTEAN, 1: AbilityId.PROTEAN, 2: AbilityId.PROTEAN, 3: AbilityId.PROTEAN, 4: AbilityId.PROTEAN },
0: AbilityId.PROTEAN,
1: AbilityId.PROTEAN,
2: AbilityId.PROTEAN,
3: AbilityId.PROTEAN,
4: AbilityId.PROTEAN,
},
[SpeciesId.CHESPIN]: { 0: AbilityId.ROUGH_SKIN }, [SpeciesId.CHESPIN]: { 0: AbilityId.ROUGH_SKIN },
[SpeciesId.QUILLADIN]: { 0: AbilityId.DAUNTLESS_SHIELD }, [SpeciesId.QUILLADIN]: { 0: AbilityId.DAUNTLESS_SHIELD },
@ -793,111 +697,19 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.FLETCHLING]: { 0: AbilityId.FLAME_BODY }, [SpeciesId.FLETCHLING]: { 0: AbilityId.FLAME_BODY },
[SpeciesId.FLETCHINDER]: { 0: AbilityId.MAGIC_GUARD }, [SpeciesId.FLETCHINDER]: { 0: AbilityId.MAGIC_GUARD },
[SpeciesId.TALONFLAME]: { 0: AbilityId.MAGIC_GUARD }, [SpeciesId.TALONFLAME]: { 0: AbilityId.MAGIC_GUARD },
[SpeciesId.SCATTERBUG]: { [SpeciesId.SCATTERBUG]: { 0: AbilityId.RUN_AWAY, 1: AbilityId.RUN_AWAY, 2: AbilityId.RUN_AWAY, 3: AbilityId.RUN_AWAY, 4: AbilityId.RUN_AWAY, 5: AbilityId.RUN_AWAY, 6: AbilityId.RUN_AWAY, 7: AbilityId.RUN_AWAY, 8: AbilityId.RUN_AWAY, 9: AbilityId.RUN_AWAY, 10: AbilityId.RUN_AWAY, 11: AbilityId.RUN_AWAY, 12: AbilityId.RUN_AWAY, 13: AbilityId.RUN_AWAY, 14: AbilityId.RUN_AWAY, 15: AbilityId.RUN_AWAY, 16: AbilityId.RUN_AWAY, 17: AbilityId.RUN_AWAY, 18: AbilityId.RUN_AWAY, 19: AbilityId.RUN_AWAY },
0: AbilityId.RUN_AWAY, [SpeciesId.SPEWPA]: { 0: AbilityId.COMPOUND_EYES, 1: AbilityId.COMPOUND_EYES, 2: AbilityId.COMPOUND_EYES, 3: AbilityId.COMPOUND_EYES, 4: AbilityId.COMPOUND_EYES, 5: AbilityId.COMPOUND_EYES, 6: AbilityId.COMPOUND_EYES, 7: AbilityId.COMPOUND_EYES, 8: AbilityId.COMPOUND_EYES, 9: AbilityId.COMPOUND_EYES, 10: AbilityId.COMPOUND_EYES, 11: AbilityId.COMPOUND_EYES, 12: AbilityId.COMPOUND_EYES, 13: AbilityId.COMPOUND_EYES, 14: AbilityId.COMPOUND_EYES, 15: AbilityId.COMPOUND_EYES, 16: AbilityId.COMPOUND_EYES, 17: AbilityId.COMPOUND_EYES, 18: AbilityId.COMPOUND_EYES, 19: AbilityId.COMPOUND_EYES },
1: AbilityId.RUN_AWAY, [SpeciesId.VIVILLON]: { 0: AbilityId.PRANKSTER, 1: AbilityId.PRANKSTER, 2: AbilityId.PRANKSTER, 3: AbilityId.PRANKSTER, 4: AbilityId.PRANKSTER, 5: AbilityId.PRANKSTER, 6: AbilityId.PRANKSTER, 7: AbilityId.PRANKSTER, 8: AbilityId.PRANKSTER, 9: AbilityId.PRANKSTER, 10: AbilityId.PRANKSTER, 11: AbilityId.PRANKSTER, 12: AbilityId.PRANKSTER, 13: AbilityId.PRANKSTER, 14: AbilityId.PRANKSTER, 15: AbilityId.PRANKSTER, 16: AbilityId.PRANKSTER, 17: AbilityId.PRANKSTER, 18: AbilityId.PRANKSTER, 19: AbilityId.PRANKSTER },
2: AbilityId.RUN_AWAY,
3: AbilityId.RUN_AWAY,
4: AbilityId.RUN_AWAY,
5: AbilityId.RUN_AWAY,
6: AbilityId.RUN_AWAY,
7: AbilityId.RUN_AWAY,
8: AbilityId.RUN_AWAY,
9: AbilityId.RUN_AWAY,
10: AbilityId.RUN_AWAY,
11: AbilityId.RUN_AWAY,
12: AbilityId.RUN_AWAY,
13: AbilityId.RUN_AWAY,
14: AbilityId.RUN_AWAY,
15: AbilityId.RUN_AWAY,
16: AbilityId.RUN_AWAY,
17: AbilityId.RUN_AWAY,
18: AbilityId.RUN_AWAY,
19: AbilityId.RUN_AWAY,
},
[SpeciesId.SPEWPA]: {
0: AbilityId.COMPOUND_EYES,
1: AbilityId.COMPOUND_EYES,
2: AbilityId.COMPOUND_EYES,
3: AbilityId.COMPOUND_EYES,
4: AbilityId.COMPOUND_EYES,
5: AbilityId.COMPOUND_EYES,
6: AbilityId.COMPOUND_EYES,
7: AbilityId.COMPOUND_EYES,
8: AbilityId.COMPOUND_EYES,
9: AbilityId.COMPOUND_EYES,
10: AbilityId.COMPOUND_EYES,
11: AbilityId.COMPOUND_EYES,
12: AbilityId.COMPOUND_EYES,
13: AbilityId.COMPOUND_EYES,
14: AbilityId.COMPOUND_EYES,
15: AbilityId.COMPOUND_EYES,
16: AbilityId.COMPOUND_EYES,
17: AbilityId.COMPOUND_EYES,
18: AbilityId.COMPOUND_EYES,
19: AbilityId.COMPOUND_EYES,
},
[SpeciesId.VIVILLON]: {
0: AbilityId.PRANKSTER,
1: AbilityId.PRANKSTER,
2: AbilityId.PRANKSTER,
3: AbilityId.PRANKSTER,
4: AbilityId.PRANKSTER,
5: AbilityId.PRANKSTER,
6: AbilityId.PRANKSTER,
7: AbilityId.PRANKSTER,
8: AbilityId.PRANKSTER,
9: AbilityId.PRANKSTER,
10: AbilityId.PRANKSTER,
11: AbilityId.PRANKSTER,
12: AbilityId.PRANKSTER,
13: AbilityId.PRANKSTER,
14: AbilityId.PRANKSTER,
15: AbilityId.PRANKSTER,
16: AbilityId.PRANKSTER,
17: AbilityId.PRANKSTER,
18: AbilityId.PRANKSTER,
19: AbilityId.PRANKSTER,
},
[SpeciesId.LITLEO]: { 0: AbilityId.BEAST_BOOST }, [SpeciesId.LITLEO]: { 0: AbilityId.BEAST_BOOST },
[SpeciesId.PYROAR]: { 0: AbilityId.BEAST_BOOST }, [SpeciesId.PYROAR]: { 0: AbilityId.BEAST_BOOST },
[SpeciesId.FLABEBE]: { [SpeciesId.FLABEBE]: { 0: AbilityId.GRASSY_SURGE, 1: AbilityId.GRASSY_SURGE, 2: AbilityId.GRASSY_SURGE, 3: AbilityId.GRASSY_SURGE, 4: AbilityId.GRASSY_SURGE },
0: AbilityId.GRASSY_SURGE, [SpeciesId.FLOETTE]: { 0: AbilityId.GRASSY_SURGE, 1: AbilityId.GRASSY_SURGE, 2: AbilityId.GRASSY_SURGE, 3: AbilityId.GRASSY_SURGE, 4: AbilityId.GRASSY_SURGE },
1: AbilityId.GRASSY_SURGE, [SpeciesId.FLORGES]: { 0: AbilityId.GRASSY_SURGE, 1: AbilityId.GRASSY_SURGE, 2: AbilityId.GRASSY_SURGE, 3: AbilityId.GRASSY_SURGE, 4: AbilityId.GRASSY_SURGE },
2: AbilityId.GRASSY_SURGE,
3: AbilityId.GRASSY_SURGE,
4: AbilityId.GRASSY_SURGE,
},
[SpeciesId.FLOETTE]: {
0: AbilityId.GRASSY_SURGE,
1: AbilityId.GRASSY_SURGE,
2: AbilityId.GRASSY_SURGE,
3: AbilityId.GRASSY_SURGE,
4: AbilityId.GRASSY_SURGE,
},
[SpeciesId.FLORGES]: {
0: AbilityId.GRASSY_SURGE,
1: AbilityId.GRASSY_SURGE,
2: AbilityId.GRASSY_SURGE,
3: AbilityId.GRASSY_SURGE,
4: AbilityId.GRASSY_SURGE,
},
[SpeciesId.SKIDDO]: { 0: AbilityId.SEED_SOWER }, [SpeciesId.SKIDDO]: { 0: AbilityId.SEED_SOWER },
[SpeciesId.GOGOAT]: { 0: AbilityId.SEED_SOWER }, [SpeciesId.GOGOAT]: { 0: AbilityId.SEED_SOWER },
[SpeciesId.PANCHAM]: { 0: AbilityId.TECHNICIAN }, [SpeciesId.PANCHAM]: { 0: AbilityId.TECHNICIAN },
[SpeciesId.PANGORO]: { 0: AbilityId.FUR_COAT }, [SpeciesId.PANGORO]: { 0: AbilityId.FUR_COAT },
[SpeciesId.FURFROU]: { [SpeciesId.FURFROU]: { 0: AbilityId.FLUFFY, 1: AbilityId.FLUFFY, 2: AbilityId.FLUFFY, 3: AbilityId.FLUFFY, 4: AbilityId.FLUFFY, 5: AbilityId.FLUFFY, 6: AbilityId.FLUFFY, 7: AbilityId.FLUFFY, 8: AbilityId.FLUFFY, 9: AbilityId.FLUFFY },
0: AbilityId.FLUFFY,
1: AbilityId.FLUFFY,
2: AbilityId.FLUFFY,
3: AbilityId.FLUFFY,
4: AbilityId.FLUFFY,
5: AbilityId.FLUFFY,
6: AbilityId.FLUFFY,
7: AbilityId.FLUFFY,
8: AbilityId.FLUFFY,
9: AbilityId.FLUFFY,
},
[SpeciesId.ESPURR]: { 0: AbilityId.PRANKSTER }, [SpeciesId.ESPURR]: { 0: AbilityId.PRANKSTER },
[SpeciesId.MEOWSTIC]: { 0: AbilityId.FUR_COAT, 1: AbilityId.NEUROFORCE }, [SpeciesId.MEOWSTIC]: { 0: AbilityId.FUR_COAT, 1: AbilityId.NEUROFORCE },
[SpeciesId.HONEDGE]: { 0: AbilityId.SHARPNESS }, [SpeciesId.HONEDGE]: { 0: AbilityId.SHARPNESS },
@ -932,18 +744,8 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.KLEFKI]: { 0: AbilityId.LEVITATE }, [SpeciesId.KLEFKI]: { 0: AbilityId.LEVITATE },
[SpeciesId.PHANTUMP]: { 0: AbilityId.SHADOW_TAG }, [SpeciesId.PHANTUMP]: { 0: AbilityId.SHADOW_TAG },
[SpeciesId.TREVENANT]: { 0: AbilityId.SHADOW_TAG }, [SpeciesId.TREVENANT]: { 0: AbilityId.SHADOW_TAG },
[SpeciesId.PUMPKABOO]: { [SpeciesId.PUMPKABOO]: { 0: AbilityId.ILLUMINATE, 1: AbilityId.ADAPTABILITY, 2: AbilityId.WELL_BAKED_BODY, 3: AbilityId.SEED_SOWER },
0: AbilityId.ILLUMINATE, [SpeciesId.GOURGEIST]: { 0: AbilityId.ILLUMINATE, 1: AbilityId.ADAPTABILITY, 2: AbilityId.WELL_BAKED_BODY, 3: AbilityId.SEED_SOWER },
1: AbilityId.ADAPTABILITY,
2: AbilityId.WELL_BAKED_BODY,
3: AbilityId.SEED_SOWER,
},
[SpeciesId.GOURGEIST]: {
0: AbilityId.ILLUMINATE,
1: AbilityId.ADAPTABILITY,
2: AbilityId.WELL_BAKED_BODY,
3: AbilityId.SEED_SOWER,
},
[SpeciesId.BERGMITE]: { 0: AbilityId.ICE_SCALES }, [SpeciesId.BERGMITE]: { 0: AbilityId.ICE_SCALES },
[SpeciesId.AVALUGG]: { 0: AbilityId.ICE_SCALES }, [SpeciesId.AVALUGG]: { 0: AbilityId.ICE_SCALES },
[SpeciesId.HISUI_AVALUGG]: { 0: AbilityId.ICE_SCALES }, [SpeciesId.HISUI_AVALUGG]: { 0: AbilityId.ICE_SCALES },
@ -951,14 +753,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.NOIVERN]: { 0: AbilityId.PUNK_ROCK }, [SpeciesId.NOIVERN]: { 0: AbilityId.PUNK_ROCK },
[SpeciesId.XERNEAS]: { 0: AbilityId.HARVEST, 1: AbilityId.HARVEST }, [SpeciesId.XERNEAS]: { 0: AbilityId.HARVEST, 1: AbilityId.HARVEST },
[SpeciesId.YVELTAL]: { 0: AbilityId.SOUL_HEART }, [SpeciesId.YVELTAL]: { 0: AbilityId.SOUL_HEART },
[SpeciesId.ZYGARDE]: { [SpeciesId.ZYGARDE]: { 0: AbilityId.UNNERVE, 1: AbilityId.MOXIE, 2: AbilityId.UNNERVE, 3: AbilityId.MOXIE, 4: AbilityId.ADAPTABILITY, 5: AbilityId.ADAPTABILITY },
0: AbilityId.UNNERVE,
1: AbilityId.MOXIE,
2: AbilityId.UNNERVE,
3: AbilityId.MOXIE,
4: AbilityId.ADAPTABILITY,
5: AbilityId.ADAPTABILITY,
},
[SpeciesId.DIANCIE]: { 0: AbilityId.SOLID_ROCK, 1: AbilityId.PRISM_ARMOR }, [SpeciesId.DIANCIE]: { 0: AbilityId.SOLID_ROCK, 1: AbilityId.PRISM_ARMOR },
[SpeciesId.HOOPA]: { 0: AbilityId.OPPORTUNIST, 1: AbilityId.OPPORTUNIST }, [SpeciesId.HOOPA]: { 0: AbilityId.OPPORTUNIST, 1: AbilityId.OPPORTUNIST },
[SpeciesId.VOLCANION]: { 0: AbilityId.NEUTRALIZING_GAS }, [SpeciesId.VOLCANION]: { 0: AbilityId.NEUTRALIZING_GAS },
@ -984,12 +779,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.VIKAVOLT]: { 0: AbilityId.SPEED_BOOST }, [SpeciesId.VIKAVOLT]: { 0: AbilityId.SPEED_BOOST },
[SpeciesId.CRABRAWLER]: { 0: AbilityId.WATER_BUBBLE }, [SpeciesId.CRABRAWLER]: { 0: AbilityId.WATER_BUBBLE },
[SpeciesId.CRABOMINABLE]: { 0: AbilityId.WATER_BUBBLE }, [SpeciesId.CRABOMINABLE]: { 0: AbilityId.WATER_BUBBLE },
[SpeciesId.ORICORIO]: { [SpeciesId.ORICORIO]: { 0: AbilityId.ADAPTABILITY, 1: AbilityId.ADAPTABILITY, 2: AbilityId.ADAPTABILITY, 3: AbilityId.ADAPTABILITY },
0: AbilityId.ADAPTABILITY,
1: AbilityId.ADAPTABILITY,
2: AbilityId.ADAPTABILITY,
3: AbilityId.ADAPTABILITY,
},
[SpeciesId.CUTIEFLY]: { 0: AbilityId.PICKUP }, [SpeciesId.CUTIEFLY]: { 0: AbilityId.PICKUP },
[SpeciesId.RIBOMBEE]: { 0: AbilityId.PICKUP }, [SpeciesId.RIBOMBEE]: { 0: AbilityId.PICKUP },
[SpeciesId.ROCKRUFF]: { 0: AbilityId.PICKUP, 1: AbilityId.PICKUP }, [SpeciesId.ROCKRUFF]: { 0: AbilityId.PICKUP, 1: AbilityId.PICKUP },
@ -1021,42 +811,8 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.PALOSSAND]: { 0: AbilityId.SAND_SPIT }, [SpeciesId.PALOSSAND]: { 0: AbilityId.SAND_SPIT },
[SpeciesId.PYUKUMUKU]: { 0: AbilityId.PURIFYING_SALT }, [SpeciesId.PYUKUMUKU]: { 0: AbilityId.PURIFYING_SALT },
[SpeciesId.TYPE_NULL]: { 0: AbilityId.CLEAR_BODY }, [SpeciesId.TYPE_NULL]: { 0: AbilityId.CLEAR_BODY },
[SpeciesId.SILVALLY]: { [SpeciesId.SILVALLY]: { 0: AbilityId.ADAPTABILITY, 1: AbilityId.ADAPTABILITY, 2: AbilityId.ADAPTABILITY, 3: AbilityId.ADAPTABILITY, 4: AbilityId.ADAPTABILITY, 5: AbilityId.ADAPTABILITY, 6: AbilityId.ADAPTABILITY, 7: AbilityId.ADAPTABILITY, 8: AbilityId.ADAPTABILITY, 9: AbilityId.ADAPTABILITY, 10: AbilityId.ADAPTABILITY, 11: AbilityId.ADAPTABILITY, 12: AbilityId.ADAPTABILITY, 13: AbilityId.ADAPTABILITY, 14: AbilityId.ADAPTABILITY, 15: AbilityId.ADAPTABILITY, 16: AbilityId.ADAPTABILITY, 17: AbilityId.ADAPTABILITY },
0: AbilityId.ADAPTABILITY, [SpeciesId.MINIOR]: { 0: AbilityId.STURDY, 1: AbilityId.STURDY, 2: AbilityId.STURDY, 3: AbilityId.STURDY, 4: AbilityId.STURDY, 5: AbilityId.STURDY, 6: AbilityId.STURDY, 7: AbilityId.AERILATE, 8: AbilityId.AERILATE, 9: AbilityId.AERILATE, 10: AbilityId.AERILATE, 11: AbilityId.AERILATE, 12: AbilityId.AERILATE, 13: AbilityId.AERILATE },
1: AbilityId.ADAPTABILITY,
2: AbilityId.ADAPTABILITY,
3: AbilityId.ADAPTABILITY,
4: AbilityId.ADAPTABILITY,
5: AbilityId.ADAPTABILITY,
6: AbilityId.ADAPTABILITY,
7: AbilityId.ADAPTABILITY,
8: AbilityId.ADAPTABILITY,
9: AbilityId.ADAPTABILITY,
10: AbilityId.ADAPTABILITY,
11: AbilityId.ADAPTABILITY,
12: AbilityId.ADAPTABILITY,
13: AbilityId.ADAPTABILITY,
14: AbilityId.ADAPTABILITY,
15: AbilityId.ADAPTABILITY,
16: AbilityId.ADAPTABILITY,
17: AbilityId.ADAPTABILITY,
},
[SpeciesId.MINIOR]: {
0: AbilityId.STURDY,
1: AbilityId.STURDY,
2: AbilityId.STURDY,
3: AbilityId.STURDY,
4: AbilityId.STURDY,
5: AbilityId.STURDY,
6: AbilityId.STURDY,
7: AbilityId.AERILATE,
8: AbilityId.AERILATE,
9: AbilityId.AERILATE,
10: AbilityId.AERILATE,
11: AbilityId.AERILATE,
12: AbilityId.AERILATE,
13: AbilityId.AERILATE,
},
[SpeciesId.KOMALA]: { 0: AbilityId.GUTS }, [SpeciesId.KOMALA]: { 0: AbilityId.GUTS },
[SpeciesId.TURTONATOR]: { 0: AbilityId.DAUNTLESS_SHIELD }, [SpeciesId.TURTONATOR]: { 0: AbilityId.DAUNTLESS_SHIELD },
[SpeciesId.TOGEDEMARU]: { 0: AbilityId.CHEEK_POUCH }, [SpeciesId.TOGEDEMARU]: { 0: AbilityId.CHEEK_POUCH },
@ -1082,12 +838,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.CELESTEELA]: { 0: AbilityId.HEATPROOF }, [SpeciesId.CELESTEELA]: { 0: AbilityId.HEATPROOF },
[SpeciesId.KARTANA]: { 0: AbilityId.TECHNICIAN }, [SpeciesId.KARTANA]: { 0: AbilityId.TECHNICIAN },
[SpeciesId.GUZZLORD]: { 0: AbilityId.POISON_HEAL }, [SpeciesId.GUZZLORD]: { 0: AbilityId.POISON_HEAL },
[SpeciesId.NECROZMA]: { [SpeciesId.NECROZMA]: { 0: AbilityId.BEAST_BOOST, 1: AbilityId.FULL_METAL_BODY, 2: AbilityId.SHADOW_SHIELD, 3: AbilityId.UNNERVE },
0: AbilityId.BEAST_BOOST,
1: AbilityId.FULL_METAL_BODY,
2: AbilityId.SHADOW_SHIELD,
3: AbilityId.UNNERVE,
},
[SpeciesId.MAGEARNA]: { 0: AbilityId.STEELY_SPIRIT, 1: AbilityId.STEELY_SPIRIT }, [SpeciesId.MAGEARNA]: { 0: AbilityId.STEELY_SPIRIT, 1: AbilityId.STEELY_SPIRIT },
[SpeciesId.MARSHADOW]: { 0: AbilityId.IRON_FIST }, [SpeciesId.MARSHADOW]: { 0: AbilityId.IRON_FIST },
[SpeciesId.POIPOLE]: { 0: AbilityId.LEVITATE }, [SpeciesId.POIPOLE]: { 0: AbilityId.LEVITATE },
@ -1168,18 +919,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.MORGREM]: { 0: AbilityId.INTIMIDATE }, [SpeciesId.MORGREM]: { 0: AbilityId.INTIMIDATE },
[SpeciesId.GRIMMSNARL]: { 0: AbilityId.INTIMIDATE, 1: AbilityId.INTIMIDATE }, [SpeciesId.GRIMMSNARL]: { 0: AbilityId.INTIMIDATE, 1: AbilityId.INTIMIDATE },
[SpeciesId.MILCERY]: { 0: AbilityId.REGENERATOR }, [SpeciesId.MILCERY]: { 0: AbilityId.REGENERATOR },
[SpeciesId.ALCREMIE]: { [SpeciesId.ALCREMIE]: { 0: AbilityId.REGENERATOR, 1: AbilityId.REGENERATOR, 2: AbilityId.REGENERATOR, 3: AbilityId.REGENERATOR, 4: AbilityId.REGENERATOR, 5: AbilityId.REGENERATOR, 6: AbilityId.REGENERATOR, 7: AbilityId.REGENERATOR, 8: AbilityId.REGENERATOR, 9: AbilityId.REGENERATOR },
0: AbilityId.REGENERATOR,
1: AbilityId.REGENERATOR,
2: AbilityId.REGENERATOR,
3: AbilityId.REGENERATOR,
4: AbilityId.REGENERATOR,
5: AbilityId.REGENERATOR,
6: AbilityId.REGENERATOR,
7: AbilityId.REGENERATOR,
8: AbilityId.REGENERATOR,
9: AbilityId.REGENERATOR,
},
[SpeciesId.FALINKS]: { 0: AbilityId.DAUNTLESS_SHIELD }, [SpeciesId.FALINKS]: { 0: AbilityId.DAUNTLESS_SHIELD },
[SpeciesId.PINCURCHIN]: { 0: AbilityId.ELECTROMORPHOSIS }, [SpeciesId.PINCURCHIN]: { 0: AbilityId.ELECTROMORPHOSIS },
[SpeciesId.SNOM]: { 0: AbilityId.SNOW_WARNING }, [SpeciesId.SNOM]: { 0: AbilityId.SNOW_WARNING },
@ -1203,12 +943,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.ZAMAZENTA]: { 0: AbilityId.UNNERVE, 1: AbilityId.UNNERVE }, [SpeciesId.ZAMAZENTA]: { 0: AbilityId.UNNERVE, 1: AbilityId.UNNERVE },
[SpeciesId.ETERNATUS]: { 0: AbilityId.NEUTRALIZING_GAS, 1: AbilityId.NEUTRALIZING_GAS }, [SpeciesId.ETERNATUS]: { 0: AbilityId.NEUTRALIZING_GAS, 1: AbilityId.NEUTRALIZING_GAS },
[SpeciesId.KUBFU]: { 0: AbilityId.IRON_FIST }, [SpeciesId.KUBFU]: { 0: AbilityId.IRON_FIST },
[SpeciesId.URSHIFU]: { [SpeciesId.URSHIFU]: { 0: AbilityId.IRON_FIST, 1: AbilityId.IRON_FIST, 2: AbilityId.IRON_FIST, 3: AbilityId.IRON_FIST },
0: AbilityId.IRON_FIST,
1: AbilityId.IRON_FIST,
2: AbilityId.IRON_FIST,
3: AbilityId.IRON_FIST,
},
[SpeciesId.ZARUDE]: { 0: AbilityId.TOUGH_CLAWS, 1: AbilityId.TOUGH_CLAWS }, [SpeciesId.ZARUDE]: { 0: AbilityId.TOUGH_CLAWS, 1: AbilityId.TOUGH_CLAWS },
[SpeciesId.REGIELEKI]: { 0: AbilityId.ELECTRIC_SURGE }, [SpeciesId.REGIELEKI]: { 0: AbilityId.ELECTRIC_SURGE },
[SpeciesId.REGIDRAGO]: { 0: AbilityId.MULTISCALE }, [SpeciesId.REGIDRAGO]: { 0: AbilityId.MULTISCALE },
@ -1309,14 +1044,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.FINIZEN]: { 0: AbilityId.FRIEND_GUARD }, [SpeciesId.FINIZEN]: { 0: AbilityId.FRIEND_GUARD },
[SpeciesId.PALAFIN]: { 0: AbilityId.EMERGENCY_EXIT, 1: AbilityId.IRON_FIST }, [SpeciesId.PALAFIN]: { 0: AbilityId.EMERGENCY_EXIT, 1: AbilityId.IRON_FIST },
[SpeciesId.VAROOM]: { 0: AbilityId.LEVITATE }, [SpeciesId.VAROOM]: { 0: AbilityId.LEVITATE },
[SpeciesId.REVAVROOM]: { [SpeciesId.REVAVROOM]: { 0: AbilityId.LEVITATE, 1: AbilityId.INTIMIDATE, 2: AbilityId.SPEED_BOOST, 3: AbilityId.TOXIC_DEBRIS, 4: AbilityId.MISTY_SURGE, 5: AbilityId.STAMINA },
0: AbilityId.LEVITATE,
1: AbilityId.INTIMIDATE,
2: AbilityId.SPEED_BOOST,
3: AbilityId.TOXIC_DEBRIS,
4: AbilityId.MISTY_SURGE,
5: AbilityId.STAMINA,
},
[SpeciesId.CYCLIZAR]: { 0: AbilityId.PROTEAN }, [SpeciesId.CYCLIZAR]: { 0: AbilityId.PROTEAN },
[SpeciesId.ORTHWORM]: { 0: AbilityId.REGENERATOR }, [SpeciesId.ORTHWORM]: { 0: AbilityId.REGENERATOR },
[SpeciesId.GLIMMET]: { 0: AbilityId.STURDY }, [SpeciesId.GLIMMET]: { 0: AbilityId.STURDY },
@ -1361,16 +1089,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.OKIDOGI]: { 0: AbilityId.DARK_AURA }, [SpeciesId.OKIDOGI]: { 0: AbilityId.DARK_AURA },
[SpeciesId.MUNKIDORI]: { 0: AbilityId.MAGICIAN }, [SpeciesId.MUNKIDORI]: { 0: AbilityId.MAGICIAN },
[SpeciesId.FEZANDIPITI]: { 0: AbilityId.PIXILATE }, [SpeciesId.FEZANDIPITI]: { 0: AbilityId.PIXILATE },
[SpeciesId.OGERPON]: { [SpeciesId.OGERPON]: { 0: AbilityId.OPPORTUNIST, 1: AbilityId.SUPER_LUCK, 2: AbilityId.FLASH_FIRE, 3: AbilityId.MAGIC_GUARD, 4: AbilityId.OPPORTUNIST, 5: AbilityId.SUPER_LUCK, 6: AbilityId.FLASH_FIRE, 7: AbilityId.MAGIC_GUARD },
0: AbilityId.OPPORTUNIST,
1: AbilityId.SUPER_LUCK,
2: AbilityId.FLASH_FIRE,
3: AbilityId.MAGIC_GUARD,
4: AbilityId.OPPORTUNIST,
5: AbilityId.SUPER_LUCK,
6: AbilityId.FLASH_FIRE,
7: AbilityId.MAGIC_GUARD,
},
[SpeciesId.GOUGING_FIRE]: { 0: AbilityId.BEAST_BOOST }, [SpeciesId.GOUGING_FIRE]: { 0: AbilityId.BEAST_BOOST },
[SpeciesId.RAGING_BOLT]: { 0: AbilityId.BEAST_BOOST }, [SpeciesId.RAGING_BOLT]: { 0: AbilityId.BEAST_BOOST },
[SpeciesId.IRON_BOULDER]: { 0: AbilityId.SHARPNESS }, [SpeciesId.IRON_BOULDER]: { 0: AbilityId.SHARPNESS },
@ -1380,5 +1099,5 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.PALDEA_TAUROS]: { 0: AbilityId.STAMINA, 1: AbilityId.ADAPTABILITY, 2: AbilityId.ADAPTABILITY }, [SpeciesId.PALDEA_TAUROS]: { 0: AbilityId.STAMINA, 1: AbilityId.ADAPTABILITY, 2: AbilityId.ADAPTABILITY },
[SpeciesId.PALDEA_WOOPER]: { 0: AbilityId.POISON_TOUCH }, [SpeciesId.PALDEA_WOOPER]: { 0: AbilityId.POISON_TOUCH },
[SpeciesId.CLODSIRE]: { 0: AbilityId.THICK_FAT }, [SpeciesId.CLODSIRE]: { 0: AbilityId.THICK_FAT },
[SpeciesId.BLOODMOON_URSALUNA]: { 0: AbilityId.BERSERK }, [SpeciesId.BLOODMOON_URSALUNA]: { 0: AbilityId.BERSERK }
}; };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -16,94 +16,91 @@ export type SignatureSpecies = {
* This means that accessing `signatureSpecies` will not throw an error if the property does not exist, * This means that accessing `signatureSpecies` will not throw an error if the property does not exist,
* but instead default to an empty array. * but instead default to an empty array.
*/ */
export const signatureSpecies: SignatureSpecies = new Proxy( export const signatureSpecies: SignatureSpecies = new Proxy({
{ // Gym Leaders- Kanto
// Gym Leaders- Kanto BROCK: [SpeciesId.ONIX, SpeciesId.GEODUDE, [SpeciesId.OMANYTE, SpeciesId.KABUTO], SpeciesId.AERODACTYL],
BROCK: [SpeciesId.ONIX, SpeciesId.GEODUDE, [SpeciesId.OMANYTE, SpeciesId.KABUTO], SpeciesId.AERODACTYL], MISTY: [SpeciesId.STARYU, SpeciesId.PSYDUCK, SpeciesId.WOOPER, SpeciesId.LAPRAS],
MISTY: [SpeciesId.STARYU, SpeciesId.PSYDUCK, SpeciesId.WOOPER, SpeciesId.LAPRAS], LT_SURGE: [SpeciesId.PICHU, SpeciesId.VOLTORB, SpeciesId.ELEKID, SpeciesId.JOLTEON],
LT_SURGE: [SpeciesId.PICHU, SpeciesId.VOLTORB, SpeciesId.ELEKID, SpeciesId.JOLTEON], ERIKA: [SpeciesId.ODDISH, SpeciesId.BELLSPROUT, SpeciesId.TANGELA, SpeciesId.HOPPIP],
ERIKA: [SpeciesId.ODDISH, SpeciesId.BELLSPROUT, SpeciesId.TANGELA, SpeciesId.HOPPIP], JANINE: [SpeciesId.VENONAT, SpeciesId.SPINARAK, SpeciesId.ZUBAT, SpeciesId.KOFFING],
JANINE: [SpeciesId.VENONAT, SpeciesId.SPINARAK, SpeciesId.ZUBAT, SpeciesId.KOFFING], SABRINA: [SpeciesId.ABRA, SpeciesId.MR_MIME, SpeciesId.SMOOCHUM, SpeciesId.ESPEON],
SABRINA: [SpeciesId.ABRA, SpeciesId.MR_MIME, SpeciesId.SMOOCHUM, SpeciesId.ESPEON], BLAINE: [SpeciesId.GROWLITHE, SpeciesId.PONYTA, SpeciesId.MAGBY, SpeciesId.VULPIX],
BLAINE: [SpeciesId.GROWLITHE, SpeciesId.PONYTA, SpeciesId.MAGBY, SpeciesId.VULPIX], GIOVANNI: [SpeciesId.RHYHORN, SpeciesId.MEOWTH, [SpeciesId.NIDORAN_F, SpeciesId.NIDORAN_M], SpeciesId.DIGLETT], // Tera Ground Meowth
GIOVANNI: [SpeciesId.RHYHORN, SpeciesId.MEOWTH, [SpeciesId.NIDORAN_F, SpeciesId.NIDORAN_M], SpeciesId.DIGLETT], // Tera Ground Meowth // Gym Leaders- Johto
// Gym Leaders- Johto FALKNER: [SpeciesId.PIDGEY, SpeciesId.HOOTHOOT, SpeciesId.NATU, SpeciesId.MURKROW],
FALKNER: [SpeciesId.PIDGEY, SpeciesId.HOOTHOOT, SpeciesId.NATU, SpeciesId.MURKROW], BUGSY: [SpeciesId.SCYTHER, SpeciesId.SHUCKLE, SpeciesId.YANMA, [SpeciesId.PINSIR, SpeciesId.HERACROSS]],
BUGSY: [SpeciesId.SCYTHER, SpeciesId.SHUCKLE, SpeciesId.YANMA, [SpeciesId.PINSIR, SpeciesId.HERACROSS]], WHITNEY: [SpeciesId.MILTANK, SpeciesId.AIPOM, SpeciesId.IGGLYBUFF, [SpeciesId.GIRAFARIG, SpeciesId.STANTLER]],
WHITNEY: [SpeciesId.MILTANK, SpeciesId.AIPOM, SpeciesId.IGGLYBUFF, [SpeciesId.GIRAFARIG, SpeciesId.STANTLER]], MORTY: [SpeciesId.GASTLY, SpeciesId.MISDREAVUS, SpeciesId.DUSKULL, SpeciesId.HISUI_TYPHLOSION],
MORTY: [SpeciesId.GASTLY, SpeciesId.MISDREAVUS, SpeciesId.DUSKULL, SpeciesId.HISUI_TYPHLOSION], CHUCK: [SpeciesId.POLIWRATH, SpeciesId.MANKEY, SpeciesId.TYROGUE, SpeciesId.MACHOP],
CHUCK: [SpeciesId.POLIWRATH, SpeciesId.MANKEY, SpeciesId.TYROGUE, SpeciesId.MACHOP], JASMINE: [SpeciesId.STEELIX, SpeciesId.MAGNEMITE, SpeciesId.PINECO, SpeciesId.SKARMORY],
JASMINE: [SpeciesId.STEELIX, SpeciesId.MAGNEMITE, SpeciesId.PINECO, SpeciesId.SKARMORY], PRYCE: [SpeciesId.SWINUB, SpeciesId.SEEL, SpeciesId.SHELLDER, SpeciesId.SNEASEL],
PRYCE: [SpeciesId.SWINUB, SpeciesId.SEEL, SpeciesId.SHELLDER, SpeciesId.SNEASEL], CLAIR: [SpeciesId.HORSEA, SpeciesId.DRATINI, SpeciesId.MAGIKARP, SpeciesId.DRUDDIGON], // Tera Dragon Magikarp
CLAIR: [SpeciesId.HORSEA, SpeciesId.DRATINI, SpeciesId.MAGIKARP, SpeciesId.DRUDDIGON], // Tera Dragon Magikarp // Gym Leaders- Hoenn
// Gym Leaders- Hoenn ROXANNE: [SpeciesId.NOSEPASS, SpeciesId.GEODUDE, [SpeciesId.LILEEP, SpeciesId.ANORITH], SpeciesId.ARON],
ROXANNE: [SpeciesId.NOSEPASS, SpeciesId.GEODUDE, [SpeciesId.LILEEP, SpeciesId.ANORITH], SpeciesId.ARON], BRAWLY: [SpeciesId.MAKUHITA, SpeciesId.MACHOP, SpeciesId.MEDITITE, SpeciesId.SHROOMISH],
BRAWLY: [SpeciesId.MAKUHITA, SpeciesId.MACHOP, SpeciesId.MEDITITE, SpeciesId.SHROOMISH], WATTSON: [SpeciesId.ELECTRIKE, SpeciesId.VOLTORB, SpeciesId.MAGNEMITE, [SpeciesId.PLUSLE, SpeciesId.MINUN]],
WATTSON: [SpeciesId.ELECTRIKE, SpeciesId.VOLTORB, SpeciesId.MAGNEMITE, [SpeciesId.PLUSLE, SpeciesId.MINUN]], FLANNERY: [SpeciesId.TORKOAL, SpeciesId.SLUGMA, SpeciesId.NUMEL, SpeciesId.HOUNDOUR],
FLANNERY: [SpeciesId.TORKOAL, SpeciesId.SLUGMA, SpeciesId.NUMEL, SpeciesId.HOUNDOUR], NORMAN: [SpeciesId.SLAKOTH, SpeciesId.KECLEON, SpeciesId.WHISMUR, SpeciesId.ZANGOOSE],
NORMAN: [SpeciesId.SLAKOTH, SpeciesId.KECLEON, SpeciesId.WHISMUR, SpeciesId.ZANGOOSE], WINONA: [SpeciesId.SWABLU, SpeciesId.WINGULL, SpeciesId.TROPIUS, SpeciesId.SKARMORY],
WINONA: [SpeciesId.SWABLU, SpeciesId.WINGULL, SpeciesId.TROPIUS, SpeciesId.SKARMORY], TATE: [SpeciesId.SOLROCK, SpeciesId.NATU, SpeciesId.CHINGLING, SpeciesId.GALLADE],
TATE: [SpeciesId.SOLROCK, SpeciesId.NATU, SpeciesId.CHINGLING, SpeciesId.GALLADE], LIZA: [SpeciesId.LUNATONE, SpeciesId.BALTOY, SpeciesId.SPOINK, SpeciesId.GARDEVOIR],
LIZA: [SpeciesId.LUNATONE, SpeciesId.BALTOY, SpeciesId.SPOINK, SpeciesId.GARDEVOIR], JUAN: [SpeciesId.HORSEA, SpeciesId.SPHEAL, SpeciesId.BARBOACH, SpeciesId.CORPHISH],
JUAN: [SpeciesId.HORSEA, SpeciesId.SPHEAL, SpeciesId.BARBOACH, SpeciesId.CORPHISH], // Gym Leaders- Sinnoh
// Gym Leaders- Sinnoh ROARK: [SpeciesId.CRANIDOS, SpeciesId.GEODUDE, SpeciesId.NOSEPASS, SpeciesId.LARVITAR],
ROARK: [SpeciesId.CRANIDOS, SpeciesId.GEODUDE, SpeciesId.NOSEPASS, SpeciesId.LARVITAR], GARDENIA: [SpeciesId.BUDEW, SpeciesId.CHERUBI, SpeciesId.TURTWIG, SpeciesId.LEAFEON],
GARDENIA: [SpeciesId.BUDEW, SpeciesId.CHERUBI, SpeciesId.TURTWIG, SpeciesId.LEAFEON], MAYLENE: [SpeciesId.RIOLU, SpeciesId.MEDITITE, SpeciesId.CHIMCHAR, SpeciesId.CROAGUNK],
MAYLENE: [SpeciesId.RIOLU, SpeciesId.MEDITITE, SpeciesId.CHIMCHAR, SpeciesId.CROAGUNK], CRASHER_WAKE: [SpeciesId.BUIZEL, SpeciesId.WOOPER, SpeciesId.PIPLUP, SpeciesId.MAGIKARP],
CRASHER_WAKE: [SpeciesId.BUIZEL, SpeciesId.WOOPER, SpeciesId.PIPLUP, SpeciesId.MAGIKARP], FANTINA: [SpeciesId.MISDREAVUS, SpeciesId.DRIFLOON, SpeciesId.DUSKULL, SpeciesId.SPIRITOMB],
FANTINA: [SpeciesId.MISDREAVUS, SpeciesId.DRIFLOON, SpeciesId.DUSKULL, SpeciesId.SPIRITOMB], BYRON: [SpeciesId.SHIELDON, SpeciesId.BRONZOR, SpeciesId.ARON, SpeciesId.SKARMORY],
BYRON: [SpeciesId.SHIELDON, SpeciesId.BRONZOR, SpeciesId.ARON, SpeciesId.SKARMORY], CANDICE: [SpeciesId.FROSLASS, SpeciesId.SNOVER, SpeciesId.SNEASEL, SpeciesId.GLACEON],
CANDICE: [SpeciesId.FROSLASS, SpeciesId.SNOVER, SpeciesId.SNEASEL, SpeciesId.GLACEON], VOLKNER: [SpeciesId.ELEKID, SpeciesId.SHINX, SpeciesId.CHINCHOU, SpeciesId.ROTOM],
VOLKNER: [SpeciesId.ELEKID, SpeciesId.SHINX, SpeciesId.CHINCHOU, SpeciesId.ROTOM], // Gym Leaders- Unova
// Gym Leaders- Unova CILAN: [SpeciesId.PANSAGE, SpeciesId.SNIVY, SpeciesId.MARACTUS, SpeciesId.FERROSEED],
CILAN: [SpeciesId.PANSAGE, SpeciesId.SNIVY, SpeciesId.MARACTUS, SpeciesId.FERROSEED], CHILI: [SpeciesId.PANSEAR, SpeciesId.TEPIG, SpeciesId.HEATMOR, SpeciesId.DARUMAKA],
CHILI: [SpeciesId.PANSEAR, SpeciesId.TEPIG, SpeciesId.HEATMOR, SpeciesId.DARUMAKA], CRESS: [SpeciesId.PANPOUR, SpeciesId.OSHAWOTT, SpeciesId.BASCULIN, SpeciesId.TYMPOLE],
CRESS: [SpeciesId.PANPOUR, SpeciesId.OSHAWOTT, SpeciesId.BASCULIN, SpeciesId.TYMPOLE], CHEREN: [SpeciesId.LILLIPUP, SpeciesId.MINCCINO, SpeciesId.PIDOVE, SpeciesId.BOUFFALANT],
CHEREN: [SpeciesId.LILLIPUP, SpeciesId.MINCCINO, SpeciesId.PIDOVE, SpeciesId.BOUFFALANT], LENORA: [SpeciesId.PATRAT, SpeciesId.DEERLING, SpeciesId.AUDINO, SpeciesId.BRAVIARY],
LENORA: [SpeciesId.PATRAT, SpeciesId.DEERLING, SpeciesId.AUDINO, SpeciesId.BRAVIARY], ROXIE: [SpeciesId.VENIPEDE, SpeciesId.KOFFING, SpeciesId.TRUBBISH, SpeciesId.TOXEL],
ROXIE: [SpeciesId.VENIPEDE, SpeciesId.KOFFING, SpeciesId.TRUBBISH, SpeciesId.TOXEL], BURGH: [SpeciesId.SEWADDLE, SpeciesId.DWEBBLE, [SpeciesId.KARRABLAST, SpeciesId.SHELMET], SpeciesId.DURANT],
BURGH: [SpeciesId.SEWADDLE, SpeciesId.DWEBBLE, [SpeciesId.KARRABLAST, SpeciesId.SHELMET], SpeciesId.DURANT], ELESA: [SpeciesId.BLITZLE, SpeciesId.EMOLGA, SpeciesId.JOLTIK, SpeciesId.TYNAMO],
ELESA: [SpeciesId.BLITZLE, SpeciesId.EMOLGA, SpeciesId.JOLTIK, SpeciesId.TYNAMO], CLAY: [SpeciesId.DRILBUR, SpeciesId.SANDILE, SpeciesId.TYMPOLE, SpeciesId.GOLETT],
CLAY: [SpeciesId.DRILBUR, SpeciesId.SANDILE, SpeciesId.TYMPOLE, SpeciesId.GOLETT], SKYLA: [SpeciesId.DUCKLETT, SpeciesId.WOOBAT, [SpeciesId.RUFFLET, SpeciesId.VULLABY], SpeciesId.ARCHEN],
SKYLA: [SpeciesId.DUCKLETT, SpeciesId.WOOBAT, [SpeciesId.RUFFLET, SpeciesId.VULLABY], SpeciesId.ARCHEN], BRYCEN: [SpeciesId.CRYOGONAL, SpeciesId.VANILLITE, SpeciesId.CUBCHOO, SpeciesId.GALAR_DARUMAKA],
BRYCEN: [SpeciesId.CRYOGONAL, SpeciesId.VANILLITE, SpeciesId.CUBCHOO, SpeciesId.GALAR_DARUMAKA], DRAYDEN: [SpeciesId.AXEW, SpeciesId.DRUDDIGON, SpeciesId.TRAPINCH, SpeciesId.DEINO],
DRAYDEN: [SpeciesId.AXEW, SpeciesId.DRUDDIGON, SpeciesId.TRAPINCH, SpeciesId.DEINO], MARLON: [SpeciesId.FRILLISH, SpeciesId.TIRTOUGA, SpeciesId.WAILMER, SpeciesId.MANTYKE],
MARLON: [SpeciesId.FRILLISH, SpeciesId.TIRTOUGA, SpeciesId.WAILMER, SpeciesId.MANTYKE], // Gym Leaders- Kalos
// Gym Leaders- Kalos VIOLA: [SpeciesId.SCATTERBUG, SpeciesId.SURSKIT, SpeciesId.CUTIEFLY, SpeciesId.BLIPBUG],
VIOLA: [SpeciesId.SCATTERBUG, SpeciesId.SURSKIT, SpeciesId.CUTIEFLY, SpeciesId.BLIPBUG], GRANT: [SpeciesId.TYRUNT, SpeciesId.AMAURA, SpeciesId.BINACLE, SpeciesId.DWEBBLE],
GRANT: [SpeciesId.TYRUNT, SpeciesId.AMAURA, SpeciesId.BINACLE, SpeciesId.DWEBBLE], KORRINA: [SpeciesId.RIOLU, SpeciesId.MIENFOO, SpeciesId.HAWLUCHA, SpeciesId.PANCHAM],
KORRINA: [SpeciesId.RIOLU, SpeciesId.MIENFOO, SpeciesId.HAWLUCHA, SpeciesId.PANCHAM], RAMOS: [SpeciesId.SKIDDO, SpeciesId.HOPPIP, SpeciesId.BELLSPROUT, [SpeciesId.PHANTUMP, SpeciesId.PUMPKABOO]],
RAMOS: [SpeciesId.SKIDDO, SpeciesId.HOPPIP, SpeciesId.BELLSPROUT, [SpeciesId.PHANTUMP, SpeciesId.PUMPKABOO]], CLEMONT: [SpeciesId.HELIOPTILE, SpeciesId.MAGNEMITE, SpeciesId.DEDENNE, SpeciesId.ROTOM],
CLEMONT: [SpeciesId.HELIOPTILE, SpeciesId.MAGNEMITE, SpeciesId.DEDENNE, SpeciesId.ROTOM], VALERIE: [SpeciesId.SYLVEON, SpeciesId.MAWILE, SpeciesId.MR_MIME, [SpeciesId.SPRITZEE, SpeciesId.SWIRLIX]],
VALERIE: [SpeciesId.SYLVEON, SpeciesId.MAWILE, SpeciesId.MR_MIME, [SpeciesId.SPRITZEE, SpeciesId.SWIRLIX]], OLYMPIA: [SpeciesId.ESPURR, SpeciesId.SIGILYPH, SpeciesId.INKAY, SpeciesId.SLOWKING],
OLYMPIA: [SpeciesId.ESPURR, SpeciesId.SIGILYPH, SpeciesId.INKAY, SpeciesId.SLOWKING], WULFRIC: [SpeciesId.BERGMITE, SpeciesId.SNOVER, SpeciesId.CRYOGONAL, SpeciesId.SWINUB],
WULFRIC: [SpeciesId.BERGMITE, SpeciesId.SNOVER, SpeciesId.CRYOGONAL, SpeciesId.SWINUB], // Gym Leaders- Galar
// Gym Leaders- Galar MILO: [SpeciesId.GOSSIFLEUR, SpeciesId.SEEDOT, SpeciesId.APPLIN, SpeciesId.LOTAD],
MILO: [SpeciesId.GOSSIFLEUR, SpeciesId.SEEDOT, SpeciesId.APPLIN, SpeciesId.LOTAD], NESSA: [SpeciesId.CHEWTLE, SpeciesId.WIMPOD, SpeciesId.ARROKUDA, SpeciesId.MAREANIE],
NESSA: [SpeciesId.CHEWTLE, SpeciesId.WIMPOD, SpeciesId.ARROKUDA, SpeciesId.MAREANIE], KABU: [SpeciesId.SIZZLIPEDE, SpeciesId.VULPIX, SpeciesId.GROWLITHE, SpeciesId.TORKOAL],
KABU: [SpeciesId.SIZZLIPEDE, SpeciesId.VULPIX, SpeciesId.GROWLITHE, SpeciesId.TORKOAL], BEA: [SpeciesId.MACHOP, SpeciesId.GALAR_FARFETCHD, SpeciesId.CLOBBOPUS, SpeciesId.FALINKS],
BEA: [SpeciesId.MACHOP, SpeciesId.GALAR_FARFETCHD, SpeciesId.CLOBBOPUS, SpeciesId.FALINKS], ALLISTER: [SpeciesId.GASTLY, SpeciesId.GALAR_YAMASK, SpeciesId.GALAR_CORSOLA, SpeciesId.SINISTEA],
ALLISTER: [SpeciesId.GASTLY, SpeciesId.GALAR_YAMASK, SpeciesId.GALAR_CORSOLA, SpeciesId.SINISTEA], OPAL: [SpeciesId.MILCERY, SpeciesId.GALAR_WEEZING, SpeciesId.TOGEPI, SpeciesId.MAWILE],
OPAL: [SpeciesId.MILCERY, SpeciesId.GALAR_WEEZING, SpeciesId.TOGEPI, SpeciesId.MAWILE], BEDE: [SpeciesId.HATENNA, SpeciesId.GALAR_PONYTA, SpeciesId.GARDEVOIR, SpeciesId.SYLVEON],
BEDE: [SpeciesId.HATENNA, SpeciesId.GALAR_PONYTA, SpeciesId.GARDEVOIR, SpeciesId.SYLVEON], GORDIE: [SpeciesId.ROLYCOLY, [SpeciesId.SHUCKLE, SpeciesId.BINACLE], SpeciesId.STONJOURNER, SpeciesId.LARVITAR],
GORDIE: [SpeciesId.ROLYCOLY, [SpeciesId.SHUCKLE, SpeciesId.BINACLE], SpeciesId.STONJOURNER, SpeciesId.LARVITAR], MELONY: [SpeciesId.LAPRAS, SpeciesId.SNOM, SpeciesId.EISCUE, [SpeciesId.GALAR_MR_MIME, SpeciesId.GALAR_DARUMAKA]],
MELONY: [SpeciesId.LAPRAS, SpeciesId.SNOM, SpeciesId.EISCUE, [SpeciesId.GALAR_MR_MIME, SpeciesId.GALAR_DARUMAKA]], PIERS: [SpeciesId.GALAR_ZIGZAGOON, SpeciesId.SCRAGGY, SpeciesId.TOXEL, SpeciesId.INKAY], // Tera Dark Toxel
PIERS: [SpeciesId.GALAR_ZIGZAGOON, SpeciesId.SCRAGGY, SpeciesId.TOXEL, SpeciesId.INKAY], // Tera Dark Toxel MARNIE: [SpeciesId.IMPIDIMP, SpeciesId.MORPEKO, SpeciesId.PURRLOIN, SpeciesId.CROAGUNK], // Tera Dark Croagunk
MARNIE: [SpeciesId.IMPIDIMP, SpeciesId.MORPEKO, SpeciesId.PURRLOIN, SpeciesId.CROAGUNK], // Tera Dark Croagunk RAIHAN: [SpeciesId.DURALUDON, SpeciesId.TRAPINCH, SpeciesId.GOOMY, SpeciesId.TURTONATOR],
RAIHAN: [SpeciesId.DURALUDON, SpeciesId.TRAPINCH, SpeciesId.GOOMY, SpeciesId.TURTONATOR], // Gym Leaders- Paldea; First slot is Tera
// Gym Leaders- Paldea; First slot is Tera KATY: [SpeciesId.TEDDIURSA, SpeciesId.NYMBLE, SpeciesId.TAROUNTULA, SpeciesId.RELLOR], // Tera Bug Teddiursa
KATY: [SpeciesId.TEDDIURSA, SpeciesId.NYMBLE, SpeciesId.TAROUNTULA, SpeciesId.RELLOR], // Tera Bug Teddiursa BRASSIUS: [SpeciesId.BONSLY, SpeciesId.SMOLIV, SpeciesId.BRAMBLIN, SpeciesId.SUNKERN], // Tera Grass Bonsly
BRASSIUS: [SpeciesId.BONSLY, SpeciesId.SMOLIV, SpeciesId.BRAMBLIN, SpeciesId.SUNKERN], // Tera Grass Bonsly IONO: [SpeciesId.MISDREAVUS, SpeciesId.TADBULB, SpeciesId.WATTREL, SpeciesId.MAGNEMITE], // Tera Ghost Misdreavus
IONO: [SpeciesId.MISDREAVUS, SpeciesId.TADBULB, SpeciesId.WATTREL, SpeciesId.MAGNEMITE], // Tera Ghost Misdreavus KOFU: [SpeciesId.CRABRAWLER, SpeciesId.VELUZA, SpeciesId.WIGLETT, SpeciesId.WINGULL], // Tera Water Crabrawler
KOFU: [SpeciesId.CRABRAWLER, SpeciesId.VELUZA, SpeciesId.WIGLETT, SpeciesId.WINGULL], // Tera Water Crabrawler LARRY: [SpeciesId.STARLY, SpeciesId.DUNSPARCE, SpeciesId.LECHONK, SpeciesId.KOMALA], // Tera Normal Starly
LARRY: [SpeciesId.STARLY, SpeciesId.DUNSPARCE, SpeciesId.LECHONK, SpeciesId.KOMALA], // Tera Normal Starly RYME: [SpeciesId.TOXEL, SpeciesId.GREAVARD, SpeciesId.SHUPPET, SpeciesId.MIMIKYU], // Tera Ghost Toxel
RYME: [SpeciesId.TOXEL, SpeciesId.GREAVARD, SpeciesId.SHUPPET, SpeciesId.MIMIKYU], // Tera Ghost Toxel TULIP: [SpeciesId.FLABEBE, SpeciesId.FLITTLE, SpeciesId.RALTS, SpeciesId.GIRAFARIG], // Tera Psychic Flabebe
TULIP: [SpeciesId.FLABEBE, SpeciesId.FLITTLE, SpeciesId.RALTS, SpeciesId.GIRAFARIG], // Tera Psychic Flabebe GRUSHA: [SpeciesId.SWABLU, SpeciesId.CETODDLE, SpeciesId.SNOM, SpeciesId.CUBCHOO], // Tera Ice Swablu
GRUSHA: [SpeciesId.SWABLU, SpeciesId.CETODDLE, SpeciesId.SNOM, SpeciesId.CUBCHOO], // Tera Ice Swablu }, {
}, get(target, prop: string) {
{ return target[prop as keyof SignatureSpecies] ?? [];
get(target, prop: string) { }
return target[prop as keyof SignatureSpecies] ?? []; });
},
},
);

View File

@ -581,5 +581,5 @@ export const speciesEggTiers = {
[SpeciesId.PECHARUNT]: EggTier.EPIC, [SpeciesId.PECHARUNT]: EggTier.EPIC,
[SpeciesId.PALDEA_TAUROS]: EggTier.RARE, [SpeciesId.PALDEA_TAUROS]: EggTier.RARE,
[SpeciesId.PALDEA_WOOPER]: EggTier.RARE, [SpeciesId.PALDEA_WOOPER]: EggTier.RARE,
[SpeciesId.BLOODMOON_URSALUNA]: EggTier.EPIC, [SpeciesId.BLOODMOON_URSALUNA]: EggTier.EPIC
}; };

View File

@ -618,17 +618,17 @@ export const speciesStarterCosts = {
[SpeciesId.BLOODMOON_URSALUNA]: 5, [SpeciesId.BLOODMOON_URSALUNA]: 5,
} as const; } as const;
const starterCandyCosts: { passive: number; costReduction: [number, number]; egg: number }[] = [ const starterCandyCosts: { passive: number; costReduction: [number, number]; egg: number; }[] = [
{ passive: 40, costReduction: [25, 60], egg: 30 }, // 1 Cost { passive: 40, costReduction: [ 25, 60 ], egg: 30 }, // 1 Cost
{ passive: 40, costReduction: [25, 60], egg: 30 }, // 2 Cost { passive: 40, costReduction: [ 25, 60 ], egg: 30 }, // 2 Cost
{ passive: 35, costReduction: [20, 50], egg: 25 }, // 3 Cost { passive: 35, costReduction: [ 20, 50 ], egg: 25 }, // 3 Cost
{ passive: 30, costReduction: [15, 40], egg: 20 }, // 4 Cost { passive: 30, costReduction: [ 15, 40 ], egg: 20 }, // 4 Cost
{ passive: 25, costReduction: [12, 35], egg: 18 }, // 5 Cost { passive: 25, costReduction: [ 12, 35 ], egg: 18 }, // 5 Cost
{ passive: 20, costReduction: [10, 30], egg: 15 }, // 6 Cost { passive: 20, costReduction: [ 10, 30 ], egg: 15 }, // 6 Cost
{ passive: 15, costReduction: [8, 20], egg: 12 }, // 7 Cost { passive: 15, costReduction: [ 8, 20 ], egg: 12 }, // 7 Cost
{ passive: 10, costReduction: [5, 15], egg: 10 }, // 8 Cost { passive: 10, costReduction: [ 5, 15 ], egg: 10 }, // 8 Cost
{ passive: 10, costReduction: [5, 15], egg: 10 }, // 9 Cost { passive: 10, costReduction: [ 5, 15 ], egg: 10 }, // 9 Cost
{ passive: 10, costReduction: [5, 15], egg: 10 }, // 10 Cost { passive: 10, costReduction: [ 5, 15 ], egg: 10 }, // 10 Cost
]; ];
/** /**

View File

@ -812,8 +812,8 @@ export abstract class BattleAnim {
x = point[0]; x = point[0];
y = point[1]; y = point[1];
if ( if (
frame.target === AnimFrameTarget.GRAPHIC frame.target === AnimFrameTarget.GRAPHIC &&
&& isReversed(this.srcLine[0], this.srcLine[2], this.dstLine[0], this.dstLine[2]) isReversed(this.srcLine[0], this.srcLine[2], this.dstLine[0], this.dstLine[2])
) { ) {
scaleX = scaleX * -1; scaleX = scaleX * -1;
} }
@ -822,7 +822,7 @@ export abstract class BattleAnim {
} }
const angle = -frame.angle; const angle = -frame.angle;
const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++; const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++;
ret.get(frame.target)!.set(key, { x, y, scaleX, scaleY, angle }); // TODO: is the bang correct? ret.get(frame.target)!.set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle }); // TODO: is the bang correct?
} }
return ret; return ret;
@ -1145,11 +1145,11 @@ export abstract class BattleAnim {
const angle = -frame.angle; const angle = -frame.angle;
const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++; const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++;
ret.get(frame.target)?.set(key, { ret.get(frame.target)?.set(key, {
x, x: x,
y, y: y,
scaleX, scaleX: scaleX,
scaleY, scaleY: scaleY,
angle, angle: angle,
}); });
} }

View File

@ -1134,8 +1134,8 @@ export class PowderTag extends BattlerTag {
const move = movePhase.move.getMove(); const move = movePhase.move.getMove();
const weather = globalScene.arena.weather; const weather = globalScene.arena.weather;
if ( if (
pokemon.getMoveType(move) !== PokemonType.FIRE pokemon.getMoveType(move) !== PokemonType.FIRE ||
|| (weather?.weatherType === WeatherType.HEAVY_RAIN && !weather.isEffectSuppressed()) // Heavy rain takes priority over powder (weather?.weatherType === WeatherType.HEAVY_RAIN && !weather.isEffectSuppressed()) // Heavy rain takes priority over powder
) { ) {
return true; return true;
} }
@ -1792,9 +1792,9 @@ export abstract class ContactProtectedTag extends ProtectedTag {
const moveData = getMoveEffectPhaseData(pokemon); const moveData = getMoveEffectPhaseData(pokemon);
if ( if (
lapseType === BattlerTagLapseType.CUSTOM lapseType === BattlerTagLapseType.CUSTOM &&
&& moveData moveData &&
&& moveData.move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: moveData.attacker, target: pokemon }) moveData.move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: moveData.attacker, target: pokemon })
) { ) {
this.onContact(moveData.attacker, pokemon); this.onContact(moveData.attacker, pokemon);
} }
@ -2816,9 +2816,9 @@ export class GulpMissileTag extends SerializableBattlerTag {
// Bang here is OK as if sourceMove was undefined, this would just evaluate to false // Bang here is OK as if sourceMove was undefined, this would just evaluate to false
const isSurfOrDive = [MoveId.SURF, MoveId.DIVE].includes(this.sourceMove!); const isSurfOrDive = [MoveId.SURF, MoveId.DIVE].includes(this.sourceMove!);
const isNormalForm = const isNormalForm =
pokemon.formIndex === 0 pokemon.formIndex === 0 &&
&& !pokemon.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA) !pokemon.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA) &&
&& !pokemon.getTag(BattlerTagType.GULP_MISSILE_PIKACHU); !pokemon.getTag(BattlerTagType.GULP_MISSILE_PIKACHU);
const isCramorant = pokemon.species.speciesId === SpeciesId.CRAMORANT; const isCramorant = pokemon.species.speciesId === SpeciesId.CRAMORANT;
return isSurfOrDive && isNormalForm && isCramorant; return isSurfOrDive && isNormalForm && isCramorant;
@ -3859,7 +3859,7 @@ function getMoveEffectPhaseData(_pokemon: Pokemon): { phase: MoveEffectPhase; at
const phase = globalScene.phaseManager.getCurrentPhase(); const phase = globalScene.phaseManager.getCurrentPhase();
if (phase?.is("MoveEffectPhase")) { if (phase?.is("MoveEffectPhase")) {
return { return {
phase, phase: phase,
attacker: phase.getPokemon(), attacker: phase.getPokemon(),
move: phase.move, move: phase.move,
}; };

View File

@ -141,8 +141,8 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
{ {
// Pick the first move completely out of PP, or else the first one that has any PP missing // Pick the first move completely out of PP, or else the first one that has any PP missing
const ppRestoreMove = const ppRestoreMove =
consumer.getMoveset().find(m => m.ppUsed === m.getMovePp()) consumer.getMoveset().find(m => m.ppUsed === m.getMovePp()) ??
?? consumer.getMoveset().find(m => m.ppUsed < m.getMovePp()); consumer.getMoveset().find(m => m.ppUsed < m.getMovePp());
if (ppRestoreMove) { if (ppRestoreMove) {
ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0); ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0);
globalScene.phaseManager.queueMessage( globalScene.phaseManager.queueMessage(

View File

@ -439,8 +439,8 @@ export class SingleGenerationChallenge extends Challenge {
const baseGeneration = getPokemonSpecies(pokemon.species.speciesId).generation; const baseGeneration = getPokemonSpecies(pokemon.species.speciesId).generation;
const fusionGeneration = pokemon.isFusion() ? getPokemonSpecies(pokemon.fusionSpecies!.speciesId).generation : 0; const fusionGeneration = pokemon.isFusion() ? getPokemonSpecies(pokemon.fusionSpecies!.speciesId).generation : 0;
if ( if (
pokemon.isPlayer() pokemon.isPlayer() &&
&& (baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value)) (baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value))
) { ) {
valid.value = false; valid.value = false;
return true; return true;
@ -707,12 +707,12 @@ export class SingleTypeChallenge extends Challenge {
applyPokemonInBattle(pokemon: Pokemon, valid: BooleanHolder): boolean { applyPokemonInBattle(pokemon: Pokemon, valid: BooleanHolder): boolean {
if ( if (
pokemon.isPlayer() pokemon.isPlayer() &&
&& !pokemon.isOfType(this.value - 1, false, false, true) !pokemon.isOfType(this.value - 1, false, false, true) &&
&& !SingleTypeChallenge.TYPE_OVERRIDES.some( !SingleTypeChallenge.TYPE_OVERRIDES.some(
o => o =>
o.type === this.value - 1 o.type === this.value - 1 &&
&& (pokemon.isFusion() && o.fusion ? pokemon.fusionSpecies! : pokemon.species).speciesId === o.species, (pokemon.isFusion() && o.fusion ? pokemon.fusionSpecies! : pokemon.species).speciesId === o.species,
) )
) { ) {
// TODO: is the bang on fusionSpecies correct? // TODO: is the bang on fusionSpecies correct?
@ -794,8 +794,8 @@ export class FreshStartChallenge extends Challenge {
if (pokemon.species.speciesId === SpeciesId.ZYGARDE && pokemon.formIndex >= 2) { if (pokemon.species.speciesId === SpeciesId.ZYGARDE && pokemon.formIndex >= 2) {
pokemon.formIndex -= 2; // Sets 10%-PC to 10%-AB and 50%-PC to 50%-AB pokemon.formIndex -= 2; // Sets 10%-PC to 10%-AB and 50%-PC to 50%-AB
} else if ( } else if (
pokemon.formIndex > 0 pokemon.formIndex > 0 &&
&& [ [
SpeciesId.PIKACHU, SpeciesId.PIKACHU,
SpeciesId.EEVEE, SpeciesId.EEVEE,
SpeciesId.PICHU, SpeciesId.PICHU,

View File

@ -221,9 +221,9 @@ export class Egg {
public isManaphyEgg(): boolean { public isManaphyEgg(): boolean {
return ( return (
this._species === SpeciesId.PHIONE this._species === SpeciesId.PHIONE ||
|| this._species === SpeciesId.MANAPHY this._species === SpeciesId.MANAPHY ||
|| (this._tier === EggTier.COMMON && !(this._id % 204) && !this._species) (this._tier === EggTier.COMMON && !(this._id % 204) && !this._species)
); );
} }
@ -325,15 +325,15 @@ export class Egg {
switch (this.sourceType) { switch (this.sourceType) {
case EggSourceType.SAME_SPECIES_EGG: case EggSourceType.SAME_SPECIES_EGG:
return ( return (
this._eggDescriptor this._eggDescriptor ??
?? i18next.t("egg:sameSpeciesEgg", { i18next.t("egg:sameSpeciesEgg", {
species: getPokemonSpecies(this._species).getName(), species: getPokemonSpecies(this._species).getName(),
}) })
); );
case EggSourceType.GACHA_LEGENDARY: case EggSourceType.GACHA_LEGENDARY:
return ( return (
this._eggDescriptor this._eggDescriptor ??
?? `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(this.timestamp)).getName()})` `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(this.timestamp)).getName()})`
); );
case EggSourceType.GACHA_SHINY: case EggSourceType.GACHA_SHINY:
return this._eggDescriptor ?? i18next.t("egg:gachaTypeShiny"); return this._eggDescriptor ?? i18next.t("egg:gachaTypeShiny");
@ -452,9 +452,9 @@ export class Egg {
.map(s => Number.parseInt(s) as SpeciesId) .map(s => Number.parseInt(s) as SpeciesId)
.filter( .filter(
s => s =>
!pokemonPrevolutions.hasOwnProperty(s) !pokemonPrevolutions.hasOwnProperty(s) &&
&& getPokemonSpecies(s).isObtainable() getPokemonSpecies(s).isObtainable() &&
&& ignoredSpecies.indexOf(s) === -1, ignoredSpecies.indexOf(s) === -1,
); );
// If this is the 10th egg without unlocking something new, attempt to force it. // If this is the 10th egg without unlocking something new, attempt to force it.
@ -508,8 +508,8 @@ export class Egg {
species = species!; // tell TS compiled it's defined now! species = species!; // tell TS compiled it's defined now!
if ( if (
globalScene.gameData.dexData[species].caughtAttr globalScene.gameData.dexData[species].caughtAttr ||
|| globalScene.gameData.eggs.some(e => e.species === species) globalScene.gameData.eggs.some(e => e.species === species)
) { ) {
globalScene.gameData.unlockPity[this.tier] = Math.min(globalScene.gameData.unlockPity[this.tier] + 1, 10); globalScene.gameData.unlockPity[this.tier] = Math.min(globalScene.gameData.unlockPity[this.tier] + 1, 10);
} else { } else {
@ -565,8 +565,8 @@ export class Egg {
globalScene.gameData.eggPity[EggTier.LEGENDARY] += 1 + tierValueOffset; globalScene.gameData.eggPity[EggTier.LEGENDARY] += 1 + tierValueOffset;
// These numbers are roughly the 80% mark. That is, 80% of the time you'll get an egg before this gets triggered. // These numbers are roughly the 80% mark. That is, 80% of the time you'll get an egg before this gets triggered.
if ( if (
globalScene.gameData.eggPity[EggTier.LEGENDARY] >= EGG_PITY_LEGENDARY_THRESHOLD globalScene.gameData.eggPity[EggTier.LEGENDARY] >= EGG_PITY_LEGENDARY_THRESHOLD &&
&& this._tier === EggTier.COMMON this._tier === EggTier.COMMON
) { ) {
this._tier = EggTier.LEGENDARY; this._tier = EggTier.LEGENDARY;
} else if (globalScene.gameData.eggPity[EggTier.EPIC] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) { } else if (globalScene.gameData.eggPity[EggTier.EPIC] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) {

View File

@ -86,11 +86,11 @@ import { PokemonHealPhase } from "#phases/pokemon-heal-phase";
import { SwitchSummonPhase } from "#phases/switch-summon-phase"; import { SwitchSummonPhase } from "#phases/switch-summon-phase";
import type { AttackMoveResult } from "#types/attack-move-result"; import type { AttackMoveResult } from "#types/attack-move-result";
import type { Localizable } from "#types/locales"; import type { Localizable } from "#types/locales";
import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindString, MoveMessageFunc } from "#types/move-types"; import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindString } from "#types/move-types";
import type { TurnMove } from "#types/turn-move"; import type { TurnMove } from "#types/turn-move";
import { BooleanHolder, type Constructor, isNullOrUndefined, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common"; import { BooleanHolder, type Constructor, isNullOrUndefined, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common";
import { getEnumValues } from "#utils/enums"; import { getEnumValues } from "#utils/enums";
import { toCamelCase, toTitleCase } from "#utils/strings"; import { toTitleCase } from "#utils/strings";
import i18next from "i18next"; import i18next from "i18next";
import { applyChallenges } from "#utils/challenge-utils"; import { applyChallenges } from "#utils/challenge-utils";
@ -162,16 +162,10 @@ export abstract class Move implements Localizable {
} }
localize(): void { localize(): void {
const i18nKey = toCamelCase(MoveId[this.id]) const i18nKey = MoveId[this.id].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("") as unknown as string;
if (this.id === MoveId.NONE) { this.name = this.id ? `${i18next.t(`move:${i18nKey}.name`)}${this.nameAppend}` : "";
this.name = ""; this.effect = this.id ? `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}` : "";
this.effect = ""
return;
}
this.name = `${i18next.t(`move:${i18nKey}.name`)}${this.nameAppend}`;
this.effect = `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}`;
} }
/** /**
@ -1363,20 +1357,20 @@ export class MoveHeaderAttr extends MoveAttr {
/** /**
* Header attribute to queue a message at the beginning of a turn. * Header attribute to queue a message at the beginning of a turn.
* @see {@link MoveHeaderAttr}
*/ */
export class MessageHeaderAttr extends MoveHeaderAttr { export class MessageHeaderAttr extends MoveHeaderAttr {
/** The message to display, or a function producing one. */ private message: string | ((user: Pokemon, move: Move) => string);
private message: string | MoveMessageFunc;
constructor(message: string | MoveMessageFunc) { constructor(message: string | ((user: Pokemon, move: Move) => string)) {
super(); super();
this.message = message; this.message = message;
} }
apply(user: Pokemon, target: Pokemon, move: Move): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const message = typeof this.message === "string" const message = typeof this.message === "string"
? this.message ? this.message
: this.message(user, target, move); : this.message(user, move);
if (message) { if (message) {
globalScene.phaseManager.queueMessage(message); globalScene.phaseManager.queueMessage(message);
@ -1424,21 +1418,21 @@ export class BeakBlastHeaderAttr extends AddBattlerTagHeaderAttr {
*/ */
export class PreMoveMessageAttr extends MoveAttr { export class PreMoveMessageAttr extends MoveAttr {
/** The message to display or a function returning one */ /** The message to display or a function returning one */
private message: string | MoveMessageFunc; private message: string | ((user: Pokemon, target: Pokemon, move: Move) => string | undefined);
/** /**
* Create a new {@linkcode PreMoveMessageAttr} to display a message before move execution. * Create a new {@linkcode PreMoveMessageAttr} to display a message before move execution.
* @param message - The message to display before move use, either` a literal string or a function producing one. * @param message - The message to display before move use, either as a string or a function producing one.
* @remarks * @remarks
* If {@linkcode message} evaluates to an empty string (`""`), no message will be displayed * If {@linkcode message} evaluates to an empty string (`''`), no message will be displayed
* (though the move will still succeed). * (though the move will still succeed).
*/ */
constructor(message: string | MoveMessageFunc) { constructor(message: string | ((user: Pokemon, target: Pokemon, move: Move) => string)) {
super(); super();
this.message = message; this.message = message;
} }
apply(user: Pokemon, target: Pokemon, move: Move): boolean { apply(user: Pokemon, target: Pokemon, move: Move, _args: any[]): boolean {
const message = typeof this.message === "function" const message = typeof this.message === "function"
? this.message(user, target, move) ? this.message(user, target, move)
: this.message; : this.message;
@ -1459,17 +1453,18 @@ export class PreMoveMessageAttr extends MoveAttr {
* @extends MoveAttr * @extends MoveAttr
*/ */
export class PreUseInterruptAttr extends MoveAttr { export class PreUseInterruptAttr extends MoveAttr {
protected message: string | MoveMessageFunc; protected message?: string | ((user: Pokemon, target: Pokemon, move: Move) => string);
protected overridesFailedMessage: boolean;
protected conditionFunc: MoveConditionFunc; protected conditionFunc: MoveConditionFunc;
/** /**
* Create a new MoveInterruptedMessageAttr. * Create a new MoveInterruptedMessageAttr.
* @param message The message to display when the move is interrupted, or a function that formats the message based on the user, target, and move. * @param message The message to display when the move is interrupted, or a function that formats the message based on the user, target, and move.
*/ */
constructor(message: string | MoveMessageFunc, conditionFunc: MoveConditionFunc) { constructor(message?: string | ((user: Pokemon, target: Pokemon, move: Move) => string), conditionFunc?: MoveConditionFunc) {
super(); super();
this.message = message; this.message = message;
this.conditionFunc = conditionFunc; this.conditionFunc = conditionFunc ?? (() => true);
} }
/** /**
@ -1490,9 +1485,11 @@ export class PreUseInterruptAttr extends MoveAttr {
*/ */
override getFailedText(user: Pokemon, target: Pokemon, move: Move): string | undefined { override getFailedText(user: Pokemon, target: Pokemon, move: Move): string | undefined {
if (this.message && this.conditionFunc(user, target, move)) { if (this.message && this.conditionFunc(user, target, move)) {
return typeof this.message === "string" const message =
? this.message typeof this.message === "string"
? (this.message as string)
: this.message(user, target, move); : this.message(user, target, move);
return message;
} }
} }
} }
@ -1697,30 +1694,17 @@ export class SurviveDamageAttr extends ModifiedDamageAttr {
} }
} }
/** export class SplashAttr extends MoveEffectAttr {
* Move attribute to display arbitrary text during a move's execution. apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
*/ globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:splash"));
export class MessageAttr extends MoveEffectAttr { return true;
/** The message to display, either as a string or a function returning one. */
private message: string | MoveMessageFunc;
constructor(message: string | MoveMessageFunc, options?: MoveEffectAttrOptions) {
// TODO: Do we need to respect `selfTarget` if we're just displaying text?
super(false, options)
this.message = message;
} }
}
override apply(user: Pokemon, target: Pokemon, move: Move): boolean { export class CelebrateAttr extends MoveEffectAttr {
const message = typeof this.message === "function" apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
? this.message(user, target, move) globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:celebrate", { playerName: loggedInUser?.username }));
: this.message; return true;
// TODO: Consider changing if/when MoveAttr `apply` return values become significant
if (message) {
globalScene.phaseManager.queueMessage(message, 500);
return true;
}
return false;
} }
} }
@ -5932,8 +5916,8 @@ export class ProtectAttr extends AddBattlerTagAttr {
for (const turnMove of user.getLastXMoves(-1).slice()) { for (const turnMove of user.getLastXMoves(-1).slice()) {
if ( if (
// Quick & Wide guard increment the Protect counter without using it for fail chance // Quick & Wide guard increment the Protect counter without using it for fail chance
!(allMoves[turnMove.move].hasAttr("ProtectAttr") || !(allMoves[turnMove.move].hasAttr("ProtectAttr") ||
[MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) || [MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) ||
turnMove.result !== MoveResult.SUCCESS turnMove.result !== MoveResult.SUCCESS
) { ) {
break; break;
@ -5947,6 +5931,38 @@ export class ProtectAttr extends AddBattlerTagAttr {
} }
} }
export class IgnoreAccuracyAttr extends AddBattlerTagAttr {
constructor() {
super(BattlerTagType.IGNORE_ACCURACY, true, false, 2);
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args)) {
return false;
}
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:tookAimAtTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) }));
return true;
}
}
export class FaintCountdownAttr extends AddBattlerTagAttr {
constructor() {
super(BattlerTagType.PERISH_SONG, false, true, 4);
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args)) {
return false;
}
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:faintCountdown", { pokemonName: getPokemonNameWithAffix(target), turnCount: this.turnCountMin - 1 }));
return true;
}
}
/** /**
* Attribute to remove all Substitutes from the field. * Attribute to remove all Substitutes from the field.
* @extends MoveEffectAttr * @extends MoveEffectAttr
@ -6587,10 +6603,8 @@ export class ChillyReceptionAttr extends ForceSwitchOutAttr {
return (user, target, move) => globalScene.arena.weather?.weatherType !== WeatherType.SNOW || super.getSwitchOutCondition()(user, target, move); return (user, target, move) => globalScene.arena.weather?.weatherType !== WeatherType.SNOW || super.getSwitchOutCondition()(user, target, move);
} }
} }
export class RemoveTypeAttr extends MoveEffectAttr { export class RemoveTypeAttr extends MoveEffectAttr {
// TODO: Remove the message callback
private removedType: PokemonType; private removedType: PokemonType;
private messageCallback: ((user: Pokemon) => void) | undefined; private messageCallback: ((user: Pokemon) => void) | undefined;
@ -8285,6 +8299,8 @@ const MoveAttrs = Object.freeze({
RandomLevelDamageAttr, RandomLevelDamageAttr,
ModifiedDamageAttr, ModifiedDamageAttr,
SurviveDamageAttr, SurviveDamageAttr,
SplashAttr,
CelebrateAttr,
RecoilAttr, RecoilAttr,
SacrificialAttr, SacrificialAttr,
SacrificialAttrOnHit, SacrificialAttrOnHit,
@ -8427,7 +8443,8 @@ const MoveAttrs = Object.freeze({
RechargeAttr, RechargeAttr,
TrapAttr, TrapAttr,
ProtectAttr, ProtectAttr,
MessageAttr, IgnoreAccuracyAttr,
FaintCountdownAttr,
RemoveAllSubstitutesAttr, RemoveAllSubstitutesAttr,
HitsTagAttr, HitsTagAttr,
HitsTagForDoubleDamageAttr, HitsTagForDoubleDamageAttr,
@ -8921,7 +8938,7 @@ export function initMoves() {
new AttackMove(MoveId.PSYWAVE, PokemonType.PSYCHIC, MoveCategory.SPECIAL, -1, 100, 15, -1, 0, 1) new AttackMove(MoveId.PSYWAVE, PokemonType.PSYCHIC, MoveCategory.SPECIAL, -1, 100, 15, -1, 0, 1)
.attr(RandomLevelDamageAttr), .attr(RandomLevelDamageAttr),
new SelfStatusMove(MoveId.SPLASH, PokemonType.NORMAL, -1, 40, -1, 0, 1) new SelfStatusMove(MoveId.SPLASH, PokemonType.NORMAL, -1, 40, -1, 0, 1)
.attr(MessageAttr, i18next.t("moveTriggers:splash")) .attr(SplashAttr)
.condition(failOnGravityCondition), .condition(failOnGravityCondition),
new SelfStatusMove(MoveId.ACID_ARMOR, PokemonType.POISON, -1, 20, -1, 0, 1) new SelfStatusMove(MoveId.ACID_ARMOR, PokemonType.POISON, -1, 20, -1, 0, 1)
.attr(StatStageChangeAttr, [ Stat.DEF ], 2, true), .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true),
@ -8983,10 +9000,7 @@ export function initMoves() {
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1)
.reflectable(), .reflectable(),
new StatusMove(MoveId.MIND_READER, PokemonType.NORMAL, -1, 5, -1, 0, 2) new StatusMove(MoveId.MIND_READER, PokemonType.NORMAL, -1, 5, -1, 0, 2)
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_ACCURACY, true, false, 2) .attr(IgnoreAccuracyAttr),
.attr(MessageAttr, (user, target) =>
i18next.t("moveTriggers:tookAimAtTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })
),
new StatusMove(MoveId.NIGHTMARE, PokemonType.GHOST, 100, 15, -1, 0, 2) new StatusMove(MoveId.NIGHTMARE, PokemonType.GHOST, 100, 15, -1, 0, 2)
.attr(AddBattlerTagAttr, BattlerTagType.NIGHTMARE) .attr(AddBattlerTagAttr, BattlerTagType.NIGHTMARE)
.condition(targetSleptOrComatoseCondition), .condition(targetSleptOrComatoseCondition),
@ -9074,9 +9088,7 @@ export function initMoves() {
return lastTurnMove.length === 0 || lastTurnMove[0].move !== move.id || lastTurnMove[0].result !== MoveResult.SUCCESS; return lastTurnMove.length === 0 || lastTurnMove[0].move !== move.id || lastTurnMove[0].result !== MoveResult.SUCCESS;
}), }),
new StatusMove(MoveId.PERISH_SONG, PokemonType.NORMAL, -1, 5, -1, 0, 2) new StatusMove(MoveId.PERISH_SONG, PokemonType.NORMAL, -1, 5, -1, 0, 2)
.attr(AddBattlerTagAttr, BattlerTagType.PERISH_SONG, false, true, 4) .attr(FaintCountdownAttr)
.attr(MessageAttr, (_user, target) =>
i18next.t("moveTriggers:faintCountdown", { pokemonName: getPokemonNameWithAffix(target), turnCount: 3 }))
.ignoresProtect() .ignoresProtect()
.soundBased() .soundBased()
.condition(failOnBossCondition) .condition(failOnBossCondition)
@ -9092,10 +9104,7 @@ export function initMoves() {
.attr(MultiHitAttr) .attr(MultiHitAttr)
.makesContact(false), .makesContact(false),
new StatusMove(MoveId.LOCK_ON, PokemonType.NORMAL, -1, 5, -1, 0, 2) new StatusMove(MoveId.LOCK_ON, PokemonType.NORMAL, -1, 5, -1, 0, 2)
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_ACCURACY, true, false, 2) .attr(IgnoreAccuracyAttr),
.attr(MessageAttr, (user, target) =>
i18next.t("moveTriggers:tookAimAtTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })
),
new AttackMove(MoveId.OUTRAGE, PokemonType.DRAGON, MoveCategory.PHYSICAL, 120, 100, 10, -1, 0, 2) new AttackMove(MoveId.OUTRAGE, PokemonType.DRAGON, MoveCategory.PHYSICAL, 120, 100, 10, -1, 0, 2)
.attr(FrenzyAttr) .attr(FrenzyAttr)
.attr(MissEffectAttr, frenzyMissFunc) .attr(MissEffectAttr, frenzyMissFunc)
@ -9322,8 +9331,8 @@ export function initMoves() {
&& (user.status.effect === StatusEffect.BURN || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.PARALYSIS) ? 2 : 1) && (user.status.effect === StatusEffect.BURN || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.PARALYSIS) ? 2 : 1)
.attr(BypassBurnDamageReductionAttr), .attr(BypassBurnDamageReductionAttr),
new AttackMove(MoveId.FOCUS_PUNCH, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 20, -1, -3, 3) new AttackMove(MoveId.FOCUS_PUNCH, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 20, -1, -3, 3)
.attr(MessageHeaderAttr, (user) => i18next.t("moveTriggers:isTighteningFocus", { pokemonName: getPokemonNameWithAffix(user) })) .attr(MessageHeaderAttr, (user, move) => i18next.t("moveTriggers:isTighteningFocus", { pokemonName: getPokemonNameWithAffix(user) }))
.attr(PreUseInterruptAttr, (user) => i18next.t("moveTriggers:lostFocus", { pokemonName: getPokemonNameWithAffix(user) }), user => user.turnData.attacksReceived.some(r => r.damage > 0)) .attr(PreUseInterruptAttr, (user, target, move) => i18next.t("moveTriggers:lostFocus", { pokemonName: getPokemonNameWithAffix(user) }), user => !!user.turnData.attacksReceived.find(r => r.damage))
.punchingMove(), .punchingMove(),
new AttackMove(MoveId.SMELLING_SALTS, PokemonType.NORMAL, MoveCategory.PHYSICAL, 70, 100, 10, -1, 0, 3) new AttackMove(MoveId.SMELLING_SALTS, PokemonType.NORMAL, MoveCategory.PHYSICAL, 70, 100, 10, -1, 0, 3)
.attr(MovePowerMultiplierAttr, (user, target, move) => target.status?.effect === StatusEffect.PARALYSIS ? 2 : 1) .attr(MovePowerMultiplierAttr, (user, target, move) => target.status?.effect === StatusEffect.PARALYSIS ? 2 : 1)
@ -10424,8 +10433,7 @@ export function initMoves() {
new AttackMove(MoveId.DAZZLING_GLEAM, PokemonType.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 6) new AttackMove(MoveId.DAZZLING_GLEAM, PokemonType.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 6)
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
new SelfStatusMove(MoveId.CELEBRATE, PokemonType.NORMAL, -1, 40, -1, 0, 6) new SelfStatusMove(MoveId.CELEBRATE, PokemonType.NORMAL, -1, 40, -1, 0, 6)
// NB: This needs a lambda function as the user will not be logged in by the time the moves are initialized .attr(CelebrateAttr),
.attr(MessageAttr, () => i18next.t("moveTriggers:celebrate", { playerName: loggedInUser?.username })),
new StatusMove(MoveId.HOLD_HANDS, PokemonType.NORMAL, -1, 40, -1, 0, 6) new StatusMove(MoveId.HOLD_HANDS, PokemonType.NORMAL, -1, 40, -1, 0, 6)
.ignoresSubstitute() .ignoresSubstitute()
.target(MoveTarget.NEAR_ALLY), .target(MoveTarget.NEAR_ALLY),
@ -10600,12 +10608,7 @@ export function initMoves() {
.attr(StatStageChangeAttr, [ Stat.SPD ], -1) .attr(StatStageChangeAttr, [ Stat.SPD ], -1)
.reflectable(), .reflectable(),
new SelfStatusMove(MoveId.LASER_FOCUS, PokemonType.NORMAL, -1, 30, -1, 0, 7) new SelfStatusMove(MoveId.LASER_FOCUS, PokemonType.NORMAL, -1, 30, -1, 0, 7)
.attr(AddBattlerTagAttr, BattlerTagType.ALWAYS_CRIT, true, false) .attr(AddBattlerTagAttr, BattlerTagType.ALWAYS_CRIT, true, false),
.attr(MessageAttr, (user) =>
i18next.t("battlerTags:laserFocusOnAdd", {
pokemonNameWithAffix: getPokemonNameWithAffix(user),
}),
),
new StatusMove(MoveId.GEAR_UP, PokemonType.STEEL, -1, 20, -1, 0, 7) new StatusMove(MoveId.GEAR_UP, PokemonType.STEEL, -1, 20, -1, 0, 7)
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, false, { condition: (user, target, move) => !![ AbilityId.PLUS, AbilityId.MINUS ].find(a => target.hasAbility(a, false)) }) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, false, { condition: (user, target, move) => !![ AbilityId.PLUS, AbilityId.MINUS ].find(a => target.hasAbility(a, false)) })
.ignoresSubstitute() .ignoresSubstitute()

View File

@ -50,9 +50,10 @@ export class PokemonMove {
const move = this.getMove(); const move = this.getMove();
// TODO: Add Sky Drop's 1 turn stall // TODO: Add Sky Drop's 1 turn stall
const usability = new BooleanHolder( const usability = new BooleanHolder(
!move.name.endsWith(" (N)") !move.name.endsWith(" (N)") &&
&& (ignorePp || this.ppUsed < this.getMovePp() || move.pp === -1) // TODO: Review if the `MoveId.NONE` check is even necessary anymore (ignorePp || this.ppUsed < this.getMovePp() || move.pp === -1) &&
&& !(this.moveId !== MoveId.NONE && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)), // TODO: Review if the `MoveId.NONE` check is even necessary anymore
!(this.moveId !== MoveId.NONE && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)),
); );
if (pokemon.isPlayer()) { if (pokemon.isPlayer()) {
applyChallenges(ChallengeType.POKEMON_MOVE, move.id, usability); applyChallenges(ChallengeType.POKEMON_MOVE, move.id, usability);

View File

@ -111,7 +111,7 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder.
const trainerSpriteKey = trainerConfig.getSpriteKey(); const trainerSpriteKey = trainerConfig.getSpriteKey();
encounter.enemyPartyConfigs.push({ encounter.enemyPartyConfigs.push({
levelAdditiveModifier: 1, levelAdditiveModifier: 1,
trainerConfig, trainerConfig: trainerConfig,
}); });
encounter.spriteConfigs = [ encounter.spriteConfigs = [

View File

@ -93,8 +93,8 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB
// Store pokemon and price // Store pokemon and price
encounter.misc = { encounter.misc = {
pokemon, pokemon: pokemon,
price, price: price,
}; };
// If player meets the combo OR requirements for option 2, populate the token // If player meets the combo OR requirements for option 2, populate the token

View File

@ -71,7 +71,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
pokemonConfigs: [ pokemonConfigs: [
{ {
level, level: level,
species: bossPokemon.species, species: bossPokemon.species,
dataSource: new PokemonData(bossPokemon), dataSource: new PokemonData(bossPokemon),
isBoss: true, isBoss: true,
@ -105,8 +105,8 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
hasShadow: true, hasShadow: true,
}, },
{ {
spriteKey, spriteKey: spriteKey,
fileRoot, fileRoot: fileRoot,
hasShadow: true, hasShadow: true,
tint: 0.25, tint: 0.25,
x: -5, x: -5,
@ -320,9 +320,9 @@ function tryGiveBerry(prioritizedPokemon?: PlayerPokemon) {
if (prioritizedPokemon) { if (prioritizedPokemon) {
const heldBerriesOfType = globalScene.findModifier( const heldBerriesOfType = globalScene.findModifier(
m => m =>
m instanceof BerryModifier m instanceof BerryModifier &&
&& m.pokemonId === prioritizedPokemon.id m.pokemonId === prioritizedPokemon.id &&
&& (m as BerryModifier).berryType === berryType, (m as BerryModifier).berryType === berryType,
true, true,
) as BerryModifier; ) as BerryModifier;

View File

@ -248,7 +248,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
yShadow: -4, yShadow: -4,
}, },
{ {
spriteKey, spriteKey: spriteKey,
fileRoot: "trainer", fileRoot: "trainer",
hasShadow: true, hasShadow: true,
x: 4, x: 4,
@ -441,11 +441,11 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
// Get Pokemon held items and filter for valid ones // Get Pokemon held items and filter for valid ones
const validItems = pokemon.getHeldItems().filter(item => { const validItems = pokemon.getHeldItems().filter(item => {
return ( return (
(item instanceof BypassSpeedChanceModifier (item instanceof BypassSpeedChanceModifier ||
|| item instanceof ContactHeldItemTransferChanceModifier item instanceof ContactHeldItemTransferChanceModifier ||
|| (item instanceof AttackTypeBoosterModifier (item instanceof AttackTypeBoosterModifier &&
&& (item.type as AttackTypeBoosterModifierType).moveType === PokemonType.BUG)) (item.type as AttackTypeBoosterModifierType).moveType === PokemonType.BUG)) &&
&& item.isTransferable item.isTransferable
); );
}); });
@ -470,10 +470,10 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
// If pokemon has valid item, it can be selected // If pokemon has valid item, it can be selected
const hasValidItem = pokemon.getHeldItems().some(item => { const hasValidItem = pokemon.getHeldItems().some(item => {
return ( return (
item instanceof BypassSpeedChanceModifier item instanceof BypassSpeedChanceModifier ||
|| item instanceof ContactHeldItemTransferChanceModifier item instanceof ContactHeldItemTransferChanceModifier ||
|| (item instanceof AttackTypeBoosterModifier (item instanceof AttackTypeBoosterModifier &&
&& (item.type as AttackTypeBoosterModifierType).moveType === PokemonType.BUG) (item.type as AttackTypeBoosterModifierType).moveType === PokemonType.BUG)
); );
}); });
if (!hasValidItem) { if (!hasValidItem) {

View File

@ -163,7 +163,7 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
// Blacephalon has the random ability from pool, and 2 entirely random types to fit with the theme of the encounter // Blacephalon has the random ability from pool, and 2 entirely random types to fit with the theme of the encounter
species: getPokemonSpecies(SpeciesId.BLACEPHALON), species: getPokemonSpecies(SpeciesId.BLACEPHALON),
customPokemonData: new CustomPokemonData({ customPokemonData: new CustomPokemonData({
ability, ability: ability,
types: [firstType, secondType], types: [firstType, secondType],
}), }),
isBoss: true, isBoss: true,

View File

@ -168,7 +168,7 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
pokemonConfigs: [ pokemonConfigs: [
{ {
species, species: species,
dataSource: oricorioData, dataSource: oricorioData,
isBoss: true, isBoss: true,
// Gets +1 to all stats except SPD on battle start // Gets +1 to all stats except SPD on battle start

View File

@ -249,6 +249,6 @@ function pokemonAndMoveChosen(pokemon: PlayerPokemon, move: PokemonMove, correct
setEncounterExp([pokemon.id], 100); setEncounterExp([pokemon.id], 100);
} }
encounter.misc = { encounter.misc = {
correctMove, correctMove: correctMove,
}; };
} }

View File

@ -63,7 +63,7 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder.
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
pokemonConfigs: [ pokemonConfigs: [
{ {
level, level: level,
species: bossPokemon.species, species: bossPokemon.species,
dataSource: new PokemonData(bossPokemon), dataSource: new PokemonData(bossPokemon),
isBoss: true, isBoss: true,
@ -120,8 +120,8 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder.
disableAnimation: true, disableAnimation: true,
}, },
{ {
spriteKey, spriteKey: spriteKey,
fileRoot, fileRoot: fileRoot,
hasShadow: true, hasShadow: true,
tint: 0.25, tint: 0.25,
x: -5, x: -5,

View File

@ -192,10 +192,10 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = MysteryEncounterBuil
: "" : ""
}`; }`;
const line2 = const line2 =
i18next.t("pokemonInfoContainer:nature") i18next.t("pokemonInfoContainer:nature") +
+ " " " " +
+ getNatureName(tradePokemon.getNature()) getNatureName(tradePokemon.getNature()) +
+ (formName ? ` | ${i18next.t("pokemonInfoContainer:form")} ${formName}` : ""); (formName ? ` | ${i18next.t("pokemonInfoContainer:form")} ${formName}` : "");
showEncounterText(`${line1}\n${line2}`, 0, 0, false); showEncounterText(`${line1}\n${line2}`, 0, 0, false);
}, },
}; };

View File

@ -50,7 +50,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
const normalSpriteKey = normalConfig.getSpriteKey(female, normalConfig.doubleOnly); const normalSpriteKey = normalConfig.getSpriteKey(female, normalConfig.doubleOnly);
encounter.enemyPartyConfigs.push({ encounter.enemyPartyConfigs.push({
trainerConfig: normalConfig, trainerConfig: normalConfig,
female, female: female,
}); });
// Hard difficulty trainer is another random trainer, but with AVERAGE_BALANCED config // Hard difficulty trainer is another random trainer, but with AVERAGE_BALANCED config
@ -81,7 +81,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
encounter.enemyPartyConfigs.push({ encounter.enemyPartyConfigs.push({
trainerConfig: hardConfig, trainerConfig: hardConfig,
levelAdditiveModifier: 1, levelAdditiveModifier: 1,
female, female: female,
}); });
// Brutal trainer is pulled from pool of boss trainers (gym leaders) for the biome // Brutal trainer is pulled from pool of boss trainers (gym leaders) for the biome
@ -101,7 +101,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
encounter.enemyPartyConfigs.push({ encounter.enemyPartyConfigs.push({
trainerConfig: brutalConfig, trainerConfig: brutalConfig,
levelAdditiveModifier: 1.5, levelAdditiveModifier: 1.5,
female, female: female,
}); });
encounter.spriteConfigs = [ encounter.spriteConfigs = [

View File

@ -163,12 +163,8 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde
queueEncounterMessage(`${namespace}:option.1.great`); queueEncounterMessage(`${namespace}:option.1.great`);
leaveEncounterWithoutBattle(); leaveEncounterWithoutBattle();
} else if ( } else if (
roll roll >=
>= RAND_LENGTH RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT - MASTER_REWARDS_PERCENT
- COMMON_REWARDS_PERCENT
- ULTRA_REWARDS_PERCENT
- ROGUE_REWARDS_PERCENT
- MASTER_REWARDS_PERCENT
) { ) {
// Choose 1 MASTER tier item (5%) // Choose 1 MASTER tier item (5%)
setEncounterRewards({ setEncounterRewards({

View File

@ -104,7 +104,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui
encounter.setDialogueToken("boost2", modifiers[1].name); encounter.setDialogueToken("boost2", modifiers[1].name);
encounter.misc = { encounter.misc = {
chosenPokemon: pokemon, chosenPokemon: pokemon,
modifiers, modifiers: modifiers,
}; };
}; };
@ -187,7 +187,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui
encounter.setDialogueToken("boost2", modifiers[1].name); encounter.setDialogueToken("boost2", modifiers[1].name);
encounter.misc = { encounter.misc = {
chosenPokemon: pokemon, chosenPokemon: pokemon,
modifiers, modifiers: modifiers,
}; };
}; };

View File

@ -165,7 +165,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter = MysteryEncounterBui
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
pokemonConfigs: [ pokemonConfigs: [
{ {
level, level: level,
species: bossSpecies, species: bossSpecies,
dataSource: new PokemonData(bossPokemon), dataSource: new PokemonData(bossPokemon),
isBoss: true, isBoss: true,
@ -221,7 +221,7 @@ async function doBiomeTransitionDialogueAndBattleInit() {
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
pokemonConfigs: [ pokemonConfigs: [
{ {
level, level: level,
species: bossSpecies, species: bossSpecies,
dataSource: new PokemonData(bossPokemon), dataSource: new PokemonData(bossPokemon),
isBoss: true, isBoss: true,

View File

@ -92,11 +92,11 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
.getEventEncounters() .getEventEncounters()
.filter( .filter(
s => s =>
!getPokemonSpecies(s.species).legendary !getPokemonSpecies(s.species).legendary &&
&& !getPokemonSpecies(s.species).subLegendary !getPokemonSpecies(s.species).subLegendary &&
&& !getPokemonSpecies(s.species).mythical !getPokemonSpecies(s.species).mythical &&
&& !NON_LEGEND_PARADOX_POKEMON.includes(s.species) !NON_LEGEND_PARADOX_POKEMON.includes(s.species) &&
&& !NON_LEGEND_ULTRA_BEASTS.includes(s.species), !NON_LEGEND_ULTRA_BEASTS.includes(s.species),
); );
let pokemon: PlayerPokemon; let pokemon: PlayerPokemon;
@ -109,16 +109,16 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
* Mons rolled from the event encounter pool get 3 extra shiny rolls * Mons rolled from the event encounter pool get 3 extra shiny rolls
*/ */
if ( if (
r === 0 r === 0 ||
|| ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE) ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE) &&
&& validEventEncounters.length === 0) validEventEncounters.length === 0)
) { ) {
// If you roll 1%, give shiny Magikarp with random variant // If you roll 1%, give shiny Magikarp with random variant
species = getPokemonSpecies(SpeciesId.MAGIKARP); species = getPokemonSpecies(SpeciesId.MAGIKARP);
pokemon = new PlayerPokemon(species, 5, 2, undefined, undefined, true); pokemon = new PlayerPokemon(species, 5, 2, undefined, undefined, true);
} else if ( } else if (
validEventEncounters.length > 0 validEventEncounters.length > 0 &&
&& (r <= EVENT_THRESHOLD || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE) (r <= EVENT_THRESHOLD || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE)
) { ) {
tries = 0; tries = 0;
do { do {
@ -162,8 +162,8 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(pokemon); const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(pokemon);
encounter.spriteConfigs.push({ encounter.spriteConfigs.push({
spriteKey, spriteKey: spriteKey,
fileRoot, fileRoot: fileRoot,
hasShadow: true, hasShadow: true,
repeat: true, repeat: true,
isPokemon: true, isPokemon: true,
@ -185,8 +185,8 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
encounter.setDialogueToken("purchasePokemon", pokemon.getNameToRender()); encounter.setDialogueToken("purchasePokemon", pokemon.getNameToRender());
encounter.setDialogueToken("price", price.toString()); encounter.setDialogueToken("price", price.toString());
encounter.misc = { encounter.misc = {
price, price: price,
pokemon, pokemon: pokemon,
}; };
pokemon.calculateStats(); pokemon.calculateStats();

View File

@ -212,9 +212,9 @@ function endTrainerBattleAndShowDialogue(): Promise<void> {
// Only trigger form change when Eiscue is in Noice form // Only trigger form change when Eiscue is in Noice form
// Hardcoded Eiscue for now in case it is fused with another pokemon // Hardcoded Eiscue for now in case it is fused with another pokemon
if ( if (
pokemon.species.speciesId === SpeciesId.EISCUE pokemon.species.speciesId === SpeciesId.EISCUE &&
&& pokemon.hasAbility(AbilityId.ICE_FACE) pokemon.hasAbility(AbilityId.ICE_FACE) &&
&& pokemon.formIndex === 1 pokemon.formIndex === 1
) { ) {
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger); globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger);
} }

View File

@ -113,7 +113,7 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde
let ivIndexes: any[] = []; let ivIndexes: any[] = [];
playerPokemon.ivs.forEach((iv, index) => { playerPokemon.ivs.forEach((iv, index) => {
if (iv < 31) { if (iv < 31) {
ivIndexes.push({ iv, index }); ivIndexes.push({ iv: iv, index: index });
} }
}); });
@ -324,9 +324,9 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde
// Only update the fusion's dex data if the Pokemon is already caught in dex (ignore rentals) // Only update the fusion's dex data if the Pokemon is already caught in dex (ignore rentals)
const rootFusionSpecies = playerPokemon.fusionSpecies?.getRootSpeciesId(); const rootFusionSpecies = playerPokemon.fusionSpecies?.getRootSpeciesId();
if ( if (
!isNullOrUndefined(rootFusionSpecies) !isNullOrUndefined(rootFusionSpecies) &&
&& speciesStarterCosts.hasOwnProperty(rootFusionSpecies) speciesStarterCosts.hasOwnProperty(rootFusionSpecies) &&
&& !!globalScene.gameData.dexData[rootFusionSpecies].caughtAttr !!globalScene.gameData.dexData[rootFusionSpecies].caughtAttr
) { ) {
globalScene.gameData.starterData[rootFusionSpecies].abilityAttr |= globalScene.gameData.starterData[rootFusionSpecies].abilityAttr |=
playerPokemon.fusionAbilityIndex !== 1 || playerPokemon.fusionSpecies?.ability2 playerPokemon.fusionAbilityIndex !== 1 || playerPokemon.fusionSpecies?.ability2
@ -396,7 +396,7 @@ function getEnemyConfig(playerPokemon: PlayerPokemon, segments: number, modifier
formIndex: playerPokemon.formIndex, formIndex: playerPokemon.formIndex,
level: playerPokemon.level, level: playerPokemon.level,
dataSource: data, dataSource: data,
modifierConfigs, modifierConfigs: modifierConfigs,
}, },
], ],
}; };

View File

@ -71,7 +71,7 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
const randomEggMove: MoveId = eggMoves[eggMoveIndex]; const randomEggMove: MoveId = eggMoves[eggMoveIndex];
encounter.misc = { encounter.misc = {
eggMove: randomEggMove, eggMove: randomEggMove,
pokemon, pokemon: pokemon,
}; };
if (pokemon.moveset.length < 4) { if (pokemon.moveset.length < 4) {
pokemon.moveset.push(new PokemonMove(randomEggMove)); pokemon.moveset.push(new PokemonMove(randomEggMove));
@ -91,7 +91,7 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
pokemonConfigs: [ pokemonConfigs: [
{ {
level, level: level,
species: pokemon.species, species: pokemon.species,
dataSource: new PokemonData(pokemon), dataSource: new PokemonData(pokemon),
isBoss: false, isBoss: false,
@ -114,8 +114,8 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(pokemon); const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(pokemon);
encounter.spriteConfigs = [ encounter.spriteConfigs = [
{ {
spriteKey, spriteKey: spriteKey,
fileRoot, fileRoot: fileRoot,
hasShadow: true, hasShadow: true,
x: -5, x: -5,
repeat: true, repeat: true,

View File

@ -282,7 +282,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
species: transformation.newSpecies, species: transformation.newSpecies,
isBoss: newPokemon.getSpeciesForm().getBaseStatTotal() > NON_LEGENDARY_BST_THRESHOLD, isBoss: newPokemon.getSpeciesForm().getBaseStatTotal() > NON_LEGENDARY_BST_THRESHOLD,
level: previousPokemon.level, level: previousPokemon.level,
dataSource, dataSource: dataSource,
modifierConfigs: newPokemonHeldItemConfigs, modifierConfigs: newPokemonHeldItemConfigs,
}; };
@ -296,7 +296,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
].clone(); ].clone();
trainerConfig.setPartyTemplates(new TrainerPartyTemplate(transformations.length, PartyMemberStrength.STRONG)); trainerConfig.setPartyTemplates(new TrainerPartyTemplate(transformations.length, PartyMemberStrength.STRONG));
const enemyPartyConfig: EnemyPartyConfig = { const enemyPartyConfig: EnemyPartyConfig = {
trainerConfig, trainerConfig: trainerConfig,
pokemonConfigs: enemyPokemonConfigs, pokemonConfigs: enemyPokemonConfigs,
female: genderIndex === PlayerGender.FEMALE, female: genderIndex === PlayerGender.FEMALE,
}; };
@ -521,12 +521,12 @@ async function postProcessTransformedPokemon(
// For pokemon at/below 570 BST or any shiny pokemon, unlock it permanently as if you had caught it // For pokemon at/below 570 BST or any shiny pokemon, unlock it permanently as if you had caught it
if ( if (
!forBattle !forBattle &&
&& (newPokemon.getSpeciesForm().getBaseStatTotal() <= NON_LEGENDARY_BST_THRESHOLD || newPokemon.isShiny()) (newPokemon.getSpeciesForm().getBaseStatTotal() <= NON_LEGENDARY_BST_THRESHOLD || newPokemon.isShiny())
) { ) {
if ( if (
newPokemon.getSpeciesForm().abilityHidden newPokemon.getSpeciesForm().abilityHidden &&
&& newPokemon.abilityIndex === newPokemon.getSpeciesForm().getAbilityCount() - 1 newPokemon.abilityIndex === newPokemon.getSpeciesForm().getAbilityCount() - 1
) { ) {
globalScene.validateAchv(achvs.HIDDEN_ABILITY); globalScene.validateAchv(achvs.HIDDEN_ABILITY);
} }
@ -624,10 +624,10 @@ function getTransformedSpecies(
const bstInRange = speciesBst >= bstMin && speciesBst <= bstCap; const bstInRange = speciesBst >= bstMin && speciesBst <= bstCap;
// Checks that a Pokemon has not already been added in the +600 or 570-600 slots; // Checks that a Pokemon has not already been added in the +600 or 570-600 slots;
const validBst = const validBst =
(!hasPokemonBstBetween570And600 (!hasPokemonBstBetween570And600 ||
|| speciesBst < NON_LEGENDARY_BST_THRESHOLD speciesBst < NON_LEGENDARY_BST_THRESHOLD ||
|| speciesBst > SUPER_LEGENDARY_BST_THRESHOLD) speciesBst > SUPER_LEGENDARY_BST_THRESHOLD) &&
&& (!hasPokemonBstHigherThan600 || speciesBst <= SUPER_LEGENDARY_BST_THRESHOLD); (!hasPokemonBstHigherThan600 || speciesBst <= SUPER_LEGENDARY_BST_THRESHOLD);
return bstInRange && validBst && !EXCLUDED_TRANSFORMATION_SPECIES.includes(s.speciesId); return bstInRange && validBst && !EXCLUDED_TRANSFORMATION_SPECIES.includes(s.speciesId);
}); });
@ -772,9 +772,9 @@ async function addEggMoveToNewPokemonMoveset(
// For pokemon that the player owns (including ones just caught), unlock the egg move // For pokemon that the player owns (including ones just caught), unlock the egg move
if ( if (
!forBattle !forBattle &&
&& !isNullOrUndefined(randomEggMoveIndex) !isNullOrUndefined(randomEggMoveIndex) &&
&& !!globalScene.gameData.dexData[speciesRootForm].caughtAttr !!globalScene.gameData.dexData[speciesRootForm].caughtAttr
) { ) {
await globalScene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), randomEggMoveIndex, true); await globalScene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), randomEggMoveIndex, true);
} }

View File

@ -76,9 +76,9 @@ export class MysteryEncounterOption implements IMysteryEncounterOption {
*/ */
hasRequirements(): boolean { hasRequirements(): boolean {
return ( return (
this.requirements.length > 0 this.requirements.length > 0 ||
|| this.primaryPokemonRequirements.length > 0 this.primaryPokemonRequirements.length > 0 ||
|| this.secondaryPokemonRequirements.length > 0 this.secondaryPokemonRequirements.length > 0
); );
} }
@ -87,9 +87,9 @@ export class MysteryEncounterOption implements IMysteryEncounterOption {
*/ */
meetsRequirements(): boolean { meetsRequirements(): boolean {
return ( return (
!this.requirements.some(requirement => !requirement.meetsRequirement()) !this.requirements.some(requirement => !requirement.meetsRequirement()) &&
&& this.meetsSupportingRequirementAndSupportingPokemonSelected() this.meetsSupportingRequirementAndSupportingPokemonSelected() &&
&& this.meetsPrimaryRequirementAndPrimaryPokemonSelected() this.meetsPrimaryRequirementAndPrimaryPokemonSelected()
); );
} }
@ -209,7 +209,7 @@ export class MysteryEncounterOptionBuilder implements Partial<IMysteryEncounterO
} }
withHasDexProgress(hasDexProgress: boolean): this & Required<Pick<IMysteryEncounterOption, "hasDexProgress">> { withHasDexProgress(hasDexProgress: boolean): this & Required<Pick<IMysteryEncounterOption, "hasDexProgress">> {
return Object.assign(this, { hasDexProgress }); return Object.assign(this, { hasDexProgress: hasDexProgress });
} }
/** /**
@ -240,7 +240,7 @@ export class MysteryEncounterOptionBuilder implements Partial<IMysteryEncounterO
withPreOptionPhase( withPreOptionPhase(
onPreOptionPhase: OptionPhaseCallback, onPreOptionPhase: OptionPhaseCallback,
): this & Required<Pick<IMysteryEncounterOption, "onPreOptionPhase">> { ): this & Required<Pick<IMysteryEncounterOption, "onPreOptionPhase">> {
return Object.assign(this, { onPreOptionPhase }); return Object.assign(this, { onPreOptionPhase: onPreOptionPhase });
} }
/** /**
@ -248,13 +248,13 @@ export class MysteryEncounterOptionBuilder implements Partial<IMysteryEncounterO
* @param onOptionPhase * @param onOptionPhase
*/ */
withOptionPhase(onOptionPhase: OptionPhaseCallback): this & Required<Pick<IMysteryEncounterOption, "onOptionPhase">> { withOptionPhase(onOptionPhase: OptionPhaseCallback): this & Required<Pick<IMysteryEncounterOption, "onOptionPhase">> {
return Object.assign(this, { onOptionPhase }); return Object.assign(this, { onOptionPhase: onOptionPhase });
} }
withPostOptionPhase( withPostOptionPhase(
onPostOptionPhase: OptionPhaseCallback, onPostOptionPhase: OptionPhaseCallback,
): this & Required<Pick<IMysteryEncounterOption, "onPostOptionPhase">> { ): this & Required<Pick<IMysteryEncounterOption, "onPostOptionPhase">> {
return Object.assign(this, { onPostOptionPhase }); return Object.assign(this, { onPostOptionPhase: onPostOptionPhase });
} }
/** /**

View File

@ -222,8 +222,8 @@ export class WaveRangeRequirement extends EncounterSceneRequirement {
if (!isNullOrUndefined(this.waveRange) && this.waveRange[0] <= this.waveRange[1]) { if (!isNullOrUndefined(this.waveRange) && this.waveRange[0] <= this.waveRange[1]) {
const waveIndex = globalScene.currentBattle.waveIndex; const waveIndex = globalScene.currentBattle.waveIndex;
if ( if (
(waveIndex >= 0 && this.waveRange[0] >= 0 && this.waveRange[0] > waveIndex) (waveIndex >= 0 && this.waveRange[0] >= 0 && this.waveRange[0] > waveIndex) ||
|| (this.waveRange[1] >= 0 && this.waveRange[1] < waveIndex) (this.waveRange[1] >= 0 && this.waveRange[1] < waveIndex)
) { ) {
return false; return false;
} }
@ -276,9 +276,9 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
const timeOfDay = globalScene.arena?.getTimeOfDay(); const timeOfDay = globalScene.arena?.getTimeOfDay();
return !( return !(
!isNullOrUndefined(timeOfDay) !isNullOrUndefined(timeOfDay) &&
&& this.requiredTimeOfDay?.length > 0 this.requiredTimeOfDay?.length > 0 &&
&& !this.requiredTimeOfDay.includes(timeOfDay) !this.requiredTimeOfDay.includes(timeOfDay)
); );
} }
@ -298,9 +298,9 @@ export class WeatherRequirement extends EncounterSceneRequirement {
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
const currentWeather = globalScene.arena.weather?.weatherType; const currentWeather = globalScene.arena.weather?.weatherType;
return !( return !(
!isNullOrUndefined(currentWeather) !isNullOrUndefined(currentWeather) &&
&& this.requiredWeather?.length > 0 this.requiredWeather?.length > 0 &&
&& !this.requiredWeather.includes(currentWeather!) !this.requiredWeather.includes(currentWeather!)
); );
} }
@ -336,8 +336,8 @@ export class PartySizeRequirement extends EncounterSceneRequirement {
? globalScene.getPokemonAllowedInBattle().length ? globalScene.getPokemonAllowedInBattle().length
: globalScene.getPlayerParty().length; : globalScene.getPlayerParty().length;
if ( if (
(partySize >= 0 && this.partySizeRange[0] >= 0 && this.partySizeRange[0] > partySize) (partySize >= 0 && this.partySizeRange[0] >= 0 && this.partySizeRange[0] > partySize) ||
|| (this.partySizeRange[1] >= 0 && this.partySizeRange[1] < partySize) (this.partySizeRange[1] >= 0 && this.partySizeRange[1] < partySize)
) { ) {
return false; return false;
} }
@ -572,15 +572,15 @@ export class MoveRequirement extends EncounterPokemonRequirement {
// get the Pokemon with at least one move in the required moves list // get the Pokemon with at least one move in the required moves list
return partyPokemon.filter( return partyPokemon.filter(
pokemon => pokemon =>
(!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) (!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) &&
&& pokemon.moveset.some(move => move.moveId && this.requiredMoves.includes(move.moveId)), pokemon.moveset.some(move => move.moveId && this.requiredMoves.includes(move.moveId)),
); );
} }
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed moves // for an inverted query, we only want to get the pokemon that don't have ANY of the listed moves
return partyPokemon.filter( return partyPokemon.filter(
pokemon => pokemon =>
(!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) (!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) &&
&& !pokemon.moveset.some(move => move.moveId && this.requiredMoves.includes(move.moveId)), !pokemon.moveset.some(move => move.moveId && this.requiredMoves.includes(move.moveId)),
); );
} }
@ -678,15 +678,15 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
if (!this.invertQuery) { if (!this.invertQuery) {
return partyPokemon.filter( return partyPokemon.filter(
pokemon => pokemon =>
(!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) (!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) &&
&& this.requiredAbilities.some(ability => pokemon.hasAbility(ability, false)), this.requiredAbilities.some(ability => pokemon.hasAbility(ability, false)),
); );
} }
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed abilities // for an inverted query, we only want to get the pokemon that don't have ANY of the listed abilities
return partyPokemon.filter( return partyPokemon.filter(
pokemon => pokemon =>
(!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) (!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) &&
&& this.requiredAbilities.filter(ability => pokemon.hasAbility(ability, false)).length === 0, this.requiredAbilities.filter(ability => pokemon.hasAbility(ability, false)).length === 0,
); );
} }
@ -728,9 +728,9 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
if (statusEffect === StatusEffect.NONE) { if (statusEffect === StatusEffect.NONE) {
// StatusEffect.NONE also checks for null or undefined status // StatusEffect.NONE also checks for null or undefined status
return ( return (
isNullOrUndefined(pokemon.status) isNullOrUndefined(pokemon.status) ||
|| isNullOrUndefined(pokemon.status.effect) isNullOrUndefined(pokemon.status.effect) ||
|| pokemon.status.effect === statusEffect pokemon.status.effect === statusEffect
); );
} }
return pokemon.status?.effect === statusEffect; return pokemon.status?.effect === statusEffect;
@ -743,9 +743,9 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
if (statusEffect === StatusEffect.NONE) { if (statusEffect === StatusEffect.NONE) {
// StatusEffect.NONE also checks for null or undefined status // StatusEffect.NONE also checks for null or undefined status
return ( return (
isNullOrUndefined(pokemon.status) isNullOrUndefined(pokemon.status) ||
|| isNullOrUndefined(pokemon.status.effect) isNullOrUndefined(pokemon.status.effect) ||
|| pokemon.status.effect === statusEffect pokemon.status.effect === statusEffect
); );
} }
return pokemon.status?.effect === statusEffect; return pokemon.status?.effect === statusEffect;
@ -796,8 +796,9 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
filterByForm(pokemon, formChangeItem) { filterByForm(pokemon, formChangeItem) {
return ( return (
pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) // Get all form changes for this species with an item trigger, including any compound triggers pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) &&
&& pokemonFormChanges[pokemon.species.speciesId] // Get all form changes for this species with an item trigger, including any compound triggers
pokemonFormChanges[pokemon.species.speciesId]
.filter(fc => fc.trigger.hasTriggerType(SpeciesFormChangeItemTrigger)) .filter(fc => fc.trigger.hasTriggerType(SpeciesFormChangeItemTrigger))
// Returns true if any form changes match this item // Returns true if any form changes match this item
.flatMap(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger) .flatMap(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger)
@ -869,8 +870,8 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
pokemon => pokemon =>
pokemon.getHeldItems().filter(it => { pokemon.getHeldItems().filter(it => {
return ( return (
!this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem) !this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem) &&
&& (!this.requireTransferable || it.isTransferable) (!this.requireTransferable || it.isTransferable)
); );
}).length > 0, }).length > 0,
); );
@ -879,8 +880,8 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const requiredItems = pokemon?.getHeldItems().filter(it => { const requiredItems = pokemon?.getHeldItems().filter(it => {
return ( return (
this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem) this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem) &&
&& (!this.requireTransferable || it.isTransferable) (!this.requireTransferable || it.isTransferable)
); );
}); });
if (requiredItems && requiredItems.length > 0) { if (requiredItems && requiredItems.length > 0) {
@ -923,9 +924,9 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
this.requiredHeldItemTypes.some(heldItemType => { this.requiredHeldItemTypes.some(heldItemType => {
return pokemon.getHeldItems().some(it => { return pokemon.getHeldItems().some(it => {
return ( return (
it instanceof AttackTypeBoosterModifier it instanceof AttackTypeBoosterModifier &&
&& (it.type as AttackTypeBoosterModifierType).moveType === heldItemType (it.type as AttackTypeBoosterModifierType).moveType === heldItemType &&
&& (!this.requireTransferable || it.isTransferable) (!this.requireTransferable || it.isTransferable)
); );
}); });
}), }),
@ -938,9 +939,9 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
pokemon.getHeldItems().filter(it => { pokemon.getHeldItems().filter(it => {
return !this.requiredHeldItemTypes.some( return !this.requiredHeldItemTypes.some(
heldItemType => heldItemType =>
it instanceof AttackTypeBoosterModifier it instanceof AttackTypeBoosterModifier &&
&& (it.type as AttackTypeBoosterModifierType).moveType === heldItemType (it.type as AttackTypeBoosterModifierType).moveType === heldItemType &&
&& (!this.requireTransferable || it.isTransferable), (!this.requireTransferable || it.isTransferable),
); );
}).length > 0, }).length > 0,
); );
@ -951,10 +952,10 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
return ( return (
this.requiredHeldItemTypes.some( this.requiredHeldItemTypes.some(
heldItemType => heldItemType =>
it instanceof AttackTypeBoosterModifier it instanceof AttackTypeBoosterModifier &&
&& (it.type as AttackTypeBoosterModifierType).moveType === heldItemType, (it.type as AttackTypeBoosterModifierType).moveType === heldItemType,
) ) &&
&& (!this.requireTransferable || it.isTransferable) (!this.requireTransferable || it.isTransferable)
); );
}); });
if (requiredItems && requiredItems.length > 0) { if (requiredItems && requiredItems.length > 0) {
@ -1020,8 +1021,8 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
// Party Pokemon inside required friendship range // Party Pokemon inside required friendship range
if ( if (
!isNullOrUndefined(this.requiredFriendshipRange) !isNullOrUndefined(this.requiredFriendshipRange) &&
&& this.requiredFriendshipRange[0] <= this.requiredFriendshipRange[1] this.requiredFriendshipRange[0] <= this.requiredFriendshipRange[1]
) { ) {
const partyPokemon = globalScene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
const pokemonInRange = this.queryParty(partyPokemon); const pokemonInRange = this.queryParty(partyPokemon);
@ -1036,8 +1037,8 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
if (!this.invertQuery) { if (!this.invertQuery) {
return partyPokemon.filter( return partyPokemon.filter(
pokemon => pokemon =>
pokemon.friendship >= this.requiredFriendshipRange[0] pokemon.friendship >= this.requiredFriendshipRange[0] &&
&& pokemon.friendship <= this.requiredFriendshipRange[1], pokemon.friendship <= this.requiredFriendshipRange[1],
); );
} }
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed requiredFriendshipRanges // for an inverted query, we only want to get the pokemon that don't have ANY of the listed requiredFriendshipRanges

View File

@ -658,7 +658,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
withIntroSpriteConfigs( withIntroSpriteConfigs(
spriteConfigs: MysteryEncounterSpriteConfig[], spriteConfigs: MysteryEncounterSpriteConfig[],
): this & Pick<IMysteryEncounter, "spriteConfigs"> { ): this & Pick<IMysteryEncounter, "spriteConfigs"> {
return Object.assign(this, { spriteConfigs }); return Object.assign(this, { spriteConfigs: spriteConfigs });
} }
withIntroDialogue(dialogue: MysteryEncounterDialogue["intro"] = []): this { withIntroDialogue(dialogue: MysteryEncounterDialogue["intro"] = []): this {
@ -703,7 +703,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @returns * @returns
*/ */
withEncounterTier(encounterTier: MysteryEncounterTier): this & Pick<IMysteryEncounter, "encounterTier"> { withEncounterTier(encounterTier: MysteryEncounterTier): this & Pick<IMysteryEncounter, "encounterTier"> {
return Object.assign(this, { encounterTier }); return Object.assign(this, { encounterTier: encounterTier });
} }
/** /**
@ -753,7 +753,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
withContinuousEncounter( withContinuousEncounter(
continuousEncounter: boolean, continuousEncounter: boolean,
): this & Required<Pick<IMysteryEncounter, "continuousEncounter">> { ): this & Required<Pick<IMysteryEncounter, "continuousEncounter">> {
return Object.assign(this, { continuousEncounter }); return Object.assign(this, { continuousEncounter: continuousEncounter });
} }
/** /**
@ -807,7 +807,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
withMaxAllowedEncounters( withMaxAllowedEncounters(
maxAllowedEncounters: number, maxAllowedEncounters: number,
): this & Required<Pick<IMysteryEncounter, "maxAllowedEncounters">> { ): this & Required<Pick<IMysteryEncounter, "maxAllowedEncounters">> {
return Object.assign(this, { maxAllowedEncounters }); return Object.assign(this, { maxAllowedEncounters: maxAllowedEncounters });
} }
/** /**
@ -939,7 +939,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @returns * @returns
*/ */
withRewards(doEncounterRewards: () => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterRewards">> { withRewards(doEncounterRewards: () => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterRewards">> {
return Object.assign(this, { doEncounterRewards }); return Object.assign(this, { doEncounterRewards: doEncounterRewards });
} }
/** /**
@ -953,7 +953,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @returns * @returns
*/ */
withExp(doEncounterExp: () => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterExp">> { withExp(doEncounterExp: () => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterExp">> {
return Object.assign(this, { doEncounterExp }); return Object.assign(this, { doEncounterExp: doEncounterExp });
} }
/** /**
@ -974,7 +974,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @returns * @returns
*/ */
withOnVisualsStart(onVisualsStart: () => boolean): this & Required<Pick<IMysteryEncounter, "onVisualsStart">> { withOnVisualsStart(onVisualsStart: () => boolean): this & Required<Pick<IMysteryEncounter, "onVisualsStart">> {
return Object.assign(this, { onVisualsStart }); return Object.assign(this, { onVisualsStart: onVisualsStart });
} }
/** /**
@ -984,7 +984,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @returns * @returns
*/ */
withCatchAllowed(catchAllowed: boolean): this & Required<Pick<IMysteryEncounter, "catchAllowed">> { withCatchAllowed(catchAllowed: boolean): this & Required<Pick<IMysteryEncounter, "catchAllowed">> {
return Object.assign(this, { catchAllowed }); return Object.assign(this, { catchAllowed: catchAllowed });
} }
/** /**
@ -1004,7 +1004,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
hideBattleIntroMessage: boolean, hideBattleIntroMessage: boolean,
): this & Required<Pick<IMysteryEncounter, "hideBattleIntroMessage">> { ): this & Required<Pick<IMysteryEncounter, "hideBattleIntroMessage">> {
return Object.assign(this, { return Object.assign(this, {
hideBattleIntroMessage, hideBattleIntroMessage: hideBattleIntroMessage,
}); });
} }
@ -1015,7 +1015,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
withAutoHideIntroVisuals( withAutoHideIntroVisuals(
autoHideIntroVisuals: boolean, autoHideIntroVisuals: boolean,
): this & Required<Pick<IMysteryEncounter, "autoHideIntroVisuals">> { ): this & Required<Pick<IMysteryEncounter, "autoHideIntroVisuals">> {
return Object.assign(this, { autoHideIntroVisuals }); return Object.assign(this, { autoHideIntroVisuals: autoHideIntroVisuals });
} }
/** /**
@ -1027,7 +1027,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
enterIntroVisualsFromRight: boolean, enterIntroVisualsFromRight: boolean,
): this & Required<Pick<IMysteryEncounter, "enterIntroVisualsFromRight">> { ): this & Required<Pick<IMysteryEncounter, "enterIntroVisualsFromRight">> {
return Object.assign(this, { return Object.assign(this, {
enterIntroVisualsFromRight, enterIntroVisualsFromRight: enterIntroVisualsFromRight,
}); });
} }

View File

@ -683,7 +683,7 @@ export function selectOptionThenPokemon(
globalScene.ui.setMode(modeToSetOnExit).then(() => { globalScene.ui.setMode(modeToSetOnExit).then(() => {
const result: PokemonAndOptionSelected = { const result: PokemonAndOptionSelected = {
selectedPokemonIndex: slotIndex, selectedPokemonIndex: slotIndex,
selectedOptionIndex, selectedOptionIndex: selectedOptionIndex,
}; };
resolve(result); resolve(result);
}); });
@ -965,10 +965,10 @@ export function transitionMysteryEncounterIntroVisuals(hide = true, destroy = tr
export function handleMysteryEncounterBattleStartEffects() { export function handleMysteryEncounterBattleStartEffects() {
const encounter = globalScene.currentBattle.mysteryEncounter; const encounter = globalScene.currentBattle.mysteryEncounter;
if ( if (
globalScene.currentBattle.isBattleMysteryEncounter() globalScene.currentBattle.isBattleMysteryEncounter() &&
&& encounter encounter &&
&& encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE &&
&& !encounter.startOfBattleEffectsComplete !encounter.startOfBattleEffectsComplete
) { ) {
const effects = encounter.startOfBattleEffects; const effects = encounter.startOfBattleEffects;
effects.forEach(effect => { effects.forEach(effect => {

View File

@ -265,11 +265,11 @@ export function getRandomSpeciesByStarterCost(
.filter(s => { .filter(s => {
const pokemonSpecies = getPokemonSpecies(s[0]); const pokemonSpecies = getPokemonSpecies(s[0]);
return ( return (
pokemonSpecies pokemonSpecies &&
&& (!excludedSpecies || !excludedSpecies.includes(s[0])) (!excludedSpecies || !excludedSpecies.includes(s[0])) &&
&& (allowSubLegendary || !pokemonSpecies.subLegendary) (allowSubLegendary || !pokemonSpecies.subLegendary) &&
&& (allowLegendary || !pokemonSpecies.legendary) (allowLegendary || !pokemonSpecies.legendary) &&
&& (allowMythical || !pokemonSpecies.mythical) (allowMythical || !pokemonSpecies.mythical)
); );
}) })
.map(s => [getPokemonSpecies(s[0]), s[1]]); .map(s => [getPokemonSpecies(s[0]), s[1]]);
@ -409,10 +409,10 @@ export async function applyModifierTypeToPlayerPokemon(
const modifier = modType.newModifier(pokemon); const modifier = modType.newModifier(pokemon);
const existing = globalScene.findModifier( const existing = globalScene.findModifier(
(m): m is PokemonHeldItemModifier => (m): m is PokemonHeldItemModifier =>
m instanceof PokemonHeldItemModifier m instanceof PokemonHeldItemModifier &&
&& m.type.id === modType.id m.type.id === modType.id &&
&& m.pokemonId === pokemon.id m.pokemonId === pokemon.id &&
&& m.matchType(modifier), m.matchType(modifier),
) as PokemonHeldItemModifier | undefined; ) as PokemonHeldItemModifier | undefined;
// At max stacks // At max stacks
@ -650,8 +650,8 @@ export async function catchPokemon(
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm(); const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
if ( if (
speciesForm.abilityHidden speciesForm.abilityHidden &&
&& (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1 (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1
) { ) {
globalScene.validateAchv(achvs.HIDDEN_ABILITY); globalScene.validateAchv(achvs.HIDDEN_ABILITY);
} }
@ -982,8 +982,8 @@ export async function addPokemonDataToDexAndValidateAchievements(pokemon: Player
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm(); const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
if ( if (
speciesForm.abilityHidden speciesForm.abilityHidden &&
&& (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1 (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1
) { ) {
globalScene.validateAchv(achvs.HIDDEN_ABILITY); globalScene.validateAchv(achvs.HIDDEN_ABILITY);
} }

View File

@ -58,8 +58,8 @@ export class PostSummonPhasePriorityQueue extends PhasePriorityQueue {
this.queue.sort((phaseA: PostSummonPhase, phaseB: PostSummonPhase) => { this.queue.sort((phaseA: PostSummonPhase, phaseB: PostSummonPhase) => {
if (phaseA.getPriority() === phaseB.getPriority()) { if (phaseA.getPriority() === phaseB.getPriority()) {
return ( return (
(phaseB.getPokemon().getEffectiveStat(Stat.SPD) - phaseA.getPokemon().getEffectiveStat(Stat.SPD)) (phaseB.getPokemon().getEffectiveStat(Stat.SPD) - phaseA.getPokemon().getEffectiveStat(Stat.SPD)) *
* (isTrickRoom() ? -1 : 1) (isTrickRoom() ? -1 : 1)
); );
} }

View File

@ -12,7 +12,6 @@ import { WeatherType } from "#enums/weather-type";
import type { Pokemon } from "#field/pokemon"; import type { Pokemon } from "#field/pokemon";
import type { PokemonFormChangeItemModifier } from "#modifiers/modifier"; import type { PokemonFormChangeItemModifier } from "#modifiers/modifier";
import { type Constructor, coerceArray } from "#utils/common"; import { type Constructor, coerceArray } from "#utils/common";
import { toCamelCase } from "#utils/strings";
import i18next from "i18next"; import i18next from "i18next";
export abstract class SpeciesFormChangeTrigger { export abstract class SpeciesFormChangeTrigger {
@ -82,10 +81,10 @@ export class SpeciesFormChangeItemTrigger extends SpeciesFormChangeTrigger {
// Assume that if m has the `formChangeItem` property, then it is a PokemonFormChangeItemModifier // Assume that if m has the `formChangeItem` property, then it is a PokemonFormChangeItemModifier
const m = r as PokemonFormChangeItemModifier; const m = r as PokemonFormChangeItemModifier;
return ( return (
"formChangeItem" in m "formChangeItem" in m &&
&& m.pokemonId === pokemon.id m.pokemonId === pokemon.id &&
&& m.formChangeItem === this.item m.formChangeItem === this.item &&
&& m.active === this.active m.active === this.active
); );
}); });
} }
@ -144,7 +143,11 @@ export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigge
super(); super();
this.move = move; this.move = move;
this.known = known; this.known = known;
const moveKey = toCamelCase(MoveId[this.move]); const moveKey = MoveId[this.move]
.split("_")
.filter(f => f)
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
.join("") as unknown as string;
this.description = known this.description = known
? i18next.t("pokemonEvolutions:Forms.moveLearned", { ? i18next.t("pokemonEvolutions:Forms.moveLearned", {
move: i18next.t(`move:${moveKey}.name`), move: i18next.t(`move:${moveKey}.name`),
@ -211,10 +214,9 @@ export class SpeciesDefaultFormMatchTrigger extends SpeciesFormChangeTrigger {
canChange(pokemon: Pokemon): boolean { canChange(pokemon: Pokemon): boolean {
return ( return (
this.formKey this.formKey ===
=== pokemon.species.forms[ pokemon.species.forms[globalScene.getSpeciesFormIndex(pokemon.species, pokemon.gender, pokemon.getNature(), true)]
globalScene.getSpeciesFormIndex(pokemon.species, pokemon.gender, pokemon.getNature(), true) .formKey
].formKey
); );
} }
} }
@ -260,10 +262,10 @@ export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger {
const isAbilitySuppressed = pokemon.summonData.abilitySuppressed; const isAbilitySuppressed = pokemon.summonData.abilitySuppressed;
return ( return (
!isAbilitySuppressed !isAbilitySuppressed &&
&& !isWeatherSuppressed !isWeatherSuppressed &&
&& pokemon.hasAbility(this.ability) pokemon.hasAbility(this.ability) &&
&& this.weathers.includes(currentWeather) this.weathers.includes(currentWeather)
); );
} }
} }

View File

@ -202,8 +202,8 @@ export abstract class PokemonSpeciesForm {
} }
let starterSpeciesId = this.speciesId; let starterSpeciesId = this.speciesId;
while ( while (
!(starterSpeciesId in starterPassiveAbilities) !(starterSpeciesId in starterPassiveAbilities) ||
|| !(formIndex in starterPassiveAbilities[starterSpeciesId]) !(formIndex in starterPassiveAbilities[starterSpeciesId])
) { ) {
if (pokemonPrevolutions.hasOwnProperty(starterSpeciesId)) { if (pokemonPrevolutions.hasOwnProperty(starterSpeciesId)) {
starterSpeciesId = pokemonPrevolutions[starterSpeciesId]; starterSpeciesId = pokemonPrevolutions[starterSpeciesId];
@ -221,8 +221,8 @@ export abstract class PokemonSpeciesForm {
getLevelMoves(): LevelMoves { getLevelMoves(): LevelMoves {
if ( if (
pokemonSpeciesFormLevelMoves.hasOwnProperty(this.speciesId) pokemonSpeciesFormLevelMoves.hasOwnProperty(this.speciesId) &&
&& pokemonSpeciesFormLevelMoves[this.speciesId].hasOwnProperty(this.formIndex) pokemonSpeciesFormLevelMoves[this.speciesId].hasOwnProperty(this.formIndex)
) { ) {
return pokemonSpeciesFormLevelMoves[this.speciesId][this.formIndex].slice(0); return pokemonSpeciesFormLevelMoves[this.speciesId][this.formIndex].slice(0);
} }
@ -302,9 +302,9 @@ export abstract class PokemonSpeciesForm {
const formSpriteKey = this.getFormSpriteKey(formIndex); const formSpriteKey = this.getFormSpriteKey(formIndex);
const showGenderDiffs = const showGenderDiffs =
this.genderDiffs this.genderDiffs &&
&& female female &&
&& ![SpeciesFormKey.MEGA, SpeciesFormKey.GIGANTAMAX].includes(formSpriteKey as SpeciesFormKey); ![SpeciesFormKey.MEGA, SpeciesFormKey.GIGANTAMAX].includes(formSpriteKey as SpeciesFormKey);
return `${showGenderDiffs ? "female__" : ""}${this.speciesId}${formSpriteKey ? `-${formSpriteKey}` : ""}`; return `${showGenderDiffs ? "female__" : ""}${this.speciesId}${formSpriteKey ? `-${formSpriteKey}` : ""}`;
} }
@ -487,8 +487,8 @@ export abstract class PokemonSpeciesForm {
} }
} }
if ( if (
pokemonFormLevelMoves.hasOwnProperty(this.speciesId) pokemonFormLevelMoves.hasOwnProperty(this.speciesId) &&
&& pokemonFormLevelMoves[this.speciesId].hasOwnProperty(this.formIndex) pokemonFormLevelMoves[this.speciesId].hasOwnProperty(this.formIndex)
) { ) {
if (!pokemonFormLevelMoves[this.speciesId][this.formIndex].find(lm => lm[0] <= 5 && lm[1] === moveId)) { if (!pokemonFormLevelMoves[this.speciesId][this.formIndex].find(lm => lm[0] <= 5 && lm[1] === moveId)) {
return false; return false;
@ -844,9 +844,9 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
? i18next.t(`battlePokemonForm:${formKey}`, { pokemonName: this.name }) ? i18next.t(`battlePokemonForm:${formKey}`, { pokemonName: this.name })
: i18next.t(`pokemonForm:battleForm.${formKey}`); : i18next.t(`pokemonForm:battleForm.${formKey}`);
} else if ( } else if (
region === Region.NORMAL region === Region.NORMAL ||
|| (this.speciesId === SpeciesId.GALAR_DARMANITAN && formIndex > 0) (this.speciesId === SpeciesId.GALAR_DARMANITAN && formIndex > 0) ||
|| this.speciesId === SpeciesId.PALDEA_TAUROS this.speciesId === SpeciesId.PALDEA_TAUROS
) { ) {
// More special cases can be added here // More special cases can be added here
const i18key = `pokemonForm:${speciesName}${formText}`; const i18key = `pokemonForm:${speciesName}${formText}`;
@ -956,11 +956,11 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
if ( if (
// If evolutions shouldn't happen, add more cases here :) // If evolutions shouldn't happen, add more cases here :)
!allowEvolving !allowEvolving ||
|| !pokemonEvolutions.hasOwnProperty(this.speciesId) !pokemonEvolutions.hasOwnProperty(this.speciesId) ||
|| (globalScene.currentBattle?.waveIndex === 20 (globalScene.currentBattle?.waveIndex === 20 &&
&& globalScene.gameMode.isClassic globalScene.gameMode.isClassic &&
&& globalScene.currentBattle.trainer) globalScene.currentBattle.trainer)
) { ) {
return this.speciesId; return this.speciesId;
} }
@ -1012,9 +1012,9 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
} }
evolutionChance = Math.min( evolutionChance = Math.min(
0.65 * easeInFunc(Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel) / preferredMinLevel) 0.65 * easeInFunc(Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel) / preferredMinLevel) +
+ 0.35 0.35 *
* easeOutFunc( easeOutFunc(
Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel * 2.5) / (preferredMinLevel * 2.5), Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel * 2.5) / (preferredMinLevel * 2.5),
), ),
1, 1,
@ -1093,9 +1093,9 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
for (const p of allEvolvingPokemon) { for (const p of allEvolvingPokemon) {
for (const e of pokemonEvolutions[p]) { for (const e of pokemonEvolutions[p]) {
if ( if (
e.speciesId === this.speciesId e.speciesId === this.speciesId &&
&& (!this.forms.length || !e.evoFormKey || e.evoFormKey === this.forms[this.formIndex].formKey) (!this.forms.length || !e.evoFormKey || e.evoFormKey === this.forms[this.formIndex].formKey) &&
&& prevolutionLevels.every(pe => pe[0] !== Number.parseInt(p)) prevolutionLevels.every(pe => pe[0] !== Number.parseInt(p))
) { ) {
const speciesId = Number.parseInt(p) as SpeciesId; const speciesId = Number.parseInt(p) as SpeciesId;
const level = e.level; const level = e.level;
@ -1131,9 +1131,9 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
prevolutionLevels[l][0], prevolutionLevels[l][0],
Math.min( Math.min(
Math.max( Math.max(
evolution?.level! evolution?.level! +
+ Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5) Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5) -
- 1, 1,
2, 2,
evolution?.level!, evolution?.level!,
), ),
@ -1149,8 +1149,8 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
this.speciesId, this.speciesId,
Math.min( Math.min(
Math.max( Math.max(
lastPrevolutionLevel lastPrevolutionLevel +
+ Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5), Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5),
lastPrevolutionLevel + 1, lastPrevolutionLevel + 1,
evolution?.level!, evolution?.level!,
), ),
@ -1172,16 +1172,16 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
const mythical = this.mythical; const mythical = this.mythical;
return species => { return species => {
return ( return (
(subLegendary (subLegendary ||
|| legendary legendary ||
|| mythical mythical ||
|| (pokemonEvolutions.hasOwnProperty(species.speciesId) === hasEvolution (pokemonEvolutions.hasOwnProperty(species.speciesId) === hasEvolution &&
&& pokemonPrevolutions.hasOwnProperty(species.speciesId) === hasPrevolution)) pokemonPrevolutions.hasOwnProperty(species.speciesId) === hasPrevolution)) &&
&& species.subLegendary === subLegendary species.subLegendary === subLegendary &&
&& species.legendary === legendary species.legendary === legendary &&
&& species.mythical === mythical species.mythical === mythical &&
&& (this.isTrainerForbidden() || !species.isTrainerForbidden()) (this.isTrainerForbidden() || !species.isTrainerForbidden()) &&
&& species.speciesId !== SpeciesId.DITTO species.speciesId !== SpeciesId.DITTO
); );
}; };
} }

View File

@ -59,12 +59,12 @@ export function getStatusEffectObtainText(
if (!sourceText) { if (!sourceText) {
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.obtain` as ParseKeys; const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.obtain` as ParseKeys;
return i18next.t(i18nKey, { pokemonNameWithAffix }); return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
} }
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.obtainSource` as ParseKeys; const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.obtainSource` as ParseKeys;
return i18next.t(i18nKey, { return i18next.t(i18nKey, {
pokemonNameWithAffix, pokemonNameWithAffix: pokemonNameWithAffix,
sourceText, sourceText: sourceText,
}); });
} }
@ -73,7 +73,7 @@ export function getStatusEffectActivationText(statusEffect: StatusEffect, pokemo
return ""; return "";
} }
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.activation` as ParseKeys; const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.activation` as ParseKeys;
return i18next.t(i18nKey, { pokemonNameWithAffix }); return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
} }
export function getStatusEffectOverlapText(statusEffect: StatusEffect, pokemonNameWithAffix: string): string { export function getStatusEffectOverlapText(statusEffect: StatusEffect, pokemonNameWithAffix: string): string {
@ -81,7 +81,7 @@ export function getStatusEffectOverlapText(statusEffect: StatusEffect, pokemonNa
return ""; return "";
} }
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.overlap` as ParseKeys; const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.overlap` as ParseKeys;
return i18next.t(i18nKey, { pokemonNameWithAffix }); return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
} }
export function getStatusEffectHealText(statusEffect: StatusEffect, pokemonNameWithAffix: string): string { export function getStatusEffectHealText(statusEffect: StatusEffect, pokemonNameWithAffix: string): string {
@ -89,7 +89,7 @@ export function getStatusEffectHealText(statusEffect: StatusEffect, pokemonNameW
return ""; return "";
} }
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.heal` as ParseKeys; const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.heal` as ParseKeys;
return i18next.t(i18nKey, { pokemonNameWithAffix }); return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
} }
export function getStatusEffectDescriptor(statusEffect: StatusEffect): string { export function getStatusEffectDescriptor(statusEffect: StatusEffect): string {

View File

@ -65,10 +65,14 @@ export class Terrain {
// Psychic terrain will only cancel a move if it: // Psychic terrain will only cancel a move if it:
return ( return (
// ... is neither spread nor field-targeted, // ... is neither spread nor field-targeted,
!isFieldTargeted(move) !isFieldTargeted(move) &&
&& !isSpreadMove(move) // .. has positive final priority, !isSpreadMove(move) &&
&& move.getPriority(user) > 0 // ...and is targeting at least 1 grounded opponent // .. has positive final priority,
&& user.getOpponents(true).some(o => targets.includes(o.getBattlerIndex()) && o.isGrounded()) move.getPriority(user) > 0 &&
// ...and is targeting at least 1 grounded opponent
user
.getOpponents(true)
.some(o => targets.includes(o.getBattlerIndex()) && o.isGrounded())
); );
} }

View File

@ -140,8 +140,8 @@ export class TrainerConfig {
this.victoryBgm = "victory_trainer"; this.victoryBgm = "victory_trainer";
this.partyTemplates = [trainerPartyTemplates.TWO_AVG]; this.partyTemplates = [trainerPartyTemplates.TWO_AVG];
this.speciesFilter = species => this.speciesFilter = species =>
(allowLegendaries || (!species.legendary && !species.subLegendary && !species.mythical)) (allowLegendaries || (!species.legendary && !species.subLegendary && !species.mythical)) &&
&& !species.isTrainerForbidden(); !species.isTrainerForbidden();
} }
getKey(): string { getKey(): string {
@ -818,8 +818,8 @@ export class TrainerConfig {
if (this.nameFemale) { if (this.nameFemale) {
// Check if the variant is either female or this is for the partner in a double battle // Check if the variant is either female or this is for the partner in a double battle
if ( if (
variant === TrainerVariant.FEMALE variant === TrainerVariant.FEMALE ||
|| (variant === TrainerVariant.DOUBLE && trainerSlot === TrainerSlot.TRAINER_PARTNER) (variant === TrainerVariant.DOUBLE && trainerSlot === TrainerSlot.TRAINER_PARTNER)
) { ) {
return this.nameFemale; return this.nameFemale;
} }
@ -1090,8 +1090,8 @@ export const trainerConfigs: TrainerConfigs = {
s => s =>
[s.ability1, s.ability2, s.abilityHidden].some( [s.ability1, s.ability2, s.abilityHidden].some(
a => a =>
!!a !!a &&
&& [ [
AbilityId.WHITE_SMOKE, AbilityId.WHITE_SMOKE,
AbilityId.GLUTTONY, AbilityId.GLUTTONY,
AbilityId.HONEY_GATHER, AbilityId.HONEY_GATHER,
@ -1104,8 +1104,8 @@ export const trainerConfigs: TrainerConfigs = {
AbilityId.SUPERSWEET_SYRUP, AbilityId.SUPERSWEET_SYRUP,
AbilityId.HOSPITALITY, AbilityId.HOSPITALITY,
].includes(a), ].includes(a),
) ) ||
|| s s
.getLevelMoves() .getLevelMoves()
.some(plm => .some(plm =>
[MoveId.SOFT_BOILED, MoveId.SPORE, MoveId.MILK_DRINK, MoveId.OVERHEAT, MoveId.TEATIME].includes(plm[1]), [MoveId.SOFT_BOILED, MoveId.SPORE, MoveId.MILK_DRINK, MoveId.OVERHEAT, MoveId.TEATIME].includes(plm[1]),
@ -1569,8 +1569,8 @@ export const trainerConfigs: TrainerConfigs = {
s => s =>
[s.ability1, s.ability2, s.abilityHidden].some( [s.ability1, s.ability2, s.abilityHidden].some(
a => a =>
!!a !!a &&
&& [ [
AbilityId.DRIZZLE, AbilityId.DRIZZLE,
AbilityId.SWIFT_SWIM, AbilityId.SWIFT_SWIM,
AbilityId.HYDRATION, AbilityId.HYDRATION,
@ -4658,9 +4658,9 @@ export const trainerConfigs: TrainerConfigs = {
2, 2,
getSpeciesFilterRandomPartyMemberFunc( getSpeciesFilterRandomPartyMemberFunc(
(species: PokemonSpecies) => (species: PokemonSpecies) =>
!pokemonEvolutions.hasOwnProperty(species.speciesId) !pokemonEvolutions.hasOwnProperty(species.speciesId) &&
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId) !pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
&& species.baseTotal >= 450, species.baseTotal >= 450,
), ),
), ),
[TrainerType.RIVAL_3]: new TrainerConfig(++t) [TrainerType.RIVAL_3]: new TrainerConfig(++t)
@ -4733,9 +4733,9 @@ export const trainerConfigs: TrainerConfigs = {
2, 2,
getSpeciesFilterRandomPartyMemberFunc( getSpeciesFilterRandomPartyMemberFunc(
(species: PokemonSpecies) => (species: PokemonSpecies) =>
!pokemonEvolutions.hasOwnProperty(species.speciesId) !pokemonEvolutions.hasOwnProperty(species.speciesId) &&
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId) !pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
&& species.baseTotal >= 450, species.baseTotal >= 450,
), ),
) )
.setSpeciesFilter(species => species.baseTotal >= 540), .setSpeciesFilter(species => species.baseTotal >= 540),
@ -4814,9 +4814,9 @@ export const trainerConfigs: TrainerConfigs = {
2, 2,
getSpeciesFilterRandomPartyMemberFunc( getSpeciesFilterRandomPartyMemberFunc(
(species: PokemonSpecies) => (species: PokemonSpecies) =>
!pokemonEvolutions.hasOwnProperty(species.speciesId) !pokemonEvolutions.hasOwnProperty(species.speciesId) &&
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId) !pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
&& species.baseTotal >= 450, species.baseTotal >= 450,
), ),
) )
.setSpeciesFilter(species => species.baseTotal >= 540) .setSpeciesFilter(species => species.baseTotal >= 540)
@ -4896,9 +4896,9 @@ export const trainerConfigs: TrainerConfigs = {
2, 2,
getSpeciesFilterRandomPartyMemberFunc( getSpeciesFilterRandomPartyMemberFunc(
(species: PokemonSpecies) => (species: PokemonSpecies) =>
!pokemonEvolutions.hasOwnProperty(species.speciesId) !pokemonEvolutions.hasOwnProperty(species.speciesId) &&
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId) !pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
&& species.baseTotal >= 450, species.baseTotal >= 450,
), ),
) )
.setSpeciesFilter(species => species.baseTotal >= 540) .setSpeciesFilter(species => species.baseTotal >= 540)
@ -4992,9 +4992,9 @@ export const trainerConfigs: TrainerConfigs = {
2, 2,
getSpeciesFilterRandomPartyMemberFunc( getSpeciesFilterRandomPartyMemberFunc(
(species: PokemonSpecies) => (species: PokemonSpecies) =>
!pokemonEvolutions.hasOwnProperty(species.speciesId) !pokemonEvolutions.hasOwnProperty(species.speciesId) &&
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId) !pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
&& species.baseTotal >= 450, species.baseTotal >= 450,
), ),
) )
.setSpeciesFilter(species => species.baseTotal >= 540) .setSpeciesFilter(species => species.baseTotal >= 540)

View File

@ -10,4 +10,4 @@ export const AbilityAttr = Object.freeze({
ABILITY_HIDDEN: 4, ABILITY_HIDDEN: 4,
}); });
export type AbilityAttr = ObjectValues<typeof AbilityAttr>; export type AbilityAttr = ObjectValues<typeof AbilityAttr>;

View File

@ -1,5 +1,5 @@
export enum AiType { export enum AiType {
RANDOM, RANDOM,
SMART_RANDOM, SMART_RANDOM,
SMART, SMART
} }

View File

@ -1,5 +1,5 @@
export enum ArenaTagSide { export enum ArenaTagSide {
BOTH, BOTH,
PLAYER, PLAYER,
ENEMY, ENEMY
} }

View File

@ -1,10 +1,13 @@
import type { ArenaTagTypeMap } from "#data/arena-tag";
import type { NonSerializableArenaTagType, SerializableArenaTagType } from "#types/arena-tags";
/** /**
* Enum representing all different types of {@linkcode ArenaTag}s. * Enum representing all different types of {@linkcode ArenaTag}s.
* @privateRemarks * @privateRemarks
* When modifying the fields in this enum, ensure that: * When modifying the fields in this enum, ensure that:
* - The entry is added to / removed from {@linkcode ArenaTagTypeMap} * - The entry is added to / removed from {@linkcode ArenaTagTypeMap}
* - The tag is added to / removed from {@linkcode NonSerializableArenaTagType} or {@linkcode SerializableArenaTagType} * - The tag is added to / removed from {@linkcode NonSerializableArenaTagType} or {@linkcode SerializableArenaTagType}
*/ */
export enum ArenaTagType { export enum ArenaTagType {
NONE = "NONE", NONE = "NONE",
MUD_SPORT = "MUD_SPORT", MUD_SPORT = "MUD_SPORT",
@ -33,5 +36,5 @@ export enum ArenaTagType {
WATER_FIRE_PLEDGE = "WATER_FIRE_PLEDGE", WATER_FIRE_PLEDGE = "WATER_FIRE_PLEDGE",
GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE", GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE",
FAIRY_LOCK = "FAIRY_LOCK", FAIRY_LOCK = "FAIRY_LOCK",
NEUTRALIZING_GAS = "NEUTRALIZING_GAS", NEUTRALIZING_GAS = "NEUTRALIZING_GAS"
} }

View File

@ -1,4 +1,4 @@
export enum BattleSpec { export enum BattleSpec {
DEFAULT, DEFAULT,
FINAL_BOSS, FINAL_BOSS
} }

View File

@ -3,5 +3,5 @@ export enum BattleStyle {
/** Display option to switch active pokemon at battle start. */ /** Display option to switch active pokemon at battle start. */
SWITCH, SWITCH,
/** Hide option to switch active pokemon at battle start. */ /** Hide option to switch active pokemon at battle start. */
SET, SET
} }

View File

@ -2,5 +2,5 @@ export enum BattleType {
WILD, WILD,
TRAINER, TRAINER,
CLEAR, CLEAR,
MYSTERY_ENCOUNTER, MYSTERY_ENCOUNTER
} }

View File

@ -7,5 +7,5 @@ export enum BattlerIndex {
PLAYER, PLAYER,
PLAYER_2, PLAYER_2,
ENEMY, ENEMY,
ENEMY_2, ENEMY_2
} }

View File

@ -9,5 +9,5 @@ export enum BerryType {
SALAC, SALAC,
LANSAT, LANSAT,
STARF, STARF,
LEPPA, LEPPA
} }

View File

@ -34,5 +34,5 @@ export enum BiomeId {
SNOWY_FOREST, SNOWY_FOREST,
ISLAND = 40, ISLAND = 40,
LABORATORY, LABORATORY,
END = 50, END = 50
} }

View File

@ -1,19 +1,19 @@
export enum Button { export enum Button {
UP, UP,
DOWN, DOWN,
LEFT, LEFT,
RIGHT, RIGHT,
SUBMIT, SUBMIT,
ACTION, ACTION,
CANCEL, CANCEL,
MENU, MENU,
STATS, STATS,
CYCLE_SHINY, CYCLE_SHINY,
CYCLE_FORM, CYCLE_FORM,
CYCLE_GENDER, CYCLE_GENDER,
CYCLE_ABILITY, CYCLE_ABILITY,
CYCLE_NATURE, CYCLE_NATURE,
CYCLE_TERA, CYCLE_TERA,
SPEED_UP, SPEED_UP,
SLOW_DOWN, SLOW_DOWN,
} }

View File

@ -1,12 +1,12 @@
export enum Challenges { export enum Challenges {
SINGLE_GENERATION, SINGLE_GENERATION,
SINGLE_TYPE, SINGLE_TYPE,
LOWER_MAX_STARTER_COST, LOWER_MAX_STARTER_COST,
LOWER_STARTER_POINTS, LOWER_STARTER_POINTS,
FRESH_START, FRESH_START,
INVERSE_BATTLE, INVERSE_BATTLE,
FLIP_STAT, FLIP_STAT,
LIMITED_CATCH, LIMITED_CATCH,
LIMITED_SUPPORT, LIMITED_SUPPORT,
HARDCORE, HARDCORE,
} }

View File

@ -1,83 +1,83 @@
export enum Color { export enum Color {
WHITE = "#ffffff", WHITE = "#ffffff",
OFF_WHITE = "#f8f8f8", OFF_WHITE = "#f8f8f8",
LIGHT_GREY = "#a0a0a0", LIGHT_GREY = "#a0a0a0",
GREY = "#484848", GREY = "#484848",
DARK_GREY = "#404040", DARK_GREY = "#404040",
PINK = "#f89890", PINK = "#f89890",
RED = "#e13d3d", RED = "#e13d3d",
RED2 = "#e70808", RED2 = "#e70808",
REDORANGE = "#d64b00", REDORANGE = "#d64b00",
ORANGE = "#f8b050", ORANGE = "#f8b050",
LIGHT_YELLOW = "#e8e8a8", LIGHT_YELLOW = "#e8e8a8",
YELLOW = "#ccbe00", YELLOW = "#ccbe00",
DARK_YELLOW = "#a68e17", DARK_YELLOW = "#a68e17",
GREEN = "#78c850", GREEN = "#78c850",
BLUE = "#40c8f8", BLUE = "#40c8f8",
COMMON = "#ffffff", COMMON = "#ffffff",
GREAT = "#3890f8", GREAT = "#3890f8",
ULTRA = "#f8d038", ULTRA = "#f8d038",
ROGUE = "#d52929", ROGUE = "#d52929",
MASTER = "#e020c0", MASTER = "#e020c0",
LUXURY = "#e64a18", LUXURY = "#e64a18"
} }
export enum TypeColor { export enum TypeColor {
NORMAL = "#ADA594", NORMAL = "#ADA594",
FIGHTING = "#A55239", FIGHTING = "#A55239",
FLYING = "#9CADF7", FLYING = "#9CADF7",
POISON = "#9141CB", POISON = "#9141CB",
GROUND = "#AE7A3B", GROUND = "#AE7A3B",
ROCK = "#BDA55A", ROCK = "#BDA55A",
BUG = "#ADBD21", BUG = "#ADBD21",
GHOST = "#6363B5", GHOST = "#6363B5",
STEEL = "#81A6BE", STEEL = "#81A6BE",
FIRE = "#F75231", FIRE = "#F75231",
WATER = "#399CFF", WATER = "#399CFF",
GRASS = "#7BCE52", GRASS = "#7BCE52",
ELECTRIC = "#FFC631", ELECTRIC = "#FFC631",
PSYCHIC = "#EF4179", PSYCHIC = "#EF4179",
ICE = "#5ACEE7", ICE = "#5ACEE7",
DRAGON = "#7B63E7", DRAGON = "#7B63E7",
DARK = "#735A4A", DARK = "#735A4A",
FAIRY = "#EF70EF", FAIRY = "#EF70EF",
} }
export enum TypeShadow { export enum TypeShadow {
NORMAL = "#574F4A", NORMAL = "#574F4A",
FIGHTING = "#4E637C", FIGHTING = "#4E637C",
FLYING = "#4E637C", FLYING = "#4E637C",
POISON = "#352166", POISON = "#352166",
GROUND = "#572D1E", GROUND = "#572D1E",
ROCK = "#5F442D", ROCK = "#5F442D",
BUG = "#5F5010", BUG = "#5F5010",
GHOST = "#323D5B", GHOST = "#323D5B",
STEEL = "#415C5F", STEEL = "#415C5F",
FIRE = "#7C1818", FIRE = "#7C1818",
WATER = "#1C4E80", WATER = "#1C4E80",
GRASS = "#4F6729", GRASS = "#4F6729",
ELECTRIC = "#804618", ELECTRIC = "#804618",
PSYCHIC = "#782155", PSYCHIC = "#782155",
ICE = "#2D5C74", ICE = "#2D5C74",
DRAGON = "#313874", DRAGON = "#313874",
DARK = "#392725", DARK = "#392725",
FAIRY = "#663878", FAIRY = "#663878",
} }
export enum ShadowColor { export enum ShadowColor {
GREY = "#636363", GREY = "#636363",
PURPLE = "#6b5a73", PURPLE = "#6b5a73",
LIGHT_GREY = "#d0d0c8", LIGHT_GREY = "#d0d0c8",
BROWN = "#69402a", BROWN = "#69402a",
PINK = "#fca2a2", PINK = "#fca2a2",
BRIGHT_RED = "#f83018", BRIGHT_RED = "#f83018",
RED = "#984038", RED = "#984038",
MAROON = "#632929", MAROON = "#632929",
GREEN = "#306850", GREEN = "#306850",
BLUE = "#006090", BLUE = "#006090",
LIGHT_YELLOW = "#ded6b5", LIGHT_YELLOW = "#ded6b5",
YELLOW = "#ebd773", YELLOW = "#ebd773",
DARK_YELLOW = "#a0a060", DARK_YELLOW = "#a0a060",
ORANGE = "#c07800", ORANGE = "#c07800",
LIGHT_ORANGE = "#ffbd73", LIGHT_ORANGE = "#ffbd73",
} }

View File

@ -3,5 +3,5 @@ export enum Command {
BALL, BALL,
POKEMON, POKEMON,
RUN, RUN,
TERA, TERA
} }

View File

@ -1,4 +1,4 @@
export enum Device { export enum Device {
GAMEPAD, GAMEPAD,
KEYBOARD, KEYBOARD,
} }

View File

@ -5,5 +5,5 @@ export enum DropDownColumn {
CAUGHT, CAUGHT,
UNLOCKS, UNLOCKS,
MISC, MISC,
SORT, SORT
} }

View File

@ -3,5 +3,5 @@
*/ */
// TODO: We currently assume these are in order // TODO: We currently assume these are in order
export enum DynamicPhaseType { export enum DynamicPhaseType {
POST_SUMMON, POST_SUMMON
} }

View File

@ -1,15 +1,15 @@
export enum EaseType { export enum EaseType {
NONE, NONE,
LINEAR = "Linear", LINEAR = "Linear",
QUADRATIC = "Quad", QUADRATIC = "Quad",
CUBIC = "Cubic", CUBIC = "Cubic",
QUARTIC = "Quart", QUARTIC = "Quart",
QUINTIC = "Quint", QUINTIC = "Quint",
SINUSOIDAL = "Sine", SINUSOIDAL = "Sine",
EXPONENTIAL = "Expo", EXPONENTIAL = "Expo",
CIRCULAR = "Circ", CIRCULAR = "Circ",
ELASTIC = "Elastic", ELASTIC = "Elastic",
BACK = "Back", BACK = "Back",
BOUNCE = "Bounce", BOUNCE = "Bounce",
STEPPED = "Stepped", STEPPED = "Stepped",
} }

View File

@ -1,7 +1,7 @@
export enum EggSourceType { export enum EggSourceType {
GACHA_MOVE, GACHA_MOVE,
GACHA_LEGENDARY, GACHA_LEGENDARY,
GACHA_SHINY, GACHA_SHINY,
SAME_SPECIES_EGG, SAME_SPECIES_EGG,
EVENT, EVENT
} }

View File

@ -2,5 +2,5 @@ export enum EggTier {
COMMON, COMMON,
RARE, RARE,
EPIC, EPIC,
LEGENDARY, LEGENDARY
} }

View File

@ -7,5 +7,5 @@ export enum EncounterAnim {
MAGMA_BG, MAGMA_BG,
MAGMA_SPOUT, MAGMA_SPOUT,
SMOKESCREEN, SMOKESCREEN,
DANCE, DANCE
} }

View File

@ -7,5 +7,5 @@ export enum ExpGainsSpeed {
/** Faster speed. */ /** Faster speed. */
FASTER, FASTER,
/** Skip gaining exp animation. */ /** Skip gaining exp animation. */
SKIP, SKIP
} }

View File

@ -5,5 +5,5 @@ export enum ExpNotification {
/** Display smaller flyout showing level gained on gaining a new level. */ /** Display smaller flyout showing level gained on gaining a new level. */
ONLY_LEVEL_UP, ONLY_LEVEL_UP,
/** Do not show any flyouts for EXP gains or levelups. */ /** Do not show any flyouts for EXP gains or levelups. */
SKIP, SKIP
} }

View File

@ -1,5 +1,5 @@
export enum FieldPosition { export enum FieldPosition {
CENTER, CENTER,
LEFT, LEFT,
RIGHT, RIGHT
} }

View File

@ -18,5 +18,5 @@ export enum ClassicFixedBossWaves {
ELITE_FOUR_3 = 186, ELITE_FOUR_3 = 186,
ELITE_FOUR_4 = 188, ELITE_FOUR_4 = 188,
CHAMPION = 190, CHAMPION = 190,
RIVAL_6 = 195, RIVAL_6 = 195
} }

View File

@ -96,8 +96,8 @@ export enum FormChangeItem {
DRACO_PLATE, DRACO_PLATE,
DREAD_PLATE, DREAD_PLATE,
PIXIE_PLATE, PIXIE_PLATE,
BLANK_PLATE, // TODO: Find a potential use for this BLANK_PLATE,// TODO: Find a potential use for this
LEGEND_PLATE, // TODO: Find a potential use for this LEGEND_PLATE,// TODO: Find a potential use for this
FIGHTING_MEMORY, FIGHTING_MEMORY,
FLYING_MEMORY, FLYING_MEMORY,
POISON_MEMORY, POISON_MEMORY,
@ -115,5 +115,5 @@ export enum FormChangeItem {
DRAGON_MEMORY, DRAGON_MEMORY,
DARK_MEMORY, DARK_MEMORY,
FAIRY_MEMORY, FAIRY_MEMORY,
NORMAL_MEMORY, NORMAL_MEMORY
} }

View File

@ -1,9 +1,9 @@
import type { ObjectValues } from "#types/type-helpers"; import type { ObjectValues } from "#types/type-helpers";
export const GachaType = Object.freeze({ export const GachaType = Object.freeze({
MOVE: 0, MOVE: 0,
LEGENDARY: 1, LEGENDARY: 1,
SHINY: 2, SHINY: 2
}); });
export type GachaType = ObjectValues<typeof GachaType>; export type GachaType = ObjectValues<typeof GachaType>;

View File

@ -7,5 +7,5 @@ export enum GameDataType {
SETTINGS, SETTINGS,
TUTORIALS, TUTORIALS,
SEEN_DIALOGUES, SEEN_DIALOGUES,
RUN_HISTORY, RUN_HISTORY
} }

View File

@ -3,5 +3,5 @@ export enum GameModes {
ENDLESS, ENDLESS,
SPLICED_ENDLESS, SPLICED_ENDLESS,
DAILY, DAILY,
CHALLENGE, CHALLENGE
} }

View File

@ -11,5 +11,5 @@ export enum HitResult {
INDIRECT, INDIRECT,
IMMUNE, IMMUNE,
CONFUSION, CONFUSION,
INDIRECT_KO, INDIRECT_KO
} }

View File

@ -3,6 +3,6 @@ export enum LearnMoveSituation {
LEVEL_UP, LEVEL_UP,
RELEARN, RELEARN,
EVOLUTION, EVOLUTION,
EVOLUTION_FUSED, // If fusionSpecies has Evolved EVOLUTION_FUSED,// If fusionSpecies has Evolved
EVOLUTION_FUSED_BASE, EVOLUTION_FUSED_BASE
} }

View File

@ -4,5 +4,5 @@ export enum LearnMoveType {
/** For learning a move via Memory Mushroom */ /** For learning a move via Memory Mushroom */
MEMORY, MEMORY,
/** For learning a move via TM */ /** For learning a move via TM */
TM, TM
} }

View File

@ -3,5 +3,5 @@ export enum ModifierPoolType {
WILD, WILD,
TRAINER, TRAINER,
ENEMY_BUFF, ENEMY_BUFF,
DAILY_STARTER, DAILY_STARTER
} }

View File

@ -1,4 +1,4 @@
export enum MoneyFormat { export enum MoneyFormat {
NORMAL, NORMAL,
ABBREVIATED, ABBREVIATED
} }

View File

@ -1,20 +1,20 @@
export enum AnimFrameTarget { export enum AnimFrameTarget {
USER, USER,
TARGET, TARGET,
GRAPHIC, GRAPHIC
} }
export enum AnimFocus { export enum AnimFocus {
TARGET = 1, TARGET = 1,
USER, USER,
USER_TARGET, USER_TARGET,
SCREEN, SCREEN
} }
export enum AnimBlendType { export enum AnimBlendType {
NORMAL, NORMAL,
ADD, ADD,
SUBTRACT, SUBTRACT
} }
export enum ChargeAnim { export enum ChargeAnim {
@ -38,7 +38,7 @@ export enum ChargeAnim {
SOLAR_BLADE_CHARGING, SOLAR_BLADE_CHARGING,
BEAK_BLAST_CHARGING, BEAK_BLAST_CHARGING,
METEOR_BEAM_CHARGING, METEOR_BEAM_CHARGING,
ELECTRO_SHOT_CHARGING, ELECTRO_SHOT_CHARGING
} }
export enum CommonAnim { export enum CommonAnim {
@ -91,5 +91,5 @@ export enum CommonAnim {
ELECTRIC_TERRAIN, ELECTRIC_TERRAIN,
GRASSY_TERRAIN, GRASSY_TERRAIN,
PSYCHIC_TERRAIN, PSYCHIC_TERRAIN,
LOCK_ON = 2120, LOCK_ON = 2120
} }

View File

@ -1,5 +1,5 @@
export enum MoveCategory { export enum MoveCategory {
PHYSICAL, PHYSICAL,
SPECIAL, SPECIAL,
STATUS, STATUS
} }

View File

@ -2,5 +2,5 @@ export enum MoveEffectTrigger {
PRE_APPLY, PRE_APPLY,
POST_APPLY, POST_APPLY,
/** Triggers one time after all target effects have applied */ /** Triggers one time after all target effects have applied */
POST_TARGET, POST_TARGET
} }

View File

@ -50,5 +50,5 @@ export enum MoveFlags {
/** Indicates a move is able to be redirected to allies in a double battle if the attacker faints */ /** Indicates a move is able to be redirected to allies in a double battle if the attacker faints */
REDIRECT_COUNTER = 1 << 18, REDIRECT_COUNTER = 1 << 18,
/** Indicates a move is able to be reflected by {@linkcode AbilityId.MAGIC_BOUNCE} and {@linkcode MoveId.MAGIC_COAT} */ /** Indicates a move is able to be reflected by {@linkcode AbilityId.MAGIC_BOUNCE} and {@linkcode MoveId.MAGIC_COAT} */
REFLECTABLE = 1 << 19, REFLECTABLE = 1 << 19
} }

View File

@ -3,5 +3,5 @@ export enum MoveResult {
SUCCESS, SUCCESS,
FAIL, FAIL,
MISS, MISS,
OTHER, OTHER
} }

View File

@ -2,11 +2,11 @@
* Used for challenge types that modify movesets, these denote the various sources of moves for pokemon. * Used for challenge types that modify movesets, these denote the various sources of moves for pokemon.
*/ */
export enum MoveSourceType { export enum MoveSourceType {
LEVEL_UP, // Currently unimplemented for move access LEVEL_UP,// Currently unimplemented for move access
RELEARNER, // Relearner moves currently unimplemented RELEARNER,// Relearner moves currently unimplemented
COMMON_TM, COMMON_TM,
GREAT_TM, GREAT_TM,
ULTRA_TM, ULTRA_TM,
COMMON_EGG, COMMON_EGG,
RARE_EGG, RARE_EGG
} }

View File

@ -25,5 +25,5 @@ export enum MoveTarget {
ENEMY_SIDE, ENEMY_SIDE,
BOTH_SIDES, BOTH_SIDES,
PARTY, PARTY,
CURSE, CURSE
} }

View File

@ -1,3 +1,6 @@
import type { PostDancingMoveAbAttr } from "#abilities/ability";
import type { DelayedAttackAttr } from "#app/@types/move-types";
import type { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
import type { ObjectValues } from "#types/type-helpers"; import type { ObjectValues } from "#types/type-helpers";
/** /**
@ -12,9 +15,9 @@ import type { ObjectValues } from "#types/type-helpers";
*/ */
export const MoveUseMode = { export const MoveUseMode = {
/** /**
* This move was used normally (i.e. clicking on the button) or called via Instruct. * This move was used normally (i.e. clicking on the button) or called via Instruct.
* It deducts PP from the user's moveset (failing if out of PP), and interacts normally with other moves and abilities. * It deducts PP from the user's moveset (failing if out of PP), and interacts normally with other moves and abilities.
*/ */
NORMAL: 1, NORMAL: 1,
/** /**
@ -68,7 +71,7 @@ export const MoveUseMode = {
* @todo Consider other means of implementing FS/DD than this - we currently only use it * @todo Consider other means of implementing FS/DD than this - we currently only use it
* to prevent pushing to move history and avoid re-delaying the attack portion * to prevent pushing to move history and avoid re-delaying the attack portion
*/ */
DELAYED_ATTACK: 6, DELAYED_ATTACK: 6
} as const; } as const;
export type MoveUseMode = ObjectValues<typeof MoveUseMode>; export type MoveUseMode = ObjectValues<typeof MoveUseMode>;
@ -93,7 +96,7 @@ export type MoveUseMode = ObjectValues<typeof MoveUseMode>;
* | {@linkcode MoveUseMode.DELAYED_ATTACK} | `true` | * | {@linkcode MoveUseMode.DELAYED_ATTACK} | `true` |
*/ */
export function isVirtual(useMode: MoveUseMode): boolean { export function isVirtual(useMode: MoveUseMode): boolean {
return useMode >= MoveUseMode.INDIRECT; return useMode >= MoveUseMode.INDIRECT
} }
/** /**
@ -158,4 +161,4 @@ export function isIgnorePP(useMode: MoveUseMode): boolean {
*/ */
export function isReflected(useMode: MoveUseMode): boolean { export function isReflected(useMode: MoveUseMode): boolean {
return useMode === MoveUseMode.REFLECTED; return useMode === MoveUseMode.REFLECTED;
} }

View File

@ -3,5 +3,5 @@ export enum MultiHitType {
_2_TO_5, _2_TO_5,
_3, _3,
_10, _10,
BEAT_UP, BEAT_UP
} }

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