From 076ef81691984df94d78212866145c505d221baa Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Wed, 13 Aug 2025 20:49:46 -0500 Subject: [PATCH 1/3] [Bug] [UI/UX] [Beta] Fix icons not showing in save slot selection (#6262) Fix icons not showing in save slot selection --- src/ui/save-slot-select-ui-handler.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index c86f2ea66bf..52e145e6439 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -594,13 +594,9 @@ class SessionSlot extends Phaser.GameObjects.Container { TextStyle.PARTY, { fontSize: "54px", color: "#f8f8f8" }, ); - text.setShadow(0, 0, undefined); - text.setStroke("#424242", 14); - text.setOrigin(1, 0); - - iconContainer.add(icon); - iconContainer.add(text); + text.setShadow(0, 0, undefined).setStroke("#424242", 14).setOrigin(1, 0); + iconContainer.add([icon, text]); pokemonIconsContainer.add(iconContainer); pokemon.destroy(); @@ -645,6 +641,7 @@ class SessionSlot extends Phaser.GameObjects.Container { return; } this.saveData = sessionData; + this.setupWithData(sessionData); resolve(true); }) .catch(e => { From b44f0a4176a383c6f2427a3f8da4c80aadeb1534 Mon Sep 17 00:00:00 2001 From: fabske0 <192151969+fabske0@users.noreply.github.com> Date: Thu, 14 Aug 2025 18:52:56 +0200 Subject: [PATCH 2/3] [Refactor] Remove bgm param from arena constructor (#6254) --- src/battle-scene.ts | 4 ++-- src/field/arena.ts | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 271cde1aaa9..52af0c1b706 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1513,8 +1513,8 @@ export class BattleScene extends SceneBase { return this.currentBattle; } - newArena(biome: BiomeId, playerFaints?: number): Arena { - this.arena = new Arena(biome, BiomeId[biome].toLowerCase(), playerFaints); + newArena(biome: BiomeId, playerFaints = 0): Arena { + this.arena = new Arena(biome, playerFaints); this.eventTarget.dispatchEvent(new NewArenaEvent()); this.arenaBg.pipelineData = { diff --git a/src/field/arena.ts b/src/field/arena.ts index 6f2310b95c2..2ce347b5337 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -54,7 +54,7 @@ export class Arena { public bgm: string; public ignoreAbilities: boolean; public ignoringEffectSource: BattlerIndex | null; - public playerTerasUsed: number; + public playerTerasUsed = 0; /** * Saves the number of times a party pokemon faints during a arena encounter. * {@linkcode globalScene.currentBattle.enemyFaints} is the corresponding faint counter for the enemy (this resets every wave). @@ -68,12 +68,11 @@ export class Arena { public readonly eventTarget: EventTarget = new EventTarget(); - constructor(biome: BiomeId, bgm: string, playerFaints = 0) { + constructor(biome: BiomeId, playerFaints = 0) { this.biomeType = biome; - this.bgm = bgm; + this.bgm = BiomeId[biome].toLowerCase(); this.trainerPool = biomeTrainerPools[biome]; this.updatePoolsForTimeOfDay(); - this.playerTerasUsed = 0; this.playerFaints = playerFaints; } From f42237d415596f0f07b12a71524c7b1c2b145c7d Mon Sep 17 00:00:00 2001 From: Bertie690 <136088738+Bertie690@users.noreply.github.com> Date: Thu, 14 Aug 2025 13:25:44 -0400 Subject: [PATCH 3/3] [Refactor] Removed `map(x => x)` (#6256) * Enforced a few usages of `toCamelCase` * Removed `map(x => x)` * Removed more maps and sufff * Update test/mystery-encounter/encounters/weird-dream-encounter.test.ts * Update game-data.ts types to work --- src/battle-scene.ts | 12 ++-- src/data/abilities/ability.ts | 9 +-- src/data/balance/pokemon-evolutions.ts | 9 ++- src/data/moves/move.ts | 18 +++-- .../pokemon-forms/form-change-triggers.ts | 7 +- src/loading-scene.ts | 4 +- src/modifier/modifier.ts | 4 +- src/system/game-data.ts | 67 ++++++++++--------- src/ui/run-info-ui-handler.ts | 6 +- src/ui/test-dialogue-ui-handler.ts | 2 +- .../encounters/weird-dream-encounter.test.ts | 2 +- 11 files changed, 72 insertions(+), 68 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 52af0c1b706..2a24d4144d8 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -104,6 +104,7 @@ import { getLuckString, getLuckTextTint, getPartyLuckValue, + type ModifierType, PokemonHeldItemModifierType, } from "#modifiers/modifier-type"; import { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; @@ -1203,7 +1204,9 @@ export class BattleScene extends SceneBase { this.updateScoreText(); this.scoreText.setVisible(false); - [this.luckLabelText, this.luckText].map(t => t.setVisible(false)); + [this.luckLabelText, this.luckText].forEach(t => { + t.setVisible(false); + }); this.newArena(Overrides.STARTING_BIOME_OVERRIDE || BiomeId.TOWN); @@ -1237,8 +1240,7 @@ export class BattleScene extends SceneBase { Object.values(mp) .flat() .map(mt => mt.modifierType) - .filter(mt => "localize" in mt) - .map(lpb => lpb as unknown as Localizable), + .filter((mt): mt is ModifierType & Localizable => "localize" in mt && typeof mt.localize === "function"), ), ]; for (const item of localizable) { @@ -2711,7 +2713,9 @@ export class BattleScene extends SceneBase { } } - this.party.map(p => p.updateInfo(instant)); + this.party.forEach(p => { + p.updateInfo(instant); + }); } else { const args = [this]; if (modifier.shouldApply(...args)) { diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index c7c10d46d38..03670835dbd 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -74,6 +74,7 @@ import { randSeedItem, toDmgValue, } from "#utils/common"; +import { toCamelCase } from "#utils/strings"; import i18next from "i18next"; export class Ability implements Localizable { @@ -109,13 +110,9 @@ export class Ability implements Localizable { } localize(): void { - const i18nKey = AbilityId[this.id] - .split("_") - .filter(f => f) - .map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase())) - .join("") as string; + const i18nKey = toCamelCase(AbilityId[this.id]); - this.name = this.id ? `${i18next.t(`ability:${i18nKey}.name`) as string}${this.nameAppend}` : ""; + this.name = this.id ? `${i18next.t(`ability:${i18nKey}.name`)}${this.nameAppend}` : ""; this.description = this.id ? (i18next.t(`ability:${i18nKey}.description`) as string) : ""; } diff --git a/src/data/balance/pokemon-evolutions.ts b/src/data/balance/pokemon-evolutions.ts index ab535682e86..5d3537f4255 100644 --- a/src/data/balance/pokemon-evolutions.ts +++ b/src/data/balance/pokemon-evolutions.ts @@ -1866,17 +1866,16 @@ interface PokemonPrevolutions { export const pokemonPrevolutions: PokemonPrevolutions = {}; export function initPokemonPrevolutions(): void { - const megaFormKeys = [ SpeciesFormKey.MEGA, "", SpeciesFormKey.MEGA_X, "", SpeciesFormKey.MEGA_Y ].map(sfk => sfk as string); - const prevolutionKeys = Object.keys(pokemonEvolutions); - prevolutionKeys.forEach(pk => { - const evolutions = pokemonEvolutions[pk]; + // TODO: Why do we have empty strings in our array? + const megaFormKeys = [ SpeciesFormKey.MEGA, "", SpeciesFormKey.MEGA_X, "", SpeciesFormKey.MEGA_Y ]; + for (const [pk, evolutions] of Object.entries(pokemonEvolutions)) { for (const ev of evolutions) { if (ev.evoFormKey && megaFormKeys.indexOf(ev.evoFormKey) > -1) { continue; } pokemonPrevolutions[ev.speciesId] = Number.parseInt(pk) as SpeciesId; } - }); + } } diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 442b6fabb51..5c4061ec388 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -90,7 +90,7 @@ import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindS import type { TurnMove } from "#types/turn-move"; import { BooleanHolder, type Constructor, isNullOrUndefined, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common"; import { getEnumValues } from "#utils/enums"; -import { toTitleCase } from "#utils/strings"; +import { toCamelCase, toTitleCase } from "#utils/strings"; import i18next from "i18next"; import { applyChallenges } from "#utils/challenge-utils"; @@ -162,10 +162,16 @@ export abstract class Move implements Localizable { } localize(): void { - const i18nKey = MoveId[this.id].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("") as unknown as string; + const i18nKey = toCamelCase(MoveId[this.id]) - this.name = this.id ? `${i18next.t(`move:${i18nKey}.name`)}${this.nameAppend}` : ""; - this.effect = this.id ? `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}` : ""; + if (this.id === MoveId.NONE) { + this.name = ""; + this.effect = "" + return; + } + + this.name = `${i18next.t(`move:${i18nKey}.name`)}${this.nameAppend}`; + this.effect = `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}`; } /** @@ -5926,8 +5932,8 @@ export class ProtectAttr extends AddBattlerTagAttr { for (const turnMove of user.getLastXMoves(-1).slice()) { if ( // Quick & Wide guard increment the Protect counter without using it for fail chance - !(allMoves[turnMove.move].hasAttr("ProtectAttr") || - [MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) || + !(allMoves[turnMove.move].hasAttr("ProtectAttr") || + [MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) || turnMove.result !== MoveResult.SUCCESS ) { break; diff --git a/src/data/pokemon-forms/form-change-triggers.ts b/src/data/pokemon-forms/form-change-triggers.ts index 75734bf085b..c24466eb5ec 100644 --- a/src/data/pokemon-forms/form-change-triggers.ts +++ b/src/data/pokemon-forms/form-change-triggers.ts @@ -12,6 +12,7 @@ import { WeatherType } from "#enums/weather-type"; import type { Pokemon } from "#field/pokemon"; import type { PokemonFormChangeItemModifier } from "#modifiers/modifier"; import { type Constructor, coerceArray } from "#utils/common"; +import { toCamelCase } from "#utils/strings"; import i18next from "i18next"; export abstract class SpeciesFormChangeTrigger { @@ -143,11 +144,7 @@ export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigge super(); this.move = move; this.known = known; - const moveKey = MoveId[this.move] - .split("_") - .filter(f => f) - .map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase())) - .join("") as unknown as string; + const moveKey = toCamelCase(MoveId[this.move]); this.description = known ? i18next.t("pokemonEvolutions:Forms.moveLearned", { move: i18next.t(`move:${moveKey}.name`), diff --git a/src/loading-scene.ts b/src/loading-scene.ts index d2b4a76ef10..6ff39ef3dde 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -447,7 +447,9 @@ export class LoadingScene extends SceneBase { ); if (!mobile) { - loadingGraphics.map(g => g.setVisible(false)); + loadingGraphics.forEach(g => { + g.setVisible(false); + }); } const intro = this.add.video(0, 0); diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 6907b6907ca..75c18c67f38 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -121,8 +121,8 @@ export class ModifierBar extends Phaser.GameObjects.Container { } updateModifierOverflowVisibility(ignoreLimit: boolean) { - const modifierIcons = this.getAll().reverse(); - for (const modifier of modifierIcons.map(m => m as Phaser.GameObjects.Container).slice(iconOverflowIndex)) { + const modifierIcons = this.getAll().reverse() as Phaser.GameObjects.Container[]; + for (const modifier of modifierIcons.slice(iconOverflowIndex)) { modifier.setVisible(ignoreLimit); } } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 0313d64dd80..589e1271e3c 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -207,10 +207,12 @@ export interface StarterData { [key: number]: StarterDataEntry; } -export interface TutorialFlags { - [key: string]: boolean; -} +// TODO: Rework into a bitmask +export type TutorialFlags = { + [key in Tutorial]: boolean; +}; +// TODO: Rework into a bitmask export interface SeenDialogues { [key: string]: boolean; } @@ -823,52 +825,51 @@ export class GameData { return true; // TODO: is `true` the correct return value? } - private loadGamepadSettings(): boolean { - Object.values(SettingGamepad) - .map(setting => setting as SettingGamepad) - .forEach(setting => setSettingGamepad(setting, settingGamepadDefaults[setting])); + private loadGamepadSettings(): void { + Object.values(SettingGamepad).forEach(setting => { + setSettingGamepad(setting, settingGamepadDefaults[setting]); + }); if (!localStorage.hasOwnProperty("settingsGamepad")) { - return false; + return; } const settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad")!); // TODO: is this bang correct? for (const setting of Object.keys(settingsGamepad)) { setSettingGamepad(setting as SettingGamepad, settingsGamepad[setting]); } - - return true; // TODO: is `true` the correct return value? } - public saveTutorialFlag(tutorial: Tutorial, flag: boolean): boolean { - const key = getDataTypeKey(GameDataType.TUTORIALS); - let tutorials: object = {}; - if (localStorage.hasOwnProperty(key)) { - tutorials = JSON.parse(localStorage.getItem(key)!); // TODO: is this bang correct? + /** + * Save the specified tutorial as having the specified completion status. + * @param tutorial - The {@linkcode Tutorial} whose completion status is being saved + * @param status - The completion status to set + */ + public saveTutorialFlag(tutorial: Tutorial, status: boolean): void { + // Grab the prior save data tutorial + const saveDataKey = getDataTypeKey(GameDataType.TUTORIALS); + const tutorials: TutorialFlags = localStorage.hasOwnProperty(saveDataKey) + ? JSON.parse(localStorage.getItem(saveDataKey)!) + : {}; + + // TODO: We shouldn't be storing this like that + for (const key of Object.values(Tutorial)) { + if (key === tutorial) { + tutorials[key] = status; + } else { + tutorials[key] ??= false; + } } - Object.keys(Tutorial) - .map(t => t as Tutorial) - .forEach(t => { - const key = Tutorial[t]; - if (key === tutorial) { - tutorials[key] = flag; - } else { - tutorials[key] ??= false; - } - }); - - localStorage.setItem(key, JSON.stringify(tutorials)); - - return true; + localStorage.setItem(saveDataKey, JSON.stringify(tutorials)); } public getTutorialFlags(): TutorialFlags { const key = getDataTypeKey(GameDataType.TUTORIALS); - const ret: TutorialFlags = {}; - Object.values(Tutorial) - .map(tutorial => tutorial as Tutorial) - .forEach(tutorial => (ret[Tutorial[tutorial]] = false)); + const ret: TutorialFlags = Object.values(Tutorial).reduce((acc, tutorial) => { + acc[Tutorial[tutorial]] = false; + return acc; + }, {} as TutorialFlags); if (!localStorage.hasOwnProperty(key)) { return ret; diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index 072eefad65a..8facd8e73b1 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -26,6 +26,7 @@ import { addBBCodeTextObject, addTextObject, getTextColor } from "#ui/text"; import { UiHandler } from "#ui/ui-handler"; import { addWindow } from "#ui/ui-theme"; import { formatFancyLargeNumber, formatLargeNumber, formatMoney, getPlayTimeString } from "#utils/common"; +import { toCamelCase } from "#utils/strings"; import i18next from "i18next"; import RoundRectangle from "phaser3-rex-plugins/plugins/roundrectangle"; @@ -706,10 +707,7 @@ export class RunInfoUiHandler extends UiHandler { rules.push(i18next.t("challenges:inverseBattle.shortName")); break; default: { - const localizationKey = Challenges[this.runInfo.challenges[i].id] - .split("_") - .map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase())) - .join(""); + const localizationKey = toCamelCase(Challenges[this.runInfo.challenges[i].id]); rules.push(i18next.t(`challenges:${localizationKey}.name`)); break; } diff --git a/src/ui/test-dialogue-ui-handler.ts b/src/ui/test-dialogue-ui-handler.ts index 4f825ed95ea..6f7c79a151b 100644 --- a/src/ui/test-dialogue-ui-handler.ts +++ b/src/ui/test-dialogue-ui-handler.ts @@ -31,7 +31,7 @@ export class TestDialogueUiHandler extends FormModalUiHandler { // we check for null or undefined here as per above - the typeof is still an object but the value is null so we need to exit out of this and pass the null key // Return in the format expected by i18next - return middleKey ? `${topKey}:${middleKey.map(m => m).join(".")}.${t}` : `${topKey}:${t}`; + return middleKey ? `${topKey}:${middleKey.join(".")}.${t}` : `${topKey}:${t}`; } }) .filter(t => t); diff --git a/test/mystery-encounter/encounters/weird-dream-encounter.test.ts b/test/mystery-encounter/encounters/weird-dream-encounter.test.ts index ed0d612e967..9b430ec046e 100644 --- a/test/mystery-encounter/encounters/weird-dream-encounter.test.ts +++ b/test/mystery-encounter/encounters/weird-dream-encounter.test.ts @@ -112,7 +112,7 @@ describe("Weird Dream - Mystery Encounter", () => { it("should transform the new party into new species, 2 at +90/+110, the rest at +40/50 BST", async () => { await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty); - const pokemonPrior = scene.getPlayerParty().map(pokemon => pokemon); + const pokemonPrior = scene.getPlayerParty().slice(); const bstsPrior = pokemonPrior.map(species => species.getSpeciesForm().getBaseStatTotal()); await runMysteryEncounterToEnd(game, 1);