mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-30 13:33:01 +02:00
Merge branch 'beta' into recoil-substitute
This commit is contained in:
commit
03411ba442
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@ -65,7 +65,7 @@ Do the reviewers need to do something special in order to test your changes?
|
||||
- [ ] The PR is self-contained and cannot be split into smaller PRs?
|
||||
- [ ] Have I provided a clear explanation of the changes?
|
||||
- [ ] Have I tested the changes manually?
|
||||
- [ ] Are all unit tests still passing? (`npm run test`)
|
||||
- [ ] Are all unit tests still passing? (`npm run test:silent`)
|
||||
- [ ] Have I created new automated tests (`npm run create-test`) or updated existing tests related to the PR's changes?
|
||||
- [ ] Have I provided screenshots/videos of the changes (if applicable)?
|
||||
- [ ] Have I made sure that any UI change works for both UI themes (default and legacy)?
|
||||
|
20
biome.jsonc
20
biome.jsonc
@ -38,6 +38,9 @@
|
||||
"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: Remove if we ever get down to 0 circular imports
|
||||
"organizeImports": { "enabled": false },
|
||||
"linter": {
|
||||
"ignore": [
|
||||
@ -55,13 +58,13 @@
|
||||
},
|
||||
"style": {
|
||||
"noVar": "error",
|
||||
"useEnumInitializers": "off",
|
||||
"useEnumInitializers": "off", // large enums like Moves/Species would make this cumbersome
|
||||
"useBlockStatements": "error",
|
||||
"useConst": "error",
|
||||
"useImportType": "error",
|
||||
"noNonNullAssertion": "off", // TODO: Turn this on ASAP and fix all non-null assertions
|
||||
"noNonNullAssertion": "off", // TODO: Turn this on ASAP and fix all non-null assertions in non-test files
|
||||
"noParameterAssign": "off",
|
||||
"useExponentiationOperator": "off",
|
||||
"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
|
||||
"useSingleVarDeclarator": "off",
|
||||
"useNodejsImportProtocol": "off",
|
||||
@ -70,17 +73,20 @@
|
||||
},
|
||||
"suspicious": {
|
||||
"noDoubleEquals": "error",
|
||||
// While this would be a nice rule to enable, the current structure of the codebase makes this infeasible
|
||||
// due to being used for move/ability `args` params and save data-related code.
|
||||
// This can likely be enabled for all non-utils files once these are eventually reworked, but until then we leave it off.
|
||||
"noExplicitAny": "off",
|
||||
"noAssignInExpressions": "off",
|
||||
"noPrototypeBuiltins": "off",
|
||||
"noFallthroughSwitchClause": "off",
|
||||
"noImplicitAnyLet": "info", // TODO: Refactor and make this an error
|
||||
"noRedeclare": "off", // TODO: Refactor and make this an error
|
||||
"noFallthroughSwitchClause": "error", // Prevents accidental automatic fallthroughs in switch cases (use disable comment if needed)
|
||||
"noImplicitAnyLet": "warn", // TODO: Refactor and make this an error
|
||||
"noRedeclare": "info", // TODO: Refactor and make this an error
|
||||
"noGlobalIsNan": "off",
|
||||
"noAsyncPromiseExecutor": "warn" // TODO: Refactor and make this an error
|
||||
},
|
||||
"complexity": {
|
||||
"noExcessiveCognitiveComplexity": "warn",
|
||||
"noExcessiveCognitiveComplexity": "warn", // TODO: Refactor and make this an error
|
||||
"useLiteralKeys": "off",
|
||||
"noForEach": "off", // Foreach vs for of is not that simple.
|
||||
"noUselessSwitchCase": "off", // Explicit > Implicit
|
||||
|
@ -1,40 +1,34 @@
|
||||
# ESLint
|
||||
# Biome
|
||||
|
||||
## Key Features
|
||||
|
||||
1. **Automation**:
|
||||
- A pre-commit hook has been added to automatically run ESLint on the added or modified files, ensuring code quality before commits.
|
||||
- A pre-commit hook has been added to automatically run Biome on the added or modified files, ensuring code quality before commits.
|
||||
|
||||
2. **Manual Usage**:
|
||||
- If you prefer not to use the pre-commit hook, you can manually run ESLint to automatically fix issues using the command:
|
||||
- If you prefer not to use the pre-commit hook, you can manually run biome to automatically fix issues using the command:
|
||||
|
||||
```sh
|
||||
npx eslint --fix . or npm run eslint
|
||||
npx @biomejs/biome --write
|
||||
```
|
||||
|
||||
- Running this command will lint all files in the repository.
|
||||
|
||||
3. **GitHub Action**:
|
||||
- A GitHub Action has been added to automatically run ESLint on every push and pull request, ensuring code quality in the CI/CD pipeline.
|
||||
- A GitHub Action has been added to automatically run Biome on every push and pull request, ensuring code quality in the CI/CD pipeline.
|
||||
|
||||
## Summary of ESLint Rules
|
||||
If you are getting linting errors from biome and want to see which files they are coming from, you can find that out by running biome in a way that is configured to only show the errors for that specific rule: ``npx @biomejs/biome lint --only=category/ruleName``
|
||||
|
||||
1. **General Rules**:
|
||||
- **Equality**: Use `===` and `!==` instead of `==` and `!=` (`eqeqeq`).
|
||||
- **Indentation**: Enforce 2-space indentation (`indent`).
|
||||
- **Quotes**: Use doublequotes for strings (`quotes`).
|
||||
- **Variable Declarations**:
|
||||
- Disallow `var`; use `let` or `const` (`no-var`).
|
||||
- Prefer `const` for variables that are never reassigned (`prefer-const`).
|
||||
- **Unused Variables**: Allow unused function parameters but enforce error for other unused variables (`@typescript-eslint/no-unused-vars`).
|
||||
- **End of Line**: Ensure at least one newline at the end of files (`eol-last`).
|
||||
- **Curly Braces**: Enforce the use of curly braces for all control statements (`curly`).
|
||||
- **Brace Style**: Use one true brace style (`1tbs`) for TypeScript-specific syntax (`@typescript-eslint/brace-style`).
|
||||
## Summary of Biome Rules
|
||||
|
||||
2. **TypeScript-Specific Rules**:
|
||||
- **Semicolons**:
|
||||
- Enforce semicolons for TypeScript-specific syntax (`@typescript-eslint/semi`).
|
||||
- Disallow unnecessary semicolons (`@typescript-eslint/no-extra-semi`).
|
||||
We use the [recommended ruleset](https://biomejs.dev/linter/rules/) for Biome, with some customizations to better suit our project's needs.
|
||||
|
||||
## Benefits
|
||||
For a complete list of rules and their configurations, refer to the `biome.jsonc` file in the project root.
|
||||
|
||||
- **Consistency**: Ensures consistent coding style across the project.
|
||||
- **Code Quality**: Helps catch potential errors and improve overall code quality.
|
||||
- **Readability**: Makes the codebase easier to read and maintain.
|
||||
Some things to consider:
|
||||
|
||||
- We have disabled rules that prioritize style over performance, such as `useTemplate`
|
||||
- Some rules are currently marked as warnings (`warn`) to allow for gradual refactoring without blocking development. Do not write new code that triggers these warnings.
|
||||
- The linter is configured to ignore specific files and folders, such as large or complex files that are pending refactors, to improve performance and focus on actionable areas.
|
||||
|
||||
Formatting is also handled by Biome. You should not have to worry about manually formatting your code.
|
||||
|
@ -1,9 +1,10 @@
|
||||
import tseslint from "@typescript-eslint/eslint-plugin";
|
||||
/** @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 [
|
||||
export default tseslint.config(
|
||||
{
|
||||
name: "eslint-config",
|
||||
files: ["src/**/*.{ts,tsx,js,jsx}", "test/**/*.{ts,tsx,js,jsx}"],
|
||||
@ -14,12 +15,11 @@ export default [
|
||||
plugins: {
|
||||
"import-x": importX,
|
||||
"@stylistic/ts": stylisticTs,
|
||||
"@typescript-eslint": tseslint,
|
||||
"@typescript-eslint": tseslint.plugin,
|
||||
},
|
||||
rules: {
|
||||
"prefer-const": "error", // Enforces the use of `const` for variables that are never reassigned
|
||||
"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
|
||||
"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
|
||||
},
|
||||
},
|
||||
@ -33,11 +33,11 @@ export default [
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
"@typescript-eslint": tseslint,
|
||||
"@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/
|
||||
},
|
||||
},
|
||||
];
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "pokemon-rogue-battle",
|
||||
"private": true,
|
||||
"version": "1.8.4",
|
||||
"version": "1.8.5",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
@ -9,7 +9,7 @@
|
||||
"build": "vite build",
|
||||
"build:beta": "vite build --mode beta",
|
||||
"preview": "vite preview",
|
||||
"test": "vitest run",
|
||||
"test": "vitest run --no-isolate",
|
||||
"test:cov": "vitest run --coverage --no-isolate",
|
||||
"test:watch": "vitest watch --coverage --no-isolate",
|
||||
"test:silent": "vitest run --silent --no-isolate",
|
||||
|
@ -516,8 +516,36 @@
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 28, "h": 11 },
|
||||
"sourceSize": { "w": 28, "h": 11 }
|
||||
},
|
||||
"BACK_SLASH.png": {
|
||||
"frame": { "x": 147, "y": 66, "w": 12, "h": 11 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 },
|
||||
"sourceSize": { "w": 12, "h": 11 }
|
||||
},
|
||||
"FORWARD_SLASH.png": {
|
||||
"frame": { "x": 144, "y": 55, "w": 12, "h": 11 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 },
|
||||
"sourceSize": { "w": 12, "h": 11 }
|
||||
},
|
||||
"COMMA.png": {
|
||||
"frame": { "x": 144, "y": 44, "w": 12, "h": 11 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 11 },
|
||||
"sourceSize": { "w": 12, "h": 11 }
|
||||
},
|
||||
"PERIOD.png": {
|
||||
"frame": { "x": 143, "y": 22, "w": 11, "h": 11 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 11 },
|
||||
"sourceSize": { "w": 11, "h": 11 }
|
||||
}
|
||||
},
|
||||
},
|
||||
"meta": {
|
||||
"app": "https://www.aseprite.org/",
|
||||
"version": "1.3.7-dev",
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.6 KiB |
@ -1 +1 @@
|
||||
Subproject commit 18c1963ef309612a5a7fef76f9879709a7202189
|
||||
Subproject commit 833dc40ec7409031fcea147ccbc45ec9c0ba0213
|
@ -31,6 +31,7 @@ const cfg_keyboard_qwerty = {
|
||||
KEY_X: Phaser.Input.Keyboard.KeyCodes.X,
|
||||
KEY_Y: Phaser.Input.Keyboard.KeyCodes.Y,
|
||||
KEY_Z: Phaser.Input.Keyboard.KeyCodes.Z,
|
||||
|
||||
KEY_0: Phaser.Input.Keyboard.KeyCodes.ZERO,
|
||||
KEY_1: Phaser.Input.Keyboard.KeyCodes.ONE,
|
||||
KEY_2: Phaser.Input.Keyboard.KeyCodes.TWO,
|
||||
@ -41,11 +42,7 @@ const cfg_keyboard_qwerty = {
|
||||
KEY_7: Phaser.Input.Keyboard.KeyCodes.SEVEN,
|
||||
KEY_8: Phaser.Input.Keyboard.KeyCodes.EIGHT,
|
||||
KEY_9: Phaser.Input.Keyboard.KeyCodes.NINE,
|
||||
KEY_CTRL: Phaser.Input.Keyboard.KeyCodes.CTRL,
|
||||
KEY_DEL: Phaser.Input.Keyboard.KeyCodes.DELETE,
|
||||
KEY_END: Phaser.Input.Keyboard.KeyCodes.END,
|
||||
KEY_ENTER: Phaser.Input.Keyboard.KeyCodes.ENTER,
|
||||
KEY_ESC: Phaser.Input.Keyboard.KeyCodes.ESC,
|
||||
|
||||
KEY_F1: Phaser.Input.Keyboard.KeyCodes.F1,
|
||||
KEY_F2: Phaser.Input.Keyboard.KeyCodes.F2,
|
||||
KEY_F3: Phaser.Input.Keyboard.KeyCodes.F3,
|
||||
@ -58,24 +55,41 @@ const cfg_keyboard_qwerty = {
|
||||
KEY_F10: Phaser.Input.Keyboard.KeyCodes.F10,
|
||||
KEY_F11: Phaser.Input.Keyboard.KeyCodes.F11,
|
||||
KEY_F12: Phaser.Input.Keyboard.KeyCodes.F12,
|
||||
KEY_HOME: Phaser.Input.Keyboard.KeyCodes.HOME,
|
||||
KEY_INSERT: Phaser.Input.Keyboard.KeyCodes.INSERT,
|
||||
|
||||
KEY_PAGE_DOWN: Phaser.Input.Keyboard.KeyCodes.PAGE_DOWN,
|
||||
KEY_PAGE_UP: Phaser.Input.Keyboard.KeyCodes.PAGE_UP,
|
||||
|
||||
KEY_CTRL: Phaser.Input.Keyboard.KeyCodes.CTRL,
|
||||
KEY_DEL: Phaser.Input.Keyboard.KeyCodes.DELETE,
|
||||
KEY_END: Phaser.Input.Keyboard.KeyCodes.END,
|
||||
KEY_ENTER: Phaser.Input.Keyboard.KeyCodes.ENTER,
|
||||
KEY_ESC: Phaser.Input.Keyboard.KeyCodes.ESC,
|
||||
KEY_HOME: Phaser.Input.Keyboard.KeyCodes.HOME,
|
||||
KEY_INSERT: Phaser.Input.Keyboard.KeyCodes.INSERT,
|
||||
|
||||
KEY_PLUS: Phaser.Input.Keyboard.KeyCodes.NUMPAD_ADD, // Assuming numpad plus
|
||||
KEY_MINUS: Phaser.Input.Keyboard.KeyCodes.NUMPAD_SUBTRACT, // Assuming numpad minus
|
||||
KEY_QUOTATION: Phaser.Input.Keyboard.KeyCodes.QUOTES,
|
||||
KEY_SHIFT: Phaser.Input.Keyboard.KeyCodes.SHIFT,
|
||||
|
||||
KEY_SPACE: Phaser.Input.Keyboard.KeyCodes.SPACE,
|
||||
KEY_TAB: Phaser.Input.Keyboard.KeyCodes.TAB,
|
||||
KEY_TILDE: Phaser.Input.Keyboard.KeyCodes.BACKTICK,
|
||||
|
||||
KEY_ARROW_UP: Phaser.Input.Keyboard.KeyCodes.UP,
|
||||
KEY_ARROW_DOWN: Phaser.Input.Keyboard.KeyCodes.DOWN,
|
||||
KEY_ARROW_LEFT: Phaser.Input.Keyboard.KeyCodes.LEFT,
|
||||
KEY_ARROW_RIGHT: Phaser.Input.Keyboard.KeyCodes.RIGHT,
|
||||
|
||||
KEY_LEFT_BRACKET: Phaser.Input.Keyboard.KeyCodes.OPEN_BRACKET,
|
||||
KEY_RIGHT_BRACKET: Phaser.Input.Keyboard.KeyCodes.CLOSED_BRACKET,
|
||||
|
||||
KEY_SEMICOLON: Phaser.Input.Keyboard.KeyCodes.SEMICOLON,
|
||||
KEY_COMMA: Phaser.Input.Keyboard.KeyCodes.COMMA,
|
||||
KEY_PERIOD: Phaser.Input.Keyboard.KeyCodes.PERIOD,
|
||||
KEY_BACK_SLASH: Phaser.Input.Keyboard.KeyCodes.BACK_SLASH,
|
||||
KEY_FORWARD_SLASH: Phaser.Input.Keyboard.KeyCodes.FORWARD_SLASH,
|
||||
|
||||
KEY_BACKSPACE: Phaser.Input.Keyboard.KeyCodes.BACKSPACE,
|
||||
KEY_ALT: Phaser.Input.Keyboard.KeyCodes.ALT,
|
||||
},
|
||||
@ -160,6 +174,10 @@ const cfg_keyboard_qwerty = {
|
||||
KEY_RIGHT_BRACKET: "RIGHT_BRACKET.png",
|
||||
|
||||
KEY_SEMICOLON: "SEMICOLON.png",
|
||||
KEY_COMMA: "COMMA.png",
|
||||
KEY_PERIOD: "PERIOD.png",
|
||||
KEY_BACK_SLASH: "BACK_SLASH.png",
|
||||
KEY_FORWARD_SLASH: "FORWARD_SLASH.png",
|
||||
|
||||
KEY_BACKSPACE: "BACK.png",
|
||||
KEY_ALT: "ALT.png",
|
||||
|
@ -3172,6 +3172,7 @@ export class PreSetStatusAbAttr extends AbAttr {
|
||||
*/
|
||||
export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr {
|
||||
protected immuneEffects: StatusEffect[];
|
||||
private lastEffect: StatusEffect;
|
||||
|
||||
/**
|
||||
* @param immuneEffects - The status effects to which the Pokémon is immune.
|
||||
@ -3197,6 +3198,7 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr {
|
||||
*/
|
||||
override applyPreSetStatus(pokemon: Pokemon, passive: boolean, simulated: boolean, effect: StatusEffect, cancelled: BooleanHolder, args: any[]): void {
|
||||
cancelled.value = true;
|
||||
this.lastEffect = effect;
|
||||
}
|
||||
|
||||
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
|
||||
@ -3204,7 +3206,7 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr {
|
||||
i18next.t("abilityTriggers:statusEffectImmunityWithName", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
abilityName,
|
||||
statusEffectName: getStatusEffectDescriptor(args[0] as StatusEffect)
|
||||
statusEffectName: getStatusEffectDescriptor(this.lastEffect)
|
||||
}) :
|
||||
i18next.t("abilityTriggers:statusEffectImmunity", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
|
@ -19383,6 +19383,44 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = {
|
||||
[ 100, Moves.SEED_FLARE ],
|
||||
]
|
||||
},
|
||||
[Species.BASCULIN]: {
|
||||
1: [
|
||||
[ 1, Moves.TAIL_WHIP ],
|
||||
[ 1, Moves.WATER_GUN ],
|
||||
[ 4, Moves.TACKLE ],
|
||||
[ 8, Moves.FLAIL ],
|
||||
[ 12, Moves.AQUA_JET ],
|
||||
[ 16, Moves.BITE ],
|
||||
[ 20, Moves.SCARY_FACE ],
|
||||
[ 24, Moves.HEADBUTT ],
|
||||
[ 28, Moves.SOAK ],
|
||||
[ 32, Moves.CRUNCH ],
|
||||
[ 36, Moves.TAKE_DOWN ],
|
||||
[ 40, Moves.FINAL_GAMBIT ],
|
||||
[ 44, Moves.WAVE_CRASH ],
|
||||
[ 48, Moves.THRASH ],
|
||||
[ 52, Moves.DOUBLE_EDGE ],
|
||||
[ 56, Moves.HEAD_SMASH ],
|
||||
],
|
||||
2: [
|
||||
[ 1, Moves.TAIL_WHIP ],
|
||||
[ 1, Moves.WATER_GUN ],
|
||||
[ 4, Moves.TACKLE ],
|
||||
[ 8, Moves.FLAIL ],
|
||||
[ 12, Moves.AQUA_JET ],
|
||||
[ 16, Moves.BITE ],
|
||||
[ 20, Moves.SCARY_FACE ],
|
||||
[ 24, Moves.HEADBUTT ],
|
||||
[ 28, Moves.SOAK ],
|
||||
[ 32, Moves.CRUNCH ],
|
||||
[ 36, Moves.TAKE_DOWN ],
|
||||
[ 40, Moves.UPROAR ],
|
||||
[ 44, Moves.WAVE_CRASH ],
|
||||
[ 48, Moves.THRASH ],
|
||||
[ 52, Moves.DOUBLE_EDGE ],
|
||||
[ 56, Moves.HEAD_SMASH ],
|
||||
]
|
||||
},
|
||||
[Species.KYUREM]: {
|
||||
1: [
|
||||
[ 1, Moves.DRAGON_BREATH ],
|
||||
|
@ -4,12 +4,19 @@ export type SignatureSpecies = {
|
||||
[key in string]: (Species | Species[])[];
|
||||
};
|
||||
|
||||
/*
|
||||
/**
|
||||
* The signature species for each Gym Leader, Elite Four member, and Champion.
|
||||
* The key is the trainer type, and the value is an array of Species or Species arrays.
|
||||
* This is in a separate const so it can be accessed from other places and not just the trainerConfigs
|
||||
*
|
||||
* @remarks
|
||||
* The `Proxy` object allows us to define a handler that will intercept
|
||||
* the property access and return an empty array if the property does not exist in the object.
|
||||
*
|
||||
* This means that accessing `signatureSpecies` will not throw an error if the property does not exist,
|
||||
* but instead default to an empty array.
|
||||
*/
|
||||
export const signatureSpecies: SignatureSpecies = {
|
||||
export const signatureSpecies: SignatureSpecies = new Proxy({
|
||||
// Gym Leaders- Kanto
|
||||
BROCK: [Species.ONIX, Species.GEODUDE, [Species.OMANYTE, Species.KABUTO], Species.AERODACTYL],
|
||||
MISTY: [Species.STARYU, Species.PSYDUCK, Species.WOOPER, Species.LAPRAS],
|
||||
@ -92,71 +99,8 @@ export const signatureSpecies: SignatureSpecies = {
|
||||
RYME: [Species.TOXEL, Species.GREAVARD, Species.SHUPPET, Species.MIMIKYU], // Tera Ghost Toxel
|
||||
TULIP: [Species.FLABEBE, Species.FLITTLE, Species.RALTS, Species.GIRAFARIG], // Tera Psychic Flabebe
|
||||
GRUSHA: [Species.SWABLU, Species.CETODDLE, Species.SNOM, Species.CUBCHOO], // Tera Ice Swablu
|
||||
|
||||
// Elite Four- Kanto
|
||||
LORELEI: [
|
||||
Species.JYNX,
|
||||
[Species.SLOWBRO, Species.GALAR_SLOWBRO],
|
||||
Species.LAPRAS,
|
||||
[Species.CLOYSTER, Species.ALOLA_SANDSLASH],
|
||||
],
|
||||
BRUNO: [Species.MACHAMP, Species.HITMONCHAN, Species.HITMONLEE, [Species.GOLEM, Species.ALOLA_GOLEM]],
|
||||
AGATHA: [Species.GENGAR, [Species.ARBOK, Species.WEEZING], Species.CROBAT, Species.ALOLA_MAROWAK],
|
||||
LANCE: [Species.DRAGONITE, Species.GYARADOS, Species.AERODACTYL, Species.ALOLA_EXEGGUTOR],
|
||||
// Elite Four- Johto (Bruno included)
|
||||
WILL: [Species.XATU, Species.JYNX, [Species.SLOWBRO, Species.SLOWKING], Species.EXEGGUTOR],
|
||||
KOGA: [[Species.MUK, Species.WEEZING], [Species.VENOMOTH, Species.ARIADOS], Species.CROBAT, Species.TENTACRUEL],
|
||||
KAREN: [Species.UMBREON, Species.HONCHKROW, Species.HOUNDOOM, Species.WEAVILE],
|
||||
// Elite Four- Hoenn
|
||||
SIDNEY: [
|
||||
[Species.SHIFTRY, Species.CACTURNE],
|
||||
[Species.SHARPEDO, Species.CRAWDAUNT],
|
||||
Species.ABSOL,
|
||||
Species.MIGHTYENA,
|
||||
],
|
||||
PHOEBE: [Species.SABLEYE, Species.DUSKNOIR, Species.BANETTE, [Species.DRIFBLIM, Species.MISMAGIUS]],
|
||||
GLACIA: [Species.GLALIE, Species.WALREIN, Species.FROSLASS, Species.ABOMASNOW],
|
||||
DRAKE: [Species.ALTARIA, Species.SALAMENCE, Species.FLYGON, Species.KINGDRA],
|
||||
// Elite Four- Sinnoh
|
||||
AARON: [[Species.SCIZOR, Species.KLEAVOR], Species.HERACROSS, [Species.VESPIQUEN, Species.YANMEGA], Species.DRAPION],
|
||||
BERTHA: [Species.WHISCASH, Species.HIPPOWDON, Species.GLISCOR, Species.RHYPERIOR],
|
||||
FLINT: [
|
||||
[Species.RAPIDASH, Species.FLAREON],
|
||||
Species.MAGMORTAR,
|
||||
[Species.STEELIX, Species.LOPUNNY],
|
||||
Species.INFERNAPE,
|
||||
], // Tera Fire Steelix or Lopunny
|
||||
LUCIAN: [Species.MR_MIME, Species.GALLADE, Species.BRONZONG, [Species.ALAKAZAM, Species.ESPEON]],
|
||||
// Elite Four- Unova
|
||||
SHAUNTAL: [Species.COFAGRIGUS, Species.CHANDELURE, Species.GOLURK, Species.JELLICENT],
|
||||
MARSHAL: [Species.CONKELDURR, Species.MIENSHAO, Species.THROH, Species.SAWK],
|
||||
GRIMSLEY: [Species.LIEPARD, Species.KINGAMBIT, Species.SCRAFTY, Species.KROOKODILE],
|
||||
CAITLIN: [Species.MUSHARNA, Species.GOTHITELLE, Species.SIGILYPH, Species.REUNICLUS],
|
||||
// Elite Four- Kalos
|
||||
MALVA: [Species.PYROAR, Species.TORKOAL, Species.CHANDELURE, Species.TALONFLAME],
|
||||
SIEBOLD: [Species.CLAWITZER, Species.GYARADOS, Species.BARBARACLE, Species.STARMIE],
|
||||
WIKSTROM: [Species.KLEFKI, Species.PROBOPASS, Species.SCIZOR, Species.AEGISLASH],
|
||||
DRASNA: [Species.DRAGALGE, Species.DRUDDIGON, Species.ALTARIA, Species.NOIVERN],
|
||||
// Elite Four- Alola
|
||||
HALA: [Species.HARIYAMA, Species.BEWEAR, Species.CRABOMINABLE, [Species.POLIWRATH, Species.ANNIHILAPE]],
|
||||
MOLAYNE: [Species.KLEFKI, Species.MAGNEZONE, Species.METAGROSS, Species.ALOLA_DUGTRIO],
|
||||
OLIVIA: [Species.RELICANTH, Species.CARBINK, Species.ALOLA_GOLEM, Species.LYCANROC],
|
||||
ACEROLA: [[Species.BANETTE, Species.DRIFBLIM], Species.MIMIKYU, Species.DHELMISE, Species.PALOSSAND],
|
||||
KAHILI: [[Species.BRAVIARY, Species.MANDIBUZZ], Species.HAWLUCHA, Species.ORICORIO, Species.TOUCANNON],
|
||||
// Elite Four- Galar
|
||||
MARNIE_ELITE: [Species.MORPEKO, Species.LIEPARD, [Species.TOXICROAK, Species.SCRAFTY], Species.GRIMMSNARL],
|
||||
NESSA_ELITE: [Species.GOLISOPOD, [Species.QUAGSIRE, Species.PELIPPER], Species.TOXAPEX, Species.DREDNAW],
|
||||
BEA_ELITE: [Species.HAWLUCHA, [Species.GRAPPLOCT, Species.SIRFETCHD], Species.FALINKS, Species.MACHAMP],
|
||||
ALLISTER_ELITE: [Species.DUSKNOIR, [Species.POLTEAGEIST, Species.RUNERIGUS], Species.CURSOLA, Species.GENGAR],
|
||||
RAIHAN_ELITE: [Species.GOODRA, [Species.TORKOAL, Species.TURTONATOR], Species.FLYGON, Species.ARCHALUDON],
|
||||
// Elite Four- Paldea
|
||||
RIKA: [Species.CLODSIRE, [Species.DUGTRIO, Species.DONPHAN], Species.CAMERUPT, Species.WHISCASH], // Tera Ground Clodsire
|
||||
POPPY: [Species.TINKATON, Species.BRONZONG, Species.CORVIKNIGHT, Species.COPPERAJAH], // Tera Steel Tinkaton
|
||||
LARRY_ELITE: [Species.FLAMIGO, Species.STARAPTOR, [Species.ALTARIA, Species.TROPIUS], Species.ORICORIO], // Tera Flying Flamigo; random Oricorio
|
||||
HASSEL: [Species.BAXCALIBUR, [Species.FLAPPLE, Species.APPLETUN], Species.DRAGALGE, Species.NOIVERN], // Tera Dragon Baxcalibur
|
||||
// Elite Four- BBL
|
||||
CRISPIN: [Species.BLAZIKEN, Species.MAGMORTAR, [Species.CAMERUPT, Species.TALONFLAME], Species.ROTOM], // Tera Fire Blaziken; Heat Rotom
|
||||
AMARYS: [Species.METAGROSS, Species.SCIZOR, Species.EMPOLEON, Species.SKARMORY], // Tera Steel Metagross
|
||||
LACEY: [Species.EXCADRILL, Species.PRIMARINA, [Species.WHIMSICOTT, Species.ALCREMIE], Species.GRANBULL], // Tera Fairy Excadrill
|
||||
DRAYTON: [Species.ARCHALUDON, Species.DRAGONITE, Species.HAXORUS, Species.SCEPTILE], // Tera Dragon Archaludon
|
||||
};
|
||||
}, {
|
||||
get(target, prop: string) {
|
||||
return target[prop as keyof SignatureSpecies] ?? [];
|
||||
}
|
||||
});
|
||||
|
@ -5724,7 +5724,6 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.SCOLIPEDE,
|
||||
Species.WHIMSICOTT,
|
||||
Species.LILLIGANT,
|
||||
Species.BASCULIN,
|
||||
Species.KROOKODILE,
|
||||
Species.DARMANITAN,
|
||||
Species.CRUSTLE,
|
||||
@ -6023,6 +6022,11 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.HISUI_DECIDUEYE,
|
||||
Species.PALDEA_TAUROS,
|
||||
Species.BLOODMOON_URSALUNA,
|
||||
[
|
||||
Species.BASCULIN,
|
||||
"blue-striped",
|
||||
"red-striped",
|
||||
]
|
||||
],
|
||||
[Moves.LOW_KICK]: [
|
||||
Species.SANDSHREW,
|
||||
@ -19335,7 +19339,6 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.CONKELDURR,
|
||||
Species.THROH,
|
||||
Species.SAWK,
|
||||
Species.BASCULIN,
|
||||
Species.DARMANITAN,
|
||||
Species.SCRAFTY,
|
||||
Species.ESCAVALIER,
|
||||
@ -19449,6 +19452,11 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.HISUI_BRAVIARY,
|
||||
Species.HISUI_DECIDUEYE,
|
||||
Species.PALDEA_TAUROS,
|
||||
[
|
||||
Species.BASCULIN,
|
||||
"blue-striped",
|
||||
"red-striped",
|
||||
],
|
||||
],
|
||||
[Moves.SPITE]: [
|
||||
Species.EKANS,
|
||||
@ -51341,7 +51349,6 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.SCOLIPEDE,
|
||||
Species.WHIMSICOTT,
|
||||
Species.LILLIGANT,
|
||||
Species.BASCULIN,
|
||||
Species.KROOKODILE,
|
||||
Species.DARMANITAN,
|
||||
Species.CRUSTLE,
|
||||
@ -51655,6 +51662,11 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.HISUI_DECIDUEYE,
|
||||
Species.PALDEA_TAUROS,
|
||||
Species.BLOODMOON_URSALUNA,
|
||||
[
|
||||
Species.BASCULIN,
|
||||
"blue-striped",
|
||||
"red-striped",
|
||||
],
|
||||
],
|
||||
[Moves.NASTY_PLOT]: [
|
||||
Species.PIKACHU,
|
||||
|
@ -652,7 +652,7 @@ export default class Move implements Localizable {
|
||||
break;
|
||||
case MoveFlags.IGNORE_ABILITIES:
|
||||
if (user.hasAbilityWithAttr(MoveAbilityBypassAbAttr)) {
|
||||
const abilityEffectsIgnored = new BooleanHolder(false);
|
||||
const abilityEffectsIgnored = new BooleanHolder(false);
|
||||
applyAbAttrs(MoveAbilityBypassAbAttr, user, abilityEffectsIgnored, false, this);
|
||||
if (abilityEffectsIgnored.value) {
|
||||
return true;
|
||||
@ -2463,7 +2463,7 @@ export class StatusEffectAttr extends MoveEffectAttr {
|
||||
return false;
|
||||
}
|
||||
if (((!pokemon.status || this.overrideStatus) || (pokemon.status.effect === this.effect && moveChance < 0))
|
||||
&& pokemon.trySetStatus(this.effect, true, user, this.turnsRemaining, null, this.overrideStatus)) {
|
||||
&& pokemon.trySetStatus(this.effect, true, user, this.turnsRemaining, null, this.overrideStatus, false)) {
|
||||
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, false, this.effect);
|
||||
return true;
|
||||
}
|
||||
@ -3160,7 +3160,7 @@ export class StatStageChangeAttr extends MoveEffectAttr {
|
||||
private get showMessage () {
|
||||
return this.options?.showMessage ?? true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to change stats of the user or target (depending on value of selfTarget) if conditions are met
|
||||
* @param user {@linkcode Pokemon} the user of the move
|
||||
@ -6326,11 +6326,11 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
||||
|
||||
if (!allyPokemon?.isActive(true) && switchOutTarget.hp) {
|
||||
globalScene.pushPhase(new BattleEndPhase(false));
|
||||
|
||||
|
||||
if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) {
|
||||
globalScene.pushPhase(new SelectBiomePhase());
|
||||
}
|
||||
|
||||
|
||||
globalScene.pushPhase(new NewBattlePhase());
|
||||
}
|
||||
}
|
||||
@ -7665,20 +7665,6 @@ export class AverageStatsAttr extends MoveEffectAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class DiscourageFrequentUseAttr extends MoveAttr {
|
||||
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
|
||||
const lastMoves = user.getLastXMoves(4);
|
||||
console.log(lastMoves);
|
||||
for (let m = 0; m < lastMoves.length; m++) {
|
||||
if (lastMoves[m].move === move.id) {
|
||||
return (4 - (m + 1)) * -10;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class MoneyAttr extends MoveEffectAttr {
|
||||
constructor() {
|
||||
super(true, {firstHitOnly: true });
|
||||
@ -8632,7 +8618,9 @@ export function initMoves() {
|
||||
.condition((user, target, move) => !target.summonData?.illusion && !user.summonData?.illusion)
|
||||
// transforming from or into fusion pokemon causes various problems (such as crashes)
|
||||
.condition((user, target, move) => !target.getTag(BattlerTagType.SUBSTITUTE) && !user.fusionSpecies && !target.fusionSpecies)
|
||||
.ignoresProtect(),
|
||||
.ignoresProtect()
|
||||
// Transforming should copy the target's rage fist hit count
|
||||
.edgeCase(),
|
||||
new AttackMove(Moves.BUBBLE, PokemonType.WATER, MoveCategory.SPECIAL, 40, 100, 30, 10, 0, 1)
|
||||
.attr(StatStageChangeAttr, [ Stat.SPD ], -1)
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||
@ -10517,8 +10505,7 @@ export function initMoves() {
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
})
|
||||
.attr(DiscourageFrequentUseAttr),
|
||||
}),
|
||||
|
||||
new AttackMove(Moves.SNIPE_SHOT, PokemonType.WATER, MoveCategory.SPECIAL, 80, 100, 15, -1, 0, 8)
|
||||
.attr(HighCritAttr)
|
||||
|
@ -424,6 +424,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
|
||||
console.log(
|
||||
`Pokemon: ${getPokemonNameWithAffix(enemyPokemon)}`,
|
||||
`| Species ID: ${enemyPokemon.species.speciesId}`,
|
||||
`| Level: ${enemyPokemon.level}`,
|
||||
`| Nature: ${getNatureName(enemyPokemon.nature, true, true, true)}`,
|
||||
);
|
||||
console.log(`Stats (IVs): ${stats}`);
|
||||
|
@ -488,6 +488,7 @@ export abstract class PokemonSpeciesForm {
|
||||
if (formSpriteKey.startsWith("behemoth")) {
|
||||
formSpriteKey = "crowned";
|
||||
}
|
||||
// biome-ignore lint/suspicious/no-fallthrough: Falls through
|
||||
default:
|
||||
ret += `-${formSpriteKey}`;
|
||||
break;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -369,6 +369,7 @@ export function getRandomWeatherType(arena: Arena): WeatherType {
|
||||
if (hasSun) {
|
||||
weatherPool.push({ weatherType: WeatherType.SUNNY, weight: 2 });
|
||||
}
|
||||
break;
|
||||
case Biome.VOLCANO:
|
||||
weatherPool = [
|
||||
{
|
||||
|
@ -12,7 +12,6 @@ import BattleInfo, {
|
||||
import type Move from "#app/data/moves/move";
|
||||
import {
|
||||
HighCritAttr,
|
||||
StatChangeBeforeDmgCalcAttr,
|
||||
HitsTagAttr,
|
||||
applyMoveAttrs,
|
||||
FixedDamageAttr,
|
||||
@ -70,10 +69,8 @@ import {
|
||||
EFFECTIVE_STATS,
|
||||
} from "#enums/stat";
|
||||
import {
|
||||
DamageMoneyRewardModifier,
|
||||
EnemyDamageBoosterModifier,
|
||||
EnemyDamageReducerModifier,
|
||||
EnemyEndureChanceModifier,
|
||||
EnemyFusionChanceModifier,
|
||||
HiddenAbilityRateBoosterModifier,
|
||||
BaseStatModifier,
|
||||
@ -119,7 +116,6 @@ import {
|
||||
TypeImmuneTag,
|
||||
getBattlerTag,
|
||||
SemiInvulnerableTag,
|
||||
TypeBoostTag,
|
||||
MoveRestrictionBattlerTag,
|
||||
ExposedTag,
|
||||
DragonCheerTag,
|
||||
@ -188,7 +184,7 @@ import {
|
||||
PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr,
|
||||
applyAllyStatMultiplierAbAttrs,
|
||||
AllyStatMultiplierAbAttr,
|
||||
MoveAbilityBypassAbAttr
|
||||
MoveAbilityBypassAbAttr,
|
||||
} from "#app/data/abilities/ability";
|
||||
import { allAbilities } from "#app/data/data-lists";
|
||||
import type PokemonData from "#app/system/pokemon-data";
|
||||
@ -202,7 +198,7 @@ import {
|
||||
EVOLVE_MOVE,
|
||||
RELEARN_MOVE,
|
||||
} from "#app/data/balance/pokemon-level-moves";
|
||||
import { DamageAchv, achvs } from "#app/system/achv";
|
||||
import { achvs } from "#app/system/achv";
|
||||
import type { StarterDataEntry, StarterMoveset } from "#app/system/game-data";
|
||||
import { DexAttr } from "#app/system/game-data";
|
||||
import {
|
||||
@ -248,7 +244,7 @@ import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
||||
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||
import { SwitchType } from "#enums/switch-type";
|
||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||
import {getStatusEffectOverlapText } from "#app/data/status-effect";
|
||||
import { getStatusEffectOverlapText } from "#app/data/status-effect";
|
||||
import {
|
||||
BASE_HIDDEN_ABILITY_CHANCE,
|
||||
BASE_SHINY_CHANCE,
|
||||
@ -5533,9 +5529,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
sourcePokemon: Pokemon | null = null,
|
||||
turnsRemaining = 0,
|
||||
sourceText: string | null = null,
|
||||
overrideStatus?: boolean
|
||||
overrideStatus?: boolean,
|
||||
quiet = true,
|
||||
): boolean {
|
||||
if (!this.canSetStatus(effect, false, overrideStatus, sourcePokemon)) {
|
||||
if (!this.canSetStatus(effect, quiet, overrideStatus, sourcePokemon)) {
|
||||
return false;
|
||||
}
|
||||
if (this.isFainted() && effect !== StatusEffect.FAINT) {
|
||||
@ -7030,6 +7027,15 @@ export class EnemyPokemon extends Pokemon {
|
||||
}
|
||||
speciesId = prevolution;
|
||||
}
|
||||
|
||||
if (this.hasTrainer() && globalScene.currentBattle) {
|
||||
const { waveIndex } = globalScene.currentBattle;
|
||||
const ivs: number[] = [];
|
||||
while (ivs.length < 6) {
|
||||
ivs.push(this.randSeedIntRange(Math.floor(waveIndex / 10), 31));
|
||||
}
|
||||
this.ivs = ivs;
|
||||
}
|
||||
}
|
||||
|
||||
this.aiType =
|
||||
|
@ -2,7 +2,12 @@ import { BattlerIndex } from "#app/battle";
|
||||
import { BattleType } from "#enums/battle-type";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
||||
import { applyAbAttrs, SyncEncounterNatureAbAttr, applyPreSummonAbAttrs, PreSummonAbAttr } from "#app/data/abilities/ability";
|
||||
import {
|
||||
applyAbAttrs,
|
||||
SyncEncounterNatureAbAttr,
|
||||
applyPreSummonAbAttrs,
|
||||
PreSummonAbAttr,
|
||||
} from "#app/data/abilities/ability";
|
||||
import { initEncounterAnims, loadEncounterAnimAssets } from "#app/data/battle-anims";
|
||||
import { getCharVariantFromDialogue } from "#app/data/dialogue";
|
||||
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
@ -196,6 +201,7 @@ export class EncounterPhase extends BattlePhase {
|
||||
console.log(
|
||||
`Pokemon: ${getPokemonNameWithAffix(enemyPokemon)}`,
|
||||
`| Species ID: ${enemyPokemon.species.speciesId}`,
|
||||
`| Level: ${enemyPokemon.level}`,
|
||||
`| Nature: ${getNatureName(enemyPokemon.nature, true, true, true)}`,
|
||||
);
|
||||
console.log(`Stats (IVs): ${stats}`);
|
||||
|
@ -31,6 +31,7 @@ import ChallengeData from "#app/system/challenge-data";
|
||||
import TrainerData from "#app/system/trainer-data";
|
||||
import ArenaData from "#app/system/arena-data";
|
||||
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
||||
import { MessagePhase } from "./message-phase";
|
||||
|
||||
export class GameOverPhase extends BattlePhase {
|
||||
private isVictory: boolean;
|
||||
@ -122,7 +123,7 @@ export class GameOverPhase extends BattlePhase {
|
||||
globalScene.disableMenu = true;
|
||||
globalScene.time.delayedCall(1000, () => {
|
||||
let firstClear = false;
|
||||
if (this.isVictory && newClear) {
|
||||
if (this.isVictory) {
|
||||
if (globalScene.gameMode.isClassic) {
|
||||
firstClear = globalScene.validateAchv(achvs.CLASSIC_VICTORY);
|
||||
globalScene.validateAchv(achvs.UNEVOLVED_CLASSIC_VICTORY);
|
||||
@ -226,7 +227,17 @@ export class GameOverPhase extends BattlePhase {
|
||||
isVictory: this.isVictory,
|
||||
clientSessionId: clientSessionId,
|
||||
})
|
||||
.then(success => doGameOver(!!success));
|
||||
.then(success => doGameOver(!globalScene.gameMode.isDaily || !!success))
|
||||
.catch(_err => {
|
||||
globalScene.clearPhaseQueue();
|
||||
globalScene.clearPhaseQueueSplice();
|
||||
globalScene.unshiftPhase(new MessagePhase(i18next.t("menu:serverCommunicationFailed"), 2500));
|
||||
// force the game to reload after 2 seconds.
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 2000);
|
||||
this.end();
|
||||
});
|
||||
} else if (this.isVictory) {
|
||||
globalScene.gameData.offlineNewClear().then(result => {
|
||||
doGameOver(result);
|
||||
|
@ -864,7 +864,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
});
|
||||
|
||||
if (isCritical) {
|
||||
globalScene.queueMessage(i18next.t("battle:criticalHit"));
|
||||
globalScene.queueMessage(i18next.t("battle:hitResultCriticalHit"));
|
||||
}
|
||||
|
||||
if (damage <= 0) {
|
||||
|
@ -20,17 +20,20 @@ export class PokerogueSessionSavedataApi extends ApiBase {
|
||||
* *This is **NOT** the same as {@linkcode clear | clear()}.*
|
||||
* @param params The {@linkcode NewClearSessionSavedataRequest} to send
|
||||
* @returns The raw savedata as `string`.
|
||||
* @throws Error if the request fails
|
||||
*/
|
||||
public async newclear(params: NewClearSessionSavedataRequest) {
|
||||
try {
|
||||
const urlSearchParams = this.toUrlSearchParams(params);
|
||||
const response = await this.doGet(`/savedata/session/newclear?${urlSearchParams}`);
|
||||
const json = await response.json();
|
||||
|
||||
return Boolean(json);
|
||||
if (response.ok) {
|
||||
return Boolean(json);
|
||||
}
|
||||
throw new Error("Could not newclear session!");
|
||||
} catch (err) {
|
||||
console.warn("Could not newclear session!", err);
|
||||
return false;
|
||||
throw new Error("Could not newclear session!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,14 +15,17 @@ export class PokerogueSystemSavedataApi extends ApiBase {
|
||||
/**
|
||||
* Get a system savedata.
|
||||
* @param params The {@linkcode GetSystemSavedataRequest} to send
|
||||
* @returns The system savedata as `string` or `null` on error
|
||||
* @returns The system savedata as `string` or either the status code or `null` on error
|
||||
*/
|
||||
public async get(params: GetSystemSavedataRequest) {
|
||||
public async get(params: GetSystemSavedataRequest): Promise<string | number | null> {
|
||||
try {
|
||||
const urlSearchParams = this.toUrlSearchParams(params);
|
||||
const response = await this.doGet(`/savedata/system/get?${urlSearchParams}`);
|
||||
const rawSavedata = await response.text();
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn("Could not get system savedata!", response.status, rawSavedata);
|
||||
return response.status;
|
||||
}
|
||||
return rawSavedata;
|
||||
} catch (err) {
|
||||
console.warn("Could not get system savedata!", err);
|
||||
|
@ -462,8 +462,13 @@ export class GameData {
|
||||
|
||||
if (!bypassLogin) {
|
||||
pokerogueApi.savedata.system.get({ clientSessionId }).then(saveDataOrErr => {
|
||||
if (!saveDataOrErr || saveDataOrErr.length === 0 || saveDataOrErr[0] !== "{") {
|
||||
if (saveDataOrErr?.startsWith("sql: no rows in result set")) {
|
||||
if (
|
||||
typeof saveDataOrErr === "number" ||
|
||||
!saveDataOrErr ||
|
||||
saveDataOrErr.length === 0 ||
|
||||
saveDataOrErr[0] !== "{"
|
||||
) {
|
||||
if (saveDataOrErr === 404) {
|
||||
globalScene.queueMessage(
|
||||
"Save data could not be found. If this is a new account, you can safely ignore this message.",
|
||||
null,
|
||||
@ -471,7 +476,7 @@ export class GameData {
|
||||
);
|
||||
return resolve(true);
|
||||
}
|
||||
if (saveDataOrErr?.includes("Too many connections")) {
|
||||
if (typeof saveDataOrErr === "string" && saveDataOrErr?.includes("Too many connections")) {
|
||||
globalScene.queueMessage(
|
||||
"Too many people are trying to connect and the server is overloaded. Please try again later.",
|
||||
null,
|
||||
@ -479,7 +484,6 @@ export class GameData {
|
||||
);
|
||||
return resolve(false);
|
||||
}
|
||||
console.error(saveDataOrErr);
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
@ -1500,7 +1504,7 @@ export class GameData {
|
||||
link.remove();
|
||||
};
|
||||
if (!bypassLogin && dataType < GameDataType.SETTINGS) {
|
||||
let promise: Promise<string | null> = Promise.resolve(null);
|
||||
let promise: Promise<string | null | number> = Promise.resolve(null);
|
||||
|
||||
if (dataType === GameDataType.SYSTEM) {
|
||||
promise = pokerogueApi.savedata.system.get({ clientSessionId });
|
||||
@ -1512,7 +1516,7 @@ export class GameData {
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
if (!response?.length || response[0] !== "{") {
|
||||
if (typeof response === "number" || !response?.length || response[0] !== "{") {
|
||||
console.error(response);
|
||||
resolve(false);
|
||||
return;
|
||||
|
@ -804,6 +804,7 @@ export function setSetting(setting: string, value: number): boolean {
|
||||
break;
|
||||
case SettingKeys.Candy_Upgrade_Display:
|
||||
globalScene.candyUpgradeDisplay = value;
|
||||
break;
|
||||
case SettingKeys.Money_Format:
|
||||
switch (Setting[index].options[value].value) {
|
||||
case "Normal":
|
||||
|
@ -176,11 +176,13 @@ export class UiInputs {
|
||||
return;
|
||||
}
|
||||
switch (globalScene.ui?.getMode()) {
|
||||
case UiMode.MESSAGE:
|
||||
case UiMode.MESSAGE: {
|
||||
const messageHandler = globalScene.ui.getHandler<MessageUiHandler>();
|
||||
if (!messageHandler.pendingPrompt || messageHandler.isTextAnimationInProgress()) {
|
||||
return;
|
||||
}
|
||||
// biome-ignore lint/suspicious/noFallthroughSwitchClause: falls through to show menu overlay
|
||||
}
|
||||
case UiMode.TITLE:
|
||||
case UiMode.COMMAND:
|
||||
case UiMode.MODIFIER_SELECT:
|
||||
|
@ -557,11 +557,11 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||
this.ownedIcon,
|
||||
this.championRibbon,
|
||||
this.statusIndicator,
|
||||
this.levelContainer,
|
||||
this.statValuesContainer,
|
||||
].map(e => (e.x += 48 * (boss ? -1 : 1)));
|
||||
this.hpBar.x += 38 * (boss ? -1 : 1);
|
||||
this.hpBar.y += 2 * (this.boss ? -1 : 1);
|
||||
this.levelContainer.x += 2 * (boss ? -1 : 1);
|
||||
this.hpBar.setTexture(`overlay_hp${boss ? "_boss" : ""}`);
|
||||
this.box.setTexture(this.getTextureName());
|
||||
this.statsBox.setTexture(`${this.getTextureName()}_stats`);
|
||||
|
@ -292,6 +292,13 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
||||
starterSelectBg.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(starterSelectBg);
|
||||
|
||||
this.pokemonSprite = globalScene.add.sprite(53, 63, "pkmn__sub");
|
||||
this.pokemonSprite.setPipeline(globalScene.spritePipeline, {
|
||||
tone: [0.0, 0.0, 0.0, 0.0],
|
||||
ignoreTimeTint: true,
|
||||
});
|
||||
this.starterSelectContainer.add(this.pokemonSprite);
|
||||
|
||||
this.shinyOverlay = globalScene.add.image(6, 6, "summary_overlay_shiny");
|
||||
this.shinyOverlay.setOrigin(0, 0);
|
||||
this.shinyOverlay.setVisible(false);
|
||||
@ -343,13 +350,6 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
||||
|
||||
this.starterSelectContainer.add(starterBoxContainer);
|
||||
|
||||
this.pokemonSprite = globalScene.add.sprite(53, 63, "pkmn__sub");
|
||||
this.pokemonSprite.setPipeline(globalScene.spritePipeline, {
|
||||
tone: [0.0, 0.0, 0.0, 0.0],
|
||||
ignoreTimeTint: true,
|
||||
});
|
||||
this.starterSelectContainer.add(this.pokemonSprite);
|
||||
|
||||
this.type1Icon = globalScene.add.sprite(8, 98, getLocalizedSpriteKey("types"));
|
||||
this.type1Icon.setScale(0.5);
|
||||
this.type1Icon.setOrigin(0, 0);
|
||||
@ -921,16 +921,22 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
||||
return biomes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the caughtAttr of a given species, sanitized.
|
||||
*
|
||||
* @param otherSpecies The species to check; defaults to current species
|
||||
* @returns caught DexAttr for the species
|
||||
*/
|
||||
isCaught(otherSpecies?: PokemonSpecies): bigint {
|
||||
const species = otherSpecies ? otherSpecies : this.species;
|
||||
|
||||
if (globalScene.dexForDevs) {
|
||||
return 255n;
|
||||
species.getFullUnlocksData();
|
||||
}
|
||||
|
||||
const species = otherSpecies ? otherSpecies : this.species;
|
||||
const dexEntry = globalScene.gameData.dexData[species.speciesId];
|
||||
const starterDexEntry = globalScene.gameData.dexData[this.getStarterSpeciesId(species.speciesId)];
|
||||
|
||||
return (dexEntry?.caughtAttr ?? 0n) & (starterDexEntry?.caughtAttr ?? 0n) & species.getFullUnlocksData();
|
||||
return (dexEntry?.caughtAttr ?? 0n) & species.getFullUnlocksData();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -939,7 +945,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
||||
*
|
||||
* @param otherSpecies The species to check; defaults to current species
|
||||
* @param otherFormIndex The form index of the form to check; defaults to current form
|
||||
* @returns StarterAttributes for the species
|
||||
* @returns `true` if the form is caught
|
||||
*/
|
||||
isFormCaught(otherSpecies?: PokemonSpecies, otherFormIndex?: number | undefined): boolean {
|
||||
if (globalScene.dexForDevs) {
|
||||
@ -954,6 +960,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
||||
}
|
||||
|
||||
const isFormCaught = (caughtAttr & globalScene.gameData.getFormAttr(formIndex ?? 0)) > 0n;
|
||||
|
||||
return isFormCaught;
|
||||
}
|
||||
|
||||
@ -1151,7 +1158,6 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
||||
this.blockInput = false;
|
||||
} else {
|
||||
ui.revertMode().then(() => {
|
||||
console.log("exitCallback", this.exitCallback);
|
||||
if (this.exitCallback instanceof Function) {
|
||||
const exitCallback = this.exitCallback;
|
||||
this.exitCallback = null;
|
||||
|
@ -596,6 +596,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
this.iconAnimHandler = new PokemonIconAnimHandler();
|
||||
this.iconAnimHandler.setup();
|
||||
|
||||
this.pokemonSprite = globalScene.add.sprite(53, 63, "pkmn__sub");
|
||||
this.pokemonSprite.setPipeline(globalScene.spritePipeline, {
|
||||
tone: [0.0, 0.0, 0.0, 0.0],
|
||||
ignoreTimeTint: true,
|
||||
});
|
||||
this.starterSelectContainer.add(this.pokemonSprite);
|
||||
|
||||
this.pokemonNumberText = addTextObject(17, 1, "0000", TextStyle.SUMMARY);
|
||||
this.pokemonNumberText.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.pokemonNumberText);
|
||||
@ -825,13 +832,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
return icon;
|
||||
});
|
||||
|
||||
this.pokemonSprite = globalScene.add.sprite(53, 63, "pkmn__sub");
|
||||
this.pokemonSprite.setPipeline(globalScene.spritePipeline, {
|
||||
tone: [0.0, 0.0, 0.0, 0.0],
|
||||
ignoreTimeTint: true,
|
||||
});
|
||||
this.starterSelectContainer.add(this.pokemonSprite);
|
||||
|
||||
this.type1Icon = globalScene.add.sprite(8, 98, getLocalizedSpriteKey("types"));
|
||||
this.type1Icon.setScale(0.5);
|
||||
this.type1Icon.setOrigin(0, 0);
|
||||
|
@ -23,18 +23,18 @@ describe("Abilities - Illusion", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override.battleStyle("single");
|
||||
game.override.enemySpecies(Species.ZORUA);
|
||||
game.override.enemyAbility(Abilities.ILLUSION);
|
||||
game.override.enemyMoveset(Moves.TACKLE);
|
||||
game.override.enemyHeldItems([{ name: "WIDE_LENS", count: 3 }]);
|
||||
|
||||
game.override.moveset([Moves.WORRY_SEED, Moves.SOAK, Moves.TACKLE]);
|
||||
game.override.startingHeldItems([{ name: "WIDE_LENS", count: 3 }]);
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.enemySpecies(Species.ZORUA)
|
||||
.enemyAbility(Abilities.ILLUSION)
|
||||
.enemyMoveset(Moves.TACKLE)
|
||||
.enemyHeldItems([{ name: "WIDE_LENS", count: 3 }])
|
||||
.moveset([Moves.WORRY_SEED, Moves.SOAK, Moves.TACKLE])
|
||||
.startingHeldItems([{ name: "WIDE_LENS", count: 3 }]);
|
||||
});
|
||||
|
||||
it("creates illusion at the start", async () => {
|
||||
await game.classicMode.startBattle([Species.ZOROARK, Species.AXEW]);
|
||||
await game.classicMode.startBattle([Species.ZOROARK, Species.FEEBAS]);
|
||||
const zoroark = game.scene.getPlayerPokemon()!;
|
||||
const zorua = game.scene.getEnemyPokemon()!;
|
||||
|
||||
@ -43,7 +43,7 @@ describe("Abilities - Illusion", () => {
|
||||
});
|
||||
|
||||
it("break after receiving damaging move", async () => {
|
||||
await game.classicMode.startBattle([Species.AXEW]);
|
||||
await game.classicMode.startBattle([Species.FEEBAS]);
|
||||
game.move.select(Moves.TACKLE);
|
||||
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
@ -55,7 +55,7 @@ describe("Abilities - Illusion", () => {
|
||||
});
|
||||
|
||||
it("break after getting ability changed", async () => {
|
||||
await game.classicMode.startBattle([Species.AXEW]);
|
||||
await game.classicMode.startBattle([Species.FEEBAS]);
|
||||
game.move.select(Moves.WORRY_SEED);
|
||||
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
@ -76,7 +76,7 @@ describe("Abilities - Illusion", () => {
|
||||
|
||||
it("causes enemy AI to consider the illusion's type instead of the actual type when considering move effectiveness", async () => {
|
||||
game.override.enemyMoveset([Moves.FLAMETHROWER, Moves.PSYCHIC, Moves.TACKLE]);
|
||||
await game.classicMode.startBattle([Species.ZOROARK, Species.AXEW]);
|
||||
await game.classicMode.startBattle([Species.ZOROARK, Species.FEEBAS]);
|
||||
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
const zoroark = game.scene.getPlayerPokemon()!;
|
||||
|
@ -209,4 +209,19 @@ describe("Spec - Pokemon", () => {
|
||||
expect(types[1]).toBe(PokemonType.DARK);
|
||||
});
|
||||
});
|
||||
|
||||
it.each([5, 25, 55, 95, 145, 195])(
|
||||
"should set minimum IVs for enemy trainer pokemon based on wave (%i)",
|
||||
async wave => {
|
||||
game.override.startingWave(wave);
|
||||
await game.classicMode.startBattle([Species.FEEBAS]);
|
||||
const { waveIndex } = game.scene.currentBattle;
|
||||
|
||||
for (const pokemon of game.scene.getEnemyParty()) {
|
||||
for (const index in pokemon.ivs) {
|
||||
expect(pokemon.ivs[index]).toBeGreaterThanOrEqual(Math.floor(waveIndex / 10));
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
@ -57,9 +57,7 @@ describe("Pokerogue Session Savedata API", () => {
|
||||
it("should return false and report a warning on ERROR", async () => {
|
||||
server.use(http.get(`${apiBase}/savedata/session/newclear`, () => HttpResponse.error()));
|
||||
|
||||
const success = await sessionSavedataApi.newclear(params);
|
||||
|
||||
expect(success).toBe(false);
|
||||
await expect(sessionSavedataApi.newclear(params)).rejects.toThrow("Could not newclear session!");
|
||||
expect(console.warn).toHaveBeenCalledWith("Could not newclear session!", expect.any(Error));
|
||||
});
|
||||
});
|
||||
|
1
test/testUtils/saves/data_pokedex_tests_v2.prsv
Normal file
1
test/testUtils/saves/data_pokedex_tests_v2.prsv
Normal file
File diff suppressed because one or more lines are too long
@ -12,6 +12,8 @@ import { DropDownColumn } from "#app/ui/filter-bar";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { UiMode } from "#enums/ui-mode";
|
||||
import PokedexPageUiHandler from "#app/ui/pokedex-page-ui-handler";
|
||||
import type { StarterAttributes } from "#app/system/game-data";
|
||||
|
||||
/*
|
||||
Information for the `data_pokedex_tests.psrv`:
|
||||
@ -80,6 +82,26 @@ describe("UI - Pokedex", () => {
|
||||
return handler as PokedexUiHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the game to open the pokedex UI.
|
||||
* @returns The handler for the pokedex UI.
|
||||
*/
|
||||
async function runToPokedexPage(
|
||||
species: PokemonSpecies,
|
||||
starterAttributes: StarterAttributes = {},
|
||||
): Promise<PokedexPageUiHandler> {
|
||||
// Open the pokedex UI.
|
||||
await game.runToTitle();
|
||||
|
||||
await game.phaseInterceptor.setOverlayMode(UiMode.POKEDEX_PAGE, species, starterAttributes);
|
||||
|
||||
// Get the handler for the current UI.
|
||||
const handler = game.scene.ui.getHandler();
|
||||
expect(handler).toBeInstanceOf(PokedexPageUiHandler);
|
||||
|
||||
return handler as PokedexPageUiHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a set of pokemon that have a specific ability in allAbilities
|
||||
* @param ability - The ability to filter for
|
||||
@ -489,4 +511,37 @@ describe("UI - Pokedex", () => {
|
||||
expect(selectedPokemon).toEqual(pokedexHandler.lastSpecies.speciesId);
|
||||
},
|
||||
);
|
||||
|
||||
/****************************
|
||||
* Tests for Pokédex Pages *
|
||||
****************************/
|
||||
|
||||
it("should show caught battle form as caught", async () => {
|
||||
await game.importData("./test/testUtils/saves/data_pokedex_tests_v2.prsv");
|
||||
const pageHandler = await runToPokedexPage(getPokemonSpecies(Species.VENUSAUR), { form: 1 });
|
||||
|
||||
// @ts-expect-error - `species` is private
|
||||
expect(pageHandler.species.speciesId).toEqual(Species.VENUSAUR);
|
||||
|
||||
// @ts-expect-error - `formIndex` is private
|
||||
expect(pageHandler.formIndex).toEqual(1);
|
||||
|
||||
expect(pageHandler.isFormCaught()).toEqual(true);
|
||||
expect(pageHandler.isSeen()).toEqual(true);
|
||||
});
|
||||
|
||||
//TODO: check tint of the sprite
|
||||
it("should show uncaught battle form as seen", async () => {
|
||||
await game.importData("./test/testUtils/saves/data_pokedex_tests_v2.prsv");
|
||||
const pageHandler = await runToPokedexPage(getPokemonSpecies(Species.VENUSAUR), { form: 2 });
|
||||
|
||||
// @ts-expect-error - `species` is private
|
||||
expect(pageHandler.species.speciesId).toEqual(Species.VENUSAUR);
|
||||
|
||||
// @ts-expect-error - `formIndex` is private
|
||||
expect(pageHandler.formIndex).toEqual(2);
|
||||
|
||||
expect(pageHandler.isFormCaught()).toEqual(false);
|
||||
expect(pageHandler.isSeen()).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
@ -7,7 +7,7 @@
|
||||
"esModuleInterop": true,
|
||||
"strictNullChecks": true,
|
||||
"sourceMap": false,
|
||||
"strict": false,
|
||||
"strict": false, // TODO: Enable this eventually
|
||||
"rootDir": ".",
|
||||
"baseUrl": "./src",
|
||||
"paths": {
|
||||
|
Loading…
Reference in New Issue
Block a user