PURPOSE

Loads and serves a tiny set of HUD icon sprites (gear, heart) used by in-game UI chrome. Not for weapon, upgrade, or entity artwork. Provides a synchronous accessor that returns null until preload resolves, so callers must fall back gracefully during the first few frames after boot.

OWNS

  • ICON_PATHS const map from UiIconId to public asset paths (/icons/gear-outline.png, /icons/heart-outline.png).
  • UiIconId type, derived as keyof typeof ICON_PATHS.
  • Private _cache partial record of decoded HTMLImageElement per icon id.

READS FROM

  • Browser Image constructor and the public /icons/ asset directory.
  • Image naturalWidth and naturalHeight after load, used to reject empty/broken decodes before caching.

PUSHES TO

  • The internal _cache, populated only when an image decodes with non-zero dimensions.
  • console.warn on load error, tagged [ui-icons] with id and path.

DOES NOT

  • Does not draw. Returns image handles only; rendering is the caller’s job.
  • Does not retry failed loads. A failed icon stays null for the session.
  • Does not throw on missing assets — resolves the preload promise regardless so boot never blocks on missing icons.
  • Does not cover gameplay sprites, weapon icons, upgrade thumbnails, or any artwork outside the gear/heart pair.

Signals

  • Successful preload: getUiIcon(id) transitions from null to an HTMLImageElement after preloadUiIcons resolves.
  • Failed preload: [ui-icons] <id>: failed to load <path> warning; that id remains null forever.

Entry points

  • getUiIcon(id: UiIconId): HTMLImageElement | null — synchronous HUD accessor.
  • preloadUiIcons(): Promise<void> — boot-time loader, resolves after all icons settle (success or error).

Pattern notes

  • Null-tolerant accessor pattern: synchronous getter returns null pre-load instead of throwing, matching the HUD’s per-frame render loop where missing chrome is acceptable on early frames.
  • Boundary defensiveness — image onerror is caught and logged as a warning rather than rejected, so a single missing icon cannot block boot or other icons.
  • Cache validation gate — only images with non-zero naturalWidth/naturalHeight are cached, filtering out broken or zero-byte responses that technically fired onload.
  • Closed icon set — adding a new HUD icon requires extending ICON_PATHS; UiIconId derives from it, so callers get a compile error if they pass an unknown id.