diff --git a/resources/images/test/test.png b/resources/images/test/test.png new file mode 100644 index 000000000..c915f2cc5 Binary files /dev/null and b/resources/images/test/test.png differ diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index 3d38c9a34..76d6e5996 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -53,6 +53,25 @@ export interface LobbyConfig { serverConfig: ServerConfig; pattern: PlayerPattern | undefined; flag: string; + + structurePort: string | undefined; + structureCity: string | undefined; + structureFactory: string | undefined; + structureMissilesilo: string | undefined; + structureDefensepost: string | undefined; + structureSamlauncher: string | undefined; + + spriteTransportship: string | undefined; + spriteWarship: string | undefined; + spriteSammissile: string | undefined; + spriteAtombomb: string | undefined; + spriteHydrogenbomb: string | undefined; + spriteTradeship: string | undefined; + spriteMirv: string | undefined; + spriteEngine: string | undefined; + spriteCarriage: string | undefined; + spriteLoadedcarriage: string | undefined; + playerName: string; clientID: ClientID; gameID: GameID; diff --git a/src/client/CosmeticPackLoader.ts b/src/client/CosmeticPackLoader.ts new file mode 100644 index 000000000..b1afef9c0 --- /dev/null +++ b/src/client/CosmeticPackLoader.ts @@ -0,0 +1,22 @@ +export function fetchUrl( + packId: string | undefined, + type: string, +): string | undefined { + // TODO: Fetches the resource URL from the API server. + + // Request parameters: + // - packKey: identifier of the cosmetic pack + // - type: asset type (e.g., "structurePort", "structureCity") + // Response: + // - URL string pointing to the requested asset + + // Even if this approach changes, this function will be responsible for obtaining the URL by some method. + + switch (packId) { + case "base": + return; + case "test": + return "/images/test/test.png"; // Example URL for testing + } + return; +} diff --git a/src/client/Main.ts b/src/client/Main.ts index 9d4f1eba8..b4f2d08c4 100644 --- a/src/client/Main.ts +++ b/src/client/Main.ts @@ -516,6 +516,37 @@ class Client { this.flagInput === null || this.flagInput.getCurrentFlag() === "xx" ? "" : this.flagInput.getCurrentFlag(), + structurePort: + this.userSettings.getSelectedStructurePort() ?? undefined, + structureCity: + this.userSettings.getSelectedStructureCity() ?? undefined, + structureFactory: + this.userSettings.getSelectedStructureFactory() ?? undefined, + structureMissilesilo: + this.userSettings.getSelectedStructureMissilesilo() ?? undefined, + structureDefensepost: + this.userSettings.getSelectedStructureDefensepost() ?? undefined, + structureSamlauncher: + this.userSettings.getSelectedStructureSamlauncher() ?? undefined, + + spriteTransportship: + this.userSettings.getSelectedSpriteTransportship() ?? undefined, + spriteWarship: + this.userSettings.getSelectedSpriteWarship() ?? undefined, + spriteSammissile: + this.userSettings.getSelectedSpriteSammissile() ?? undefined, + spriteAtombomb: + this.userSettings.getSelectedSpriteAtombomb() ?? undefined, + spriteHydrogenbomb: + this.userSettings.getSelectedSpriteHydrogenbomb() ?? undefined, + spriteTradeship: + this.userSettings.getSelectedSpriteTradeship() ?? undefined, + spriteMirv: this.userSettings.getSelectedSpriteMirv() ?? undefined, + spriteEngine: this.userSettings.getSelectedSpriteEngine() ?? undefined, + spriteCarriage: + this.userSettings.getSelectedSpriteCarriage() ?? undefined, + spriteLoadedcarriage: + this.userSettings.getSelectedSpriteLoadedcarriage() ?? undefined, playerName: this.usernameInput?.getCurrentUsername() ?? "", token: getPlayToken(), clientID: lobby.clientID, diff --git a/src/client/SinglePlayerModal.ts b/src/client/SinglePlayerModal.ts index 0b36b715a..48881c404 100644 --- a/src/client/SinglePlayerModal.ts +++ b/src/client/SinglePlayerModal.ts @@ -21,6 +21,7 @@ import "./components/baseComponents/Button"; import "./components/baseComponents/Modal"; import "./components/Difficulties"; import "./components/Maps"; +import { fetchUrl } from "./CosmeticPackLoader"; import { fetchCosmetics } from "./Cosmetics"; import { FlagInput } from "./FlagInput"; import { JoinLobbyEvent } from "./Main"; @@ -466,6 +467,84 @@ export class SinglePlayerModal extends LitElement { ? "" : flagInput.getCurrentFlag(), pattern: selectedPattern ?? undefined, + pack: { + structurePort: fetchUrl( + this.userSettings.getSelectedStructurePort() ?? undefined, + "structurePort", + ), + structureCity: fetchUrl( + this.userSettings.getSelectedStructureCity() ?? undefined, + "structureCity", + ), + structureFactory: fetchUrl( + this.userSettings.getSelectedStructureFactory() ?? + undefined, + "structureFactory", + ), + structureMissilesilo: fetchUrl( + this.userSettings.getSelectedStructureMissilesilo() ?? + undefined, + "structureMissilesilo", + ), + structureDefensepost: fetchUrl( + this.userSettings.getSelectedStructureDefensepost() ?? + undefined, + "structureDefensepost", + ), + structureSamlauncher: fetchUrl( + this.userSettings.getSelectedStructureSamlauncher() ?? + undefined, + "structureSamlauncher", + ), + + spriteTransportship: fetchUrl( + this.userSettings.getSelectedSpriteTransportship() ?? + undefined, + "spriteTransportship", + ), + spriteWarship: fetchUrl( + this.userSettings.getSelectedSpriteWarship() ?? undefined, + "spriteWarship", + ), + spriteSammissile: fetchUrl( + this.userSettings.getSelectedSpriteSammissile() ?? + undefined, + "spriteSammissile", + ), + spriteAtombomb: fetchUrl( + this.userSettings.getSelectedSpriteAtombomb() ?? + undefined, + "spriteAtombomb", + ), + spriteHydrogenbomb: fetchUrl( + this.userSettings.getSelectedSpriteHydrogenbomb() ?? + undefined, + "spriteHydrogenbomb", + ), + spriteTradeship: fetchUrl( + this.userSettings.getSelectedSpriteTradeship() ?? + undefined, + "spriteTradeship", + ), + spriteMirv: fetchUrl( + this.userSettings.getSelectedSpriteMirv() ?? undefined, + "spriteMirv", + ), + spriteEngine: fetchUrl( + this.userSettings.getSelectedSpriteEngine() ?? undefined, + "spriteEngine", + ), + spriteCarriage: fetchUrl( + this.userSettings.getSelectedSpriteCarriage() ?? + undefined, + "spriteCarriage", + ), + spriteLoadedcarriage: fetchUrl( + this.userSettings.getSelectedSpriteLoadedcarriage() ?? + undefined, + "spriteLoadedcarriage", + ), + }, }, }, ], diff --git a/src/client/Transport.ts b/src/client/Transport.ts index ee49f0c4c..c5247c85a 100644 --- a/src/client/Transport.ts +++ b/src/client/Transport.ts @@ -381,6 +381,24 @@ export class Transport { flag: this.lobbyConfig.flag, patternName: this.lobbyConfig.pattern?.name, patternColorPaletteName: this.lobbyConfig.pattern?.colorPalette?.name, + + structurePort: this.lobbyConfig.structurePort, + structureCity: this.lobbyConfig.structureCity, + structureFactory: this.lobbyConfig.structureFactory, + structureMissilesilo: this.lobbyConfig.structureMissilesilo, + structureDefensepost: this.lobbyConfig.structureDefensepost, + structureSamlauncher: this.lobbyConfig.structureSamlauncher, + + spriteTransportship: this.lobbyConfig.spriteTransportship, + spriteWarship: this.lobbyConfig.spriteWarship, + spriteSammissile: this.lobbyConfig.spriteSammissile, + spriteAtombomb: this.lobbyConfig.spriteAtombomb, + spriteHydrogenbomb: this.lobbyConfig.spriteHydrogenbomb, + spriteTradeship: this.lobbyConfig.spriteTradeship, + spriteMirv: this.lobbyConfig.spriteMirv, + spriteEngine: this.lobbyConfig.spriteEngine, + spriteCarriage: this.lobbyConfig.spriteCarriage, + spriteLoadedcarriage: this.lobbyConfig.spriteLoadedcarriage, }, } satisfies ClientJoinMessage); } diff --git a/src/client/graphics/SpriteLoader.ts b/src/client/graphics/SpriteLoader.ts index 29d5b7791..aa3b438e9 100644 --- a/src/client/graphics/SpriteLoader.ts +++ b/src/client/graphics/SpriteLoader.ts @@ -12,6 +12,7 @@ import warshipSprite from "../../../resources/sprites/warship.png"; import { Theme } from "../../core/configuration/Config"; import { TrainType, UnitType } from "../../core/game/Game"; import { UnitView } from "../../core/game/GameView"; +import { PlayerPack } from "../../core/Schemas"; // Can't reuse TrainType because "loaded" is not a type, just an attribute const TrainTypeSprite = { @@ -22,36 +23,55 @@ const TrainTypeSprite = { type TrainTypeSprite = (typeof TrainTypeSprite)[keyof typeof TrainTypeSprite]; -const SPRITE_CONFIG: Partial> = { - [UnitType.TransportShip]: transportShipSprite, - [UnitType.Warship]: warshipSprite, - [UnitType.SAMMissile]: samMissileSprite, - [UnitType.AtomBomb]: atomBombSprite, - [UnitType.HydrogenBomb]: hydrogenBombSprite, - [UnitType.TradeShip]: tradeShipSprite, - [UnitType.MIRV]: mirvSprite, - [TrainTypeSprite.Engine]: trainEngineSprite, - [TrainTypeSprite.Carriage]: trainCarriageSprite, - [TrainTypeSprite.LoadedCarriage]: trainLoadedCarriageSprite, +const SPRITE_CONFIG: Partial< + Record +> = { + [UnitType.TransportShip]: { + key: "spriteTransportship", + url: transportShipSprite, + }, + [UnitType.Warship]: { key: "spriteWarship", url: warshipSprite }, + [UnitType.SAMMissile]: { key: "spriteSammissile", url: samMissileSprite }, + [UnitType.AtomBomb]: { key: "spriteAtombomb", url: atomBombSprite }, + [UnitType.HydrogenBomb]: { + key: "spriteHydrogenbomb", + url: hydrogenBombSprite, + }, + [UnitType.TradeShip]: { key: "spriteTradeship", url: tradeShipSprite }, + [UnitType.MIRV]: { key: "spriteMirv", url: mirvSprite }, + [TrainTypeSprite.Engine]: { key: "spriteEngine", url: trainEngineSprite }, + [TrainTypeSprite.Carriage]: { + key: "spriteCarriage", + url: trainCarriageSprite, + }, + [TrainTypeSprite.LoadedCarriage]: { + key: "spriteLoadedcarriage", + url: trainLoadedCarriageSprite, + }, }; const spriteMap: Map = new Map(); // preload all images -export const loadAllSprites = async (): Promise => { +export const loadAllSprites = async (pack: PlayerPack): Promise => { const entries = Object.entries(SPRITE_CONFIG); const totalSprites = entries.length; let loadedCount = 0; await Promise.all( - entries.map(async ([unitType, url]) => { + entries.map(async ([unitType, value]) => { const typedUnitType = unitType as UnitType | TrainTypeSprite; - if (!url || url === "") { - console.warn(`No sprite URL for ${typedUnitType}, skipping...`); + const key = value?.key; + const fallbackUrl = value?.url; + + if (!fallbackUrl) { + console.warn(`No sprite url for ${typedUnitType}, skipping...`); return; } + const url = pack?.[key] ?? fallbackUrl; + try { const img = new Image(); img.crossOrigin = "anonymous"; diff --git a/src/client/graphics/layers/StructureLayer.ts b/src/client/graphics/layers/StructureLayer.ts index dbfaf5cb7..6a5430666 100644 --- a/src/client/graphics/layers/StructureLayer.ts +++ b/src/client/graphics/layers/StructureLayer.ts @@ -1,19 +1,19 @@ import { colord, Colord } from "colord"; -import { Theme } from "../../../core/configuration/Config"; -import { EventBus } from "../../../core/EventBus"; -import { TransformHandler } from "../TransformHandler"; -import { Layer } from "./Layer"; - import cityIcon from "../../../../resources/images/buildings/cityAlt1.png"; import factoryIcon from "../../../../resources/images/buildings/factoryAlt1.png"; import shieldIcon from "../../../../resources/images/buildings/fortAlt3.png"; import anchorIcon from "../../../../resources/images/buildings/port1.png"; import missileSiloIcon from "../../../../resources/images/buildings/silo1.png"; import SAMMissileIcon from "../../../../resources/images/buildings/silo4.png"; +import { Theme } from "../../../core/configuration/Config"; +import { EventBus } from "../../../core/EventBus"; import { Cell, UnitType } from "../../../core/game/Game"; import { euclDistFN, isometricDistFN } from "../../../core/game/GameMap"; import { GameUpdateType } from "../../../core/game/GameUpdates"; import { GameView, UnitView } from "../../../core/game/GameView"; +import { PlayerPack } from "../../../core/Schemas"; +import { TransformHandler } from "../TransformHandler"; +import { Layer } from "./Layer"; const underConstructionColor = colord({ r: 150, g: 150, b: 150 }); @@ -25,6 +25,7 @@ const ZOOM_THRESHOLD = 4.3; // below this zoom level, structures are not rendere interface UnitRenderConfig { icon: string; + key: string; borderRadius: number; territoryRadius: number; } @@ -34,38 +35,46 @@ export class StructureLayer implements Layer { private context: CanvasRenderingContext2D; private unitIcons: Map = new Map(); private theme: Theme; + private pack: PlayerPack; + private structureLoaded = false; private tempCanvas: HTMLCanvasElement; private tempContext: CanvasRenderingContext2D; // Configuration for supported unit types only - private readonly unitConfigs: Partial> = { + private unitConfigs: Partial> = { [UnitType.Port]: { icon: anchorIcon, + key: "structurePort", borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR, territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR, }, [UnitType.City]: { icon: cityIcon, + key: "structureCity", borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR, territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR, }, [UnitType.Factory]: { icon: factoryIcon, + key: "structureFactory", borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR, territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR, }, [UnitType.MissileSilo]: { icon: missileSiloIcon, + key: "structureMissilesilo", borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR, territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR, }, [UnitType.DefensePost]: { icon: shieldIcon, + key: "structureDefensepost", borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR, territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR, }, [UnitType.SAMLauncher]: { icon: SAMMissileIcon, + key: "structureSamlauncher", borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR, territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR, }, @@ -86,6 +95,7 @@ export class StructureLayer implements Layer { private loadIcon(unitType: string, config: UnitRenderConfig) { const image = new Image(); + console.log(`loading icon for ${unitType} from ${config.icon}`); image.src = config.icon; image.onload = () => { this.unitIcons.set(unitType, image); @@ -98,8 +108,9 @@ export class StructureLayer implements Layer { }; } - private loadIconData() { + private async loadIconData() { Object.entries(this.unitConfigs).forEach(([unitType, config]) => { + config.icon = this.pack?.[config.key] ?? config.icon; this.loadIcon(unitType, config); }); } @@ -116,6 +127,14 @@ export class StructureLayer implements Layer { if (unit === undefined) continue; this.handleUnitRendering(unit); } + if (!this.structureLoaded) { + const myPlayer = this.game.myPlayer(); + if (myPlayer) { + this.pack = myPlayer.cosmetics.pack ?? {}; + this.loadIconData(); + this.structureLoaded = true; + } + } } init() { diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 4945969be..90c7faacf 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -14,6 +14,7 @@ import { MoveWarshipIntentEvent } from "../../Transport"; import { TransformHandler } from "../TransformHandler"; import { Layer } from "./Layer"; +import { PlayerPack } from "../../../core/Schemas"; import { GameUpdateType } from "../../../core/game/GameUpdates"; import { getColoredSprite, @@ -36,6 +37,8 @@ export class UnitLayer implements Layer { private unitToTrail = new Map(); private theme: Theme; + private pack: PlayerPack; + private spritesLoaded = false; private alternateView = false; @@ -68,6 +71,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.pack = myPlayer.cosmetics.pack ?? {}; + loadAllSprites(this.pack); + this.spritesLoaded = true; + } + } } init() { @@ -75,8 +87,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.pack); } /** diff --git a/src/core/CosmeticSchemas.ts b/src/core/CosmeticSchemas.ts index a4bcd6762..0f575848b 100644 --- a/src/core/CosmeticSchemas.ts +++ b/src/core/CosmeticSchemas.ts @@ -94,3 +94,32 @@ export const DefaultPattern = { patternData: "AAAAAA", colorPalette: undefined, } satisfies PlayerPattern; + +const imageFile = z + .string() + .regex(/\.(png|webp|jpg|jpeg|gif)$/i, "Invalid image extension"); +const audioFile = z + .string() + .regex(/\.(ogg|mp3|wav|m4a)$/i, "Invalid audio extension"); + +const ImageAssetNode: z.ZodType = z.lazy(() => + z.union([imageFile, z.record(z.string(), ImageAssetNode)]), +); +const AudioAssetNode: z.ZodType = z.lazy(() => + z.union([audioFile, z.record(z.string(), AudioAssetNode)]), +); + +export const CosmeticManifestSchema = z + .object({ + id: z.string(), + name: z.string(), + assets: z + .object({ + structure: z.record(z.string(), ImageAssetNode).optional(), + sprites: z.record(z.string(), ImageAssetNode).optional(), + audio: z.record(z.string(), AudioAssetNode).optional(), + }) + .strict(), + }) + .strict(); +export type CosmeticManifest = z.infer; diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index 2e2fbc2ee..210322b6a 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -390,6 +390,24 @@ export const PlayerCosmeticRefsSchema = z.object({ flag: FlagSchema.optional(), patternName: PatternNameSchema.optional(), patternColorPaletteName: z.string().optional(), + + structurePort: z.string().optional(), + structureCity: z.string().optional(), + structureFactory: z.string().optional(), + structureMissilesilo: z.string().optional(), + structureDefensepost: z.string().optional(), + structureSamlauncher: z.string().optional(), + + spriteTransportship: z.string().optional(), + spriteWarship: z.string().optional(), + spriteSammissile: z.string().optional(), + spriteAtombomb: z.string().optional(), + spriteHydrogenbomb: z.string().optional(), + spriteTradeship: z.string().optional(), + spriteMirv: z.string().optional(), + spriteEngine: z.string().optional(), + spriteCarriage: z.string().optional(), + spriteLoadedcarriage: z.string().optional(), }); export const PlayerPatternSchema = z.object({ @@ -397,9 +415,32 @@ export const PlayerPatternSchema = z.object({ patternData: PatternDataSchema, colorPalette: ColorPaletteSchema.optional(), }); + +export const PlayerPackSchema = z.object({ + structurePort: z.string().optional(), + structureCity: z.string().optional(), + structureFactory: z.string().optional(), + structureMissilesilo: z.string().optional(), + structureDefensepost: z.string().optional(), + structureSamlauncher: z.string().optional(), + + spriteTransportship: z.string().optional(), + spriteWarship: z.string().optional(), + spriteSammissile: z.string().optional(), + spriteAtombomb: z.string().optional(), + spriteHydrogenbomb: z.string().optional(), + spriteTradeship: z.string().optional(), + spriteMirv: z.string().optional(), + spriteEngine: z.string().optional(), + spriteCarriage: z.string().optional(), + spriteLoadedcarriage: z.string().optional(), +}); +export type PlayerPack = z.infer; + export const PlayerCosmeticsSchema = z.object({ flag: FlagSchema.optional(), pattern: PlayerPatternSchema.optional(), + pack: PlayerPackSchema.optional(), }); export const PlayerSchema = z.object({ clientID: ID, diff --git a/src/core/game/UserSettings.ts b/src/core/game/UserSettings.ts index 9ff2ffd1f..1635e5c18 100644 --- a/src/core/game/UserSettings.ts +++ b/src/core/game/UserSettings.ts @@ -169,6 +169,70 @@ export class UserSettings { } } + getSelectedStructurePort(): string | undefined { + return localStorage.getItem("structurePort") ?? undefined; + } + + getSelectedStructureCity(): string | undefined { + return localStorage.getItem("structureCity") ?? undefined; + } + + getSelectedStructureFactory(): string | undefined { + return localStorage.getItem("structureFactory") ?? undefined; + } + + getSelectedStructureMissilesilo(): string | undefined { + return localStorage.getItem("structureMissilesilo") ?? undefined; + } + + getSelectedStructureDefensepost(): string | undefined { + return localStorage.getItem("structureDefensepost") ?? undefined; + } + + getSelectedStructureSamlauncher(): string | undefined { + return localStorage.getItem("structureSamlauncher") ?? undefined; + } + + getSelectedSpriteTransportship(): string | undefined { + return localStorage.getItem("spriteTransportship") ?? undefined; + } + + getSelectedSpriteWarship(): string | undefined { + return localStorage.getItem("spriteWarship") ?? undefined; + } + + getSelectedSpriteSammissile(): string | undefined { + return localStorage.getItem("spriteSammissile") ?? undefined; + } + + getSelectedSpriteAtombomb(): string | undefined { + return localStorage.getItem("spriteAtombomb") ?? undefined; + } + + getSelectedSpriteHydrogenbomb(): string | undefined { + return localStorage.getItem("spriteHydrogenbomb") ?? undefined; + } + + getSelectedSpriteTradeship(): string | undefined { + return localStorage.getItem("spriteTradeship") ?? undefined; + } + + getSelectedSpriteMirv(): string | undefined { + return localStorage.getItem("spriteMirv") ?? undefined; + } + + getSelectedSpriteEngine(): string | undefined { + return localStorage.getItem("spriteEngine") ?? undefined; + } + + getSelectedSpriteCarriage(): string | undefined { + return localStorage.getItem("spriteCarriage") ?? undefined; + } + + getSelectedSpriteLoadedcarriage(): string | undefined { + return localStorage.getItem("spriteLoadedcarriage") ?? undefined; + } + backgroundMusicVolume(): number { return this.getFloat("settings.backgroundMusicVolume", 0); } diff --git a/src/server/Worker.ts b/src/server/Worker.ts index e565f7ab7..a619119c9 100644 --- a/src/server/Worker.ts +++ b/src/server/Worker.ts @@ -7,6 +7,7 @@ import path from "path"; import { fileURLToPath } from "url"; import { WebSocket, WebSocketServer } from "ws"; import { z } from "zod"; +import { fetchUrl } from "../client/CosmeticPackLoader"; import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; import { GameType } from "../core/game/Game"; import { @@ -485,11 +486,56 @@ export async function startWorker() { } } + // Temporary full assignment (unless there’s a better approach). + const pack = { + structurePort: fetchUrl(cosmetics.structurePort, "structurePort"), + structureCity: fetchUrl(cosmetics.structureCity, "structureCity"), + structureFactory: fetchUrl( + cosmetics.structureFactory, + "structureFactory", + ), + structureMissilesilo: fetchUrl( + cosmetics.structureMissilesilo, + "structureMissilesilo", + ), + structureDefensepost: fetchUrl( + cosmetics.structureDefensepost, + "structureDefensepost", + ), + structureSamlauncher: fetchUrl( + cosmetics.structureSamlauncher, + "structureSamlauncher", + ), + spriteTransportship: fetchUrl( + cosmetics.spriteTransportship, + "spriteTransportship", + ), + spriteWarship: fetchUrl(cosmetics.spriteWarship, "spriteWarship"), + spriteSammissile: fetchUrl( + cosmetics.spriteSammissile, + "spriteSammissile", + ), + spriteAtombomb: fetchUrl(cosmetics.spriteAtombomb, "spriteAtombomb"), + spriteHydrogenbomb: fetchUrl( + cosmetics.spriteHydrogenbomb, + "spriteHydrogenbomb", + ), + spriteTradeship: fetchUrl(cosmetics.spriteTradeship, "spriteTradeship"), + spriteMirv: fetchUrl(cosmetics.spriteMirv, "spriteMirv"), + spriteEngine: fetchUrl(cosmetics.spriteEngine, "spriteEngine"), + spriteCarriage: fetchUrl(cosmetics.spriteCarriage, "spriteCarriage"), + spriteLoadedcarriage: fetchUrl( + cosmetics.spriteLoadedcarriage, + "spriteLoadedcarriage", + ), + }; + return { perm: "allowed", cosmetics: { flag: cosmetics.flag, pattern: pattern, + pack: pack, }, }; }