docs(ui): align architecture with retained runtime

This commit is contained in:
Erik 2026-07-10 16:57:44 +02:00
parent 00ab4a2a64
commit f98050854d
6 changed files with 109 additions and 70 deletions

View file

@ -486,7 +486,8 @@ behavior. Estimated 1726 days focused work, 35 weeks calendar.
- **✓ SHIPPED — Importer dat-fidelity** (Fix A/B/C, 2026-06-26, same branch). **Boundary established: look = importer (font/justification/color from dat); state/behavior = runtime controller logic.** Fix A: justification property `0x14`/`0x15``UiText` Centered/RightAligned/VerticalJustify. Fix B: FontColor property `0x1B``UiText.DefaultColor`; character colors proved RUNTIME (no importer fix). Fix C: FontDid → per-element dat font via a resolver in `DatWidgetFactory`**STUDIO path only**; GameWindow passes `null` (Issue #157). Fix 4 (UIState-group activation): INVESTIGATED — no importer fix exists; the dat has no Visible encoding; runtime `gm*UI` is correct.
- **✓ SHIPPED — Character window** (`LayoutDesc 0x2100002E`, `CharacterStatController`, 2026-06-26, same branch). **Visually user-confirmed 2026-06-26 — Attributes tab reads as retail.** Three tabs, header (name/heritage/PK), large-gold level number (dat font, `largeDatFont` 18px), "Total Experience (XP):" + "XP for next level:" captions, 9-row attribute list (icons + right-aligned values + Health/Stamina/Mana vitals), click-to-select (top/bottom selection bars + footer State-B "{Attr}: {value}" / "Experience To Raise: Infinity!" + affordability-gated raise triangles), centered footer. User noted "still needs some polish for later" — deferred to Issue #158.
- **✓ SHIPPED — D.5.4 — Client object/item data model (foundation).** Shipped 2026-06-18 (`b506f53`..`a33e897`, 11 commits). Renamed `ItemRepository``ClientObjectTable` / `ItemInstance``ClientObject`; broadened the table to hold EVERY server object (retail `weenie_object_table` shape). `CreateObject` is now the canonical merge-upsert (`ClientObjectTable.Ingest`, retail `SetWeenieDesc` semantics) via a new Core.Net `ObjectTableWiring` (off GameWindow); `DeleteObject` evicts; `PlayerDescription` is a membership manifest (`RecordMembership`); live container-membership index (`GetContents`, retail `object_inventory_table`). `_liveEntityInfoByGuid` retired (selection/describe resolve from the one table). Root fix: the old enrich-existing-only `EnrichItem` dropped `CreateObject`s for items with no `PlayerDescription` stub — live-Coldeve 4/6 hotbar slots blank; items are now created, not dropped. **Crux resolved:** retail is TWO tables (`object_table` + `weenie_object_table`), NOT one — acdream's `WorldEntity` (3D system) + `ClientObjectTable` (data/UI) split was already architecturally faithful; the fix was the ingestion path, not a table unification. 2671 tests green.
- **☐ D.5.3 — Toolbar selected-object display (issue #140) + spell shortcuts.** Wire the B.4 `WorldPicker`/selection state → the two hidden meters (`0x100001A1` health / `0x100001A2` mana) + the stack slider (`0x100001A4`) + the object-name line, so the bar shows the player's currently-selected world object. Plus **spell shortcuts** — pinned *spells* (vs items) don't render their glyphs yet (`ToolbarController.Populate` skips `ObjectGuid==0`). Together these finish "the bar." (Click-to-use + the peace/war stance indicator landed in D.5.1.)
- **Roadmap correction (2026-07-10):** the completion order is now the architecture-first campaign in `docs/superpowers/plans/2026-07-10-retail-ui-fidelity-completion.md`. Retail `gmToolbarUI` is object-only: preserve `ShortCutData.index_`, `objectID_`, and `spellID_`, but do not invent spell glyphs on this bar. `PlayerModule::favorite_spells_[8]` feeds separate spell bars.
- **☐ D.5.3 — Toolbar selected-object display (issue #141) + exact shortcut preservation.** Wire unified selection to the hidden mana meter (`0x100001A2`), stack entry (`0x100001A3`), stack slider (`0x100001A4`), and complete health/name behavior. Preserve all raw `ShortCutData` fields while keeping `gmToolbarUI` object-only, matching `UpdateFromPlayerDesc @ 0x004BF810`. The eight `favorite_spells_` lists belong to the separate spellbook/spell-bar phase. (Click-to-use + peace/war + health/name already landed.)
- **☐ D.5.5+ — Core panels.** Inventory (`gmInventoryUI`/`gmBackpackUI`), equipment/paperdoll (`gmPaperDollUI`/`gm3DItemsUI` + the `UiViewport` 3D doll), vendor, trade, spellbook. Research drop done (`docs/research/2026-06-16-*`). Depends on **D.5.4** (data model) + the item-slot/list/icon spine (D.5.1/D.5.2) + the **window manager** (Plan 2: open/close/z-order/persist + faithful grip/dragbar drag-resize) + the drag-drop spine wired (`UiRoot` has the chain; the per-cell accept/drop hooks are still stubs in `UiField`). Also deferred from D.5.1: drag/reorder + the `AddShortcut`/`RemoveShortcut` mutate wire.
- **D.6 — HUD.** **Radar/compass IMPLEMENTED 2026-07-10; live-world visual gate pending.** It is the retained retail `gmRadarUI` (`LayoutDesc 0x21000074`): a 120×140 circular radar (`0x06004CC1`) with N/E/S/W token sprites orbiting the face, a fixed bright-green player marker, and the coordinate footer (`0x06004CC0`). Projection is `radarPixelRadius / radarRange` with 75 m outdoor / 25 m indoor ranges — there is no 1.18 factor or scrolling compass strip. It lives in `AcDream.App/UI` beside the other imported retail panels. Remaining D.6: target name plate, damage floaters, and other world-space HUD elements through the raw `TextRenderer` path. See slice 06 §A.2, `docs/research/2026-07-10-retail-radar-pseudocode.md`, and the named `gmRadarUI` decomp.
- **D.7 — Cursor manager.** OS + dat-sourced custom cursors (`FUN_0043c1c0` GDI HCURSOR builder pattern from slice 03). **(D.2b dependency.)**

View file

@ -1,11 +1,11 @@
# UI framework plan
**Date:** 2026-04-24 (design), shipped 2026-04-25
**Status:** **Phase D.2a shipped**`AcDream.UI.Abstractions` + ImGui backend
+ `VitalsPanel` gated on `ACDREAM_DEVTOOLS=1`. Backend pivoted from
`Hexa.NET.ImGui` to `ImGui.NET` + `Silk.NET.OpenGL.Extensions.ImGui` during
first-light integration — see the pivot note below. Phase D.2b (custom
retail-look backend) remains design-only.
**Status:** **Phase D.2a and the D.2b retained gameplay UI have shipped.**
ImGui remains the `ACDREAM_DEVTOOLS=1` developer stack. Retail gameplay UI is
the independent `UiHost`/`UiRoot` retained tree under `AcDream.App/UI`, built
from production LayoutDesc/DAT assets. The stable cross-stack seam is game
state, ViewModels, and commands — not an `IPanelRenderer` backend swap.
**Owner:** lead engineer (erik) + Claude
Captures the UI strategy agreed via discussion on 2026-04-24. Documents
@ -53,12 +53,12 @@ panel, skills, spellbook, fellowship, allegiance, trade, options, map,
quest log, tooltips — and a first-class plugin API so plugin authors can
ship their own panels.
## Strategy: two-phase, one stable interface
## Current strategy: two coexisting stacks, shared state contracts
```
┌─────────────────────────────────────────┐
UI backend ─ ImGui (short-term) │ ← swappable
─ Custom retail (later)
Developer UI ─ ImGui + IPanelRenderer │
Gameplay UI ─ UiRoot retained widgets
├─────────────────────────────────────────┤
│ ViewModels + Commands (per panel) │ ← stable contracts
├─────────────────────────────────────────┤
@ -66,14 +66,16 @@ ship their own panels.
└─────────────────────────────────────────┘
```
- **Near term:** wire an ImGui-based overlay so we can iterate on game
logic fast — chat that actually sends + receives, inventory that
reflects real state, vitals bar that reads real HP/stam/mana. Looks
like a debugger, that's fine for now.
- **Later:** replace the visual layer with a custom toolkit that uses
retail dat assets (icons, panels, fonts) and matches retail's feel.
- **Always:** ViewModels and Commands stay stable across the swap. The
game logic never learns which backend is drawing it.
- **Developer stack:** `AcDream.UI.Abstractions` panels render through
`IPanelRenderer` on ImGui. This stack is permanent for diagnostics, packet
inspection, settings development, and other devtools.
- **Gameplay stack:** `UiHost` owns a retained `UiRoot` tree. `LayoutImporter`
builds retail windows from LayoutDesc/DAT assets and focused `gm*UI`-style
controllers bind runtime values and actions.
- **Shared seam:** both stacks consume the same session state, ViewModels, and
command/event services. A panel is ported to retained UI by binding those
shared models to its DAT-authored tree, not by implementing
`IPanelRenderer` a second time.
## Choice: Hexa.NET.ImGui for the short-term backend
@ -158,7 +160,7 @@ public sealed record EquipItemCmd(uint ItemGuid, EquipSlot Slot);
Dispatched to an `ICommandBus` that routes to the appropriate subsystem
(`WorldSession.SendSelect`, `ChatService.Send`, etc.).
### Layer 3 — UI backend
### Layer 3a — ImGui developer backend
New module: `src/AcDream.UI.ImGui/`. References `AcDream.UI.Abstractions`.
@ -168,11 +170,34 @@ New module: `src/AcDream.UI.ImGui/`. References `AcDream.UI.Abstractions`.
per frame.
- Keyboard / mouse / focus handled by ImGui natively.
Later: `src/AcDream.UI.Retail/` references the same `AcDream.UI.Abstractions`
and implements the same `IPanel` / `IPanelHost` interfaces — but draws
with our own retained-mode toolkit + retail dat assets.
This layer remains the permanent devtools surface. It is not the production
retail gameplay renderer.
## Plugin API (must be backend-agnostic)
### Layer 3b — retained retail gameplay UI
`src/AcDream.App/UI/` contains the GL-free widget tree, LayoutDesc importer,
window runtime, and panel controllers. Rendering dependencies enter through
small sprite/font/viewport resolver seams. Controllers consume the same
ViewModels and command services used by the ImGui panels, while binding
behavior to existing retail element ids instead of procedurally redrawing the
panel through `IPanelRenderer`.
## Plugin UI API
The shipped plugin-facing gameplay UI contract is
`AcDream.Plugin.Abstractions.IUiRegistry.AddMarkupPanel`: a plugin provides
KSML-style markup and a binding object; the host builds it into the retained
`UiRoot` tree. `IPanel`/`IPanelRenderer` remains a first-party developer-panel
contract and is intentionally not referenced by `Plugin.Abstractions`.
This makes plugin gameplay panels independent of ImGui while allowing them to
share the retained input, window, and DAT-sprite runtime. Registrations made
before the GL host exists are buffered. In builds where retail UI is disabled,
they remain registered but have no gameplay surface; the long-term release
configuration enables retained gameplay UI.
The following was the original pre-D.2b proposal and remains historical
context, not the shipped plugin contract:
```csharp
public interface IPanel
@ -227,32 +252,34 @@ walk around / take damage / regen.
to equip, drag target for future move.
- `CharacterPanel` — attributes, skills, XP.
### Sprint 3 — Plugin API hardening
### Sprint 3 — Plugin API hardening (superseded shape)
- Document the `IPanel` contract.
- Port the smoke plugin to register a demo panel via the API.
- Confirm plugins can subscribe to game events AND draw UI through the
same interface.
- The shipped route is `IUiRegistry.AddMarkupPanel`, not plugin-owned
`IPanel` implementations.
- Confirm plugins can subscribe to game events and expose retained markup
bindings without referencing App or ImGui assemblies.
### Sprint 4+ — More panels
Spellbook, allegiance, fellowship, trade, map, quest log, options.
Continue to expand `InventoryPanel` with drag-drop, split, appraise.
### Later — Custom retail-look backend
### D.2b — retained retail-look gameplay UI (shipped, expanding panel by panel)
`AcDream.UI.Retail` implements `IPanelRenderer` with our own toolkit +
retail dat assets. Swap panels one at a time. ImGui overlay remains for
devtools.
`AcDream.App/UI` imports LayoutDesc trees and draws retail DAT assets through
the retained `UiRoot` runtime. ImGui remains available for devtools; shared
ViewModels and commands prevent duplicate game-state logic.
## Non-goals for this first pass
- **Not** going to theme ImGui to look retail. Waste of effort when we'll
swap the backend. Devtools aesthetic is fine.
- **Not** porting retail's widget code. We use their ASSETS later, not
their widget implementation.
- **Not** building layout DSL / XAML-like markup. Panels register and
draw procedurally, same as ImGui.
- **Not** byte-porting Keystone internals that are unavailable. Observable
widget behavior is recovered from the named client call sites, DAT
properties, and live retail evidence.
- **Not** hand-authoring first-party gameplay layouts where retail LayoutDesc
data exists. KSML-style markup remains the plugin/extension layout surface.
## Alternatives considered
@ -271,13 +298,13 @@ devtools.
**Mitigation:** code review every addition; if a feature only exists
in ImGui and the retail toolkit can't express it, don't add it.
- **Risk:** Swap to custom backend breaks a dozen panels simultaneously.
**Mitigation:** swap one panel at a time, keep ImGui rendering the
rest until all are ported.
- **Risk:** ImGui and retained controllers grow separate game-state truth.
**Mitigation:** one session model/ViewModel and one command path per
subsystem; each surface is only a projection.
- **Risk:** Plugin authors write panels that only work in ImGui.
**Mitigation:** smoke plugin registers a panel early; use it as a
canary whenever backend changes.
- **Risk:** Plugin markup relies on App-only widget details.
**Mitigation:** keep `IUiRegistry` BCL-only, resolve bindings by contract,
and use a smoke plugin as the retained-runtime canary.
- **Risk:** Hexa.NET.ImGui stops being maintained.
**Mitigation:** integration is small (<100 LOC), switching to

View file

@ -532,7 +532,7 @@ Open the inventory panel — retail-skinned with the right font, icons, and
local lighting).
- **C.3** — Palette range tuning (skin/hair/eye colors match retail).
- **C.4** — Double-sided translucent polys.
- **D.2b** — Custom retail-look UI backend. **In progress:** vitals + chat + toolbar + inventory (window manager, B-Grid, B-Controller, B-Wire, drag-drop, container-switching) + paperdoll (equip slots, 3-D doll UiViewport, Slots toggle) + **Character window** (Attributes tab, visually confirmed 2026-06-26) + **UI Studio** (standalone panel previewer + 26-window retail dump + interactive click-routing + headless screenshot) + **importer dat-fidelity** (Fix A/B/C: justification, FontColor, per-element FontDid resolver [studio-only; #157 tracks live-game path]) are shipped. Next: D.5.3 selected-object bar + spell shortcuts; #158 Character window polish deferred.
- **D.2b** — Custom retail-look UI backend. **In progress:** vitals + chat + toolbar + inventory (window manager, B-Grid, B-Controller, B-Wire, drag-drop, container-switching) + paperdoll (equip slots, 3-D doll UiViewport, Slots toggle) + **Character window** (Attributes tab, visually confirmed 2026-06-26) + **UI Studio** (standalone panel previewer + 26-window retail dump + interactive click-routing + headless screenshot) + **importer dat-fidelity** (Fix A/B/C: justification, FontColor, per-element FontDid resolver [studio-only; #157 tracks live-game path]) are shipped. Current campaign: shared retained-widget/window architecture, then selected-object mana/stack completion. **Retail correction:** the main toolbar is object-only; the eight favorite-spell lists drive separate spell bars. Execution plan: `docs/superpowers/plans/2026-07-10-retail-ui-fidelity-completion.md`.
- **D.3D.7** — AcFont + dat sprites + core panels reskinned + HUD orbs +
cursor manager. **D.6 radar/compass shipped 2026-07-10** through the retained
UI architecture using the retail DAT layout and recovered runtime behavior;