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

167 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<uint>` (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).