Demos/TarkovInventory/CrateTheme.cs
using Goo;
using Sandbox;
using Sandbox.UI;

namespace Sandbox.TarkovInventory;

// Industrial crate theme for the TarkovStash demo: matte-black metal panels, a worn
// safety-orange ink-stamp header, nine-slice skins with flat-color fallback. Demo-local
// because the safety-orange industrial look is specific here, not a Goo brand token.
internal static class CrateTheme
{
    public static readonly Color Steel    = Color.FromBytes( 0x0D, 0x0F, 0x14 );   // panel face / fallback
    public static readonly Color Stamp    = Color.FromBytes( 0xD4, 0x64, 0x1E );   // safety orange
    public static readonly Color StampInk = Color.FromBytes( 0x0A, 0x0A, 0x0C );   // text on the orange stamp
    public static readonly Color Edge     = new( 1f, 1f, 1f, 0.16f );              // fallback border
    public static readonly Color Socket   = Color.FromBytes( 0x07, 0x07, 0x09 );   // empty slot recess

    public const string Font = "Space Mono";
    public static readonly Length Tracking = 3f;
    public static readonly Length HeaderHeight = 40f;

    // Assets/-relative skin paths (loaded via FileSystem.Mounted, same as NineSliceUI).
    public const string PanelSkin  = "ui/crate-panel.png";
    public const string HeaderSkin = "ui/crate-header.png";
    public const string SlotSkin   = "ui/crate-slot.png";

    // Source-pixel slice insets, measured from the delivered art (1024 panel, 1774x887 header,
    // 1254 slot). Must contain each asset's corner bracket. Nudge in-editor at UAT.
    public static readonly Length PanelSlice   = 190f;
    public static readonly Length SlotSlice    = 300f;
    public static readonly Length HeaderSliceX = 440f;
    public static readonly Length HeaderSliceY = 120f;

    // On-screen border thickness (smaller than the inset scales the corner art down).
    public static readonly Length PanelBorder  = 24f;
    public static readonly Length SlotBorder   = 18f;
    // Header is a thin bar, so its borders are per-side: small left/right worn end-caps and a
    // sliver top/bottom, leaving the orange center to fill the bar (a uniform border would eat
    // the whole height and show only the dark frame).
    public static readonly Length HeaderCap  = 12f;
    public static readonly Length HeaderEdge = 3f;

    static Texture? _panel, _header, _slot;

    public static Texture? Panel  => Load( ref _panel,  PanelSkin );
    public static Texture? Header => Load( ref _header, HeaderSkin );
    public static Texture? Slot   => Load( ref _slot,   SlotSkin );

    // Lazy + self-healing: reload if the cached handle is null or went invalid across a hotload.
    // Returns null when the file is missing so callers fall back to flat colors.
    static Texture? Load( ref Texture? cache, string path )
    {
        if ( cache is not null && cache != Texture.Invalid ) return cache;
        var t = Texture.LoadFromFileSystem( path, FileSystem.Mounted );
        cache = (t is null || t == Texture.Invalid) ? null : t;
        return cache;
    }
}