fix(chargen): Campaign CC gate round 1 Batch B — authored selection states, label state, zoom/swatch feedback

GF-1/GF-8: UiButton now recognizes retail's custom Unselected/Selected
radio-pair (0x10000016/0x10000017), bypassing the standard Normal/
Highlight machine that never admitted those state names — .Selected now
lights the heritage/template/gender/Face-Clothes rows it was always a
no-op for.

AP-222/GF-11b: per-state label color/outline (dat 0x1B/0x21) now applies
off the REQUESTED retail state id, not the art-gated committed
ActiveState — resolves the Appearance spins' current-part highlight
(text recolors even though no Highlight art exists on either client) and
the Town caption's Normal-to-white swap.

GF-11c: UiButton.LabelBox lets a lifted caption with its own authored
rect draw there instead of the face-relative offset that's only correct
when the label is authored directly on the button (heritage/template
family, unchanged).

GF-9: wires the real nine companion overlay elements (SetColor's
SetVisible mechanism) that swatch clicks were always meant to drive,
retiring AP-215 item 1 (the swatch.Selected substitution was a permanent
no-op — swatches author no Highlight media at all).

GF-10: zoom buttons now set the retail-mirrored mutual-exclusive
Highlight/Normal pair on click; InitializePage carries no initial
SetState for either button, so both stay at "Normal" until first click.

Register: AP-222 retired (mechanism identified and ported), AP-215
narrowed (item 1 retired, item 2 unrelated and unchanged), row count
recount corrected 164 (was already one high before this batch).

