feat(D.2b): vital bars use retail dat sprites (back track + fill-cropped front)
UiMeter gains SpriteResolve/BackSpriteId/FrontSpriteId; when both are set, OnDraw draws the empty-track sprite full-width then the colored-fill sprite UV-cropped to the live fill fraction (left-to-right drain). Falls back to solid rects when sprite ids are absent, keeping existing behavior and tests intact. MarkupDocument.Build() parses `back`/`front` hex attrs on <meter> and passes `resolve` into every UiMeter. vitals.xml wires the authoritative LayoutDesc 0x21000014 sprites (Health 0x06005F3C/3D, Stamina 3E/3F, Mana 40/41). The bar prove-out block in GameWindow.cs was already gone. If the sprites decode as 1x1 magenta at runtime they are paletted (INDEX16/P8) — the solid-color fallback will display instead and can be investigated separately. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
56ee5eff60
commit
84630517e3
4 changed files with 68 additions and 16 deletions
|
|
@ -24,6 +24,14 @@ public sealed class UiMeter : UiElement
|
|||
public Vector4 BgColor { get; set; } = new(0f, 0f, 0f, 0.5f);
|
||||
public Vector4 LabelColor { get; set; } = new(1f, 1f, 1f, 1f);
|
||||
|
||||
/// <summary>Resolver from a RenderSurface DataId to (GL handle, w, h). When set
|
||||
/// with Back/Front sprite ids, the bar draws the retail sprites instead of solid color.</summary>
|
||||
public Func<uint, (uint tex, int w, int h)>? SpriteResolve { get; set; }
|
||||
/// <summary>Empty-track sprite (drawn full width). 0 = none.</summary>
|
||||
public uint BackSpriteId { get; set; }
|
||||
/// <summary>Colored-fill sprite (drawn cropped to the fill fraction). 0 = none.</summary>
|
||||
public uint FrontSpriteId { get; set; }
|
||||
|
||||
public UiMeter() { ClickThrough = true; }
|
||||
|
||||
/// <summary>Clamp <paramref name="pct"/> to [0,1] and return the fill rect
|
||||
|
|
@ -38,13 +46,32 @@ public sealed class UiMeter : UiElement
|
|||
|
||||
protected override void OnDraw(UiRenderContext ctx)
|
||||
{
|
||||
ctx.DrawRect(0, 0, Width, Height, BgColor);
|
||||
|
||||
float? pct = Fill();
|
||||
if (pct is float p)
|
||||
float p = pct is float pf ? (pf < 0f ? 0f : pf > 1f ? 1f : pf) : 0f;
|
||||
|
||||
if (SpriteResolve is { } resolve && (BackSpriteId != 0 || FrontSpriteId != 0))
|
||||
{
|
||||
var (fx, fy, fw, fh) = ComputeFillRect(p, Width, Height);
|
||||
if (fw > 0f) ctx.DrawRect(fx, fy, fw, fh, BarColor);
|
||||
// Retail bar: empty track full width, colored fill cropped to p (left→right).
|
||||
if (BackSpriteId != 0)
|
||||
{
|
||||
var (bt, _, _) = resolve(BackSpriteId);
|
||||
if (bt != 0) ctx.DrawSprite(bt, 0, 0, Width, Height, 0, 0, 1, 1, Vector4.One);
|
||||
}
|
||||
if (FrontSpriteId != 0 && pct is not null && p > 0f)
|
||||
{
|
||||
var (ft, _, _) = resolve(FrontSpriteId);
|
||||
if (ft != 0) ctx.DrawSprite(ft, 0, 0, Width * p, Height, 0, 0, p, 1, Vector4.One);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Placeholder solid-color fallback.
|
||||
ctx.DrawRect(0, 0, Width, Height, BgColor);
|
||||
if (pct is not null && p > 0f)
|
||||
{
|
||||
var (fx, fy, fw, fh) = ComputeFillRect(p, Width, Height);
|
||||
if (fw > 0f) ctx.DrawRect(fx, fy, fw, fh, BarColor);
|
||||
}
|
||||
}
|
||||
|
||||
string? label = Label();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue