11 KiB
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):
5875ac8BurdenMathencumbrance ports (Core)fb050aeClientObjectTable.SumCarriedBurden(Core)5e75d2aUiMetervertical fill (Vertical/FillFromBottom/DrawVBar)89c640aInventoryControllerbind + populate + burden + captions383e8b7follow-up (Populate usesGetContentsonly — dropped a subagent's index-fallback workaround)03fbf44GameWindow wiring (theInventoryController.Bindin the inventory-init block)7bb4bd3divergence rows AP-48..51 + ISSUES/roadmap1ccf07bOpus phase-boundary review fixes (burden % saturation + equipped-item filter)417b137render fixes (backdrop wash-out [#145 continuation] + captions) — the visual-gate fixc38f098docs (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)
-
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 (
0x100001C8backpack,0x100001C43D-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.Resolvesetsresult.ZLevel = self.ZLevelinside theShouldMountBaseChildrenblock. Root-caused via a one-shotTextRenderersprite-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. -
Burden formula (ported into
Core.Items.BurdenMath):capacity = Str × (150 + clamp(aug×30, 0, 150))(EncumbranceSystem::EncumbranceCapacity0x004fcc00);load = burden / capacity(EncumbranceSystem::Load0x004fcc40— the decompiler MANGLED the FP divide toreturn burden; cross-checked vs the existingComputeMax- 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::InqLoad0x0058f130. acdream has no wireEncumbranceValyet → client-sideSumCarriedBurdenfallback (divergence AP-48; B-Wire pins the real wire value).
- the r06 doc);
-
DatWidgetFactory.BuildMeter's single-image path is ALREADY correct for the burden meter (meter-own DirectState0x0600121D= back/track; its one Type-3 child0x0600121C= fill) perUIElement_Meter::DrawChildren0x0046fbd0(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. -
Captions: drive the HOST
UiTextdirectly. The caption elements (0x100001D7"Burden",0x100001D8%,0x100001C5"Contents of Backpack") resolve toUiText. A nested childUiTextdoes NOT paint —AttachCaptionnow sets the host UiText'sLinesProvider/Centered/DatFont. -
Equipped items excluded from the pack grid + selector (
CurrentlyEquippedLocation != None) — a mid-session self-wield routes them throughMoveItem(item, WielderGuid=player)intoGetContents(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
UiDatElementborder, 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
InventoryFrameImportProbereal-dat test SKIPS in CI (no dat). It only asserts whenACDREAM_DAT_DIR(or the default~/Documents/Asheron's Call) exists. ClientObjectTable.GetContents(playerGuid)returnsIReadOnlyList<uint>(guids), NOTClientObjects — 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.mdfirst. B-Controller (inventory population + burden meter + captions) shipped + visually confirmed onclaude/hopeful-maxwell-214a12(tipc38f098;mainis 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 wireEncumbranceVal(PropertyInt 5) to retire the client-side burden-sum fallback (AP-48), plus the inventory wire gaps in2026-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 gutter0x100001C7), B-Drag (inventory drag SOURCE), then Sub-phase C (paperdollUiViewportdoll +0x10000032UiItemSlot 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).