From 8c719cd3e9d12840e54ab25741bc661ffc98e976 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 22 Jun 2026 11:05:12 +0200 Subject: [PATCH] =?UTF-8?q?feat(ui):=20D.2b=20empty-slot=20art=20=E2=80=94?= =?UTF-8?q?=20InventoryController=20applies=20per-list=20empty=20sprites?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- .../UI/Layout/InventoryController.cs | 19 ++++++++++++++++--- .../UI/Layout/InventoryControllerTests.cs | 13 +++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/AcDream.App/UI/Layout/InventoryController.cs b/src/AcDream.App/UI/Layout/InventoryController.cs index a96537b3..81007e89 100644 --- a/src/AcDream.App/UI/Layout/InventoryController.cs +++ b/src/AcDream.App/UI/Layout/InventoryController.cs @@ -62,7 +62,10 @@ public sealed class InventoryController Func playerGuid, Func iconIds, Func strength, - UiDatFont? datFont) + UiDatFont? datFont, + uint contentsEmptySprite, + uint sideBagEmptySprite, + uint mainPackEmptySprite) { _objects = objects; _playerGuid = playerGuid; @@ -101,6 +104,12 @@ public sealed class InventoryController _containerList.CellHeight = BackpackCellPx; } + // Per-list empty-slot art, resolved from the dat cell template (retail attr 0x1000000e + // -> catalog 0x21000037 prototype's ItemSlot_Empty). 0 = leave the UiItemSlot default. + if (_contentsGrid is not null) _contentsGrid.CellEmptySprite = contentsEmptySprite; + if (_containerList is not null) _containerList.CellEmptySprite = sideBagEmptySprite; + if (_topContainer is not null) _topContainer.CellEmptySprite = mainPackEmptySprite; + // Burden meter: vertical 11×58 bar (gmBackpackUI m_burdenMeter, retail direction 4). _burdenMeter = layout.FindElement(BurdenMeterId) as UiMeter; if (_burdenMeter is not null) @@ -132,8 +141,12 @@ public sealed class InventoryController Func playerGuid, Func iconIds, Func strength, - UiDatFont? datFont) - => new InventoryController(layout, objects, playerGuid, iconIds, strength, datFont); + UiDatFont? datFont, + uint contentsEmptySprite = 0u, + uint sideBagEmptySprite = 0u, + uint mainPackEmptySprite = 0u) + => new InventoryController(layout, objects, playerGuid, iconIds, strength, datFont, + contentsEmptySprite, sideBagEmptySprite, mainPackEmptySprite); private void OnObjectChanged(ClientObject o) { if (Concerns(o)) Populate(); } private void OnObjectMoved(ClientObject o, uint from, uint to) diff --git a/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs b/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs index b44f0cd2..fc2a5de2 100644 --- a/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs +++ b/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs @@ -240,6 +240,19 @@ public class InventoryControllerTests Assert.Equal(0u, containers.GetItem(2)!.ItemId); // empty frame } + [Fact] + public void Empty_sprites_are_applied_to_the_three_lists() + { + var (layout, grid, containers, top, _, _, _, _) = BuildLayout(); + InventoryController.Bind(layout, new ClientObjectTable(), () => Player, + iconIds: (_, _, _, _, _) => 0u, strength: () => 100, datFont: null, + contentsEmptySprite: 0x06004D20u, sideBagEmptySprite: 0x06005D9Cu, mainPackEmptySprite: 0x06005D9Cu); + + Assert.Equal(0x06004D20u, grid.GetItem(0)!.EmptySprite); // a padded empty contents cell + Assert.Equal(0x06005D9Cu, containers.GetItem(0)!.EmptySprite); // a padded empty side-bag cell + Assert.Equal(0x06005D9Cu, top.GetItem(0)!.EmptySprite); // the main-pack cell + } + // Reads the text of the UiText caption child attached by the controller. private static string CaptionText(UiElement host) {