mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-09-23 15:03:24 +02:00
Merge branch 'beta' of https://github.com/pagefaultgames/pokerogue into refactor-starter-handling
This commit is contained in:
commit
9f28500cae
@ -5,9 +5,7 @@ module.exports = {
|
||||
name: "no-non-type-@type-exports",
|
||||
severity: "error",
|
||||
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: {},
|
||||
to: {
|
||||
path: "(^|/)src/@types",
|
||||
@ -29,8 +27,7 @@ module.exports = {
|
||||
name: "no-circular-at-runtime",
|
||||
severity: "error",
|
||||
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: {},
|
||||
to: {
|
||||
circular: true,
|
||||
@ -42,11 +39,7 @@ module.exports = {
|
||||
{
|
||||
name: "no-orphans",
|
||||
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",
|
||||
from: {
|
||||
orphan: true,
|
||||
@ -63,8 +56,7 @@ module.exports = {
|
||||
{
|
||||
name: "no-deprecated-core",
|
||||
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",
|
||||
from: {},
|
||||
to: {
|
||||
@ -96,8 +88,7 @@ module.exports = {
|
||||
{
|
||||
name: "not-to-deprecated",
|
||||
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",
|
||||
from: {},
|
||||
to: {
|
||||
@ -108,10 +99,7 @@ module.exports = {
|
||||
name: "no-non-package-json",
|
||||
severity: "error",
|
||||
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: {},
|
||||
to: {
|
||||
dependencyTypes: ["npm-no-pkg", "npm-unknown"],
|
||||
@ -120,8 +108,7 @@ module.exports = {
|
||||
{
|
||||
name: "not-to-unresolvable",
|
||||
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",
|
||||
from: {},
|
||||
to: {
|
||||
@ -131,9 +118,7 @@ module.exports = {
|
||||
{
|
||||
name: "no-duplicate-dep-types",
|
||||
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",
|
||||
from: {},
|
||||
to: {
|
||||
@ -150,9 +135,7 @@ module.exports = {
|
||||
{
|
||||
name: "not-to-spec",
|
||||
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",
|
||||
from: {},
|
||||
to: {
|
||||
@ -163,11 +146,7 @@ module.exports = {
|
||||
name: "not-to-dev-dep",
|
||||
severity: "error",
|
||||
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: {
|
||||
path: "^(src)",
|
||||
pathNot: ["[.](?:spec|test|setup|script)[.](?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$", "./test"],
|
||||
@ -184,10 +163,7 @@ module.exports = {
|
||||
name: "optional-deps-used",
|
||||
severity: "info",
|
||||
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 your" +
|
||||
"dependency-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 yourdependency-cruiser configuration.",
|
||||
from: {},
|
||||
to: {
|
||||
dependencyTypes: ["npm-optional"],
|
||||
@ -196,10 +172,7 @@ module.exports = {
|
||||
{
|
||||
name: "peer-deps-used",
|
||||
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",
|
||||
from: {},
|
||||
to: {
|
||||
|
@ -3,7 +3,7 @@
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[src/*.{js,ts}]
|
||||
[**/*.{js,ts,json,jsonc}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,6 +11,7 @@ node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
build
|
||||
|
||||
# Editor directories and files (excluding `extensions.json` for devcontainer)
|
||||
*.code-workspace
|
||||
|
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@ -10,4 +10,4 @@
|
||||
"aaron-bond.better-comments",
|
||||
"MuTsunTsai.jsdoc-link"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
146
biome.jsonc
146
biome.jsonc
@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.2.3/schema.json",
|
||||
"vcs": {
|
||||
"enabled": false,
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true,
|
||||
"defaultBranch": "beta"
|
||||
@ -10,7 +10,7 @@
|
||||
"enabled": true,
|
||||
"useEditorconfig": true,
|
||||
"indentStyle": "space",
|
||||
"includes": ["**", "!**/src/enums/**/*", "!**/src/data/balance/**/*"],
|
||||
"includes": ["**", "!**/src/data/balance/**"],
|
||||
"lineWidth": 120
|
||||
},
|
||||
"files": {
|
||||
@ -19,14 +19,12 @@
|
||||
// and having to verify whether each individual file is ignored
|
||||
"includes": [
|
||||
"**",
|
||||
"!**/dist/**/*",
|
||||
"!**/build/**/*",
|
||||
"!**/coverage/**/*",
|
||||
"!**/public/**/*",
|
||||
"!**/.github/**/*",
|
||||
"!**/node_modules/**/*",
|
||||
"!**/.vscode/**/*",
|
||||
"!**/typedoc/**/*",
|
||||
"!**/dist",
|
||||
"!**/coverage",
|
||||
"!**/public",
|
||||
"!**/.github",
|
||||
"!**/node_modules",
|
||||
"!**/typedoc",
|
||||
// TODO: lint css and html?
|
||||
"!**/*.css",
|
||||
"!**/*.html",
|
||||
@ -48,28 +46,75 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
// TODO: Remove unneeded `options` blocks once biome's JSON schema is fixed to not require them
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"correctness": {
|
||||
"noUndeclaredVariables": "off",
|
||||
"noUndeclaredVariables": "error",
|
||||
"noUnusedVariables": "error",
|
||||
"noSwitchDeclarations": "error",
|
||||
"noVoidTypeReturn": "error",
|
||||
"noUnusedImports": {
|
||||
"level": "error",
|
||||
"fix": "safe"
|
||||
"fix": "safe",
|
||||
"options": {}
|
||||
},
|
||||
"noUnusedFunctionParameters": "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": {
|
||||
"useEnumInitializers": "off", // large enums like Moves/Species would make this cumbersome
|
||||
"useExplicitLengthCheck": {
|
||||
"level": "error",
|
||||
"fix": "safe",
|
||||
"options": {}
|
||||
},
|
||||
"useAtIndex": "error",
|
||||
"noNegationElse": {
|
||||
"level": "info", // TODO: Promote to error eventually
|
||||
"fix": "unsafe", // duplicates else blocks
|
||||
"options": {}
|
||||
},
|
||||
// TODO: Fix all instances of this and promote to `error` - this and enums are the 2 things
|
||||
// barring us from `esModuleInterop`
|
||||
"noParameterProperties": "warn",
|
||||
"useConsistentBuiltinInstantiation": {
|
||||
"level": "error",
|
||||
"fix": "safe",
|
||||
"options": {}
|
||||
},
|
||||
"noDefaultExport": "warn", // TODO: Fix `overrides.ts` and enable
|
||||
"noShoutyConstants": "error",
|
||||
"useThrowNewError": {
|
||||
"level": "error",
|
||||
"fix": "safe",
|
||||
"options": {}
|
||||
},
|
||||
"useThrowOnlyError": "error",
|
||||
"useTrimStartEnd": "error",
|
||||
"useReadonlyClassProperties": {
|
||||
"level": "info", // TODO: Graduate to error eventually
|
||||
"options": { "checkAllProperties": true }
|
||||
},
|
||||
"useConsistentObjectDefinitions": {
|
||||
"level": "error",
|
||||
"options": { "syntax": "shorthand" }
|
||||
},
|
||||
"useCollapsedIf": "error",
|
||||
"useCollapsedElseIf": "error",
|
||||
|
||||
"noSubstr": "error",
|
||||
"noYodaExpression": "error",
|
||||
"useForOf": "error",
|
||||
"useEnumInitializers": "off", // large enums like MoveId/SpeciesId would make this cumbersome
|
||||
"useBlockStatements": {
|
||||
"level": "error",
|
||||
"fix": "safe"
|
||||
"fix": "safe",
|
||||
"options": {}
|
||||
},
|
||||
"useConst": "error",
|
||||
"useImportType": "error",
|
||||
@ -80,9 +125,14 @@
|
||||
// TODO: Fix spots in the codebase where this flag would be triggered
|
||||
// and then set to "error" and re-enable the fixer
|
||||
"level": "warn",
|
||||
"fix": "none"
|
||||
"fix": "none",
|
||||
"options": {}
|
||||
},
|
||||
"useSingleVarDeclarator": {
|
||||
"level": "error",
|
||||
"fix": "safe",
|
||||
"options": {}
|
||||
},
|
||||
"useSingleVarDeclarator": "off",
|
||||
"useNodejsImportProtocol": "off",
|
||||
"useTemplate": "off", // string concatenation is faster: https://stackoverflow.com/questions/29055518/are-es6-template-literals-faster-than-string-concatenation
|
||||
"useAsConstAssertion": "error",
|
||||
@ -100,58 +150,80 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: Wait until the rule gets options for ignoring doc comments and/or different parameter names,
|
||||
// and THEN enable it codebase-wide
|
||||
"useUnifiedTypeSignatures": {
|
||||
"level": "info",
|
||||
"fix": "none",
|
||||
"options": {}
|
||||
},
|
||||
"useGroupedAccessorPairs": "error",
|
||||
"useObjectSpread": "error",
|
||||
"useNumericSeparators": "off" // TODO: Consider enabling?
|
||||
},
|
||||
"suspicious": {
|
||||
"useErrorMessage": "error",
|
||||
"noEvolvingTypes": "warn", // TODO: Review and enable ASAP - this is VERY VERY BAD
|
||||
"useNumberToFixedDigitsArgument": "error",
|
||||
"useGuardForIn": "warn", // TODO: Review and enable ASAP - this is EVEN FRICKING WORSE
|
||||
"noDoubleEquals": "error",
|
||||
// While this would be a nice rule to enable, the current structure of the codebase makes this infeasible
|
||||
// due to being used for move/ability `args` params and save data-related code.
|
||||
// This can likely be enabled for all non-utils files once these are eventually reworked, but until then we leave it off.
|
||||
"noExplicitAny": "off",
|
||||
"noAssignInExpressions": "off",
|
||||
"noPrototypeBuiltins": "off",
|
||||
"noPrototypeBuiltins": "off", // TODO: enable this
|
||||
"noFallthroughSwitchClause": "error", // Prevents accidental automatic fallthroughs in switch cases (use disable comment if needed)
|
||||
"noImplicitAnyLet": "warn", // TODO: Refactor and make this an error
|
||||
"noRedeclare": "info", // TODO: Refactor and make this an error
|
||||
"noGlobalIsNan": "off",
|
||||
"noGlobalIsNan": "error",
|
||||
"noAsyncPromiseExecutor": "warn", // TODO: Refactor and make this an 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": {
|
||||
"useWhile": "error",
|
||||
"noVoid": "warn", // TODO: Review and enable ASAP - this is also bad
|
||||
"noUselessStringConcat": "error",
|
||||
"noExcessiveCognitiveComplexity": "info", // TODO: Refactor and make this an error
|
||||
"useLiteralKeys": "off",
|
||||
"useLiteralKeys": "off", // TODO: enable?
|
||||
"noForEach": "off", // Foreach vs for of is not that simple.
|
||||
"noUselessSwitchCase": "off", // Explicit > Implicit
|
||||
"noUselessConstructor": "error",
|
||||
"noBannedTypes": "warn", // TODO: Refactor and make this an error
|
||||
"noThisInStatic": "error",
|
||||
"noUselessThisAlias": "error",
|
||||
"noUselessTernary": "error"
|
||||
"noUselessTernary": "error",
|
||||
"useIndexOf": "error"
|
||||
},
|
||||
"performance": {
|
||||
"noNamespaceImport": "error",
|
||||
"noDelete": "error"
|
||||
"noDelete": "error",
|
||||
"noBarrelFile": "error"
|
||||
},
|
||||
"nursery": {
|
||||
"useAdjacentGetterSetter": "error",
|
||||
"noConstantBinaryExpression": "error",
|
||||
"noTsIgnore": "error",
|
||||
"noAwaitInLoop": "off",
|
||||
"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"
|
||||
"noUselessUndefined": "error",
|
||||
"useMaxParams": {
|
||||
"level": "warn", // TODO: Change to "error"... eventually...
|
||||
"options": { "max": 4 } // A lot of stuff has a few params, but
|
||||
},
|
||||
"noShadow": "warn", // TODO: refactor and make "error"
|
||||
"noNonNullAssertedOptionalChain": "warn" // TODO: refactor and make "error"
|
||||
}
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double",
|
||||
"arrowParentheses": "asNeeded"
|
||||
"arrowParentheses": "asNeeded",
|
||||
"operatorLinebreak": "before"
|
||||
},
|
||||
"globals": ["Phaser"],
|
||||
"parser": {
|
||||
"jsxEverywhere": false
|
||||
}
|
||||
@ -166,7 +238,7 @@
|
||||
"noNamespaceImport": "off" // this is required for `vi.spyOn` to work in some tests
|
||||
},
|
||||
"style": {
|
||||
"noNonNullAssertion": "off"
|
||||
"noNonNullAssertion": "off" // tedious in some tests
|
||||
},
|
||||
"nursery": {
|
||||
"noFloatingPromises": "error"
|
||||
|
@ -1,48 +1,57 @@
|
||||
# Linting & Formatting
|
||||
|
||||
Writing clean, readable code is important, and linters and formatters are an integral part of ensuring code quality and readability.
|
||||
Writing clean, readable code is important, and linters and formatters are an integral part of ensuring code quality and readability. \
|
||||
It is for this reason we are using [Biome](https://biomejs.dev), an opinionated linter/formatter (akin to Prettier) with a heavy focus on speed and performance.
|
||||
|
||||
### Installation
|
||||
You probably installed Biome already without noticing it - it's included inside `package.json` and should've been downloaded when you ran `pnpm install` after cloning the repo. If you haven't done that yet, go do it.
|
||||
You probably installed Biome already without noticing it - it's included inside `package.json` and should've been downloaded when you ran `pnpm install` after cloning the repo. If you haven't done that yet, go do that first.
|
||||
|
||||
# Using Biome
|
||||
|
||||
For the most part, Biome attempts to stay "out of your hair", letting you write code while enforcing a consistent formatting standard and only notifying for errors it can't automatically fix.\
|
||||
For the most part, Biome attempts to stay "out of your hair", letting you write code while enforcing a consistent formatting standard and only notifying for errors it can't automatically fix. \
|
||||
On the other hand, if Biome complains about a piece of code, **there's probably a good reason why**. Disable comments should be used sparingly or when readabilty demands it - your first instinct should be to fix the code in question, not disable the rule.
|
||||
|
||||
## Editor Integration
|
||||
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
|
||||
Generally speaking, most users shouldn't need to run Biome directly; in addition to editor integration, [pre-commit hook](../lefthook.yml) will periodically run Biome before each commit.
|
||||
You will **not** be able to push code with `error`-level linting problems - fix them beforehand.
|
||||
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.
|
||||
|
||||
We also have a [Github Action](../.github/workflows/quality.yml) to verify code quality each time a PR is updated, preventing bad code from inadvertently making its way upstream.
|
||||
> ![WARNING]
|
||||
> 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,
|
||||
> pass the `--no-verify` flag to `git commit`.
|
||||
|
||||
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.
|
||||
|
||||
## Running Biome via CLI
|
||||
If you want Biome to check your files manually, you can run it from the command line like so:
|
||||
To run you Biome on your files manually, you have 2 main options:
|
||||
1. Run the scripts included in `package.json` (`pnpm biome` and `pnpm biome:all`). \
|
||||
These have sensible defaults for command-line options, but do not allow altering certain flags (as some cannot be specified twice in the same command)
|
||||
|
||||
```sh
|
||||
pnpm exec biome check --[flags]
|
||||
```
|
||||
2. Execute the Biome executable manually from the command line like so:
|
||||
```sh
|
||||
pnpm exec biome check --[flags]
|
||||
```
|
||||
This allows customizing non-overridable flags like `--diagnostic-level` on a more granular level, but requires slightly more verbosity and specifying more options.
|
||||
|
||||
A full list of flags and options can be found on [their website](https://biomejs.dev/reference/cli/), but here's a few useful ones to keep in mind:
|
||||
|
||||
- `--write` will cause Biome to write all "safe" fixes and formatting changes directly to your files (rather than just complaining and doing nothing).
|
||||
- `--changed` and `--staged` will only perform checks on all changed or staged files respectively. Biome sources this info from the relevant version control system (in this case Git).
|
||||
- `--write` will cause Biome to write all "safe" fixes and formatting changes directly to your files (rather than just complaining and erroring out).
|
||||
- `--changed` and `--staged` will limit checking to all changed or staged files respectively. Biome sources this info from the relevant version control system (in this case `git`).
|
||||
- `diagnostic-level=XXX` will only show diagnostics with at least the given severity level (`info/warn/error`). Useful to only focus on errors causing a failed workflow run or similar.
|
||||
|
||||
## Linting Rules
|
||||
|
||||
We primarily use Biome's [recommended ruleset](https://biomejs.dev/linter/rules/) for linting JS/TS, with some customizations to better suit our project's needs[^1].
|
||||
We primarily use Biome's [recommended ruleset](https://biomejs.dev/linter/rules/) for linting JS/TS files, with some customizations to better suit our project's needs[^1].
|
||||
|
||||
Some things to consider:
|
||||
|
||||
- We have disabled rules that prioritize style over performance, such as `useTemplate`.
|
||||
- Some rules are currently disabled or marked as warnings (`warn`) to allow for gradual refactoring without blocking development. **Do not write new code that triggers these warnings.**
|
||||
- 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.
|
||||
|
||||
Any questions about linting rules should be brought up in the `#dev-corner` channel in the discord.
|
||||
Any questions about linting rules can be brought up in the `#dev-corner` channel in the community Discord.
|
||||
|
||||
[^1]: A complete list of rules can be found in the `biome.jsonc` file in the project root.
|
||||
[^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,6 +1,6 @@
|
||||
# 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.
|
||||
|
||||
This document aims to cover everything you need to know to help keep the integration process for localization smooth and simple.
|
||||
@ -19,11 +19,12 @@ This repository is integrated into the main one as a [git submodule](https://git
|
||||
|
||||
## What Is a Submodule?
|
||||
|
||||
In essence, a submodule is a way for one repository (i.e. `pokerogue`) to use another repository (i.e. `pokerogue-locales`) internally.
|
||||
In essence, a submodule is a way for one repository (i.e. `pokerogue`) to use another repository (i.e. `pokerogue-locales`) internally.
|
||||
The parent repo (the "superproject") houses a cloned version of the 2nd repository (the "submodule") inside it, making locales effectively a "repository within a repository", so to speak.
|
||||
|
||||
>[!TIP]
|
||||
> Many popular IDEs have integrated `git` support with special handling around submodules:
|
||||
>
|
||||
> 
|
||||
>
|
||||
> 
|
||||
@ -32,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:
|
||||
```bash
|
||||
git submodule update --init --recursive
|
||||
pnpm update-locales
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> This command is run _automatically_ after cloning, merging or changing branches, so you should rarely have to run it manually.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If you run into issues with the `locales` submodule, try deleting the `.git/modules/public` and `public/locales` folders before re-running the command.
|
||||
> 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.
|
||||
|
||||
## How Are Translations Integrated?
|
||||
|
||||
@ -65,7 +66,7 @@ The basic process for fetching translated text goes roughly as follows:
|
||||
```
|
||||
|
||||
# Submitting Locales Changes
|
||||
If you have a feature or enhancement that requires additions or changes to in-game text, you will need to make a fork of the `pokerogue-locales` repo and submit your text changes as a pull request _in addition_ to your pull request to the main project.
|
||||
If you have a feature or enhancement that requires additions or changes to in-game text, you will need to make a fork of the `pokerogue-locales` repo and submit your text changes as a pull request _in addition_ to your pull request to the main project. \
|
||||
Since these two PRs aren't _technically_ linked, it's important to coordinate with the Translation Team to ensure that both PRs are integrated safely into the project.
|
||||
|
||||
> [!CAUTION]
|
||||
@ -73,29 +74,29 @@ Since these two PRs aren't _technically_ linked, it's important to coordinate wi
|
||||
|
||||
## Making Changes
|
||||
|
||||
One perk of submodules is you don't actually _need_ to clone the locales repository to start contributing - initializing the submodule already does that for you.
|
||||
One perk of submodules is you don't actually _need_ to clone the locales repository to start contributing - `git` already does that for you on initialization.
|
||||
|
||||
Given `pokerogue-locales` is a full-fledged `git` repository _inside_ `pokerogue`, making changes is roughly the same as normal, merely using `public/locales` as your root directory.
|
||||
|
||||
> [!WARNING]
|
||||
> Make sure to checkout or rebase onto `upstream/HEAD` **BEFORE** creating a 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`.
|
||||
|
||||
## Requirements for Adding Translated Text
|
||||
When your new feature or enhancement requires adding a new locales key **without changing text in existing keys**, we require 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:
|
||||
1. You (the developer) make a pull request to the main repository for your new feature.
|
||||
If this feature requires new text, the text should be integrated into the code with a new `i18next` key pointing to where you plan to add it into the locales repository.
|
||||
2. You then make another pull request — this time to the `pokerogue-locales` repository — adding a new entry with text for each key you added to your main PR.
|
||||
- You must add the corresponding **English keys** while making the PR; the Translation Team can take care of the rest[^2].
|
||||
- For any feature pulled from the mainline Pokémon games (e.g. a Move or Ability implementation), it's best practice to include a source link for any added text.
|
||||
[Poké Corpus](https://abcboy101.github.io/poke-corpus/) is a great resource for finding text from the mainline games; otherwise, a video/picture showing the text being displayed should suffice.
|
||||
- You should also [notify the current Head of Translation](#notifying-translation) to ensure a fast response.
|
||||
- You must add the corresponding **English keys** while making the PR; the Translation Team can take care of the rest[^2].
|
||||
- For any feature pulled from the mainline Pokémon games (e.g. a Move or Ability implementation), it's best practice to include a source link for any added text. \
|
||||
[Poké Corpus](https://abcboy101.github.io/poke-corpus/) is a great resource for finding text from the mainline games; otherwise, a video/picture showing the text being displayed should suffice.
|
||||
- You should also [notify the current Head of Translation](#notifying-translation) to ensure a fast response.
|
||||
3. Your locales should use 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 need to use `snake_case` for the context key. Example: `aceTrainer_male`
|
||||
4. At this point, you may begin [testing locales integration in your main PR](#documenting-locales-changes).
|
||||
5. The Translation Team will approve the locale PR (after corrections, if necessary), then merge it into `pokerogue-locales`.
|
||||
5. The Translation Team will approve the locales PR (after corrections, if necessary), then merge it into `pokerogue-locales`.
|
||||
6. The Dev Team will approve your main PR for your feature, then merge it into PokéRogue's beta environment.
|
||||
|
||||
[^2]: For those wondering, the reason for choosing English specifically is due to it being the master language set in Pontoon (the program used by the Translation Team to perform locale updates).
|
||||
@ -106,7 +107,7 @@ If a key is present in any language _except_ the master language, it won't appea
|
||||
PRs that modify existing text have different risks with respect to coordination between development and translation, so their requirements are slightly different:
|
||||
- As above, you set up 2 PRs: one for the feature itself in the main repo, and another for the associated locales changes in the locale repo.
|
||||
- Now, however, you need to have your main PR be approved by the Dev Team **before** your corresponding locale changes are merged in.
|
||||
- After your main PR is approved, the Translation Team will merge your locale PR, and you may update the submodule and post video evidence of integration into the **locales PR**.
|
||||
- After your main PR is approved, you may update the submodule and post video evidence of integration into the **locales PR**.
|
||||
- A Lead or Senior Translator from the Translation Team will then approve your main PR (if all is well), clearing your feature for merging into `beta`.
|
||||
|
||||
## Documenting Locales Changes
|
||||
@ -115,12 +116,12 @@ After making a PR involving any outwards-facing behavior (but _especially_ local
|
||||
|
||||
The basic procedure is roughly as follows:
|
||||
1. Update your locales submodule to point to **the branch you used to make the locales PR**. \
|
||||
Many IDEs with `git` integration support doing this from the GUI, \
|
||||
or you can simply do it via command-line:
|
||||
```bash
|
||||
cd public/locales
|
||||
git checkout your-branch-name-here
|
||||
```
|
||||
Many IDEs with `git` integration support doing this from the GUI, \
|
||||
or you can simply do it via command-line:
|
||||
```bash
|
||||
cd public/locales
|
||||
git checkout your-branch-name-here
|
||||
```
|
||||
2. Set some of the [in-game overrides](../CONTRIBUTING.md#1---manual-testing) inside `overrides.ts` to values corresponding to the interactions being tested.
|
||||
3. Start a local dev server (`pnpm start:dev`) and open localhost in your browser.
|
||||
4. Take screenshots or record a video of the locales changes being displayed in-game using the software of your choice[^2].
|
||||
|
10
lefthook.yml
10
lefthook.yml
@ -1,10 +1,12 @@
|
||||
pre-commit:
|
||||
skip:
|
||||
skip:
|
||||
- merge
|
||||
- rebase
|
||||
commands:
|
||||
biome-lint:
|
||||
run: pnpm exec biome check --write --reporter=summary --staged --no-errors-on-unmatched
|
||||
# Disable colors as certain IDEs don't support it in the output pane.
|
||||
# Summary mode looks decent in plain ASCII anyhow
|
||||
run: pnpm exec biome check --write --colors=off --reporter=summary --staged --no-errors-on-unmatched --diagnostic-level=error
|
||||
stage_fixed: true
|
||||
ls-lint:
|
||||
run: pnpm exec ls-lint
|
||||
@ -12,11 +14,11 @@ pre-commit:
|
||||
post-merge:
|
||||
commands:
|
||||
update-submodules:
|
||||
run: git submodule update --init --recursive
|
||||
run: pnpm update-locales
|
||||
|
||||
post-checkout:
|
||||
commands:
|
||||
update-submodules:
|
||||
# cf https://git-scm.com/docs/githooks#_post_checkout:
|
||||
# The 3rd argument is 1 for branch checkouts and 0 for file checkouts.
|
||||
run: if test {3} -eq "1"; then git submodule update --init --recursive; fi
|
||||
run: if test {3} -eq "1"; then pnpm update-locales; fi
|
@ -20,16 +20,18 @@
|
||||
"typecheck": "tsc --noEmit",
|
||||
"typecheck:scripts": "tsc -p scripts/jsconfig.json",
|
||||
"biome": "biome check --write --changed --no-errors-on-unmatched --diagnostic-level=error",
|
||||
"biome:all": "biome check --write --no-errors-on-unmatched --diagnostic-level=error",
|
||||
"biome-ci": "biome ci --diagnostic-level=error --reporter=github --no-errors-on-unmatched",
|
||||
"typedoc": "typedoc",
|
||||
"depcruise": "depcruise src test",
|
||||
"postinstall": "lefthook install; git submodule update --init --recursive",
|
||||
"update-version:patch": "pnpm version patch --force --no-git-tag-version",
|
||||
"update-version:minor": "pnpm version minor --force --no-git-tag-version",
|
||||
"update-locales": "git submodule update --progress --init --recursive",
|
||||
"update-locales:remote": "git submodule update --progress --init --recursive --force --remote"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@biomejs/biome": "2.2.3",
|
||||
"@ls-lint/ls-lint": "2.3.1",
|
||||
"@types/crypto-js": "^4.2.0",
|
||||
"@types/jsdom": "^21.1.7",
|
||||
|
@ -43,8 +43,8 @@ importers:
|
||||
version: 1.80.16(graphology-types@0.24.8)
|
||||
devDependencies:
|
||||
'@biomejs/biome':
|
||||
specifier: 2.0.0
|
||||
version: 2.0.0
|
||||
specifier: 2.2.3
|
||||
version: 2.2.3
|
||||
'@ls-lint/ls-lint':
|
||||
specifier: 2.3.1
|
||||
version: 2.3.1
|
||||
@ -195,55 +195,55 @@ packages:
|
||||
resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@biomejs/biome@2.0.0':
|
||||
resolution: {integrity: sha512-BlUoXEOI/UQTDEj/pVfnkMo8SrZw3oOWBDrXYFT43V7HTkIUDkBRY53IC5Jx1QkZbaB+0ai1wJIfYwp9+qaJTQ==}
|
||||
'@biomejs/biome@2.2.3':
|
||||
resolution: {integrity: sha512-9w0uMTvPrIdvUrxazZ42Ib7t8Y2yoGLKLdNne93RLICmaHw7mcLv4PPb5LvZLJF3141gQHiCColOh/v6VWlWmg==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
hasBin: true
|
||||
|
||||
'@biomejs/cli-darwin-arm64@2.0.0':
|
||||
resolution: {integrity: sha512-QvqWYtFFhhxdf8jMAdJzXW+Frc7X8XsnHQLY+TBM1fnT1TfeV/v9vsFI5L2J7GH6qN1+QEEJ19jHibCY2Ypplw==}
|
||||
'@biomejs/cli-darwin-arm64@2.2.3':
|
||||
resolution: {integrity: sha512-OrqQVBpadB5eqzinXN4+Q6honBz+tTlKVCsbEuEpljK8ASSItzIRZUA02mTikl3H/1nO2BMPFiJ0nkEZNy3B1w==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@biomejs/cli-darwin-x64@2.0.0':
|
||||
resolution: {integrity: sha512-5JFhls1EfmuIH4QGFPlNpxJQFC6ic3X1ltcoLN+eSRRIPr6H/lUS1ttuD0Fj7rPgPhZqopK/jfH8UVj/1hIsQw==}
|
||||
'@biomejs/cli-darwin-x64@2.2.3':
|
||||
resolution: {integrity: sha512-OCdBpb1TmyfsTgBAM1kPMXyYKTohQ48WpiN9tkt9xvU6gKVKHY4oVwteBebiOqyfyzCNaSiuKIPjmHjUZ2ZNMg==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@biomejs/cli-linux-arm64-musl@2.0.0':
|
||||
resolution: {integrity: sha512-Bxsz8ki8+b3PytMnS5SgrGV+mbAWwIxI3ydChb/d1rURlJTMdxTTq5LTebUnlsUWAX6OvJuFeiVq9Gjn1YbCyA==}
|
||||
'@biomejs/cli-linux-arm64-musl@2.2.3':
|
||||
resolution: {integrity: sha512-q3w9jJ6JFPZPeqyvwwPeaiS/6NEszZ+pXKF+IczNo8Xj6fsii45a4gEEicKyKIytalV+s829ACZujQlXAiVLBQ==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@biomejs/cli-linux-arm64@2.0.0':
|
||||
resolution: {integrity: sha512-BAH4QVi06TzAbVchXdJPsL0Z/P87jOfes15rI+p3EX9/EGTfIjaQ9lBVlHunxcmoptaA5y1Hdb9UYojIhmnjIw==}
|
||||
'@biomejs/cli-linux-arm64@2.2.3':
|
||||
resolution: {integrity: sha512-g/Uta2DqYpECxG+vUmTAmUKlVhnGEcY7DXWgKP8ruLRa8Si1QHsWknPY3B/wCo0KgYiFIOAZ9hjsHfNb9L85+g==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@biomejs/cli-linux-x64-musl@2.0.0':
|
||||
resolution: {integrity: sha512-tiQ0ABxMJb9I6GlfNp0ulrTiQSFacJRJO8245FFwE3ty3bfsfxlU/miblzDIi+qNrgGsLq5wIZcVYGp4c+HXZA==}
|
||||
'@biomejs/cli-linux-x64-musl@2.2.3':
|
||||
resolution: {integrity: sha512-y76Dn4vkP1sMRGPFlNc+OTETBhGPJ90jY3il6jAfur8XWrYBQV3swZ1Jo0R2g+JpOeeoA0cOwM7mJG6svDz79w==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@biomejs/cli-linux-x64@2.0.0':
|
||||
resolution: {integrity: sha512-09PcOGYTtkopWRm6mZ/B6Mr6UHdkniUgIG/jLBv+2J8Z61ezRE+xQmpi3yNgUrFIAU4lPA9atg7mhvE/5Bo7Wg==}
|
||||
'@biomejs/cli-linux-x64@2.2.3':
|
||||
resolution: {integrity: sha512-LEtyYL1fJsvw35CxrbQ0gZoxOG3oZsAjzfRdvRBRHxOpQ91Q5doRVjvWW/wepgSdgk5hlaNzfeqpyGmfSD0Eyw==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@biomejs/cli-win32-arm64@2.0.0':
|
||||
resolution: {integrity: sha512-vrTtuGu91xNTEQ5ZcMJBZuDlqr32DWU1r14UfePIGndF//s2WUAmer4FmgoPgruo76rprk37e8S2A2c0psXdxw==}
|
||||
'@biomejs/cli-win32-arm64@2.2.3':
|
||||
resolution: {integrity: sha512-Ms9zFYzjcJK7LV+AOMYnjN3pV3xL8Prxf9aWdDVL74onLn5kcvZ1ZMQswE5XHtnd/r/0bnUd928Rpbs14BzVmA==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@biomejs/cli-win32-x64@2.0.0':
|
||||
resolution: {integrity: sha512-2USVQ0hklNsph/KIR72ZdeptyXNnQ3JdzPn3NbjI4Sna34CnxeiYAaZcZzXPDl5PYNFBivV4xmvT3Z3rTmyDBg==}
|
||||
'@biomejs/cli-win32-x64@2.2.3':
|
||||
resolution: {integrity: sha512-gvCpewE7mBwBIpqk1YrUqNR4mCiyJm6UI3YWQQXkedSSEwzRdodRpaKhbdbHw1/hmTWOVXQ+Eih5Qctf4TCVOQ==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@ -2154,39 +2154,39 @@ snapshots:
|
||||
'@babel/helper-string-parser': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.27.1
|
||||
|
||||
'@biomejs/biome@2.0.0':
|
||||
'@biomejs/biome@2.2.3':
|
||||
optionalDependencies:
|
||||
'@biomejs/cli-darwin-arm64': 2.0.0
|
||||
'@biomejs/cli-darwin-x64': 2.0.0
|
||||
'@biomejs/cli-linux-arm64': 2.0.0
|
||||
'@biomejs/cli-linux-arm64-musl': 2.0.0
|
||||
'@biomejs/cli-linux-x64': 2.0.0
|
||||
'@biomejs/cli-linux-x64-musl': 2.0.0
|
||||
'@biomejs/cli-win32-arm64': 2.0.0
|
||||
'@biomejs/cli-win32-x64': 2.0.0
|
||||
'@biomejs/cli-darwin-arm64': 2.2.3
|
||||
'@biomejs/cli-darwin-x64': 2.2.3
|
||||
'@biomejs/cli-linux-arm64': 2.2.3
|
||||
'@biomejs/cli-linux-arm64-musl': 2.2.3
|
||||
'@biomejs/cli-linux-x64': 2.2.3
|
||||
'@biomejs/cli-linux-x64-musl': 2.2.3
|
||||
'@biomejs/cli-win32-arm64': 2.2.3
|
||||
'@biomejs/cli-win32-x64': 2.2.3
|
||||
|
||||
'@biomejs/cli-darwin-arm64@2.0.0':
|
||||
'@biomejs/cli-darwin-arm64@2.2.3':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-darwin-x64@2.0.0':
|
||||
'@biomejs/cli-darwin-x64@2.2.3':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-linux-arm64-musl@2.0.0':
|
||||
'@biomejs/cli-linux-arm64-musl@2.2.3':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-linux-arm64@2.0.0':
|
||||
'@biomejs/cli-linux-arm64@2.2.3':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-linux-x64-musl@2.0.0':
|
||||
'@biomejs/cli-linux-x64-musl@2.2.3':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-linux-x64@2.0.0':
|
||||
'@biomejs/cli-linux-x64@2.2.3':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-win32-arm64@2.0.0':
|
||||
'@biomejs/cli-win32-arm64@2.2.3':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-win32-x64@2.0.0':
|
||||
'@biomejs/cli-win32-x64@2.2.3':
|
||||
optional: true
|
||||
|
||||
'@bundled-es-modules/cookie@2.0.1':
|
||||
|
@ -4,8 +4,8 @@ import chalk from "chalk";
|
||||
export function showHelpText() {
|
||||
console.log(`
|
||||
Usage: ${chalk.cyan("pnpm eggMoves:parse [options]")}
|
||||
If given no options, assumes ${chalk.blue("\`--interactive\`")}.
|
||||
If given only a file path, assumes ${chalk.blue("\`--file\`")}.
|
||||
If given no options, assumes ${chalk.blue("`--interactive`")}.
|
||||
If given only a file path, assumes ${chalk.blue("`--file`")}.
|
||||
|
||||
${chalk.hex("#ffa500")("Options:")}
|
||||
${chalk.blue("-h, --help")} Show this help message.
|
||||
|
@ -129,7 +129,7 @@ async function scrapeTrainerNames(classes) {
|
||||
reason = `Server produced error code of ${+errCode}`;
|
||||
}
|
||||
throw new Error(
|
||||
chalk.red.bold(`Failed to parse URL for ${chalk.hex("#7fff00")(`\"${trainerClass}\"`)}!\nReason: ${reason}`),
|
||||
chalk.red.bold(`Failed to parse URL for ${chalk.hex("#7fff00")(`"${trainerClass}"`)}!\nReason: ${reason}`),
|
||||
);
|
||||
}
|
||||
}),
|
||||
@ -191,9 +191,7 @@ async function doFetch(trainerClass, seenClasses) {
|
||||
const [female, counterpartURLs] = checkGenderAndType(document);
|
||||
const names = fetchNames(trainerListHeader, female);
|
||||
if (names === INVALID_URL) {
|
||||
return Promise.reject(
|
||||
new Error(chalk.red.bold(`URL \"${classURL}\" did not correspond to a valid trainer class!`)),
|
||||
);
|
||||
return Promise.reject(new Error(chalk.red.bold(`URL "${classURL}" did not correspond to a valid trainer class!`)));
|
||||
}
|
||||
|
||||
// Recurse into all unseen gender counterparts' URLs, using the first male name we find
|
||||
@ -285,7 +283,7 @@ async function promptExisting(outFile) {
|
||||
{
|
||||
type: "confirm",
|
||||
name: "continue",
|
||||
message: `File ${chalk.blue(outFile)} already exists!` + "\nDo you want to replace it?",
|
||||
message: `File ${chalk.blue(outFile)} already exists!\nDo you want to replace it?`,
|
||||
default: false,
|
||||
},
|
||||
])
|
||||
|
@ -804,7 +804,7 @@ export class BattleScene extends SceneBase {
|
||||
* @returns An array of {@linkcode Pokemon}, as described above.
|
||||
*/
|
||||
public getField(activeOnly = false): Pokemon[] {
|
||||
const ret = new Array(4).fill(null);
|
||||
const ret: Pokemon[] = new Array(4).fill(null);
|
||||
const playerField = this.getPlayerField();
|
||||
const enemyField = this.getEnemyField();
|
||||
ret.splice(0, playerField.length, ...playerField);
|
||||
@ -827,10 +827,10 @@ export class BattleScene extends SceneBase {
|
||||
do {
|
||||
targetingMovePhase = this.phaseManager.findPhase(
|
||||
mp =>
|
||||
mp.is("MovePhase") &&
|
||||
mp.targets.length === 1 &&
|
||||
mp.targets[0] === removedPokemon.getBattlerIndex() &&
|
||||
mp.pokemon.isPlayer() !== allyPokemon.isPlayer(),
|
||||
mp.is("MovePhase")
|
||||
&& mp.targets.length === 1
|
||||
&& mp.targets[0] === removedPokemon.getBattlerIndex()
|
||||
&& mp.pokemon.isPlayer() !== allyPokemon.isPlayer(),
|
||||
) as MovePhase;
|
||||
if (targetingMovePhase && targetingMovePhase.targets[0] !== allyPokemon.getBattlerIndex()) {
|
||||
targetingMovePhase.targets[0] = allyPokemon.getBattlerIndex();
|
||||
@ -1317,16 +1317,16 @@ export class BattleScene extends SceneBase {
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
!this.gameMode.hasTrainers ||
|
||||
Overrides.BATTLE_TYPE_OVERRIDE === BattleType.WILD ||
|
||||
(Overrides.DISABLE_STANDARD_TRAINERS_OVERRIDE && isNullOrUndefined(trainerData))
|
||||
!this.gameMode.hasTrainers
|
||||
|| Overrides.BATTLE_TYPE_OVERRIDE === BattleType.WILD
|
||||
|| (Overrides.DISABLE_STANDARD_TRAINERS_OVERRIDE && isNullOrUndefined(trainerData))
|
||||
) {
|
||||
newBattleType = BattleType.WILD;
|
||||
} else {
|
||||
newBattleType =
|
||||
Overrides.BATTLE_TYPE_OVERRIDE ??
|
||||
battleType ??
|
||||
(this.gameMode.isWaveTrainer(newWaveIndex, this.arena) ? BattleType.TRAINER : BattleType.WILD);
|
||||
Overrides.BATTLE_TYPE_OVERRIDE
|
||||
?? battleType
|
||||
?? (this.gameMode.isWaveTrainer(newWaveIndex, this.arena) ? BattleType.TRAINER : BattleType.WILD);
|
||||
}
|
||||
|
||||
if (newBattleType === BattleType.TRAINER) {
|
||||
@ -1337,12 +1337,12 @@ export class BattleScene extends SceneBase {
|
||||
doubleTrainer = true;
|
||||
} else if (trainerConfigs[trainerType].hasDouble) {
|
||||
doubleTrainer =
|
||||
Overrides.RANDOM_TRAINER_OVERRIDE?.alwaysDouble ||
|
||||
!randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField));
|
||||
Overrides.RANDOM_TRAINER_OVERRIDE?.alwaysDouble
|
||||
|| !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
|
||||
if (
|
||||
trainerConfigs[trainerType].trainerTypeDouble &&
|
||||
![TrainerType.TATE, TrainerType.LIZA].includes(trainerType)
|
||||
trainerConfigs[trainerType].trainerTypeDouble
|
||||
&& ![TrainerType.TATE, TrainerType.LIZA].includes(trainerType)
|
||||
) {
|
||||
doubleTrainer = false;
|
||||
}
|
||||
@ -1359,8 +1359,8 @@ export class BattleScene extends SceneBase {
|
||||
// Check for mystery encounter
|
||||
// Can only occur in place of a standard (non-boss) wild battle, waves 10-180
|
||||
if (
|
||||
!Overrides.BATTLE_TYPE_OVERRIDE &&
|
||||
(this.isWaveMysteryEncounter(newBattleType, newWaveIndex) || newBattleType === BattleType.MYSTERY_ENCOUNTER)
|
||||
!Overrides.BATTLE_TYPE_OVERRIDE
|
||||
&& (this.isWaveMysteryEncounter(newBattleType, newWaveIndex) || newBattleType === BattleType.MYSTERY_ENCOUNTER)
|
||||
) {
|
||||
newBattleType = BattleType.MYSTERY_ENCOUNTER;
|
||||
// Reset to base spawn weight
|
||||
@ -1450,9 +1450,9 @@ export class BattleScene extends SceneBase {
|
||||
const isNewBiome = this.isNewBiome(lastBattle);
|
||||
/** Whether to reset and recall pokemon */
|
||||
const resetArenaState =
|
||||
isNewBiome ||
|
||||
[BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(this.currentBattle.battleType) ||
|
||||
this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
|
||||
isNewBiome
|
||||
|| [BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(this.currentBattle.battleType)
|
||||
|| this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
|
||||
|
||||
for (const enemyPokemon of this.getEnemyParty()) {
|
||||
enemyPokemon.destroy();
|
||||
@ -1478,6 +1478,7 @@ export class BattleScene extends SceneBase {
|
||||
pokemon.resetBattleAndWaveData();
|
||||
pokemon.resetTera();
|
||||
applyAbAttrs("PostBattleInitAbAttr", { pokemon });
|
||||
// Terapagos resets tera on each fight
|
||||
if (pokemon.hasSpecies(SpeciesId.TERAPAGOS)) {
|
||||
this.arena.playerTerasUsed = 0;
|
||||
}
|
||||
@ -1523,8 +1524,8 @@ export class BattleScene extends SceneBase {
|
||||
const fieldScale =
|
||||
Math.floor(
|
||||
Math.pow(
|
||||
1 /
|
||||
this.getField(true)
|
||||
1
|
||||
/ this.getField(true)
|
||||
.map(p => p.getSpriteScale())
|
||||
.reduce((highestScale: number, scale: number) => (highestScale = Math.max(scale, highestScale)), 0),
|
||||
0.7,
|
||||
@ -1548,10 +1549,10 @@ export class BattleScene extends SceneBase {
|
||||
|
||||
this.tweens.add({
|
||||
targets: this.field,
|
||||
scale: scale,
|
||||
scale,
|
||||
x: (defaultWidth - scaledWidth) / 2,
|
||||
y: defaultHeight - scaledHeight,
|
||||
duration: !instant ? fixedInt(Math.abs(this.field.scale - scale) * 200) : 0,
|
||||
duration: instant ? 0 : fixedInt(Math.abs(this.field.scale - scale) * 200),
|
||||
ease: "Sine.easeInOut",
|
||||
onComplete: () => resolve(),
|
||||
});
|
||||
@ -1559,20 +1560,20 @@ export class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
getSpeciesFormIndex(species: PokemonSpecies, gender?: Gender, nature?: Nature, ignoreArena?: boolean): number {
|
||||
if (!species.forms?.length) {
|
||||
if (species.forms?.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const isEggPhase =
|
||||
this.phaseManager.getCurrentPhase().is("EggLapsePhase") ||
|
||||
this.phaseManager.getCurrentPhase().is("EggHatchPhase");
|
||||
this.phaseManager.getCurrentPhase().is("EggLapsePhase")
|
||||
|| this.phaseManager.getCurrentPhase().is("EggHatchPhase");
|
||||
|
||||
if (
|
||||
// Give trainers with specialty types an appropriately-typed form for Wormadam, Rotom, Arceus, Oricorio, Silvally, or Paldean Tauros.
|
||||
!isEggPhase &&
|
||||
this.currentBattle?.battleType === BattleType.TRAINER &&
|
||||
!isNullOrUndefined(this.currentBattle.trainer) &&
|
||||
this.currentBattle.trainer.config.hasSpecialtyType()
|
||||
!isEggPhase
|
||||
&& this.currentBattle?.battleType === BattleType.TRAINER
|
||||
&& !isNullOrUndefined(this.currentBattle.trainer)
|
||||
&& this.currentBattle.trainer.config.hasSpecialtyType()
|
||||
) {
|
||||
if (species.speciesId === SpeciesId.WORMADAM) {
|
||||
switch (this.currentBattle.trainer.config.specialtyType) {
|
||||
@ -1664,9 +1665,9 @@ export class BattleScene extends SceneBase {
|
||||
return randSeedInt(8);
|
||||
case SpeciesId.EEVEE:
|
||||
if (
|
||||
this.currentBattle?.battleType === BattleType.TRAINER &&
|
||||
this.currentBattle?.waveIndex < 30 &&
|
||||
!isEggPhase
|
||||
this.currentBattle?.battleType === BattleType.TRAINER
|
||||
&& this.currentBattle?.waveIndex < 30
|
||||
&& !isEggPhase
|
||||
) {
|
||||
return 0; // No Partner Eevee for Wave 12 Preschoolers
|
||||
}
|
||||
@ -1776,9 +1777,9 @@ export class BattleScene extends SceneBase {
|
||||
} else {
|
||||
this.executeWithSeedOffset(() => {
|
||||
isBoss =
|
||||
waveIndex % 10 === 0 ||
|
||||
(this.gameMode.hasRandomBosses &&
|
||||
randSeedInt(100) < Math.min(Math.max(Math.ceil((waveIndex - 250) / 50), 0) * 2, 30));
|
||||
waveIndex % 10 === 0
|
||||
|| (this.gameMode.hasRandomBosses
|
||||
&& randSeedInt(100) < Math.min(Math.max(Math.ceil((waveIndex - 250) / 50), 0) * 2, 30));
|
||||
}, waveIndex << 2);
|
||||
}
|
||||
if (!isBoss) {
|
||||
@ -1790,10 +1791,8 @@ export class BattleScene extends SceneBase {
|
||||
if (level >= 100) {
|
||||
ret++;
|
||||
}
|
||||
if (species) {
|
||||
if (species.baseTotal >= 670) {
|
||||
ret++;
|
||||
}
|
||||
if (species && species.baseTotal >= 670) {
|
||||
ret++;
|
||||
}
|
||||
ret += Math.floor(waveIndex / 250);
|
||||
|
||||
@ -1837,12 +1836,7 @@ export class BattleScene extends SceneBase {
|
||||
this.rngCounter = 0;
|
||||
}
|
||||
|
||||
executeWithSeedOffset(
|
||||
// biome-ignore lint/complexity/noBannedTypes: Refactor to not use Function
|
||||
func: Function,
|
||||
offset: number,
|
||||
seedOverride?: string,
|
||||
): void {
|
||||
executeWithSeedOffset(func: () => void, offset: number, seedOverride?: string): void {
|
||||
if (!func) {
|
||||
return;
|
||||
}
|
||||
@ -1899,8 +1893,8 @@ export class BattleScene extends SceneBase {
|
||||
): Phaser.GameObjects.Sprite {
|
||||
sprite.setPipeline(this.spritePipeline, {
|
||||
tone: [0.0, 0.0, 0.0, 0.0],
|
||||
hasShadow: hasShadow,
|
||||
ignoreOverride: ignoreOverride,
|
||||
hasShadow,
|
||||
ignoreOverride,
|
||||
teraColor: pokemon ? getTypeRgb(pokemon.getTeraType()) : undefined,
|
||||
isTerastallized: pokemon ? pokemon.isTerastallized : false,
|
||||
});
|
||||
@ -1921,7 +1915,7 @@ export class BattleScene extends SceneBase {
|
||||
targets: this.fieldOverlay,
|
||||
alpha: 0.5,
|
||||
ease: "Sine.easeOut",
|
||||
duration: duration,
|
||||
duration,
|
||||
onComplete: () => resolve(),
|
||||
});
|
||||
});
|
||||
@ -1932,7 +1926,7 @@ export class BattleScene extends SceneBase {
|
||||
this.tweens.add({
|
||||
targets: this.fieldOverlay,
|
||||
alpha: 0,
|
||||
duration: duration,
|
||||
duration,
|
||||
ease: "Cubic.easeIn",
|
||||
onComplete: () => resolve(),
|
||||
});
|
||||
@ -1966,7 +1960,7 @@ export class BattleScene extends SceneBase {
|
||||
this.tweens.add({
|
||||
targets: this.shopOverlay,
|
||||
alpha: 0,
|
||||
duration: duration,
|
||||
duration,
|
||||
ease: "Cubic.easeIn",
|
||||
onComplete: () => resolve(),
|
||||
});
|
||||
@ -2043,7 +2037,7 @@ export class BattleScene extends SceneBase {
|
||||
this.luckLabelText.setX(this.scaledCanvas.width - 2 - (this.luckText.displayWidth + 2));
|
||||
this.tweens.add({
|
||||
targets: labels,
|
||||
duration: duration,
|
||||
duration,
|
||||
alpha: 1,
|
||||
onComplete: () => {
|
||||
for (const label of labels) {
|
||||
@ -2060,7 +2054,7 @@ export class BattleScene extends SceneBase {
|
||||
const labels = [this.luckLabelText, this.luckText];
|
||||
this.tweens.add({
|
||||
targets: labels,
|
||||
duration: duration,
|
||||
duration,
|
||||
alpha: 0,
|
||||
onComplete: () => {
|
||||
for (const label of labels) {
|
||||
@ -2074,9 +2068,9 @@ export class BattleScene extends SceneBase {
|
||||
const enemyModifierCount = this.enemyModifiers.filter(m => m.isIconVisible()).length;
|
||||
const biomeWaveTextHeight = this.biomeWaveText.getBottomLeft().y - this.biomeWaveText.getTopLeft().y;
|
||||
this.biomeWaveText.setY(
|
||||
-this.scaledCanvas.height +
|
||||
(enemyModifierCount ? (enemyModifierCount <= 12 ? 15 : 24) : 0) +
|
||||
biomeWaveTextHeight / 2,
|
||||
-this.scaledCanvas.height
|
||||
+ (enemyModifierCount ? (enemyModifierCount <= 12 ? 15 : 24) : 0)
|
||||
+ biomeWaveTextHeight / 2,
|
||||
);
|
||||
this.moneyText.setY(this.biomeWaveText.y + 10);
|
||||
this.scoreText.setY(this.moneyText.y + 10);
|
||||
@ -2100,9 +2094,9 @@ export class BattleScene extends SceneBase {
|
||||
|
||||
addFaintedEnemyScore(enemy: EnemyPokemon): void {
|
||||
let scoreIncrease =
|
||||
enemy.getSpeciesForm().getBaseExp() *
|
||||
(enemy.level / this.getMaxExpLevel()) *
|
||||
((enemy.ivs.reduce((iv: number, total: number) => (total += iv), 0) / 93) * 0.2 + 0.8);
|
||||
enemy.getSpeciesForm().getBaseExp()
|
||||
* (enemy.level / this.getMaxExpLevel())
|
||||
* ((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(
|
||||
m => (scoreIncrease *= (m as PokemonHeldItemModifier).getScoreMultiplier()),
|
||||
);
|
||||
@ -2623,8 +2617,8 @@ export class BattleScene extends SceneBase {
|
||||
const waveIndex = this.currentBattle.waveIndex;
|
||||
const waveSetIndex = Math.ceil(waveIndex / 10) - 1;
|
||||
const moneyValue =
|
||||
Math.pow((waveSetIndex + 1 + (0.75 + (((waveIndex - 1) % 10) + 1) / 10)) * 100, 1 + 0.005 * waveSetIndex) *
|
||||
moneyMultiplier;
|
||||
Math.pow((waveSetIndex + 1 + (0.75 + (((waveIndex - 1) % 10) + 1) / 10)) * 100, 1 + 0.005 * waveSetIndex)
|
||||
* moneyMultiplier;
|
||||
return Math.floor(moneyValue / 10) * 10;
|
||||
}
|
||||
|
||||
@ -2738,10 +2732,8 @@ export class BattleScene extends SceneBase {
|
||||
}
|
||||
if (!ignoreUpdate) {
|
||||
this.updateModifiers(false, instant);
|
||||
resolve();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
@ -2938,8 +2930,8 @@ export class BattleScene extends SceneBase {
|
||||
}
|
||||
} else {
|
||||
const isBoss =
|
||||
enemyPokemon.isBoss() ||
|
||||
(this.currentBattle.battleType === BattleType.TRAINER && !!this.currentBattle.trainer?.config.isBoss);
|
||||
enemyPokemon.isBoss()
|
||||
|| (this.currentBattle.battleType === BattleType.TRAINER && !!this.currentBattle.trainer?.config.isBoss);
|
||||
let upgradeChance = 32;
|
||||
if (isBoss) {
|
||||
upgradeChance /= 2;
|
||||
@ -3008,15 +3000,15 @@ export class BattleScene extends SceneBase {
|
||||
for (let m = 0; m < modifiers.length; m++) {
|
||||
const modifier = modifiers[m];
|
||||
if (
|
||||
modifier instanceof PokemonHeldItemModifier &&
|
||||
!this.getPokemonById((modifier as PokemonHeldItemModifier).pokemonId)
|
||||
modifier instanceof PokemonHeldItemModifier
|
||||
&& !this.getPokemonById((modifier as PokemonHeldItemModifier).pokemonId)
|
||||
) {
|
||||
modifiers.splice(m--, 1);
|
||||
}
|
||||
if (
|
||||
modifier instanceof PokemonHeldItemModifier &&
|
||||
!isNullOrUndefined(modifier.getSpecies()) &&
|
||||
!this.getPokemonById(modifier.pokemonId)?.hasSpecies(modifier.getSpecies()!)
|
||||
modifier instanceof PokemonHeldItemModifier
|
||||
&& !isNullOrUndefined(modifier.getSpecies())
|
||||
&& !this.getPokemonById(modifier.pokemonId)?.hasSpecies(modifier.getSpecies()!)
|
||||
) {
|
||||
modifiers.splice(m--, 1);
|
||||
}
|
||||
@ -3130,7 +3122,7 @@ export class BattleScene extends SceneBase {
|
||||
this.executeWithSeedOffset(
|
||||
() => {
|
||||
const shuffleModifiers = mods => {
|
||||
if (mods.length < 1) {
|
||||
if (mods.length === 0) {
|
||||
return mods;
|
||||
}
|
||||
const rand = randSeedInt(mods.length);
|
||||
@ -3280,8 +3272,8 @@ export class BattleScene extends SceneBase {
|
||||
|
||||
validateAchv(achv: Achv, args?: unknown[]): boolean {
|
||||
if (
|
||||
(!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE) &&
|
||||
achv.validate(args)
|
||||
(!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE)
|
||||
&& achv.validate(args)
|
||||
) {
|
||||
this.gameData.achvUnlocks[achv.id] = Date.now();
|
||||
this.ui.achvBar.showAchv(achv);
|
||||
@ -3419,8 +3411,8 @@ export class BattleScene extends SceneBase {
|
||||
|
||||
if (participantIds.size > 0) {
|
||||
if (
|
||||
this.currentBattle.battleType === BattleType.TRAINER ||
|
||||
this.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE
|
||||
this.currentBattle.battleType === BattleType.TRAINER
|
||||
|| this.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE
|
||||
) {
|
||||
expValue = Math.floor(expValue * 1.5);
|
||||
} else if (this.currentBattle.isBattleMysteryEncounter() && this.currentBattle.mysteryEncounter) {
|
||||
@ -3516,12 +3508,12 @@ export class BattleScene extends SceneBase {
|
||||
isMysteryEncounterValidForWave(battleType: BattleType, waveIndex: number): boolean {
|
||||
const [lowestMysteryEncounterWave, highestMysteryEncounterWave] = this.gameMode.getMysteryEncounterLegalWaves();
|
||||
return (
|
||||
this.gameMode.hasMysteryEncounters &&
|
||||
battleType === BattleType.WILD &&
|
||||
!this.gameMode.isBoss(waveIndex) &&
|
||||
waveIndex % 10 !== 1 &&
|
||||
waveIndex < highestMysteryEncounterWave &&
|
||||
waveIndex > lowestMysteryEncounterWave
|
||||
this.gameMode.hasMysteryEncounters
|
||||
&& battleType === BattleType.WILD
|
||||
&& !this.gameMode.isBoss(waveIndex)
|
||||
&& waveIndex % 10 !== 1
|
||||
&& waveIndex < highestMysteryEncounterWave
|
||||
&& waveIndex > lowestMysteryEncounterWave
|
||||
);
|
||||
}
|
||||
|
||||
@ -3543,18 +3535,17 @@ export class BattleScene extends SceneBase {
|
||||
// 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
|
||||
const expectedEncountersByFloor =
|
||||
(AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave)) *
|
||||
(waveIndex - lowestMysteryEncounterWave);
|
||||
(AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave))
|
||||
* (waveIndex - lowestMysteryEncounterWave);
|
||||
const currentRunDiffFromAvg = expectedEncountersByFloor - encounteredEvents.length;
|
||||
const favoredEncounterRate =
|
||||
sessionEncounterRate +
|
||||
Math.min(currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT / 2);
|
||||
sessionEncounterRate
|
||||
+ Math.min(currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT / 2);
|
||||
|
||||
const successRate = Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE ?? favoredEncounterRate;
|
||||
|
||||
// MEs can only spawn 3 or more waves after the previous ME, barring overrides
|
||||
const canSpawn =
|
||||
encounteredEvents.length === 0 || waveIndex - encounteredEvents[encounteredEvents.length - 1].waveIndex > 3;
|
||||
const canSpawn = encounteredEvents.length === 0 || waveIndex - encounteredEvents.at(-1)!.waveIndex > 3;
|
||||
|
||||
if (canSpawn || Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE !== null) {
|
||||
let roll = MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT;
|
||||
@ -3582,8 +3573,8 @@ export class BattleScene extends SceneBase {
|
||||
// Loading override or session encounter
|
||||
let encounter: MysteryEncounter | null;
|
||||
if (
|
||||
!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) &&
|
||||
allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)
|
||||
!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)
|
||||
&& allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)
|
||||
) {
|
||||
encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE];
|
||||
if (canBypass) {
|
||||
@ -3598,9 +3589,9 @@ export class BattleScene extends SceneBase {
|
||||
|
||||
// Check for queued encounters first
|
||||
if (
|
||||
!encounter &&
|
||||
this.mysteryEncounterSaveData?.queuedEncounters &&
|
||||
this.mysteryEncounterSaveData.queuedEncounters.length > 0
|
||||
!encounter
|
||||
&& this.mysteryEncounterSaveData?.queuedEncounters
|
||||
&& this.mysteryEncounterSaveData.queuedEncounters.length > 0
|
||||
) {
|
||||
let i = 0;
|
||||
while (i < this.mysteryEncounterSaveData.queuedEncounters.length && !!encounter) {
|
||||
@ -3657,11 +3648,7 @@ export class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
let availableEncounters: MysteryEncounter[] = [];
|
||||
const previousEncounter =
|
||||
this.mysteryEncounterSaveData.encounteredEvents.length > 0
|
||||
? this.mysteryEncounterSaveData.encounteredEvents[this.mysteryEncounterSaveData.encounteredEvents.length - 1]
|
||||
.type
|
||||
: null;
|
||||
const previousEncounter = this.mysteryEncounterSaveData.encounteredEvents.at(-1)?.type ?? null; // TODO: This being `null` is a bit weird
|
||||
const disabledEncounters = this.eventManager.getEventMysteryEncountersDisabled();
|
||||
const biomeMysteryEncounters =
|
||||
mysteryEncountersByBiome.get(this.arena.biomeType)?.filter(enc => !disabledEncounters.includes(enc)) ?? [];
|
||||
@ -3680,9 +3667,9 @@ export class BattleScene extends SceneBase {
|
||||
}
|
||||
const disallowedGameModes = encounterCandidate.disallowedGameModes;
|
||||
if (
|
||||
disallowedGameModes &&
|
||||
disallowedGameModes.length > 0 &&
|
||||
disallowedGameModes.includes(this.gameMode.modeId)
|
||||
disallowedGameModes
|
||||
&& disallowedGameModes.length > 0
|
||||
&& disallowedGameModes.includes(this.gameMode.modeId)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -3696,11 +3683,11 @@ export class BattleScene extends SceneBase {
|
||||
return false;
|
||||
}
|
||||
return !(
|
||||
this.mysteryEncounterSaveData.encounteredEvents.length > 0 &&
|
||||
encounterCandidate.maxAllowedEncounters &&
|
||||
encounterCandidate.maxAllowedEncounters > 0 &&
|
||||
this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length >=
|
||||
encounterCandidate.maxAllowedEncounters
|
||||
this.mysteryEncounterSaveData.encounteredEvents.length > 0
|
||||
&& encounterCandidate.maxAllowedEncounters
|
||||
&& encounterCandidate.maxAllowedEncounters > 0
|
||||
&& this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length
|
||||
>= encounterCandidate.maxAllowedEncounters
|
||||
);
|
||||
})
|
||||
.map(m => allMysteryEncounters[m]);
|
||||
|
@ -237,11 +237,11 @@ export class Battle {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
this.battleType === BattleType.TRAINER ||
|
||||
this.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE
|
||||
this.battleType === BattleType.TRAINER
|
||||
|| this.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE
|
||||
) {
|
||||
if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) {
|
||||
return `encounter_${this.trainer?.getEncounterBgm()}`;
|
||||
if (!this.started && this.trainer?.config.encounterBgm && this.trainer.getEncounterMessages().length > 0) {
|
||||
return `encounter_${this.trainer.getEncounterBgm()}`;
|
||||
}
|
||||
if (globalScene.musicPreference === MusicPreference.GENFIVE) {
|
||||
return this.trainer?.getBattleBgm() ?? null;
|
||||
|
@ -116,8 +116,8 @@ export function getIconForLatestInput(configs, source, devices, settingName) {
|
||||
export function assign(config, settingNameTarget, keycode): boolean {
|
||||
// first, we need to check if this keycode is already used on another settingName
|
||||
if (
|
||||
!canIAssignThisKey(config, getKeyWithKeycode(config, keycode)) ||
|
||||
!canIOverrideThisSetting(config, settingNameTarget)
|
||||
!canIAssignThisKey(config, getKeyWithKeycode(config, keycode))
|
||||
|| !canIOverrideThisSetting(config, settingNameTarget)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
@ -591,13 +591,10 @@ export abstract class PreDefendAbAttr extends AbAttr {
|
||||
export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr {
|
||||
override canApply({ pokemon, damage }: PreDefendModifyDamageAbAttrParams): boolean {
|
||||
return (
|
||||
pokemon.isFullHp() &&
|
||||
// Checks if pokemon has wonder_guard (which forces 1hp)
|
||||
pokemon.getMaxHp() > 1 &&
|
||||
// Damage >= hp
|
||||
damage.value >= pokemon.hp &&
|
||||
// Cannot apply if the pokemon already has sturdy from some other source
|
||||
!pokemon.getTag(BattlerTagType.STURDY)
|
||||
pokemon.isFullHp() // Checks if pokemon has wonder_guard (which forces 1hp)
|
||||
&& pokemon.getMaxHp() > 1 // Damage >= hp
|
||||
&& damage.value >= pokemon.hp // Cannot apply if the pokemon already has sturdy from some other source
|
||||
&& !pokemon.getTag(BattlerTagType.STURDY)
|
||||
);
|
||||
}
|
||||
|
||||
@ -711,9 +708,9 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr {
|
||||
|
||||
override canApply({ move, opponent: attacker, pokemon }: TypeMultiplierAbAttrParams): boolean {
|
||||
return (
|
||||
![MoveTarget.BOTH_SIDES, MoveTarget.ENEMY_SIDE, MoveTarget.USER_SIDE].includes(move.moveTarget) &&
|
||||
attacker !== pokemon &&
|
||||
attacker.getMoveType(move) === this.immuneType
|
||||
![MoveTarget.BOTH_SIDES, MoveTarget.ENEMY_SIDE, MoveTarget.USER_SIDE].includes(move.moveTarget)
|
||||
&& attacker !== pokemon
|
||||
&& attacker.getMoveType(move) === this.immuneType
|
||||
);
|
||||
}
|
||||
|
||||
@ -739,9 +736,9 @@ export class AttackTypeImmunityAbAttr extends TypeImmunityAbAttr {
|
||||
override canApply(params: TypeMultiplierAbAttrParams): boolean {
|
||||
const { move } = params;
|
||||
return (
|
||||
move.category !== MoveCategory.STATUS &&
|
||||
!move.hasAttr("NeutralDamageAgainstFlyingTypeMultiplierAttr") &&
|
||||
super.canApply(params)
|
||||
move.category !== MoveCategory.STATUS
|
||||
&& !move.hasAttr("NeutralDamageAgainstFlyingTypeMultiplierAttr")
|
||||
&& super.canApply(params)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -853,10 +850,10 @@ export class FullHpResistTypeAbAttr extends PreDefendAbAttr {
|
||||
*/
|
||||
override canApply({ typeMultiplier, move, pokemon }: TypeMultiplierAbAttrParams): boolean {
|
||||
return (
|
||||
typeMultiplier instanceof NumberHolder &&
|
||||
!move?.hasAttr("FixedDamageAttr") &&
|
||||
pokemon.isFullHp() &&
|
||||
typeMultiplier.value > 0.5
|
||||
typeMultiplier instanceof NumberHolder
|
||||
&& !move?.hasAttr("FixedDamageAttr")
|
||||
&& pokemon.isFullHp()
|
||||
&& typeMultiplier.value > 0.5
|
||||
);
|
||||
}
|
||||
|
||||
@ -883,10 +880,10 @@ export interface FieldPriorityMoveImmunityAbAttrParams extends AugmentMoveIntera
|
||||
export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr {
|
||||
override canApply({ move, opponent: attacker, cancelled }: FieldPriorityMoveImmunityAbAttrParams): boolean {
|
||||
return (
|
||||
!cancelled.value &&
|
||||
!(move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY) &&
|
||||
move.getPriority(attacker) > 0 &&
|
||||
!move.isMultiTarget()
|
||||
!cancelled.value
|
||||
&& !(move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY)
|
||||
&& move.getPriority(attacker) > 0
|
||||
&& !move.isMultiTarget()
|
||||
);
|
||||
}
|
||||
|
||||
@ -1092,9 +1089,13 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr {
|
||||
this.selfTarget = selfTarget;
|
||||
}
|
||||
|
||||
override canApply({ pokemon, opponent: attacker, move, damage }: PostMoveInteractionAbAttrParams): boolean {
|
||||
override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean {
|
||||
const hpGateFlat: number = Math.ceil(pokemon.getMaxHp() * this.hpGate);
|
||||
return this.condition(pokemon, attacker, move) && pokemon.hp <= hpGateFlat && pokemon.hp + damage > hpGateFlat;
|
||||
const lastAttackReceived = pokemon.turnData.attacksReceived.at(-1);
|
||||
const damageReceived = lastAttackReceived?.damage ?? 0;
|
||||
return (
|
||||
this.condition(pokemon, attacker, move) && pokemon.hp <= hpGateFlat && pokemon.hp + damageReceived > hpGateFlat
|
||||
);
|
||||
}
|
||||
|
||||
override apply({ simulated, pokemon, opponent }: PostMoveInteractionAbAttrParams): void {
|
||||
@ -1124,8 +1125,8 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr {
|
||||
override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean {
|
||||
const tag = globalScene.arena.getTag(this.arenaTagType) as EntryHazardTag;
|
||||
return (
|
||||
this.condition(pokemon, attacker, move) &&
|
||||
(!globalScene.arena.getTag(this.arenaTagType) || tag.layers < tag.maxLayers)
|
||||
this.condition(pokemon, attacker, move)
|
||||
&& (!globalScene.arena.getTag(this.arenaTagType) || tag.layers < tag.maxLayers)
|
||||
);
|
||||
}
|
||||
|
||||
@ -1233,10 +1234,10 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
|
||||
const effect =
|
||||
this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)];
|
||||
return (
|
||||
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
|
||||
!attacker.status &&
|
||||
(this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance) &&
|
||||
attacker.canSetStatus(effect, true, false, pokemon)
|
||||
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
|
||||
&& !attacker.status
|
||||
&& (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance)
|
||||
&& attacker.canSetStatus(effect, true, false, pokemon)
|
||||
);
|
||||
}
|
||||
|
||||
@ -1274,9 +1275,9 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
|
||||
|
||||
override canApply({ move, pokemon, opponent }: PostMoveInteractionAbAttrParams): boolean {
|
||||
return (
|
||||
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: opponent, target: pokemon }) &&
|
||||
pokemon.randBattleSeedInt(100) < this.chance &&
|
||||
opponent.canAddTag(this.tagType)
|
||||
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: opponent, target: pokemon })
|
||||
&& pokemon.randBattleSeedInt(100) < this.chance
|
||||
&& opponent.canAddTag(this.tagType)
|
||||
);
|
||||
}
|
||||
|
||||
@ -1331,9 +1332,9 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr {
|
||||
|
||||
override canApply({ simulated, move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
|
||||
return (
|
||||
!simulated &&
|
||||
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
|
||||
!attacker.hasAbilityWithAttr("BlockNonDirectDamageAbAttr")
|
||||
!simulated
|
||||
&& move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
|
||||
&& !attacker.hasAbilityWithAttr("BlockNonDirectDamageAbAttr")
|
||||
);
|
||||
}
|
||||
|
||||
@ -1367,8 +1368,8 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr {
|
||||
|
||||
override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
|
||||
return (
|
||||
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
|
||||
!attacker.getTag(BattlerTagType.PERISH_SONG)
|
||||
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
|
||||
&& !attacker.getTag(BattlerTagType.PERISH_SONG)
|
||||
);
|
||||
}
|
||||
|
||||
@ -1382,7 +1383,7 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr {
|
||||
override getTriggerMessage({ pokemon }: PostMoveInteractionAbAttrParams, abilityName: string): string {
|
||||
return i18next.t("abilityTriggers:perishBody", {
|
||||
pokemonName: getPokemonNameWithAffix(pokemon),
|
||||
abilityName: abilityName,
|
||||
abilityName,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1400,9 +1401,9 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr {
|
||||
|
||||
override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean {
|
||||
return (
|
||||
!(this.condition && !this.condition(pokemon, attacker, move)) &&
|
||||
!globalScene.arena.weather?.isImmutable() &&
|
||||
globalScene.arena.canSetWeather(this.weatherType)
|
||||
!(this.condition && !this.condition(pokemon, attacker, move))
|
||||
&& !globalScene.arena.weather?.isImmutable()
|
||||
&& globalScene.arena.canSetWeather(this.weatherType)
|
||||
);
|
||||
}
|
||||
|
||||
@ -1416,8 +1417,8 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr {
|
||||
export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr {
|
||||
override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
|
||||
return (
|
||||
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
|
||||
attacker.getAbility().isSwappable
|
||||
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
|
||||
&& attacker.getAbility().isSwappable
|
||||
);
|
||||
}
|
||||
|
||||
@ -1446,9 +1447,9 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
|
||||
|
||||
override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
|
||||
return (
|
||||
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
|
||||
attacker.getAbility().isSuppressable &&
|
||||
!attacker.getAbility().hasAttr("PostDefendAbilityGiveAbAttr")
|
||||
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
|
||||
&& attacker.getAbility().isSuppressable
|
||||
&& !attacker.getAbility().hasAttr("PostDefendAbilityGiveAbAttr")
|
||||
);
|
||||
}
|
||||
|
||||
@ -1469,7 +1470,6 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
|
||||
export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
|
||||
private chance: number;
|
||||
private attacker: Pokemon;
|
||||
private move: Move;
|
||||
|
||||
constructor(chance: number) {
|
||||
super();
|
||||
@ -1479,17 +1479,15 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
|
||||
|
||||
override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean {
|
||||
return (
|
||||
isNullOrUndefined(attacker.getTag(BattlerTagType.DISABLED)) &&
|
||||
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) &&
|
||||
(this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance)
|
||||
isNullOrUndefined(attacker.getTag(BattlerTagType.DISABLED))
|
||||
&& move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
|
||||
&& (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance)
|
||||
);
|
||||
}
|
||||
|
||||
override apply({ simulated, opponent: attacker, move, pokemon }: PostMoveInteractionAbAttrParams): void {
|
||||
// TODO: investigate why this is setting properties
|
||||
override apply({ simulated, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): void {
|
||||
if (!simulated) {
|
||||
this.attacker = attacker;
|
||||
this.move = move;
|
||||
this.attacker.addTag(BattlerTagType.DISABLED, 4, 0, pokemon.id);
|
||||
}
|
||||
}
|
||||
@ -1643,10 +1641,10 @@ export class FieldMultiplyStatAbAttr extends AbAttr {
|
||||
|
||||
canApply({ hasApplied, target, stat }: FieldMultiplyStatAbAttrParams): boolean {
|
||||
return (
|
||||
this.canStack ||
|
||||
(!hasApplied.value &&
|
||||
this.stat === stat &&
|
||||
target.getAbilityAttrs("FieldMultiplyStatAbAttr").every(attr => attr.stat !== stat))
|
||||
this.canStack
|
||||
|| (!hasApplied.value
|
||||
&& this.stat === stat
|
||||
&& target.getAbilityAttrs("FieldMultiplyStatAbAttr").every(attr => attr.stat !== stat))
|
||||
);
|
||||
}
|
||||
|
||||
@ -1688,14 +1686,14 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr {
|
||||
*/
|
||||
override canApply({ pokemon, opponent: target, move }: MoveTypeChangeAbAttrParams): boolean {
|
||||
return (
|
||||
(!this.condition || this.condition(pokemon, target, move)) &&
|
||||
!noAbilityTypeOverrideMoves.has(move.id) &&
|
||||
!(
|
||||
pokemon.isTerastallized &&
|
||||
(move.id === MoveId.TERA_BLAST ||
|
||||
(move.id === MoveId.TERA_STARSTORM &&
|
||||
pokemon.getTeraType() === PokemonType.STELLAR &&
|
||||
pokemon.hasSpecies(SpeciesId.TERAPAGOS)))
|
||||
(!this.condition || this.condition(pokemon, target, move))
|
||||
&& !noAbilityTypeOverrideMoves.has(move.id)
|
||||
&& !(
|
||||
pokemon.isTerastallized
|
||||
&& (move.id === MoveId.TERA_BLAST
|
||||
|| (move.id === MoveId.TERA_STARSTORM
|
||||
&& pokemon.getTeraType() === PokemonType.STELLAR
|
||||
&& pokemon.hasSpecies(SpeciesId.TERAPAGOS)))
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -1718,14 +1716,13 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr {
|
||||
|
||||
override canApply({ move, pokemon }: AugmentMoveInteractionAbAttrParams): boolean {
|
||||
if (
|
||||
pokemon.isTerastallized ||
|
||||
move.id === MoveId.STRUGGLE ||
|
||||
/*
|
||||
pokemon.isTerastallized
|
||||
|| move.id === MoveId.STRUGGLE /*
|
||||
* 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
|
||||
*/
|
||||
move.hasAttr("CallMoveAttr") ||
|
||||
move.hasAttr("NaturePowerAttr") // TODO: remove this line when nature power is made to extend from `CallMoveAttr`
|
||||
|| move.hasAttr("CallMoveAttr")
|
||||
|| move.hasAttr("NaturePowerAttr") // TODO: remove this line when nature power is made to extend from `CallMoveAttr`
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -2153,13 +2150,13 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
|
||||
// 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.
|
||||
if (
|
||||
super.canApply(params) &&
|
||||
!simulated &&
|
||||
hitResult < HitResult.NO_EFFECT &&
|
||||
(!this.stealCondition || this.stealCondition(pokemon, opponent, move))
|
||||
super.canApply(params)
|
||||
&& !simulated
|
||||
&& hitResult < HitResult.NO_EFFECT
|
||||
&& (!this.stealCondition || this.stealCondition(pokemon, opponent, move))
|
||||
) {
|
||||
const heldItems = this.getTargetHeldItems(opponent).filter(i => i.isTransferable);
|
||||
if (heldItems.length) {
|
||||
if (heldItems.length > 0) {
|
||||
// Ensure that the stolen item in testing is the same as when the effect is applied
|
||||
this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)];
|
||||
if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) {
|
||||
@ -2212,14 +2209,14 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr {
|
||||
override canApply(params: PostMoveInteractionAbAttrParams): boolean {
|
||||
const { simulated, pokemon, move, opponent } = params;
|
||||
if (
|
||||
super.canApply(params) &&
|
||||
(simulated ||
|
||||
(!opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") &&
|
||||
pokemon !== opponent &&
|
||||
(!this.contactRequired ||
|
||||
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: pokemon, target: opponent })) &&
|
||||
pokemon.randBattleSeedInt(100) < this.chance &&
|
||||
!pokemon.status))
|
||||
super.canApply(params)
|
||||
&& (simulated
|
||||
|| (!opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr")
|
||||
&& pokemon !== opponent
|
||||
&& (!this.contactRequired
|
||||
|| move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: pokemon, target: opponent }))
|
||||
&& pokemon.randBattleSeedInt(100) < this.chance
|
||||
&& !pokemon.status))
|
||||
) {
|
||||
const effect =
|
||||
this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)];
|
||||
@ -2263,13 +2260,13 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr {
|
||||
const { pokemon, move, opponent } = params;
|
||||
/**Battler tags inflicted by abilities post attacking are also considered additional effects.*/
|
||||
return (
|
||||
super.canApply(params) &&
|
||||
!opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") &&
|
||||
pokemon !== opponent &&
|
||||
(!this.contactRequired ||
|
||||
move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: opponent, target: pokemon })) &&
|
||||
pokemon.randBattleSeedInt(100) < this.chance(opponent, pokemon, move) &&
|
||||
!pokemon.status
|
||||
super.canApply(params)
|
||||
&& !opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr")
|
||||
&& pokemon !== opponent
|
||||
&& (!this.contactRequired
|
||||
|| move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: opponent, target: pokemon }))
|
||||
&& pokemon.randBattleSeedInt(100) < this.chance(opponent, pokemon, move)
|
||||
&& !pokemon.status
|
||||
);
|
||||
}
|
||||
|
||||
@ -2295,7 +2292,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
|
||||
override canApply({ simulated, pokemon, opponent, move, hitResult }: PostMoveInteractionAbAttrParams): boolean {
|
||||
if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, opponent, move))) {
|
||||
const heldItems = this.getTargetHeldItems(opponent).filter(i => i.isTransferable);
|
||||
if (heldItems.length) {
|
||||
if (heldItems.length > 0) {
|
||||
this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)];
|
||||
if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) {
|
||||
return true;
|
||||
@ -2922,10 +2919,10 @@ export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr {
|
||||
|
||||
override canApply(_params: AbAttrBaseParams): boolean {
|
||||
const weatherReplaceable =
|
||||
this.weatherType === WeatherType.HEAVY_RAIN ||
|
||||
this.weatherType === WeatherType.HARSH_SUN ||
|
||||
this.weatherType === WeatherType.STRONG_WINDS ||
|
||||
!globalScene.arena.weather?.isImmutable();
|
||||
this.weatherType === WeatherType.HEAVY_RAIN
|
||||
|| this.weatherType === WeatherType.HARSH_SUN
|
||||
|| this.weatherType === WeatherType.STRONG_WINDS
|
||||
|| !globalScene.arena.weather?.isImmutable();
|
||||
return weatherReplaceable && globalScene.arena.canSetWeather(this.weatherType);
|
||||
}
|
||||
|
||||
@ -2973,7 +2970,7 @@ export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr {
|
||||
|
||||
public override canApply({ pokemon }: AbAttrBaseParams): boolean {
|
||||
const status = pokemon.status?.effect;
|
||||
return !isNullOrUndefined(status) && (this.immuneEffects.length < 1 || this.immuneEffects.includes(status));
|
||||
return !isNullOrUndefined(status) && (this.immuneEffects.length === 0 || this.immuneEffects.includes(status));
|
||||
}
|
||||
|
||||
public override apply({ pokemon }: AbAttrBaseParams): void {
|
||||
@ -3030,7 +3027,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr {
|
||||
const targets = pokemon
|
||||
.getOpponents()
|
||||
.filter(t => t.getAbility().isCopiable || t.getAbility().id === AbilityId.WONDER_GUARD);
|
||||
if (!targets.length) {
|
||||
if (targets.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3161,7 +3158,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr {
|
||||
// If none are eligible to copy, it will not activate.
|
||||
const targets = user.getOpponents().filter(opp => user.canTransformInto(opp));
|
||||
if (targets.length === 0) {
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const mon = targets[user.randBattleSeedInt(targets.length)];
|
||||
@ -3254,10 +3251,10 @@ export class CommanderAbAttr extends AbAttr {
|
||||
// TODO: Should this work with X + Dondozo fusions?
|
||||
const ally = pokemon.getAlly();
|
||||
return (
|
||||
globalScene.currentBattle?.double &&
|
||||
!isNullOrUndefined(ally) &&
|
||||
ally.species.speciesId === SpeciesId.DONDOZO &&
|
||||
!(ally.isFainted() || ally.getTag(BattlerTagType.COMMANDED))
|
||||
globalScene.currentBattle?.double
|
||||
&& !isNullOrUndefined(ally)
|
||||
&& ally.species.speciesId === SpeciesId.DONDOZO
|
||||
&& !(ally.isFainted() || ally.getTag(BattlerTagType.COMMANDED))
|
||||
);
|
||||
}
|
||||
|
||||
@ -3323,8 +3320,8 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr {
|
||||
switch (weatherType) {
|
||||
case WeatherType.HARSH_SUN:
|
||||
if (
|
||||
pokemon.hasAbility(AbilityId.DESOLATE_LAND) &&
|
||||
globalScene
|
||||
pokemon.hasAbility(AbilityId.DESOLATE_LAND)
|
||||
&& globalScene
|
||||
.getField(true)
|
||||
.filter(p => p !== pokemon)
|
||||
.filter(p => p.hasAbility(AbilityId.DESOLATE_LAND)).length === 0
|
||||
@ -3334,8 +3331,8 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr {
|
||||
break;
|
||||
case WeatherType.HEAVY_RAIN:
|
||||
if (
|
||||
pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA) &&
|
||||
globalScene
|
||||
pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA)
|
||||
&& globalScene
|
||||
.getField(true)
|
||||
.filter(p => p !== pokemon)
|
||||
.filter(p => p.hasAbility(AbilityId.PRIMORDIAL_SEA)).length === 0
|
||||
@ -3345,8 +3342,8 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr {
|
||||
break;
|
||||
case WeatherType.STRONG_WINDS:
|
||||
if (
|
||||
pokemon.hasAbility(AbilityId.DELTA_STREAM) &&
|
||||
globalScene
|
||||
pokemon.hasAbility(AbilityId.DELTA_STREAM)
|
||||
&& globalScene
|
||||
.getField(true)
|
||||
.filter(p => p !== pokemon)
|
||||
.filter(p => p.hasAbility(AbilityId.DELTA_STREAM)).length === 0
|
||||
@ -3431,8 +3428,8 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr {
|
||||
switch (weatherType) {
|
||||
case WeatherType.HARSH_SUN:
|
||||
if (
|
||||
pokemon.hasAbility(AbilityId.DESOLATE_LAND) &&
|
||||
globalScene
|
||||
pokemon.hasAbility(AbilityId.DESOLATE_LAND)
|
||||
&& globalScene
|
||||
.getField(true)
|
||||
.filter(p => p !== pokemon)
|
||||
.filter(p => p.hasAbility(AbilityId.DESOLATE_LAND)).length === 0
|
||||
@ -3442,8 +3439,8 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr {
|
||||
break;
|
||||
case WeatherType.HEAVY_RAIN:
|
||||
if (
|
||||
pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA) &&
|
||||
globalScene
|
||||
pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA)
|
||||
&& globalScene
|
||||
.getField(true)
|
||||
.filter(p => p !== pokemon)
|
||||
.filter(p => p.hasAbility(AbilityId.PRIMORDIAL_SEA)).length === 0
|
||||
@ -3453,8 +3450,8 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr {
|
||||
break;
|
||||
case WeatherType.STRONG_WINDS:
|
||||
if (
|
||||
pokemon.hasAbility(AbilityId.DELTA_STREAM) &&
|
||||
globalScene
|
||||
pokemon.hasAbility(AbilityId.DELTA_STREAM)
|
||||
&& globalScene
|
||||
.getField(true)
|
||||
.filter(p => p !== pokemon)
|
||||
.filter(p => p.hasAbility(AbilityId.DELTA_STREAM)).length === 0
|
||||
@ -3673,8 +3670,8 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr {
|
||||
|
||||
override canApply({ effect, cancelled }: PreSetStatusAbAttrParams): boolean {
|
||||
return (
|
||||
!cancelled.value &&
|
||||
((this.immuneEffects.length === 0 && effect !== StatusEffect.FAINT) || this.immuneEffects.includes(effect))
|
||||
!cancelled.value
|
||||
&& ((this.immuneEffects.length === 0 && effect !== StatusEffect.FAINT) || this.immuneEffects.includes(effect))
|
||||
);
|
||||
}
|
||||
|
||||
@ -3686,7 +3683,7 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr {
|
||||
}
|
||||
|
||||
override getTriggerMessage({ pokemon, effect }: PreSetStatusAbAttrParams, abilityName: string): string {
|
||||
return this.immuneEffects.length
|
||||
return this.immuneEffects.length > 0
|
||||
? i18next.t("abilityTriggers:statusEffectImmunityWithName", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
abilityName,
|
||||
@ -3741,8 +3738,8 @@ export class UserFieldStatusEffectImmunityAbAttr extends CancelInteractionAbAttr
|
||||
|
||||
override canApply({ effect, cancelled }: UserFieldStatusEffectImmunityAbAttrParams): boolean {
|
||||
return (
|
||||
(!cancelled.value && this.immuneEffects.length === 0 && effect !== StatusEffect.FAINT) ||
|
||||
this.immuneEffects.includes(effect)
|
||||
(!cancelled.value && this.immuneEffects.length === 0 && effect !== StatusEffect.FAINT)
|
||||
|| this.immuneEffects.includes(effect)
|
||||
);
|
||||
}
|
||||
|
||||
@ -3817,9 +3814,9 @@ export class ConditionalUserFieldProtectStatAbAttr extends PreStatStageChangeAbA
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
!cancelled.value &&
|
||||
(isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) &&
|
||||
this.condition(target)
|
||||
!cancelled.value
|
||||
&& (isNullOrUndefined(this.protectedStat) || stat === this.protectedStat)
|
||||
&& this.condition(target)
|
||||
);
|
||||
}
|
||||
|
||||
@ -4119,7 +4116,7 @@ export class BlockWeatherDamageAttr extends PreWeatherDamageAbAttr {
|
||||
return false;
|
||||
}
|
||||
const weatherType = weather.weatherType;
|
||||
return !this.weatherTypes.length || this.weatherTypes.includes(weatherType);
|
||||
return this.weatherTypes.length === 0 || this.weatherTypes.includes(weatherType);
|
||||
}
|
||||
|
||||
override apply({ cancelled }: PreWeatherEffectAbAttrParams): void {
|
||||
@ -4197,8 +4194,8 @@ function getAnticipationCondition(): AbAttrCondition {
|
||||
}
|
||||
// the move's base type (not accounting for variable type changes) is super effective
|
||||
if (
|
||||
move.getMove().is("AttackMove") &&
|
||||
pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true, undefined, move.getMove()) >= 2
|
||||
move.getMove().is("AttackMove")
|
||||
&& pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true, undefined, move.getMove()) >= 2
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@ -4209,14 +4206,14 @@ function getAnticipationCondition(): AbAttrCondition {
|
||||
// edge case for hidden power, type is computed
|
||||
if (move.getMove().id === MoveId.HIDDEN_POWER) {
|
||||
const iv_val = Math.floor(
|
||||
(((opponent.ivs[Stat.HP] & 1) +
|
||||
(opponent.ivs[Stat.ATK] & 1) * 2 +
|
||||
(opponent.ivs[Stat.DEF] & 1) * 4 +
|
||||
(opponent.ivs[Stat.SPD] & 1) * 8 +
|
||||
(opponent.ivs[Stat.SPATK] & 1) * 16 +
|
||||
(opponent.ivs[Stat.SPDEF] & 1) * 32) *
|
||||
15) /
|
||||
63,
|
||||
(((opponent.ivs[Stat.HP] & 1)
|
||||
+ (opponent.ivs[Stat.ATK] & 1) * 2
|
||||
+ (opponent.ivs[Stat.DEF] & 1) * 4
|
||||
+ (opponent.ivs[Stat.SPD] & 1) * 8
|
||||
+ (opponent.ivs[Stat.SPATK] & 1) * 16
|
||||
+ (opponent.ivs[Stat.SPDEF] & 1) * 32)
|
||||
* 15)
|
||||
/ 63,
|
||||
);
|
||||
|
||||
const type = [
|
||||
@ -4283,9 +4280,9 @@ export class ForewarnAbAttr extends PostSummonAbAttr {
|
||||
} else if (move?.getMove().hasAttr("OneHitKOAttr")) {
|
||||
movePower = 150;
|
||||
} else if (
|
||||
move?.getMove().id === MoveId.COUNTER ||
|
||||
move?.getMove().id === MoveId.MIRROR_COAT ||
|
||||
move?.getMove().id === MoveId.METAL_BURST
|
||||
move?.getMove().id === MoveId.COUNTER
|
||||
|| move?.getMove().id === MoveId.MIRROR_COAT
|
||||
|| move?.getMove().id === MoveId.METAL_BURST
|
||||
) {
|
||||
movePower = 120;
|
||||
} else if (move?.getMove().power === -1) {
|
||||
@ -4661,7 +4658,7 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr {
|
||||
|
||||
this.berriesUnderCap = pokemon.battleData.berriesEaten.filter(bt => !cappedBerries.has(bt));
|
||||
|
||||
if (!this.berriesUnderCap.length) {
|
||||
if (this.berriesUnderCap.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4727,7 +4724,7 @@ export class CudChewConsumeBerryAbAttr extends AbAttr {
|
||||
* @returns `true` if the pokemon ate anything last turn
|
||||
*/
|
||||
override canApply({ pokemon }: AbAttrBaseParams): boolean {
|
||||
return !!pokemon.summonData.berriesEatenLast.length;
|
||||
return pokemon.summonData.berriesEatenLast.length > 0;
|
||||
}
|
||||
|
||||
override apply({ pokemon }: AbAttrBaseParams): void {
|
||||
@ -4876,9 +4873,9 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr {
|
||||
.getOpponents()
|
||||
.some(
|
||||
opp =>
|
||||
(opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(AbilityId.COMATOSE)) &&
|
||||
!opp.hasAbilityWithAttr("BlockNonDirectDamageAbAttr") &&
|
||||
!opp.switchOutStatus,
|
||||
(opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(AbilityId.COMATOSE))
|
||||
&& !opp.hasAbilityWithAttr("BlockNonDirectDamageAbAttr")
|
||||
&& !opp.switchOutStatus,
|
||||
);
|
||||
}
|
||||
|
||||
@ -5012,8 +5009,8 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
|
||||
];
|
||||
// The move to replicate cannot come from the Dancer
|
||||
return (
|
||||
source.getBattlerIndex() !== pokemon.getBattlerIndex() &&
|
||||
!pokemon.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType))
|
||||
source.getBattlerIndex() !== pokemon.getBattlerIndex()
|
||||
&& !pokemon.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType))
|
||||
);
|
||||
}
|
||||
|
||||
@ -5262,12 +5259,12 @@ export interface CheckTrappedAbAttrParams extends AbAttrBaseParams {
|
||||
export class ArenaTrapAbAttr extends CheckTrappedAbAttr {
|
||||
override canApply({ pokemon, opponent }: CheckTrappedAbAttrParams): boolean {
|
||||
return (
|
||||
this.arenaTrapCondition(pokemon, opponent) &&
|
||||
!(
|
||||
opponent.getTypes(true).includes(PokemonType.GHOST) ||
|
||||
(opponent.getTypes(true).includes(PokemonType.STELLAR) && opponent.getTypes().includes(PokemonType.GHOST))
|
||||
) &&
|
||||
!opponent.hasAbility(AbilityId.RUN_AWAY)
|
||||
this.arenaTrapCondition(pokemon, opponent)
|
||||
&& !(
|
||||
opponent.getTypes(true).includes(PokemonType.GHOST)
|
||||
|| (opponent.getTypes(true).includes(PokemonType.STELLAR) && opponent.getTypes().includes(PokemonType.GHOST))
|
||||
)
|
||||
&& !opponent.hasAbility(AbilityId.RUN_AWAY)
|
||||
);
|
||||
}
|
||||
|
||||
@ -5328,7 +5325,7 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr {
|
||||
|
||||
override canApply({ simulated, victory, pokemon }: PostBattleAbAttrParams): boolean {
|
||||
const postBattleLoot = globalScene.currentBattle.postBattleLoot;
|
||||
if (!simulated && postBattleLoot.length && victory) {
|
||||
if (!simulated && postBattleLoot.length > 0 && victory) {
|
||||
this.randItem = randSeedItem(postBattleLoot);
|
||||
return globalScene.canTransferHeldItemModifier(this.randItem, pokemon, 1);
|
||||
}
|
||||
@ -5427,9 +5424,9 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
|
||||
|
||||
override canApply({ pokemon, attacker, move, simulated }: PostFaintAbAttrParams): boolean {
|
||||
if (
|
||||
move === undefined ||
|
||||
attacker === undefined ||
|
||||
!move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
|
||||
move === undefined
|
||||
|| attacker === undefined
|
||||
|| !move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -6000,9 +5997,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.
|
||||
// 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 (
|
||||
lastPokemon === pokemon ||
|
||||
((speciesId === SpeciesId.OGERPON || speciesId === SpeciesId.TERAPAGOS) &&
|
||||
(lastPokemon.isTerastallized || pokemon.isTerastallized))
|
||||
lastPokemon === pokemon
|
||||
|| ((speciesId === SpeciesId.OGERPON || speciesId === SpeciesId.TERAPAGOS)
|
||||
&& (lastPokemon.isTerastallized || pokemon.isTerastallized))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -6156,7 +6153,7 @@ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr {
|
||||
override apply({ pokemon }: AbAttrBaseParams): void {
|
||||
const currentTerrain = globalScene.arena.getTerrainType();
|
||||
const typeChange: PokemonType[] = this.determineTypeChange(pokemon, currentTerrain);
|
||||
if (typeChange.length !== 0) {
|
||||
if (typeChange.length > 0) {
|
||||
if (pokemon.summonData.addedType && typeChange.includes(pokemon.summonData.addedType)) {
|
||||
pokemon.summonData.addedType = null;
|
||||
}
|
||||
@ -6226,7 +6223,7 @@ class ForceSwitchOutHelper {
|
||||
* - If the Pokémon is still alive (hp > 0), and if so, it leaves the field and a new SwitchPhase is initiated.
|
||||
*/
|
||||
if (switchOutTarget.isPlayer()) {
|
||||
if (globalScene.getPlayerParty().filter(p => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
|
||||
if (globalScene.getPlayerParty().filter(p => p.isAllowedInBattle() && !p.isOnField()).length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -6247,7 +6244,7 @@ class ForceSwitchOutHelper {
|
||||
* If yes, the Pokémon leaves the field and a new SwitchSummonPhase is initiated.
|
||||
*/
|
||||
} else if (globalScene.currentBattle.battleType !== BattleType.WILD) {
|
||||
if (globalScene.getEnemyParty().filter(p => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
|
||||
if (globalScene.getEnemyParty().filter(p => p.isAllowedInBattle() && !p.isOnField()).length === 0) {
|
||||
return false;
|
||||
}
|
||||
if (switchOutTarget.hp > 0) {
|
||||
@ -6324,28 +6321,31 @@ class ForceSwitchOutHelper {
|
||||
return !blockedByAbility.value;
|
||||
}
|
||||
|
||||
if (!player && globalScene.currentBattle.battleType === BattleType.WILD) {
|
||||
if (!globalScene.currentBattle.waveIndex && globalScene.currentBattle.waveIndex % 10 === 0) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
!player
|
||||
&& globalScene.currentBattle.battleType === BattleType.WILD
|
||||
&& !globalScene.currentBattle.waveIndex
|
||||
&& globalScene.currentBattle.waveIndex % 10 === 0
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
!player &&
|
||||
globalScene.currentBattle.isBattleMysteryEncounter() &&
|
||||
!globalScene.currentBattle.mysteryEncounter?.fleeAllowed
|
||||
!player
|
||||
&& globalScene.currentBattle.isBattleMysteryEncounter()
|
||||
&& !globalScene.currentBattle.mysteryEncounter?.fleeAllowed
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const party = player ? globalScene.getPlayerParty() : globalScene.getEnemyParty();
|
||||
return (
|
||||
(!player && globalScene.currentBattle.battleType === BattleType.WILD) ||
|
||||
party.filter(
|
||||
(!player && globalScene.currentBattle.battleType === BattleType.WILD)
|
||||
|| party.filter(
|
||||
p =>
|
||||
p.isAllowedInBattle() &&
|
||||
!p.isOnField() &&
|
||||
(player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot),
|
||||
p.isAllowedInBattle()
|
||||
&& !p.isOnField()
|
||||
&& (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot),
|
||||
).length > 0
|
||||
);
|
||||
}
|
||||
@ -6421,26 +6421,22 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr {
|
||||
|
||||
// TODO: Refactor to use more early returns
|
||||
public override canApply({ pokemon, source, damage }: PostDamageAbAttrParams): boolean {
|
||||
const moveHistory = pokemon.getMoveHistory();
|
||||
// Will not activate when the Pokémon's HP is lowered by cutting its own HP
|
||||
const forbiddenAttackingMoves = [MoveId.BELLY_DRUM, MoveId.SUBSTITUTE, MoveId.CURSE, MoveId.PAIN_SPLIT];
|
||||
if (moveHistory.length > 0) {
|
||||
const lastMoveUsed = moveHistory[moveHistory.length - 1];
|
||||
if (forbiddenAttackingMoves.includes(lastMoveUsed.move)) {
|
||||
return false;
|
||||
}
|
||||
const lastMoveUsed = pokemon.getLastXMoves()[0];
|
||||
if (forbiddenAttackingMoves.includes(lastMoveUsed?.move)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dragon Tail and Circle Throw switch out Pokémon before the Ability activates.
|
||||
const forbiddenDefendingMoves = [MoveId.DRAGON_TAIL, MoveId.CIRCLE_THROW];
|
||||
if (source) {
|
||||
const enemyMoveHistory = source.getMoveHistory();
|
||||
if (enemyMoveHistory.length > 0) {
|
||||
const enemyLastMoveUsed = enemyMoveHistory[enemyMoveHistory.length - 1];
|
||||
const enemyLastMoveUsed = source.getLastXMoves()[0];
|
||||
if (enemyLastMoveUsed) {
|
||||
// Will not activate if the Pokémon's HP falls below half while it is in the air during Sky Drop.
|
||||
if (
|
||||
forbiddenDefendingMoves.includes(enemyLastMoveUsed.move) ||
|
||||
(enemyLastMoveUsed.move === MoveId.SKY_DROP && enemyLastMoveUsed.result === MoveResult.OTHER)
|
||||
forbiddenDefendingMoves.includes(enemyLastMoveUsed.move)
|
||||
|| (enemyLastMoveUsed.move === MoveId.SKY_DROP && enemyLastMoveUsed.result === MoveResult.OTHER)
|
||||
) {
|
||||
return false;
|
||||
// Will not activate if the Pokémon's HP falls below half by a move affected by Sheer Force.
|
||||
@ -6729,8 +6725,8 @@ function getPokemonWithWeatherBasedForms() {
|
||||
.getField(true)
|
||||
.filter(
|
||||
p =>
|
||||
(p.hasAbility(AbilityId.FORECAST) && p.species.speciesId === SpeciesId.CASTFORM) ||
|
||||
(p.hasAbility(AbilityId.FLOWER_GIFT) && p.species.speciesId === SpeciesId.CHERRIM),
|
||||
(p.hasAbility(AbilityId.FORECAST) && p.species.speciesId === SpeciesId.CASTFORM)
|
||||
|| (p.hasAbility(AbilityId.FLOWER_GIFT) && p.species.speciesId === SpeciesId.CHERRIM),
|
||||
);
|
||||
}
|
||||
|
||||
@ -7429,7 +7425,7 @@ export function initAbilities() {
|
||||
.conditionalAttr(pokemon => pokemon.formIndex === 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.DISGUISE, 0, false)
|
||||
.attr(FormBlockDamageAbAttr,
|
||||
(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: abilityName }),
|
||||
(pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }),
|
||||
(pokemon) => toDmgValue(pokemon.getMaxHp() / 8))
|
||||
.attr(PostBattleInitFormChangeAbAttr, () => 0)
|
||||
.attr(PostFaintFormChangeAbAttr, () => 0)
|
||||
@ -7602,7 +7598,7 @@ export function initAbilities() {
|
||||
.attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.ICE_FACE, 0, WeatherType.HAIL, WeatherType.SNOW)
|
||||
.attr(FormBlockDamageAbAttr,
|
||||
(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: abilityName }))
|
||||
(pokemon, abilityName) => i18next.t("abilityTriggers:iceFaceAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }))
|
||||
.attr(PostBattleInitFormChangeAbAttr, () => 0)
|
||||
.uncopiable()
|
||||
.unreplaceable()
|
||||
|
@ -572,10 +572,10 @@ class MatBlockTag extends ConditionalProtectTag {
|
||||
const CraftyShieldConditionFunc: ProtectConditionFunc = (_arena, moveId) => {
|
||||
const move = allMoves[moveId];
|
||||
return (
|
||||
move.category === MoveCategory.STATUS &&
|
||||
move.moveTarget !== MoveTarget.ENEMY_SIDE &&
|
||||
move.moveTarget !== MoveTarget.BOTH_SIDES &&
|
||||
move.moveTarget !== MoveTarget.ALL
|
||||
move.category === MoveCategory.STATUS
|
||||
&& move.moveTarget !== MoveTarget.ENEMY_SIDE
|
||||
&& move.moveTarget !== MoveTarget.BOTH_SIDES
|
||||
&& move.moveTarget !== MoveTarget.ALL
|
||||
);
|
||||
};
|
||||
|
||||
@ -765,9 +765,10 @@ export abstract class EntryHazardTag extends SerializableArenaTag {
|
||||
const source = this.getSourcePokemon();
|
||||
if (!source) {
|
||||
console.warn(
|
||||
"Failed to get source Pokemon for AernaTrapTag on add message!" +
|
||||
`\nTag type: ${this.tagType}` +
|
||||
`\nPID: ${this.sourceId}`,
|
||||
// biome-ignore lint/complexity/noUselessStringConcat: Rule bugs out with operator linebreaks set to `before`
|
||||
"Failed to get source Pokemon for AernaTrapTag on add message!"
|
||||
+ `\nTag type: ${this.tagType}`
|
||||
+ `\nPID: ${this.sourceId}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -7649,7 +7649,7 @@ export function initBiomes() {
|
||||
? pokemonEvolutions[speciesId]
|
||||
: [];
|
||||
|
||||
if (!biomeEntries.filter(b => b[0] !== BiomeId.END).length && !speciesEvolutions.filter(es => !!((pokemonBiomes.find(p => p[0] === es.speciesId)!)[3] as any[]).filter(b => b[0] !== BiomeId.END).length).length) { // TODO: is the bang on the `find()` correct?
|
||||
if (biomeEntries.filter(b => b[0] !== BiomeId.END).length === 0&& speciesEvolutions.filter(es => ((pokemonBiomes.find(p => p[0] === es.speciesId)!)[3] as any[]).filter(b => b[0] !== BiomeId.END).length > 0).length === 0) { // TODO: is the bang on the `find()` correct?
|
||||
uncatchableSpecies.push(speciesId);
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ export enum EvolutionItem {
|
||||
}
|
||||
|
||||
const tyrogueMoves = [MoveId.LOW_SWEEP, MoveId.MACH_PUNCH, MoveId.RAPID_SPIN] as const;
|
||||
type TyrogueMove = typeof tyrogueMoves[number];
|
||||
type TyrogueMove = (typeof tyrogueMoves)[number];
|
||||
|
||||
/**
|
||||
* Pokemon Evolution tuple type consisting of:
|
||||
@ -136,7 +136,7 @@ export class SpeciesEvolutionCondition {
|
||||
case EvoCondKey.FRIENDSHIP:
|
||||
return i18next.t("pokemonEvolutions:friendship");
|
||||
case EvoCondKey.TIME:
|
||||
return i18next.t(`pokemonEvolutions:timeOfDay.${toCamelCase(TimeOfDay[cond.time[cond.time.length - 1]])}`); // For Day and Night evos, the key we want goes last
|
||||
return i18next.t(`pokemonEvolutions:timeOfDay.${toCamelCase(TimeOfDay[cond.time.at(-1)!])}`); // For Day and Night evos, the key we want goes last
|
||||
case EvoCondKey.MOVE_TYPE:
|
||||
return i18next.t("pokemonEvolutions:moveType", {type: i18next.t(`pokemonInfo:type.${toCamelCase(PokemonType[cond.pkmnType])}`)});
|
||||
case EvoCondKey.PARTY_TYPE:
|
||||
@ -193,7 +193,10 @@ export class SpeciesEvolutionCondition {
|
||||
case EvoCondKey.WEATHER:
|
||||
return cond.weather.includes(globalScene.arena.getWeatherType());
|
||||
case EvoCondKey.TYROGUE:
|
||||
return pokemon.getMoveset(true).find(m => (tyrogueMoves as readonly MoveId[]) .includes(m.moveId))?.moveId === cond.move;
|
||||
return (
|
||||
pokemon.getMoveset(true).find(m => (tyrogueMoves as readonly MoveId[]).includes(m.moveId))?.moveId
|
||||
=== cond.move
|
||||
);
|
||||
case EvoCondKey.NATURE:
|
||||
return cond.nature.includes(pokemon.getNature());
|
||||
case EvoCondKey.RANDOM_FORM: {
|
||||
@ -656,18 +659,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(SpeciesId.GARDEVOIR, 30, null, null),
|
||||
new SpeciesEvolution(SpeciesId.GALLADE, 1, EvolutionItem.DAWN_STONE, {key: EvoCondKey.GENDER, gender: Gender.MALE}, SpeciesWildEvolutionDelay.LONG),
|
||||
],
|
||||
[SpeciesId.SURSKIT]: [
|
||||
new SpeciesEvolution(SpeciesId.MASQUERAIN, 22, null, null)
|
||||
],
|
||||
[SpeciesId.SHROOMISH]: [
|
||||
new SpeciesEvolution(SpeciesId.BRELOOM, 23, null, null)
|
||||
],
|
||||
[SpeciesId.SLAKOTH]: [
|
||||
new SpeciesEvolution(SpeciesId.VIGOROTH, 18, null, null)
|
||||
],
|
||||
[SpeciesId.VIGOROTH]: [
|
||||
new SpeciesEvolution(SpeciesId.SLAKING, 36, null, null)
|
||||
],
|
||||
[SpeciesId.SURSKIT]: [new SpeciesEvolution(SpeciesId.MASQUERAIN, 22, null, null)],
|
||||
[SpeciesId.SHROOMISH]: [new SpeciesEvolution(SpeciesId.BRELOOM, 23, null, null)],
|
||||
[SpeciesId.SLAKOTH]: [new SpeciesEvolution(SpeciesId.VIGOROTH, 18, null, null)],
|
||||
[SpeciesId.VIGOROTH]: [new SpeciesEvolution(SpeciesId.SLAKING, 36, null, null)],
|
||||
[SpeciesId.NINCADA]: [
|
||||
new SpeciesEvolution(SpeciesId.NINJASK, 20, null, null),
|
||||
new SpeciesEvolution(SpeciesId.SHEDINJA, 20, null, {key: EvoCondKey.SHEDINJA})
|
||||
@ -745,66 +740,26 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(SpeciesId.GLALIE, 42, null, null),
|
||||
new SpeciesEvolution(SpeciesId.FROSLASS, 1, EvolutionItem.DAWN_STONE, {key: EvoCondKey.GENDER, gender: Gender.FEMALE}, SpeciesWildEvolutionDelay.LONG),
|
||||
],
|
||||
[SpeciesId.SPHEAL]: [
|
||||
new SpeciesEvolution(SpeciesId.SEALEO, 32, null, null)
|
||||
],
|
||||
[SpeciesId.SEALEO]: [
|
||||
new SpeciesEvolution(SpeciesId.WALREIN, 44, null, null)
|
||||
],
|
||||
[SpeciesId.BAGON]: [
|
||||
new SpeciesEvolution(SpeciesId.SHELGON, 30, null, null)
|
||||
],
|
||||
[SpeciesId.SHELGON]: [
|
||||
new SpeciesEvolution(SpeciesId.SALAMENCE, 50, null, null)
|
||||
],
|
||||
[SpeciesId.BELDUM]: [
|
||||
new SpeciesEvolution(SpeciesId.METANG, 20, null, null)
|
||||
],
|
||||
[SpeciesId.METANG]: [
|
||||
new SpeciesEvolution(SpeciesId.METAGROSS, 45, null, null)
|
||||
],
|
||||
[SpeciesId.TURTWIG]: [
|
||||
new SpeciesEvolution(SpeciesId.GROTLE, 18, null, null)
|
||||
],
|
||||
[SpeciesId.GROTLE]: [
|
||||
new SpeciesEvolution(SpeciesId.TORTERRA, 32, null, null)
|
||||
],
|
||||
[SpeciesId.CHIMCHAR]: [
|
||||
new SpeciesEvolution(SpeciesId.MONFERNO, 14, null, null)
|
||||
],
|
||||
[SpeciesId.MONFERNO]: [
|
||||
new SpeciesEvolution(SpeciesId.INFERNAPE, 36, null, null)
|
||||
],
|
||||
[SpeciesId.PIPLUP]: [
|
||||
new SpeciesEvolution(SpeciesId.PRINPLUP, 16, null, null)
|
||||
],
|
||||
[SpeciesId.PRINPLUP]: [
|
||||
new SpeciesEvolution(SpeciesId.EMPOLEON, 36, null, null)
|
||||
],
|
||||
[SpeciesId.STARLY]: [
|
||||
new SpeciesEvolution(SpeciesId.STARAVIA, 14, null, null)
|
||||
],
|
||||
[SpeciesId.STARAVIA]: [
|
||||
new SpeciesEvolution(SpeciesId.STARAPTOR, 34, null, null)
|
||||
],
|
||||
[SpeciesId.BIDOOF]: [
|
||||
new SpeciesEvolution(SpeciesId.BIBAREL, 15, null, null)
|
||||
],
|
||||
[SpeciesId.KRICKETOT]: [
|
||||
new SpeciesEvolution(SpeciesId.KRICKETUNE, 10, null, null)
|
||||
],
|
||||
[SpeciesId.SHINX]: [
|
||||
new SpeciesEvolution(SpeciesId.LUXIO, 15, null, null)
|
||||
],
|
||||
[SpeciesId.LUXIO]: [
|
||||
new SpeciesEvolution(SpeciesId.LUXRAY, 30, null, null)
|
||||
],
|
||||
[SpeciesId.CRANIDOS]: [
|
||||
new SpeciesEvolution(SpeciesId.RAMPARDOS, 30, null, null)
|
||||
],
|
||||
[SpeciesId.SHIELDON]: [
|
||||
new SpeciesEvolution(SpeciesId.BASTIODON, 30, null, null)
|
||||
],
|
||||
[SpeciesId.SPHEAL]: [new SpeciesEvolution(SpeciesId.SEALEO, 32, null, null)],
|
||||
[SpeciesId.SEALEO]: [new SpeciesEvolution(SpeciesId.WALREIN, 44, null, null)],
|
||||
[SpeciesId.BAGON]: [new SpeciesEvolution(SpeciesId.SHELGON, 30, null, null)],
|
||||
[SpeciesId.SHELGON]: [new SpeciesEvolution(SpeciesId.SALAMENCE, 50, null, null)],
|
||||
[SpeciesId.BELDUM]: [new SpeciesEvolution(SpeciesId.METANG, 20, null, null)],
|
||||
[SpeciesId.METANG]: [new SpeciesEvolution(SpeciesId.METAGROSS, 45, null, null)],
|
||||
[SpeciesId.TURTWIG]: [new SpeciesEvolution(SpeciesId.GROTLE, 18, null, null)],
|
||||
[SpeciesId.GROTLE]: [new SpeciesEvolution(SpeciesId.TORTERRA, 32, null, null)],
|
||||
[SpeciesId.CHIMCHAR]: [new SpeciesEvolution(SpeciesId.MONFERNO, 14, null, null)],
|
||||
[SpeciesId.MONFERNO]: [new SpeciesEvolution(SpeciesId.INFERNAPE, 36, null, null)],
|
||||
[SpeciesId.PIPLUP]: [new SpeciesEvolution(SpeciesId.PRINPLUP, 16, null, null)],
|
||||
[SpeciesId.PRINPLUP]: [new SpeciesEvolution(SpeciesId.EMPOLEON, 36, null, null)],
|
||||
[SpeciesId.STARLY]: [new SpeciesEvolution(SpeciesId.STARAVIA, 14, null, null)],
|
||||
[SpeciesId.STARAVIA]: [new SpeciesEvolution(SpeciesId.STARAPTOR, 34, null, null)],
|
||||
[SpeciesId.BIDOOF]: [new SpeciesEvolution(SpeciesId.BIBAREL, 15, null, null)],
|
||||
[SpeciesId.KRICKETOT]: [new SpeciesEvolution(SpeciesId.KRICKETUNE, 10, null, null)],
|
||||
[SpeciesId.SHINX]: [new SpeciesEvolution(SpeciesId.LUXIO, 15, null, null)],
|
||||
[SpeciesId.LUXIO]: [new SpeciesEvolution(SpeciesId.LUXRAY, 30, null, null)],
|
||||
[SpeciesId.CRANIDOS]: [new SpeciesEvolution(SpeciesId.RAMPARDOS, 30, null, null)],
|
||||
[SpeciesId.SHIELDON]: [new SpeciesEvolution(SpeciesId.BASTIODON, 30, null, null)],
|
||||
[SpeciesId.BURMY]: [
|
||||
new SpeciesEvolution(SpeciesId.MOTHIM, 20, null, {key: EvoCondKey.GENDER, gender: Gender.MALE}),
|
||||
new SpeciesEvolution(SpeciesId.WORMADAM, 20, null, {key: EvoCondKey.GENDER, gender: Gender.FEMALE})
|
||||
@ -1870,7 +1825,7 @@ export const pokemonPrevolutions: PokemonPrevolutions = {};
|
||||
|
||||
export function initPokemonPrevolutions(): void {
|
||||
// TODO: Why do we have empty strings in our array?
|
||||
const megaFormKeys = [ SpeciesFormKey.MEGA, "", SpeciesFormKey.MEGA_X, "", SpeciesFormKey.MEGA_Y ];
|
||||
const megaFormKeys = [SpeciesFormKey.MEGA, "", SpeciesFormKey.MEGA_X, "", SpeciesFormKey.MEGA_Y];
|
||||
for (const [pk, evolutions] of Object.entries(pokemonEvolutions)) {
|
||||
for (const ev of evolutions) {
|
||||
if (ev.evoFormKey && megaFormKeys.indexOf(ev.evoFormKey) > -1) {
|
||||
|
@ -139,8 +139,8 @@ class AnimFrame {
|
||||
focus: AnimFocus,
|
||||
init?: boolean,
|
||||
) {
|
||||
this.x = !init ? ((x || 0) - 128) * 0.5 : x;
|
||||
this.y = !init ? ((y || 0) - 224) * 0.5 : y;
|
||||
this.x = init ? x : ((x || 0) - 128) * 0.5;
|
||||
this.y = init ? y : ((y || 0) - 224) * 0.5;
|
||||
if (zoomX) {
|
||||
this.zoomX = zoomX;
|
||||
} else if (init) {
|
||||
@ -360,7 +360,7 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent {
|
||||
if (this.opacity !== undefined) {
|
||||
tweenProps["alpha"] = (this.opacity || 0) / 255;
|
||||
}
|
||||
if (Object.keys(tweenProps).length) {
|
||||
if (Object.keys(tweenProps).length > 0) {
|
||||
globalScene.tweens.add({
|
||||
targets: moveAnim.bgSprite,
|
||||
duration: getFrameMs(this.duration * 3),
|
||||
@ -625,7 +625,7 @@ function loadAnimAssets(anims: AnimConfig[], startLoad?: boolean): Promise<void>
|
||||
const backgrounds = new Set<string>();
|
||||
const sounds = new Set<string>();
|
||||
for (const a of anims) {
|
||||
if (!a.frames?.length) {
|
||||
if (a.frames?.length === 0) {
|
||||
continue;
|
||||
}
|
||||
const animSounds = a.getSoundResourceNames();
|
||||
@ -816,8 +816,8 @@ export abstract class BattleAnim {
|
||||
x = point[0];
|
||||
y = point[1];
|
||||
if (
|
||||
frame.target === AnimFrameTarget.GRAPHIC &&
|
||||
isReversed(this.srcLine[0], this.srcLine[2], this.dstLine[0], this.dstLine[2])
|
||||
frame.target === AnimFrameTarget.GRAPHIC
|
||||
&& isReversed(this.srcLine[0], this.srcLine[2], this.dstLine[0], this.dstLine[2])
|
||||
) {
|
||||
scaleX = scaleX * -1;
|
||||
}
|
||||
@ -826,7 +826,7 @@ export abstract class BattleAnim {
|
||||
}
|
||||
const angle = -frame.angle;
|
||||
const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++;
|
||||
ret.get(frame.target)!.set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle }); // TODO: is the bang correct?
|
||||
ret.get(frame.target)!.set(key, { x, y, scaleX, scaleY, angle }); // TODO: is the bang correct?
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -835,8 +835,8 @@ export abstract class BattleAnim {
|
||||
// biome-ignore lint/complexity/noBannedTypes: callback is used liberally
|
||||
play(onSubstitute?: boolean, callback?: Function) {
|
||||
const isOppAnim = this.isOppAnim();
|
||||
const user = !isOppAnim ? this.user! : this.target!; // TODO: These bangs are LITERALLY not correct at all
|
||||
const target = !isOppAnim ? this.target! : this.user!;
|
||||
const user = isOppAnim ? this.target! : this.user!;
|
||||
const target = isOppAnim ? this.user! : this.target!; // TODO: These bangs are LITERALLY not correct at all
|
||||
|
||||
if (!target?.isOnField() && !this.playRegardlessOfIssues) {
|
||||
if (callback) {
|
||||
@ -888,8 +888,8 @@ export abstract class BattleAnim {
|
||||
* and `this.target` prevent the target's Substitute doll from disappearing
|
||||
* after being the target of an animation.
|
||||
*/
|
||||
const userSpriteToShow = !isOppAnim ? userSprite : targetSprite;
|
||||
const targetSpriteToShow = !isOppAnim ? targetSprite : userSprite;
|
||||
const userSpriteToShow = isOppAnim ? targetSprite : userSprite;
|
||||
const targetSpriteToShow = isOppAnim ? userSprite : targetSprite;
|
||||
if (!this.isHideUser() && userSpriteToShow) {
|
||||
userSpriteToShow.setVisible(true);
|
||||
}
|
||||
@ -1142,18 +1142,18 @@ export abstract class BattleAnim {
|
||||
|
||||
for (const frame of frames) {
|
||||
let { x, y } = frame;
|
||||
const scaleX = (frame.zoomX / 100) * (!frame.mirror ? 1 : -1);
|
||||
const scaleX = (frame.zoomX / 100) * (frame.mirror ? -1 : 1);
|
||||
const scaleY = frame.zoomY / 100;
|
||||
x += targetInitialX;
|
||||
y += targetInitialY;
|
||||
const angle = -frame.angle;
|
||||
const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++;
|
||||
ret.get(frame.target)?.set(key, {
|
||||
x: x,
|
||||
y: y,
|
||||
scaleX: scaleX,
|
||||
scaleY: scaleY,
|
||||
angle: angle,
|
||||
x,
|
||||
y,
|
||||
scaleX,
|
||||
scaleY,
|
||||
angle,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1135,8 +1135,8 @@ export class PowderTag extends BattlerTag {
|
||||
const move = movePhase.move.getMove();
|
||||
const weather = globalScene.arena.weather;
|
||||
if (
|
||||
pokemon.getMoveType(move) !== PokemonType.FIRE ||
|
||||
(weather?.weatherType === WeatherType.HEAVY_RAIN && !weather.isEffectSuppressed()) // Heavy rain takes priority over powder
|
||||
pokemon.getMoveType(move) !== PokemonType.FIRE
|
||||
|| (weather?.weatherType === WeatherType.HEAVY_RAIN && !weather.isEffectSuppressed()) // Heavy rain takes priority over powder
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@ -1793,9 +1793,9 @@ export abstract class ContactProtectedTag extends ProtectedTag {
|
||||
|
||||
const moveData = getMoveEffectPhaseData(pokemon);
|
||||
if (
|
||||
lapseType === BattlerTagLapseType.CUSTOM &&
|
||||
moveData &&
|
||||
moveData.move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: moveData.attacker, target: pokemon })
|
||||
lapseType === BattlerTagLapseType.CUSTOM
|
||||
&& moveData
|
||||
&& moveData.move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: moveData.attacker, target: pokemon })
|
||||
) {
|
||||
this.onContact(moveData.attacker, pokemon);
|
||||
}
|
||||
@ -2538,12 +2538,10 @@ export class RoostedTag extends BattlerTag {
|
||||
let modifiedTypes: PokemonType[];
|
||||
if (this.isBasePureFlying && !isCurrentlyDualType) {
|
||||
modifiedTypes = [PokemonType.NORMAL];
|
||||
} else if (!!pokemon.getTag(RemovedTypeTag) && isOriginallyDualType && !isCurrentlyDualType) {
|
||||
modifiedTypes = [PokemonType.UNKNOWN];
|
||||
} else {
|
||||
if (!!pokemon.getTag(RemovedTypeTag) && isOriginallyDualType && !isCurrentlyDualType) {
|
||||
modifiedTypes = [PokemonType.UNKNOWN];
|
||||
} else {
|
||||
modifiedTypes = currentTypes.filter(type => type !== PokemonType.FLYING);
|
||||
}
|
||||
modifiedTypes = currentTypes.filter(type => type !== PokemonType.FLYING);
|
||||
}
|
||||
pokemon.summonData.types = modifiedTypes;
|
||||
pokemon.updateInfo();
|
||||
@ -2819,9 +2817,9 @@ export class GulpMissileTag extends SerializableBattlerTag {
|
||||
// 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 isNormalForm =
|
||||
pokemon.formIndex === 0 &&
|
||||
!pokemon.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA) &&
|
||||
!pokemon.getTag(BattlerTagType.GULP_MISSILE_PIKACHU);
|
||||
pokemon.formIndex === 0
|
||||
&& !pokemon.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)
|
||||
&& !pokemon.getTag(BattlerTagType.GULP_MISSILE_PIKACHU);
|
||||
const isCramorant = pokemon.species.speciesId === SpeciesId.CRAMORANT;
|
||||
|
||||
return isSurfOrDive && isNormalForm && isCramorant;
|
||||
@ -3862,7 +3860,7 @@ function getMoveEffectPhaseData(_pokemon: Pokemon): { phase: MoveEffectPhase; at
|
||||
const phase = globalScene.phaseManager.getCurrentPhase();
|
||||
if (phase?.is("MoveEffectPhase")) {
|
||||
return {
|
||||
phase: phase,
|
||||
phase,
|
||||
attacker: phase.getPokemon(),
|
||||
move: phase.move,
|
||||
};
|
||||
|
@ -28,7 +28,7 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
|
||||
return (pokemon: Pokemon) => !!pokemon.status || !!pokemon.getTag(BattlerTagType.CONFUSED);
|
||||
case BerryType.ENIGMA:
|
||||
return (pokemon: Pokemon) =>
|
||||
!!pokemon.turnData.attacksReceived.filter(a => a.result === HitResult.SUPER_EFFECTIVE).length;
|
||||
pokemon.turnData.attacksReceived.filter(a => a.result === HitResult.SUPER_EFFECTIVE).length > 0;
|
||||
case BerryType.LIECHI:
|
||||
case BerryType.GANLON:
|
||||
case BerryType.PETAYA:
|
||||
@ -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
|
||||
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) {
|
||||
ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0);
|
||||
globalScene.phaseManager.queueMessage(
|
||||
|
@ -467,8 +467,8 @@ export class SingleGenerationChallenge extends Challenge {
|
||||
const baseGeneration = getPokemonSpecies(pokemon.species.speciesId).generation;
|
||||
const fusionGeneration = pokemon.isFusion() ? getPokemonSpecies(pokemon.fusionSpecies!.speciesId).generation : 0;
|
||||
if (
|
||||
pokemon.isPlayer() &&
|
||||
(baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value))
|
||||
pokemon.isPlayer()
|
||||
&& (baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value))
|
||||
) {
|
||||
valid.value = false;
|
||||
return true;
|
||||
@ -741,12 +741,12 @@ export class SingleTypeChallenge extends Challenge {
|
||||
|
||||
applyPokemonInBattle(pokemon: Pokemon, valid: BooleanHolder): boolean {
|
||||
if (
|
||||
pokemon.isPlayer() &&
|
||||
!pokemon.isOfType(this.value - 1, false, false, true) &&
|
||||
!SingleTypeChallenge.TYPE_OVERRIDES.some(
|
||||
pokemon.isPlayer()
|
||||
&& !pokemon.isOfType(this.value - 1, false, false, true)
|
||||
&& !SingleTypeChallenge.TYPE_OVERRIDES.some(
|
||||
o =>
|
||||
o.type === this.value - 1 &&
|
||||
(pokemon.isFusion() && o.fusion ? pokemon.fusionSpecies! : pokemon.species).speciesId === o.species,
|
||||
o.type === this.value - 1
|
||||
&& (pokemon.isFusion() && o.fusion ? pokemon.fusionSpecies! : pokemon.species).speciesId === o.species,
|
||||
)
|
||||
) {
|
||||
// TODO: is the bang on fusionSpecies correct?
|
||||
@ -823,11 +823,11 @@ export class FreshStartChallenge extends Challenge {
|
||||
|
||||
// Remove natures except for the default ones
|
||||
const neutralNaturesAttr =
|
||||
(1 << (Nature.HARDY + 1)) |
|
||||
(1 << (Nature.DOCILE + 1)) |
|
||||
(1 << (Nature.SERIOUS + 1)) |
|
||||
(1 << (Nature.BASHFUL + 1)) |
|
||||
(1 << (Nature.QUIRKY + 1));
|
||||
(1 << (Nature.HARDY + 1))
|
||||
| (1 << (Nature.DOCILE + 1))
|
||||
| (1 << (Nature.SERIOUS + 1))
|
||||
| (1 << (Nature.BASHFUL + 1))
|
||||
| (1 << (Nature.QUIRKY + 1));
|
||||
dexEntry.natureAttr &= neutralNaturesAttr;
|
||||
|
||||
// Cap all ivs at 15
|
||||
@ -882,8 +882,8 @@ export class FreshStartChallenge extends Challenge {
|
||||
if (pokemon.species.speciesId === SpeciesId.ZYGARDE && pokemon.formIndex >= 2) {
|
||||
pokemon.formIndex -= 2; // Sets 10%-PC to 10%-AB and 50%-PC to 50%-AB
|
||||
} else if (
|
||||
pokemon.formIndex > 0 &&
|
||||
[
|
||||
pokemon.formIndex > 0
|
||||
&& [
|
||||
SpeciesId.PIKACHU,
|
||||
SpeciesId.EEVEE,
|
||||
SpeciesId.PICHU,
|
||||
|
@ -42,10 +42,9 @@ export function getDailyRunStarters(seed: string): Starter[] {
|
||||
starterCosts.push(randSeedInt(9 - starterCosts[0], 1));
|
||||
starterCosts.push(10 - (starterCosts[0] + starterCosts[1]));
|
||||
|
||||
for (let c = 0; c < starterCosts.length; c++) {
|
||||
const cost = starterCosts[c];
|
||||
for (const cost of starterCosts) {
|
||||
const costSpecies = Object.keys(speciesStarterCosts)
|
||||
.map(s => Number.parseInt(s) as SpeciesId)
|
||||
.map(s => Number.parseInt(s) as SpeciesId) // TODO: Remove
|
||||
.filter(s => speciesStarterCosts[s] === cost);
|
||||
const randPkmSpecies = getPokemonSpecies(randSeedItem(costSpecies));
|
||||
const starterSpecies = getPokemonSpecies(
|
||||
|
@ -220,9 +220,9 @@ export class Egg {
|
||||
|
||||
public isManaphyEgg(): boolean {
|
||||
return (
|
||||
this._species === SpeciesId.PHIONE ||
|
||||
this._species === SpeciesId.MANAPHY ||
|
||||
(this._tier === EggTier.COMMON && !(this._id % 204) && !this._species)
|
||||
this._species === SpeciesId.PHIONE
|
||||
|| this._species === SpeciesId.MANAPHY
|
||||
|| (this._tier === EggTier.COMMON && !(this._id % 204) && !this._species)
|
||||
);
|
||||
}
|
||||
|
||||
@ -324,15 +324,15 @@ export class Egg {
|
||||
switch (this.sourceType) {
|
||||
case EggSourceType.SAME_SPECIES_EGG:
|
||||
return (
|
||||
this._eggDescriptor ??
|
||||
i18next.t("egg:sameSpeciesEgg", {
|
||||
this._eggDescriptor
|
||||
?? i18next.t("egg:sameSpeciesEgg", {
|
||||
species: getPokemonSpecies(this._species).getName(),
|
||||
})
|
||||
);
|
||||
case EggSourceType.GACHA_LEGENDARY:
|
||||
return (
|
||||
this._eggDescriptor ??
|
||||
`${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(this.timestamp)).getName()})`
|
||||
this._eggDescriptor
|
||||
?? `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(this.timestamp)).getName()})`
|
||||
);
|
||||
case EggSourceType.GACHA_SHINY:
|
||||
return this._eggDescriptor ?? i18next.t("egg:gachaTypeShiny");
|
||||
@ -419,10 +419,8 @@ export class Egg {
|
||||
const rand = randSeedInt(MANAPHY_EGG_MANAPHY_RATE) !== 1;
|
||||
return rand ? SpeciesId.PHIONE : SpeciesId.MANAPHY;
|
||||
}
|
||||
if (this.tier === EggTier.LEGENDARY && this._sourceType === EggSourceType.GACHA_LEGENDARY) {
|
||||
if (!randSeedInt(2)) {
|
||||
return getLegendaryGachaSpeciesForTimestamp(this.timestamp);
|
||||
}
|
||||
if (this.tier === EggTier.LEGENDARY && this._sourceType === EggSourceType.GACHA_LEGENDARY && !randSeedInt(2)) {
|
||||
return getLegendaryGachaSpeciesForTimestamp(this.timestamp);
|
||||
}
|
||||
|
||||
let minStarterValue: number;
|
||||
@ -454,9 +452,9 @@ export class Egg {
|
||||
.map(s => Number.parseInt(s) as SpeciesId)
|
||||
.filter(
|
||||
s =>
|
||||
!pokemonPrevolutions.hasOwnProperty(s) &&
|
||||
getPokemonSpecies(s).isObtainable() &&
|
||||
ignoredSpecies.indexOf(s) === -1,
|
||||
!pokemonPrevolutions.hasOwnProperty(s)
|
||||
&& getPokemonSpecies(s).isObtainable()
|
||||
&& ignoredSpecies.indexOf(s) === -1,
|
||||
);
|
||||
|
||||
// If this is the 10th egg without unlocking something new, attempt to force it.
|
||||
@ -464,7 +462,7 @@ export class Egg {
|
||||
const lockedPool = speciesPool.filter(
|
||||
s => !globalScene.gameData.dexData[s].caughtAttr && !globalScene.gameData.eggs.some(e => e.species === s),
|
||||
);
|
||||
if (lockedPool.length) {
|
||||
if (lockedPool.length > 0) {
|
||||
// Skip this if everything is unlocked
|
||||
speciesPool = lockedPool;
|
||||
}
|
||||
@ -510,8 +508,8 @@ export class Egg {
|
||||
species = species!; // tell TS compiled it's defined now!
|
||||
|
||||
if (
|
||||
globalScene.gameData.dexData[species].caughtAttr ||
|
||||
globalScene.gameData.eggs.some(e => e.species === species)
|
||||
globalScene.gameData.dexData[species].caughtAttr
|
||||
|| globalScene.gameData.eggs.some(e => e.species === species)
|
||||
) {
|
||||
globalScene.gameData.unlockPity[this.tier] = Math.min(globalScene.gameData.unlockPity[this.tier] + 1, 10);
|
||||
} else {
|
||||
@ -567,8 +565,8 @@ export class Egg {
|
||||
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.
|
||||
if (
|
||||
globalScene.gameData.eggPity[EggTier.LEGENDARY] >= EGG_PITY_LEGENDARY_THRESHOLD &&
|
||||
this._tier === EggTier.COMMON
|
||||
globalScene.gameData.eggPity[EggTier.LEGENDARY] >= EGG_PITY_LEGENDARY_THRESHOLD
|
||||
&& this._tier === EggTier.COMMON
|
||||
) {
|
||||
this._tier = EggTier.LEGENDARY;
|
||||
} else if (globalScene.gameData.eggPity[EggTier.EPIC] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) {
|
||||
|
@ -126,7 +126,7 @@ export function getMoveTargets(user: Pokemon, move: MoveId, replaceTarget?: Move
|
||||
}
|
||||
|
||||
export const frenzyMissFunc: UserMoveConditionFunc = (user: Pokemon, move: Move) => {
|
||||
while (user.getMoveQueue().length && user.getMoveQueue()[0].move === move.id) {
|
||||
while (user.getMoveQueue().length > 0 && user.getMoveQueue()[0].move === move.id) {
|
||||
user.getMoveQueue().shift();
|
||||
}
|
||||
user.removeTag(BattlerTagType.FRENZY); // FRENZY tag should be disrupted on miss/no effect
|
||||
|
@ -50,10 +50,9 @@ export class PokemonMove {
|
||||
const move = this.getMove();
|
||||
// TODO: Add Sky Drop's 1 turn stall
|
||||
const usability = new BooleanHolder(
|
||||
!move.name.endsWith(" (N)") &&
|
||||
(ignorePp || this.ppUsed < this.getMovePp() || move.pp === -1) &&
|
||||
// TODO: Review if the `MoveId.NONE` check is even necessary anymore
|
||||
!(this.moveId !== MoveId.NONE && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)),
|
||||
!move.name.endsWith(" (N)")
|
||||
&& (ignorePp || this.ppUsed < this.getMovePp() || move.pp === -1) // TODO: Review if the `MoveId.NONE` check is even necessary anymore
|
||||
&& !(this.moveId !== MoveId.NONE && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)),
|
||||
);
|
||||
if (pokemon.isPlayer()) {
|
||||
applyChallenges(ChallengeType.POKEMON_MOVE, move.id, usability);
|
||||
|
@ -111,7 +111,7 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
const trainerSpriteKey = trainerConfig.getSpriteKey();
|
||||
encounter.enemyPartyConfigs.push({
|
||||
levelAdditiveModifier: 1,
|
||||
trainerConfig: trainerConfig,
|
||||
trainerConfig,
|
||||
});
|
||||
|
||||
encounter.spriteConfigs = [
|
||||
|
@ -527,7 +527,8 @@ function doBerrySpritePile(isEat = false) {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
animationOrder.forEach((berry, i) => {
|
||||
const introVisualsIndex = encounter.spriteConfigs.findIndex(config => config.spriteKey?.includes(berry));
|
||||
let sprite: Phaser.GameObjects.Sprite, tintSprite: Phaser.GameObjects.Sprite;
|
||||
let sprite: Phaser.GameObjects.Sprite;
|
||||
let tintSprite: Phaser.GameObjects.Sprite;
|
||||
const sprites = encounter.introVisuals?.getSpriteAtIndex(introVisualsIndex);
|
||||
if (sprites) {
|
||||
sprite = sprites[0];
|
||||
|
@ -93,8 +93,8 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB
|
||||
|
||||
// Store pokemon and price
|
||||
encounter.misc = {
|
||||
pokemon: pokemon,
|
||||
price: price,
|
||||
pokemon,
|
||||
price,
|
||||
};
|
||||
|
||||
// If player meets the combo OR requirements for option 2, populate the token
|
||||
|
@ -71,7 +71,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
const config: EnemyPartyConfig = {
|
||||
pokemonConfigs: [
|
||||
{
|
||||
level: level,
|
||||
level,
|
||||
species: bossPokemon.species,
|
||||
dataSource: new PokemonData(bossPokemon),
|
||||
isBoss: true,
|
||||
@ -105,8 +105,8 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
hasShadow: true,
|
||||
},
|
||||
{
|
||||
spriteKey: spriteKey,
|
||||
fileRoot: fileRoot,
|
||||
spriteKey,
|
||||
fileRoot,
|
||||
hasShadow: true,
|
||||
tint: 0.25,
|
||||
x: -5,
|
||||
@ -320,9 +320,9 @@ function tryGiveBerry(prioritizedPokemon?: PlayerPokemon) {
|
||||
if (prioritizedPokemon) {
|
||||
const heldBerriesOfType = globalScene.findModifier(
|
||||
m =>
|
||||
m instanceof BerryModifier &&
|
||||
m.pokemonId === prioritizedPokemon.id &&
|
||||
(m as BerryModifier).berryType === berryType,
|
||||
m instanceof BerryModifier
|
||||
&& m.pokemonId === prioritizedPokemon.id
|
||||
&& (m as BerryModifier).berryType === berryType,
|
||||
true,
|
||||
) as BerryModifier;
|
||||
|
||||
|
@ -213,7 +213,8 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
female: true,
|
||||
});
|
||||
|
||||
let beedrillKeys: { spriteKey: string; fileRoot: string }, butterfreeKeys: { spriteKey: string; fileRoot: string };
|
||||
let beedrillKeys: { spriteKey: string; fileRoot: string };
|
||||
let butterfreeKeys: { spriteKey: string; fileRoot: string };
|
||||
if (globalScene.currentBattle.waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) {
|
||||
beedrillKeys = getSpriteKeysFromSpecies(SpeciesId.BEEDRILL, false);
|
||||
butterfreeKeys = getSpriteKeysFromSpecies(SpeciesId.BUTTERFREE, false);
|
||||
@ -247,7 +248,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
yShadow: -4,
|
||||
},
|
||||
{
|
||||
spriteKey: spriteKey,
|
||||
spriteKey,
|
||||
fileRoot: "trainer",
|
||||
hasShadow: true,
|
||||
x: 4,
|
||||
@ -440,11 +441,11 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
// Get Pokemon held items and filter for valid ones
|
||||
const validItems = pokemon.getHeldItems().filter(item => {
|
||||
return (
|
||||
(item instanceof BypassSpeedChanceModifier ||
|
||||
item instanceof ContactHeldItemTransferChanceModifier ||
|
||||
(item instanceof AttackTypeBoosterModifier &&
|
||||
(item.type as AttackTypeBoosterModifierType).moveType === PokemonType.BUG)) &&
|
||||
item.isTransferable
|
||||
(item instanceof BypassSpeedChanceModifier
|
||||
|| item instanceof ContactHeldItemTransferChanceModifier
|
||||
|| (item instanceof AttackTypeBoosterModifier
|
||||
&& (item.type as AttackTypeBoosterModifierType).moveType === PokemonType.BUG))
|
||||
&& item.isTransferable
|
||||
);
|
||||
});
|
||||
|
||||
@ -469,10 +470,10 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
// If pokemon has valid item, it can be selected
|
||||
const hasValidItem = pokemon.getHeldItems().some(item => {
|
||||
return (
|
||||
item instanceof BypassSpeedChanceModifier ||
|
||||
item instanceof ContactHeldItemTransferChanceModifier ||
|
||||
(item instanceof AttackTypeBoosterModifier &&
|
||||
(item.type as AttackTypeBoosterModifierType).moveType === PokemonType.BUG)
|
||||
item instanceof BypassSpeedChanceModifier
|
||||
|| item instanceof ContactHeldItemTransferChanceModifier
|
||||
|| (item instanceof AttackTypeBoosterModifier
|
||||
&& (item.type as AttackTypeBoosterModifierType).moveType === PokemonType.BUG)
|
||||
);
|
||||
});
|
||||
if (!hasValidItem) {
|
||||
|
@ -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
|
||||
species: getPokemonSpecies(SpeciesId.BLACEPHALON),
|
||||
customPokemonData: new CustomPokemonData({
|
||||
ability: ability,
|
||||
ability,
|
||||
types: [firstType, secondType],
|
||||
}),
|
||||
isBoss: true,
|
||||
|
@ -168,7 +168,7 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
const config: EnemyPartyConfig = {
|
||||
pokemonConfigs: [
|
||||
{
|
||||
species: species,
|
||||
species,
|
||||
dataSource: oricorioData,
|
||||
isBoss: true,
|
||||
// Gets +1 to all stats except SPD on battle start
|
||||
|
@ -249,6 +249,6 @@ function pokemonAndMoveChosen(pokemon: PlayerPokemon, move: PokemonMove, correct
|
||||
setEncounterExp([pokemon.id], 100);
|
||||
}
|
||||
encounter.misc = {
|
||||
correctMove: correctMove,
|
||||
correctMove,
|
||||
};
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
const config: EnemyPartyConfig = {
|
||||
pokemonConfigs: [
|
||||
{
|
||||
level: level,
|
||||
level,
|
||||
species: bossPokemon.species,
|
||||
dataSource: new PokemonData(bossPokemon),
|
||||
isBoss: true,
|
||||
@ -120,8 +120,8 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
disableAnimation: true,
|
||||
},
|
||||
{
|
||||
spriteKey: spriteKey,
|
||||
fileRoot: fileRoot,
|
||||
spriteKey,
|
||||
fileRoot,
|
||||
hasShadow: true,
|
||||
tint: 0.25,
|
||||
x: -5,
|
||||
|
@ -195,10 +195,10 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = MysteryEncounterBuil
|
||||
: ""
|
||||
}`;
|
||||
const line2 =
|
||||
i18next.t("pokemonInfoContainer:nature") +
|
||||
" " +
|
||||
getNatureName(tradePokemon.getNature()) +
|
||||
(formName ? ` | ${i18next.t("pokemonInfoContainer:form")} ${formName}` : "");
|
||||
i18next.t("pokemonInfoContainer:nature")
|
||||
+ " "
|
||||
+ getNatureName(tradePokemon.getNature())
|
||||
+ (formName ? ` | ${i18next.t("pokemonInfoContainer:form")} ${formName}` : "");
|
||||
showEncounterText(`${line1}\n${line2}`, 0, 0, false);
|
||||
},
|
||||
};
|
||||
@ -292,16 +292,14 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = MysteryEncounterBuil
|
||||
|
||||
// Extra HA roll at base 1/64 odds (boosted by events and charms)
|
||||
const hiddenIndex = tradePokemon.species.ability2 ? 2 : 1;
|
||||
if (tradePokemon.species.abilityHidden) {
|
||||
if (tradePokemon.abilityIndex < hiddenIndex) {
|
||||
const hiddenAbilityChance = new NumberHolder(64);
|
||||
globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
||||
if (tradePokemon.species.abilityHidden && tradePokemon.abilityIndex < hiddenIndex) {
|
||||
const hiddenAbilityChance = new NumberHolder(64);
|
||||
globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
||||
|
||||
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
|
||||
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
|
||||
|
||||
if (hasHiddenAbility) {
|
||||
tradePokemon.abilityIndex = hiddenIndex;
|
||||
}
|
||||
if (hasHiddenAbility) {
|
||||
tradePokemon.abilityIndex = hiddenIndex;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
|
||||
const normalSpriteKey = normalConfig.getSpriteKey(female, normalConfig.doubleOnly);
|
||||
encounter.enemyPartyConfigs.push({
|
||||
trainerConfig: normalConfig,
|
||||
female: female,
|
||||
female,
|
||||
});
|
||||
|
||||
// Hard difficulty trainer is another random trainer, but with AVERAGE_BALANCED config
|
||||
@ -81,7 +81,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
|
||||
encounter.enemyPartyConfigs.push({
|
||||
trainerConfig: hardConfig,
|
||||
levelAdditiveModifier: 1,
|
||||
female: female,
|
||||
female,
|
||||
});
|
||||
|
||||
// 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({
|
||||
trainerConfig: brutalConfig,
|
||||
levelAdditiveModifier: 1.5,
|
||||
female: female,
|
||||
female,
|
||||
});
|
||||
|
||||
encounter.spriteConfigs = [
|
||||
|
@ -163,8 +163,12 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
queueEncounterMessage(`${namespace}:option.1.great`);
|
||||
leaveEncounterWithoutBattle();
|
||||
} else if (
|
||||
roll >=
|
||||
RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT - MASTER_REWARDS_PERCENT
|
||||
roll
|
||||
>= RAND_LENGTH
|
||||
- COMMON_REWARDS_PERCENT
|
||||
- ULTRA_REWARDS_PERCENT
|
||||
- ROGUE_REWARDS_PERCENT
|
||||
- MASTER_REWARDS_PERCENT
|
||||
) {
|
||||
// Choose 1 MASTER tier item (5%)
|
||||
setEncounterRewards({
|
||||
|
@ -104,7 +104,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui
|
||||
encounter.setDialogueToken("boost2", modifiers[1].name);
|
||||
encounter.misc = {
|
||||
chosenPokemon: pokemon,
|
||||
modifiers: modifiers,
|
||||
modifiers,
|
||||
};
|
||||
};
|
||||
|
||||
@ -187,7 +187,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui
|
||||
encounter.setDialogueToken("boost2", modifiers[1].name);
|
||||
encounter.misc = {
|
||||
chosenPokemon: pokemon,
|
||||
modifiers: modifiers,
|
||||
modifiers,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -165,7 +165,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter = MysteryEncounterBui
|
||||
const config: EnemyPartyConfig = {
|
||||
pokemonConfigs: [
|
||||
{
|
||||
level: level,
|
||||
level,
|
||||
species: bossSpecies,
|
||||
dataSource: new PokemonData(bossPokemon),
|
||||
isBoss: true,
|
||||
@ -221,7 +221,7 @@ async function doBiomeTransitionDialogueAndBattleInit() {
|
||||
const config: EnemyPartyConfig = {
|
||||
pokemonConfigs: [
|
||||
{
|
||||
level: level,
|
||||
level,
|
||||
species: bossSpecies,
|
||||
dataSource: new PokemonData(bossPokemon),
|
||||
isBoss: true,
|
||||
|
@ -92,11 +92,11 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
|
||||
.getEventEncounters()
|
||||
.filter(
|
||||
s =>
|
||||
!getPokemonSpecies(s.species).legendary &&
|
||||
!getPokemonSpecies(s.species).subLegendary &&
|
||||
!getPokemonSpecies(s.species).mythical &&
|
||||
!NON_LEGEND_PARADOX_POKEMON.includes(s.species) &&
|
||||
!NON_LEGEND_ULTRA_BEASTS.includes(s.species),
|
||||
!getPokemonSpecies(s.species).legendary
|
||||
&& !getPokemonSpecies(s.species).subLegendary
|
||||
&& !getPokemonSpecies(s.species).mythical
|
||||
&& !NON_LEGEND_PARADOX_POKEMON.includes(s.species)
|
||||
&& !NON_LEGEND_ULTRA_BEASTS.includes(s.species),
|
||||
);
|
||||
|
||||
let pokemon: PlayerPokemon;
|
||||
@ -109,16 +109,16 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
|
||||
* Mons rolled from the event encounter pool get 3 extra shiny rolls
|
||||
*/
|
||||
if (
|
||||
r === 0 ||
|
||||
((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE) &&
|
||||
validEventEncounters.length === 0)
|
||||
r === 0
|
||||
|| ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE)
|
||||
&& validEventEncounters.length === 0)
|
||||
) {
|
||||
// If you roll 1%, give shiny Magikarp with random variant
|
||||
species = getPokemonSpecies(SpeciesId.MAGIKARP);
|
||||
pokemon = new PlayerPokemon(species, 5, 2, undefined, undefined, true);
|
||||
} else if (
|
||||
validEventEncounters.length > 0 &&
|
||||
(r <= EVENT_THRESHOLD || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE)
|
||||
validEventEncounters.length > 0
|
||||
&& (r <= EVENT_THRESHOLD || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE)
|
||||
) {
|
||||
tries = 0;
|
||||
do {
|
||||
@ -162,8 +162,8 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
|
||||
|
||||
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(pokemon);
|
||||
encounter.spriteConfigs.push({
|
||||
spriteKey: spriteKey,
|
||||
fileRoot: fileRoot,
|
||||
spriteKey,
|
||||
fileRoot,
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
isPokemon: true,
|
||||
@ -185,8 +185,8 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
|
||||
encounter.setDialogueToken("purchasePokemon", pokemon.getNameToRender());
|
||||
encounter.setDialogueToken("price", price.toString());
|
||||
encounter.misc = {
|
||||
price: price,
|
||||
pokemon: pokemon,
|
||||
price,
|
||||
pokemon,
|
||||
};
|
||||
|
||||
pokemon.calculateStats();
|
||||
|
@ -212,9 +212,9 @@ function endTrainerBattleAndShowDialogue(): Promise<void> {
|
||||
// Only trigger form change when Eiscue is in Noice form
|
||||
// Hardcoded Eiscue for now in case it is fused with another pokemon
|
||||
if (
|
||||
pokemon.species.speciesId === SpeciesId.EISCUE &&
|
||||
pokemon.hasAbility(AbilityId.ICE_FACE) &&
|
||||
pokemon.formIndex === 1
|
||||
pokemon.species.speciesId === SpeciesId.EISCUE
|
||||
&& pokemon.hasAbility(AbilityId.ICE_FACE)
|
||||
&& pokemon.formIndex === 1
|
||||
) {
|
||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger);
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
let ivIndexes: any[] = [];
|
||||
playerPokemon.ivs.forEach((iv, index) => {
|
||||
if (iv < 31) {
|
||||
ivIndexes.push({ iv: iv, index: index });
|
||||
ivIndexes.push({ iv, 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)
|
||||
const rootFusionSpecies = playerPokemon.fusionSpecies?.getRootSpeciesId();
|
||||
if (
|
||||
!isNullOrUndefined(rootFusionSpecies) &&
|
||||
speciesStarterCosts.hasOwnProperty(rootFusionSpecies) &&
|
||||
!!globalScene.gameData.dexData[rootFusionSpecies].caughtAttr
|
||||
!isNullOrUndefined(rootFusionSpecies)
|
||||
&& speciesStarterCosts.hasOwnProperty(rootFusionSpecies)
|
||||
&& !!globalScene.gameData.dexData[rootFusionSpecies].caughtAttr
|
||||
) {
|
||||
globalScene.gameData.starterData[rootFusionSpecies].abilityAttr |=
|
||||
playerPokemon.fusionAbilityIndex !== 1 || playerPokemon.fusionSpecies?.ability2
|
||||
@ -396,7 +396,7 @@ function getEnemyConfig(playerPokemon: PlayerPokemon, segments: number, modifier
|
||||
formIndex: playerPokemon.formIndex,
|
||||
level: playerPokemon.level,
|
||||
dataSource: data,
|
||||
modifierConfigs: modifierConfigs,
|
||||
modifierConfigs,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -71,7 +71,7 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
const randomEggMove: MoveId = eggMoves[eggMoveIndex];
|
||||
encounter.misc = {
|
||||
eggMove: randomEggMove,
|
||||
pokemon: pokemon,
|
||||
pokemon,
|
||||
};
|
||||
if (pokemon.moveset.length < 4) {
|
||||
pokemon.moveset.push(new PokemonMove(randomEggMove));
|
||||
@ -91,7 +91,7 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
const config: EnemyPartyConfig = {
|
||||
pokemonConfigs: [
|
||||
{
|
||||
level: level,
|
||||
level,
|
||||
species: pokemon.species,
|
||||
dataSource: new PokemonData(pokemon),
|
||||
isBoss: false,
|
||||
@ -114,8 +114,8 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(pokemon);
|
||||
encounter.spriteConfigs = [
|
||||
{
|
||||
spriteKey: spriteKey,
|
||||
fileRoot: fileRoot,
|
||||
spriteKey,
|
||||
fileRoot,
|
||||
hasShadow: true,
|
||||
x: -5,
|
||||
repeat: true,
|
||||
|
@ -285,7 +285,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
|
||||
species: transformation.newSpecies,
|
||||
isBoss: newPokemon.getSpeciesForm().getBaseStatTotal() > NON_LEGENDARY_BST_THRESHOLD,
|
||||
level: previousPokemon.level,
|
||||
dataSource: dataSource,
|
||||
dataSource,
|
||||
modifierConfigs: newPokemonHeldItemConfigs,
|
||||
};
|
||||
|
||||
@ -299,7 +299,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
|
||||
].clone();
|
||||
trainerConfig.setPartyTemplates(new TrainerPartyTemplate(transformations.length, PartyMemberStrength.STRONG));
|
||||
const enemyPartyConfig: EnemyPartyConfig = {
|
||||
trainerConfig: trainerConfig,
|
||||
trainerConfig,
|
||||
pokemonConfigs: enemyPokemonConfigs,
|
||||
female: genderIndex === PlayerGender.FEMALE,
|
||||
};
|
||||
@ -541,12 +541,12 @@ async function postProcessTransformedPokemon(
|
||||
|
||||
// For pokemon at/below 570 BST or any shiny pokemon, unlock it permanently as if you had caught it
|
||||
if (
|
||||
!forBattle &&
|
||||
(newPokemon.getSpeciesForm().getBaseStatTotal() <= NON_LEGENDARY_BST_THRESHOLD || newPokemon.isShiny())
|
||||
!forBattle
|
||||
&& (newPokemon.getSpeciesForm().getBaseStatTotal() <= NON_LEGENDARY_BST_THRESHOLD || newPokemon.isShiny())
|
||||
) {
|
||||
if (
|
||||
newPokemon.getSpeciesForm().abilityHidden &&
|
||||
newPokemon.abilityIndex === newPokemon.getSpeciesForm().getAbilityCount() - 1
|
||||
newPokemon.getSpeciesForm().abilityHidden
|
||||
&& newPokemon.abilityIndex === newPokemon.getSpeciesForm().getAbilityCount() - 1
|
||||
) {
|
||||
globalScene.validateAchv(achvs.HIDDEN_ABILITY);
|
||||
}
|
||||
@ -644,10 +644,10 @@ function getTransformedSpecies(
|
||||
const bstInRange = speciesBst >= bstMin && speciesBst <= bstCap;
|
||||
// Checks that a Pokemon has not already been added in the +600 or 570-600 slots;
|
||||
const validBst =
|
||||
(!hasPokemonBstBetween570And600 ||
|
||||
speciesBst < NON_LEGENDARY_BST_THRESHOLD ||
|
||||
speciesBst > SUPER_LEGENDARY_BST_THRESHOLD) &&
|
||||
(!hasPokemonBstHigherThan600 || speciesBst <= SUPER_LEGENDARY_BST_THRESHOLD);
|
||||
(!hasPokemonBstBetween570And600
|
||||
|| speciesBst < NON_LEGENDARY_BST_THRESHOLD
|
||||
|| speciesBst > SUPER_LEGENDARY_BST_THRESHOLD)
|
||||
&& (!hasPokemonBstHigherThan600 || speciesBst <= SUPER_LEGENDARY_BST_THRESHOLD);
|
||||
return bstInRange && validBst && !EXCLUDED_TRANSFORMATION_SPECIES.includes(s.speciesId);
|
||||
});
|
||||
|
||||
@ -792,9 +792,9 @@ async function addEggMoveToNewPokemonMoveset(
|
||||
|
||||
// For pokemon that the player owns (including ones just caught), unlock the egg move
|
||||
if (
|
||||
!forBattle &&
|
||||
!isNullOrUndefined(randomEggMoveIndex) &&
|
||||
!!globalScene.gameData.dexData[speciesRootForm].caughtAttr
|
||||
!forBattle
|
||||
&& !isNullOrUndefined(randomEggMoveIndex)
|
||||
&& !!globalScene.gameData.dexData[speciesRootForm].caughtAttr
|
||||
) {
|
||||
await globalScene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), randomEggMoveIndex, true);
|
||||
}
|
||||
|
@ -76,9 +76,9 @@ export class MysteryEncounterOption implements IMysteryEncounterOption {
|
||||
*/
|
||||
hasRequirements(): boolean {
|
||||
return (
|
||||
this.requirements.length > 0 ||
|
||||
this.primaryPokemonRequirements.length > 0 ||
|
||||
this.secondaryPokemonRequirements.length > 0
|
||||
this.requirements.length > 0
|
||||
|| this.primaryPokemonRequirements.length > 0
|
||||
|| this.secondaryPokemonRequirements.length > 0
|
||||
);
|
||||
}
|
||||
|
||||
@ -87,9 +87,9 @@ export class MysteryEncounterOption implements IMysteryEncounterOption {
|
||||
*/
|
||||
meetsRequirements(): boolean {
|
||||
return (
|
||||
!this.requirements.some(requirement => !requirement.meetsRequirement()) &&
|
||||
this.meetsSupportingRequirementAndSupportingPokemonSelected() &&
|
||||
this.meetsPrimaryRequirementAndPrimaryPokemonSelected()
|
||||
!this.requirements.some(requirement => !requirement.meetsRequirement())
|
||||
&& this.meetsSupportingRequirementAndSupportingPokemonSelected()
|
||||
&& this.meetsPrimaryRequirementAndPrimaryPokemonSelected()
|
||||
);
|
||||
}
|
||||
|
||||
@ -209,7 +209,7 @@ export class MysteryEncounterOptionBuilder implements Partial<IMysteryEncounterO
|
||||
}
|
||||
|
||||
withHasDexProgress(hasDexProgress: boolean): this & Required<Pick<IMysteryEncounterOption, "hasDexProgress">> {
|
||||
return Object.assign(this, { hasDexProgress: hasDexProgress });
|
||||
return Object.assign(this, { hasDexProgress });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -220,7 +220,7 @@ export class MysteryEncounterOptionBuilder implements Partial<IMysteryEncounterO
|
||||
requirement: EncounterSceneRequirement,
|
||||
): this & Required<Pick<IMysteryEncounterOption, "requirements">> {
|
||||
if (requirement instanceof EncounterPokemonRequirement) {
|
||||
Error("Incorrectly added pokemon requirement as scene requirement.");
|
||||
new Error("Incorrectly added pokemon requirement as scene requirement.");
|
||||
}
|
||||
|
||||
this.requirements.push(requirement);
|
||||
@ -240,7 +240,7 @@ export class MysteryEncounterOptionBuilder implements Partial<IMysteryEncounterO
|
||||
withPreOptionPhase(
|
||||
onPreOptionPhase: OptionPhaseCallback,
|
||||
): this & Required<Pick<IMysteryEncounterOption, "onPreOptionPhase">> {
|
||||
return Object.assign(this, { onPreOptionPhase: onPreOptionPhase });
|
||||
return Object.assign(this, { onPreOptionPhase });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -248,13 +248,13 @@ export class MysteryEncounterOptionBuilder implements Partial<IMysteryEncounterO
|
||||
* @param onOptionPhase
|
||||
*/
|
||||
withOptionPhase(onOptionPhase: OptionPhaseCallback): this & Required<Pick<IMysteryEncounterOption, "onOptionPhase">> {
|
||||
return Object.assign(this, { onOptionPhase: onOptionPhase });
|
||||
return Object.assign(this, { onOptionPhase });
|
||||
}
|
||||
|
||||
withPostOptionPhase(
|
||||
onPostOptionPhase: OptionPhaseCallback,
|
||||
): this & Required<Pick<IMysteryEncounterOption, "onPostOptionPhase">> {
|
||||
return Object.assign(this, { onPostOptionPhase: onPostOptionPhase });
|
||||
return Object.assign(this, { onPostOptionPhase });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -265,7 +265,7 @@ export class MysteryEncounterOptionBuilder implements Partial<IMysteryEncounterO
|
||||
requirement: EncounterPokemonRequirement,
|
||||
): this & Required<Pick<IMysteryEncounterOption, "primaryPokemonRequirements">> {
|
||||
if (requirement instanceof EncounterSceneRequirement) {
|
||||
Error("Incorrectly added scene requirement as pokemon requirement.");
|
||||
new Error("Incorrectly added scene requirement as pokemon requirement.");
|
||||
}
|
||||
|
||||
this.primaryPokemonRequirements.push(requirement);
|
||||
@ -315,7 +315,7 @@ export class MysteryEncounterOptionBuilder implements Partial<IMysteryEncounterO
|
||||
excludePrimaryFromSecondaryRequirements = true,
|
||||
): this & Required<Pick<IMysteryEncounterOption, "secondaryPokemonRequirements">> {
|
||||
if (requirement instanceof EncounterSceneRequirement) {
|
||||
Error("Incorrectly added scene requirement as pokemon requirement.");
|
||||
new Error("Incorrectly added scene requirement as pokemon requirement.");
|
||||
}
|
||||
|
||||
this.secondaryPokemonRequirements.push(requirement);
|
||||
|
@ -222,8 +222,8 @@ export class WaveRangeRequirement extends EncounterSceneRequirement {
|
||||
if (!isNullOrUndefined(this.waveRange) && this.waveRange[0] <= this.waveRange[1]) {
|
||||
const waveIndex = globalScene.currentBattle.waveIndex;
|
||||
if (
|
||||
(waveIndex >= 0 && this.waveRange[0] >= 0 && this.waveRange[0] > waveIndex) ||
|
||||
(this.waveRange[1] >= 0 && this.waveRange[1] < waveIndex)
|
||||
(waveIndex >= 0 && this.waveRange[0] >= 0 && this.waveRange[0] > waveIndex)
|
||||
|| (this.waveRange[1] >= 0 && this.waveRange[1] < waveIndex)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -276,9 +276,9 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
|
||||
override meetsRequirement(): boolean {
|
||||
const timeOfDay = globalScene.arena?.getTimeOfDay();
|
||||
return !(
|
||||
!isNullOrUndefined(timeOfDay) &&
|
||||
this.requiredTimeOfDay?.length > 0 &&
|
||||
!this.requiredTimeOfDay.includes(timeOfDay)
|
||||
!isNullOrUndefined(timeOfDay)
|
||||
&& this.requiredTimeOfDay?.length > 0
|
||||
&& !this.requiredTimeOfDay.includes(timeOfDay)
|
||||
);
|
||||
}
|
||||
|
||||
@ -298,9 +298,9 @@ export class WeatherRequirement extends EncounterSceneRequirement {
|
||||
override meetsRequirement(): boolean {
|
||||
const currentWeather = globalScene.arena.weather?.weatherType;
|
||||
return !(
|
||||
!isNullOrUndefined(currentWeather) &&
|
||||
this.requiredWeather?.length > 0 &&
|
||||
!this.requiredWeather.includes(currentWeather!)
|
||||
!isNullOrUndefined(currentWeather)
|
||||
&& this.requiredWeather?.length > 0
|
||||
&& !this.requiredWeather.includes(currentWeather!)
|
||||
);
|
||||
}
|
||||
|
||||
@ -336,8 +336,8 @@ export class PartySizeRequirement extends EncounterSceneRequirement {
|
||||
? globalScene.getPokemonAllowedInBattle().length
|
||||
: globalScene.getPlayerParty().length;
|
||||
if (
|
||||
(partySize >= 0 && this.partySizeRange[0] >= 0 && this.partySizeRange[0] > partySize) ||
|
||||
(this.partySizeRange[1] >= 0 && this.partySizeRange[1] < partySize)
|
||||
(partySize >= 0 && this.partySizeRange[0] >= 0 && this.partySizeRange[0] > partySize)
|
||||
|| (this.partySizeRange[1] >= 0 && this.partySizeRange[1] < partySize)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -572,15 +572,15 @@ export class MoveRequirement extends EncounterPokemonRequirement {
|
||||
// get the Pokemon with at least one move in the required moves list
|
||||
return partyPokemon.filter(
|
||||
pokemon =>
|
||||
(!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) &&
|
||||
pokemon.moveset.some(move => move.moveId && this.requiredMoves.includes(move.moveId)),
|
||||
(!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle())
|
||||
&& 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
|
||||
return partyPokemon.filter(
|
||||
pokemon =>
|
||||
(!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) &&
|
||||
!pokemon.moveset.some(move => move.moveId && this.requiredMoves.includes(move.moveId)),
|
||||
(!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle())
|
||||
&& !pokemon.moveset.some(move => move.moveId && this.requiredMoves.includes(move.moveId)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -678,15 +678,15 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
|
||||
if (!this.invertQuery) {
|
||||
return partyPokemon.filter(
|
||||
pokemon =>
|
||||
(!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) &&
|
||||
this.requiredAbilities.some(ability => pokemon.hasAbility(ability, false)),
|
||||
(!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle())
|
||||
&& 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
|
||||
return partyPokemon.filter(
|
||||
pokemon =>
|
||||
(!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) &&
|
||||
this.requiredAbilities.filter(ability => pokemon.hasAbility(ability, false)).length === 0,
|
||||
(!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle())
|
||||
&& this.requiredAbilities.filter(ability => pokemon.hasAbility(ability, false)).length === 0,
|
||||
);
|
||||
}
|
||||
|
||||
@ -728,9 +728,9 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
|
||||
if (statusEffect === StatusEffect.NONE) {
|
||||
// StatusEffect.NONE also checks for null or undefined status
|
||||
return (
|
||||
isNullOrUndefined(pokemon.status) ||
|
||||
isNullOrUndefined(pokemon.status.effect) ||
|
||||
pokemon.status.effect === statusEffect
|
||||
isNullOrUndefined(pokemon.status)
|
||||
|| isNullOrUndefined(pokemon.status.effect)
|
||||
|| pokemon.status.effect === statusEffect
|
||||
);
|
||||
}
|
||||
return pokemon.status?.effect === statusEffect;
|
||||
@ -743,9 +743,9 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
|
||||
if (statusEffect === StatusEffect.NONE) {
|
||||
// StatusEffect.NONE also checks for null or undefined status
|
||||
return (
|
||||
isNullOrUndefined(pokemon.status) ||
|
||||
isNullOrUndefined(pokemon.status.effect) ||
|
||||
pokemon.status.effect === statusEffect
|
||||
isNullOrUndefined(pokemon.status)
|
||||
|| isNullOrUndefined(pokemon.status.effect)
|
||||
|| pokemon.status.effect === statusEffect
|
||||
);
|
||||
}
|
||||
return pokemon.status?.effect === statusEffect;
|
||||
@ -796,9 +796,8 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
|
||||
|
||||
filterByForm(pokemon, formChangeItem) {
|
||||
return (
|
||||
pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) &&
|
||||
// Get all form changes for this species with an item trigger, including any compound triggers
|
||||
pokemonFormChanges[pokemon.species.speciesId]
|
||||
pokemonFormChanges.hasOwnProperty(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))
|
||||
// Returns true if any form changes match this item
|
||||
.flatMap(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger)
|
||||
@ -870,8 +869,8 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
|
||||
pokemon =>
|
||||
pokemon.getHeldItems().filter(it => {
|
||||
return (
|
||||
!this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem) &&
|
||||
(!this.requireTransferable || it.isTransferable)
|
||||
!this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem)
|
||||
&& (!this.requireTransferable || it.isTransferable)
|
||||
);
|
||||
}).length > 0,
|
||||
);
|
||||
@ -880,8 +879,8 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
const requiredItems = pokemon?.getHeldItems().filter(it => {
|
||||
return (
|
||||
this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem) &&
|
||||
(!this.requireTransferable || it.isTransferable)
|
||||
this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem)
|
||||
&& (!this.requireTransferable || it.isTransferable)
|
||||
);
|
||||
});
|
||||
if (requiredItems && requiredItems.length > 0) {
|
||||
@ -924,9 +923,9 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
|
||||
this.requiredHeldItemTypes.some(heldItemType => {
|
||||
return pokemon.getHeldItems().some(it => {
|
||||
return (
|
||||
it instanceof AttackTypeBoosterModifier &&
|
||||
(it.type as AttackTypeBoosterModifierType).moveType === heldItemType &&
|
||||
(!this.requireTransferable || it.isTransferable)
|
||||
it instanceof AttackTypeBoosterModifier
|
||||
&& (it.type as AttackTypeBoosterModifierType).moveType === heldItemType
|
||||
&& (!this.requireTransferable || it.isTransferable)
|
||||
);
|
||||
});
|
||||
}),
|
||||
@ -939,9 +938,9 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
|
||||
pokemon.getHeldItems().filter(it => {
|
||||
return !this.requiredHeldItemTypes.some(
|
||||
heldItemType =>
|
||||
it instanceof AttackTypeBoosterModifier &&
|
||||
(it.type as AttackTypeBoosterModifierType).moveType === heldItemType &&
|
||||
(!this.requireTransferable || it.isTransferable),
|
||||
it instanceof AttackTypeBoosterModifier
|
||||
&& (it.type as AttackTypeBoosterModifierType).moveType === heldItemType
|
||||
&& (!this.requireTransferable || it.isTransferable),
|
||||
);
|
||||
}).length > 0,
|
||||
);
|
||||
@ -952,10 +951,10 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
|
||||
return (
|
||||
this.requiredHeldItemTypes.some(
|
||||
heldItemType =>
|
||||
it instanceof AttackTypeBoosterModifier &&
|
||||
(it.type as AttackTypeBoosterModifierType).moveType === heldItemType,
|
||||
) &&
|
||||
(!this.requireTransferable || it.isTransferable)
|
||||
it instanceof AttackTypeBoosterModifier
|
||||
&& (it.type as AttackTypeBoosterModifierType).moveType === heldItemType,
|
||||
)
|
||||
&& (!this.requireTransferable || it.isTransferable)
|
||||
);
|
||||
});
|
||||
if (requiredItems && requiredItems.length > 0) {
|
||||
@ -1021,8 +1020,8 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
|
||||
override meetsRequirement(): boolean {
|
||||
// Party Pokemon inside required friendship range
|
||||
if (
|
||||
!isNullOrUndefined(this.requiredFriendshipRange) &&
|
||||
this.requiredFriendshipRange[0] <= this.requiredFriendshipRange[1]
|
||||
!isNullOrUndefined(this.requiredFriendshipRange)
|
||||
&& this.requiredFriendshipRange[0] <= this.requiredFriendshipRange[1]
|
||||
) {
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
const pokemonInRange = this.queryParty(partyPokemon);
|
||||
@ -1037,8 +1036,8 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
|
||||
if (!this.invertQuery) {
|
||||
return partyPokemon.filter(
|
||||
pokemon =>
|
||||
pokemon.friendship >= this.requiredFriendshipRange[0] &&
|
||||
pokemon.friendship <= this.requiredFriendshipRange[1],
|
||||
pokemon.friendship >= this.requiredFriendshipRange[0]
|
||||
&& 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
|
||||
|
@ -658,7 +658,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
withIntroSpriteConfigs(
|
||||
spriteConfigs: MysteryEncounterSpriteConfig[],
|
||||
): this & Pick<IMysteryEncounter, "spriteConfigs"> {
|
||||
return Object.assign(this, { spriteConfigs: spriteConfigs });
|
||||
return Object.assign(this, { spriteConfigs });
|
||||
}
|
||||
|
||||
withIntroDialogue(dialogue: MysteryEncounterDialogue["intro"] = []): this {
|
||||
@ -703,7 +703,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
* @returns
|
||||
*/
|
||||
withEncounterTier(encounterTier: MysteryEncounterTier): this & Pick<IMysteryEncounter, "encounterTier"> {
|
||||
return Object.assign(this, { encounterTier: encounterTier });
|
||||
return Object.assign(this, { encounterTier });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -753,7 +753,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
withContinuousEncounter(
|
||||
continuousEncounter: boolean,
|
||||
): this & Required<Pick<IMysteryEncounter, "continuousEncounter">> {
|
||||
return Object.assign(this, { continuousEncounter: continuousEncounter });
|
||||
return Object.assign(this, { continuousEncounter });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -807,7 +807,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
withMaxAllowedEncounters(
|
||||
maxAllowedEncounters: number,
|
||||
): this & Required<Pick<IMysteryEncounter, "maxAllowedEncounters">> {
|
||||
return Object.assign(this, { maxAllowedEncounters: maxAllowedEncounters });
|
||||
return Object.assign(this, { maxAllowedEncounters });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -821,7 +821,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
requirement: EncounterSceneRequirement,
|
||||
): this & Required<Pick<IMysteryEncounter, "requirements">> {
|
||||
if (requirement instanceof EncounterPokemonRequirement) {
|
||||
Error("Incorrectly added pokemon requirement as scene requirement.");
|
||||
new Error("Incorrectly added pokemon requirement as scene requirement.");
|
||||
}
|
||||
this.requirements.push(requirement);
|
||||
return this;
|
||||
@ -864,7 +864,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
requirement: EncounterPokemonRequirement,
|
||||
): this & Required<Pick<IMysteryEncounter, "primaryPokemonRequirements">> {
|
||||
if (requirement instanceof EncounterSceneRequirement) {
|
||||
Error("Incorrectly added scene requirement as pokemon requirement.");
|
||||
new Error("Incorrectly added scene requirement as pokemon requirement.");
|
||||
}
|
||||
|
||||
this.primaryPokemonRequirements.push(requirement);
|
||||
@ -917,7 +917,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
excludePrimaryFromSecondaryRequirements = false,
|
||||
): this & Required<Pick<IMysteryEncounter, "secondaryPokemonRequirements">> {
|
||||
if (requirement instanceof EncounterSceneRequirement) {
|
||||
Error("Incorrectly added scene requirement as pokemon requirement.");
|
||||
new Error("Incorrectly added scene requirement as pokemon requirement.");
|
||||
}
|
||||
|
||||
this.secondaryPokemonRequirements.push(requirement);
|
||||
@ -939,7 +939,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
* @returns
|
||||
*/
|
||||
withRewards(doEncounterRewards: () => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterRewards">> {
|
||||
return Object.assign(this, { doEncounterRewards: doEncounterRewards });
|
||||
return Object.assign(this, { doEncounterRewards });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -953,7 +953,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
* @returns
|
||||
*/
|
||||
withExp(doEncounterExp: () => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterExp">> {
|
||||
return Object.assign(this, { doEncounterExp: doEncounterExp });
|
||||
return Object.assign(this, { doEncounterExp });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -974,7 +974,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
* @returns
|
||||
*/
|
||||
withOnVisualsStart(onVisualsStart: () => boolean): this & Required<Pick<IMysteryEncounter, "onVisualsStart">> {
|
||||
return Object.assign(this, { onVisualsStart: onVisualsStart });
|
||||
return Object.assign(this, { onVisualsStart });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -984,7 +984,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
* @returns
|
||||
*/
|
||||
withCatchAllowed(catchAllowed: boolean): this & Required<Pick<IMysteryEncounter, "catchAllowed">> {
|
||||
return Object.assign(this, { catchAllowed: catchAllowed });
|
||||
return Object.assign(this, { catchAllowed });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1004,7 +1004,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
hideBattleIntroMessage: boolean,
|
||||
): this & Required<Pick<IMysteryEncounter, "hideBattleIntroMessage">> {
|
||||
return Object.assign(this, {
|
||||
hideBattleIntroMessage: hideBattleIntroMessage,
|
||||
hideBattleIntroMessage,
|
||||
});
|
||||
}
|
||||
|
||||
@ -1015,7 +1015,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
withAutoHideIntroVisuals(
|
||||
autoHideIntroVisuals: boolean,
|
||||
): this & Required<Pick<IMysteryEncounter, "autoHideIntroVisuals">> {
|
||||
return Object.assign(this, { autoHideIntroVisuals: autoHideIntroVisuals });
|
||||
return Object.assign(this, { autoHideIntroVisuals });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1027,7 +1027,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
enterIntroVisualsFromRight: boolean,
|
||||
): this & Required<Pick<IMysteryEncounter, "enterIntroVisualsFromRight">> {
|
||||
return Object.assign(this, {
|
||||
enterIntroVisualsFromRight: enterIntroVisualsFromRight,
|
||||
enterIntroVisualsFromRight,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -193,7 +193,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
|
||||
// This can be amplified or counteracted by setting levelAdditiveModifier in config
|
||||
// levelAdditiveModifier value of 0.5 will halve the modifier scaling, 2 will double it, etc.
|
||||
// Leaving null/undefined will disable level scaling
|
||||
const mult: number = !isNullOrUndefined(partyConfig.levelAdditiveModifier) ? partyConfig.levelAdditiveModifier : 0;
|
||||
const mult = partyConfig.levelAdditiveModifier ?? 0;
|
||||
const additive = Math.max(Math.round((globalScene.currentBattle.waveIndex / 10) * mult), 0);
|
||||
battle.enemyLevels = battle.enemyLevels.map(level => level + additive);
|
||||
|
||||
@ -297,9 +297,9 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
|
||||
|
||||
// Set Boss
|
||||
if (config.isBoss) {
|
||||
let segments = !isNullOrUndefined(config.bossSegments)
|
||||
? config.bossSegments!
|
||||
: globalScene.getEncounterBossSegments(globalScene.currentBattle.waveIndex, level, enemySpecies, true);
|
||||
let segments =
|
||||
config.bossSegments
|
||||
?? globalScene.getEncounterBossSegments(globalScene.currentBattle.waveIndex, level, enemySpecies, true);
|
||||
if (!isNullOrUndefined(config.bossSegmentModifier)) {
|
||||
segments += config.bossSegmentModifier;
|
||||
}
|
||||
@ -545,83 +545,74 @@ export function selectPokemonForOption(
|
||||
UiMode.PARTY,
|
||||
PartyUiMode.SELECT,
|
||||
-1,
|
||||
(slotIndex: number, _option: PartyOption) => {
|
||||
if (slotIndex < globalScene.getPlayerParty().length) {
|
||||
globalScene.ui.setMode(modeToSetOnExit).then(() => {
|
||||
const pokemon = globalScene.getPlayerParty()[slotIndex];
|
||||
const secondaryOptions = onPokemonSelected(pokemon);
|
||||
if (!secondaryOptions) {
|
||||
globalScene.currentBattle.mysteryEncounter!.setDialogueToken(
|
||||
"selectedPokemon",
|
||||
pokemon.getNameToRender(),
|
||||
);
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// There is a second option to choose after selecting the Pokemon
|
||||
globalScene.ui.setMode(UiMode.MESSAGE).then(() => {
|
||||
const displayOptions = () => {
|
||||
// Always appends a cancel option to bottom of options
|
||||
const fullOptions = secondaryOptions
|
||||
.map(option => {
|
||||
// Update handler to resolve promise
|
||||
const onSelect = option.handler;
|
||||
option.handler = () => {
|
||||
onSelect();
|
||||
globalScene.currentBattle.mysteryEncounter!.setDialogueToken(
|
||||
"selectedPokemon",
|
||||
pokemon.getNameToRender(),
|
||||
);
|
||||
resolve(true);
|
||||
return true;
|
||||
};
|
||||
return option;
|
||||
})
|
||||
.concat({
|
||||
label: i18next.t("menu:cancel"),
|
||||
handler: () => {
|
||||
globalScene.ui.clearText();
|
||||
globalScene.ui.setMode(modeToSetOnExit);
|
||||
resolve(false);
|
||||
return true;
|
||||
},
|
||||
onHover: () => {
|
||||
showEncounterText(i18next.t("mysteryEncounterMessages:cancelOption"), 0, 0, false);
|
||||
},
|
||||
});
|
||||
|
||||
const config: OptionSelectConfig = {
|
||||
options: fullOptions,
|
||||
maxOptions: 7,
|
||||
yOffset: 0,
|
||||
supportHover: true,
|
||||
};
|
||||
|
||||
// Do hover over the starting selection option
|
||||
if (fullOptions[0].onHover) {
|
||||
fullOptions[0].onHover();
|
||||
}
|
||||
globalScene.ui.setModeWithoutClear(UiMode.OPTION_SELECT, config, null, true);
|
||||
};
|
||||
|
||||
const textPromptKey =
|
||||
globalScene.currentBattle.mysteryEncounter?.selectedOption?.dialogue?.secondOptionPrompt;
|
||||
if (!textPromptKey) {
|
||||
displayOptions();
|
||||
} else {
|
||||
showEncounterText(textPromptKey).then(() => displayOptions());
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
globalScene.ui.setMode(modeToSetOnExit).then(() => {
|
||||
if (onPokemonNotSelected) {
|
||||
onPokemonNotSelected();
|
||||
}
|
||||
resolve(false);
|
||||
});
|
||||
async (slotIndex: number, _option: PartyOption) => {
|
||||
await globalScene.ui.setMode(modeToSetOnExit);
|
||||
if (slotIndex >= globalScene.getPlayerParty().length) {
|
||||
onPokemonNotSelected?.();
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const pokemon = globalScene.getPlayerParty()[slotIndex];
|
||||
const secondaryOptions = onPokemonSelected(pokemon);
|
||||
if (!secondaryOptions) {
|
||||
globalScene.currentBattle.mysteryEncounter!.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// There is a second option to choose after selecting the Pokemon
|
||||
await globalScene.ui.setMode(UiMode.MESSAGE);
|
||||
// TODO: fix this
|
||||
const displayOptions = () => {
|
||||
// Always appends a cancel option to bottom of options
|
||||
const fullOptions = secondaryOptions
|
||||
.map(option => {
|
||||
// Update handler to resolve promise
|
||||
const onSelect = option.handler;
|
||||
option.handler = () => {
|
||||
onSelect();
|
||||
globalScene.currentBattle.mysteryEncounter!.setDialogueToken(
|
||||
"selectedPokemon",
|
||||
pokemon.getNameToRender(),
|
||||
);
|
||||
resolve(true);
|
||||
return true;
|
||||
};
|
||||
return option;
|
||||
})
|
||||
.concat({
|
||||
label: i18next.t("menu:cancel"),
|
||||
handler: () => {
|
||||
globalScene.ui.clearText();
|
||||
globalScene.ui.setMode(modeToSetOnExit);
|
||||
resolve(false);
|
||||
return true;
|
||||
},
|
||||
onHover: () => {
|
||||
showEncounterText(i18next.t("mysteryEncounterMessages:cancelOption"), 0, 0, false);
|
||||
},
|
||||
});
|
||||
|
||||
const config: OptionSelectConfig = {
|
||||
options: fullOptions,
|
||||
maxOptions: 7,
|
||||
yOffset: 0,
|
||||
supportHover: true,
|
||||
};
|
||||
|
||||
// Do hover over the starting selection option
|
||||
if (fullOptions[0]?.onHover) {
|
||||
fullOptions[0].onHover();
|
||||
}
|
||||
globalScene.ui.setModeWithoutClear(UiMode.OPTION_SELECT, config, null, true);
|
||||
};
|
||||
|
||||
const textPromptKey = globalScene.currentBattle.mysteryEncounter?.selectedOption?.dialogue?.secondOptionPrompt;
|
||||
if (textPromptKey) {
|
||||
await showEncounterText(textPromptKey);
|
||||
}
|
||||
displayOptions();
|
||||
},
|
||||
selectablePokemonFilter,
|
||||
);
|
||||
@ -651,24 +642,16 @@ export function selectOptionThenPokemon(
|
||||
return new Promise<PokemonAndOptionSelected | null>(resolve => {
|
||||
const modeToSetOnExit = globalScene.ui.getMode();
|
||||
|
||||
const displayOptions = (config: OptionSelectConfig) => {
|
||||
globalScene.ui.setMode(UiMode.MESSAGE).then(() => {
|
||||
if (!optionSelectPromptKey) {
|
||||
// Do hover over the starting selection option
|
||||
if (fullOptions[0].onHover) {
|
||||
fullOptions[0].onHover();
|
||||
}
|
||||
globalScene.ui.setMode(UiMode.OPTION_SELECT, config);
|
||||
} else {
|
||||
showEncounterText(optionSelectPromptKey).then(() => {
|
||||
// Do hover over the starting selection option
|
||||
if (fullOptions[0].onHover) {
|
||||
fullOptions[0].onHover();
|
||||
}
|
||||
globalScene.ui.setMode(UiMode.OPTION_SELECT, config);
|
||||
});
|
||||
}
|
||||
});
|
||||
const displayOptions = async (config: OptionSelectConfig) => {
|
||||
await globalScene.ui.setMode(UiMode.MESSAGE);
|
||||
if (optionSelectPromptKey) {
|
||||
showEncounterText(optionSelectPromptKey);
|
||||
}
|
||||
// Do hover over the starting selection option
|
||||
if (fullOptions[0]?.onHover) {
|
||||
fullOptions[0].onHover();
|
||||
}
|
||||
globalScene.ui.setMode(UiMode.OPTION_SELECT, config);
|
||||
};
|
||||
|
||||
const selectPokemonAfterOption = (selectedOptionIndex: number) => {
|
||||
@ -683,7 +666,7 @@ export function selectOptionThenPokemon(
|
||||
globalScene.ui.setMode(modeToSetOnExit).then(() => {
|
||||
const result: PokemonAndOptionSelected = {
|
||||
selectedPokemonIndex: slotIndex,
|
||||
selectedOptionIndex: selectedOptionIndex,
|
||||
selectedOptionIndex,
|
||||
};
|
||||
resolve(result);
|
||||
});
|
||||
@ -965,10 +948,10 @@ export function transitionMysteryEncounterIntroVisuals(hide = true, destroy = tr
|
||||
export function handleMysteryEncounterBattleStartEffects() {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter;
|
||||
if (
|
||||
globalScene.currentBattle.isBattleMysteryEncounter() &&
|
||||
encounter &&
|
||||
encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE &&
|
||||
!encounter.startOfBattleEffectsComplete
|
||||
globalScene.currentBattle.isBattleMysteryEncounter()
|
||||
&& encounter
|
||||
&& encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE
|
||||
&& !encounter.startOfBattleEffectsComplete
|
||||
) {
|
||||
const effects = encounter.startOfBattleEffects;
|
||||
effects.forEach(effect => {
|
||||
@ -1093,7 +1076,7 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) {
|
||||
.filter(b => {
|
||||
return !Array.isArray(b) || !randSeedInt(b[1]);
|
||||
})
|
||||
.map(b => (!Array.isArray(b) ? b : b[0]));
|
||||
.map(b => (Array.isArray(b) ? b[0] : b));
|
||||
}, i * 100);
|
||||
if (biomes! && biomes.length > 0) {
|
||||
const specialBiomes = biomes.filter(b => alwaysPickTheseBiomes.includes(b));
|
||||
@ -1107,12 +1090,10 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) {
|
||||
}
|
||||
} else if (biomeLinks.hasOwnProperty(currentBiome)) {
|
||||
currentBiome = biomeLinks[currentBiome] as BiomeId;
|
||||
} else if (i % 50 === 0) {
|
||||
currentBiome = BiomeId.END;
|
||||
} else {
|
||||
if (!(i % 50)) {
|
||||
currentBiome = BiomeId.END;
|
||||
} else {
|
||||
currentBiome = globalScene.generateRandomBiome(i);
|
||||
}
|
||||
currentBiome = globalScene.generateRandomBiome(i);
|
||||
}
|
||||
|
||||
currentArena = globalScene.newArena(currentBiome);
|
||||
|
@ -265,11 +265,11 @@ export function getRandomSpeciesByStarterCost(
|
||||
.filter(s => {
|
||||
const pokemonSpecies = getPokemonSpecies(s[0]);
|
||||
return (
|
||||
pokemonSpecies &&
|
||||
(!excludedSpecies || !excludedSpecies.includes(s[0])) &&
|
||||
(allowSubLegendary || !pokemonSpecies.subLegendary) &&
|
||||
(allowLegendary || !pokemonSpecies.legendary) &&
|
||||
(allowMythical || !pokemonSpecies.mythical)
|
||||
pokemonSpecies
|
||||
&& (!excludedSpecies || !excludedSpecies.includes(s[0]))
|
||||
&& (allowSubLegendary || !pokemonSpecies.subLegendary)
|
||||
&& (allowLegendary || !pokemonSpecies.legendary)
|
||||
&& (allowMythical || !pokemonSpecies.mythical)
|
||||
);
|
||||
})
|
||||
.map(s => [getPokemonSpecies(s[0]), s[1]]);
|
||||
@ -409,10 +409,10 @@ export async function applyModifierTypeToPlayerPokemon(
|
||||
const modifier = modType.newModifier(pokemon);
|
||||
const existing = globalScene.findModifier(
|
||||
(m): m is PokemonHeldItemModifier =>
|
||||
m instanceof PokemonHeldItemModifier &&
|
||||
m.type.id === modType.id &&
|
||||
m.pokemonId === pokemon.id &&
|
||||
m.matchType(modifier),
|
||||
m instanceof PokemonHeldItemModifier
|
||||
&& m.type.id === modType.id
|
||||
&& m.pokemonId === pokemon.id
|
||||
&& m.matchType(modifier),
|
||||
) as PokemonHeldItemModifier | undefined;
|
||||
|
||||
// At max stacks
|
||||
@ -650,8 +650,8 @@ export async function catchPokemon(
|
||||
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
|
||||
|
||||
if (
|
||||
speciesForm.abilityHidden &&
|
||||
(pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1
|
||||
speciesForm.abilityHidden
|
||||
&& (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1
|
||||
) {
|
||||
globalScene.validateAchv(achvs.HIDDEN_ABILITY);
|
||||
}
|
||||
@ -988,8 +988,8 @@ export async function addPokemonDataToDexAndValidateAchievements(pokemon: Player
|
||||
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
|
||||
|
||||
if (
|
||||
speciesForm.abilityHidden &&
|
||||
(pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1
|
||||
speciesForm.abilityHidden
|
||||
&& (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1
|
||||
) {
|
||||
globalScene.validateAchv(achvs.HIDDEN_ABILITY);
|
||||
}
|
||||
|
@ -86,8 +86,8 @@ export class PostSummonPhasePriorityQueue extends PhasePriorityQueue {
|
||||
this.queue.sort((phaseA: PostSummonPhase, phaseB: PostSummonPhase) => {
|
||||
if (phaseA.getPriority() === phaseB.getPriority()) {
|
||||
return (
|
||||
(phaseB.getPokemon().getEffectiveStat(Stat.SPD) - phaseA.getPokemon().getEffectiveStat(Stat.SPD)) *
|
||||
(isTrickRoom() ? -1 : 1)
|
||||
(phaseB.getPokemon().getEffectiveStat(Stat.SPD) - phaseA.getPokemon().getEffectiveStat(Stat.SPD))
|
||||
* (isTrickRoom() ? -1 : 1)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ export class SpeciesFormChange {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pokemon.species.forms.length) {
|
||||
if (pokemon.species.forms.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -82,10 +82,10 @@ export class SpeciesFormChangeItemTrigger extends SpeciesFormChangeTrigger {
|
||||
// Assume that if m has the `formChangeItem` property, then it is a PokemonFormChangeItemModifier
|
||||
const m = r as PokemonFormChangeItemModifier;
|
||||
return (
|
||||
"formChangeItem" in m &&
|
||||
m.pokemonId === pokemon.id &&
|
||||
m.formChangeItem === this.item &&
|
||||
m.active === this.active
|
||||
"formChangeItem" in m
|
||||
&& m.pokemonId === pokemon.id
|
||||
&& m.formChangeItem === this.item
|
||||
&& m.active === this.active
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -155,7 +155,7 @@ export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigge
|
||||
}
|
||||
|
||||
canChange(pokemon: Pokemon): boolean {
|
||||
return !!pokemon.moveset.filter(m => m.moveId === this.move).length === this.known;
|
||||
return pokemon.moveset.filter(m => m.moveId === this.move).length > 0 === this.known;
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,7 +182,7 @@ export class SpeciesFormChangePostMoveTrigger extends SpeciesFormChangeMoveTrigg
|
||||
description = i18next.t("pokemonEvolutions:forms.postMove");
|
||||
canChange(pokemon: Pokemon): boolean {
|
||||
return (
|
||||
pokemon.summonData && !!pokemon.getLastXMoves(1).filter(m => this.movePredicate(m.move)).length === this.used
|
||||
pokemon.summonData && pokemon.getLastXMoves(1).filter(m => this.movePredicate(m.move)).length > 0 === this.used
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -211,9 +211,10 @@ export class SpeciesDefaultFormMatchTrigger extends SpeciesFormChangeTrigger {
|
||||
|
||||
canChange(pokemon: Pokemon): boolean {
|
||||
return (
|
||||
this.formKey ===
|
||||
pokemon.species.forms[globalScene.getSpeciesFormIndex(pokemon.species, pokemon.gender, pokemon.getNature(), true)]
|
||||
.formKey
|
||||
this.formKey
|
||||
=== pokemon.species.forms[
|
||||
globalScene.getSpeciesFormIndex(pokemon.species, pokemon.gender, pokemon.getNature(), true)
|
||||
].formKey
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -259,10 +260,10 @@ export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger {
|
||||
const isAbilitySuppressed = pokemon.summonData.abilitySuppressed;
|
||||
|
||||
return (
|
||||
!isAbilitySuppressed &&
|
||||
!isWeatherSuppressed &&
|
||||
pokemon.hasAbility(this.ability) &&
|
||||
this.weathers.includes(currentWeather)
|
||||
!isAbilitySuppressed
|
||||
&& !isWeatherSuppressed
|
||||
&& pokemon.hasAbility(this.ability)
|
||||
&& this.weathers.includes(currentWeather)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -202,8 +202,8 @@ export abstract class PokemonSpeciesForm {
|
||||
}
|
||||
let starterSpeciesId = this.speciesId;
|
||||
while (
|
||||
!(starterSpeciesId in starterPassiveAbilities) ||
|
||||
!(formIndex in starterPassiveAbilities[starterSpeciesId])
|
||||
!(starterSpeciesId in starterPassiveAbilities)
|
||||
|| !(formIndex in starterPassiveAbilities[starterSpeciesId])
|
||||
) {
|
||||
if (pokemonPrevolutions.hasOwnProperty(starterSpeciesId)) {
|
||||
starterSpeciesId = pokemonPrevolutions[starterSpeciesId];
|
||||
@ -221,8 +221,8 @@ export abstract class PokemonSpeciesForm {
|
||||
|
||||
getLevelMoves(): LevelMoves {
|
||||
if (
|
||||
pokemonSpeciesFormLevelMoves.hasOwnProperty(this.speciesId) &&
|
||||
pokemonSpeciesFormLevelMoves[this.speciesId].hasOwnProperty(this.formIndex)
|
||||
pokemonSpeciesFormLevelMoves.hasOwnProperty(this.speciesId)
|
||||
&& pokemonSpeciesFormLevelMoves[this.speciesId].hasOwnProperty(this.formIndex)
|
||||
) {
|
||||
return pokemonSpeciesFormLevelMoves[this.speciesId][this.formIndex].slice(0);
|
||||
}
|
||||
@ -302,9 +302,9 @@ export abstract class PokemonSpeciesForm {
|
||||
|
||||
const formSpriteKey = this.getFormSpriteKey(formIndex);
|
||||
const showGenderDiffs =
|
||||
this.genderDiffs &&
|
||||
female &&
|
||||
![SpeciesFormKey.MEGA, SpeciesFormKey.GIGANTAMAX].includes(formSpriteKey as SpeciesFormKey);
|
||||
this.genderDiffs
|
||||
&& female
|
||||
&& ![SpeciesFormKey.MEGA, SpeciesFormKey.GIGANTAMAX].includes(formSpriteKey as SpeciesFormKey);
|
||||
|
||||
return `${showGenderDiffs ? "female__" : ""}${this.speciesId}${formSpriteKey ? `-${formSpriteKey}` : ""}`;
|
||||
}
|
||||
@ -425,7 +425,7 @@ export abstract class PokemonSpeciesForm {
|
||||
}
|
||||
let ret = speciesId.toString();
|
||||
const forms = getPokemonSpecies(speciesId).forms;
|
||||
if (forms.length) {
|
||||
if (forms.length > 0) {
|
||||
if (formIndex !== undefined && formIndex >= forms.length) {
|
||||
console.warn(
|
||||
`Attempted accessing form with index ${formIndex} of species ${getPokemonSpecies(speciesId).getName()} with only ${forms.length || 0} forms`,
|
||||
@ -487,8 +487,8 @@ export abstract class PokemonSpeciesForm {
|
||||
}
|
||||
}
|
||||
if (
|
||||
pokemonFormLevelMoves.hasOwnProperty(this.speciesId) &&
|
||||
pokemonFormLevelMoves[this.speciesId].hasOwnProperty(this.formIndex)
|
||||
pokemonFormLevelMoves.hasOwnProperty(this.speciesId)
|
||||
&& pokemonFormLevelMoves[this.speciesId].hasOwnProperty(this.formIndex)
|
||||
) {
|
||||
if (!pokemonFormLevelMoves[this.speciesId][this.formIndex].find(lm => lm[0] <= 5 && lm[1] === moveId)) {
|
||||
return false;
|
||||
@ -758,7 +758,7 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
|
||||
}
|
||||
|
||||
getName(formIndex?: number): string {
|
||||
if (formIndex !== undefined && this.forms.length) {
|
||||
if (formIndex !== undefined && this.forms.length > 0) {
|
||||
const form = this.forms[formIndex];
|
||||
let key: string | null;
|
||||
switch (form.formKey) {
|
||||
@ -846,9 +846,9 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
|
||||
? i18next.t(`battlePokemonForm:${toCamelCase(formKey)}`, { pokemonName: this.name })
|
||||
: i18next.t(`pokemonForm:battleForm.${toCamelCase(formKey)}`);
|
||||
} else if (
|
||||
region === Region.NORMAL ||
|
||||
(this.speciesId === SpeciesId.GALAR_DARMANITAN && formIndex > 0) ||
|
||||
this.speciesId === SpeciesId.PALDEA_TAUROS
|
||||
region === Region.NORMAL
|
||||
|| (this.speciesId === SpeciesId.GALAR_DARMANITAN && formIndex > 0)
|
||||
|| this.speciesId === SpeciesId.PALDEA_TAUROS
|
||||
) {
|
||||
// More special cases can be added here
|
||||
const i18key = `pokemonForm:${speciesName}${formText}`;
|
||||
@ -947,7 +947,7 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
|
||||
): SpeciesId {
|
||||
const prevolutionLevels = this.getPrevolutionLevels();
|
||||
|
||||
if (prevolutionLevels.length) {
|
||||
if (prevolutionLevels.length > 0) {
|
||||
for (let pl = prevolutionLevels.length - 1; pl >= 0; pl--) {
|
||||
const prevolutionLevel = prevolutionLevels[pl];
|
||||
if (level < prevolutionLevel[1]) {
|
||||
@ -958,11 +958,11 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
|
||||
|
||||
if (
|
||||
// If evolutions shouldn't happen, add more cases here :)
|
||||
!allowEvolving ||
|
||||
!pokemonEvolutions.hasOwnProperty(this.speciesId) ||
|
||||
(globalScene.currentBattle?.waveIndex === 20 &&
|
||||
globalScene.gameMode.isClassic &&
|
||||
globalScene.currentBattle.trainer)
|
||||
!allowEvolving
|
||||
|| !pokemonEvolutions.hasOwnProperty(this.speciesId)
|
||||
|| (globalScene.currentBattle?.waveIndex === 20
|
||||
&& globalScene.gameMode.isClassic
|
||||
&& globalScene.currentBattle.trainer)
|
||||
) {
|
||||
return this.speciesId;
|
||||
}
|
||||
@ -988,41 +988,39 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
|
||||
|
||||
if (!forTrainer && isRegionalEvolution) {
|
||||
evolutionChance = 0;
|
||||
} else {
|
||||
if (ev.wildDelay === SpeciesWildEvolutionDelay.NONE) {
|
||||
if (strength === PartyMemberStrength.STRONGER) {
|
||||
evolutionChance = 1;
|
||||
} else {
|
||||
const maxLevelDiff = this.getStrengthLevelDiff(strength); //The maximum distance from the evolution level tolerated for the mon to not evolve
|
||||
const minChance: number = 0.875 - 0.125 * strength;
|
||||
|
||||
evolutionChance = Math.min(
|
||||
minChance + easeInFunc(Math.min(level - ev.level, maxLevelDiff) / maxLevelDiff) * (1 - minChance),
|
||||
1,
|
||||
);
|
||||
}
|
||||
} else if (ev.wildDelay === SpeciesWildEvolutionDelay.NONE) {
|
||||
if (strength === PartyMemberStrength.STRONGER) {
|
||||
evolutionChance = 1;
|
||||
} else {
|
||||
const preferredMinLevel = Math.max(ev.level - 1 + ev.wildDelay! * this.getStrengthLevelDiff(strength), 1); // TODO: is the bang correct?
|
||||
let evolutionLevel = Math.max(ev.level > 1 ? ev.level : Math.floor(preferredMinLevel / 2), 1);
|
||||
|
||||
if (ev.level <= 1 && pokemonPrevolutions.hasOwnProperty(this.speciesId)) {
|
||||
const prevolutionLevel = pokemonEvolutions[pokemonPrevolutions[this.speciesId]].find(
|
||||
ev => ev.speciesId === this.speciesId,
|
||||
)!.level; // TODO: is the bang correct?
|
||||
if (prevolutionLevel > 1) {
|
||||
evolutionLevel = prevolutionLevel;
|
||||
}
|
||||
}
|
||||
const maxLevelDiff = this.getStrengthLevelDiff(strength); //The maximum distance from the evolution level tolerated for the mon to not evolve
|
||||
const minChance: number = 0.875 - 0.125 * strength;
|
||||
|
||||
evolutionChance = Math.min(
|
||||
0.65 * easeInFunc(Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel) / preferredMinLevel) +
|
||||
0.35 *
|
||||
easeOutFunc(
|
||||
Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel * 2.5) / (preferredMinLevel * 2.5),
|
||||
),
|
||||
minChance + easeInFunc(Math.min(level - ev.level, maxLevelDiff) / maxLevelDiff) * (1 - minChance),
|
||||
1,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const preferredMinLevel = Math.max(ev.level - 1 + ev.wildDelay! * this.getStrengthLevelDiff(strength), 1); // TODO: is the bang correct?
|
||||
let evolutionLevel = Math.max(ev.level > 1 ? ev.level : Math.floor(preferredMinLevel / 2), 1);
|
||||
|
||||
if (ev.level <= 1 && pokemonPrevolutions.hasOwnProperty(this.speciesId)) {
|
||||
const prevolutionLevel = pokemonEvolutions[pokemonPrevolutions[this.speciesId]].find(
|
||||
ev => ev.speciesId === this.speciesId,
|
||||
)!.level; // TODO: is the bang correct?
|
||||
if (prevolutionLevel > 1) {
|
||||
evolutionLevel = prevolutionLevel;
|
||||
}
|
||||
}
|
||||
|
||||
evolutionChance = Math.min(
|
||||
0.65 * easeInFunc(Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel) / preferredMinLevel)
|
||||
+ 0.35
|
||||
* easeOutFunc(
|
||||
Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel * 2.5) / (preferredMinLevel * 2.5),
|
||||
),
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
//TODO: Adjust templates and delays so we don't have to hardcode it
|
||||
@ -1097,9 +1095,9 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
|
||||
for (const p of allEvolvingPokemon) {
|
||||
for (const e of pokemonEvolutions[p]) {
|
||||
if (
|
||||
e.speciesId === this.speciesId &&
|
||||
(!this.forms.length || !e.evoFormKey || e.evoFormKey === this.forms[this.formIndex].formKey) &&
|
||||
prevolutionLevels.every(pe => pe[0] !== Number.parseInt(p))
|
||||
e.speciesId === this.speciesId
|
||||
&& (this.forms.length === 0 || !e.evoFormKey || e.evoFormKey === this.forms[this.formIndex].formKey)
|
||||
&& prevolutionLevels.every(pe => pe[0] !== Number.parseInt(p))
|
||||
) {
|
||||
const speciesId = Number.parseInt(p) as SpeciesId;
|
||||
const level = e.level;
|
||||
@ -1135,9 +1133,9 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
|
||||
prevolutionLevels[l][0],
|
||||
Math.min(
|
||||
Math.max(
|
||||
evolution?.level! +
|
||||
Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5) -
|
||||
1,
|
||||
evolution?.level!
|
||||
+ Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5)
|
||||
- 1,
|
||||
2,
|
||||
evolution?.level!,
|
||||
),
|
||||
@ -1146,15 +1144,13 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
|
||||
]); // TODO: are those bangs correct?
|
||||
}
|
||||
const lastPrevolutionLevel = ret[prevolutionLevels.length - 1][1];
|
||||
const evolution = pokemonEvolutions[prevolutionLevels[prevolutionLevels.length - 1][0]].find(
|
||||
e => e.speciesId === this.speciesId,
|
||||
);
|
||||
const evolution = pokemonEvolutions[prevolutionLevels.at(-1)![0]].find(e => e.speciesId === this.speciesId);
|
||||
ret.push([
|
||||
this.speciesId,
|
||||
Math.min(
|
||||
Math.max(
|
||||
lastPrevolutionLevel +
|
||||
Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5),
|
||||
lastPrevolutionLevel
|
||||
+ Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5),
|
||||
lastPrevolutionLevel + 1,
|
||||
evolution?.level!,
|
||||
),
|
||||
@ -1176,16 +1172,16 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
|
||||
const mythical = this.mythical;
|
||||
return species => {
|
||||
return (
|
||||
(subLegendary ||
|
||||
legendary ||
|
||||
mythical ||
|
||||
(pokemonEvolutions.hasOwnProperty(species.speciesId) === hasEvolution &&
|
||||
pokemonPrevolutions.hasOwnProperty(species.speciesId) === hasPrevolution)) &&
|
||||
species.subLegendary === subLegendary &&
|
||||
species.legendary === legendary &&
|
||||
species.mythical === mythical &&
|
||||
(this.isTrainerForbidden() || !species.isTrainerForbidden()) &&
|
||||
species.speciesId !== SpeciesId.DITTO
|
||||
(subLegendary
|
||||
|| legendary
|
||||
|| mythical
|
||||
|| (pokemonEvolutions.hasOwnProperty(species.speciesId) === hasEvolution
|
||||
&& pokemonPrevolutions.hasOwnProperty(species.speciesId) === hasPrevolution))
|
||||
&& species.subLegendary === subLegendary
|
||||
&& species.legendary === legendary
|
||||
&& species.mythical === mythical
|
||||
&& (this.isTrainerForbidden() || !species.isTrainerForbidden())
|
||||
&& species.speciesId !== SpeciesId.DITTO
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -1202,19 +1198,19 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
|
||||
}
|
||||
|
||||
getFormSpriteKey(formIndex?: number) {
|
||||
if (this.forms.length && formIndex !== undefined && formIndex >= this.forms.length) {
|
||||
if (this.forms.length > 0 && formIndex !== undefined && formIndex >= this.forms.length) {
|
||||
console.warn(
|
||||
`Attempted accessing form with index ${formIndex} of species ${this.getName()} with only ${this.forms.length || 0} forms`,
|
||||
);
|
||||
formIndex = Math.min(formIndex, this.forms.length - 1);
|
||||
}
|
||||
return this.forms?.length ? this.forms[formIndex || 0].getFormSpriteKey() : "";
|
||||
return this.forms?.length > 0 ? this.forms[formIndex || 0].getFormSpriteKey() : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@linkcode bigint} corresponding to the maximum unlocks possible for this species,
|
||||
* Generates a {@linkcode BigInt} corresponding to the maximum unlocks possible for this species,
|
||||
* taking into account if the species has a male/female gender, and which variants are implemented.
|
||||
* @returns {@linkcode bigint} Maximum unlocks, can be compared with {@linkcode DexEntry.caughtAttr}.
|
||||
* @returns The maximum unlocks for the species as a `BigInt`; can be compared with {@linkcode DexEntry.caughtAttr}.
|
||||
*/
|
||||
getFullUnlocksData(): bigint {
|
||||
let caughtAttr = 0n;
|
||||
|
@ -53,8 +53,8 @@ const SEASONAL_WEIGHT_MULTIPLIER = 15;
|
||||
//#region Common Messages
|
||||
|
||||
const commonSplashMessages = [
|
||||
...Array(BATTLES_WON_WEIGHT_MULTIPLIER).fill("battlesWon"),
|
||||
...Array(POKEMON_NAMES_WEIGHT_MULTIPLIER).fill("underratedPokemon"),
|
||||
...new Array(BATTLES_WON_WEIGHT_MULTIPLIER).fill("battlesWon"),
|
||||
...new Array(POKEMON_NAMES_WEIGHT_MULTIPLIER).fill("underratedPokemon"),
|
||||
"joinTheDiscord",
|
||||
"infiniteLevels",
|
||||
"everythingIsStackable",
|
||||
@ -333,7 +333,7 @@ export function getSplashMessages(): string[] {
|
||||
if (now >= startDate && now <= endDate) {
|
||||
console.log(`Adding ${messages.length} ${name} splash messages (weight: x${SEASONAL_WEIGHT_MULTIPLIER})`);
|
||||
for (const message of messages) {
|
||||
const weightedMessage = Array(SEASONAL_WEIGHT_MULTIPLIER).fill(message);
|
||||
const weightedMessage = new Array(SEASONAL_WEIGHT_MULTIPLIER).fill(message);
|
||||
splashMessages.push(...weightedMessage);
|
||||
}
|
||||
}
|
||||
|
@ -59,12 +59,12 @@ export function getStatusEffectObtainText(
|
||||
|
||||
if (!sourceText) {
|
||||
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.obtain` as ParseKeys;
|
||||
return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
|
||||
return i18next.t(i18nKey, { pokemonNameWithAffix });
|
||||
}
|
||||
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.obtainSource` as ParseKeys;
|
||||
return i18next.t(i18nKey, {
|
||||
pokemonNameWithAffix: pokemonNameWithAffix,
|
||||
sourceText: sourceText,
|
||||
pokemonNameWithAffix,
|
||||
sourceText,
|
||||
});
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ export function getStatusEffectActivationText(statusEffect: StatusEffect, pokemo
|
||||
return "";
|
||||
}
|
||||
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.activation` as ParseKeys;
|
||||
return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
|
||||
return i18next.t(i18nKey, { pokemonNameWithAffix });
|
||||
}
|
||||
|
||||
export function getStatusEffectOverlapText(statusEffect: StatusEffect, pokemonNameWithAffix: string): string {
|
||||
@ -81,7 +81,7 @@ export function getStatusEffectOverlapText(statusEffect: StatusEffect, pokemonNa
|
||||
return "";
|
||||
}
|
||||
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.overlap` as ParseKeys;
|
||||
return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
|
||||
return i18next.t(i18nKey, { pokemonNameWithAffix });
|
||||
}
|
||||
|
||||
export function getStatusEffectHealText(statusEffect: StatusEffect, pokemonNameWithAffix: string): string {
|
||||
@ -89,7 +89,7 @@ export function getStatusEffectHealText(statusEffect: StatusEffect, pokemonNameW
|
||||
return "";
|
||||
}
|
||||
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.heal` as ParseKeys;
|
||||
return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
|
||||
return i18next.t(i18nKey, { pokemonNameWithAffix });
|
||||
}
|
||||
|
||||
export function getStatusEffectDescriptor(statusEffect: StatusEffect): string {
|
||||
|
@ -65,14 +65,10 @@ export class Terrain {
|
||||
// Psychic terrain will only cancel a move if it:
|
||||
return (
|
||||
// ... is neither spread nor field-targeted,
|
||||
!isFieldTargeted(move) &&
|
||||
!isSpreadMove(move) &&
|
||||
// .. has positive final priority,
|
||||
move.getPriority(user) > 0 &&
|
||||
// ...and is targeting at least 1 grounded opponent
|
||||
user
|
||||
.getOpponents(true)
|
||||
.some(o => targets.includes(o.getBattlerIndex()) && o.isGrounded())
|
||||
!isFieldTargeted(move)
|
||||
&& !isSpreadMove(move) // .. has positive final priority,
|
||||
&& move.getPriority(user) > 0 // ...and is targeting at least 1 grounded opponent
|
||||
&& user.getOpponents(true).some(o => targets.includes(o.getBattlerIndex()) && o.isGrounded())
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -140,8 +140,8 @@ export class TrainerConfig {
|
||||
this.victoryBgm = "victory_trainer";
|
||||
this.partyTemplates = [trainerPartyTemplates.TWO_AVG];
|
||||
this.speciesFilter = species =>
|
||||
(allowLegendaries || (!species.legendary && !species.subLegendary && !species.mythical)) &&
|
||||
!species.isTrainerForbidden();
|
||||
(allowLegendaries || (!species.legendary && !species.subLegendary && !species.mythical))
|
||||
&& !species.isTrainerForbidden();
|
||||
}
|
||||
|
||||
getKey(): string {
|
||||
@ -816,8 +816,8 @@ export class TrainerConfig {
|
||||
if (this.nameFemale) {
|
||||
// Check if the variant is either female or this is for the partner in a double battle
|
||||
if (
|
||||
variant === TrainerVariant.FEMALE ||
|
||||
(variant === TrainerVariant.DOUBLE && trainerSlot === TrainerSlot.TRAINER_PARTNER)
|
||||
variant === TrainerVariant.FEMALE
|
||||
|| (variant === TrainerVariant.DOUBLE && trainerSlot === TrainerSlot.TRAINER_PARTNER)
|
||||
) {
|
||||
return this.nameFemale;
|
||||
}
|
||||
@ -1088,8 +1088,8 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
s =>
|
||||
[s.ability1, s.ability2, s.abilityHidden].some(
|
||||
a =>
|
||||
!!a &&
|
||||
[
|
||||
!!a
|
||||
&& [
|
||||
AbilityId.WHITE_SMOKE,
|
||||
AbilityId.GLUTTONY,
|
||||
AbilityId.HONEY_GATHER,
|
||||
@ -1102,8 +1102,8 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
AbilityId.SUPERSWEET_SYRUP,
|
||||
AbilityId.HOSPITALITY,
|
||||
].includes(a),
|
||||
) ||
|
||||
s
|
||||
)
|
||||
|| s
|
||||
.getLevelMoves()
|
||||
.some(plm =>
|
||||
[MoveId.SOFT_BOILED, MoveId.SPORE, MoveId.MILK_DRINK, MoveId.OVERHEAT, MoveId.TEATIME].includes(plm[1]),
|
||||
@ -1566,8 +1566,8 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
s =>
|
||||
[s.ability1, s.ability2, s.abilityHidden].some(
|
||||
a =>
|
||||
!!a &&
|
||||
[
|
||||
!!a
|
||||
&& [
|
||||
AbilityId.DRIZZLE,
|
||||
AbilityId.SWIFT_SWIM,
|
||||
AbilityId.HYDRATION,
|
||||
@ -4642,9 +4642,9 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
2,
|
||||
getSpeciesFilterRandomPartyMemberFunc(
|
||||
(species: PokemonSpecies) =>
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId) &&
|
||||
!pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
|
||||
species.baseTotal >= 450,
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId)
|
||||
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId)
|
||||
&& species.baseTotal >= 450,
|
||||
),
|
||||
),
|
||||
[TrainerType.RIVAL_3]: new TrainerConfig(++t)
|
||||
@ -4717,9 +4717,9 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
2,
|
||||
getSpeciesFilterRandomPartyMemberFunc(
|
||||
(species: PokemonSpecies) =>
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId) &&
|
||||
!pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
|
||||
species.baseTotal >= 450,
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId)
|
||||
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId)
|
||||
&& species.baseTotal >= 450,
|
||||
),
|
||||
)
|
||||
.setSpeciesFilter(species => species.baseTotal >= 540),
|
||||
@ -4798,9 +4798,9 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
2,
|
||||
getSpeciesFilterRandomPartyMemberFunc(
|
||||
(species: PokemonSpecies) =>
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId) &&
|
||||
!pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
|
||||
species.baseTotal >= 450,
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId)
|
||||
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId)
|
||||
&& species.baseTotal >= 450,
|
||||
),
|
||||
)
|
||||
.setSpeciesFilter(species => species.baseTotal >= 540)
|
||||
@ -4880,9 +4880,9 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
2,
|
||||
getSpeciesFilterRandomPartyMemberFunc(
|
||||
(species: PokemonSpecies) =>
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId) &&
|
||||
!pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
|
||||
species.baseTotal >= 450,
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId)
|
||||
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId)
|
||||
&& species.baseTotal >= 450,
|
||||
),
|
||||
)
|
||||
.setSpeciesFilter(species => species.baseTotal >= 540)
|
||||
@ -4976,9 +4976,9 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
2,
|
||||
getSpeciesFilterRandomPartyMemberFunc(
|
||||
(species: PokemonSpecies) =>
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId) &&
|
||||
!pokemonPrevolutions.hasOwnProperty(species.speciesId) &&
|
||||
species.baseTotal >= 450,
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId)
|
||||
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId)
|
||||
&& species.baseTotal >= 450,
|
||||
),
|
||||
)
|
||||
.setSpeciesFilter(species => species.baseTotal >= 540)
|
||||
|
@ -286,7 +286,7 @@ export function getTypeDamageMultiplierColor(
|
||||
case 0.5:
|
||||
return "#FE8E00";
|
||||
case 1:
|
||||
return undefined;
|
||||
return;
|
||||
case 2:
|
||||
return "#4AA500";
|
||||
case 4:
|
||||
@ -306,7 +306,7 @@ export function getTypeDamageMultiplierColor(
|
||||
case 0.5:
|
||||
return "#0093FF";
|
||||
case 1:
|
||||
return undefined;
|
||||
return;
|
||||
case 2:
|
||||
return "#FE8E00";
|
||||
case 4:
|
||||
|
@ -386,5 +386,5 @@ export function getRandomWeatherType(arena: Arena): WeatherType {
|
||||
}
|
||||
}
|
||||
|
||||
return weatherPool.length ? weatherPool[0].weatherType : WeatherType.NONE;
|
||||
return weatherPool.length > 0 ? weatherPool[0].weatherType : WeatherType.NONE;
|
||||
}
|
||||
|
@ -10,4 +10,4 @@ export const AbilityAttr = Object.freeze({
|
||||
ABILITY_HIDDEN: 4,
|
||||
});
|
||||
|
||||
export type AbilityAttr = ObjectValues<typeof AbilityAttr>;
|
||||
export type AbilityAttr = ObjectValues<typeof AbilityAttr>;
|
||||
|
@ -1,5 +1,5 @@
|
||||
export enum AiType {
|
||||
RANDOM,
|
||||
SMART_RANDOM,
|
||||
SMART
|
||||
SMART,
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
export enum ArenaTagSide {
|
||||
BOTH,
|
||||
PLAYER,
|
||||
ENEMY
|
||||
ENEMY,
|
||||
}
|
||||
|
@ -1,13 +1,10 @@
|
||||
import type { ArenaTagTypeMap } from "#data/arena-tag";
|
||||
import type { NonSerializableArenaTagType, SerializableArenaTagType } from "#types/arena-tags";
|
||||
|
||||
/**
|
||||
* Enum representing all different types of {@linkcode ArenaTag}s.
|
||||
* @privateRemarks
|
||||
* ⚠️ 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}
|
||||
*/
|
||||
*/
|
||||
export enum ArenaTagType {
|
||||
NONE = "NONE",
|
||||
MUD_SPORT = "MUD_SPORT",
|
||||
@ -36,5 +33,5 @@ export enum ArenaTagType {
|
||||
WATER_FIRE_PLEDGE = "WATER_FIRE_PLEDGE",
|
||||
GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE",
|
||||
FAIRY_LOCK = "FAIRY_LOCK",
|
||||
NEUTRALIZING_GAS = "NEUTRALIZING_GAS"
|
||||
NEUTRALIZING_GAS = "NEUTRALIZING_GAS",
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
export enum BattleSpec {
|
||||
DEFAULT,
|
||||
FINAL_BOSS
|
||||
DEFAULT,
|
||||
FINAL_BOSS,
|
||||
}
|
||||
|
@ -3,5 +3,5 @@ export enum BattleStyle {
|
||||
/** Display option to switch active pokemon at battle start. */
|
||||
SWITCH,
|
||||
/** Hide option to switch active pokemon at battle start. */
|
||||
SET
|
||||
SET,
|
||||
}
|
||||
|
@ -2,5 +2,5 @@ export enum BattleType {
|
||||
WILD,
|
||||
TRAINER,
|
||||
CLEAR,
|
||||
MYSTERY_ENCOUNTER
|
||||
MYSTERY_ENCOUNTER,
|
||||
}
|
||||
|
@ -7,5 +7,5 @@ export enum BattlerIndex {
|
||||
PLAYER,
|
||||
PLAYER_2,
|
||||
ENEMY,
|
||||
ENEMY_2
|
||||
ENEMY_2,
|
||||
}
|
||||
|
@ -9,5 +9,5 @@ export enum BerryType {
|
||||
SALAC,
|
||||
LANSAT,
|
||||
STARF,
|
||||
LEPPA
|
||||
LEPPA,
|
||||
}
|
||||
|
@ -34,5 +34,5 @@ export enum BiomeId {
|
||||
SNOWY_FOREST,
|
||||
ISLAND = 40,
|
||||
LABORATORY,
|
||||
END = 50
|
||||
END = 50,
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
export enum Button {
|
||||
UP,
|
||||
DOWN,
|
||||
LEFT,
|
||||
RIGHT,
|
||||
SUBMIT,
|
||||
ACTION,
|
||||
CANCEL,
|
||||
MENU,
|
||||
STATS,
|
||||
CYCLE_SHINY,
|
||||
CYCLE_FORM,
|
||||
CYCLE_GENDER,
|
||||
CYCLE_ABILITY,
|
||||
CYCLE_NATURE,
|
||||
CYCLE_TERA,
|
||||
SPEED_UP,
|
||||
SLOW_DOWN,
|
||||
UP,
|
||||
DOWN,
|
||||
LEFT,
|
||||
RIGHT,
|
||||
SUBMIT,
|
||||
ACTION,
|
||||
CANCEL,
|
||||
MENU,
|
||||
STATS,
|
||||
CYCLE_SHINY,
|
||||
CYCLE_FORM,
|
||||
CYCLE_GENDER,
|
||||
CYCLE_ABILITY,
|
||||
CYCLE_NATURE,
|
||||
CYCLE_TERA,
|
||||
SPEED_UP,
|
||||
SLOW_DOWN,
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
export enum Challenges {
|
||||
SINGLE_GENERATION,
|
||||
SINGLE_TYPE,
|
||||
LOWER_MAX_STARTER_COST,
|
||||
LOWER_STARTER_POINTS,
|
||||
FRESH_START,
|
||||
INVERSE_BATTLE,
|
||||
FLIP_STAT,
|
||||
LIMITED_CATCH,
|
||||
LIMITED_SUPPORT,
|
||||
HARDCORE,
|
||||
SINGLE_GENERATION,
|
||||
SINGLE_TYPE,
|
||||
LOWER_MAX_STARTER_COST,
|
||||
LOWER_STARTER_POINTS,
|
||||
FRESH_START,
|
||||
INVERSE_BATTLE,
|
||||
FLIP_STAT,
|
||||
LIMITED_CATCH,
|
||||
LIMITED_SUPPORT,
|
||||
HARDCORE,
|
||||
}
|
||||
|
@ -1,83 +1,83 @@
|
||||
export enum Color {
|
||||
WHITE = "#ffffff",
|
||||
OFF_WHITE = "#f8f8f8",
|
||||
LIGHT_GREY = "#a0a0a0",
|
||||
GREY = "#484848",
|
||||
DARK_GREY = "#404040",
|
||||
PINK = "#f89890",
|
||||
RED = "#e13d3d",
|
||||
RED2 = "#e70808",
|
||||
REDORANGE = "#d64b00",
|
||||
ORANGE = "#f8b050",
|
||||
LIGHT_YELLOW = "#e8e8a8",
|
||||
YELLOW = "#ccbe00",
|
||||
DARK_YELLOW = "#a68e17",
|
||||
GREEN = "#78c850",
|
||||
BLUE = "#40c8f8",
|
||||
COMMON = "#ffffff",
|
||||
GREAT = "#3890f8",
|
||||
ULTRA = "#f8d038",
|
||||
ROGUE = "#d52929",
|
||||
MASTER = "#e020c0",
|
||||
LUXURY = "#e64a18"
|
||||
WHITE = "#ffffff",
|
||||
OFF_WHITE = "#f8f8f8",
|
||||
LIGHT_GREY = "#a0a0a0",
|
||||
GREY = "#484848",
|
||||
DARK_GREY = "#404040",
|
||||
PINK = "#f89890",
|
||||
RED = "#e13d3d",
|
||||
RED2 = "#e70808",
|
||||
REDORANGE = "#d64b00",
|
||||
ORANGE = "#f8b050",
|
||||
LIGHT_YELLOW = "#e8e8a8",
|
||||
YELLOW = "#ccbe00",
|
||||
DARK_YELLOW = "#a68e17",
|
||||
GREEN = "#78c850",
|
||||
BLUE = "#40c8f8",
|
||||
COMMON = "#ffffff",
|
||||
GREAT = "#3890f8",
|
||||
ULTRA = "#f8d038",
|
||||
ROGUE = "#d52929",
|
||||
MASTER = "#e020c0",
|
||||
LUXURY = "#e64a18",
|
||||
}
|
||||
|
||||
export enum TypeColor {
|
||||
NORMAL = "#ADA594",
|
||||
FIGHTING = "#A55239",
|
||||
FLYING = "#9CADF7",
|
||||
POISON = "#9141CB",
|
||||
GROUND = "#AE7A3B",
|
||||
ROCK = "#BDA55A",
|
||||
BUG = "#ADBD21",
|
||||
GHOST = "#6363B5",
|
||||
STEEL = "#81A6BE",
|
||||
FIRE = "#F75231",
|
||||
WATER = "#399CFF",
|
||||
GRASS = "#7BCE52",
|
||||
ELECTRIC = "#FFC631",
|
||||
PSYCHIC = "#EF4179",
|
||||
ICE = "#5ACEE7",
|
||||
DRAGON = "#7B63E7",
|
||||
DARK = "#735A4A",
|
||||
FAIRY = "#EF70EF",
|
||||
NORMAL = "#ADA594",
|
||||
FIGHTING = "#A55239",
|
||||
FLYING = "#9CADF7",
|
||||
POISON = "#9141CB",
|
||||
GROUND = "#AE7A3B",
|
||||
ROCK = "#BDA55A",
|
||||
BUG = "#ADBD21",
|
||||
GHOST = "#6363B5",
|
||||
STEEL = "#81A6BE",
|
||||
FIRE = "#F75231",
|
||||
WATER = "#399CFF",
|
||||
GRASS = "#7BCE52",
|
||||
ELECTRIC = "#FFC631",
|
||||
PSYCHIC = "#EF4179",
|
||||
ICE = "#5ACEE7",
|
||||
DRAGON = "#7B63E7",
|
||||
DARK = "#735A4A",
|
||||
FAIRY = "#EF70EF",
|
||||
}
|
||||
|
||||
export enum TypeShadow {
|
||||
NORMAL = "#574F4A",
|
||||
FIGHTING = "#4E637C",
|
||||
FLYING = "#4E637C",
|
||||
POISON = "#352166",
|
||||
GROUND = "#572D1E",
|
||||
ROCK = "#5F442D",
|
||||
BUG = "#5F5010",
|
||||
GHOST = "#323D5B",
|
||||
STEEL = "#415C5F",
|
||||
FIRE = "#7C1818",
|
||||
WATER = "#1C4E80",
|
||||
GRASS = "#4F6729",
|
||||
ELECTRIC = "#804618",
|
||||
PSYCHIC = "#782155",
|
||||
ICE = "#2D5C74",
|
||||
DRAGON = "#313874",
|
||||
DARK = "#392725",
|
||||
FAIRY = "#663878",
|
||||
NORMAL = "#574F4A",
|
||||
FIGHTING = "#4E637C",
|
||||
FLYING = "#4E637C",
|
||||
POISON = "#352166",
|
||||
GROUND = "#572D1E",
|
||||
ROCK = "#5F442D",
|
||||
BUG = "#5F5010",
|
||||
GHOST = "#323D5B",
|
||||
STEEL = "#415C5F",
|
||||
FIRE = "#7C1818",
|
||||
WATER = "#1C4E80",
|
||||
GRASS = "#4F6729",
|
||||
ELECTRIC = "#804618",
|
||||
PSYCHIC = "#782155",
|
||||
ICE = "#2D5C74",
|
||||
DRAGON = "#313874",
|
||||
DARK = "#392725",
|
||||
FAIRY = "#663878",
|
||||
}
|
||||
|
||||
export enum ShadowColor {
|
||||
GREY = "#636363",
|
||||
PURPLE = "#6b5a73",
|
||||
LIGHT_GREY = "#d0d0c8",
|
||||
BROWN = "#69402a",
|
||||
PINK = "#fca2a2",
|
||||
BRIGHT_RED = "#f83018",
|
||||
RED = "#984038",
|
||||
MAROON = "#632929",
|
||||
GREEN = "#306850",
|
||||
BLUE = "#006090",
|
||||
LIGHT_YELLOW = "#ded6b5",
|
||||
YELLOW = "#ebd773",
|
||||
DARK_YELLOW = "#a0a060",
|
||||
ORANGE = "#c07800",
|
||||
LIGHT_ORANGE = "#ffbd73",
|
||||
GREY = "#636363",
|
||||
PURPLE = "#6b5a73",
|
||||
LIGHT_GREY = "#d0d0c8",
|
||||
BROWN = "#69402a",
|
||||
PINK = "#fca2a2",
|
||||
BRIGHT_RED = "#f83018",
|
||||
RED = "#984038",
|
||||
MAROON = "#632929",
|
||||
GREEN = "#306850",
|
||||
BLUE = "#006090",
|
||||
LIGHT_YELLOW = "#ded6b5",
|
||||
YELLOW = "#ebd773",
|
||||
DARK_YELLOW = "#a0a060",
|
||||
ORANGE = "#c07800",
|
||||
LIGHT_ORANGE = "#ffbd73",
|
||||
}
|
||||
|
@ -3,5 +3,5 @@ export enum Command {
|
||||
BALL,
|
||||
POKEMON,
|
||||
RUN,
|
||||
TERA
|
||||
TERA,
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
export enum Device {
|
||||
GAMEPAD,
|
||||
KEYBOARD,
|
||||
GAMEPAD,
|
||||
KEYBOARD,
|
||||
}
|
||||
|
@ -5,5 +5,5 @@ export enum DropDownColumn {
|
||||
CAUGHT,
|
||||
UNLOCKS,
|
||||
MISC,
|
||||
SORT
|
||||
SORT,
|
||||
}
|
||||
|
@ -3,5 +3,5 @@
|
||||
*/
|
||||
// TODO: We currently assume these are in order
|
||||
export enum DynamicPhaseType {
|
||||
POST_SUMMON
|
||||
POST_SUMMON,
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
export enum EaseType {
|
||||
NONE,
|
||||
LINEAR = "Linear",
|
||||
QUADRATIC = "Quad",
|
||||
CUBIC = "Cubic",
|
||||
QUARTIC = "Quart",
|
||||
QUINTIC = "Quint",
|
||||
SINUSOIDAL = "Sine",
|
||||
EXPONENTIAL = "Expo",
|
||||
CIRCULAR = "Circ",
|
||||
ELASTIC = "Elastic",
|
||||
BACK = "Back",
|
||||
BOUNCE = "Bounce",
|
||||
STEPPED = "Stepped",
|
||||
NONE,
|
||||
LINEAR = "Linear",
|
||||
QUADRATIC = "Quad",
|
||||
CUBIC = "Cubic",
|
||||
QUARTIC = "Quart",
|
||||
QUINTIC = "Quint",
|
||||
SINUSOIDAL = "Sine",
|
||||
EXPONENTIAL = "Expo",
|
||||
CIRCULAR = "Circ",
|
||||
ELASTIC = "Elastic",
|
||||
BACK = "Back",
|
||||
BOUNCE = "Bounce",
|
||||
STEPPED = "Stepped",
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
export enum EggSourceType {
|
||||
GACHA_MOVE,
|
||||
GACHA_LEGENDARY,
|
||||
GACHA_SHINY,
|
||||
SAME_SPECIES_EGG,
|
||||
EVENT
|
||||
GACHA_MOVE,
|
||||
GACHA_LEGENDARY,
|
||||
GACHA_SHINY,
|
||||
SAME_SPECIES_EGG,
|
||||
EVENT,
|
||||
}
|
||||
|
@ -2,5 +2,5 @@ export enum EggTier {
|
||||
COMMON,
|
||||
RARE,
|
||||
EPIC,
|
||||
LEGENDARY
|
||||
LEGENDARY,
|
||||
}
|
||||
|
@ -7,5 +7,5 @@ export enum EncounterAnim {
|
||||
MAGMA_BG,
|
||||
MAGMA_SPOUT,
|
||||
SMOKESCREEN,
|
||||
DANCE
|
||||
DANCE,
|
||||
}
|
||||
|
@ -7,5 +7,5 @@ export enum ExpGainsSpeed {
|
||||
/** Faster speed. */
|
||||
FASTER,
|
||||
/** Skip gaining exp animation. */
|
||||
SKIP
|
||||
SKIP,
|
||||
}
|
||||
|
@ -5,5 +5,5 @@ export enum ExpNotification {
|
||||
/** Display smaller flyout showing level gained on gaining a new level. */
|
||||
ONLY_LEVEL_UP,
|
||||
/** Do not show any flyouts for EXP gains or levelups. */
|
||||
SKIP
|
||||
SKIP,
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
export enum FieldPosition {
|
||||
CENTER,
|
||||
LEFT,
|
||||
RIGHT
|
||||
RIGHT,
|
||||
}
|
||||
|
@ -18,5 +18,5 @@ export enum ClassicFixedBossWaves {
|
||||
ELITE_FOUR_3 = 186,
|
||||
ELITE_FOUR_4 = 188,
|
||||
CHAMPION = 190,
|
||||
RIVAL_6 = 195
|
||||
RIVAL_6 = 195,
|
||||
}
|
||||
|
@ -96,8 +96,8 @@ export enum FormChangeItem {
|
||||
DRACO_PLATE,
|
||||
DREAD_PLATE,
|
||||
PIXIE_PLATE,
|
||||
BLANK_PLATE,// TODO: Find a potential use for this
|
||||
LEGEND_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
|
||||
FIGHTING_MEMORY,
|
||||
FLYING_MEMORY,
|
||||
POISON_MEMORY,
|
||||
@ -115,5 +115,5 @@ export enum FormChangeItem {
|
||||
DRAGON_MEMORY,
|
||||
DARK_MEMORY,
|
||||
FAIRY_MEMORY,
|
||||
NORMAL_MEMORY
|
||||
NORMAL_MEMORY,
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { ObjectValues } from "#types/type-helpers";
|
||||
|
||||
export const GachaType = Object.freeze({
|
||||
MOVE: 0,
|
||||
LEGENDARY: 1,
|
||||
SHINY: 2
|
||||
MOVE: 0,
|
||||
LEGENDARY: 1,
|
||||
SHINY: 2,
|
||||
});
|
||||
|
||||
export type GachaType = ObjectValues<typeof GachaType>;
|
||||
|
@ -7,5 +7,5 @@ export enum GameDataType {
|
||||
SETTINGS,
|
||||
TUTORIALS,
|
||||
SEEN_DIALOGUES,
|
||||
RUN_HISTORY
|
||||
RUN_HISTORY,
|
||||
}
|
||||
|
@ -3,5 +3,5 @@ export enum GameModes {
|
||||
ENDLESS,
|
||||
SPLICED_ENDLESS,
|
||||
DAILY,
|
||||
CHALLENGE
|
||||
CHALLENGE,
|
||||
}
|
||||
|
@ -11,5 +11,5 @@ export enum HitResult {
|
||||
INDIRECT,
|
||||
IMMUNE,
|
||||
CONFUSION,
|
||||
INDIRECT_KO
|
||||
INDIRECT_KO,
|
||||
}
|
||||
|
@ -3,6 +3,6 @@ export enum LearnMoveSituation {
|
||||
LEVEL_UP,
|
||||
RELEARN,
|
||||
EVOLUTION,
|
||||
EVOLUTION_FUSED,// If fusionSpecies has Evolved
|
||||
EVOLUTION_FUSED_BASE
|
||||
EVOLUTION_FUSED, // If fusionSpecies has Evolved
|
||||
EVOLUTION_FUSED_BASE,
|
||||
}
|
||||
|
@ -4,5 +4,5 @@ export enum LearnMoveType {
|
||||
/** For learning a move via Memory Mushroom */
|
||||
MEMORY,
|
||||
/** For learning a move via TM */
|
||||
TM
|
||||
TM,
|
||||
}
|
||||
|
@ -3,5 +3,5 @@ export enum ModifierPoolType {
|
||||
WILD,
|
||||
TRAINER,
|
||||
ENEMY_BUFF,
|
||||
DAILY_STARTER
|
||||
DAILY_STARTER,
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
export enum MoneyFormat {
|
||||
NORMAL,
|
||||
ABBREVIATED
|
||||
ABBREVIATED,
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
export enum AnimFrameTarget {
|
||||
USER,
|
||||
TARGET,
|
||||
GRAPHIC
|
||||
GRAPHIC,
|
||||
}
|
||||
|
||||
export enum AnimFocus {
|
||||
TARGET = 1,
|
||||
USER,
|
||||
USER_TARGET,
|
||||
SCREEN
|
||||
SCREEN,
|
||||
}
|
||||
|
||||
export enum AnimBlendType {
|
||||
NORMAL,
|
||||
ADD,
|
||||
SUBTRACT
|
||||
SUBTRACT,
|
||||
}
|
||||
|
||||
export enum ChargeAnim {
|
||||
@ -38,7 +38,7 @@ export enum ChargeAnim {
|
||||
SOLAR_BLADE_CHARGING,
|
||||
BEAK_BLAST_CHARGING,
|
||||
METEOR_BEAM_CHARGING,
|
||||
ELECTRO_SHOT_CHARGING
|
||||
ELECTRO_SHOT_CHARGING,
|
||||
}
|
||||
|
||||
export enum CommonAnim {
|
||||
@ -91,5 +91,5 @@ export enum CommonAnim {
|
||||
ELECTRIC_TERRAIN,
|
||||
GRASSY_TERRAIN,
|
||||
PSYCHIC_TERRAIN,
|
||||
LOCK_ON = 2120
|
||||
LOCK_ON = 2120,
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
export enum MoveCategory {
|
||||
PHYSICAL,
|
||||
SPECIAL,
|
||||
STATUS
|
||||
STATUS,
|
||||
}
|
||||
|
@ -2,5 +2,5 @@ export enum MoveEffectTrigger {
|
||||
PRE_APPLY,
|
||||
POST_APPLY,
|
||||
/** Triggers one time after all target effects have applied */
|
||||
POST_TARGET
|
||||
POST_TARGET,
|
||||
}
|
||||
|
@ -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 */
|
||||
REDIRECT_COUNTER = 1 << 18,
|
||||
/** Indicates a move is able to be reflected by {@linkcode AbilityId.MAGIC_BOUNCE} and {@linkcode MoveId.MAGIC_COAT} */
|
||||
REFLECTABLE = 1 << 19
|
||||
REFLECTABLE = 1 << 19,
|
||||
}
|
||||
|
@ -3,5 +3,5 @@ export enum MoveResult {
|
||||
SUCCESS,
|
||||
FAIL,
|
||||
MISS,
|
||||
OTHER
|
||||
OTHER,
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user