diff --git a/docs/architecture/retail-divergence-register.md b/docs/architecture/retail-divergence-register.md index 76aa4865..49af5d36 100644 --- a/docs/architecture/retail-divergence-register.md +++ b/docs/architecture/retail-divergence-register.md @@ -154,6 +154,8 @@ accepted-divergence entries (#96, #49, #50). | AP-52 | Side-bag column slot count defaults to 7 (the dat column height) when the player's `ContainersCapacity` is absent/0. | `src/AcDream.App/UI/Layout/InventoryController.cs` (`Populate`) | `ContainersCapacity` is not reliably on the player ClientObject yet; 7 is the standard side-pack cap + the dat column (`0x100001CA`, 36×252) holds exactly 7. | An 8th-pack (Shadow of the Seventh Mule aug) character shows one too few side-bag slots — cosmetic. | retail gmBackpackUI side-pack column; ACE `ContainersCapacity` | | AP-53 | Contents grid slot count defaults to 102 when the player's `ItemsCapacity` is absent/0; the grid always shows the main pack (container-switch to a side pack's 24 not wired). | `src/AcDream.App/UI/Layout/InventoryController.cs` (`Populate`) | `ItemsCapacity` is not reliably on the player ClientObject yet; 102 is the retail main-pack standard. ViewContents-driven container switching is a later sub-phase. | A non-102 pack shows the wrong empty-slot count; selecting a side pack doesn't show its 24-slot contents yet. | retail main-pack capacity 102; ACE `ItemsCapacity`; `ViewContents 0x0196` (deferred) | | AP-54 | Inventory window vertical resize is a toolkit approximation: bottom-edge drag, expand-only (Min = dat default 372 px, Max = 560 px constant), contents grid/sub-window/scrollbar/backdrop stretched via overridden `Left\|Top\|Bottom` anchors. | `src/AcDream.App/Rendering/GameWindow.cs` (inventory frame setup) | retail's gmInventoryUI resize lives in keystone.dll (no decomp); the anchor-stretch + 372..560 range matches the observed retail behavior for the dev loop. | The expand range / which elements reflow could differ from retail (e.g. retail may allow shrink-below-default); shrink is intentionally disabled for now. | retail gmInventoryUI resize (keystone.dll, no decomp); `UiNineSlicePanel` resize | +| AP-55 | The toolbar's item slots keep the hardcoded `UiItemSlot.EmptySprite = 0x060074CF` rather than resolving their cell template via attribute `0x1000000e`. Correct in outcome (the toolbar's `0x1000000e` resolves to a generic prototype whose empty media is `0x060074CF`) but not dat-resolved. | `src/AcDream.App/UI/UiItemSlot.cs` (`EmptySprite` default); `src/AcDream.App/UI/Layout/ToolbarController.cs` | The inventory lists now port the retail resolver (`ItemListCellTemplate`); the toolbar was left on its hardcoded default to avoid regressing frozen, working art. Retire when the toolbar is routed through `ItemListCellTemplate`. | If a future toolbar layout's `0x1000000e` points at a non-generic prototype, the toolbar would show stale art. | `UIElement_ItemList::InternalCreateItem` 0x004e3570; catalog `0x21000037` | +| AP-56 | `UiItemSlot` is a flat single-sprite cell; for a prototype with both a frame child and an inner icon (the 36×36 backpack prototype `0x1000033F`: frame `0x06005D9C` + inner `0x06000F6E`), only the dominant background (the frame) is drawn. Retail clones the full prototype subtree (frame + inner layered). | `src/AcDream.App/UI/UiItemSlot.cs`; `src/AcDream.App/UI/Layout/ItemListCellTemplate.cs` (`ResolveEmptySprite` single-sprite contract) | acdream's cell draws one empty sprite; replicating retail's multi-layer prototype needs a layered cell or a subtree clone. Frame-first chosen as the dominant visible art. Revisit at the visual gate / build a layered cell if the backpack slots read wrong. | The 36×36 backpack cells may lack the inner placeholder layer vs retail. | `UIElement_ItemList::InternalCreateItem` 0x004e3570; prototype `0x1000033F` | --- diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 56f7fdc5..308739d2 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -2214,6 +2214,20 @@ public sealed class GameWindow : IDisposable // Phase D.2b-B — populate the inventory from ClientObjectTable: the // "Contents of Backpack" grid + the pack-selector strip + the burden meter + // captions. Analogue of gmInventoryUI/gmBackpackUI/gm3DItemsUI::PostInit. + + // Resolve each inventory list's empty-slot art from the dat cell template + // (retail UIElement_ItemList::InternalCreateItem 0x004e3570: attr 0x1000000e -> + // catalog 0x21000037 prototype's ItemSlot_Empty). The contents grid lives in + // gm3DItemsUI (0x21000021); the side-bag + main-pack in gmBackpackUI (0x21000022). + uint contentsEmpty, sideBagEmpty, mainPackEmpty; + lock (_datLock) + { + contentsEmpty = AcDream.App.UI.Layout.ItemListCellTemplate.ResolveEmptySprite(_dats!, 0x21000021u, 0x100001C6u); + sideBagEmpty = AcDream.App.UI.Layout.ItemListCellTemplate.ResolveEmptySprite(_dats!, 0x21000022u, 0x100001CAu); + mainPackEmpty = AcDream.App.UI.Layout.ItemListCellTemplate.ResolveEmptySprite(_dats!, 0x21000022u, 0x100001C9u); + } + Console.WriteLine($"[D.2b empty-slot] contents=0x{contentsEmpty:X8} sideBag=0x{sideBagEmpty:X8} mainPack=0x{mainPackEmpty:X8}"); + _inventoryController = AcDream.App.UI.Layout.InventoryController.Bind( invLayout, Objects, playerGuid: () => _playerServerGuid, @@ -2221,7 +2235,10 @@ public sealed class GameWindow : IDisposable strength: () => LocalPlayer.GetAttribute( AcDream.Core.Player.LocalPlayerState.AttributeKind.Strength) is { } sa ? (int?)sa.Current : null, - datFont: vitalsDatFont); + datFont: vitalsDatFont, + contentsEmptySprite: contentsEmpty, + sideBagEmptySprite: sideBagEmpty, + mainPackEmptySprite: mainPackEmpty); Console.WriteLine("[D.2b-B] retail inventory window from LayoutDesc importer (0x21000023)."); }