fix(chargen): Campaign CC gate round 1 Batch C — rich text + labels + backdrops
Commit 1/3: chargen-scoped, low blast-radius fixes. - New DatRichText helper: escape-normalize + word-wrap + per-segment palette color, porting UIElement_Text::SetStringInfoWithFont / AppendStringInfoWithFont's composition model. Routes the Heritage (GF-2), Town (GF-11a), and Profession (GF-3) description boxes through it instead of a raw unwrapped single-Line LinesProvider. Heritage headers use font-color palette index 1 (green), bodies index 0 (white), matching AppendStringInfoWithFont's own font-index argument. Town's diagnosed GF-11a root cause: a single un-wrapped line meant the town-specific suffix rendered past the clipped viewport, so switching towns looked like "text never changes" even though the underlying composed string genuinely differed. - GF-3: bind the Profession page's description textbox (0x100003e0, gmCGProfessionPage::InitializePage @0x00483068) and compose its per-template text (UpdateProfession @0x004821b0's CustomText/ BowText/SwashText/LifeText/WarText/WayText/SoldierText, plain SetStringInfo — no palette). - GF-4: UiButton gains a coexisting ValueLabel/ValueBox/ValueFont/ ValueColor slot alongside Label. Retail's chargen display buttons (avail/health/stamina/mana credits, 0x100003e2-e5/0x100003f9) author their caption directly on P0x17 AND carry a separate, media-less Type-12 value child that UiButton.ConsumesDatChildren used to drop entirely — pages substituted the button's own Label, destroying the caption. DatWidgetFactory.BuildButton now surfaces that child (gated on ReferenceEquals(labelInfo, info) — own-caption buttons only) instead. The six Profession slider name labels (0x100002ed, CharGenState::GetAttributeName @0x005C3A20's six hardcoded literals) resolve as UiButton in this port (live-DAT- measured Type 1 — retail's UIElement_Button is DynamicCast(0xc)- compatible with UIElement_Text) and are written once at construction, matching retail's own single InitializePage write. - GF-6/AP-218: gmCGAppearancePage::Update writes a heritage-flavored STATIC caption to the Hair/Eyes/Skin spins (plain / GearText_* / OlthoiText_* variants) — never an index. Removed the prior 1-based- ordinal/gear-name substitution entirely; the other six spins keep their DAT-authored caption untouched, matching retail exactly. - Root 1d: wire the Heritage (0x100003be, 13 states) and Profession (0x100003d8, 7 states) backdrop SetState cascades (gmCGHeritagePage::Update / gmCGProfessionPage::UpdateProfession). - AP-216/AP-217 (partial, register updated honestly): swatches beyond the current part's real color count now hide (DoColorSpots' blank- blit half); the GradCircle now blanks for Eyes (DoGradDisk's blank- plug half). The "paint with the actual represented/current color" halves stay open — they need a PalSet/Palette-id -> RGB pipeline no chargen page reads at runtime yet, judged disproportionate to add alongside this batch's other ~10 fixes. Register: AP-215 rewritten (item 2's "ordinal" framing is stale after GF-6; restated as the icon-thumbnail gap), AP-216/AP-217 rewritten (partially closed), AP-218 retired, AD-103 retired (the swallowed- child Label substitution AD-103 tracked is replaced by ValueLabel's own-geometry surfacing). 22 new tests (DatRichText unit tests, UiButton/DatWidgetFactory ValueLabel tests, live-DAT structural pins, controller behavioral tests) — all green. Full App suite (Release, live-DAT): 5300 passed / 1 pre-existing unrelated flake (PortalProjectionTests allocation test, passes in isolation) / 3 skipped, up from the baseline 5282/3. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
7d09821fdc
commit
0591b9a026
13 changed files with 1018 additions and 66 deletions
|
|
@ -179,6 +179,43 @@ public sealed class UiButton : UiElement, IUiGlobalTimeListener, IUiDatStateful
|
|||
/// </summary>
|
||||
public (float X, float Y, float Width, float Height)? LabelBox { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// GF-4a (Campaign CC gate round 1 Batch C): optional secondary VALUE
|
||||
/// text, coexisting with <see cref="Label"/> (the authored CAPTION).
|
||||
/// Retail's chargen display buttons (Attribute/Skill Credits, Health,
|
||||
/// Stamina, Mana — <c>0x100003e2-e5</c>, <c>0x100003f9</c>) author the
|
||||
/// caption directly as this element's own dat property <c>0x17</c>
|
||||
/// AND carry a SEPARATE, media-less Type-12 child for the live value
|
||||
/// (<c>gmCGProfessionPage::InitializePage @0x00482f90-0x00483062</c>,
|
||||
/// <c>gmCGSkillsPage::InitializePage @0x00481e1c</c>) —
|
||||
/// <see cref="UiButton"/> consumes ALL of its dat children
|
||||
/// (<see cref="ConsumesDatChildren"/>), which used to mean a page
|
||||
/// controller had nowhere faithful to put the value except
|
||||
/// overwriting <see cref="Label"/> itself, destroying the caption.
|
||||
/// <see cref="Layout.DatWidgetFactory.BuildButton"/> now surfaces that
|
||||
/// child's geometry/font/color here instead. Null (default) draws
|
||||
/// nothing extra — every pre-existing button that only ever wrote
|
||||
/// <see cref="Label"/> is unaffected.
|
||||
/// </summary>
|
||||
public string? ValueLabel { get; set; }
|
||||
|
||||
/// <summary>Dat font for <see cref="ValueLabel"/>.</summary>
|
||||
public UiDatFont? ValueFont { get; set; }
|
||||
|
||||
/// <summary>Color for <see cref="ValueLabel"/> (default white).</summary>
|
||||
public Vector4 ValueColor { get; set; } = Vector4.One;
|
||||
|
||||
/// <summary>Authored rectangle for <see cref="ValueLabel"/>, LOCAL to
|
||||
/// this button — the lifted value child's own rect
|
||||
/// (<see cref="Layout.DatWidgetFactory.BuildButton"/> sets this). Null
|
||||
/// (no value child found) means <see cref="ValueLabel"/> is never set
|
||||
/// either, so this is never read in that case.</summary>
|
||||
public (float X, float Y, float Width, float Height)? ValueBox { get; set; }
|
||||
|
||||
/// <summary>Horizontal alignment of <see cref="ValueLabel"/> within
|
||||
/// <see cref="ValueBox"/> — the lifted child's own authored justify.</summary>
|
||||
public LabelAlignment ValueAlign { get; set; } = LabelAlignment.Center;
|
||||
|
||||
/// <summary>Label horizontal alignment options.</summary>
|
||||
public enum LabelAlignment { Center, Left }
|
||||
|
||||
|
|
@ -453,6 +490,19 @@ public sealed class UiButton : UiElement, IUiGlobalTimeListener, IUiDatStateful
|
|||
ctx.DrawStringDat(lf, label, tx, ty, LabelColor, Outline, OutlineColor);
|
||||
}
|
||||
|
||||
if (ValueLabel is { Length: > 0 } value && ValueFont is { } vf)
|
||||
{
|
||||
float boxX = ValueBox?.X ?? 0f;
|
||||
float boxY = ValueBox?.Y ?? 0f;
|
||||
float boxWidth = ValueBox?.Width ?? Width;
|
||||
float boxHeight = ValueBox?.Height ?? Height;
|
||||
float vx = ValueAlign == LabelAlignment.Left
|
||||
? boxX + LabelOffsetX
|
||||
: boxX + (boxWidth - vf.MeasureWidth(value)) * 0.5f;
|
||||
float vy = boxY + (boxHeight - vf.LineHeight) * 0.5f;
|
||||
ctx.DrawStringDat(vf, value, vx, vy, ValueColor, Outline, OutlineColor);
|
||||
}
|
||||
|
||||
uint dragSprite = _itemDragAcceptance switch
|
||||
{
|
||||
ItemDragAcceptance.Accept => ItemDragAcceptSprite,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue