docs(studio): faithful character-window element spec (decomp-grounded)
Element map for LayoutDesc 0x2100002E (the tabbed Attributes/Skills/Titles window) +
the importer-mount reality + the StringTable verdict + the follow-up list. Grounds the
CharacterStatController work in 0e644b5 and the remaining refinements.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0e644b5887
commit
0d87e4dc31
1 changed files with 82 additions and 0 deletions
82
docs/research/2026-06-25-character-window-faithful-spec.md
Normal file
82
docs/research/2026-06-25-character-window-faithful-spec.md
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
# Character window — faithful element spec (LayoutDesc 0x2100002E)
|
||||||
|
|
||||||
|
Decomp-grounded blueprint for the Character window, produced to REDO the guessed pilot.
|
||||||
|
Sources: `docs/research/named-retail/acclient_2013_pseudo_c.txt` (PDB-named decomp),
|
||||||
|
the user UI dump `docs/research/2026-06-25-retail-ui-layout-dump.json`, and acdream's own
|
||||||
|
importer-resolved tree (dumped live in the studio). **No guessing** — every row cites a
|
||||||
|
decomp address or a confirmed `FindElement` result.
|
||||||
|
|
||||||
|
## What 0x2100002E actually is
|
||||||
|
|
||||||
|
A **tabbed Attributes / Skills / Titles window** (300×600, root panel `0x10000227`, Type 8).
|
||||||
|
It is NOT a text report. Three tab-content slots each mount a sub-layout via
|
||||||
|
`BaseElement`+`BaseLayoutId` inheritance (acdream's `ShouldMountBaseChildren` path):
|
||||||
|
|
||||||
|
| Slot | BaseElement | BaseLayoutId | gm*UI | Register |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `0x1000022B` (Attributes) | `0x10000225` | `0x2100002C` | gmAttributeUI (root type 0x1000002A) | 0x0049db50 |
|
||||||
|
| `0x1000022C` (Skills) | `0x1000022E` | `0x2100002D` | gmSkillUI | 0x0049adb0 |
|
||||||
|
| `0x10000539` (Titles) | `0x1000052D` | `0x2100005E` | gmCharacterTitleUI | 0x0049aba0 |
|
||||||
|
|
||||||
|
Both Attributes + Skills tabs share a header strip filled by the base class
|
||||||
|
**`gmStatManagementUI`** (`UpdateCharacterInfo` 0x004f0770, `UpdateExperience` 0x004f0a70,
|
||||||
|
`UpdatePKStatus` 0x004f00a0). The tab sub-layout `0x2100002C` nests `0x10000226` which
|
||||||
|
chains again into `0x21000045` — the real header/list content is **two inheritance levels deep**.
|
||||||
|
|
||||||
|
The **text report** ("birth/age/deaths, innate attributes, augmentations, load") the earlier
|
||||||
|
pilot guessed is a SEPARATE sub-panel: LayoutDesc `0x2100001A`, `gmCharacterInfoUI`
|
||||||
|
(register 0x004b8c90), whose `m_pMainText` (`0x1000011d`) is created at RUNTIME. That window
|
||||||
|
is handled by `CharacterController` (create-the-element path) — different layout, not 0x2100002E.
|
||||||
|
|
||||||
|
## Importer reality (dumped live, 2026-06-25)
|
||||||
|
|
||||||
|
- `dump-vitals-layout` prints the RAW dat (no inheritance) → the content elements look "missing".
|
||||||
|
That is an artifact. The **importer** mounts them. Confirmed by `FindElement` on the studio's
|
||||||
|
imported 0x2100002E:
|
||||||
|
- `0x10000231` name → `UiText` ✓
|
||||||
|
- `0x10000232` heritage → `UiText` ✓
|
||||||
|
- `0x10000233` PK status → `UiText` ✓
|
||||||
|
- `0x1000023B` level → `UiText` ✓
|
||||||
|
- `0x10000235` total XP → `UiText` ✓
|
||||||
|
- `0x10000236` XP-to-level meter → `UiMeter` ✓
|
||||||
|
- `0x1000023D` list box → `UiDatElement` (generic container) ✓
|
||||||
|
- `0x10000238` XP-to-level text → **NULL** (nested variant; not mounted — minor)
|
||||||
|
- `UiElement.EventId` is NOT the dat element id (root shows 0x10000001, not 0x10000227). The dat
|
||||||
|
id lives in `ImportedLayout._byId` (set from `info.Id`, LayoutImporter:107). Bind via
|
||||||
|
`FindElement(id)`, never by reading `widget.EventId`.
|
||||||
|
|
||||||
|
## Header element map (gmStatManagementUI)
|
||||||
|
|
||||||
|
| Id | Field | Content source | Rect (in sub-layout) |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `0x10000231` | m_pNameText | player full name | 0,25,230,20 |
|
||||||
|
| `0x10000232` | m_pHeritageText | gender+heritage display (InqGenderHeritageDisplay) | 0,45,230,15 |
|
||||||
|
| `0x10000233` | m_pPKStatusText | PK status string (StringTable) | 0,60,230,15 |
|
||||||
|
| `0x1000023B` | m_pLevelText | PropertyInt 0x19 (level) | 235,60,65,50 |
|
||||||
|
| `0x10000234` | (label) | "Total Experience:" | 0,95,130,18 |
|
||||||
|
| `0x10000235` | m_pTotalXPText | PropertyInt64 1 | 130,95,100,18 |
|
||||||
|
| `0x10000236` | m_pXPToLevelMeter | (cur−base)/(cap−base), img 0x060011A6 | 0,113,230,17 |
|
||||||
|
| `0x10000237` | (label) | "To next level:" | 0,113,130,17 |
|
||||||
|
| `0x10000238` | m_pXPToLevelText | XP remaining | 130,113,100,17 |
|
||||||
|
| `0x1000023D` | m_pListBox | attribute/skill rows (runtime) | 0,137,300,398 |
|
||||||
|
|
||||||
|
Attribute list rows: gmAttributeUI builds `AttributeInfoRegion` rows (name + base/buffed value +
|
||||||
|
raise button). Canonical AC display order: Strength, Endurance, Coordination, Quickness, Focus, Self.
|
||||||
|
|
||||||
|
## StringTable
|
||||||
|
|
||||||
|
`DatReaderWriter` CAN read `StringTable` (CLI uses `GetAllIdsOfType<StringTable>`). The decomp's
|
||||||
|
`StringInfo::SetStringIDandTableEnum(…, stringId, 0x10000001)` references table enum 0x10000001 →
|
||||||
|
the game string table dat (0x31000001 family); StringId=0 is a placeholder overwritten by a
|
||||||
|
preceding `compute_str_hash("ID_…")`. To reproduce exact wording: read the StringTable + a
|
||||||
|
StringInfo helper (hash → id → lookup, `AddVariable_*` substitution). NOT yet ported — current
|
||||||
|
controller uses canonical AC labels.
|
||||||
|
|
||||||
|
## Status (this session)
|
||||||
|
|
||||||
|
- **DONE (commit 0e644b5):** `CharacterStatController` binds name/heritage/PK/level/total-XP +
|
||||||
|
the XP meter fill + the six attributes into the real elements. Studio renders the real panel.
|
||||||
|
- **Follow-ups (known sources):** (1) tab-button active/inactive STATE so the 3 tabs draw their
|
||||||
|
sprites (0x06005D92-97); (2) StringTable wording; (3) full AttributeInfoRegion row template
|
||||||
|
(column-aligned values + per-attribute raise buttons); (4) wire `0x10000234`/`0x10000237`
|
||||||
|
labels + the right-side level area placement.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue