Wires the dormant AcDream.App/UI retained-mode tree into GameWindow under ACDREAM_RETAIL_UI=1: an 8-piece dat-sprite UiNineSlicePanel framing three UiMeter vital bars bound to the existing VitalsVM. Render-only (UiHost input not yet bridged to the InputDispatcher — next sub-phase). Coexists with the ImGui devtools path; no regression there. Visually verified against a live retail client: the bars match retail's vitals structure (three stacked horizontal bars, current/max numbers centered) — so the earlier "orbs" assumption was wrong (retail vitals ARE bars), and stamina is GOLD not cyan (the #10F0F0 research note was wrong). UiMeter gains a centered numeric Label (stub debug font for now). Spec §8 + the markup example corrected to match. Bookkeeping: retired divergence row TS-30 (flat-rect panels -> real dat chrome) and added IA-15 (our UiHost/markup engine vs keystone.dll's LayoutDesc tree). Remaining polish (filed, §15): glassy gradient bar fill sprite + the retail dat font for the numbers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
59 lines
2.2 KiB
C#
59 lines
2.2 KiB
C#
using System.Numerics;
|
|
|
|
namespace AcDream.App.UI;
|
|
|
|
/// <summary>
|
|
/// A horizontal vital bar (retail HP/Stamina/Mana style): a background rect, a
|
|
/// partial-width solid fill, and an optional centered "current/max" numeric
|
|
/// overlay. <see cref="Fill"/> returns 0..1 (null = no data → empty bar);
|
|
/// <see cref="Label"/> returns the overlay text (null = no number).
|
|
///
|
|
/// <para>
|
|
/// Solid-color fill + debug font for Spec 1. The retail gradient bar sprite
|
|
/// (glassy center highlight) and the retail dat font are a later polish pass —
|
|
/// retail's vitals are bars exactly like this, just sprited.
|
|
/// </para>
|
|
/// </summary>
|
|
public sealed class UiMeter : UiElement
|
|
{
|
|
/// <summary>Fill fraction provider; a null result draws an empty bar.</summary>
|
|
public Func<float?> Fill { get; set; } = () => 0f;
|
|
/// <summary>Centered overlay text provider (e.g. "291/291"); null = none.</summary>
|
|
public Func<string?> Label { get; set; } = () => null;
|
|
public Vector4 BarColor { get; set; } = new(1f, 0f, 0f, 1f);
|
|
public Vector4 BgColor { get; set; } = new(0f, 0f, 0f, 0.5f);
|
|
public Vector4 LabelColor { get; set; } = new(1f, 1f, 1f, 1f);
|
|
|
|
public UiMeter() { ClickThrough = true; }
|
|
|
|
/// <summary>Clamp <paramref name="pct"/> to [0,1] and return the fill rect
|
|
/// (local px) for a bar of <paramref name="w"/> x <paramref name="h"/>.</summary>
|
|
public static (float x, float y, float w, float h) ComputeFillRect(
|
|
float pct, float w, float h)
|
|
{
|
|
if (pct < 0f) pct = 0f;
|
|
if (pct > 1f) pct = 1f;
|
|
return (0f, 0f, w * pct, h);
|
|
}
|
|
|
|
protected override void OnDraw(UiRenderContext ctx)
|
|
{
|
|
ctx.DrawRect(0, 0, Width, Height, BgColor);
|
|
|
|
float? pct = Fill();
|
|
if (pct is float p)
|
|
{
|
|
var (fx, fy, fw, fh) = ComputeFillRect(p, Width, Height);
|
|
if (fw > 0f) ctx.DrawRect(fx, fy, fw, fh, BarColor);
|
|
}
|
|
|
|
string? label = Label();
|
|
if (!string.IsNullOrEmpty(label) && ctx.DefaultFont is { } font)
|
|
{
|
|
float tw = font.MeasureWidth(label);
|
|
float tx = (Width - tw) * 0.5f;
|
|
float ty = (Height - font.LineHeight) * 0.5f;
|
|
ctx.DrawString(label, tx, ty, LabelColor);
|
|
}
|
|
}
|
|
}
|