Merge branch 'beta' into sky-battle-me

This commit is contained in:
Diogo Diniz 2025-06-21 23:33:38 +01:00
commit 8da29b7260
62 changed files with 512 additions and 2283 deletions

View File

@ -32,9 +32,6 @@ jobs:
- name: Install Node.js dependencies - name: Install Node.js dependencies
run: npm ci run: npm ci
- name: Run ESLint
run: npm run eslint-ci
- name: Lint with Biome - name: Lint with Biome
run: npm run biome-ci run: npm run biome-ci

164
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,164 @@
# Contributing to PokéRogue
Thank you for taking the time to contribute, every little bit helps. This project is entirely open-source and unmonetized - community contributions are what keep it alive!
Please make sure you understand everything relevant to your changes from the [Table of Contents](#-table-of-contents), and absolutely *feel free to reach out reach out in the **#dev-corner** channel on [Discord](https://discord.gg/pokerogue)*. We are here to help and the better you understand what you're working on, the easier it will be for it to find its way into the game.
## 📄 Table of Contents
- [Development Basics](#-development-basics)
- [Environment Setup](#-environment-setup)
- [Getting Started](#-getting-started)
- [Documentation](#-documentation)
- [Testing Your Changes](#-testing-your-changes)
- [Localization](#-localization)
- [Development Save File (Unlock Everything)](#-development-save-file)
## 🛠️ Development Basics
PokéRogue is built with [Typescript](https://www.typescriptlang.org/docs/handbook/intro.html), using the [Phaser](https://github.com/phaserjs/phaser) game framework.
If you have the motivation and experience with Typescript/Javascript (or are willing to learn) you can contribute by forking the repository and making pull requests with contributions.
## 💻 Environment Setup
### Prerequisites
- node: >=22.14.0
- npm: [how to install](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
### Running Locally
1. Clone the repo and in the root directory run `npm install`
- *if you run into any errors, reach out in the **#dev-corner** channel on Discord*
2. Run `npm run start:dev` to locally run the project in `localhost:8000`
### Linting
Check out our [in-depth file](./docs/linting.md) on linting and formatting!
## 🚀 Getting Started
A great way to develop an understanding of how the project works is to look at test cases (located in [the `test` folder](./test/)).
Tests show you both how things are supposed to work and the expected "flow" to get from point A to point B in battles.
*This is a big project and you will be confused at times - never be afraid to reach out and ask questions in **#dev-corner***!
### Where to Look
Once you have your feet under you, check out the [Issues](https://github.com/pagefaultgames/pokerogue/issues) page to see how you can help us!
Most issues are bugs and are labeled with their area, such as `Move`, `Ability`, `UI/UX`, etc. There are also priority labels:
- `P0`: Completely gamebreaking (very rare)
- `P1`: Major - Game crash
- `P2`: Minor - Incorrect (but non-crashing) move/ability/interaction
- `P3`: No gameplay impact - typo, minor graphical error, etc.
Also under issues, you can take a look at the [List of Partial / Unimplemented Moves and Abilities](https://github.com/pagefaultgames/pokerogue/issues/3503) and the [Bug Board](https://github.com/orgs/pagefaultgames/projects/3) (the latter is essentially the same as the issues page but easier to work with).
You are free to comment on any issue so that you may be assigned to it and we can avoid multiple people working on the same thing.
## 📚 Documentation
You can find the auto-generated documentation [here](https://pagefaultgames.github.io/pokerogue/main/index.html).
For information on enemy AI, check out the [enemy-ai.md](./docs/enemy-ai.md) file.
For detailed guidelines on documenting your code, refer to the [comments.md](./docs/comments.md) file.
Again, if you have unanswered questions please feel free to ask!
## 🧪 Testing Your Changes
You've just made a change - how can you check if it works? You have two areas to hit:
### 1 - Manual Testing
> This will likely be your first stop. After making a change, you'll want to spin the game up and make sure everything is as you expect. To do this, you will need a way to manipulate the game to produce the situation you're looking to test.
[src/overrides.ts](../src/overrides.ts) contains overrides for most values you'll need to change for testing, controlled through the `overrides` object.
For example, here is how you could test a scenario where the player Pokemon has the ability Drought and the enemy Pokemon has the move Water Gun:
```typescript
const overrides = {
ABILITY_OVERRIDE: AbilityId.DROUGHT,
OPP_MOVESET_OVERRIDE: MoveId.WATER_GUN,
} satisfies Partial<InstanceType<typeof DefaultOverrides>>;
```
Read through `src/overrides.ts` file to find the override that fits your needs - there are a lot of them!
If the situation you're trying to test can't be created using existing overrides (or with the [Dev Save](#-development-save-file)), reach out in **#dev-corner**.
You can get help testing your specific changes, and you might have found a new override that needs to be created!
### 2 - Automatic Testing
> PokéRogue uses [Vitest](https://vitest.dev/) for automatic testing. Checking out the existing tests in the [test](./test/) folder is a great way to understand how this works, and to get familiar with the project as a whole.
To make sure your changes didn't break any existing test cases, run `npm run test:silent` in your terminal. You can also provide an argument to the command: to run only the Dancer (ability) tests, you could write `npm run test:silent dancer`.
- __Note that passing all test cases does *not* guarantee that everything is working properly__. The project does not have complete regression testing.
Most non-trivial changes (*especially bug fixes*) should come along with new test cases.
- To make a new test file, run `npm run create-test` and follow the prompts. If the move/ability/etc. you're modifying already has tests, simply add new cases to the end of the file. As mentioned before, the easiest way to get familiar with the system and understand how to write your own tests is simply to read the existing tests, particularly ones similar to the tests you intend to write.
- Ensure that new tests:
- Are deterministic. In other words, the test should never pass or fail when it shouldn't due to randomness. This involves primarily ensuring that abilities and moves are never randomly selected.
- As much as possible, are unit tests. If you have made two distinct changes, they should be tested in two separate cases.
- Test edge cases. A good strategy is to think of edge cases beforehand and create tests for them using `it.todo`. Once the edge case has been handled, you can remove the `todo` marker.
## 📜 Localization
The project intends for all text to be localized. That is, strings are pulled from translation files using keys (depending on the current language) and *never* hardcoded as a particular language. Note that there is a PDF in a message pinned in **#dev-corner** which gives the following information in greater detail.
### Setting Up and Updating the Locales Submodule
> The locales (translation) files are set up as a git submodule. A project-in-a-project, if you will.
To fetch translations when you first start development in your fork or to update them on your local branch, run:
```bash
git submodule update --progress --init --recursive
```
### How Localizations Work
> This project uses the [i18next](https://www.i18next.com/) library to integrate translations from public/locales
into the source code based on the user's settings or location. The basic process for
fetching translated text is as follows:
1. The source code fetches text by a given key, e.g.
```ts
i18next.t("fileName:keyName", { arg1: "Hello", arg2: "an example", ... })
```
2. The game looks up the key in the corresponding JSON file in the user's
language, e.g.
```ts
// from "en/file-name.json"...
"keyName": "{{arg1}}! This is {{arg2}} of translated text!"
```
If the key doesn't exist for the user's language, the game will default to the
corresponding English key (in the case of LATAM Spanish, it will first default to ES Spanish).
3. The game shows the text to the user:
```ts
"Hello! This is an example of translated text!"
```
### Adding Translated Text
> If your feature involves new or modified text in any form, then you will be modifying the [locales](https://github.com/pagefaultgames/pokerogue-locales) repository. ***Never hardcode new text in any language!***
The workflow is:
1. 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 pokerogue-locales repository.
2. Make another pull request -- this time to the [pokerogue-locales](https://github.com/pagefaultgames/pokerogue-locales)
repository -- adding a new entry to the English locale with text for each key
you added to your main PR. You *only* need to add the English key and value - the translation team will handle the rest.
3. If your feature is pulled from the mainline Pokémon games (e.g. a Move or Ability implementation), add a source link for any added text within the locale PR.
[Poké Corpus](https://abcboy101.github.io/poke-corpus) is a great resource for finding text from the latest mainline games; otherwise, a YouTube video link showing the text in mainline is sufficient.
4. Ping @lugiadrien in **#dev-corner** or the current callout thread to make sure your locales PR is seen.
It'll be merged into the locales repository after any necessary corrections, at which point you can test it in your main PR (after updating locales from remote)
5. The Dev team will approve your main PR, and your changes will be in the beta environment!
## 😈 Development Save File
> Some issues may require you to have unlocks on your save file which go beyond normal overrides. For this reason, the repository contains a [save file](../test/testUtils/saves/everything.psrv) with _everything_ unlocked (even ones not legitimately obtainable, like unimplemented variant shinies).
1. Start the game up locally and navigate to `Menu -> Manage Data -> Import Data`
2. Select [everything.prsv](test/testUtils/saves/everything.prsv) (`test/testUtils/saves/everything.prsv`) and confirm.

View File

@ -4,47 +4,7 @@ PokéRogue is a browser based Pokémon fangame heavily inspired by the roguelite
# Contributing # Contributing
## 🛠️ Development See [CONTRIBUTING.md](./CONTRIBUTING.md), this includes instructions on how to set up the game locally.
If you have the motivation and experience with Typescript/Javascript (or are willing to learn) please feel free to fork the repository and make pull requests with contributions. If you don't know what to work on but want to help, reference the below **To-Do** section or the **#feature-vote** channel in the discord.
### 💻 Environment Setup
#### Prerequisites
- node: 22.14.0
- npm: [how to install](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
#### Running Locally
1. Clone the repo and in the root directory run `npm install`
- *if you run into any errors, reach out in the **#dev-corner** channel in discord*
2. Run `npm run start:dev` to locally run the project in `localhost:8000`
#### Linting
We're using Biome as our common linter and formatter. It will run automatically during the pre-commit hook but if you would like to manually run it, use the `npm run biome` script. To view the complete rules, check out the [biome.jsonc](./biome.jsonc) file.
### 📚 Documentation
You can find the auto-generated documentation [here](https://pagefaultgames.github.io/pokerogue/main/index.html).
For information on enemy AI, check out the [enemy-ai.md](./docs/enemy-ai.md) file.
For detailed guidelines on documenting your code, refer to the [comments.md](./docs/comments.md) file.
### ❔ FAQ
**How do I test a new _______?**
- In the `src/overrides.ts` file there are overrides for most values you'll need to change for testing
**How do I retrieve the translations?**
- The translations were moved to the [dedicated translation repository](https://github.com/pagefaultgames/pokerogue-locales) and are now applied as a submodule in this project.
- The command to retrieve the translations is `git submodule update --init --recursive`. If you still struggle to get it working, please reach out to #dev-corner channel in Discord.
## 🪧 To Do
Check out [Github Issues](https://github.com/pagefaultgames/pokerogue/issues) to see how can you help us!
# 📝 Credits # 📝 Credits
> >

View File

@ -1,5 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"vcs": { "vcs": {
"enabled": false, "enabled": false,
"clientKind": "git", "clientKind": "git",
@ -10,35 +10,46 @@
"enabled": true, "enabled": true,
"useEditorconfig": true, "useEditorconfig": true,
"indentStyle": "space", "indentStyle": "space",
"ignore": ["src/enums/*", "src/data/balance/*"], "includes": ["**", "!**/src/enums/**/*", "!**/src/data/balance/**/*"],
"lineWidth": 120 "lineWidth": 120
}, },
"files": { "files": {
"ignoreUnknown": true, "ignoreUnknown": true,
// Adding folders to the ignore list is GREAT for performance because it prevents biome from descending into them // Adding folders to the ignore list is GREAT for performance because it prevents biome from descending into them
// and having to verify whether each individual file is ignored // and having to verify whether each individual file is ignored
"ignore": [ "includes": [
"**/*.d.ts", "**",
"dist/*", "!**/*.d.ts",
"build/*", "!**/dist/**/*",
"coverage/*", "!**/build/**/*",
"public/*", "!**/coverage/**/*",
".github/*", "!**/public/**/*",
"node_modules/*", "!**/.github/**/*",
".vscode/*", "!**/node_modules/**/*",
"*.css", // TODO? "!**/.vscode/**/*",
"*.html", // TODO? // TODO: lint css and html?
// TODO: these files are too big and complex, ignore them until their respective refactors "!**/*.css",
"src/data/moves/move.ts", "!**/*.html",
// TODO: enable linting this file
// this file is just too big: "!**/src/data/moves/move.ts",
"src/data/balance/tms.ts" // this file is too big
"!**/src/data/balance/tms.ts"
] ]
}, },
// While it'd be nice to enable consistent sorting, enabling this causes issues due to circular import resolution order // TODO: Configure and enable import sorting
// TODO: Remove if we ever get down to 0 circular imports "assist": {
"organizeImports": { "enabled": false }, "actions": {
"source": {
"organizeImports": {
"level": "off",
"options": {
"groups": []
}
}
}
}
},
"linter": { "linter": {
"enabled": true, "enabled": true,
"rules": { "rules": {
@ -48,10 +59,15 @@
"noUnusedVariables": "error", "noUnusedVariables": "error",
"noSwitchDeclarations": "error", "noSwitchDeclarations": "error",
"noVoidTypeReturn": "error", "noVoidTypeReturn": "error",
"noUnusedImports": "error" "noUnusedImports": {
"level": "error",
"fix": "safe"
},
"noUnusedFunctionParameters": "error",
"noUnusedLabels": "error",
"noPrivateImports": "error"
}, },
"style": { "style": {
"noVar": "error",
"useEnumInitializers": "off", // large enums like Moves/Species would make this cumbersome "useEnumInitializers": "off", // large enums like Moves/Species would make this cumbersome
"useBlockStatements": "error", "useBlockStatements": "error",
"useConst": "error", "useConst": "error",
@ -59,11 +75,31 @@
"noNonNullAssertion": "off", // TODO: Turn this on ASAP and fix all non-null assertions in non-test files "noNonNullAssertion": "off", // TODO: Turn this on ASAP and fix all non-null assertions in non-test files
"noParameterAssign": "off", "noParameterAssign": "off",
"useExponentiationOperator": "off", // Too typo-prone and easy to mixup with standard multiplication (* vs **) "useExponentiationOperator": "off", // Too typo-prone and easy to mixup with standard multiplication (* vs **)
"useDefaultParameterLast": "off", // TODO: Fix spots in the codebase where this flag would be triggered, and then enable "useDefaultParameterLast": {
// 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"
},
"useSingleVarDeclarator": "off", "useSingleVarDeclarator": "off",
"useNodejsImportProtocol": "off", "useNodejsImportProtocol": "off",
"useTemplate": "off", // string concatenation is faster: https://stackoverflow.com/questions/29055518/are-es6-template-literals-faster-than-string-concatenation "useTemplate": "off", // string concatenation is faster: https://stackoverflow.com/questions/29055518/are-es6-template-literals-faster-than-string-concatenation
"noNamespaceImport": "error" "useAsConstAssertion": "error",
"noUnusedTemplateLiteral": "error",
"useNumberNamespace": "error",
"noInferrableTypes": "error",
"noUselessElse": "error",
"noRestrictedTypes": {
"level": "error",
"options": {
"types": {
"integer": {
"message": "This is an alias for 'number' that can provide false impressions of what values can actually be contained in this variable. Use 'number' instead.",
"use": "number"
}
}
}
}
}, },
"suspicious": { "suspicious": {
"noDoubleEquals": "error", "noDoubleEquals": "error",
@ -77,45 +113,62 @@
"noImplicitAnyLet": "warn", // TODO: Refactor and make this an error "noImplicitAnyLet": "warn", // TODO: Refactor and make this an error
"noRedeclare": "info", // TODO: Refactor and make this an error "noRedeclare": "info", // TODO: Refactor and make this an error
"noGlobalIsNan": "off", "noGlobalIsNan": "off",
"noAsyncPromiseExecutor": "warn" // TODO: Refactor and make this an error "noAsyncPromiseExecutor": "warn", // TODO: Refactor and make this an error
"noVar": "error",
"noDocumentCookie": "off" // Firefox has minimal support for the "Cookie Store API"
}, },
"complexity": { "complexity": {
"noExcessiveCognitiveComplexity": "warn", // TODO: Refactor and make this an error "noExcessiveCognitiveComplexity": "info", // TODO: Refactor and make this an error
"useLiteralKeys": "off", "useLiteralKeys": "off",
"noForEach": "off", // Foreach vs for of is not that simple. "noForEach": "off", // Foreach vs for of is not that simple.
"noUselessSwitchCase": "off", // Explicit > Implicit "noUselessSwitchCase": "off", // Explicit > Implicit
"noUselessConstructor": "error", "noUselessConstructor": "error",
"noBannedTypes": "warn" // TODO: Refactor and make this an error "noBannedTypes": "warn", // TODO: Refactor and make this an error
"noThisInStatic": "error",
"noUselessThisAlias": "error",
"noUselessTernary": "error"
},
"performance": {
"noNamespaceImport": "error",
"noDelete": "error"
}, },
"nursery": { "nursery": {
"noRestrictedTypes": { "useAdjacentGetterSetter": "error",
"level": "error", "noConstantBinaryExpression": "error",
"options": { "noTsIgnore": "error",
"types": { "noAwaitInLoop": "warn",
"integer": { "useJsonImportAttribute": "off", // "Import attributes are only supported when the '--module' option is set to 'esnext', 'node18', 'nodenext', or 'preserve'. ts(2823)"
"message": "This is an alias for 'number' that can provide false impressions of what values can actually be contained in this variable. Use 'number' instead.", "useIndexOf": "error",
"use": "number" "useObjectSpread": "error",
} "useNumericSeparators": "off", // TODO: enable?
} "useIterableCallbackReturn": "warn", // TODO: refactor and make "error"
} "noShadow": "warn" // TODO: refactor and make "error"
}
} }
} }
}, },
"javascript": { "javascript": {
"formatter": { "quoteStyle": "double", "arrowParentheses": "asNeeded" } "formatter": {
"quoteStyle": "double",
"arrowParentheses": "asNeeded"
},
"parser": {
"jsxEverywhere": false
}
}, },
"overrides": [ "overrides": [
{ {
"include": ["test/**/*.test.ts"], "includes": ["**/test/**/*.test.ts"],
"javascript": { "globals": [] },
"linter": { "linter": {
"rules": { "rules": {
"performance": { "performance": {
"noDelete": "off" // TODO: evaluate if this is necessary for the test(s) to function "noDelete": "off", // TODO: evaluate if this is necessary for the test(s) to function
"noNamespaceImport": "off" // this is required for `vi.spyOn` to work in some tests
}, },
"style": { "style": {
"noNamespaceImport": "off" // this is required for `vi.spyOn` to work in some tests "noNonNullAssertion": "off"
},
"nursery": {
"noFloatingPromises": "error"
} }
} }
} }
@ -123,7 +176,7 @@
// Overrides to prevent unused import removal inside `overrides.ts` and enums files (for TSDoc linkcodes) // Overrides to prevent unused import removal inside `overrides.ts` and enums files (for TSDoc linkcodes)
{ {
"include": ["src/overrides.ts", "src/enums/*"], "includes": ["**/src/overrides.ts", "**/src/enums/**/*"],
"linter": { "linter": {
"rules": { "rules": {
"correctness": { "correctness": {
@ -133,7 +186,7 @@
} }
}, },
{ {
"include": ["src/overrides.ts"], "includes": ["**/src/overrides.ts"],
"linter": { "linter": {
"rules": { "rules": {
"style": { "style": {

View File

@ -1,43 +0,0 @@
/** @ts-check */
import tseslint from "typescript-eslint";
import stylisticTs from "@stylistic/eslint-plugin-ts";
import parser from "@typescript-eslint/parser";
import importX from "eslint-plugin-import-x";
export default tseslint.config(
{
name: "eslint-config",
files: ["src/**/*.{ts,tsx,js,jsx}", "test/**/*.{ts,tsx,js,jsx}"],
ignores: ["dist/*", "build/*", "coverage/*", "public/*", ".github/*", "node_modules/*", ".vscode/*"],
languageOptions: {
parser: parser,
},
plugins: {
"import-x": importX,
"@stylistic/ts": stylisticTs,
"@typescript-eslint": tseslint.plugin,
},
rules: {
"no-undef": "off", // Disables the rule that disallows the use of undeclared variables (TypeScript handles this)
"no-extra-semi": "error", // Disallows unnecessary semicolons for TypeScript-specific syntax
"import-x/extensions": ["error", "never", { json: "always" }], // Enforces no extension for imports unless json
},
},
{
name: "eslint-tests",
files: ["test/**/**.test.ts"],
languageOptions: {
parser: parser,
parserOptions: {
project: ["./tsconfig.json"],
},
},
plugins: {
"@typescript-eslint": tseslint.plugin,
},
rules: {
"@typescript-eslint/no-floating-promises": "error", // Require Promise-like statements to be handled appropriately. - https://typescript-eslint.io/rules/no-floating-promises/
"@typescript-eslint/no-misused-promises": "error", // Disallow Promises in places not designed to handle them. - https://typescript-eslint.io/rules/no-misused-promises/
},
},
);

2053
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -28,18 +28,12 @@
"update-locales:remote": "git submodule update --progress --init --recursive --force --remote" "update-locales:remote": "git submodule update --progress --init --recursive --force --remote"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "1.9.4", "@biomejs/biome": "2.0.0",
"@eslint/js": "^9.23.0",
"@hpcc-js/wasm": "^2.22.4", "@hpcc-js/wasm": "^2.22.4",
"@stylistic/eslint-plugin-ts": "^4.1.0",
"@types/jsdom": "^21.1.7", "@types/jsdom": "^21.1.7",
"@types/node": "^22.13.14", "@types/node": "^22.13.14",
"@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0",
"@vitest/coverage-istanbul": "^3.0.9", "@vitest/coverage-istanbul": "^3.0.9",
"dependency-cruiser": "^16.3.10", "dependency-cruiser": "^16.3.10",
"eslint": "^9.23.0",
"eslint-plugin-import-x": "^4.9.4",
"inquirer": "^12.4.2", "inquirer": "^12.4.2",
"jsdom": "^26.0.0", "jsdom": "^26.0.0",
"lefthook": "^1.11.5", "lefthook": "^1.11.5",
@ -47,7 +41,6 @@
"phaser3spectorjs": "^0.0.8", "phaser3spectorjs": "^0.0.8",
"typedoc": "^0.28.1", "typedoc": "^0.28.1",
"typescript": "^5.8.2", "typescript": "^5.8.2",
"typescript-eslint": "^8.28.0",
"vite": "^6.3.4", "vite": "^6.3.4",
"vite-tsconfig-paths": "^5.1.4", "vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.0.9", "vitest": "^3.0.9",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -468,7 +468,7 @@ export default class BattleScene extends SceneBase {
true, true,
); );
//@ts-ignore (the defined types in the package are incromplete...) //@ts-expect-error (the defined types in the package are incromplete...)
transition.transit({ transition.transit({
mode: "blinds", mode: "blinds",
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
@ -1167,7 +1167,7 @@ export default class BattleScene extends SceneBase {
this.field.remove(this.currentBattle.mysteryEncounter?.introVisuals, true); this.field.remove(this.currentBattle.mysteryEncounter?.introVisuals, true);
} }
//@ts-ignore - allowing `null` for currentBattle causes a lot of trouble //@ts-expect-error - allowing `null` for currentBattle causes a lot of trouble
this.currentBattle = null; // TODO: resolve ts-ignore this.currentBattle = null; // TODO: resolve ts-ignore
// Reset RNG after end of game or save & quit. // Reset RNG after end of game or save & quit.
@ -3237,7 +3237,7 @@ export default class BattleScene extends SceneBase {
(!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE) && (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE) &&
achv.validate(args) achv.validate(args)
) { ) {
this.gameData.achvUnlocks[achv.id] = new Date().getTime(); this.gameData.achvUnlocks[achv.id] = Date.now();
this.ui.achvBar.showAchv(achv); this.ui.achvBar.showAchv(achv);
if (vouchers.hasOwnProperty(achv.id)) { if (vouchers.hasOwnProperty(achv.id)) {
this.validateVoucher(vouchers[achv.id]); this.validateVoucher(vouchers[achv.id]);
@ -3250,7 +3250,7 @@ export default class BattleScene extends SceneBase {
validateVoucher(voucher: Voucher, args?: unknown[]): boolean { validateVoucher(voucher: Voucher, args?: unknown[]): boolean {
if (!this.gameData.voucherUnlocks.hasOwnProperty(voucher.id) && voucher.validate(args)) { if (!this.gameData.voucherUnlocks.hasOwnProperty(voucher.id) && voucher.validate(args)) {
this.gameData.voucherUnlocks[voucher.id] = new Date().getTime(); this.gameData.voucherUnlocks[voucher.id] = Date.now();
this.ui.achvBar.showAchv(voucher); this.ui.achvBar.showAchv(voucher);
this.gameData.voucherCounts[voucher.voucherType]++; this.gameData.voucherCounts[voucher.voucherType]++;
return true; return true;

View File

@ -178,7 +178,7 @@ export default class Battle {
) )
.map(i => { .map(i => {
const ret = i as PokemonHeldItemModifier; const ret = i as PokemonHeldItemModifier;
//@ts-ignore - this is awful to fix/change //@ts-expect-error - this is awful to fix/change
ret.pokemonId = null; ret.pokemonId = null;
return ret; return ret;
}), }),

View File

@ -595,13 +595,13 @@ function parseEggMoves(content: string): void {
const cols = line.split(",").slice(0, 5); const cols = line.split(",").slice(0, 5);
const moveNames = allMoves.map(m => m.name.replace(/ \([A-Z]\)$/, "").toLowerCase()); const moveNames = allMoves.map(m => m.name.replace(/ \([A-Z]\)$/, "").toLowerCase());
const enumSpeciesName = cols[0].toUpperCase().replace(/[ -]/g, "_"); const enumSpeciesName = cols[0].toUpperCase().replace(/[ -]/g, "_");
const species = speciesValues[speciesNames.findIndex(s => s === enumSpeciesName)]; const species = speciesValues[speciesNames.indexOf(enumSpeciesName)];
const eggMoves: MoveId[] = []; const eggMoves: MoveId[] = [];
for (let m = 0; m < 4; m++) { for (let m = 0; m < 4; m++) {
const moveName = cols[m + 1].trim(); const moveName = cols[m + 1].trim();
const moveIndex = moveName !== "N/A" ? moveNames.findIndex(mn => mn === moveName.toLowerCase()) : -1; const moveIndex = moveName !== "N/A" ? moveNames.indexOf(moveName.toLowerCase()) : -1;
eggMoves.push(moveIndex > -1 ? moveIndex as MoveId : MoveId.NONE); eggMoves.push(moveIndex > -1 ? moveIndex as MoveId : MoveId.NONE);
if (moveIndex === -1) { if (moveIndex === -1) {

View File

@ -650,8 +650,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(SpeciesId.KIRLIA, 20, null, null) new SpeciesEvolution(SpeciesId.KIRLIA, 20, null, null)
], ],
[SpeciesId.KIRLIA]: [ [SpeciesId.KIRLIA]: [
new SpeciesEvolution(SpeciesId.GARDEVOIR, 30, null, {key: EvoCondKey.GENDER, gender: Gender.FEMALE}), new SpeciesEvolution(SpeciesId.GARDEVOIR, 30, null, null),
new SpeciesEvolution(SpeciesId.GALLADE, 30, null, {key: EvoCondKey.GENDER, gender: Gender.MALE}) new SpeciesEvolution(SpeciesId.GALLADE, 1, EvolutionItem.DAWN_STONE, {key: EvoCondKey.GENDER, gender: Gender.MALE})
], ],
[SpeciesId.SURSKIT]: [ [SpeciesId.SURSKIT]: [
new SpeciesEvolution(SpeciesId.MASQUERAIN, 22, null, null) new SpeciesEvolution(SpeciesId.MASQUERAIN, 22, null, null)
@ -739,8 +739,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(SpeciesId.DUSCLOPS, 37, null, null) new SpeciesEvolution(SpeciesId.DUSCLOPS, 37, null, null)
], ],
[SpeciesId.SNORUNT]: [ [SpeciesId.SNORUNT]: [
new SpeciesEvolution(SpeciesId.GLALIE, 42, null, {key: EvoCondKey.GENDER, gender: Gender.MALE}), new SpeciesEvolution(SpeciesId.GLALIE, 42, null, null),
new SpeciesEvolution(SpeciesId.FROSLASS, 42, null, {key: EvoCondKey.GENDER, gender: Gender.FEMALE}) new SpeciesEvolution(SpeciesId.FROSLASS, 1, EvolutionItem.DAWN_STONE, {key: EvoCondKey.GENDER, gender: Gender.FEMALE})
], ],
[SpeciesId.SPHEAL]: [ [SpeciesId.SPHEAL]: [
new SpeciesEvolution(SpeciesId.SEALEO, 32, null, null) new SpeciesEvolution(SpeciesId.SEALEO, 32, null, null)

View File

@ -346,7 +346,7 @@ abstract class AnimTimedBgEvent extends AnimTimedEvent {
} }
class AnimTimedUpdateBgEvent extends AnimTimedBgEvent { class AnimTimedUpdateBgEvent extends AnimTimedBgEvent {
// biome-ignore lint/correctness/noUnusedVariables: seems intentional // biome-ignore lint/correctness/noUnusedFunctionParameters: seems intentional
execute(moveAnim: MoveAnim, priority?: number): number { execute(moveAnim: MoveAnim, priority?: number): number {
const tweenProps = {}; const tweenProps = {};
if (this.bgX !== undefined) { if (this.bgX !== undefined) {
@ -359,15 +359,11 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent {
tweenProps["alpha"] = (this.opacity || 0) / 255; tweenProps["alpha"] = (this.opacity || 0) / 255;
} }
if (Object.keys(tweenProps).length) { if (Object.keys(tweenProps).length) {
globalScene.tweens.add( globalScene.tweens.add({
Object.assign(
{
targets: moveAnim.bgSprite, targets: moveAnim.bgSprite,
duration: getFrameMs(this.duration * 3), duration: getFrameMs(this.duration * 3),
}, ...tweenProps,
tweenProps, });
),
);
} }
return this.duration * 2; return this.duration * 2;
} }
@ -423,7 +419,7 @@ export function initCommonAnims(): Promise<void> {
const commonAnimId = commonAnimIds[ca]; const commonAnimId = commonAnimIds[ca];
commonAnimFetches.push( commonAnimFetches.push(
globalScene globalScene
.cachedFetch(`./battle-anims/common-${commonAnimNames[ca].toLowerCase().replace(/\_/g, "-")}.json`) .cachedFetch(`./battle-anims/common-${commonAnimNames[ca].toLowerCase().replace(/_/g, "-")}.json`)
.then(response => response.json()) .then(response => response.json())
.then(cas => commonAnims.set(commonAnimId, new AnimConfig(cas))), .then(cas => commonAnims.set(commonAnimId, new AnimConfig(cas))),
); );
@ -535,7 +531,7 @@ export async function initEncounterAnims(encounterAnim: EncounterAnim | Encounte
} }
encounterAnimFetches.push( encounterAnimFetches.push(
globalScene globalScene
.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`) .cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/_/g, "-")}.json`)
.then(response => response.json()) .then(response => response.json())
.then(cas => encounterAnims.set(anim, new AnimConfig(cas))), .then(cas => encounterAnims.set(anim, new AnimConfig(cas))),
); );
@ -559,7 +555,7 @@ export function initMoveChargeAnim(chargeAnim: ChargeAnim): Promise<void> {
} else { } else {
chargeAnims.set(chargeAnim, null); chargeAnims.set(chargeAnim, null);
globalScene globalScene
.cachedFetch(`./battle-anims/${ChargeAnim[chargeAnim].toLowerCase().replace(/\_/g, "-")}.json`) .cachedFetch(`./battle-anims/${ChargeAnim[chargeAnim].toLowerCase().replace(/_/g, "-")}.json`)
.then(response => response.json()) .then(response => response.json())
.then(ca => { .then(ca => {
if (Array.isArray(ca)) { if (Array.isArray(ca)) {
@ -1405,15 +1401,15 @@ export class EncounterBattleAnim extends BattleAnim {
export async function populateAnims() { export async function populateAnims() {
const commonAnimNames = getEnumKeys(CommonAnim).map(k => k.toLowerCase()); const commonAnimNames = getEnumKeys(CommonAnim).map(k => k.toLowerCase());
const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/\_/g, "")); const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/_/g, ""));
const commonAnimIds = getEnumValues(CommonAnim) as CommonAnim[]; const commonAnimIds = getEnumValues(CommonAnim) as CommonAnim[];
const chargeAnimNames = getEnumKeys(ChargeAnim).map(k => k.toLowerCase()); const chargeAnimNames = getEnumKeys(ChargeAnim).map(k => k.toLowerCase());
const chargeAnimMatchNames = chargeAnimNames.map(k => k.replace(/\_/g, " ")); const chargeAnimMatchNames = chargeAnimNames.map(k => k.replace(/_/g, " "));
const chargeAnimIds = getEnumValues(ChargeAnim) as ChargeAnim[]; const chargeAnimIds = getEnumValues(ChargeAnim) as ChargeAnim[];
const commonNamePattern = /name: (?:Common:)?(Opp )?(.*)/; const commonNamePattern = /name: (?:Common:)?(Opp )?(.*)/;
const moveNameToId = {}; const moveNameToId = {};
for (const move of getEnumValues(MoveId).slice(1)) { for (const move of getEnumValues(MoveId).slice(1)) {
const moveName = MoveId[move].toUpperCase().replace(/\_/g, ""); const moveName = MoveId[move].toUpperCase().replace(/_/g, "");
moveNameToId[moveName] = move; moveNameToId[moveName] = move;
} }
@ -1469,7 +1465,7 @@ export async function populateAnims() {
const frameData = framesData[fd]; const frameData = framesData[fd];
const focusFramesData = frameData.split(" - - "); const focusFramesData = frameData.split(" - - ");
for (let tf = 0; tf < focusFramesData.length; tf++) { for (let tf = 0; tf < focusFramesData.length; tf++) {
const values = focusFramesData[tf].replace(/ {6}\- /g, "").split("\n"); const values = focusFramesData[tf].replace(/ {6}- /g, "").split("\n");
const targetFrame = new AnimFrame( const targetFrame = new AnimFrame(
Number.parseFloat(values[0]), Number.parseFloat(values[0]),
Number.parseFloat(values[1]), Number.parseFloat(values[1]),
@ -1516,7 +1512,7 @@ export async function populateAnims() {
.replace(/[a-z]+: ! '', /gi, "") .replace(/[a-z]+: ! '', /gi, "")
.replace(/name: (.*?),/, 'name: "$1",') .replace(/name: (.*?),/, 'name: "$1",')
.replace( .replace(
/flashColor: !ruby\/object:Color { alpha: ([\d\.]+), blue: ([\d\.]+), green: ([\d\.]+), red: ([\d\.]+)}/, /flashColor: !ruby\/object:Color { alpha: ([\d.]+), blue: ([\d.]+), green: ([\d.]+), red: ([\d.]+)}/,
"flashRed: $4, flashGreen: $3, flashBlue: $2, flashAlpha: $1", "flashRed: $4, flashGreen: $3, flashBlue: $2, flashAlpha: $1",
); );
const frameIndex = Number.parseInt(/frame: (\d+)/.exec(timingData)![1]); // TODO: is the bang correct? const frameIndex = Number.parseInt(/frame: (\d+)/.exec(timingData)![1]); // TODO: is the bang correct?
@ -1641,12 +1637,12 @@ export async function populateAnims() {
let props: string[]; let props: string[];
for (let p = 0; p < propSets.length; p++) { for (let p = 0; p < propSets.length; p++) {
props = propSets[p]; props = propSets[p];
// @ts-ignore TODO // @ts-expect-error TODO
const ai = props.indexOf(a.key); const ai = props.indexOf(a.key);
if (ai === -1) { if (ai === -1) {
continue; continue;
} }
// @ts-ignore TODO // @ts-expect-error TODO
const bi = props.indexOf(b.key); const bi = props.indexOf(b.key);
return ai < bi ? -1 : ai > bi ? 1 : 0; return ai < bi ? -1 : ai > bi ? 1 : 0;

View File

@ -175,7 +175,7 @@ export class Egg {
this._sourceType = eggOptions?.sourceType ?? undefined; this._sourceType = eggOptions?.sourceType ?? undefined;
this._hatchWaves = eggOptions?.hatchWaves ?? this.getEggTierDefaultHatchWaves(); this._hatchWaves = eggOptions?.hatchWaves ?? this.getEggTierDefaultHatchWaves();
this._timestamp = eggOptions?.timestamp ?? new Date().getTime(); this._timestamp = eggOptions?.timestamp ?? Date.now();
// First roll shiny and variant so we can filter if species with an variant exist // First roll shiny and variant so we can filter if species with an variant exist
this._isShiny = eggOptions?.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny()); this._isShiny = eggOptions?.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny());
@ -255,7 +255,7 @@ export class Egg {
// Sets the hidden ability if a hidden ability exists and // Sets the hidden ability if a hidden ability exists and
// the override is set or the egg hits the chance // the override is set or the egg hits the chance
let abilityIndex: number | undefined = undefined; let abilityIndex: number | undefined;
const sameSpeciesEggHACheck = const sameSpeciesEggHACheck =
this._sourceType === EggSourceType.SAME_SPECIES_EGG && !randSeedInt(SAME_SPECIES_EGG_HA_RATE); this._sourceType === EggSourceType.SAME_SPECIES_EGG && !randSeedInt(SAME_SPECIES_EGG_HA_RATE);
const gachaEggHACheck = !(this._sourceType === EggSourceType.SAME_SPECIES_EGG) && !randSeedInt(GACHA_EGG_HA_RATE); const gachaEggHACheck = !(this._sourceType === EggSourceType.SAME_SPECIES_EGG) && !randSeedInt(GACHA_EGG_HA_RATE);
@ -524,7 +524,7 @@ export class Egg {
/** /**
* Rolls whether the egg is shiny or not. * Rolls whether the egg is shiny or not.
* @returns `true` if the egg is shiny * @returns `true` if the egg is shiny
**/ */
private rollShiny(): boolean { private rollShiny(): boolean {
let shinyChance = GACHA_DEFAULT_SHINY_RATE; let shinyChance = GACHA_DEFAULT_SHINY_RATE;
switch (this._sourceType) { switch (this._sourceType) {

View File

@ -16,7 +16,7 @@ import type Move from "./move";
* @see {@linkcode getMovePp} - returns amount of PP a move currently has. * @see {@linkcode getMovePp} - returns amount of PP a move currently has.
* @see {@linkcode getPpRatio} - returns the current PP amount / max PP amount. * @see {@linkcode getPpRatio} - returns the current PP amount / max PP amount.
* @see {@linkcode getName} - returns name of {@linkcode Move}. * @see {@linkcode getName} - returns name of {@linkcode Move}.
**/ */
export class PokemonMove { export class PokemonMove {
public moveId: MoveId; public moveId: MoveId;
public ppUsed: number; public ppUsed: number;

View File

@ -135,7 +135,7 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
); );
clownConfig.setPartyTemplates(clownPartyTemplate); clownConfig.setPartyTemplates(clownPartyTemplate);
clownConfig.setDoubleOnly(); clownConfig.setDoubleOnly();
// @ts-ignore // @ts-expect-error
clownConfig.partyTemplateFunc = null; // Overrides party template func if it exists clownConfig.partyTemplateFunc = null; // Overrides party template func if it exists
// Generate random ability for Blacephalon from pool // Generate random ability for Blacephalon from pool

View File

@ -92,7 +92,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
const brutalConfig = trainerConfigs[brutalTrainerType].clone(); const brutalConfig = trainerConfigs[brutalTrainerType].clone();
brutalConfig.title = trainerConfigs[brutalTrainerType].title; brutalConfig.title = trainerConfigs[brutalTrainerType].title;
brutalConfig.setPartyTemplates(e4Template); brutalConfig.setPartyTemplates(e4Template);
// @ts-ignore // @ts-expect-error
brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func
female = false; female = false;
if (brutalConfig.hasGenders) { if (brutalConfig.hasGenders) {

View File

@ -1226,7 +1226,7 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) {
); );
for (const value of meanEncountersPerRunPerBiomeSorted) { for (const value of meanEncountersPerRunPerBiomeSorted) {
stats += value[0] + "avg valid floors " + meanMEFloorsPerRunPerBiome.get(value[0]) + ", avg MEs ${value[1]},\n"; stats += value[0] + "avg valid floors " + meanMEFloorsPerRunPerBiome.get(value[0]) + `, avg MEs ${value[1]},\n`;
} }
console.log(stats); console.log(stats);

View File

@ -96,8 +96,8 @@ export function getPokemonSpeciesForm(species: SpeciesId, formIndex: number): Po
} }
export function getFusedSpeciesName(speciesAName: string, speciesBName: string): string { export function getFusedSpeciesName(speciesAName: string, speciesBName: string): string {
const fragAPattern = /([a-z]{2}.*?[aeiou(?:y$)\-\']+)(.*?)$/i; const fragAPattern = /([a-z]{2}.*?[aeiou(?:y$)\-']+)(.*?)$/i;
const fragBPattern = /([a-z]{2}.*?[aeiou(?:y$)\-\'])(.*?)$/i; const fragBPattern = /([a-z]{2}.*?[aeiou(?:y$)\-'])(.*?)$/i;
const [speciesAPrefixMatch, speciesBPrefixMatch] = [speciesAName, speciesBName].map(n => /^(?:[^ ]+) /.exec(n)); const [speciesAPrefixMatch, speciesBPrefixMatch] = [speciesAName, speciesBName].map(n => /^(?:[^ ]+) /.exec(n));
const [speciesAPrefix, speciesBPrefix] = [speciesAPrefixMatch, speciesBPrefixMatch].map(m => (m ? m[0] : "")); const [speciesAPrefix, speciesBPrefix] = [speciesAPrefixMatch, speciesBPrefixMatch].map(m => (m ? m[0] : ""));
@ -134,7 +134,7 @@ export function getFusedSpeciesName(speciesAName: string, speciesBName: string):
if (fragBMatch) { if (fragBMatch) {
const lastCharA = fragA.slice(fragA.length - 1); const lastCharA = fragA.slice(fragA.length - 1);
const prevCharB = fragBMatch[1].slice(fragBMatch.length - 1); const prevCharB = fragBMatch[1].slice(fragBMatch.length - 1);
fragB = (/[\-']/.test(prevCharB) ? prevCharB : "") + fragBMatch[2] || prevCharB; fragB = (/[-']/.test(prevCharB) ? prevCharB : "") + fragBMatch[2] || prevCharB;
if (lastCharA === fragB[0]) { if (lastCharA === fragB[0]) {
if (/[aiu]/.test(lastCharA)) { if (/[aiu]/.test(lastCharA)) {
fragB = fragB.slice(1); fragB = fragB.slice(1);
@ -379,7 +379,7 @@ export abstract class PokemonSpeciesForm {
} }
getSpriteAtlasPath(female: boolean, formIndex?: number, shiny?: boolean, variant?: number, back?: boolean): string { getSpriteAtlasPath(female: boolean, formIndex?: number, shiny?: boolean, variant?: number, back?: boolean): string {
const spriteId = this.getSpriteId(female, formIndex, shiny, variant, back).replace(/\_{2}/g, "/"); const spriteId = this.getSpriteId(female, formIndex, shiny, variant, back).replace(/_{2}/g, "/");
return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`;
} }
@ -478,8 +478,8 @@ export abstract class PokemonSpeciesForm {
case SpeciesId.DUDUNSPARCE: case SpeciesId.DUDUNSPARCE:
break; break;
case SpeciesId.ZACIAN: case SpeciesId.ZACIAN:
// biome-ignore lint/suspicious/noFallthroughSwitchClause: Intentionally falls through
case SpeciesId.ZAMAZENTA: case SpeciesId.ZAMAZENTA:
// biome-ignore lint/suspicious/noFallthroughSwitchClause: Falls through
if (formSpriteKey.startsWith("behemoth")) { if (formSpriteKey.startsWith("behemoth")) {
formSpriteKey = "crowned"; formSpriteKey = "crowned";
} }
@ -569,7 +569,7 @@ export abstract class PokemonSpeciesForm {
const rootSpeciesId = this.getRootSpeciesId(); const rootSpeciesId = this.getRootSpeciesId();
for (const moveId of moveset) { for (const moveId of moveset) {
if (speciesEggMoves.hasOwnProperty(rootSpeciesId)) { if (speciesEggMoves.hasOwnProperty(rootSpeciesId)) {
const eggMoveIndex = speciesEggMoves[rootSpeciesId].findIndex(m => m === moveId); const eggMoveIndex = speciesEggMoves[rootSpeciesId].indexOf(moveId);
if (eggMoveIndex > -1 && eggMoves & (1 << eggMoveIndex)) { if (eggMoveIndex > -1 && eggMoves & (1 << eggMoveIndex)) {
continue; continue;
} }

View File

@ -290,7 +290,7 @@ export class TrainerConfig {
* @param {string} [nameFemale] The name of the female trainer. If 'Ivy', a localized name will be assigned. * @param {string} [nameFemale] The name of the female trainer. If 'Ivy', a localized name will be assigned.
* @param {TrainerType | string} [femaleEncounterBgm] The encounter BGM for the female trainer, which can be a TrainerType or a string. * @param {TrainerType | string} [femaleEncounterBgm] The encounter BGM for the female trainer, which can be a TrainerType or a string.
* @returns {TrainerConfig} The updated TrainerConfig instance. * @returns {TrainerConfig} The updated TrainerConfig instance.
**/ */
setHasGenders(nameFemale?: string, femaleEncounterBgm?: TrainerType | string): TrainerConfig { setHasGenders(nameFemale?: string, femaleEncounterBgm?: TrainerType | string): TrainerConfig {
// If the female name is 'Ivy' (the rival), assign a localized name. // If the female name is 'Ivy' (the rival), assign a localized name.
if (nameFemale === "Ivy") { if (nameFemale === "Ivy") {
@ -335,7 +335,7 @@ export class TrainerConfig {
if (doubleEncounterBgm) { if (doubleEncounterBgm) {
this.doubleEncounterBgm = this.doubleEncounterBgm =
typeof doubleEncounterBgm === "number" typeof doubleEncounterBgm === "number"
? TrainerType[doubleEncounterBgm].toString().replace(/\_/g, " ").toLowerCase() ? TrainerType[doubleEncounterBgm].toString().replace(/_/g, " ").toLowerCase()
: doubleEncounterBgm; : doubleEncounterBgm;
} }
return this; return this;
@ -540,7 +540,7 @@ export class TrainerConfig {
* @param {SpeciesId | SpeciesId[]} signatureSpecies The signature species for the evil team leader. * @param {SpeciesId | SpeciesId[]} signatureSpecies The signature species for the evil team leader.
* @param specialtyType The specialty Type of the admin, if they have one * @param specialtyType The specialty Type of the admin, if they have one
* @returns {TrainerConfig} The updated TrainerConfig instance. * @returns {TrainerConfig} The updated TrainerConfig instance.
* **/ */
initForEvilTeamAdmin( initForEvilTeamAdmin(
title: string, title: string,
poolName: EvilTeam, poolName: EvilTeam,
@ -581,7 +581,7 @@ export class TrainerConfig {
* Initializes the trainer configuration for a Stat Trainer, as part of the Trainer's Test Mystery Encounter. * Initializes the trainer configuration for a Stat Trainer, as part of the Trainer's Test Mystery Encounter.
* @param _isMale Whether the stat trainer is Male or Female (for localization of the title). * @param _isMale Whether the stat trainer is Male or Female (for localization of the title).
* @returns {TrainerConfig} The updated TrainerConfig instance. * @returns {TrainerConfig} The updated TrainerConfig instance.
**/ */
initForStatTrainer(_isMale = false): TrainerConfig { initForStatTrainer(_isMale = false): TrainerConfig {
if (!getIsInitialized()) { if (!getIsInitialized()) {
initI18n(); initI18n();
@ -608,7 +608,7 @@ export class TrainerConfig {
* @param {PokemonType} specialtyType The specialty type for the evil team Leader. * @param {PokemonType} specialtyType The specialty type for the evil team Leader.
* @param boolean Whether or not this is the rematch fight * @param boolean Whether or not this is the rematch fight
* @returns {TrainerConfig} The updated TrainerConfig instance. * @returns {TrainerConfig} The updated TrainerConfig instance.
* **/ */
initForEvilTeamLeader( initForEvilTeamLeader(
title: string, title: string,
signatureSpecies: (SpeciesId | SpeciesId[])[], signatureSpecies: (SpeciesId | SpeciesId[])[],
@ -651,7 +651,7 @@ export class TrainerConfig {
* @param ignoreMinTeraWave Whether the Gym Leader always uses Tera (true), or only Teras after {@linkcode GYM_LEADER_TERA_WAVE} (false). Defaults to false. * @param ignoreMinTeraWave Whether the Gym Leader always uses Tera (true), or only Teras after {@linkcode GYM_LEADER_TERA_WAVE} (false). Defaults to false.
* @param teraSlot Optional, sets the party member in this slot to Terastallize. Wraps based on party size. * @param teraSlot Optional, sets the party member in this slot to Terastallize. Wraps based on party size.
* @returns {TrainerConfig} The updated TrainerConfig instance. * @returns {TrainerConfig} The updated TrainerConfig instance.
* **/ */
initForGymLeader( initForGymLeader(
signatureSpecies: (SpeciesId | SpeciesId[])[], signatureSpecies: (SpeciesId | SpeciesId[])[],
isMale: boolean, isMale: boolean,
@ -709,7 +709,7 @@ export class TrainerConfig {
* @param specialtyType - The specialty type for the Elite Four member. * @param specialtyType - The specialty type for the Elite Four member.
* @param teraSlot - Optional, sets the party member in this slot to Terastallize. * @param teraSlot - Optional, sets the party member in this slot to Terastallize.
* @returns The updated TrainerConfig instance. * @returns The updated TrainerConfig instance.
**/ */
initForEliteFour( initForEliteFour(
signatureSpecies: (SpeciesId | SpeciesId[])[], signatureSpecies: (SpeciesId | SpeciesId[])[],
isMale: boolean, isMale: boolean,
@ -765,7 +765,7 @@ export class TrainerConfig {
* @param {SpeciesId | SpeciesId[]} signatureSpecies The signature species for the Champion. * @param {SpeciesId | SpeciesId[]} signatureSpecies The signature species for the Champion.
* @param isMale Whether the Champion is Male or Female (for localization of the title). * @param isMale Whether the Champion is Male or Female (for localization of the title).
* @returns {TrainerConfig} The updated TrainerConfig instance. * @returns {TrainerConfig} The updated TrainerConfig instance.
**/ */
initForChampion(isMale: boolean): TrainerConfig { initForChampion(isMale: boolean): TrainerConfig {
// Check if the internationalization (i18n) system is initialized. // Check if the internationalization (i18n) system is initialized.
if (!getIsInitialized()) { if (!getIsInitialized()) {
@ -815,7 +815,7 @@ export class TrainerConfig {
* @param {TrainerSlot} trainerSlot - The slot to determine which title to use. Defaults to TrainerSlot.NONE. * @param {TrainerSlot} trainerSlot - The slot to determine which title to use. Defaults to TrainerSlot.NONE.
* @param {TrainerVariant} variant - The variant of the trainer to determine the specific title. * @param {TrainerVariant} variant - The variant of the trainer to determine the specific title.
* @returns {string} - The title of the trainer. * @returns {string} - The title of the trainer.
**/ */
getTitle(trainerSlot: TrainerSlot = TrainerSlot.NONE, variant: TrainerVariant): string { getTitle(trainerSlot: TrainerSlot = TrainerSlot.NONE, variant: TrainerVariant): string {
const ret = this.name; const ret = this.name;

View File

@ -898,12 +898,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
getSpriteAtlasPath(ignoreOverride?: boolean): string { getSpriteAtlasPath(ignoreOverride?: boolean): string {
const spriteId = this.getSpriteId(ignoreOverride).replace(/\_{2}/g, "/"); const spriteId = this.getSpriteId(ignoreOverride).replace(/_{2}/g, "/");
return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`;
} }
getBattleSpriteAtlasPath(back?: boolean, ignoreOverride?: boolean): string { getBattleSpriteAtlasPath(back?: boolean, ignoreOverride?: boolean): string {
const spriteId = this.getBattleSpriteId(back, ignoreOverride).replace(/\_{2}/g, "/"); const spriteId = this.getBattleSpriteId(back, ignoreOverride).replace(/_{2}/g, "/");
return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`;
} }
@ -977,7 +977,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
getFusionBattleSpriteAtlasPath(back?: boolean, ignoreOverride?: boolean): string { getFusionBattleSpriteAtlasPath(back?: boolean, ignoreOverride?: boolean): string {
return this.getFusionBattleSpriteId(back, ignoreOverride).replace(/\_{2}/g, "/"); return this.getFusionBattleSpriteId(back, ignoreOverride).replace(/_{2}/g, "/");
} }
getIconAtlasKey(ignoreOverride = false, useIllusion = true): string { getIconAtlasKey(ignoreOverride = false, useIllusion = true): string {
@ -2498,14 +2498,39 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
defScore *= defScore *=
1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[1], opponent, false, false, undefined, true), 0.25); 1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[1], opponent, false, false, undefined, true), 0.25);
} }
atkScore *= 1.25; //give more value for the pokemon's typing
const moveset = this.moveset;
let moveAtkScoreLength = 0;
for (const move of moveset) {
if (move.getMove().category === MoveCategory.SPECIAL || move.getMove().category === MoveCategory.PHYSICAL) {
atkScore += opponent.getAttackTypeEffectiveness(move.getMove().type, this, false, true, undefined, true);
moveAtkScoreLength++;
}
}
atkScore = atkScore / (moveAtkScoreLength + 1); //calculate the median for the attack score
/** /**
* Based on this Pokemon's HP ratio compared to that of the opponent. * Based on this Pokemon's HP ratio compared to that of the opponent.
* This ratio is multiplied by 1.5 if this Pokemon outspeeds the opponent; * This ratio is multiplied by 1.5 if this Pokemon outspeeds the opponent;
* however, the final ratio cannot be higher than 1. * however, the final ratio cannot be higher than 1.
*/ */
let hpDiffRatio = this.getHpRatio() + (1 - opponent.getHpRatio()); const hpRatio = this.getHpRatio();
if (outspeed) { const oppHpRatio = opponent.getHpRatio();
hpDiffRatio = Math.min(hpDiffRatio * 1.5, 1); const isDying = hpRatio <= 0.2;
let hpDiffRatio = hpRatio + (1 - oppHpRatio);
if (isDying && this.isActive(true)) {
//It might be a sacrifice candidate if hp under 20%
const badMatchup = atkScore < 1.5 && defScore < 1.5;
if (!outspeed && badMatchup) {
//It might not be a worthy sacrifice if it doesn't outspeed or doesn't do enough damage
hpDiffRatio *= 0.85;
} else {
hpDiffRatio = Math.min(1 - hpRatio + (outspeed ? 0.2 : 0.1), 1);
}
} else if (outspeed) {
hpDiffRatio = Math.min(hpDiffRatio * 1.25, 1);
} else if (hpRatio > 0.2 && hpRatio <= 0.4) {
//Might be considered to be switched because it's not in low enough health
hpDiffRatio = Math.min(hpDiffRatio * 0.5, 1);
} }
return (atkScore + defScore) * hpDiffRatio; return (atkScore + defScore) * hpDiffRatio;
} }
@ -2880,7 +2905,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
); );
}; };
let fusionOverride: PokemonSpecies | undefined = undefined; let fusionOverride: PokemonSpecies | undefined;
if (forStarter && this.isPlayer() && Overrides.STARTER_FUSION_SPECIES_OVERRIDE) { if (forStarter && this.isPlayer() && Overrides.STARTER_FUSION_SPECIES_OVERRIDE) {
fusionOverride = getPokemonSpecies(Overrides.STARTER_FUSION_SPECIES_OVERRIDE); fusionOverride = getPokemonSpecies(Overrides.STARTER_FUSION_SPECIES_OVERRIDE);
@ -4373,9 +4398,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
scene.time.delayedCall(fixedInt(Math.ceil(duration * 0.4)), () => { scene.time.delayedCall(fixedInt(Math.ceil(duration * 0.4)), () => {
try { try {
SoundFade.fadeOut(scene, cry, fixedInt(Math.ceil(duration * 0.2))); SoundFade.fadeOut(scene, cry, fixedInt(Math.ceil(duration * 0.2)));
fusionCry = this.getFusionSpeciesForm(undefined, true).cry( fusionCry = this.getFusionSpeciesForm(undefined, true).cry({
Object.assign({ seek: Math.max(fusionCry.totalDuration * 0.4, 0) }, soundConfig), seek: Math.max(fusionCry.totalDuration * 0.4, 0),
); ...soundConfig,
});
SoundFade.fadeIn( SoundFade.fadeIn(
scene, scene,
fusionCry, fusionCry,
@ -4517,13 +4543,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
if (i === transitionIndex && fusionCryKey) { if (i === transitionIndex && fusionCryKey) {
SoundFade.fadeOut(globalScene, cry, fixedInt(Math.ceil((duration / rate) * 0.2))); SoundFade.fadeOut(globalScene, cry, fixedInt(Math.ceil((duration / rate) * 0.2)));
fusionCry = globalScene.playSound( fusionCry = globalScene.playSound(fusionCryKey, {
fusionCryKey,
Object.assign({
seek: Math.max(fusionCry.totalDuration * 0.4, 0), seek: Math.max(fusionCry.totalDuration * 0.4, 0),
rate: rate, rate: rate,
}), });
);
SoundFade.fadeIn( SoundFade.fadeIn(
globalScene, globalScene,
fusionCry, fusionCry,
@ -5316,10 +5339,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
for (let sc = 0; sc < spriteColors.length; sc++) { for (let sc = 0; sc < spriteColors.length; sc++) {
const delta = Math.min(...paletteDeltas[sc]); const delta = Math.min(...paletteDeltas[sc]);
const paletteIndex = Math.min( const paletteIndex = Math.min(paletteDeltas[sc].indexOf(delta), fusionPalette.length - 1);
paletteDeltas[sc].findIndex(pd => pd === delta),
fusionPalette.length - 1,
);
if (delta < 255) { if (delta < 255) {
const ratio = easeFunc(delta / 255); const ratio = easeFunc(delta / 255);
const color = [0, 0, 0, fusionSpriteColors[sc][3]]; const color = [0, 0, 0, fusionSpriteColors[sc][3]];

View File

@ -158,7 +158,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
* @param {TrainerSlot} trainerSlot - The slot to determine which name to use. Defaults to TrainerSlot.NONE. * @param {TrainerSlot} trainerSlot - The slot to determine which name to use. Defaults to TrainerSlot.NONE.
* @param {boolean} includeTitle - Whether to include the title in the returned name. Defaults to false. * @param {boolean} includeTitle - Whether to include the title in the returned name. Defaults to false.
* @returns {string} - The formatted name of the trainer. * @returns {string} - The formatted name of the trainer.
**/ */
getName(trainerSlot: TrainerSlot = TrainerSlot.NONE, includeTitle = false): string { getName(trainerSlot: TrainerSlot = TrainerSlot.NONE, includeTitle = false): string {
// Get the base title based on the trainer slot and variant. // Get the base title based on the trainer slot and variant.
let name = this.config.getTitle(trainerSlot, this.variant); let name = this.config.getTitle(trainerSlot, this.variant);

View File

@ -70,20 +70,20 @@ const repeatInputDelayMillis = 250;
* providing a unified interface for all input-related interactions. * providing a unified interface for all input-related interactions.
*/ */
export class InputsController { export class InputsController {
private gamepads: Array<Phaser.Input.Gamepad.Gamepad> = new Array(); private gamepads: Array<Phaser.Input.Gamepad.Gamepad> = [];
public events: Phaser.Events.EventEmitter; public events: Phaser.Events.EventEmitter;
private buttonLock: Button[] = new Array(); private buttonLock: Button[] = [];
private interactions: Map<Button, Map<string, boolean>> = new Map(); private interactions: Map<Button, Map<string, boolean>> = new Map();
private configs: Map<string, InterfaceConfig> = new Map(); private configs: Map<string, InterfaceConfig> = new Map();
public gamepadSupport = true; public gamepadSupport = true;
public selectedDevice; public selectedDevice;
private disconnectedGamepads: Array<string> = new Array(); private disconnectedGamepads: Array<string> = [];
public lastSource = "keyboard"; public lastSource = "keyboard";
private inputInterval: NodeJS.Timeout[] = new Array(); private inputInterval: NodeJS.Timeout[] = [];
private touchControls: TouchControl; private touchControls: TouchControl;
public moveTouchControlsHandler: MoveTouchControlsHandler; public moveTouchControlsHandler: MoveTouchControlsHandler;

View File

@ -1585,7 +1585,9 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator {
pokemonEvolutions.hasOwnProperty(p.species.speciesId) && pokemonEvolutions.hasOwnProperty(p.species.speciesId) &&
(!p.pauseEvolutions || (!p.pauseEvolutions ||
p.species.speciesId === SpeciesId.SLOWPOKE || p.species.speciesId === SpeciesId.SLOWPOKE ||
p.species.speciesId === SpeciesId.EEVEE), p.species.speciesId === SpeciesId.EEVEE ||
p.species.speciesId === SpeciesId.KIRLIA ||
p.species.speciesId === SpeciesId.SNORUNT),
) )
.flatMap(p => { .flatMap(p => {
const evolutions = pokemonEvolutions[p.species.speciesId]; const evolutions = pokemonEvolutions[p.species.speciesId];
@ -1599,7 +1601,9 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator {
pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId) && pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId) &&
(!p.pauseEvolutions || (!p.pauseEvolutions ||
p.fusionSpecies.speciesId === SpeciesId.SLOWPOKE || p.fusionSpecies.speciesId === SpeciesId.SLOWPOKE ||
p.fusionSpecies.speciesId === SpeciesId.EEVEE), p.fusionSpecies.speciesId === SpeciesId.EEVEE ||
p.fusionSpecies.speciesId === SpeciesId.KIRLIA ||
p.fusionSpecies.speciesId === SpeciesId.SNORUNT),
) )
.flatMap(p => { .flatMap(p => {
const evolutions = pokemonEvolutions[p.fusionSpecies!.speciesId]; const evolutions = pokemonEvolutions[p.fusionSpecies!.speciesId];

View File

@ -100,7 +100,7 @@ import { UnlockPhase } from "#app/phases/unlock-phase";
import { VictoryPhase } from "#app/phases/victory-phase"; import { VictoryPhase } from "#app/phases/victory-phase";
import { WeatherEffectPhase } from "#app/phases/weather-effect-phase"; import { WeatherEffectPhase } from "#app/phases/weather-effect-phase";
/** /*
* Manager for phases used by battle scene. * Manager for phases used by battle scene.
* *
* *This file must not be imported or used directly. The manager is exclusively used by the battle scene and is not intended for external use.* * *This file must not be imported or used directly. The manager is exclusively used by the battle scene and is not intended for external use.*

View File

@ -38,14 +38,11 @@ export class AttemptRunPhase extends PokemonPhase {
alpha: 0, alpha: 0,
duration: 250, duration: 250,
ease: "Sine.easeIn", ease: "Sine.easeIn",
onComplete: () => onComplete: () => enemyField.forEach(enemyPokemon => enemyPokemon.destroy()),
// biome-ignore lint/complexity/noForEach: TODO
enemyField.forEach(enemyPokemon => enemyPokemon.destroy()),
}); });
globalScene.clearEnemyHeldItemModifiers(); globalScene.clearEnemyHeldItemModifiers();
// biome-ignore lint/complexity/noForEach: TODO
enemyField.forEach(enemyPokemon => { enemyField.forEach(enemyPokemon => {
enemyPokemon.hideInfo().then(() => enemyPokemon.destroy()); enemyPokemon.hideInfo().then(() => enemyPokemon.destroy());
enemyPokemon.hp = 0; enemyPokemon.hp = 0;

View File

@ -299,7 +299,7 @@ export class GameOverPhase extends BattlePhase {
battleType: globalScene.currentBattle.battleType, battleType: globalScene.currentBattle.battleType,
trainer: globalScene.currentBattle.trainer ? new TrainerData(globalScene.currentBattle.trainer) : null, trainer: globalScene.currentBattle.trainer ? new TrainerData(globalScene.currentBattle.trainer) : null,
gameVersion: globalScene.game.config.gameVersion, gameVersion: globalScene.game.config.gameVersion,
timestamp: new Date().getTime(), timestamp: Date.now(),
challenges: globalScene.gameMode.challenges.map(c => new ChallengeData(c)), challenges: globalScene.gameMode.challenges.map(c => new ChallengeData(c)),
mysteryEncounterType: globalScene.currentBattle.mysteryEncounter?.encounterType ?? -1, mysteryEncounterType: globalScene.currentBattle.mysteryEncounter?.encounterType ?? -1,
mysteryEncounterSaveData: globalScene.mysteryEncounterSaveData, mysteryEncounterSaveData: globalScene.mysteryEncounterSaveData,

View File

@ -696,12 +696,9 @@ export class MoveEffectPhase extends PokemonPhase {
* @param target - The {@linkcode Pokemon} to be removed * @param target - The {@linkcode Pokemon} to be removed
*/ */
protected removeTarget(target: Pokemon): void { protected removeTarget(target: Pokemon): void {
const targetIndex = this.targets.findIndex(ind => ind === target.getBattlerIndex()); const targetIndex = this.targets.indexOf(target.getBattlerIndex());
if (targetIndex !== -1) { if (targetIndex !== -1) {
this.targets.splice( this.targets.splice(this.targets.indexOf(target.getBattlerIndex()), 1);
this.targets.findIndex(ind => ind === target.getBattlerIndex()),
1,
);
} }
} }

View File

@ -44,7 +44,7 @@ export class SwitchSummonPhase extends SummonPhase {
preSummon(): void { preSummon(): void {
if (!this.player) { if (!this.player) {
if (this.slotIndex === -1) { if (this.slotIndex === -1) {
//@ts-ignore //@ts-expect-error
this.slotIndex = globalScene.currentBattle.trainer?.getNextSummonIndex( this.slotIndex = globalScene.currentBattle.trainer?.getNextSummonIndex(
!this.fieldIndex ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER, !this.fieldIndex ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER,
); // TODO: what would be the default trainer-slot fallback? ); // TODO: what would be the default trainer-slot fallback?

View File

@ -33,7 +33,7 @@ export abstract class ApiBase {
* @param dataType The data-type of the {@linkcode bodyData}. * @param dataType The data-type of the {@linkcode bodyData}.
*/ */
protected async doPost<D = undefined>(path: string, bodyData?: D, dataType: DataType = "json") { protected async doPost<D = undefined>(path: string, bodyData?: D, dataType: DataType = "json") {
let body: string | undefined = undefined; let body: string | undefined;
const headers: HeadersInit = {}; const headers: HeadersInit = {};
if (bodyData) { if (bodyData) {

View File

@ -9,7 +9,7 @@ import type BattleScene from "#app/battle-scene";
// Regex patterns // Regex patterns
/** Regex matching double underscores */ /** Regex matching double underscores */
const DUNDER_REGEX = /\_{2}/g; const DUNDER_REGEX = /_{2}/g;
/** /**
* Calculate the sprite ID from a pokemon form. * Calculate the sprite ID from a pokemon form.

View File

@ -1,6 +1,6 @@
import { expSpriteKeys } from "#app/sprites/sprite-keys"; import { expSpriteKeys } from "#app/sprites/sprite-keys";
const expKeyRegex = /^pkmn__?(back__)?(shiny__)?(female__)?(\d+)(\-.*?)?(?:_[1-3])?$/; const expKeyRegex = /^pkmn__?(back__)?(shiny__)?(female__)?(\d+)(-.*?)?(?:_[1-3])?$/;
export function hasExpSprite(key: string): boolean { export function hasExpSprite(key: string): boolean {
const keyMatch = expKeyRegex.exec(key); const keyMatch = expKeyRegex.exec(key);

View File

@ -39,7 +39,7 @@ import { setSettingGamepad, SettingGamepad, settingGamepadDefaults } from "#app/
import type { SettingKeyboard } from "#app/system/settings/settings-keyboard"; import type { SettingKeyboard } from "#app/system/settings/settings-keyboard";
import { setSettingKeyboard } from "#app/system/settings/settings-keyboard"; import { setSettingKeyboard } from "#app/system/settings/settings-keyboard";
import { TagAddedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; import { TagAddedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena";
// biome-ignore lint/style/noNamespaceImport: Something weird is going on here and I don't want to touch it // biome-ignore lint/performance/noNamespaceImport: Something weird is going on here and I don't want to touch it
import * as Modifier from "#app/modifier/modifier"; import * as Modifier from "#app/modifier/modifier";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import ChallengeData from "#app/system/challenge-data"; import ChallengeData from "#app/system/challenge-data";
@ -300,7 +300,7 @@ export class GameData {
voucherCounts: this.voucherCounts, voucherCounts: this.voucherCounts,
eggs: this.eggs.map(e => new EggData(e)), eggs: this.eggs.map(e => new EggData(e)),
gameVersion: globalScene.game.config.gameVersion, gameVersion: globalScene.game.config.gameVersion,
timestamp: new Date().getTime(), timestamp: Date.now(),
eggPity: this.eggPity.slice(0), eggPity: this.eggPity.slice(0),
unlockPity: this.unlockPity.slice(0), unlockPity: this.unlockPity.slice(0),
}; };
@ -930,7 +930,7 @@ export class GameData {
? new TrainerData(globalScene.currentBattle.trainer) ? new TrainerData(globalScene.currentBattle.trainer)
: null, : null,
gameVersion: globalScene.game.config.gameVersion, gameVersion: globalScene.game.config.gameVersion,
timestamp: new Date().getTime(), timestamp: Date.now(),
challenges: globalScene.gameMode.challenges.map(c => new ChallengeData(c)), challenges: globalScene.gameMode.challenges.map(c => new ChallengeData(c)),
mysteryEncounterType: globalScene.currentBattle.mysteryEncounter?.encounterType ?? -1, mysteryEncounterType: globalScene.currentBattle.mysteryEncounter?.encounterType ?? -1,
mysteryEncounterSaveData: globalScene.mysteryEncounterSaveData, mysteryEncounterSaveData: globalScene.mysteryEncounterSaveData,
@ -939,7 +939,7 @@ export class GameData {
} }
getSession(slotId: number): Promise<SessionSaveData | null> { getSession(slotId: number): Promise<SessionSaveData | null> {
// biome-ignore lint/suspicious/noAsyncPromiseExecutor: <explanation> // biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO: fix this
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
if (slotId < 0) { if (slotId < 0) {
return resolve(null); return resolve(null);
@ -980,7 +980,7 @@ export class GameData {
} }
loadSession(slotId: number, sessionData?: SessionSaveData): Promise<boolean> { loadSession(slotId: number, sessionData?: SessionSaveData): Promise<boolean> {
// biome-ignore lint/suspicious/noAsyncPromiseExecutor: <explanation> // biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO: fix this
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
const initSessionFromData = async (sessionData: SessionSaveData) => { const initSessionFromData = async (sessionData: SessionSaveData) => {
@ -1610,7 +1610,7 @@ export class GameData {
} }
} }
this.defaultDexData = Object.assign({}, data); this.defaultDexData = { ...data };
this.dexData = data; this.dexData = data;
} }

View File

@ -1,3 +1,5 @@
/** biome-ignore-all lint/performance/noNamespaceImport: Convenience */
import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator"; import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator";
import type { SettingsSaveMigrator } from "#app/@types/SettingsSaveMigrator"; import type { SettingsSaveMigrator } from "#app/@types/SettingsSaveMigrator";
import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator"; import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator";
@ -48,23 +50,18 @@ export const settingsMigrators: Readonly<SettingsSaveMigrator[]> = [settingsMigr
// import * as vA_B_C from "./versions/vA_B_C"; // import * as vA_B_C from "./versions/vA_B_C";
// --- v1.0.4 (and below) PATCHES --- // // --- v1.0.4 (and below) PATCHES --- //
// biome-ignore lint/style/noNamespaceImport: Convenience (TODO: make this a file-wide ignore when Biome supports those)
import * as v1_0_4 from "./versions/v1_0_4"; import * as v1_0_4 from "./versions/v1_0_4";
// --- v1.7.0 PATCHES --- // // --- v1.7.0 PATCHES --- //
// biome-ignore lint/style/noNamespaceImport: Convenience
import * as v1_7_0 from "./versions/v1_7_0"; import * as v1_7_0 from "./versions/v1_7_0";
// --- v1.8.3 PATCHES --- // // --- v1.8.3 PATCHES --- //
// biome-ignore lint/style/noNamespaceImport: Convenience
import * as v1_8_3 from "./versions/v1_8_3"; import * as v1_8_3 from "./versions/v1_8_3";
// --- v1.9.0 PATCHES --- // // --- v1.9.0 PATCHES --- //
// biome-ignore lint/style/noNamespaceImport: Convenience
import * as v1_9_0 from "./versions/v1_9_0"; import * as v1_9_0 from "./versions/v1_9_0";
// --- v1.10.0 PATCHES --- // // --- v1.10.0 PATCHES --- //
// biome-ignore lint/style/noNamespaceImport: Convenience
import * as v1_10_0 from "./versions/v1_10_0"; import * as v1_10_0 from "./versions/v1_10_0";
/** Current game version */ /** Current game version */

View File

@ -6,8 +6,8 @@ const repeatInputDelayMillis = 250;
export default class TouchControl { export default class TouchControl {
events: EventEmitter; events: EventEmitter;
private buttonLock: string[] = new Array(); private buttonLock: string[] = [];
private inputInterval: NodeJS.Timeout[] = new Array(); private inputInterval: NodeJS.Timeout[] = [];
/** Whether touch controls are disabled */ /** Whether touch controls are disabled */
private disabled = false; private disabled = false;
/** Whether the last touch event has finished before disabling */ /** Whether the last touch event has finished before disabling */
@ -42,7 +42,7 @@ export default class TouchControl {
document.querySelectorAll(".apad-button").forEach(element => this.preventElementZoom(element as HTMLElement)); document.querySelectorAll(".apad-button").forEach(element => this.preventElementZoom(element as HTMLElement));
// Select all elements with the 'data-key' attribute and bind keys to them // Select all elements with the 'data-key' attribute and bind keys to them
for (const button of document.querySelectorAll("[data-key]")) { for (const button of document.querySelectorAll("[data-key]")) {
// @ts-ignore - Bind the key to the button using the dataset key // @ts-expect-error - Bind the key to the button using the dataset key
this.bindKey(button, button.dataset.key); this.bindKey(button, button.dataset.key);
} }
} }
@ -208,7 +208,7 @@ export function isMobile(): boolean {
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test( /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(
a, a,
) || ) ||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test( /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(
a.substr(0, 4), a.substr(0, 4),
) )
) { ) {

View File

@ -176,12 +176,12 @@ export class UiInputs {
return; return;
} }
switch (globalScene.ui?.getMode()) { switch (globalScene.ui?.getMode()) {
// biome-ignore lint/suspicious/noFallthroughSwitchClause: falls through to show menu overlay
case UiMode.MESSAGE: { case UiMode.MESSAGE: {
const messageHandler = globalScene.ui.getHandler<MessageUiHandler>(); const messageHandler = globalScene.ui.getHandler<MessageUiHandler>();
if (!messageHandler.pendingPrompt || messageHandler.isTextAnimationInProgress()) { if (!messageHandler.pendingPrompt || messageHandler.isTextAnimationInProgress()) {
return; return;
} }
// biome-ignore lint/suspicious/noFallthroughSwitchClause: falls through to show menu overlay
} }
case UiMode.TITLE: case UiMode.TITLE:
case UiMode.COMMAND: case UiMode.COMMAND:

View File

@ -52,7 +52,7 @@ export default class BattleFlyout extends Phaser.GameObjects.Container {
/** The array of {@linkcode Phaser.GameObjects.Text} objects which are drawn on the flyout */ /** The array of {@linkcode Phaser.GameObjects.Text} objects which are drawn on the flyout */
private flyoutText: Phaser.GameObjects.Text[] = new Array(4); private flyoutText: Phaser.GameObjects.Text[] = new Array(4);
/** The array of {@linkcode MoveInfo} used to track moves for the {@linkcode Pokemon} linked to the flyout */ /** The array of {@linkcode MoveInfo} used to track moves for the {@linkcode Pokemon} linked to the flyout */
private moveInfo: MoveInfo[] = new Array(); private moveInfo: MoveInfo[] = [];
/** Current state of the flyout's visibility */ /** Current state of the flyout's visibility */
public flyoutVisible = false; public flyoutVisible = false;

View File

@ -41,24 +41,15 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container {
this.setup(); this.setup();
} }
/** /** When set to `true`, disables the buttons; when set to `false`, enables the buttons. */
* Sets the updating state and updates button states accordingly. get isUpdating(): boolean {
* If value is true (updating), disables the buttons; if false, enables the buttons. return this._isUpdating;
* @param {boolean} value - The new updating state. }
*/ set isUpdating(value: boolean) {
set isUpdating(value) {
this._isUpdating = value; this._isUpdating = value;
this.setButtonsState(!value); this.setButtonsState(!value);
} }
/**
* Gets the current updating state.
* @returns {boolean} - The current updating state.
*/
get isUpdating() {
return this._isUpdating;
}
setup() { setup() {
const titleWindow = addWindow(0, 0, 114, 18, false, false, undefined, undefined, WindowVariant.THIN); const titleWindow = addWindow(0, 0, 114, 18, false, false, undefined, undefined, WindowVariant.THIN);
this.add(titleWindow); this.add(titleWindow);

View File

@ -625,7 +625,7 @@ export default class EggGachaUiHandler extends MessageUiHandler {
const infoContainer = this.gachaInfoContainers[gachaType]; const infoContainer = this.gachaInfoContainers[gachaType];
switch (gachaType as GachaType) { switch (gachaType as GachaType) {
case GachaType.LEGENDARY: { case GachaType.LEGENDARY: {
const species = getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(new Date().getTime())); const species = getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(Date.now()));
const pokemonIcon = infoContainer.getAt(1) as Phaser.GameObjects.Sprite; const pokemonIcon = infoContainer.getAt(1) as Phaser.GameObjects.Sprite;
pokemonIcon.setTexture(species.getIconAtlasKey(), species.getIconId(false)); pokemonIcon.setTexture(species.getIconAtlasKey(), species.getIconId(false));
break; break;

View File

@ -591,9 +591,9 @@ export default class MysteryEncounterUiHandler extends UiHandler {
// Auto-color options green/blue for good/bad by looking for (+)/(-) // Auto-color options green/blue for good/bad by looking for (+)/(-)
if (text) { if (text) {
const primaryStyleString = [...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))!][0]; const primaryStyleString = [...text.match(new RegExp(/\[color=[^[]*\]\[shadow=[^[]*\]/i))!][0];
text = text.replace( text = text.replace(
/(\(\+\)[^\(\[]*)/gi, /(\(\+\)[^([]*)/gi,
substring => substring =>
"[/color][/shadow]" + "[/color][/shadow]" +
getBBCodeFrag(substring, TextStyle.SUMMARY_GREEN) + getBBCodeFrag(substring, TextStyle.SUMMARY_GREEN) +
@ -601,7 +601,7 @@ export default class MysteryEncounterUiHandler extends UiHandler {
primaryStyleString, primaryStyleString,
); );
text = text.replace( text = text.replace(
/(\(\-\)[^\(\[]*)/gi, /(\(-\)[^([]*)/gi,
substring => substring =>
"[/color][/shadow]" + "[/color][/shadow]" +
getBBCodeFrag(substring, TextStyle.SUMMARY_BLUE) + getBBCodeFrag(substring, TextStyle.SUMMARY_BLUE) +

View File

@ -2057,7 +2057,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
} }
let newSpecies: PokemonSpecies; let newSpecies: PokemonSpecies;
if (this.filteredIndices) { if (this.filteredIndices) {
const index = this.filteredIndices.findIndex(id => id === this.species.speciesId); const index = this.filteredIndices.indexOf(this.species.speciesId);
const newIndex = index <= 0 ? this.filteredIndices.length - 1 : index - 1; const newIndex = index <= 0 ? this.filteredIndices.length - 1 : index - 1;
newSpecies = getPokemonSpecies(this.filteredIndices[newIndex]); newSpecies = getPokemonSpecies(this.filteredIndices[newIndex]);
} else { } else {
@ -2096,7 +2096,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
} }
let newSpecies: PokemonSpecies; let newSpecies: PokemonSpecies;
if (this.filteredIndices) { if (this.filteredIndices) {
const index = this.filteredIndices.findIndex(id => id === this.species.speciesId); const index = this.filteredIndices.indexOf(this.species.speciesId);
const newIndex = index >= this.filteredIndices.length - 1 ? 0 : index + 1; const newIndex = index >= this.filteredIndices.length - 1 ? 0 : index + 1;
newSpecies = getPokemonSpecies(this.filteredIndices[newIndex]); newSpecies = getPokemonSpecies(this.filteredIndices[newIndex]);
} else { } else {
@ -2321,7 +2321,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
this.showStats(); this.showStats();
} else { } else {
this.statsContainer.setVisible(false); this.statsContainer.setVisible(false);
//@ts-ignore //@ts-expect-error
this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. what. how? huh? this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. what. how? huh?
} }
} }
@ -2786,7 +2786,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
this.statsMode = false; this.statsMode = false;
this.statsContainer.setVisible(false); this.statsContainer.setVisible(false);
this.pokemonSprite.setVisible(true); this.pokemonSprite.setVisible(true);
//@ts-ignore //@ts-expect-error
this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. !?!? this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. !?!?
} }
} }

View File

@ -1389,7 +1389,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
const fitsMoves = fitsMove1 && fitsMove2; const fitsMoves = fitsMove1 && fitsMove2;
if (fitsEggMove1 && !fitsLevelMove1) { if (fitsEggMove1 && !fitsLevelMove1) {
const em1 = eggMoves.findIndex(name => name === selectedMove1); const em1 = eggMoves.indexOf(selectedMove1);
if ((starterData.eggMoves & (1 << em1)) === 0) { if ((starterData.eggMoves & (1 << em1)) === 0) {
data.eggMove1 = false; data.eggMove1 = false;
} else { } else {
@ -1399,7 +1399,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
data.tmMove1 = true; data.tmMove1 = true;
} }
if (fitsEggMove2 && !fitsLevelMove2) { if (fitsEggMove2 && !fitsLevelMove2) {
const em2 = eggMoves.findIndex(name => name === selectedMove2); const em2 = eggMoves.indexOf(selectedMove2);
if ((starterData.eggMoves & (1 << em2)) === 0) { if ((starterData.eggMoves & (1 << em2)) === 0) {
data.eggMove2 = false; data.eggMove2 = false;
} else { } else {

View File

@ -19,7 +19,7 @@ import { PokemonType } from "#enums/pokemon-type";
import { TypeColor, TypeShadow } from "#app/enums/color"; import { TypeColor, TypeShadow } from "#app/enums/color";
import { getNatureStatMultiplier, getNatureName } from "../data/nature"; import { getNatureStatMultiplier, getNatureName } from "../data/nature";
import { getVariantTint } from "#app/sprites/variant"; import { getVariantTint } from "#app/sprites/variant";
// biome-ignore lint/style/noNamespaceImport: See `src/system/game-data.ts` // biome-ignore lint/performance/noNamespaceImport: See `src/system/game-data.ts`
import * as Modifier from "#app/modifier/modifier"; import * as Modifier from "#app/modifier/modifier";
import type { SpeciesId } from "#enums/species-id"; import type { SpeciesId } from "#enums/species-id";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";

View File

@ -2,7 +2,7 @@ import i18next from "i18next";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { Button } from "#enums/buttons"; import { Button } from "#enums/buttons";
import { GameMode } from "../game-mode"; import { GameMode } from "../game-mode";
// biome-ignore lint/style/noNamespaceImport: See `src/system/game-data.ts` // biome-ignore lint/performance/noNamespaceImport: See `src/system/game-data.ts`
import * as Modifier from "#app/modifier/modifier"; import * as Modifier from "#app/modifier/modifier";
import type { SessionSaveData } from "../system/game-data"; import type { SessionSaveData } from "../system/game-data";
import type PokemonData from "../system/pokemon-data"; import type PokemonData from "../system/pokemon-data";

View File

@ -209,7 +209,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler
settingFiltered.forEach((setting, s) => { settingFiltered.forEach((setting, s) => {
// Convert the setting key from format 'Key_Name' to 'Key name' for display. // Convert the setting key from format 'Key_Name' to 'Key name' for display.
const settingName = setting.replace(/\_/g, " "); const settingName = setting.replace(/_/g, " ");
// Create and add a text object for the setting name to the scene. // Create and add a text object for the setting name to the scene.
const isLock = this.settingBlacklisted.includes(this.setting[setting]); const isLock = this.settingBlacklisted.includes(this.setting[setting]);

View File

@ -16,7 +16,7 @@ export class NavigationManager {
private static instance: NavigationManager; private static instance: NavigationManager;
public modes: UiMode[]; public modes: UiMode[];
public selectedMode: UiMode = UiMode.SETTINGS; public selectedMode: UiMode = UiMode.SETTINGS;
public navigationMenus: NavigationMenu[] = new Array<NavigationMenu>(); public navigationMenus: NavigationMenu[] = [];
public labels: string[]; public labels: string[];
/** /**
@ -105,7 +105,7 @@ export class NavigationManager {
export default class NavigationMenu extends Phaser.GameObjects.Container { export default class NavigationMenu extends Phaser.GameObjects.Container {
private navigationIcons: InputsIcons; private navigationIcons: InputsIcons;
protected headerTitles: Phaser.GameObjects.Text[] = new Array<Phaser.GameObjects.Text>(); protected headerTitles: Phaser.GameObjects.Text[] = [];
/** /**
* Creates an instance of NavigationMenu. * Creates an instance of NavigationMenu.

View File

@ -2822,7 +2822,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
iconElement: GameObjects.Sprite, iconElement: GameObjects.Sprite,
controlLabel: GameObjects.Text, controlLabel: GameObjects.Text,
): void { ): void {
// biome-ignore lint/suspicious/noImplicitAnyLet: TODO
let iconPath: string; let iconPath: string;
// touch controls cannot be rebound as is, and are just emulating a keyboard event. // touch controls cannot be rebound as is, and are just emulating a keyboard event.
// Additionally, since keyboard controls can be rebound (and will be displayed when they are), we need to have special handling for the touch controls // Additionally, since keyboard controls can be rebound (and will be displayed when they are), we need to have special handling for the touch controls
@ -2856,7 +2855,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
} else { } else {
iconPath = globalScene.inputController?.getIconForLatestInputRecorded(iconSetting); iconPath = globalScene.inputController?.getIconForLatestInputRecorded(iconSetting);
} }
// @ts-ignore: TODO can iconPath actually be undefined? // @ts-expect-error: TODO can iconPath actually be undefined?
iconElement.setTexture(gamepadType, iconPath); iconElement.setTexture(gamepadType, iconPath);
iconElement.setPosition(this.instructionRowX, this.instructionRowY); iconElement.setPosition(this.instructionRowX, this.instructionRowY);
controlLabel.setPosition(this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY); controlLabel.setPosition(this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY);
@ -3481,7 +3480,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.showStats(); this.showStats();
} else { } else {
this.statsContainer.setVisible(false); this.statsContainer.setVisible(false);
//@ts-ignore //@ts-expect-error
this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. what. how? huh? this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. what. how? huh?
} }
} }
@ -4489,7 +4488,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.statsMode = false; this.statsMode = false;
this.statsContainer.setVisible(false); this.statsContainer.setVisible(false);
this.pokemonSprite.setVisible(!!this.speciesStarterDexEntry?.caughtAttr); this.pokemonSprite.setVisible(!!this.speciesStarterDexEntry?.caughtAttr);
//@ts-ignore //@ts-expect-error
this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. !?!? this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. !?!?
this.teraIcon.setVisible(globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id)); this.teraIcon.setVisible(globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id));
const props = globalScene.gameData.getSpeciesDexAttrProps( const props = globalScene.gameData.getSpeciesDexAttrProps(

View File

@ -117,7 +117,7 @@ export default class SummaryUiHandler extends UiHandler {
private pokemon: PlayerPokemon | null; private pokemon: PlayerPokemon | null;
private playerParty: boolean; private playerParty: boolean;
/**This is set to false when checking the summary of a freshly caught Pokemon as it is not part of a player's party yet but still needs to display its items**/ /**This is set to false when checking the summary of a freshly caught Pokemon as it is not part of a player's party yet but still needs to display its items*/
private newMove: Move | null; private newMove: Move | null;
private moveSelectFunction: Function | null; private moveSelectFunction: Function | null;
private transitioning: boolean; private transitioning: boolean;

View File

@ -300,7 +300,7 @@ export function getTextWithColors(
): string { ): string {
// Apply primary styling before anything else // Apply primary styling before anything else
let text = getBBCodeFrag(content, primaryStyle, uiTheme) + "[/color][/shadow]"; let text = getBBCodeFrag(content, primaryStyle, uiTheme) + "[/color][/shadow]";
const primaryStyleString = [...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))!][0]; const primaryStyleString = [...text.match(new RegExp(/\[color=[^[]*\]\[shadow=[^[]*\]/i))!][0];
/* For money text displayed in game windows, we can't use the default {@linkcode TextStyle.MONEY} /* For money text displayed in game windows, we can't use the default {@linkcode TextStyle.MONEY}
* or it will look wrong in legacy mode because of the different window background color * or it will look wrong in legacy mode because of the different window background color
@ -320,7 +320,7 @@ export function getTextWithColors(
}); });
// Remove extra style block at the end // Remove extra style block at the end
return text.replace(/\[color=[^\[]*\]\[shadow=[^\[]*\]\[\/color\]\[\/shadow\]/gi, ""); return text.replace(/\[color=[^[]*\]\[shadow=[^[]*\]\[\/color\]\[\/shadow\]/gi, "");
} }
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: This is a giant switch which is the best option. // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: This is a giant switch which is the best option.

View File

@ -10,7 +10,7 @@ export const MissingTextureKey = "__MISSING";
export function toReadableString(str: string): string { export function toReadableString(str: string): string {
return str return str
.replace(/\_/g, " ") .replace(/_/g, " ")
.split(" ") .split(" ")
.map(s => `${s.slice(0, 1)}${s.slice(1).toLowerCase()}`) .map(s => `${s.slice(0, 1)}${s.slice(1).toLowerCase()}`)
.join(" "); .join(" ");
@ -583,7 +583,7 @@ export function isBetween(num: number, min: number, max: number): boolean {
* @param move the move for which the animation filename is needed * @param move the move for which the animation filename is needed
*/ */
export function animationFileName(move: MoveId): string { export function animationFileName(move: MoveId): string {
return MoveId[move].toLowerCase().replace(/\_/g, "-"); return MoveId[move].toLowerCase().replace(/_/g, "-");
} }
/** /**

View File

@ -2,7 +2,7 @@ import { isBeta } from "./utility-vars";
export function setCookie(cName: string, cValue: string): void { export function setCookie(cName: string, cValue: string): void {
const expiration = new Date(); const expiration = new Date();
expiration.setTime(new Date().getTime() + 3600000 * 24 * 30 * 3 /*7*/); expiration.setTime(Date.now() + 3600000 * 24 * 30 * 3 /*7*/);
document.cookie = `${cName}=${cValue};Secure;SameSite=Strict;Domain=${window.location.hostname};Path=/;Expires=${expiration.toUTCString()}`; document.cookie = `${cName}=${cValue};Secure;SameSite=Strict;Domain=${window.location.hostname};Path=/;Expires=${expiration.toUTCString()}`;
} }

View File

@ -40,7 +40,7 @@ describe("Abilities - Mycelium Might", () => {
* https://bulbapedia.bulbagarden.net/wiki/Mycelium_Might_(Ability) * https://bulbapedia.bulbagarden.net/wiki/Mycelium_Might_(Ability)
* https://bulbapedia.bulbagarden.net/wiki/Priority * https://bulbapedia.bulbagarden.net/wiki/Priority
* https://www.smogon.com/forums/threads/scarlet-violet-battle-mechanics-research.3709545/page-24 * https://www.smogon.com/forums/threads/scarlet-violet-battle-mechanics-research.3709545/page-24
**/ */
it("will move last in its priority bracket and ignore protective abilities", async () => { it("will move last in its priority bracket and ignore protective abilities", async () => {
await game.classicMode.startBattle([SpeciesId.REGIELEKI]); await game.classicMode.startBattle([SpeciesId.REGIELEKI]);

View File

@ -35,7 +35,7 @@ describe("Abilities - Stall", () => {
* References: * References:
* https://bulbapedia.bulbagarden.net/wiki/Stall_(Ability) * https://bulbapedia.bulbagarden.net/wiki/Stall_(Ability)
* https://bulbapedia.bulbagarden.net/wiki/Priority * https://bulbapedia.bulbagarden.net/wiki/Priority
**/ */
it("Pokemon with Stall should move last in its priority bracket regardless of speed", async () => { it("Pokemon with Stall should move last in its priority bracket regardless of speed", async () => {
await game.classicMode.startBattle([SpeciesId.SHUCKLE]); await game.classicMode.startBattle([SpeciesId.SHUCKLE]);

View File

@ -66,7 +66,7 @@ describe("Abilities - Wonder Skin", () => {
it(`does not affect pokemon with ${ability[1]}`, async () => { it(`does not affect pokemon with ${ability[1]}`, async () => {
const moveToCheck = allMoves[MoveId.CHARM]; const moveToCheck = allMoves[MoveId.CHARM];
// @ts-ignore ts doesn't know that ability[0] is an ability and not a string... // @ts-expect-error ts doesn't know that ability[0] is an ability and not a string...
game.override.ability(ability[0]); game.override.ability(ability[0]);
vi.spyOn(moveToCheck, "calculateBattleAccuracy"); vi.spyOn(moveToCheck, "calculateBattleAccuracy");

View File

@ -1,4 +1,4 @@
// biome-ignore lint/style/noNamespaceImport: Necessary for mocks // biome-ignore lint/performance/noNamespaceImport: Necessary for mocks
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { Status } from "#app/data/status-effect"; import { Status } from "#app/data/status-effect";
import { CommandPhase } from "#app/phases/command-phase"; import { CommandPhase } from "#app/phases/command-phase";

View File

@ -26,9 +26,9 @@ describe("check if every variant's sprite are correctly set", () => {
femaleVariant = masterlist.female; femaleVariant = masterlist.female;
backVariant = masterlist.back; backVariant = masterlist.back;
// @ts-ignore // @ts-expect-error
delete masterlist.female; // TODO: resolve ts-ignore delete masterlist.female; // TODO: resolve ts-ignore
//@ts-ignore //@ts-expect-error
delete masterlist.back; //TODO: resolve ts-ignore delete masterlist.back; //TODO: resolve ts-ignore
}); });

View File

@ -15,15 +15,15 @@ import { vi } from "vitest";
import { version } from "../../package.json"; import { version } from "../../package.json";
import { MockGameObjectCreator } from "./mocks/mockGameObjectCreator"; import { MockGameObjectCreator } from "./mocks/mockGameObjectCreator";
import { MockTimedEventManager } from "./mocks/mockTimedEventManager"; import { MockTimedEventManager } from "./mocks/mockTimedEventManager";
import { PokedexMonContainer } from "#app/ui/pokedex-mon-container";
import MockContainer from "./mocks/mocksContainer/mockContainer";
import InputManager = Phaser.Input.InputManager; import InputManager = Phaser.Input.InputManager;
import KeyboardManager = Phaser.Input.Keyboard.KeyboardManager; import KeyboardManager = Phaser.Input.Keyboard.KeyboardManager;
import KeyboardPlugin = Phaser.Input.Keyboard.KeyboardPlugin; import KeyboardPlugin = Phaser.Input.Keyboard.KeyboardPlugin;
import GamepadPlugin = Phaser.Input.Gamepad.GamepadPlugin; import GamepadPlugin = Phaser.Input.Gamepad.GamepadPlugin;
import EventEmitter = Phaser.Events.EventEmitter; import EventEmitter = Phaser.Events.EventEmitter;
import UpdateList = Phaser.GameObjects.UpdateList; import UpdateList = Phaser.GameObjects.UpdateList;
import { PokedexMonContainer } from "#app/ui/pokedex-mon-container"; // biome-ignore lint/performance/noNamespaceImport: Necessary in order to mock the var
import MockContainer from "./mocks/mocksContainer/mockContainer";
// biome-ignore lint/style/noNamespaceImport: Necessary in order to mock the var
import * as bypassLoginModule from "#app/global-vars/bypass-login"; import * as bypassLoginModule from "#app/global-vars/bypass-login";
window.URL.createObjectURL = (blob: Blob) => { window.URL.createObjectURL = (blob: Blob) => {

View File

@ -89,7 +89,7 @@ class Fakepad extends Phaser.Input.Gamepad.Gamepad {
public index: number; public index: number;
constructor(pad) { constructor(pad) {
//@ts-ignore //@ts-expect-error
super(undefined, { ...pad, buttons: pad.deviceMapping, axes: [] }); //TODO: resolve ts-ignore super(undefined, { ...pad, buttons: pad.deviceMapping, axes: [] }); //TODO: resolve ts-ignore
this.id = "xbox_360_fakepad"; this.id = "xbox_360_fakepad";
this.index = 0; this.index = 0;

View File

@ -1,6 +1,7 @@
import MockContainer from "#test/testUtils/mocks/mocksContainer/mockContainer"; import MockContainer from "#test/testUtils/mocks/mocksContainer/mockContainer";
export class MockImage extends MockContainer { export class MockImage extends MockContainer {
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: this is intentional (?)
private texture; private texture;
constructor(textureManager, x, y, texture) { constructor(textureManager, x, y, texture) {

View File

@ -19,13 +19,13 @@ export default class MockSprite implements MockGameObject {
constructor(textureManager, x, y, texture) { constructor(textureManager, x, y, texture) {
this.textureManager = textureManager; this.textureManager = textureManager;
this.scene = textureManager.scene; this.scene = textureManager.scene;
// @ts-ignore // @ts-expect-error
Phaser.GameObjects.Sprite.prototype.setInteractive = this.setInteractive; Phaser.GameObjects.Sprite.prototype.setInteractive = this.setInteractive;
// @ts-ignore // @ts-expect-error
Phaser.GameObjects.Sprite.prototype.setTexture = this.setTexture; Phaser.GameObjects.Sprite.prototype.setTexture = this.setTexture;
// @ts-ignore // @ts-expect-error
Phaser.GameObjects.Sprite.prototype.setSizeToFrame = this.setSizeToFrame; Phaser.GameObjects.Sprite.prototype.setSizeToFrame = this.setSizeToFrame;
// @ts-ignore // @ts-expect-error
Phaser.GameObjects.Sprite.prototype.setFrame = this.setFrame; Phaser.GameObjects.Sprite.prototype.setFrame = this.setFrame;
// Phaser.GameObjects.Sprite.prototype.disable = this.disable; // Phaser.GameObjects.Sprite.prototype.disable = this.disable;

View File

@ -47,7 +47,7 @@ export function initTestFile() {
}); });
BBCodeText.prototype.destroy = () => null; BBCodeText.prototype.destroy = () => null;
// @ts-ignore // @ts-expect-error
BBCodeText.prototype.resize = () => null; BBCodeText.prototype.resize = () => null;
InputText.prototype.setElement = () => null as any; InputText.prototype.setElement = () => null as any;
InputText.prototype.resize = () => null as any; InputText.prototype.resize = () => null as any;