fix(D.2b): vitals from the real stacked-window LayoutDesc (0x2100006C)
The vitals bars were rendered from the WRONG layout. The ids in vitals.xml (0x0600113x) belong to LayoutDesc 0x21000014 -- the 800x28 floaty side-vitals ROW. The stacked vitals window the user sees is LayoutDesc 0x2100006C (160x58), which uses a different sprite set and geometry. Dumped the real tree (new dump-vitals-layout CLI, reflective) and ported it: - Sprites (#2): the stacked-window set 0x0600747E-0x0600748F (health/stamina/ mana, each back+front 3-slice; caps 10px, mid 130px). - Right cap (#1) + fill model: retail UIElement_Meter::DrawChildren draws the back 3-slice full then the front 3-slice CLIPPED to the fill fraction (its own right-cap shows at 100%, the back's shows through when partial). UiMeter now clips the front per-slice (UV-crop) instead of growing a capless slice. - Spacing (#5): three flush 150x16 bars at y=5/21/37 in a 160x58 window (16px pitch, zero gap), per the dat rects -- not the old 20px-apart guess. - Border (#3): the window is the 8-piece chrome frame (corners 0x060074C3-C6, edges 0x060074BF-C2, 5px) -- dat-confirmed identical to RetailChromeSprites. The headless render-vitals-mockup now composites this exact window (0x2100006C) from the real sprites with the same clipped-fill model, so the look was verified before launch. Font (#4, dat Font 0x40000000) is the next commit. Decomp refs: gmVitalsUI::PostInit @0x4bfce0; UIElement_Meter::DrawChildren @0x46fbd0 (scissor-fill); geometry from LayoutDesc 0x2100006C. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ada863980c
commit
ff29787f12
5 changed files with 293 additions and 89 deletions
|
|
@ -66,11 +66,14 @@ public sealed class UiMeter : UiElement
|
|||
|
||||
if (SpriteResolve is { } resolve && (BackLeft != 0 || BackTile != 0 || FrontTile != 0))
|
||||
{
|
||||
// Empty track: full-width 3-slice (left-cap + stretched gradient + right-cap).
|
||||
DrawHBar(ctx, resolve, BackLeft, BackTile, BackRight, 0, 0, Width, Height, withRightCap: true);
|
||||
// Coloured fill: grows from the left to the value, no right-cap of its own.
|
||||
// Retail meter (UIElement_Meter::DrawChildren): the BACK 3-slice is the
|
||||
// empty track, drawn full width; the FRONT 3-slice is the coloured fill,
|
||||
// drawn at FULL width too but horizontally CLIPPED to the fill fraction.
|
||||
// The front carries its own right-cap (shown at 100%); clipping below 100%
|
||||
// removes it and reveals the back track's right-cap — retail's scissor-fill.
|
||||
DrawHBar(ctx, resolve, BackLeft, BackTile, BackRight, Width);
|
||||
if (pct is not null && p > 0f)
|
||||
DrawHBar(ctx, resolve, FrontLeft, FrontTile, FrontRight, 0, 0, Width * p, Height, withRightCap: false);
|
||||
DrawHBar(ctx, resolve, FrontLeft, FrontTile, FrontRight, Width * p);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -94,33 +97,43 @@ public sealed class UiMeter : UiElement
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a horizontal 3-slice into <paramref name="w"/> x <paramref name="h"/> at
|
||||
/// (<paramref name="x"/>,<paramref name="y"/>): a native-width left-cap, a stretched
|
||||
/// middle, and (when <paramref name="withRightCap"/>) a native-width right-cap. Caps
|
||||
/// are clamped so a narrow bar never overdraws. A 0 id skips that slice.
|
||||
/// Draws the full-width horizontal 3-slice (native-width left-cap, stretched
|
||||
/// middle, native-width right-cap) over this meter's rect, horizontally CLIPPED
|
||||
/// so nothing past <paramref name="clipW"/> (local px from the left) is drawn.
|
||||
/// The back track passes <c>clipW = Width</c>; the front fill passes
|
||||
/// <c>clipW = Width * fraction</c>. Clipping UV-crops each slice proportionally,
|
||||
/// so the fill ends cleanly and the back's right-cap shows through when partial.
|
||||
/// A 0 id skips that slice.
|
||||
/// </summary>
|
||||
private static void DrawHBar(
|
||||
private void DrawHBar(
|
||||
UiRenderContext ctx, Func<uint, (uint tex, int w, int h)> resolve,
|
||||
uint leftId, uint tileId, uint rightId,
|
||||
float x, float y, float w, float h, bool withRightCap)
|
||||
uint leftId, uint midId, uint rightId, float clipW)
|
||||
{
|
||||
if (w <= 0f) return;
|
||||
if (clipW <= 0f) return;
|
||||
float w = Width, h = Height;
|
||||
var (lt, lw, _) = resolve(leftId);
|
||||
var (tt, _, _) = resolve(tileId);
|
||||
var (mt, _, _) = resolve(midId);
|
||||
var (rt, rw, _) = resolve(rightId);
|
||||
|
||||
float rcap = withRightCap && rt != 0 ? MathF.Min(rw, w) : 0f;
|
||||
float lcap = lt != 0 ? MathF.Min(lw, w - rcap) : 0f;
|
||||
float capL = lt != 0 ? MathF.Min(lw, w) : 0f;
|
||||
float capR = rt != 0 ? MathF.Min(rw, w - capL) : 0f;
|
||||
float midW = w - capL - capR;
|
||||
|
||||
if (lt != 0 && lcap > 0f)
|
||||
ctx.DrawSprite(lt, x, y, lcap, h, 0, 0, 1, 1, Vector4.One);
|
||||
DrawPiece(ctx, lt, 0f, capL, h, clipW);
|
||||
DrawPiece(ctx, mt, capL, midW, h, clipW);
|
||||
DrawPiece(ctx, rt, w - capR, capR, h, clipW);
|
||||
}
|
||||
|
||||
float midX = x + lcap;
|
||||
float midW = w - lcap - rcap;
|
||||
if (tt != 0 && midW > 0f)
|
||||
ctx.DrawSprite(tt, midX, y, midW, h, 0, 0, 1, 1, Vector4.One);
|
||||
|
||||
if (rcap > 0f)
|
||||
ctx.DrawSprite(rt, x + w - rcap, y, rcap, h, 0, 0, 1, 1, Vector4.One);
|
||||
/// <summary>Draw one slice spanning local [<paramref name="pieceX"/>,
|
||||
/// pieceX+<paramref name="pieceW"/>], UV-cropped so nothing past
|
||||
/// <paramref name="clipW"/> shows.</summary>
|
||||
private static void DrawPiece(
|
||||
UiRenderContext ctx, uint tex, float pieceX, float pieceW, float h, float clipW)
|
||||
{
|
||||
if (tex == 0 || pieceW <= 0f) return;
|
||||
float visibleW = MathF.Min(pieceW, clipW - pieceX);
|
||||
if (visibleW <= 0f) return;
|
||||
float u1 = visibleW / pieceW; // crop the texture horizontally
|
||||
ctx.DrawSprite(tex, pieceX, 0f, visibleW, h, 0f, 0f, u1, 1f, Vector4.One);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue