Files
Project-M/Assets/_Project/Scripts/Client/UI/HudTheme.cs
T
kronic 62e48a3b0b LANTERN purge: delete the superseded base/expedition shell (audit H1/H3/M5)
The 2026-08-06 audit found the shipping scene was still the abandoned
co-op-Hades game with LANTERN combat bolted on, and that a third of the
codebase was live code for a direction abandoned on 2026-07-13. Operator
chose deletion over freezing: "everything is saved in source control if
needed. I want the project to be clean."

DELETED (~140 source files, Scripts 335->231, Tests 77->43):
- Enemy variants + boss (H3). ChargerAuthoring / SpitterAuthoring /
  SwarmerAuthoring were attached to ZERO prefabs, so LungeState /
  SpitterState / SwarmerTag were never baked: ~272 lines of Bursted AI
  passes, BossAISystem (261 lines) and the whole MixBands escalation
  curve could not match a single chunk at runtime, while 734 lines of
  green tests certified them. Both shipping enemy prefabs were already
  byte-identical in stats.
- Run/room lifecycle: RunDirector FSM, RunInfo/RunMap/RoomPlan/RoomTag,
  route select, portal interact, ready-check, room field/teardown.
- Meta shop, prep loadout, boons (incl. KillRewardSystem and
  DashTrailDamageSystem, which existed only to serve boon flags).
- Build palette + structures, shared storage, inventory/equipment
  (already recorded PAUSED in CLAUDE.md).
- The HUD panels driving all of the above (HudSystem 1168 -> 610).

KEPT deliberately: BaseGridMath + BaseAnchor (8 systems use PlotCenter
for spawn rings, respawn and dynamic light), the resource ledger +
StorageMath, the save system, region/relevancy. Three of these were in
the delete set until I checked their consumers — worth remembering that
the file-level manifest was wrong about them.

Also folds in audit finding M5: PlayerClass was a second, server-only
copy of the byte FrameId already replicates. It existed for the meta
shop; with that gone, FrameId is the single frame identity.

Harvest is now single-sink (ledger). HarvestMath keeps its shape so
LANTERN's carried-vs-banked cargo split lands in one place, not two.

295/295 EditMode green, zero compile errors. Subscene re-bake and Play
validation follow in the next commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 12:59:39 -07:00

119 lines
5.7 KiB
C#

