fix(chargen): Campaign CC gate round 1 re-test 2 — R3-3 skills info-box VerticalJustify

The info-box title (0x100003fb, Y=435 H=100) and description (0x100003fc,
Y=460 H=100) panes' own authored boxes overlap by 75px, live-DAT-measured
— retail relies on vertical justification, not disjoint rects, to keep
them visually separate. Neither pane authors dat property 0x15, so both
fall to this port's shared unauthored-VJustify default (currently Center).

Byte-traced retail's real ctor default (UIElement_Text::UIElement_Text
@0x004685ff, m_eVerticalJustification = 4) against UIElement_Text::
CalcJustification @0x00467260's actual enum semantics (1=Center, 3-or-5=
the far edge/Bottom, anything else INCLUDING the ctor's own default of 4
= the near edge/Top): the correct unauthored default is Top, not Center —
a genuine client-wide enum-mapping bug in this port. Under Top both panes
render near their own box's top edge (25px apart, no collision); under
Center both cluster toward the middle of their overlapping boxes.

Scoped fix: CharacterCreationSkillsPage force-sets VerticalJustify=Top on
both panes directly, rather than fixing the shared mapping/default — that
bug is client-wide and could regress already-shipped FROZEN surfaces
(vitals, chat, main game UI, Options) that may rely on the current Center
default. The shared fix is filed as ISSUES #410 / register AD-104 for its
own dedicated investigation + regression sweep.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-16 17:20:08 +02:00
parent 7d6a7898f6
commit 7f6e93033f
4 changed files with 143 additions and 1 deletions

View file

@ -24,6 +24,87 @@ What does NOT go here:
- Every session: scan OPEN issues at start; promote/close anything we touched during the session before ending.
- Promoting to a Phase: mark as `DONE (promoted to Phase X)` + commit SHA where the Phase entry landed.
## #410 — Client-wide VJustify (vertical text justification) enum mapping + unauthored default are wrong (retail default is Top, not Center)
**Status:** OPEN
**Severity:** MEDIUM (silently mispositions every DAT-imported `UiText` that
relies on the unauthored default, or that authors a raw vertical-
justification value other than 1 — currently invisible unless two
elements' boxes are close/overlapping the way the Skills info-box panes
are, but could affect vertical alignment anywhere client-wide)
Found during Campaign CC gate round 1 re-test 2's R3-3 investigation
(`docs/research/2026-08-16-campaign-cc-gate-round1-findings.md`). The
Skills page's info-box title (`0x100003fb`) and description (`0x100003fc`)
panes author NO dat property `0x15` (vertical justification) — live-DAT-
probe-confirmed absent on both — so both fall to whatever this port's
unauthored default resolves to, currently `VJustify.Center`
(`ElementReader.cs`'s `VJustify` field default and
`ElementReader.cs`/`DatWidgetFactory.cs`'s import-time mapping switches).
Byte-traced against retail:
- `UIElement_Text::UIElement_Text` (ctor) `@0x004685ff`: unconditionally
sets `this->m_eVerticalJustification = 4` (and
`m_eHorizontalJustification = 2` at `@0x004685f5`) BEFORE any dat
property is applied — i.e. retail's real unauthored default is the raw
value **4**, not whatever a "sensible default" might suggest.
- `UIElement_Text::CalcJustification` `@0x00467260`: the ACTUAL enum
semantics, shared by both the horizontal and vertical branches via one
`ecx_5` comparison — `ecx_5 == 1`**Center**; `ecx_5 == 3 || ecx_5 == 5`
→ the FAR edge (**Right** for horizontal, **Bottom** for vertical); any
OTHER value (0, 2, 4, ...) → `edi = 0`, the NEAR edge (**Left** for
horizontal, **Top** for vertical).
Cross-referencing: the ctor's own vertical default of 4 resolves via this
real semantic table to **Top**, not Center. This port's
`ElementReader.cs:507`'s import-time switch (`2u=>Top, 4u=>Bottom,
_=>Center`) and `DatWidgetFactory.cs:704`'s build-time switch are BOTH
wrong relative to the real table — only raw value `2` (coincidentally
falling into the correct "near edge" bucket) and `1` (Center, matching the
`_=>Center` catch-all by coincidence) currently resolve correctly; `0`,
`3`, `4`, and `5` all resolve to the wrong bucket. The `ElementInfo.VJustify`
field default (`VJustify.Center`) is ALSO wrong — it should be `Top` to
match the ctor's real resolved value.
**Why this is filed instead of fixed here:** the blast radius is
client-wide — every DAT-imported `UiText` that reaches the
`Centered`/`RightAligned`/`OneLine` static paths or the multi-line
honored-justification path (`_honorDatVerticalJustification`, set
unconditionally by `ConfigureDatState` for every DAT-imported text
element) is affected, including already-shipped, visually-verified,
FROZEN surfaces (vitals numbers, chat, main game UI, Options panel) that
may be relying on the CURRENT (wrong) Center default for their existing
correct-looking vertical alignment. Flipping the shared default/mapping
without a full client-wide regression sweep risks reintroducing
regressions in surfaces this session has no budget to re-verify. R3-3's
own fix (`CharacterCreationSkillsPage`'s constructor) scopes the
correction to ONLY the two Skills info-box panes via an explicit
`VerticalJustify = VJustify.Top` post-construction assignment — a
targeted, decomp-grounded correction that does not touch the shared
mapping.
**Fix direction when this issue is picked up:** (1) correct
`ElementReader.cs`'s import-time switch AND `DatWidgetFactory.cs`'s
build-time switch to the real table above (`1=>Center, 3 or 5=>Bottom,
else=>Top`) for BOTH horizontal and vertical justification (audit the
horizontal switch too — it currently special-cases `0u or 2u=>Left`
instead of "everything except 1/3/5"; likely benign today since 2 is the
only unauthored horizontal default in practice, but should be corrected
for the same reason); (2) flip `ElementInfo.VJustify`'s field default to
`Top`; (3) fix `ElementReader.cs:435`'s `Merge` sentinel
(`derived.VJustify != VJustify.Center ? derived : base_`) to use the NEW
default (`Top`) as the "unset" sentinel instead, or restructure to a
nullable/explicit-override tracking shape so the merge doesn't rely on a
magic default value at all; (4) a full client-wide live-DAT sweep of every
Type-12/Button element that authors OR omits property `0x15`/`0x14`,
cross-checked against a fresh full visual pass of chat, main game UI,
Options, and every chargen page (this port's own `CharacterCreationSkillsPage`
override from R3-3 should be REMOVED once the shared default is corrected,
since it would then be redundant); (5) the exact same audit for the
horizontal `HJustify` mapping while in this code, since it shares the
`CalcJustification` function and the same class of latent bug.
## #409 — Client-wide UI tooltip system is unshipped (GF-16, deferred out of Campaign CC gate round 1)
**Status:** OPEN