fix(studio): Character window — bind the REAL Attributes-tab elements (no guessing)

Redo of the character pilot per faithful-port rules. The previous pilot GUESSED a text
report and put it on the wrong window. The decomp (verified by dumping acdream's
importer-resolved tree) shows LayoutDesc 0x2100002E is the tabbed Attributes/Skills/Titles
window: its tab-content slot 0x1000022B mounts sub-layout 0x2100002C (gmAttributeUI) which
chains into the gmStatManagementUI header. The importer ALREADY mounts every content element
(FindElement resolves name 0x10000231, heritage 0x10000232, PK 0x10000233, level 0x1000023B,
total-XP 0x10000235, XP meter 0x10000236 → UiMeter, list box 0x1000023D) — EventId was a red
herring (the dat id lives in _byId, not the widget's EventId field).

CharacterStatController (port of gmStatManagementUI::UpdateCharacterInfo 0x004f0770 +
UpdateExperience 0x004f0a70 + UpdatePKStatus 0x004f00a0) binds those real elements: name,
heritage, PK status, level, total XP, the XP-to-level meter fill, and the six innate
attributes in the list box. Studio (--layout 0x2100002E) now renders the actual panel
content, not an overlay. 16 controller tests green; full App suite stays green.

Refinements with known decomp sources (follow-ups): tab-button active/inactive state so the
3 tabs draw their sprites; exact label wording from the StringTable (table 0x10000001 →
dat 0x31000001); the full AttributeInfoRegion row template (column-aligned values + raise
buttons). CharacterController (text-report 0x2100001A) retained for that separate sub-panel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-25 19:47:33 +02:00
parent 33693c6412
commit 0e644b5887
5 changed files with 250 additions and 2 deletions

View file

@ -114,8 +114,11 @@ public static class FixtureProvider
break;
}
case 0x2100002Eu: // gmCharacterInfoUI — character report (LayoutDesc 0x2100002E)
CharacterController.Bind(
case 0x2100002Eu: // gmStatManagementUI — Attributes/Skills/Titles window (LayoutDesc 0x2100002E)
// Bind the REAL importer-mounted header + list elements (name/heritage/PK/level/
// total-XP/XP-meter + the attribute list). NOT the text-report sub-panel
// (that is gmCharacterInfoUI 0x2100001A → CharacterController).
CharacterStatController.Bind(
layout,
data: SampleData.SampleCharacter,
datFont: stack.VitalsDatFont);

View file

@ -133,6 +133,11 @@ public static class SampleData
PlayTime = "2 years, 114 days, 4 hours",
Deaths = 42,
PkStatus = "Non-Player Killer",
TotalXp = 1_250_000_000,
XpToNextLevel = 42_000_000,
XpFraction = 0.63f,
HealthCurrent = 80, HealthMax = 100,
StaminaCurrent = 60, StaminaMax = 100,
ManaCurrent = 90, ManaMax = 100,