From 7d624f508bb607ae4feebe1b6537e82cfda5c636 Mon Sep 17 00:00:00 2001 From: Aotumuri Date: Wed, 1 Oct 2025 21:11:53 +0900 Subject: [PATCH] Add pack handling to lobby configuration and cosmetic loading --- src/client/ClientGameRunner.ts | 1 + src/client/CosmeticPackLoader.ts | 4 +-- src/client/Main.ts | 1 + src/client/SinglePlayerModal.ts | 2 ++ src/client/Transport.ts | 1 + src/client/graphics/SpriteLoader.ts | 6 ++--- src/client/graphics/layers/StructureLayer.ts | 27 +++++++++++++++----- src/client/graphics/layers/UnitLayer.ts | 14 ++++++++-- src/core/Schemas.ts | 2 ++ src/core/game/UserSettings.ts | 4 +++ src/server/Worker.ts | 1 + 11 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index 376a5bcc2..df0dd932b 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -52,6 +52,7 @@ export interface LobbyConfig { serverConfig: ServerConfig; pattern: PlayerPattern | undefined; flag: string; + pack: string | undefined; playerName: string; clientID: ClientID; gameID: GameID; diff --git a/src/client/CosmeticPackLoader.ts b/src/client/CosmeticPackLoader.ts index 0cf5b5c25..1ffdd3587 100644 --- a/src/client/CosmeticPackLoader.ts +++ b/src/client/CosmeticPackLoader.ts @@ -23,11 +23,11 @@ function fetchManifest(packId: string): CosmeticManifest | undefined { } export async function resolveCosmeticUrl( - packId: string | null, + packId: string | undefined, key: string | undefined, fallback: string, ): Promise { - if (!packId || key === undefined) { + if (packId === undefined || key === undefined) { return fallback; } try { diff --git a/src/client/Main.ts b/src/client/Main.ts index 9d4f1eba8..42cda33d3 100644 --- a/src/client/Main.ts +++ b/src/client/Main.ts @@ -516,6 +516,7 @@ class Client { this.flagInput === null || this.flagInput.getCurrentFlag() === "xx" ? "" : this.flagInput.getCurrentFlag(), + pack: this.userSettings.getSelectedPackId() ?? undefined, playerName: this.usernameInput?.getCurrentUsername() ?? "", token: getPlayToken(), clientID: lobby.clientID, diff --git a/src/client/SinglePlayerModal.ts b/src/client/SinglePlayerModal.ts index 0b36b715a..1f1ef1582 100644 --- a/src/client/SinglePlayerModal.ts +++ b/src/client/SinglePlayerModal.ts @@ -448,6 +448,7 @@ export class SinglePlayerModal extends LitElement { selectedPattern ??= cosmetics ? (this.userSettings.getDevOnlyPattern() ?? null) : null; + const selectedPackId = this.userSettings.getSelectedPackId(); this.dispatchEvent( new CustomEvent("join-lobby", { @@ -466,6 +467,7 @@ export class SinglePlayerModal extends LitElement { ? "" : flagInput.getCurrentFlag(), pattern: selectedPattern ?? undefined, + pack: selectedPackId ?? undefined, }, }, ], diff --git a/src/client/Transport.ts b/src/client/Transport.ts index ee49f0c4c..70755adaf 100644 --- a/src/client/Transport.ts +++ b/src/client/Transport.ts @@ -381,6 +381,7 @@ export class Transport { flag: this.lobbyConfig.flag, patternName: this.lobbyConfig.pattern?.name, patternColorPaletteName: this.lobbyConfig.pattern?.colorPalette?.name, + pack: this.lobbyConfig.pack, }, } satisfies ClientJoinMessage); } diff --git a/src/client/graphics/SpriteLoader.ts b/src/client/graphics/SpriteLoader.ts index 27165e8da..7b411ec85 100644 --- a/src/client/graphics/SpriteLoader.ts +++ b/src/client/graphics/SpriteLoader.ts @@ -53,13 +53,13 @@ const SPRITE_CONFIG: Partial< const spriteMap: Map = new Map(); // preload all images -export const loadAllSprites = async (): Promise => { +export const loadAllSprites = async ( + packId: string | undefined, +): Promise => { const entries = Object.entries(SPRITE_CONFIG); const totalSprites = entries.length; let loadedCount = 0; - const packId: string | null = "test"; // TODO: wire from server/client selection - await Promise.all( entries.map(async ([unitType, value]) => { const typedUnitType = unitType as UnitType | TrainTypeSprite; diff --git a/src/client/graphics/layers/StructureLayer.ts b/src/client/graphics/layers/StructureLayer.ts index 843dc57f1..2c900bfdb 100644 --- a/src/client/graphics/layers/StructureLayer.ts +++ b/src/client/graphics/layers/StructureLayer.ts @@ -34,6 +34,8 @@ export class StructureLayer implements Layer { private context: CanvasRenderingContext2D; private unitIcons: Map = new Map(); private theme: Theme; + private packId: string | undefined; + private structureLoaded = false; private tempCanvas: HTMLCanvasElement; private tempContext: CanvasRenderingContext2D; @@ -117,6 +119,14 @@ export class StructureLayer implements Layer { if (unit === undefined) continue; this.handleUnitRendering(unit); } + if (!this.structureLoaded) { + const myPlayer = this.game.myPlayer(); + if (myPlayer) { + this.packId = myPlayer.cosmetics.pack; + this.loadIconData(); + this.structureLoaded = true; + } + } } init() { @@ -124,23 +134,26 @@ export class StructureLayer implements Layer { } private async applyCosmeticIcons(): Promise { - const packSpec = "test"; this.unitConfigs[UnitType.Port] = { ...this.unitConfigs[UnitType.Port]!, icon: await resolveCosmeticUrl( - packSpec, + this.packId, "structure/img/port", anchorIcon, ), }; this.unitConfigs[UnitType.City] = { ...this.unitConfigs[UnitType.City]!, - icon: await resolveCosmeticUrl(packSpec, "structure/img/city", cityIcon), + icon: await resolveCosmeticUrl( + this.packId, + "structure/img/city", + cityIcon, + ), }; this.unitConfigs[UnitType.Factory] = { ...this.unitConfigs[UnitType.Factory]!, icon: await resolveCosmeticUrl( - packSpec, + this.packId, "structure/img/factory", factoryIcon, ), @@ -148,7 +161,7 @@ export class StructureLayer implements Layer { this.unitConfigs[UnitType.MissileSilo] = { ...this.unitConfigs[UnitType.MissileSilo]!, icon: await resolveCosmeticUrl( - packSpec, + this.packId, "structure/img/missilesilo", missileSiloIcon, ), @@ -156,7 +169,7 @@ export class StructureLayer implements Layer { this.unitConfigs[UnitType.DefensePost] = { ...this.unitConfigs[UnitType.DefensePost]!, icon: await resolveCosmeticUrl( - packSpec, + this.packId, "structure/img/defensepost", shieldIcon, ), @@ -164,7 +177,7 @@ export class StructureLayer implements Layer { this.unitConfigs[UnitType.SAMLauncher] = { ...this.unitConfigs[UnitType.SAMLauncher]!, icon: await resolveCosmeticUrl( - packSpec, + this.packId, "structure/img/samlauncher", SAMMissileIcon, ), diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 4945969be..a0a510518 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -36,6 +36,8 @@ export class UnitLayer implements Layer { private unitToTrail = new Map(); private theme: Theme; + private packId: string | undefined = undefined; + private spritesLoaded = false; private alternateView = false; @@ -68,6 +70,15 @@ export class UnitLayer implements Layer { ?.[GameUpdateType.Unit]?.map((unit) => unit.id); this.updateUnitsSprites(unitIds ?? []); + + if (!this.spritesLoaded) { + const myPlayer = this.game.myPlayer(); + if (myPlayer) { + this.packId = myPlayer.cosmetics.pack; + loadAllSprites(this.packId); + this.spritesLoaded = true; + } + } } init() { @@ -75,8 +86,7 @@ export class UnitLayer implements Layer { this.eventBus.on(MouseUpEvent, (e) => this.onMouseUp(e)); this.eventBus.on(UnitSelectionEvent, (e) => this.onUnitSelectionChange(e)); this.redraw(); - - loadAllSprites(); + loadAllSprites(this.packId); } /** diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index 2e2fbc2ee..5c54dc556 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -390,6 +390,7 @@ export const PlayerCosmeticRefsSchema = z.object({ flag: FlagSchema.optional(), patternName: PatternNameSchema.optional(), patternColorPaletteName: z.string().optional(), + pack: z.string().optional(), }); export const PlayerPatternSchema = z.object({ @@ -400,6 +401,7 @@ export const PlayerPatternSchema = z.object({ export const PlayerCosmeticsSchema = z.object({ flag: FlagSchema.optional(), pattern: PlayerPatternSchema.optional(), + pack: z.string().optional(), }); export const PlayerSchema = z.object({ clientID: ID, diff --git a/src/core/game/UserSettings.ts b/src/core/game/UserSettings.ts index fe9f1deeb..ce1702650 100644 --- a/src/core/game/UserSettings.ts +++ b/src/core/game/UserSettings.ts @@ -154,4 +154,8 @@ export class UserSettings { localStorage.setItem(PATTERN_KEY, patternName); } } + + getSelectedPackId(): string | undefined { + return localStorage.getItem("cosmeticPackId") ?? undefined; + } } diff --git a/src/server/Worker.ts b/src/server/Worker.ts index e565f7ab7..9e1d48274 100644 --- a/src/server/Worker.ts +++ b/src/server/Worker.ts @@ -490,6 +490,7 @@ export async function startWorker() { cosmetics: { flag: cosmetics.flag, pattern: pattern, + pack: cosmetics.pack, }, }; }