diff --git a/docs/research/2026-06-21-d2b-bcontroller-shipped-next-handoff.md b/docs/research/2026-06-21-d2b-bcontroller-shipped-next-handoff.md new file mode 100644 index 00000000..8ad39e1d --- /dev/null +++ b/docs/research/2026-06-21-d2b-bcontroller-shipped-next-handoff.md @@ -0,0 +1,167 @@ +# Handoff — D.2b inventory: B-Controller shipped + visually confirmed; B-Wire next + +**Date:** 2026-06-21 (later than the B-Grid handoff of the same date) +**From:** the session that shipped **B-Controller** (inventory population + burden meter + +captions) and root-caused/fixed the **backdrop wash-out** (a #145 continuation) at the visual gate. +**Predecessor:** `docs/research/2026-06-21-d2b-bgrid-shipped-bcontroller-next-handoff.md` +(window manager + B-Grid). This doc supersedes it for "what's next." +**Branch:** `claude/hopeful-maxwell-214a12`, tip **`c38f098`**. `main` is a clean +fast-forward ancestor — ff `main` + branch fresh, or continue this branch. +**Line numbers drift — grep the symbol.** + +--- + +## 0. What shipped this session (all committed, build + tests green, VISUALLY CONFIRMED) + +**B-Controller** — `InventoryController` (the `gm*UI::PostInit` analogue) binds the imported +`gmInventoryUI 0x21000023` tree by id and populates it from `ClientObjectTable`. F12 (with +`ACDREAM_RETAIL_UI=1`) now shows your live inventory on the dark backdrop: the "Contents of +Backpack" grid, the pack-selector strip, the vertical burden bar ("Burden 17%"), and the captions. + +Commits (after the input handoff's `4e23a7b`): +- `5875ac8` `BurdenMath` encumbrance ports (Core) +- `fb050ae` `ClientObjectTable.SumCarriedBurden` (Core) +- `5e75d2a` `UiMeter` vertical fill (`Vertical`/`FillFromBottom`/`DrawVBar`) +- `89c640a` `InventoryController` bind + populate + burden + captions +- `383e8b7` follow-up (Populate uses `GetContents` only — dropped a subagent's index-fallback workaround) +- `03fbf44` GameWindow wiring (the `InventoryController.Bind` in the inventory-init block) +- `7bb4bd3` divergence rows AP-48..51 + ISSUES/roadmap +- `1ccf07b` Opus phase-boundary review fixes (burden % saturation + equipped-item filter) +- `417b137` **render fixes** (backdrop wash-out [#145 continuation] + captions) — the visual-gate fix +- `c38f098` docs (ISSUES/roadmap: visually confirmed + #145 continuation + overflow follow-up) + +Spec: `docs/superpowers/specs/2026-06-21-d2b-inventory-controller-design.md`. +Plan: `docs/superpowers/plans/2026-06-21-d2b-inventory-controller.md`. +Tests: **App 532 / Core 1526 green.** `InventoryFrameImportProbe` is a real-dat smoke test +(gated on `ACDREAM_DAT_DIR`) that **locks the ZLevel fix** (asserts each mounted panel's +ZOrder > the backdrop's). + +--- + +## 1. Read first + +- This doc. +- `claude-memory/project_d2b_retail_ui.md` — the B-Controller SHIPPED entry + DO-NOT-RETRY (updated this session). +- `docs/superpowers/specs/2026-06-21-d2b-inventory-controller-design.md` — the B-Controller spec. +- `docs/research/2026-06-16-inventory-deep-dive.md` §4 — the inventory wire catalog (for **B-Wire**). +- `docs/research/2026-06-16-ui-panels-synthesis.md` §3.3 — the de-duped wire-gap TODO. +- `docs/ISSUES.md` — the **#145** entry (now closed via the continuation), the **D.2b-B SHIPPED** entry, and the new **contents-grid overflow** issue. + +--- + +## 2. Key discoveries (durable, non-obvious — DO-NOT-RETRY) + +1. **THE BIG ONE — a mounted sub-window slot must keep its OWN frame ZLevel, NOT inherit the + base layout root's.** The gm*UI sub-window roots (`0x100001C8` backpack, `0x100001C4` + 3D-items) carry **ZLevel 1000**. `ElementReader.Merge`'s zero-wins-base rule (`ElementReader.cs:161`, + `ZLevel = derived.ZLevel != 0 ? derived.ZLevel : base_.ZLevel`) made the mounted slots + (own ZLevel 0) inherit that 1000, and the #145 ZOrder fold (`ReadOrder − ZLevel·10000`) + turned 1000 into ZOrder ≈ −10,000,000 — *behind* the frame's Alphablend backdrop + (`0x100001D0`, ZLevel 100 → ≈ −1,000,000). The backdrop then **overpainted/washed out** the + panels' captions + burden meter + item cells. The **paperdoll** root happens to be ZLevel 0, + so it survived — which is why the B-Grid session believed #145 was "done." Fix + (`417b137`): `LayoutImporter.Resolve` sets `result.ZLevel = self.ZLevel` inside the + `ShouldMountBaseChildren` block. **Root-caused via a one-shot `TextRenderer` sprite-segment-order + dump** (the backdrop's segment was drawing AFTER the panel content) + a live ZLevel probe — when + "drawn but invisible," dump the actual paint order, don't keep theorizing z-math. + +2. **Burden formula (ported into `Core.Items.BurdenMath`):** + `capacity = Str × (150 + clamp(aug×30, 0, 150))` (`EncumbranceSystem::EncumbranceCapacity` + `0x004fcc00`); `load = burden / capacity` (`EncumbranceSystem::Load` `0x004fcc40` — the + decompiler MANGLED the FP divide to `return burden`; cross-checked vs the existing `ComputeMax` + + the r06 doc); `gmBackpackUI::SetLoadLevel` (`0x004a6ea0`) → fill = `clamp(load/3, 0, 1)`, + %text = `floor(load × 100)` **SATURATING at 300%** (computed from the CLAMPED fill, not + unclamped — caught in review). `CACQualities::InqLoad` `0x0058f130`. **acdream has no wire + `EncumbranceVal` yet** → client-side `SumCarriedBurden` fallback (divergence **AP-48**; + B-Wire pins the real wire value). + +3. **`DatWidgetFactory.BuildMeter`'s single-image path is ALREADY correct for the burden meter** + (meter-own DirectState `0x0600121D` = back/track; its one Type-3 child `0x0600121C` = fill) + per `UIElement_Meter::DrawChildren` `0x0046fbd0` (the child = `m_pcChildImage = GetChildRecursive(this, 2)`). + The inventory deep-dive's "0x0600121C = back" label was backwards re: draw role. The only + meter change was a **vertical** fill path (`UiMeter.Vertical`/`FillFromBottom`/`DrawVBar`). + A near-empty fill at low load is CORRECT (Str 290 → 17% → ~3px of a 58px bar), not a bug. + +4. **Captions: drive the HOST `UiText` directly.** The caption elements (`0x100001D7` "Burden", + `0x100001D8` %, `0x100001C5` "Contents of Backpack") resolve to `UiText`. A nested *child* + `UiText` does NOT paint — `AttachCaption` now sets the host UiText's `LinesProvider`/`Centered`/`DatFont`. + +5. **Equipped items excluded** from the pack grid + selector (`CurrentlyEquippedLocation != None`) + — a mid-session self-wield routes them through `MoveItem(item, WielderGuid=player)` into + `GetContents(player)`; retail's gm3DItemsUI shows pack contents only. + +--- + +## 3. Current visual state (F12, `ACDREAM_RETAIL_UI=1`) + +Renders correctly: dark backdrop BEHIND; paperdoll equip slots (generic blue border); backpack +strip ("Burden" + % + vertical bar + side-bag cell + main-pack cell); 3D-items "Contents of +Backpack" grid populated (6-col). **Expected gaps (all are the follow-ups below):** +- Contents grid OVERFLOWS the 96px panel (no scroll) — "part hanging off the window." +- Paperdoll equip slots show a generic blue `UiDatElement` border, not per-slot silhouettes → Sub-phase C. +- The inventory window starts HIDDEN; F12 toggles it (the auto-open debug hack was reverted). + +--- + +## 4. What's next (build order; each gets its own brainstorm → spec → plan → subagent flow) + +**(a) B-Wire — RECOMMENDED NEXT.** Parse the wire `EncumbranceVal` (ACE `PropertyInt 5`) for the +player so the burden bar reads the server value (retires **AP-48**, the client-side sum). Plus the +other inventory wire gaps catalogued in `2026-06-16-inventory-deep-dive.md §4` + the de-duped TODO +in `2026-06-16-ui-panels-synthesis.md §3.3`: builders `DropItem 0x001B`, `GetAndWieldItem 0x001A`, +`NoLongerViewingContents 0x0195`; parsers `ViewContents 0x0196`, `SetStackSize`, `InventoryRemoveObject`; +fix the dropped 4th field on `0x0022` + the dropped error on `0x00A0`; register the unwired parsers; +extend `CreateObject` if any icon/stack fields are still discarded. **Mandatory grep-named → +cross-ref → pseudocode → port** for each wire format; ACE handlers + holtburger fixtures are the oracles. + +**(b) Contents-grid scroll polish.** The 6-col grid shows ALL loose items, overflowing the 96px +panel. Wire the gutter `UiScrollbar` (`0x100001C7`) to the grid + clip the grid to the panel + +scroll the overflow. Filed as an OPEN issue in `docs/ISSUES.md`; was out-of-scope for B-Controller +(spec §10). Small, self-contained. + +**(c) B-Drag.** Inventory cell as a drag SOURCE (reuse the shipped spine — `UiItemSlot` is already a +full drag source/target) + the `SourceKind == Inventory` branch in `ToolbarController.HandleDropRelease`. + +**(d) Sub-phase C — the biggest piece.** Paperdoll: register `0x10000032` (`UiItemSlot`) so equip +slots draw per-slot silhouettes (fixes the generic-blue-slot art) + the `UiViewport` (Type `0xD`) +3D character doll, which needs a new Core→App `IUiViewportRenderer` seam (Code-Structure Rule 2). +Research: `docs/research/2026-06-16-equipment-paperdoll-deep-dive.md`. + +(Then D.5.3-remaining: the toolbar mana meter + stack slider + spell shortcuts; vendor/trade/spellbook.) + +--- + +## 5. Caveats / open items + +- **Chat + toolbar #145 z-order eyeball (the input handoff's first ask):** analytically cleared + this session — chat's only non-zero-ZLevel element (`0x1000000E`, ZLevel 900) was already the + backmost top-level sibling, so the #145 fold can't change its order; the toolbar now honors + ZLevel (retail-faithful). No regression seen across ~10 launches. Not separately user-confirmed + in isolation, but low-risk — glance once if convenient. +- The `InventoryFrameImportProbe` real-dat test SKIPS in CI (no dat). It only asserts when + `ACDREAM_DAT_DIR` (or the default `~/Documents/Asheron's Call`) exists. +- `ClientObjectTable.GetContents(playerGuid)` returns `IReadOnlyList` (guids), NOT + `ClientObject`s — a prior research agent's claim was wrong; trust the source. +- The main-pack cell (`m_topContainer 0x100001C9`) uses a placeholder icon (the main pack ≡ the + player, no item IconId) — AP-51. Pin a backpack RenderSurface DID if it bothers you. + +--- + +## 6. New-session prompt (paste into a fresh session) + +> Continue acdream's D.2b retail-UI inventory arc. **Read +> `docs/research/2026-06-21-d2b-bcontroller-shipped-next-handoff.md` first.** B-Controller +> (inventory population + burden meter + captions) shipped + visually confirmed on +> `claude/hopeful-maxwell-214a12` (tip `c38f098`; `main` is a clean ff ancestor — ff + branch +> fresh, or continue). The backdrop wash-out (a #145 continuation: mounted sub-window slots were +> inheriting the base root's ZLevel 1000 → behind the backdrop) is fixed. **Next: B-Wire** — parse +> the player's wire `EncumbranceVal` (PropertyInt 5) to retire the client-side burden-sum fallback +> (AP-48), plus the inventory wire gaps in `2026-06-16-inventory-deep-dive.md §4` / +> `2026-06-16-ui-panels-synthesis.md §3.3`. Use the full brainstorm → spec → plan → subagent-driven +> flow; mandatory grep-named → cross-ref → pseudocode → port for every wire format; conformance +> tests throughout. After B-Wire: the contents-grid scroll polish (wire gutter `0x100001C7`), +> B-Drag (inventory drag SOURCE), then Sub-phase C (paperdoll `UiViewport` doll + `0x10000032` +> UiItemSlot per-slot art). + +**MEMORY.md index line** (already added this session): +- [Project: D.2b retail UI engine](project_d2b_retail_ui.md) — … Inventory B-Controller SHIPPED 2026-06-21. DO-NOT-RETRY: a mounted sub-window slot must keep its OWN frame ZLevel — inheriting the base root's ZLevel 1000 sinks the panel behind the Alphablend backdrop (the #145-continuation wash-out).