using ProjectM.Simulation;
using UnityEngine;
using UnityEngine.TextCore.Text;
using UnityEngine.UIElements;
namespace ProjectM.Client
{
/// <summary>
/// Curated Synty sci-fi-soldier HUD skin — serialized <see cref="Sprite"/> + <see cref="Font"/> references
/// harvested from the InterfaceSciFiSoldierHUD / InterfaceCore packs and authored into
/// <c>Assets/_Project/Resources/HudTheme.asset</c>. Those source sprites/fonts live under
/// <c>Assets/Synty/…</c> (NOT a Resources folder), so a serialized asset like this is the build-safe way to
/// pull a curated subset into the player build (the dependency walker follows the refs). Loaded null-safe via
/// <see cref="Get"/> (mirrors <see cref="MenuUi.LoadPanelSettings"/>); EVERY consumer must null-check the theme
/// AND each field and fall back to the flat-color HUD, so a missing asset/ref never breaks (or magenta-s) the
/// HUD. Fonts are applied as cached SDF font definitions: a <see cref="FontAsset"/> is built once per font from
/// the serialized <see cref="Font"/> (crisp at any size, supports outlines) and re-built per play session (the
/// static cache is reset on play-enter, like the project's other static presentation bridges).
/// </summary>
[CreateAssetMenu(menuName = "ProjectM/HUD Theme", fileName = "HudTheme")]
public class HudTheme : ScriptableObject
{
[Header("Fonts")]
public Font DisplayFont; // Orbitron-ExtraBold — numerics + phase words (the HUD's authoritative voice)
public Font BodyFont; // Exo 2.0 SemiBold — labels
public Font BodyLightFont; // Exo 2.0 Regular — captions / hints
[Header("Panels & meters")]
public Sprite PanelBox; // 9-sliced card background (tinted per cluster)
public Sprite BarTrack; // bar fill skin
public Sprite Vignette; // radial edge gradient (low-HP / hurt flash / downed)
public Sprite Glow; // soft glow accent (selection / pulse)
public Sprite PipActive; // filled hex pip (goal meter)
public Sprite PipInactive; // empty hex pip
[Header("Status / info icons")]
public Sprite HealthIcon;
public Sprite ShieldIcon;
public Sprite ThreatIcon;
public Sprite GoalIcon;
public Sprite CooldownIcon;
public Sprite LocationBaseIcon;
public Sprite LocationExpeditionIcon;
[Header("Resource icons")]
public Sprite AetherIcon;
public Sprite OreIcon;
public Sprite BioIcon;
[Header("Structure icons")]
public Sprite WallIcon;
public Sprite PylonIcon;
[Header("Build-ghost preview meshes (authored real-size, ground pivot — BuildSendSystem)")]
public Mesh WallGhostMesh;
[Header("Build-mode control glyphs")]
public Sprite KbmPlace; // LMB
public Sprite KbmCancel; // RMB
public Sprite PadPlace; // gamepad A / south
public Sprite PadCancel; // gamepad B / east
public Sprite PadRotate; // gamepad LB
public Sprite PadExit; // gamepad Menu / start
// ---- null-safe load + cache (mirrors MenuUi.LoadPanelSettings Resources idiom) ----
static HudTheme _cached;
static bool _tried;
/// <summary>The loaded theme, or null if the asset is missing. Callers MUST null-check this and each field.</summary>
public static HudTheme Get()
{
if (_tried) return _cached;
_tried = true;
_cached = Resources.Load<HudTheme>("HudTheme");
return _cached;
}
/// <summary>Icon for a <see cref="StructureType"/> byte (null → caller falls back to the structure name text).</summary>
/// <summary>Placement-ghost preview mesh for a <see cref="StructureType"/> byte (null → the cube fallback).</summary>
// ---- cached SDF font definitions (one FontAsset per font, built once, reset per play session) ----
static FontAsset _displayFa, _bodyFa, _bodyLightFa;
static bool _displayTried, _bodyTried, _bodyLightTried;
/// <summary>Apply the display font (Orbitron) to a style, if available. No-op otherwise (stock font).</summary>
public void ApplyDisplay(IStyle style) => Apply(style, DisplayFont, ref _displayFa, ref _displayTried);
/// <summary>Apply the body font (Exo 2.0 SemiBold) to a style, if available.</summary>
public void ApplyBody(IStyle style) => Apply(style, BodyFont, ref _bodyFa, ref _bodyTried);
/// <summary>Apply the light body font (Exo 2.0 Regular) to a style, if available.</summary>
public void ApplyBodyLight(IStyle style) => Apply(style, BodyLightFont, ref _bodyLightFa, ref _bodyLightTried);
static void Apply(IStyle style, Font font, ref FontAsset fa, ref bool tried)
{
if (!tried)
{
tried = true;
if (font != null) fa = FontAsset.CreateFontAsset(font); // dynamic SDF atlas, built once per session
}
if (fa != null)
style.unityFontDefinition = new StyleFontDefinition(FontDefinition.FromSDFFont(fa));
}
// Fast-enter-playmode keeps statics alive across sessions; the runtime FontAssets are destroyed on play-exit,
// so a stale cache would reference a destroyed atlas. Reset everything so it re-loads/re-builds per session.
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void ResetStatics()
{
_cached = null; _tried = false;
_displayFa = _bodyFa = _bodyLightFa = null;
_displayTried = _bodyTried = _bodyLightTried = false;
}
}
}