# 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.