From 1be7e65fad75a516e618ec8fdef3a29eb177730f Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 21 Jun 2026 21:21:20 +0200 Subject: [PATCH] =?UTF-8?q?feat(ui):=20D.2b=20inventory=20finish=20?= =?UTF-8?q?=E2=80=94=20contents=20grid=20shows=20full=20main-pack=20capaci?= =?UTF-8?q?ty?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The contents grid now pads empty slot frames up to the main-pack capacity (player ItemsCapacity, default 102 per retail "up to 102 items"), so the pack reads like retail's fixed 102-slot grid you scroll through — not just the loose items. Mirrors the side-bag column padding. Three existing grid tests updated for the now-padded count; new test covers the 102-pad. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../UI/Layout/InventoryController.cs | 13 +++++++++ .../UI/Layout/InventoryControllerTests.cs | 27 ++++++++++++++++--- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/AcDream.App/UI/Layout/InventoryController.cs b/src/AcDream.App/UI/Layout/InventoryController.cs index 01de0aea..a96537b3 100644 --- a/src/AcDream.App/UI/Layout/InventoryController.cs +++ b/src/AcDream.App/UI/Layout/InventoryController.cs @@ -36,6 +36,7 @@ public sealed class InventoryController private const float ContentsCellPx = 32f; // gm3DItemsUI grid (192x96 = 6x3 of 32px) private const float BackpackCellPx = 36f; // gmBackpackUI column cells (0x100001C9/CA = 36px) private const int SideBagSlots = 7; // 0x100001CA is 36x252 = 7 slots + private const int MainPackSlots = 102; // retail main-pack capacity (Wiki: "up to 102 items") private readonly ClientObjectTable _objects; private readonly Func _playerGuid; @@ -179,6 +180,18 @@ public sealed class InventoryController list.AddItem(cell); } + // Contents grid: show the FULL main-pack capacity (default 102) — occupied slots + empty + // frames — so the pack reads like retail (a fixed 102-slot grid you scroll), not just the + // loose items. The grid shows the SELECTED pack (default = the main pack / player); side-pack + // contents are a container-switch (later). Capacity = the player's ItemsCapacity, else 102. + if (_contentsGrid is not null) + { + int cap = _objects.Get(p)?.ItemsCapacity ?? 0; + int slots = cap > 0 ? cap : MainPackSlots; + while (_contentsGrid.GetNumUIItems() < slots) + _contentsGrid.AddItem(new UiItemSlot { SpriteResolve = _contentsGrid.SpriteResolve }); + } + // Side-bag column: pad with empty slot frames up to the player's container capacity // (clamped to the 7-slot column) so it reads like retail (bags on top, empty frames // below) rather than one lone cell. Falls back to the full 7 slots when capacity is diff --git a/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs b/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs index 0fa5d270..b44f0cd2 100644 --- a/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs +++ b/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs @@ -80,9 +80,10 @@ public class InventoryControllerTests Bind(layout, objects); - Assert.Equal(2, grid.GetNumUIItems()); // 2 loose + Assert.Equal(102, grid.GetNumUIItems()); // 2 loose + 100 empty (main-pack capacity) Assert.Equal(0xAu, grid.GetItem(0)!.ItemId); Assert.Equal(0xBu, grid.GetItem(1)!.ItemId); + Assert.Equal(0u, grid.GetItem(2)!.ItemId); // padded empty Assert.Equal(7, containers.GetNumUIItems()); // 1 side bag + 6 empty (no capacity → 7-slot column) Assert.Equal(0xCu, containers.GetItem(0)!.ItemId); Assert.Equal(0u, containers.GetItem(1)!.ItemId); // padded empty frame @@ -101,7 +102,7 @@ public class InventoryControllerTests Bind(layout, objects); - Assert.Equal(1, grid.GetNumUIItems()); // only the loose item + Assert.Equal(102, grid.GetNumUIItems()); // 1 loose + 101 empty (main-pack capacity) Assert.Equal(0xAu, grid.GetItem(0)!.ItemId); Assert.Equal(7, containers.GetNumUIItems()); // 7 empty slots (no bags; the equipped item isn't here) Assert.Equal(0u, containers.GetItem(0)!.ItemId); @@ -123,12 +124,13 @@ public class InventoryControllerTests var (layout, grid, _, _, _, _, _, _) = BuildLayout(); var objects = new ClientObjectTable(); Bind(layout, objects); - Assert.Equal(0, grid.GetNumUIItems()); + Assert.Equal(102, grid.GetNumUIItems()); // empty grid still shows the full 102-slot pack + Assert.Equal(0u, grid.GetItem(0)!.ItemId); // slot 0 empty before the add objects.Ingest(new WeenieData(0xA, "Sword", ItemType.MeleeWeapon, 1, 0, 0, 0, 0, null, null, null, 5, Player, null, null, null, null, null, null, null, null, null)); - Assert.Equal(1, grid.GetNumUIItems()); + Assert.Equal(102, grid.GetNumUIItems()); // still 102; the item fills slot 0 Assert.Equal(0xAu, grid.GetItem(0)!.ItemId); } @@ -205,6 +207,23 @@ public class InventoryControllerTests Assert.Same(grid.Scroll, bar.Model); // the bar drives the grid's scroll } + [Fact] + public void Contents_grid_pads_empty_slots_to_main_pack_capacity() + { + // The grid shows the full main-pack capacity (default 102) — occupied + empty — so the + // pack reads like retail's fixed 102-slot grid you scroll, not just the loose items. + var (layout, grid, _, _, _, _, _, _) = BuildLayout(); + var objects = new ClientObjectTable(); + objects.AddOrUpdate(new ClientObject { ObjectId = Player, ItemsCapacity = 102 }); + SeedContained(objects, 0xA, Player, slot: 0, burden: 5); // one loose item + + Bind(layout, objects); + + Assert.Equal(102, grid.GetNumUIItems()); // 1 item + 101 empty frames = 102 + Assert.Equal(0xAu, grid.GetItem(0)!.ItemId); + Assert.Equal(0u, grid.GetItem(50)!.ItemId); // a padded empty slot + } + [Fact] public void Side_bag_column_pads_empty_slots_up_to_capacity() {