Merge branch 'beta' into recoil-substitute

This commit is contained in:
Desroi 2025-05-01 00:53:13 -05:00 committed by GitHub
commit 03411ba442
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 1073 additions and 573 deletions

View File

@ -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)?

View File

@ -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

View File

@ -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.

View File

@ -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/
},
},
];
);

View File

@ -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",

View File

@ -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

View File

@ -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",

View File

@ -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),

View File

@ -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 ],

View File

@ -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] ?? [];
}
});

View File

@ -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,

View File

@ -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)

View File

@ -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}`);

View File

@ -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

View File

@ -369,6 +369,7 @@ export function getRandomWeatherType(arena: Arena): WeatherType {
if (hasSun) {
weatherPool.push({ weatherType: WeatherType.SUNNY, weight: 2 });
}
break;
case Biome.VOLCANO:
weatherPool = [
{

View File

@ -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 =

View File

@ -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}`);

View File

@ -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);

View File

@ -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) {

View File

@ -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!");
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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":

View File

@ -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:

View File

@ -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`);

View File

@ -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;

View File

@ -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);

View File

@ -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()!;

View File

@ -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));
}
}
},
);
});

View File

@ -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));
});
});

File diff suppressed because one or more lines are too long

View File

@ -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);
});
});

View File

@ -7,7 +7,7 @@
"esModuleInterop": true,
"strictNullChecks": true,
"sourceMap": false,
"strict": false,
"strict": false, // TODO: Enable this eventually
"rootDir": ".",
"baseUrl": "./src",
"paths": {