fix(ui): render live component counts

Port retail text-surface clipping so the 15px component count field remains visible under the 16px DAT font. Cover SetStackSize decrements and final component removal while preserving desired restock values.

Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-15 18:11:02 +02:00
parent a1175e6aac
commit 0527325b25
7 changed files with 217 additions and 36 deletions

View file

@ -336,6 +336,25 @@ public sealed class UiText : UiElement, IUiDatStateful
}
private void DrawText(UiRenderContext ctx)
{
// Retail UIElement_Text::DrawSelf @ 0x00467AA0 receives the text element's
// visible surface as arg3 and clips each glyph blit to that rectangle. This is
// observable in LayoutDesc 0x21000033: the owned component count is a 15px-high
// text element using a 16px DAT font, so rejecting a partially visible line makes
// the value disappear entirely. The shared render context clips both DAT and
// bitmap glyph quads and composes this bound with any list/window ancestor clip.
ctx.PushClip(0f, 0f, Width, Height);
try
{
DrawClippedText(ctx);
}
finally
{
ctx.PopClip();
}
}
private void DrawClippedText(UiRenderContext ctx)
{
// Static centered single-line mode (vitals cur/max numbers etc.): draw the first
// line centered H+V (or H+Top/Bottom per VerticalJustify) with the SAME formula
@ -451,7 +470,7 @@ public sealed class UiText : UiElement, IUiDatStateful
for (int i = 0; i < lines.Count; i++)
{
float y = baseY + i * lh;
if (y < top || y + lh > bottom) continue; // whole-line vertical clip (no scissor yet)
if (!LineIntersectsViewport(y, lh, top, bottom)) continue;
string text = lines[i].Text;
float lineX = HorizontalOffset(text, datFont, bitmapFont);
@ -489,6 +508,19 @@ public sealed class UiText : UiElement, IUiDatStateful
}
}
/// <summary>
/// True when any vertical portion of a line intersects a text viewport. Retail
/// clips the glyphs at the viewport edge; it does not require the full line box to fit.
/// </summary>
internal static bool LineIntersectsViewport(
float lineTop,
float lineHeight,
float viewportTop,
float viewportBottom)
=> lineHeight > 0f
&& lineTop < viewportBottom
&& lineTop + lineHeight > viewportTop;
private float HorizontalOffset(string text, UiDatFont? datFont, BitmapFont? bitmapFont)
{
float width = datFont is not null