Merge branch 'beta' into Elite4Changes

This commit is contained in:
Blitzy 2025-04-27 19:23:55 -05:00 committed by GitHub
commit 560730479a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 89 additions and 59 deletions

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,7 +1,7 @@
{
"name": "pokemon-rogue-battle",
"private": true,
"version": "1.8.4",
"version": "1.8.5",
"type": "module",
"scripts": {
"start": "vite",

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

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

@ -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,
@ -7030,6 +7026,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

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

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

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

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

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