diff --git a/CLAUDE.md b/CLAUDE.md index a5396c59..004860ac 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -76,19 +76,17 @@ and `~/.claude/projects/.../memory/` (the latter is browsable in Obsidian via the `claude-memory/` junction in the repo root; see `memory/reference_obsidian_vault.md`). -**UI strategy:** three-layer split — swappable backend (ImGui.NET + -`Silk.NET.OpenGL.Extensions.ImGui` for Phase D.2a, custom retail-look -toolkit for D.2b later) / stable `AcDream.UI.Abstractions` layer -(ViewModels + Commands + `IPanel` / `IPanelRenderer`) / unchanged game -state. **As of Phase I, ImGui hosts every dev/debug panel** — Vitals, -Chat, Debug. The previous custom-StbTrueTypeSharp `DebugOverlay` was -deleted in I.2; `TextRenderer` + `BitmapFont` are kept alive -specifically for the future world-space HUD (D.6 — damage floaters, -name plates) where ImGui can't reach into the 3D scene. D.2b remains -the long-term retail-look path (panels reskinned one at a time using -dat assets); ImGui persists forever as the `ACDREAM_DEVTOOLS=1` -overlay. **All plugin-facing UI targets `AcDream.UI.Abstractions` — -never import a backend namespace from a panel.** Full design: +**UI strategy:** two coexisting presentation stacks over shared state, +ViewModels, events, and commands. ImGui.NET + +`Silk.NET.OpenGL.Extensions.ImGui` is the permanent +`ACDREAM_DEVTOOLS=1` developer stack using `IPanel`/`IPanelRenderer`. +Retail gameplay UI is the independent retained `UiHost`/`UiRoot` tree in +`AcDream.App/UI`, imported from LayoutDesc/DAT assets and bound by focused +controllers. The stable cross-stack seam is ViewModels/commands, not a backend +swap. `TextRenderer` + `BitmapFont` also serve D.6 world-space HUD elements +where ImGui cannot reach the 3D scene. Plugin gameplay UI uses the BCL-only +`AcDream.Plugin.Abstractions.IUiRegistry.AddMarkupPanel` contract; plugins +never import App or ImGui namespaces. Full design: [`docs/plans/2026-04-24-ui-framework.md`](docs/plans/2026-04-24-ui-framework.md). Memory cribs: `memory/project_chat_pipeline.md` (chat pipeline as of Phase I), `memory/project_input_pipeline.md` (input pipeline as of diff --git a/docs/ISSUES.md b/docs/ISSUES.md index 151c3681..6c82106c 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -1674,7 +1674,7 @@ R5/R6 touches the action list. **Where:** ## #165 — Reconcile the UI design docs with the shipped D.2b shape -**Status:** OPEN +**Status:** DONE (2026-07-10, retail UI fidelity Wave 0) **Severity:** MEDIUM **Filed:** 2026-07-02 **Component:** docs @@ -1689,6 +1689,13 @@ R5/R6 touches the action list. **Where:** **Acceptance:** both docs describe the two-stack reality; the plugin markup-vs-IPanel decision is written down. +**Resolution:** `CLAUDE.md`, `acdream-architecture.md`, and +`2026-04-24-ui-framework.md` now describe the shipped two-stack architecture. +`IPanel`/`IPanelRenderer` is the permanent first-party ImGui devtools contract; +the retained `UiRoot` tree is gameplay UI; both share ViewModels/commands/state. +Plugin gameplay UI is explicitly `IUiRegistry.AddMarkupPanel` and remains +BCL-only. + --- ## #158 — Character window — deferred polish diff --git a/docs/architecture/acdream-architecture.md b/docs/architecture/acdream-architecture.md index 477dcc20..ade10126 100644 --- a/docs/architecture/acdream-architecture.md +++ b/docs/architecture/acdream-architecture.md @@ -71,32 +71,38 @@ well-defined interfaces that the retail client never had. └──────────────────────────────────────────────────────────────┘ ``` -### UI Architecture (companion stack, spans Layers 1 & 5) +### UI Architecture (two coexisting presentation stacks) -The UI is split into its own three-layer stack with a swappable backend, -designed 2026-04-24. Full design: `docs/plans/2026-04-24-ui-framework.md`. +The 2026-04-24 design began with a swappable renderer abstraction. D.2b proved +that retail fidelity needs a retained LayoutDesc/DAT tree, while ImGui remains +valuable as permanent devtools. The current architecture therefore has two +presentation stacks over shared state, ViewModels, events, and commands. Full +history and the corrected contract live in +`docs/plans/2026-04-24-ui-framework.md`. ``` ┌─────────────────────────────────────────────────────────────┐ -│ UI BACKEND (swappable) │ -│ ImGui.NET + Silk.NET.OpenGL.Extensions.ImGui │ -│ (Phase D.2a, short-term) │ -│ or custom retail-look toolkit (Phase D.2b, later) │ +│ DEVELOPER UI │ +│ IPanel/IPanelRenderer → permanent ImGui devtools │ +│ GAMEPLAY UI │ +│ LayoutDesc/DAT → UiRoot retained widgets + controllers │ ├─────────────────────────────────────────────────────────────┤ -│ AcDream.UI.Abstractions (stable contract) │ -│ ViewModels, Commands, IPanel, IPanelHost, IPanelRenderer │ -│ ► plugin-facing UI API lives HERE, not in the backend │ +│ SHARED CONTRACTS │ +│ ViewModels, commands, input actions, state/event services │ +│ ► one model and mutation path, two presentation projections│ ├─────────────────────────────────────────────────────────────┤ │ Game state + events (unchanged) │ │ IGameState / IEvents / WorldSession — UI only reads │ └─────────────────────────────────────────────────────────────┘ ``` -The backend is pluggable; ViewModels / Commands / `IPanelRenderer` are -stable across the swap. ImGui persists forever as the -`ACDREAM_DEVTOOLS=1` devtools overlay regardless of which backend owns -the game UI. The full UI design lives in -`docs/plans/2026-04-24-ui-framework.md`. +`AcDream.UI.Abstractions` owns backend-neutral ViewModels, commands, input, +and the `IPanel`/`IPanelRenderer` devtools contract. `AcDream.App/UI` owns the +retained gameplay tree, LayoutDesc importer, window runtime, and panel +controllers. Neither presentation stack owns independent game-state truth. +Plugins register retained gameplay markup through the BCL-only +`AcDream.Plugin.Abstractions.IUiRegistry`; they do not import App or ImGui +assemblies. --- diff --git a/docs/plans/2026-04-11-roadmap.md b/docs/plans/2026-04-11-roadmap.md index bba53b01..422a971a 100644 --- a/docs/plans/2026-04-11-roadmap.md +++ b/docs/plans/2026-04-11-roadmap.md @@ -486,7 +486,8 @@ behavior. Estimated 17–26 days focused work, 3–5 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.)** diff --git a/docs/plans/2026-04-24-ui-framework.md b/docs/plans/2026-04-24-ui-framework.md index e6fce264..a216406e 100644 --- a/docs/plans/2026-04-24-ui-framework.md +++ b/docs/plans/2026-04-24-ui-framework.md @@ -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 diff --git a/docs/plans/2026-05-12-milestones.md b/docs/plans/2026-05-12-milestones.md index 3be29344..c66f0efa 100644 --- a/docs/plans/2026-05-12-milestones.md +++ b/docs/plans/2026-05-12-milestones.md @@ -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.3–D.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;