import { artManifest } from "./manifest"; /** Where `scripts/map-art.mjs` output is served from (`public/assets/map-gen`). */ export const ART_BASE_URL = "/assets/map-gen"; export const ART_BUNDLE = "map-art"; /** The generated files to load: 2x sprites on high-density screens (Step 10C.4). */ export function artBundle(devicePixelRatio: number): Record { const density = devicePixelRatio >= 2 ? "2x" : "1x"; const bundle: Record = {}; for (const [id, asset] of Object.entries(artManifest.assets)) { if (asset.file === null) continue; bundle[id] = asset.kind === "background" ? `${ART_BASE_URL}/${id}.webp` : `${ART_BASE_URL}/${id}@${density}.png`; } return bundle; } /** The part of PixiJS's `Assets` the loader uses (a fake in tests). */ export interface AssetLoader { addBundle(name: string, assets: Record): void; load(alias: string): Promise; } /** Loaded textures by asset id. An id that is absent draws its placeholder (0.5.41). */ export type ArtTextures = ReadonlyMap; let registered = false; /** * Registers the map art as one bundle and loads each asset on its own, so one * failure only costs that asset: it is left out (placeholder) and one warning * names it. Never rejects. */ export async function loadArt( loader: AssetLoader, devicePixelRatio: number, warn: (message: string) => void = console.warn, ): Promise> { const bundle = artBundle(devicePixelRatio); if (!registered) { loader.addBundle(ART_BUNDLE, bundle); registered = true; } const ids = Object.keys(bundle); const results = await Promise.allSettled(ids.map((id) => loader.load(id))); const textures = new Map(); results.forEach((result, i) => { const id = ids[i]!; if (result.status === "fulfilled" && result.value) textures.set(id, result.value); else warn(`Map art "${id}" failed to load; drawing its placeholder.`); }); return textures; } /** Test hook: forget that the bundle was registered. */ export function resetArtRegistration(): void { registered = false; }