acdream/docs/research/2026-06-21-d2b-bcontroller-shipped-next-handoff.md
Erik 1230c07af3 docs(handoff): D.2b B-Controller shipped + visually confirmed; B-Wire next
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 18:08:05 +02:00

11 KiB
Raw Blame History

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-ControllerInventoryController (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<uint> (guids), NOT ClientObjects — 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 — … 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).