120 lines
7 KiB
Markdown
120 lines
7 KiB
Markdown
# LA8 retained character-management UI evidence
|
|
|
|
Date: 2026-08-14
|
|
|
|
This note records the retail and installed-DAT evidence for Campaign LA slice
|
|
LA8, plus the exact ownership and presentation boundary implemented by the
|
|
slice. LA7b remains the authority for pre-world Runtime and wire behavior.
|
|
|
|
## Named-retail evidence
|
|
|
|
The implementation was derived from
|
|
`docs/research/named-retail/acclient_2013_pseudo_c.txt` and the corresponding
|
|
`acclient.h` definition before the screen was written.
|
|
|
|
- `DBObj::GetDIDByEnum` (`0x004153A0`) forwards to
|
|
`DBCache::GetDIDFromEnumStatic`; retail resolves the category/table mapping
|
|
before loading a LayoutDesc.
|
|
- `gmCharacterManagementUI::gmCharacterManagementUI` (`0x004EC8F0`) calls
|
|
`UIMainFramework::CreateAndAddRootElement(0x10000005, 0x1000039A)`, then
|
|
binds ListBox `0x1000039D`, Create `0x100003A0`, Enter `0x100003A2`, Delete
|
|
`0x1000039F`, and Restore `0x1000039E`.
|
|
- The verbatim header at `acclient.h:56545` declares exactly that ListBox,
|
|
those four button pointers, the selected row/guid, and four dialog contexts.
|
|
It declares no viewport, `gmCG3DView`, or preview owner.
|
|
- `RebuildCharacterList` (`0x004EC3A0`) creates each row through
|
|
`AddItemFromTemplateList`, then resizes it using signed integer division:
|
|
`max(listHeight / max(rosterCount, allowedSlots), listHeight / 10)`. Thus a
|
|
320-pixel list with five allowed slots uses 64-pixel rows, while rosters over
|
|
ten clamp at 32 pixels. It retains character identity, displays pending
|
|
deletion in red, sorts by ordinal name, moves greyed entries to the tail,
|
|
and restores/falls back selection. LA8 preserves the already canonical LA7b
|
|
display order and identity instead of sorting an App copy.
|
|
- `SelectCharacter` (`0x004EC160`) and `UpdateButtons` (`0x004EC240`) establish
|
|
the highlight and button matrix: no or greyed selection disables Enter and
|
|
Delete; an active selection shows/enables Delete; a greyed selection hides
|
|
Delete and shows/enables Restore.
|
|
- `ListenToElementMessage` (`0x004ED5A0`) routes the list selection message,
|
|
button clicks, and row-template `0x100003A5` activation message `0x1A`.
|
|
Double-activating a row calls `EnterGame` (`0x004ED440`).
|
|
- `MakeDeleteCharacterConfirmationDialog` (`0x004ECCA0`) uses retail dialog
|
|
type 5 and compares the typed response with the localized DELETE response
|
|
case-insensitively. `MakePleaseWaitDialog` (`0x004ECED0`) and
|
|
`MakeEnteringWorldDialog` (`0x004ED090`) use the wait machinery. Error
|
|
presentation enters through `MakeErrorMessageDialog` (`0x004ECB10`). The
|
|
destructor (`0x004EC080`) closes every owned dialog context.
|
|
|
|
The shared dialog factory switch supplies catalog roots/classes used here:
|
|
message type 3 is root `0x24` / class `0x17`; confirmation-text-input type 5
|
|
is root `0x2C` / class `0x15`; the existing wait type 2 is root `0x31` /
|
|
class `0x19`. The message button is `0x26`. Type 5 uses field `0x2C`, accept
|
|
`0x2E`, reject `0x2F`, and result property `0x9C`.
|
|
|
|
## Installed-DAT proof
|
|
|
|
The permanent read-only acceptance probe is
|
|
`tests/AcDream.App.Tests/UI/Layout/CharacterManagementLiveDatTests.cs`. Run it
|
|
with `ACDREAM_PROBE_LIVE_MOUNT=1`; it reads the ordinary
|
|
`%USERPROFILE%/Documents/Asheron's Call` DAT set unless `ACDREAM_DAT_DIR`
|
|
overrides the location. It uses production `DatCollection`,
|
|
`RetailDataIdResolver`, and `LayoutImporter`; it does not write the DATs.
|
|
When the opt-in flag or installed data is absent, discovery records an explicit
|
|
skip rather than adding a no-op pass to default suite totals.
|
|
|
|
The installed September-2013 data proves:
|
|
|
|
- enum category/table 5 maps `0x10000005` to concrete LayoutDesc DID
|
|
**`0x21000004`**;
|
|
- selected root `0x1000039A` is 800 x 600 with eight authored children;
|
|
- the root itself authors image media `0x06007576`; that proves a retained
|
|
layout asset, not a separate render-loop background scene;
|
|
- its ListBox template is `{ 0x21000004, 0x100003A5 }`;
|
|
- the template is a 160 x 16 `UiButton`, font `0x40000009`, with Normal,
|
|
NormalRollover, NormalPressed, Highlight, HighlightRollover, and the authored
|
|
`0xFFFFFFFF` default state;
|
|
- the authored captions are Create Character, ENTER, DELETE, and RESTORE;
|
|
- neither the selected root nor any descendant is a `UiViewport`;
|
|
- enum-table-5 dialog key 2 maps to catalog DID `0x2100003C`, containing the
|
|
type-3 and type-5 roots/children above;
|
|
- string table `0x23000002` contains DELETE, Please Wait, Entering World, and
|
|
the delete-confirmation template. The template has the PLAYER variable and
|
|
resolves it into the selected character name.
|
|
|
|
## Ownership, composition, and lifecycle
|
|
|
|
`RetailUiRuntime` imports the exact enum-resolved root only for a graphical
|
|
launch with no explicit character selector. Its focused binding borrows
|
|
`IRuntimeCharacterSelectionView`; every highlight, enter, delete-request,
|
|
delete-confirm, restore, and cancel action crosses the existing deferred
|
|
adapter as a generation-capturing Runtime command. App retains no gameplay
|
|
mirror. Explicit-selector graphical launches keep their existing flow, and
|
|
headless does not compose this App presentation.
|
|
|
|
The controller instantiates the authored row template in Runtime display
|
|
order, projects red pending-delete rows and the exact button matrix, and opens
|
|
the shared retail dialogs. Delete wait survives the opcode-only acknowledgement
|
|
until the fresh roster arrives. Restore is fire-and-observe: a silent ACE
|
|
no-reply ends only when Runtime expires its correlation; retail's Please Wait
|
|
opens before the synchronous restore command and closes immediately if that
|
|
command rejects or throws. Entering-world wait opens before the existing
|
|
synchronous Enter command; error, reset, reconnect, missing/displaced adapter,
|
|
and disposal close owned contexts without re-entrant commands. A failed
|
|
transient row-template import leaves the Runtime revision unconsumed and
|
|
retries on the next frame. Initial dialog-catalog, character root, and string
|
|
misses likewise retry on later ticks without mounting a duplicate root or
|
|
controller. Dialog presenter/catalog failures move their contexts to an
|
|
internal retry ledger, so UI callbacks do not retain poisoned active/queued
|
|
entries and the same context can appear after resource recovery. Priority
|
|
contexts remain ahead of ordinary retries and preserve retail's nested
|
|
preemption order when creation recovers. The mount coordinator owns a detached
|
|
controller before attaching its root or running the first template-resolving
|
|
tick; any partial failure disposes that exact controller before retry, so roots
|
|
and handlers cannot accumulate.
|
|
|
|
There is deliberately no 3D preview and no claimed character-select background
|
|
scene. The screen root remains neutral with respect to render-loop background
|
|
composition. LA11's user visual gate owns that unresolved visual choice, plus
|
|
the live local-ACE delete/restore check. Because Enter currently completes its
|
|
established ServerReady transaction synchronously, LA11 must also verify that
|
|
the entering-world wait is perceptible on the real frame path; this slice does
|
|
not introduce a second queue or lifecycle owner merely to force a paint.
|