deadhead-space-freight-tycoon/apps/web/src/map/art/load.ts

62 lines
2.1 KiB
TypeScript

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<string, string> {
const density = devicePixelRatio >= 2 ? "2x" : "1x";
const bundle: Record<string, string> = {};
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<T> {
addBundle(name: string, assets: Record<string, string>): void;
load(alias: string): Promise<T>;
}
/** Loaded textures by asset id. An id that is absent draws its placeholder (0.5.41). */
export type ArtTextures<T> = ReadonlyMap<string, T>;
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<T>(
loader: AssetLoader<T>,
devicePixelRatio: number,
warn: (message: string) => void = console.warn,
): Promise<ArtTextures<T>> {
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<string, T>();
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;
}