acdream/docs/research/2026-06-22-container-switching-handoff.md
Erik ad27d1395a docs(D.2b): container-switching handoff + flag stale M1.5 banner
Banks the next clean inventory win (container-switching: Use 0x0036 -> ViewContents
0x0196 full-replace + the selected-container indicator that retires AP-56) as a
handoff for a fresh session, per the late-session-handoff lesson. Wire layer
already exists; design sketch + open questions captured.

Also flags the CLAUDE.md "Current state" banner as stale — it still tracks M1.5
(indoor world, 2026-06-14) while the active stream has been the D.2b retail-UI
track for weeks. Left the milestone reframing for a fresh reconciliation against
the milestones doc rather than a tired guess.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 13:17:55 +02:00

6.8 KiB
Raw Blame History

Handoff — D.2b inventory container-switching (next clean win)

Date: 2026-06-22 (banked at the end of the empty-slot-art session, per feedback_session_handoff) Branch: claude/hopeful-maxwell-214a12, tip 1f8dd7a (+ this docs commit). main is a clean ff ancestor (68 commits behind). Status: SCOPED, not started. Brainstorm explored the wire + controller; this captures the design sketch so a fresh session executes it with the live-wire round-trip getting proper attention. Run the full brainstorm → spec → plan → execute flow. Line numbers drift — grep the symbol.


Why this is the next pick

After the empty-slot-art arc (inventory contents/side-bag/main-pack empty cells now dat-resolve their art — see project_d2b_retail_ui + the empty-slot handoff), the side-bag column is rendered but inert: clicking a bag does nothing. Container-switching makes it functional AND closes the AP-56 deferral (the selected-container triangle + green/yellow selection square live here). The user picked this over the paperdoll (Sub-phase C) because the paperdoll's empty equip slots are entangled with the heavy 3D doll viewport (retail empties show the doll), whereas container-switching is self-contained.

This is the first inventory feature with live two-way wire interaction (a UseViewContents round-trip), a step up from the read-only display work so far — hence executing it fresh rather than tired.

Read first

  • This doc.
  • claude-memory/project_d2b_retail_ui.md — the D.2b SSOT + the empty-slot-art SHIPPED entry (the resolver + AP-55/AP-56).
  • claude-memory/reference_retail_inventory_paperdoll.md — the player-facing spec: the "preferred pack" auto-fit rules + the 7-side-bag / 102-main-pack capacities.
  • docs/architecture/retail-divergence-register.mdAP-56 (the selected-container indicator deferred — this phase retires it) + AP-53 (container-switch noted as deferred).

What's already in place (the wire layer is DONE)

  • InteractRequests.BuildUse (0x0036) — open/use a container by guid (src/AcDream.Core.Net/Messages/InteractRequests.cs:38). This is how retail opens a container.
  • GameEvents.ParseViewContents (0x0196) — the server's full contents list (ContainerGuid, [guid, containerType]×N) (src/AcDream.Core.Net/Messages/GameEvents.cs:372). Handler at GameEventWiring.cs:260.
  • WorldSession.SendNoLongerViewingContents (0x0195) — close a container view (WorldSession.cs:1212).
  • ClientObjectTable.GetContents(containerId) — the per-container item index (src/AcDream.Core/Items/ClientObjectTable.cs:313).
  • The standing TODO at GameEventWiring.cs:256: ViewContents currently does an additive merge; container-open must treat it as a full REPLACE (flush the container's prior membership first) so server-side removals don't linger. This phase consumes that TODO.

Design sketch (to be confirmed in the brainstorm)

  1. Open-container state in InventoryController. Add an _openContainer field (default = the player guid / main pack). Populate shows GetContents(_openContainer) instead of always the player. Repaint when the open container's membership changes (extend Concerns).
  2. Click a side-bag cell → open it. The side-bag UiItemSlots already carry the bag guid (bound in Populate). On click: set _openContainer = bagGuid; SendUse(bagGuid) (opens it server-side); when the previous container was a side bag, SendNoLongerViewingContents(prevGuid). Click the main-pack cell (0x100001C9) → reset _openContainer to the player.
  3. ViewContents 0x0196 → full REPLACE. Flush the container's membership in ClientObjectTable before recording the entries (the TODO). Then InventoryController repaints the grid for _openContainer.
  4. Selected-container indicator (retires AP-56). Draw the open-container triangle 0x06005D9C + the green/yellow selection square on the selected side-bag cell — a new UiItemSlot "selected" overlay (model it on the existing DragAccept overlay in UiItemSlot.OnDraw; guard id != 0 per feedback_ui_resolve_zero_magenta).
  5. Caption. "Contents of Backpack" → the open container's name when a side bag is selected (the caption host is already driven by InventoryController.AttachCaption).

Open questions for the brainstorm

  • The green/yellow selection-square sprite id — the triangle is 0x06005D9C (from the container prototype 0x1000033F child 0x10000450); the selection square's id is NOT yet found. Dump 0x21000037 / the gmBackpackUI layout (0x21000022) for the selected-container state media, or cdb a live retail client with a side bag open.
  • Are a side bag's contents pre-known, or do they arrive only on open? Likely the latter (retail sends contents on open via ViewContents, not upfront) — so the Use round-trip is required, and GetContents(bagGuid) is empty until the reply lands. Confirm against a live capture (WireMCP on 127.0.0.1:9000, or watch the ViewContents handler at the live gate).
  • Preferred-pack semantics (from reference_retail_inventory_paperdoll): opening a pack makes it the "preferred" pack for auto-fit. Out of scope for the first cut (display-only switching), but note it.
  • Empty-container display — a side bag with no contents shows an empty 24-slot grid (its ItemsCapacity, default 24 per AP-52/the wiki). Pad like the main pack does.

Acceptance / scope

  • First cut: click a side bag → its contents show in the grid; the selected bag shows the triangle + selection square; click the main pack → back. ViewContents is a full replace. Build + full suite green; visual gate (F12, the user clicks bags and confirms the grid switches + the selected indicator).
  • Retires AP-56 (selected-container indicator) in the same commit that draws it.
  • Out of scope: preferred-pack auto-fit, drag-INTO a side bag (that's B-Drag), the side-bag column scrollbar (0x100001CB, inert — 7 fit).

New-session prompt

Continue acdream's D.2b retail-UI inventory arc. Read docs/research/2026-06-22-container-switching-handoff.md first. The empty-slot art shipped + was visually confirmed (claude/hopeful-maxwell-214a12, tip 1f8dd7a). Next: inventory container-switching — click a side bag → show its contents in the grid (via Use 0x0036ViewContents 0x0196 full-replace), draw the selected-container triangle 0x06005D9C + green/yellow selection square (retires AP-56), click the main pack to go back. The wire builders/parsers all exist (BuildUse, ParseViewContents, SendNoLongerViewingContents); consume the GameEventWiring.cs:256 full-REPLACE TODO. Run the full brainstorm → spec → plan → subagent-driven → visual-gate flow. DO NOT auto-kill the running client — launch with plain dotnet run; if a rebuild is locked, ask the user to close it.