From d3088c1729ea5c89d9e85d60f21f72a2efc1b3c8 Mon Sep 17 00:00:00 2001 From: Bertie690 <136088738+Bertie690@users.noreply.github.com> Date: Sat, 1 Nov 2025 23:38:04 -0400 Subject: [PATCH] [Dev] Add more Biome rules (#6604) * Added `noBannedTypes` as a biome rule * Added `useShorthandAssign` rule * Added `useConsistentArrayType` * Update src/field/pokemon.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/data/pokeball.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Apply Biome after merge --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- biome.jsonc | 14 ++++- src/battle-scene.ts | 4 +- src/data/battle-anims.ts | 6 +- src/data/challenge.ts | 2 +- src/data/dialogue.ts | 2 +- .../encounters/absolute-avarice-encounter.ts | 2 +- .../utils/encounter-phase-utils.ts | 6 +- src/data/pokeball.ts | 3 +- src/data/pokemon-species.ts | 2 +- src/data/status-effect.ts | 2 +- src/field/anims.ts | 2 +- src/field/pokemon.ts | 32 +++++------ src/inputs-controller.ts | 6 +- src/modifier/modifier.ts | 4 +- src/overrides.ts | 4 +- src/plugins/i18n.ts | 4 +- src/system/settings/settings.ts | 2 +- src/ui/handlers/awaitable-ui-handler.ts | 5 +- src/ui/handlers/battle-message-ui-handler.ts | 4 +- src/ui/handlers/egg-gacha-ui-handler.ts | 2 +- src/ui/handlers/form-modal-ui-handler.ts | 3 +- src/ui/handlers/game-stats-ui-handler.ts | 5 +- src/ui/handlers/login-form-ui-handler.ts | 57 ++++++++++--------- src/ui/handlers/menu-ui-handler.ts | 2 +- src/ui/handlers/message-ui-handler.ts | 8 +-- src/ui/handlers/modifier-select-ui-handler.ts | 5 +- src/ui/handlers/party-ui-handler.ts | 2 +- src/ui/handlers/pokedex-page-ui-handler.ts | 2 +- src/ui/handlers/pokedex-scan-ui-handler.ts | 2 +- src/ui/handlers/pokedex-ui-handler.ts | 5 +- .../handlers/registration-form-ui-handler.ts | 2 +- src/ui/handlers/rename-form-ui-handler.ts | 2 +- src/ui/handlers/rename-run-ui-handler.ts | 2 +- .../handlers/save-slot-select-ui-handler.ts | 2 +- src/ui/handlers/starter-select-ui-handler.ts | 2 +- src/ui/handlers/summary-ui-handler.ts | 46 +++++++++++---- src/ui/handlers/test-dialogue-ui-handler.ts | 4 +- .../abstract-control-settings-ui-handler.ts | 6 +- .../settings/abstract-settings-ui-handler.ts | 5 +- src/ui/ui.ts | 4 +- test/abilities/dry-skin.test.ts | 2 +- test/abilities/volt-absorb.test.ts | 4 +- .../mocks/mocks-container/mock-container.ts | 1 + .../mocks/mocks-container/mock-text.ts | 6 +- 44 files changed, 162 insertions(+), 125 deletions(-) diff --git a/biome.jsonc b/biome.jsonc index 994720ef9e3..277edf89366 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -116,7 +116,17 @@ }, "useCollapsedIf": "error", "useCollapsedElseIf": "error", - + "useDeprecatedReason": "error", + "useConsistentArrayType": { + "level": "error", + "fix": "safe", + "options": {} + }, + "useShorthandAssign": { + "level": "error", + "fix": "safe", + "options": {} + }, "noSubstr": "error", "noYodaExpression": "error", "useForOf": "error", @@ -205,7 +215,7 @@ "noForEach": "off", // Foreach vs for of is not that simple. "noUselessSwitchCase": "off", // Explicit > Implicit "noUselessConstructor": "error", - "noBannedTypes": "warn", // TODO: Refactor and make this an error + "noBannedTypes": "error", "noThisInStatic": "error", "noUselessThisAlias": "error", "noUselessTernary": "error", diff --git a/src/battle-scene.ts b/src/battle-scene.ts index a7679f4479f..b0199980981 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -3625,9 +3625,9 @@ export class BattleScene extends SceneBase { // biome-ignore format: biome sucks at formatting this line for (const seenEncounterData of this.mysteryEncounterSaveData.encounteredEvents) { if (seenEncounterData.tier === MysteryEncounterTier.COMMON) { - tierWeights[0] = tierWeights[0] - 6; + tierWeights[0] -= 6; } else if (seenEncounterData.tier === MysteryEncounterTier.GREAT) { - tierWeights[1] = tierWeights[1] - 4; + tierWeights[1] -= 4; } } diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index e1966805b40..fb9082d6ef6 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -821,7 +821,7 @@ export abstract class BattleAnim { frame.target === AnimFrameTarget.GRAPHIC && isReversed(this.srcLine[0], this.srcLine[2], this.dstLine[0], this.dstLine[2]) ) { - scaleX = scaleX * -1; + scaleX *= -1; } } break; @@ -835,7 +835,7 @@ export abstract class BattleAnim { } // biome-ignore lint/complexity/noBannedTypes: callback is used liberally - play(onSubstitute?: boolean, callback?: Function) { + play(onSubstitute?: boolean, callback?: () => void) { const isOppAnim = this.isOppAnim(); const user = isOppAnim ? this.target! : this.user!; const target = isOppAnim ? this.user! : this.target!; // TODO: These bangs are LITERALLY not correct at all @@ -1179,7 +1179,7 @@ export abstract class BattleAnim { frameTimeMult: number, frameTimedEventPriority?: 0 | 1 | 3 | 5, // biome-ignore lint/complexity/noBannedTypes: callback is used liberally - callback?: Function, + callback?: () => void, ) { const spriteCache: SpriteCache = { [AnimFrameTarget.GRAPHIC]: [], diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 67d0bf60a7f..22d48432376 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -933,7 +933,7 @@ export class FreshStartChallenge extends Challenge { } applyStarterModify(pokemon: Pokemon): boolean { - pokemon.abilityIndex = pokemon.abilityIndex % 2; // Always base ability, if you set it to hidden it wraps to first ability + pokemon.abilityIndex %= 2; // Always base ability, if you set it to hidden it wraps to first ability pokemon.passive = false; // Passive isn't unlocked let validMoves = pokemon.species .getLevelMoves() diff --git a/src/data/dialogue.ts b/src/data/dialogue.ts index 1d672397777..802615a8aef 100644 --- a/src/data/dialogue.ts +++ b/src/data/dialogue.ts @@ -10,7 +10,7 @@ export interface TrainerTypeMessages { } export interface TrainerTypeDialogue { - [key: number]: TrainerTypeMessages | Array; + [key: number]: TrainerTypeMessages | TrainerTypeMessages[]; } export function getTrainerTypeDialogue(): TrainerTypeDialogue { diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index 8b28065dad1..644d03658f3 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -567,7 +567,7 @@ function doBerryBounce(berrySprites: Phaser.GameObjects.Sprite[], yd: number, ba bouncePower = bouncePower > 0.01 ? bouncePower * 0.5 : 0; if (bouncePower) { - bounceYOffset = bounceYOffset * bouncePower; + bounceYOffset *= bouncePower; globalScene.tweens.add({ targets: berrySprites, diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 64162a1b2ad..b21d25955ca 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -739,7 +739,7 @@ export function selectOptionThenPokemon( export function setEncounterRewards( customShopRewards?: CustomModifierSettings, eggRewards?: IEggOptions[], - preRewardsCallback?: Function, + preRewardsCallback?: () => void, ): void { globalScene.currentBattle.mysteryEncounter!.doEncounterRewards = () => { if (preRewardsCallback) { @@ -1172,8 +1172,8 @@ export function calculateMEAggregateStats(baseSpawnWeight: number): void { const tierWeights = [66, 40, 19, 3]; // Adjust tier weights by currently encountered events (pity system that lowers odds of multiple Common/Great) - tierWeights[0] = tierWeights[0] - 6 * numEncounters[0]; - tierWeights[1] = tierWeights[1] - 4 * numEncounters[1]; + tierWeights[0] -= 6 * numEncounters[0]; + tierWeights[1] -= 4 * numEncounters[1]; const totalWeight = tierWeights.reduce((a, b) => a + b); const tierValue = randSeedInt(totalWeight); diff --git a/src/data/pokeball.ts b/src/data/pokeball.ts index 50ea5076aee..52241597dd7 100644 --- a/src/data/pokeball.ts +++ b/src/data/pokeball.ts @@ -115,8 +115,7 @@ export function doPokeballBounceAnim( y1: number, y2: number, baseBounceDuration: number, - // biome-ignore lint/complexity/noBannedTypes: TODO - callback: Function, + callback: () => void, isCritical = false, ) { let bouncePower = 1; diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index fe8a80f7df1..5ee057704df 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -421,7 +421,7 @@ export abstract class PokemonSpeciesForm { case SpeciesId.BLOODMOON_URSALUNA: break; default: - speciesId = speciesId % 2000; + speciesId %= 2000; break; } } diff --git a/src/data/status-effect.ts b/src/data/status-effect.ts index 34cd2d87617..389a25b805c 100644 --- a/src/data/status-effect.ts +++ b/src/data/status-effect.ts @@ -158,7 +158,7 @@ export function getRandomStatus(statusA: Status | null, statusB: Status | null): * Gets all non volatile status effects * @returns A list containing all non volatile status effects */ -export function getNonVolatileStatusEffects(): Array { +export function getNonVolatileStatusEffects(): StatusEffect[] { return [ StatusEffect.POISON, StatusEffect.TOXIC, diff --git a/src/field/anims.ts b/src/field/anims.ts index 82aa7b7e894..e6ba014f4b8 100644 --- a/src/field/anims.ts +++ b/src/field/anims.ts @@ -134,7 +134,7 @@ function doFanOutParticle( } particle.x = x + sin(trigIndex, f * xSpeed); particle.y = y + cos(trigIndex, f * ySpeed); - trigIndex = trigIndex + angle; + trigIndex += angle; f++; }; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 03521359ce4..2801fef0ffa 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1847,21 +1847,21 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { * @returns An array of {@linkcode PokemonMove}, as described above. */ getMoveset(ignoreOverride = false): PokemonMove[] { - // Overrides moveset based on arrays specified in overrides.ts - let overrideArray: MoveId | Array = this.isPlayer() - ? Overrides.MOVESET_OVERRIDE - : Overrides.ENEMY_MOVESET_OVERRIDE; - overrideArray = coerceArray(overrideArray); - if (overrideArray.length > 0) { - if (!this.isPlayer()) { - this.moveset = []; - } - overrideArray.forEach((move: MoveId, index: number) => { - const ppUsed = this.moveset[index]?.ppUsed ?? 0; - this.moveset[index] = new PokemonMove(move, Math.min(ppUsed, allMoves[move].pp)); - }); + // Override moveset based on arrays specified in overrides.ts + const overrideArray = coerceArray(this.isPlayer() ? Overrides.MOVESET_OVERRIDE : Overrides.ENEMY_MOVESET_OVERRIDE); + if (overrideArray.length === 0) { + return !ignoreOverride && this.summonData.moveset ? this.summonData.moveset : this.moveset; } + if (!this.isPlayer()) { + this.moveset = []; + } + // TODO: Preserve PP used while the moveset override is active + overrideArray.forEach((move: MoveId, index: number) => { + const ppUsed = this.moveset[index]?.ppUsed ?? 0; + this.moveset[index] = new PokemonMove(move, Math.min(ppUsed, allMoves[move].pp)); + }); + return !ignoreOverride && this.summonData.moveset ? this.summonData.moveset : this.moveset; } @@ -2658,10 +2658,10 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { hpDiffRatio = 1 - hpRatio + (outspeed ? 0.2 : 0.1); } } else if (outspeed) { - hpDiffRatio = hpDiffRatio * 1.25; + hpDiffRatio *= 1.25; } else if (hpRatio > 0.2 && hpRatio <= 0.4) { // Might be considered to be switched because it's not in low enough health - hpDiffRatio = hpDiffRatio * 0.5; + hpDiffRatio *= 0.5; } return (atkScore + defScore) * Math.min(hpDiffRatio, 1); } @@ -3909,7 +3909,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { } damage = Math.min(damage, this.hp); - this.hp = this.hp - damage; + this.hp -= damage; if (this.isFainted() && !ignoreFaintPhase) { globalScene.phaseManager.queueFaintPhase(this.getBattlerIndex(), preventEndure); this.destroySubstitute(); diff --git a/src/inputs-controller.ts b/src/inputs-controller.ts index 0207297fd58..afe374f24dd 100644 --- a/src/inputs-controller.ts +++ b/src/inputs-controller.ts @@ -70,7 +70,7 @@ const repeatInputDelayMillis = 250; * providing a unified interface for all input-related interactions. */ export class InputsController { - private gamepads: Array = []; + private gamepads: Phaser.Input.Gamepad.Gamepad[] = []; public events: Phaser.Events.EventEmitter; private buttonLock: Button[] = []; @@ -80,7 +80,7 @@ export class InputsController { public gamepadSupport = true; public selectedDevice; - private disconnectedGamepads: Array = []; + private disconnectedGamepads: string[] = []; public lastSource = "keyboard"; private inputInterval: NodeJS.Timeout[] = []; @@ -223,7 +223,7 @@ export class InputsController { * Retrieves the identifiers of all connected gamepads, excluding any that are currently marked as disconnected. * @returns Array An array of strings representing the IDs of the connected gamepads. */ - getGamepadsName(): Array { + getGamepadsName(): string[] { return this.gamepads.filter(g => !this.disconnectedGamepads.includes(g.id)).map(g => g.id); } diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index cefe8c9367a..aa9c20996a7 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -479,7 +479,7 @@ export class DoubleBattleChanceBoosterModifier extends LapsingPersistentModifier override apply(doubleBattleChance: NumberHolder): boolean { // This is divided because the chance is generated as a number from 0 to doubleBattleChance.value using randSeedInt // A double battle will initiate if the generated number is 0 - doubleBattleChance.value = doubleBattleChance.value / 4; + doubleBattleChance.value /= 4; return true; } @@ -2684,7 +2684,7 @@ export class PokemonMoveAccuracyBoosterModifier extends PokemonHeldItemModifier * @returns always `true` */ override apply(_pokemon: Pokemon, moveAccuracy: NumberHolder): boolean { - moveAccuracy.value = moveAccuracy.value + this.accuracyAmount * this.getStackCount(); + moveAccuracy.value += this.accuracyAmount * this.getStackCount(); return true; } diff --git a/src/overrides.ts b/src/overrides.ts index 3f61196f0b4..cceb677d450 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -159,7 +159,7 @@ class DefaultOverrides { readonly HAS_PASSIVE_ABILITY_OVERRIDE: boolean | null = null; readonly STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE; readonly GENDER_OVERRIDE: Gender | null = null; - readonly MOVESET_OVERRIDE: MoveId | Array = []; + readonly MOVESET_OVERRIDE: MoveId | MoveId[] = []; readonly SHINY_OVERRIDE: boolean | null = null; readonly VARIANT_OVERRIDE: Variant | null = null; /** @@ -186,7 +186,7 @@ class DefaultOverrides { readonly ENEMY_HAS_PASSIVE_ABILITY_OVERRIDE: boolean | null = null; readonly ENEMY_STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE; readonly ENEMY_GENDER_OVERRIDE: Gender | null = null; - readonly ENEMY_MOVESET_OVERRIDE: MoveId | Array = []; + readonly ENEMY_MOVESET_OVERRIDE: MoveId | MoveId[] = []; readonly ENEMY_SHINY_OVERRIDE: boolean | null = null; readonly ENEMY_VARIANT_OVERRIDE: Variant | null = null; diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index 11dd2467476..b41cf298186 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -11,7 +11,7 @@ import { namespaceMap } from "./utils-plugins"; interface LoadingFontFaceProperty { face: FontFace; extraOptions?: { [key: string]: any }; - only?: Array; + only?: string[]; } //#region Constants @@ -35,7 +35,7 @@ const rangesByLanguage = { ), }; -const fonts: Array = [ +const fonts: LoadingFontFaceProperty[] = [ // unicode (special character from PokePT) { face: new FontFace("emerald", "url(./fonts/PokePT_Wansung.woff2)", { diff --git a/src/system/settings/settings.ts b/src/system/settings/settings.ts index e1843eb5c8e..d69028be696 100644 --- a/src/system/settings/settings.ts +++ b/src/system/settings/settings.ts @@ -199,7 +199,7 @@ for (let i = 0; i < 5; i++) { /** * All Settings not related to controls */ -export const Setting: Array = [ +export const Setting: Setting[] = [ { key: SettingKeys.Game_Speed, label: i18next.t("settings:gameSpeed"), diff --git a/src/ui/handlers/awaitable-ui-handler.ts b/src/ui/handlers/awaitable-ui-handler.ts index e8513b4acc1..51a8165c17f 100644 --- a/src/ui/handlers/awaitable-ui-handler.ts +++ b/src/ui/handlers/awaitable-ui-handler.ts @@ -1,11 +1,14 @@ import { globalScene } from "#app/global-scene"; import { Button } from "#enums/buttons"; import type { UiMode } from "#enums/ui-mode"; +import type { AnyFn } from "#types/type-helpers"; import { UiHandler } from "#ui/ui-handler"; +// TODO: Why does this class exist? export abstract class AwaitableUiHandler extends UiHandler { protected awaitingActionInput: boolean; - protected onActionInput: Function | null; + // TODO: Add strong typing for subclasses rather than using `AnyFn` + protected onActionInput: AnyFn | null; public tutorialActive = false; public tutorialOverlay: Phaser.GameObjects.Rectangle; diff --git a/src/ui/handlers/battle-message-ui-handler.ts b/src/ui/handlers/battle-message-ui-handler.ts index f845f22a730..263240a035d 100644 --- a/src/ui/handlers/battle-message-ui-handler.ts +++ b/src/ui/handlers/battle-message-ui-handler.ts @@ -171,7 +171,7 @@ export class BattleMessageUiHandler extends MessageUiHandler { showText( text: string, delay?: number | null, - callback?: Function | null, + callback?: (() => void) | null, callbackDelay?: number | null, prompt?: boolean | null, promptDelay?: number | null, @@ -184,7 +184,7 @@ export class BattleMessageUiHandler extends MessageUiHandler { text: string, name?: string, delay?: number | null, - callback?: Function, + callback?: () => void, callbackDelay?: number, prompt?: boolean, promptDelay?: number, diff --git a/src/ui/handlers/egg-gacha-ui-handler.ts b/src/ui/handlers/egg-gacha-ui-handler.ts index fcb4cb09538..2c9c9ea2c33 100644 --- a/src/ui/handlers/egg-gacha-ui-handler.ts +++ b/src/ui/handlers/egg-gacha-ui-handler.ts @@ -660,7 +660,7 @@ export class EggGachaUiHandler extends MessageUiHandler { showText( text: string, delay?: number, - callback?: Function, + callback?: () => void, callbackDelay?: number, prompt?: boolean, promptDelay?: number, diff --git a/src/ui/handlers/form-modal-ui-handler.ts b/src/ui/handlers/form-modal-ui-handler.ts index 19d09c172e2..a4771721995 100644 --- a/src/ui/handlers/form-modal-ui-handler.ts +++ b/src/ui/handlers/form-modal-ui-handler.ts @@ -1,7 +1,6 @@ import { globalScene } from "#app/global-scene"; import { Button } from "#enums/buttons"; import { TextStyle } from "#enums/text-style"; -import type { AnyFn } from "#types/type-helpers"; import type { ModalConfig } from "#ui/modal-ui-handler"; import { ModalUiHandler } from "#ui/modal-ui-handler"; import { addTextInputObject, addTextObject, getTextColor } from "#ui/text"; @@ -18,7 +17,7 @@ export abstract class FormModalUiHandler extends ModalUiHandler { protected inputContainers: Phaser.GameObjects.Container[] = []; protected inputs: InputText[] = []; protected errorMessage: Phaser.GameObjects.Text; - protected submitAction: AnyFn | undefined; + protected submitAction: (() => void) | undefined; protected cancelAction: (() => void) | undefined; protected tween: Phaser.Tweens.Tween | undefined; protected formLabels: Phaser.GameObjects.Text[] = []; diff --git a/src/ui/handlers/game-stats-ui-handler.ts b/src/ui/handlers/game-stats-ui-handler.ts index 30243008626..802bd982813 100644 --- a/src/ui/handlers/game-stats-ui-handler.ts +++ b/src/ui/handlers/game-stats-ui-handler.ts @@ -7,7 +7,6 @@ import { PlayerGender } from "#enums/player-gender"; import { TextStyle } from "#enums/text-style"; import { UiTheme } from "#enums/ui-theme"; import type { GameData } from "#system/game-data"; -import type { AnyFn } from "#types/type-helpers"; import { addTextObject } from "#ui/text"; import { UiHandler } from "#ui/ui-handler"; import { addWindow } from "#ui/ui-theme"; @@ -244,7 +243,7 @@ export class GameStatsUiHandler extends UiHandler { private gameData: GameData; /** A callback invoked when {@linkcode clear} is called */ - private exitCallback?: AnyFn | undefined; + private exitCallback?: (() => void) | undefined; /** Whether the UI is single column mode */ private get singleCol(): boolean { @@ -402,7 +401,7 @@ export class GameStatsUiHandler extends UiHandler { this.gameStatsContainer.setVisible(false); } - show([username, data, callback]: [] | [username: string, data: GameData, callback?: AnyFn]): boolean { + show([username, data, callback]: [] | [username: string, data: GameData, callback?: () => void]): boolean { super.show([]); if (username != null && data != null) { diff --git a/src/ui/handlers/login-form-ui-handler.ts b/src/ui/handlers/login-form-ui-handler.ts index 034023d5789..6c4a7cfeaa6 100644 --- a/src/ui/handlers/login-form-ui-handler.ts +++ b/src/ui/handlers/login-form-ui-handler.ts @@ -165,35 +165,36 @@ export class LoginFormUiHandler extends FormModalUiHandler { const config = args[0] as ModalConfig; this.processExternalProvider(config); const originalLoginAction = this.submitAction; - this.submitAction = _ => { - if (globalScene.tweens.getTweensOf(this.modalContainer).length === 0) { - // Prevent overlapping overrides on action modification - this.submitAction = originalLoginAction; - this.sanitizeInputs(); - globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); - const onFail = error => { - globalScene.ui.setMode(UiMode.LOGIN_FORM, Object.assign(config, { errorMessage: error?.trim() })); - globalScene.ui.playError(); - }; - if (!this.inputs[0].text) { - return onFail(i18next.t("menu:emptyUsername")); - } - - const [usernameInput, passwordInput] = this.inputs; - - pokerogueApi.account - .login({ - username: usernameInput.text, - password: passwordInput.text, - }) - .then(error => { - if (!error && originalLoginAction) { - originalLoginAction(); - } else { - onFail(error); - } - }); + this.submitAction = () => { + if (globalScene.tweens.getTweensOf(this.modalContainer).length > 0) { + return; } + // Prevent overlapping overrides on action modification + this.submitAction = originalLoginAction; + this.sanitizeInputs(); + globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); + const onFail = error => { + globalScene.ui.setMode(UiMode.LOGIN_FORM, Object.assign(config, { errorMessage: error?.trim() })); + globalScene.ui.playError(); + }; + if (!this.inputs[0].text) { + return onFail(i18next.t("menu:emptyUsername")); + } + + const [usernameInput, passwordInput] = this.inputs; + + pokerogueApi.account + .login({ + username: usernameInput.text, + password: passwordInput.text, + }) + .then(error => { + if (!error && originalLoginAction) { + originalLoginAction(); + } else { + onFail(error); + } + }); }; return true; diff --git a/src/ui/handlers/menu-ui-handler.ts b/src/ui/handlers/menu-ui-handler.ts index 62e475aec16..9f9c2a44bad 100644 --- a/src/ui/handlers/menu-ui-handler.ts +++ b/src/ui/handlers/menu-ui-handler.ts @@ -781,7 +781,7 @@ export class MenuUiHandler extends MessageUiHandler { showText( text: string, delay?: number, - callback?: Function, + callback?: () => void, callbackDelay?: number, prompt?: boolean, promptDelay?: number, diff --git a/src/ui/handlers/message-ui-handler.ts b/src/ui/handlers/message-ui-handler.ts index 1deaca78493..c414cbe83ab 100644 --- a/src/ui/handlers/message-ui-handler.ts +++ b/src/ui/handlers/message-ui-handler.ts @@ -37,7 +37,7 @@ export abstract class MessageUiHandler extends AwaitableUiHandler { showText( text: string, delay?: number | null, - callback?: Function | null, + callback?: (() => void) | null, callbackDelay?: number | null, prompt?: boolean | null, promptDelay?: number | null, @@ -49,7 +49,7 @@ export abstract class MessageUiHandler extends AwaitableUiHandler { text: string, _name?: string, delay?: number | null, - callback?: Function | null, + callback?: (() => void) | null, callbackDelay?: number | null, prompt?: boolean | null, promptDelay?: number | null, @@ -60,7 +60,7 @@ export abstract class MessageUiHandler extends AwaitableUiHandler { private showTextInternal( text: string, delay?: number | null, - callback?: Function | null, + callback?: (() => void) | null, callbackDelay?: number | null, prompt?: boolean | null, promptDelay?: number | null, @@ -219,7 +219,7 @@ export abstract class MessageUiHandler extends AwaitableUiHandler { } } - showPrompt(callback?: Function | null, callbackDelay?: number | null) { + showPrompt(callback?: (() => void) | null, callbackDelay?: number | null) { const wrappedTextLines = this.message.runWordWrap(this.message.text).split(/\n/g); const textLinesCount = wrappedTextLines.length; const lastTextLine = wrappedTextLines.at(-1) ?? ""; diff --git a/src/ui/handlers/modifier-select-ui-handler.ts b/src/ui/handlers/modifier-select-ui-handler.ts index 95bc30fb97c..59f4bc00105 100644 --- a/src/ui/handlers/modifier-select-ui-handler.ts +++ b/src/ui/handlers/modifier-select-ui-handler.ts @@ -11,6 +11,7 @@ import { UiMode } from "#enums/ui-mode"; import { HealShopCostModifier, LockModifierTiersModifier, PokemonHeldItemModifier } from "#modifiers/modifier"; import type { ModifierTypeOption } from "#modifiers/modifier-type"; import { getPlayerShopModifierTypeOptionsForWave, TmModifierType } from "#modifiers/modifier-type"; +import type { ModifierSelectCallback } from "#phases/select-modifier-phase"; import { AwaitableUiHandler } from "#ui/awaitable-ui-handler"; import { MoveInfoOverlay } from "#ui/move-info-overlay"; import { addTextObject, getModifierTierTextTint, getTextColor, getTextStyleOptions } from "#ui/text"; @@ -34,6 +35,7 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler { private lockRarityButtonText: Phaser.GameObjects.Text; private moveInfoOverlay: MoveInfoOverlay; private moveInfoOverlayActive = false; + protected declare onActionInput: ModifierSelectCallback | null; private rowCursor = 0; private player: boolean; @@ -424,7 +426,8 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler { const originalOnActionInput = this.onActionInput; this.awaitingActionInput = false; this.onActionInput = null; - originalOnActionInput(-1); + // TODO: What is a good fallback to pass to this? + originalOnActionInput(-1, -1); this.moveInfoOverlayActive = this.moveInfoOverlay.active; this.moveInfoOverlay.setVisible(false); this.moveInfoOverlay.active = false; // don't clear here as we might need to restore the UI in case the user cancels the action diff --git a/src/ui/handlers/party-ui-handler.ts b/src/ui/handlers/party-ui-handler.ts index 6fbb4052aeb..9e30f68e0a2 100644 --- a/src/ui/handlers/party-ui-handler.ts +++ b/src/ui/handlers/party-ui-handler.ts @@ -1289,7 +1289,7 @@ export class PartyUiHandler extends MessageUiHandler { showText( text: string, delay?: number | null, - callback?: Function | null, + callback?: (() => void) | null, callbackDelay?: number | null, prompt?: boolean | null, promptDelay?: number | null, diff --git a/src/ui/handlers/pokedex-page-ui-handler.ts b/src/ui/handlers/pokedex-page-ui-handler.ts index c3be9f87d21..be11a752541 100644 --- a/src/ui/handlers/pokedex-page-ui-handler.ts +++ b/src/ui/handlers/pokedex-page-ui-handler.ts @@ -1085,7 +1085,7 @@ export class PokedexPageUiHandler extends MessageUiHandler { showText( text: string, delay?: number, - callback?: Function, + callback?: () => void, callbackDelay?: number, prompt?: boolean, promptDelay?: number, diff --git a/src/ui/handlers/pokedex-scan-ui-handler.ts b/src/ui/handlers/pokedex-scan-ui-handler.ts index b046a3dab3d..9c013623c61 100644 --- a/src/ui/handlers/pokedex-scan-ui-handler.ts +++ b/src/ui/handlers/pokedex-scan-ui-handler.ts @@ -165,7 +165,7 @@ export class PokedexScanUiHandler extends FormModalUiHandler { } else { this.inputs[0].text = args[1]; } - this.submitAction = _ => { + this.submitAction = () => { if (ui.getMode() === UiMode.POKEDEX_SCAN) { this.sanitizeInputs(); const outputName = this.reducedKeys.includes(this.inputs[0].text) ? this.inputs[0].text : ""; diff --git a/src/ui/handlers/pokedex-ui-handler.ts b/src/ui/handlers/pokedex-ui-handler.ts index 33f8498d37c..b0afd88cf7a 100644 --- a/src/ui/handlers/pokedex-ui-handler.ts +++ b/src/ui/handlers/pokedex-ui-handler.ts @@ -34,7 +34,6 @@ import type { GameData } from "#system/game-data"; import { SettingKeyboard } from "#system/settings-keyboard"; import type { DexEntry } from "#types/dex-data"; import type { DexAttrProps, StarterAttributes } from "#types/save-data"; -import type { AnyFn } from "#types/type-helpers"; import type { OptionSelectConfig } from "#ui/abstract-option-select-ui-handler"; import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#ui/dropdown"; import { FilterBar } from "#ui/filter-bar"; @@ -239,7 +238,7 @@ export class PokedexUiHandler extends MessageUiHandler { private filteredIndices: SpeciesId[]; private gameData: GameData; - private exitCallback?: AnyFn; + private exitCallback?: () => void; private blockOpenPage = false; constructor() { @@ -793,7 +792,7 @@ export class PokedexUiHandler extends MessageUiHandler { showText( text: string, delay?: number, - callback?: Function, + callback?: () => void, callbackDelay?: number, prompt?: boolean, promptDelay?: number, diff --git a/src/ui/handlers/registration-form-ui-handler.ts b/src/ui/handlers/registration-form-ui-handler.ts index 2c8080d534d..e5220351497 100644 --- a/src/ui/handlers/registration-form-ui-handler.ts +++ b/src/ui/handlers/registration-form-ui-handler.ts @@ -76,7 +76,7 @@ export class RegistrationFormUiHandler extends FormModalUiHandler { const config = args[0] as ModalConfig; const originalRegistrationAction = this.submitAction; - this.submitAction = _ => { + this.submitAction = () => { if (globalScene.tweens.getTweensOf(this.modalContainer).length === 0) { // Prevent overlapping overrides on action modification this.submitAction = originalRegistrationAction; diff --git a/src/ui/handlers/rename-form-ui-handler.ts b/src/ui/handlers/rename-form-ui-handler.ts index 9da5b0e8554..a7da11844ce 100644 --- a/src/ui/handlers/rename-form-ui-handler.ts +++ b/src/ui/handlers/rename-form-ui-handler.ts @@ -42,7 +42,7 @@ export class RenameFormUiHandler extends FormModalUiHandler { } else { this.inputs[0].text = args[1]; } - this.submitAction = _ => { + this.submitAction = () => { this.sanitizeInputs(); const sanitizedName = btoa(unescape(encodeURIComponent(this.inputs[0].text))); config.buttonActions[0](sanitizedName); diff --git a/src/ui/handlers/rename-run-ui-handler.ts b/src/ui/handlers/rename-run-ui-handler.ts index 43094d3cf60..b0a62cd6acc 100644 --- a/src/ui/handlers/rename-run-ui-handler.ts +++ b/src/ui/handlers/rename-run-ui-handler.ts @@ -42,7 +42,7 @@ export class RenameRunFormUiHandler extends FormModalUiHandler { }); } const config = args[0] as ModalConfig; - this.submitAction = _ => { + this.submitAction = () => { this.sanitizeInputs(); const sanitizedName = btoa(encodeURIComponent(this.inputs[0].text)); config.buttonActions[0](sanitizedName); diff --git a/src/ui/handlers/save-slot-select-ui-handler.ts b/src/ui/handlers/save-slot-select-ui-handler.ts index 90bb6b1febf..4fc80bcf1ee 100644 --- a/src/ui/handlers/save-slot-select-ui-handler.ts +++ b/src/ui/handlers/save-slot-select-ui-handler.ts @@ -343,7 +343,7 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler { showText( text: string, delay?: number, - callback?: Function, + callback?: () => void, callbackDelay?: number, prompt?: boolean, promptDelay?: number, diff --git a/src/ui/handlers/starter-select-ui-handler.ts b/src/ui/handlers/starter-select-ui-handler.ts index c527b40dbff..e57173cf7f0 100644 --- a/src/ui/handlers/starter-select-ui-handler.ts +++ b/src/ui/handlers/starter-select-ui-handler.ts @@ -1334,7 +1334,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { showText( text: string, delay?: number, - callback?: Function, + callback?: () => void, callbackDelay?: number, prompt?: boolean, promptDelay?: number, diff --git a/src/ui/handlers/summary-ui-handler.ts b/src/ui/handlers/summary-ui-handler.ts index d659834f208..b93cee6e1d5 100644 --- a/src/ui/handlers/summary-ui-handler.ts +++ b/src/ui/handlers/summary-ui-handler.ts @@ -126,7 +126,7 @@ export class SummaryUiHandler extends UiHandler { private playerParty: boolean; /**This is set to false when checking the summary of a freshly caught Pokemon as it is not part of a player's party yet but still needs to display its items*/ private newMove: Move | null; - private moveSelectFunction: Function | null; + private moveSelectFunction: ((cursor: number) => void) | null; private transitioning: boolean; private statusVisible: boolean; private moveEffectsVisible: boolean; @@ -134,7 +134,7 @@ export class SummaryUiHandler extends UiHandler { private moveSelect: boolean; private moveCursor: number; private selectedMoveIndex: number; - private selectCallback: Function | null; + private selectCallback: ((cursor: number) => void) | null; constructor() { super(UiMode.SUMMARY); @@ -335,18 +335,44 @@ export class SummaryUiHandler extends UiHandler { return `summary_${Page[page].toLowerCase()}`; } - show(args: any[]): boolean { + show( + args: [ + pokemon: PlayerPokemon, + uiMode?: SummaryUiMode.DEFAULT, + startPage?: Page, + selectCallback?: (cursor: number) => void, + player?: boolean, + ], + ): boolean; + show( + args: [ + pokemon: PlayerPokemon, + uiMode: SummaryUiMode.LEARN_MOVE, + move?: Move, + moveSelectCallback?: (cursor: number) => void, + player?: boolean, + ], + ): boolean; + show( + args: [ + pokemon: PlayerPokemon, + uiMode?: SummaryUiMode, + startPage?: Page | Move, + callback?: (cursor: number) => void, + player?: boolean, + ], + ): boolean { super.show(args); /* args[] information * args[0] : the Pokemon displayed in the Summary-UI * args[1] : the summaryUiMode (defaults to 0) - * args[2] : the start page (defaults to Page.PROFILE) + * args[2] : the start page (defaults to Page.PROFILE), or the move being selected * args[3] : contains the function executed when the user exits out of Summary UI * args[4] : optional boolean used to determine if the Pokemon is part of the player's party or not (defaults to true, necessary for PR #2921 to display all relevant information) */ this.pokemon = args[0] as PlayerPokemon; - this.summaryUiMode = args.length > 1 ? (args[1] as SummaryUiMode) : SummaryUiMode.DEFAULT; + this.summaryUiMode = (args[1] as SummaryUiMode) ?? SummaryUiMode.DEFAULT; this.playerParty = args[4] ?? true; globalScene.ui.bringToTop(this.summaryContainer); @@ -486,17 +512,15 @@ export class SummaryUiHandler extends UiHandler { switch (this.summaryUiMode) { case SummaryUiMode.DEFAULT: { - const page = args.length < 2 ? Page.PROFILE : (args[2] as Page); + const page = (args[2] as Page) ?? Page.PROFILE; this.hideMoveEffect(true); this.setCursor(page); - if (args.length > 3) { - this.selectCallback = args[3]; - } + this.selectCallback = args[3] ?? null; break; } case SummaryUiMode.LEARN_MOVE: this.newMove = args[2] as Move; - this.moveSelectFunction = args[3] as Function; + this.moveSelectFunction = args[3] ?? null; this.showMoveEffect(true); this.setCursor(Page.MOVES); @@ -615,7 +639,7 @@ export class SummaryUiHandler extends UiHandler { if (this.selectCallback instanceof Function) { const selectCallback = this.selectCallback; this.selectCallback = null; - selectCallback(); + selectCallback(-1); } if (!fromPartyMode) { diff --git a/src/ui/handlers/test-dialogue-ui-handler.ts b/src/ui/handlers/test-dialogue-ui-handler.ts index 0bad8f6f0a9..b692377e415 100644 --- a/src/ui/handlers/test-dialogue-ui-handler.ts +++ b/src/ui/handlers/test-dialogue-ui-handler.ts @@ -12,7 +12,7 @@ export class TestDialogueUiHandler extends FormModalUiHandler { setup() { super.setup(); - const flattenKeys = (object?: any, topKey?: string, middleKey?: string[]): Array => { + const flattenKeys = (object?: any, topKey?: string, middleKey?: string[]): any[] => { return Object.keys(object ?? {}) .map((t, i) => { const value = Object.values(object)[i]; @@ -141,7 +141,7 @@ export class TestDialogueUiHandler extends FormModalUiHandler { } else { this.inputs[0].text = args[1]; } - this.submitAction = _ => { + this.submitAction = () => { if (ui.getMode() === UiMode.TEST_DIALOGUE) { this.sanitizeInputs(); const sanitizedName = btoa(unescape(encodeURIComponent(this.inputs[0].text))); diff --git a/src/ui/settings/abstract-control-settings-ui-handler.ts b/src/ui/settings/abstract-control-settings-ui-handler.ts index 17812785d1e..fdb87bc30c8 100644 --- a/src/ui/settings/abstract-control-settings-ui-handler.ts +++ b/src/ui/settings/abstract-control-settings-ui-handler.ts @@ -24,7 +24,7 @@ export interface LayoutConfig { optionValueLabels: Phaser.GameObjects.Text[][]; optionCursors: number[]; keys: string[]; - bindingSettings: Array; + bindingSettings: string[]; } /** * Abstract class for handling UI elements related to control settings. @@ -51,10 +51,10 @@ export abstract class AbstractControlSettingsUiHandler extends UiHandler { protected inputsIcons: InputsIcons; protected navigationIcons: InputsIcons; // list all the setting keys used in the selected layout (because dualshock has more buttons than xbox) - protected keys: Array; + protected keys: string[]; // Store the specific settings related to key bindings for the current gamepad configuration. - protected bindingSettings: Array; + protected bindingSettings: string[]; protected setting; protected settingBlacklisted; diff --git a/src/ui/settings/abstract-settings-ui-handler.ts b/src/ui/settings/abstract-settings-ui-handler.ts index 78c34a47c06..78315176df9 100644 --- a/src/ui/settings/abstract-settings-ui-handler.ts +++ b/src/ui/settings/abstract-settings-ui-handler.ts @@ -4,7 +4,6 @@ import { TextStyle } from "#enums/text-style"; import { UiMode } from "#enums/ui-mode"; import type { SettingType } from "#system/settings"; import { Setting, SettingKeys } from "#system/settings"; -import type { AnyFn } from "#types/type-helpers"; import type { InputsIcons } from "#ui/abstract-control-settings-ui-handler"; import { MessageUiHandler } from "#ui/message-ui-handler"; import { NavigationManager, NavigationMenu } from "#ui/navigation-menu"; @@ -40,7 +39,7 @@ export class AbstractSettingsUiHandler extends MessageUiHandler { protected rowsToDisplay: number; protected title: string; - protected settings: Array; + protected settings: Setting[]; protected localStorageKey: string; constructor(type: SettingType, mode: UiMode | null = null) { @@ -518,7 +517,7 @@ export class AbstractSettingsUiHandler extends MessageUiHandler { override showText( text: string, delay?: number, - callback?: AnyFn, + callback?: () => void, callbackDelay?: number, prompt?: boolean, promptDelay?: number, diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 76b07d7bfa5..29a3acd983e 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -277,7 +277,7 @@ export class UI extends Phaser.GameObjects.Container { showText( text: string, delay?: number | null, - callback?: Function | null, + callback?: (() => void) | null, callbackDelay?: number | null, prompt?: boolean | null, promptDelay?: number | null, @@ -316,7 +316,7 @@ export class UI extends Phaser.GameObjects.Container { keyOrText: string, name: string | undefined, delay: number | null = 0, - callback: Function, + callback: () => void, callbackDelay?: number, promptDelay?: number, ): void { diff --git a/test/abilities/dry-skin.test.ts b/test/abilities/dry-skin.test.ts index ad88c5aa377..47f8dd8fabc 100644 --- a/test/abilities/dry-skin.test.ts +++ b/test/abilities/dry-skin.test.ts @@ -148,7 +148,7 @@ describe("Abilities - Dry Skin", () => { const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.WATER_GUN); - enemy.hp = enemy.hp - 1; + enemy.hp -= 1; await game.phaseInterceptor.to("MoveEffectPhase"); await game.move.forceMiss(); diff --git a/test/abilities/volt-absorb.test.ts b/test/abilities/volt-absorb.test.ts index 9ba76028703..bf1098fc8ce 100644 --- a/test/abilities/volt-absorb.test.ts +++ b/test/abilities/volt-absorb.test.ts @@ -65,7 +65,7 @@ describe("Abilities - Volt Absorb", () => { const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.THUNDERBOLT); - enemyPokemon.hp = enemyPokemon.hp - 1; + enemyPokemon.hp -= 1; await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to("MoveEffectPhase"); @@ -86,7 +86,7 @@ describe("Abilities - Volt Absorb", () => { const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.THUNDERBOLT); - enemyPokemon.hp = enemyPokemon.hp - 1; + enemyPokemon.hp -= 1; await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to("BerryPhase", false); diff --git a/test/test-utils/mocks/mocks-container/mock-container.ts b/test/test-utils/mocks/mocks-container/mock-container.ts index 0c99545a109..ef173114023 100644 --- a/test/test-utils/mocks/mocks-container/mock-container.ts +++ b/test/test-utils/mocks/mocks-container/mock-container.ts @@ -2,6 +2,7 @@ import type { MockGameObject } from "#test/test-utils/mocks/mock-game-object"; import type { MockTextureManager } from "#test/test-utils/mocks/mock-texture-manager"; import { coerceArray } from "#utils/array"; +// TODO: Make this implement Phaser.GameObjects.Container export class MockContainer implements MockGameObject { protected x: number; protected y: number; diff --git a/test/test-utils/mocks/mocks-container/mock-text.ts b/test/test-utils/mocks/mocks-container/mock-text.ts index 1216597fbe0..4856935fa5a 100644 --- a/test/test-utils/mocks/mocks-container/mock-text.ts +++ b/test/test-utils/mocks/mocks-container/mock-text.ts @@ -82,7 +82,7 @@ export class MockText implements MockGameObject { showText( text: string, _delay?: number | null, - callback?: Function | null, + callback?: (() => void) | null, _callbackDelay?: number | null, _prompt?: boolean | null, _promptDelay?: number | null, @@ -98,7 +98,7 @@ export class MockText implements MockGameObject { keyOrText: string, name: string, _delay: number | null, - callback: Function, + callback: () => void, _callbackDelay?: number, _promptDelay?: number, ) { @@ -354,7 +354,7 @@ export class MockText implements MockGameObject { } // biome-ignore lint/complexity/noBannedTypes: This matches the signature of the class this mocks - on(_event: string | symbol, _fn: Function, _context?: any) {} + on(_event: string | symbol, _fn: () => void, _context?: any) {} setActive(_active: boolean): this { return this;