fix(ui): D.2b empty-slot art — container cells use inner slot bg, not the selected-container triangle

Visual gate (2026-06-22) showed the side-bag/main-pack empty cells drawing yellow
triangles. Root cause: the frame-first heuristic grabbed the 36x36 container
prototype's DirectState child 0x06005D9C — which is the open/SELECTED-container
triangle indicator, NOT a background frame — and stamped it onto every empty cell.

Fix: drop frame-first; FindIconEmpty resolves the inner m_elem_Icon ItemSlot_Empty
THROUGH BaseElement inheritance (0x1000033F -> 0x10000340 -> base 0x1000033E ->
0x1000033B -> 0x06000F6E), so containers get the dark slot background matching the
inventory. Pin test now asserts exact ids (0x06004D20 contents / 0x06000F6E
containers) — the old structural-only assertion let the wrong-but-valid 0x06005D9C
pass (Opus review Minor 2, now vindicated). The triangle + green/yellow selection
square is deferred to container-switching (AP-56 reworded). Full suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-22 12:45:36 +02:00
parent 499485a672
commit 22d92315e5
4 changed files with 53 additions and 16 deletions

View file

@ -5397,7 +5397,7 @@ The burden bar now reads the server's wire `EncumbranceVal` (PropertyInt 5) inst
**Root cause / status:** `UiItemSlot.EmptySprite` is a single hardcoded default (`0x060074CF`) shared by all empty cells. The inventory/side-bag pack slots need the gm3DItemsUI/gmBackpackUI cell-template (`0x21000037`) empty-state background instead. The equip slots need the `0x10000032` `UIElement_UIItem` registration + per-slot silhouette art (Sub-phase C).
**Resolution (inventory portion, 2026-06-22):** Ported retail's `UIElement_ItemList::InternalCreateItem` (`0x004e3570`) resolver into `ItemListCellTemplate.ResolveEmptySprite` — reads attribute `0x1000000e` off each list element → catalog `0x21000037` prototype's `ItemSlot_Empty`. **Correction to the original diagnosis:** `0x060074CF` is the *generic shared* item-slot empty (the `ItemSlot_Empty` of many catalog prototypes), not "the toolbar's"; `0x21000037` is a catalog of ~50 per-slot-kind empties — so it was never a one-constant swap. Pinned per-list (real-dat test): contents `0x100001C6``0x06004D20`; side-bag `0x100001CA` + main-pack `0x100001C9``0x06005D9C` (the 36×36 frame). `UiItemList.CellEmptySprite` carries it; `InventoryController` + `GameWindow` wire it. Divergence AP-55 (toolbar still hardcoded) + AP-56 (flat single-sprite cell vs retail's layered prototype). Spec/plan `docs/superpowers/{specs,plans}/2026-06-22-d2b-empty-slot-art*.md`.
**Resolution (inventory portion, 2026-06-22):** Ported retail's `UIElement_ItemList::InternalCreateItem` (`0x004e3570`) resolver into `ItemListCellTemplate.ResolveEmptySprite` — reads attribute `0x1000000e` off each list element → catalog `0x21000037` prototype's `ItemSlot_Empty`. **Correction to the original diagnosis:** `0x060074CF` is the *generic shared* item-slot empty (the `ItemSlot_Empty` of many catalog prototypes), not "the toolbar's"; `0x21000037` is a catalog of ~50 per-slot-kind empties — so it was never a one-constant swap. Pinned per-list (real-dat test): contents `0x100001C6``0x06004D20`; side-bag `0x100001CA` + main-pack `0x100001C9``0x06000F6E` (the inner dark slot, resolved through `BaseElement` inheritance). **Visual-gate correction (2026-06-22):** the first cut's frame-first heuristic grabbed the container prototype's DirectState child `0x06005D9C` — which is the open/**selected**-container TRIANGLE indicator, not a background — and stamped it onto every empty container cell. Fixed: resolve the inner `ItemSlot_Empty` through inheritance (`FindIconEmpty`); the triangle + green/yellow selection square is deferred to the container-switching sub-phase (AP-56). `UiItemList.CellEmptySprite` carries it; `InventoryController` + `GameWindow` wire it. Divergence AP-55 (toolbar still hardcoded) + AP-56 (selected-container indicator deferred). Spec/plan `docs/superpowers/{specs,plans}/2026-06-22-d2b-empty-slot-art*.md`.
**Files:** `src/AcDream.App/UI/UiItemSlot.cs` (`EmptySprite`); `src/AcDream.App/UI/Layout/InventoryController.cs` (set the inventory pack-slot empty sprite on the cells it creates); paperdoll equip slots → Sub-phase C.