App suite 5282/3 (was 5266/3), Runtime 1735/0 unchanged. Fixture + live-
DAT tests only — no graphical client launch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-16 11:37:09 +02:00
parent 1d9de5e095
commit 7d09821fdc
11 changed files with 1043 additions and 42 deletions

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,20 @@
# Campaign CC connected gate — round 1 findings (2026-08-16)
**MILESTONE (2026-08-16, post-Batch-A build `1.0.2-cc.g`): the user
completed the FIRST LIVE CHARACTER CREATE from acdream against local ACE —
launcher → character select → Create → six pages → name → Finish → real
character created. USER-CONFIRMED: "Yes i could now create a char." The
create flow's core path is live; the round continues for visual parity
(Batches B-D) and the remaining script checks (rejection dialogs,
log-straight-in confirmation, credit/randomize/exit warnings).**
**Batch B (selection state media + label state) is CODE-COMPLETE
2026-08-16, pending the user's visual gate.** GF-1, GF-8, GF-9, GF-10,
GF-11b, and GF-11c are fixed — see each entry's own FIXED note below.
Fixture + live-DAT tests only this round (no graphical client launch);
App suite 5282/3 (was 5266/3), Runtime 1735/0 unchanged. Register:
AP-222 RETIRED, AP-215 NARROWED (item 1 retired, item 2 stays open).
User ran the six-page chargen flow live (build `1.0.2-cc.e`, RDP session,
windowed). Screenshots: retail Heritage, acdream Heritage, retail
Profession. The user's side-by-side retail reports are AXIOMS
@ -9,12 +24,31 @@ ISSUES.md; this doc is the six-page batch.
## Functional (blocking or behavior-dead)
- **GF-1 Heritage selection dead/unmarked.** Clicking a heritage row does
not light its radio dot (retail: orange lit dot on the selected row —
screenshot 1). Unclear whether the click dispatches at all (the
description text that would confirm is itself broken, GF-2). ALSO: the
open-roll's own rolled heritage shows NO lit dot on entry — every dot
dark in the acdream screenshot.
- **GF-1 Heritage selection dead/unmarked — FIXED (Campaign CC gate round
1, Batch B).** Root cause: retail authors a custom radio-selection state
pair (`RetailUiStateIds.Unselected`/`Selected`, `0x10000016`/
`0x10000017`) on the heritage row (property-only state descriptors, no
media) with the actual art on a single stateful CHILD (the dot,
`0x100003C0`, media `0x06006E35`/`0x06006E21`, live-DAT-probe-confirmed).
`UiButton.AddAvailableStates` only recognized the standard Normal/
Highlight/Ghosted name space, so `_availableStates` never admitted the
custom pair and `.Selected` committed nothing (probe-verified before the
fix: `Selected=true` left `ActiveState=="Unselected"`, while the raw
`TrySetRetailState(0x10000017)` already worked). Fixed by teaching
`UiButton` to detect the authored pair (`HasStateMedia("Unselected") &&
HasStateMedia("Selected")`) at construction and bypass the standard
state machine for it — `.Selected` now routes directly to
`RetailUiStateIds.Selected`/`Unselected`, additive and gated on the
pair's presence, so every OTHER button's Normal/Highlight path is
byte-identical. The SAME fix also lights the Profession template icon
(`0x100003D9`), the Appearance Face/Clothes sub-tabs (GF-8, below), and
the gender buttons (whose media lives directly on the button, not a
child — the OTHER shape this fix covers). The open-roll's own
no-lit-dot-on-entry symptom shares this same root: `CharacterCreationHeritagePage.Refresh`
already sets `button.Selected = heritageId == snapshot.HeritageId` for
every row on every refresh (including the first one after open), so the
same `.Selected`-was-a-no-op bug silently ate the initial roll's own dot
too — this fix closes both halves of GF-1 with the same change.
- **GF-5 Skills page empty — FIXED (Campaign CC gate round
1, Batch A).** Root cause was `CharacterCreationSkillsPage.RebuildRows`
resolving `Templates[0]` (retail's own 3-child bucket-HEADER row,
@ -33,10 +67,25 @@ ISSUES.md; this doc is the six-page batch.
fully retired — the flat-list-vs-four-bucket half stays). The credits-
caption clobber (`SkillsPage.cs:81-82`, now different line numbers) is
UNCHANGED — Batch C's scope.
- **GF-9 Appearance color swatches do nothing observable.** Clicking a
color produces no visible change (model recolor absent). Could be a dead
dispatch or could be working-but-invisible (AP-216 authored-art swatches
+ a recolor that fails); investigate, don't guess.
- **GF-9 Appearance color swatches do nothing observable — FIXED (Campaign
CC gate round 1, Batch B).** Root cause confirmed as working-but-
invisible, not a dead dispatch: the `SelectColor`/`SetAppearanceIndex`
click path was already intact end to end (unchanged by this fix). The
swatch buttons themselves author ONLY an unnamed DirectState sprite —
live-DAT-probe-confirmed NO Normal/Highlight media at all — so the
existing `swatch.Selected = ...` highlight assignment in
`RefreshColorAndShadeControls` was a permanent no-op; nothing could ever
have shown a click's effect. Retail's REAL feedback mechanism is nine
separate companion overlay elements (`0x10000318`-`0x10000320`,
`CharacterCreationAppearancePage.SwatchOverlayIds`, live-DAT-confirmed
siblings of the swatches under the color-wheel container `0x100003B9`,
index-paired 1:1 with `SwatchIds`) that retail's `SetColor @0x0047DD50`
shows/hides via `m_tColorWheel[...][0x10][iCurColor*7]->SetVisible`
cross-confirmed against `gmCGAppearancePage::InitializePage`'s own
swatch/overlay id-pair table (`@0x004800ff-00480164`). Fixed by wiring
exactly one overlay visible per part, tracking the current part's
selected color index; retires AP-215's swatch-selection substitution
(item 1 — the icon-vs-ordinal item 2 stays open).
- **GF-11a Town description text does not change** when switching towns.
- **GF-13 Summary shows "-Non-admin or Non-envoy" below the name — FIXED
(Campaign CC gate round 1, Batch A) — this commit.** Root cause: dat
@ -113,18 +162,62 @@ ISSUES.md; this doc is the six-page batch.
per-attribute name labels (Strength…Self), Health/Stamina/Mana labels +
values. Sliders and template selection themselves WORK.
- **GF-6 Appearance spin captions are numbers,** not part names
("Hair Style", "Eyes", …). Known rows AP-215/AP-218 — the gate promotes
them to must-port.
("Hair Style", "Eyes", …). Known rows AP-215 (item 2 — item 1, the
swatch-selection substitution, RETIRED at Batch B/GF-9)/AP-218 — the
gate promotes them to must-port.
- **GF-7 Preview backdrop black** on Appearance (and Summary, GF-14);
retail's chargen 3D view shows a scenic backdrop. (The Heritage-page
preview area shows terrain in BOTH clients — establish from the decomp
what actually renders behind the model per page/view.)
- **GF-8 Appearance Face/Clothes sub-tab selection unmarked** (AP-222
family, promoted by the gate).
- **GF-10 Zoom buttons show identical art** whichever is pushed.
- **GF-11b Town selected marker does not turn white** (button highlights,
but retail's selected-town graphic swaps to white).
- **GF-11c Town names misaligned on the map** vs retail.
- **GF-8 Appearance Face/Clothes sub-tab selection unmarked — FIXED
(Campaign CC gate round 1, Batch B).** Same root and same fix as GF-1:
the Face (`0x100003A9`)/Clothes (`0x100003AA`) sub-tab buttons author
the identical custom Unselected/Selected radio-pair shape (media on a
stateful icon child, `0x100002E9`, live-DAT-probe-confirmed) — not the
AP-222 family as originally suspected (AP-222 turned out to be a
DIFFERENT mechanism, the per-state label color/outline gap fixed
alongside GF-11b below). `UiButton`'s custom-selection-pair bypass
fixes both in one change.
- **GF-10 Zoom buttons show identical art — FIXED (Campaign CC gate round
1, Batch B).** Pure wiring gap, not a widget mechanism problem — both
zoom buttons already author a standard Normal/Highlight(/rollover) pair
(live-DAT-probe-confirmed). `gmCGAppearancePage::ZoomIn @0x0047CF00`
(`@0x0047d005/0x0047d00f`) ends `ZoomInButton->SetState(6)` (Highlight),
`ZoomOutButton->SetState(1)` (Normal); `ZoomOut @0x0047D050` mirrors.
`CharacterCreationAppearancePage`'s click handlers only ever called
`PreviewControl.ZoomIn()/ZoomOut()`, never touching either button's
state — fixed to set the mutual-exclusive pair on every click. Re-
derived the INITIAL state from `InitializePage @0x0047fdd0-0048032e`:
`m_bZoomedIn = 0` is set at construction, but NO explicit initial
`SetState` call exists for either zoom button anywhere in
`InitializePage` — both start at their DAT-authored "Normal" default
until the first real zoom click; this port does not force an initial
Highlight either.
- **GF-11b Town selected marker does not turn white — FIXED (Campaign CC
gate round 1, Batch B).** The marker PIN art itself already swapped
correctly (the town button's own Normal/Highlight state machine was
never broken — its marker child, `0x1000040C`, authors real Highlight
media). What was missing: retail ALSO recolors the town NAME caption
(a lifted Type-12 child, id collides with the page-level description
panel's own id `0x10000409` in the installed dat — two distinct
elements in two distinct subtrees, harmless for the per-button lift)
from gold (218,167,85) to white (255,255,255) on selection, live-DAT-
measured. `DatWidgetFactory.BuildButton` lifted the caption's font
COLOR once at build time with no per-state override. Same root and fix
as AP-222 (below): per-state label color/outline, applied off the
REQUESTED retail state id.
- **GF-11c Town names misaligned on the map — FIXED (Campaign CC gate
round 1, Batch B).** The per-button caption's own authored rect
(`(0,4,100,37)`, Center-justified, live-DAT-measured) was being
discarded in favor of a Left-aligned offset computed from the marker
FACE's rect (`face.X + face.Width + 4`) — correct for the heritage/
template/Face-Clothes row family (label authored DIRECTLY on the
button, beside a single-purpose face segment) but wrong here, where a
DISTINCT Type-12 caption child was lifted with its own independent
geometry. Fixed by adding `UiButton.LabelBox`: when a distinct lifted
caption carries its own rect, the label draws within THAT box using
its own authored justify instead of the face-relative offset; every
other button (`LabelBox` null) keeps the EXACT prior draw math.
- **GF-12 Missing authored gold frames** around boxes on every page
(Skills/Appearance/Town/Summary called out explicitly).
- **GF-14 Summary paperdoll backdrop black** (same family as GF-7);
@ -138,9 +231,19 @@ ISSUES.md; this doc is the six-page batch.
decide per element from the authored DAT + decomp.
2. Rich text (escape decoding, wrap, scroll, frame) — one text-widget gap
feeding GF-2/GF-3/GF-11a/GF-14.
3. Selection state media (GF-1 dot, GF-8 sub-tabs, GF-11b white marker,
3. ~~Selection state media (GF-1 dot, GF-8 sub-tabs, GF-11b white marker,
GF-10 zoom art) — the AP-222 measured mechanism (state media authored
vs applied) across widget kinds.
vs applied) across widget kinds.~~ CLOSED, split into TWO distinct
mechanisms, both fixed at Batch B: (a) GF-1/GF-8 share a genuinely
UNRECOGNIZED custom state-name pair (`UiButton` never admitted
"Unselected"/"Selected" into its available-states set at all); GF-10
was pure wiring (the standard Normal/Highlight pair was never even
requested). (b) GF-11b turned out NOT to be a state-media gap — the
marker's own media swap already worked; the actual gap was AP-222's
real mechanism, per-state LABEL COLOR/OUTLINE (a property commit
distinct from the art/media commit, and NOT gated by the same art-
availability check `ActiveState` is). See each GF's own FIXED entry
above and the retired AP-222 / narrowed AP-215 register rows.
4. Preview backdrop (GF-7/GF-14) — what gmCG3DView clears/draws.
5. ~~Input routing on Summary (GF-15) — focus/typing path on the stacked
chargen screen.~~ CLOSED: focus/typing routing was never broken (live-