From 077586a0f0b993e9bd0f7500a1a26db7de7e6ae2 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 22 Jun 2026 15:33:58 +0200 Subject: [PATCH] fix(D.2b): main-pack backpack icon is 0x0600127E, not 0x060011F4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prior commit pinned 0x060011F4 from a research dat-dump of GetDIDByEnum(0x10000004,7) — it rendered as a GREEN TILE (green slot, no pack) at the visual gate. Dat-exported the candidates (AcDream.Cli dump-sprite-sheet / export-ui-sprite): 0x0600127E is the 32x32 brown backpack (user-hinted, PNG-confirmed). Swap the pinned literal; the test + AP-51 register row updated to the visually-verified id. Container type-underlay (green) + backpack base still composited via _iconIds. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/architecture/retail-divergence-register.md | 2 +- src/AcDream.App/UI/Layout/InventoryController.cs | 12 +++++++----- .../UI/Layout/InventoryControllerTests.cs | 7 ++++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/architecture/retail-divergence-register.md b/docs/architecture/retail-divergence-register.md index d578fef3..d4ecf02f 100644 --- a/docs/architecture/retail-divergence-register.md +++ b/docs/architecture/retail-divergence-register.md @@ -150,7 +150,7 @@ accepted-divergence entries (#96, #49, #50). | AP-48 | Inventory burden reads the player's wire `EncumbranceVal` (PropertyInt 5) when present — delivered by B-Wire (login PD-bundle `UpsertProperties` + live `PrivateUpdatePropertyInt 0x02CD`); `ClientObjectTable.SumCarriedBurden` remains only as a defensive fallback for when the server omits it. | `src/AcDream.App/UI/Layout/InventoryController.cs` (`RefreshBurden`); `src/AcDream.Core.Net/ObjectTableWiring.cs` (player-int route) | B-Wire ports the retail read (`CACQualities::InqLoad` reads the server value); the client sum is now fallback-only, kept defensively. CONFIRM the server actually sends EncumbranceVal at the B-Wire visual gate, then DELETE this row. | If the server omits EncumbranceVal, the bar falls back to the client sum (the original drift) until the first 0x02CD — confirm at the gate. | `CACQualities::InqLoad` 0x0058f130; ACE PropertyInt.EncumbranceVal=5 | | AP-49 | Carry-capacity augmentation (PropertyInt `0xE6`) read from the player's wire property bundle when present (B-Wire PD `UpsertProperties`); defaults to 0 (correct for un-augmented characters) when absent. | `src/AcDream.App/UI/Layout/InventoryController.cs` (`RefreshBurden`) caller of `BurdenMath.EncumbranceCapacity` | B-Wire delivers the player's full PropertyInt table (incl. `0xE6` when the server sets it) via `UpsertProperties`; aug=0 is the correct no-aug default. CONFIRM the server sends `0xE6` for an augmented char at the gate, then DELETE. | An augmented character whose server omits `0xE6` reads slightly low capacity (bar fills higher than retail); un-augmented chars are exact. | `EncumbranceSystem::EncumbranceCapacity` decomp 256393 (0x004fcc00); retail `0xE6` PropertyInt augmentation | | AP-50 | Burden meter orientation/direction set programmatically (`Vertical=true`, `FillFromBottom=true`) rather than reading retail's `m_eDirection` property from the LayoutDesc element (property id `0x6f`). | `src/AcDream.App/UI/Layout/InventoryController.cs`; `src/AcDream.App/UI/UiMeter.cs` (`Vertical`/`FillFromBottom`) | The `m_eDirection` property is not yet read by `ElementReader`; bottom-up fill is visually confirmed against retail's burden bar art. Retire when `0x6f` is wired through `ElementReader`→`DatWidgetFactory`. | Wrong fill direction (top-down instead of bottom-up, or horizontal) if a future meter whose art requires a different direction is forced through the same code path. | `UIElement_Meter::DrawChildren` @0x46fbd0; property `0x6f` (`m_eDirection`) in the LayoutDesc | -| AP-51 | Main-pack `m_topContainer` cell (element `0x100001C9`) draws the constant backpack icon via a hardcoded base-icon literal `0x060011F4` + `ItemType.Container`, rather than resolving it at runtime through `GetDIDByEnum(0x10000004, 7)`. Correct in outcome (0x060011F4 IS that map entry, dat-verified) but not dat-resolved. | `src/AcDream.App/UI/Layout/InventoryController.cs` (`Populate`, main-pack cell) | Retail's `IconData::RenderIcons` IsThePlayer branch sets `m_idIcon = GetDIDByEnum(0x10000004,7)` + `m_itemType = TYPE_CONTAINER` (a CONSTANT — the original AP-51 "equipped-pack weenie icon" premise was wrong). We pin the resolved value as a literal (cf. AP-55's toolbar empty sprite) to avoid threading a fixed-index map resolve through the icon delegate. Retire when an index-N resolve is exposed on `IconComposer`. | If the portal dat's underlay map index 7 ever changed, the hardcoded icon would go stale where a runtime resolve would track it. | `IconData::RenderIcons` IsThePlayer branch 0x0058d1ee (decomp 407546-407549); EnumIDMap 0x10000004→0x25000008 index 7 | +| AP-51 | Main-pack `m_topContainer` cell (element `0x100001C9`) draws the constant backpack icon via a hardcoded base-icon literal `0x0600127E` + `ItemType.Container` (composited over the Container type-underlay), rather than resolving it at runtime the way retail does. Sprite VISUALLY CONFIRMED (2026-06-22 live gate). | `src/AcDream.App/UI/Layout/InventoryController.cs` (`Populate`, main-pack cell) | Retail's `IconData::RenderIcons` IsThePlayer branch draws a CONSTANT backpack with `m_itemType = TYPE_CONTAINER` (the original AP-51 "equipped-pack weenie icon" premise was wrong). The exact runtime resolution is unconfirmed: a research dat-dump misreported `GetDIDByEnum(0x10000004,7)` as the green tile `0x060011F4` (which rendered green-no-pack at the gate); the real backpack `0x0600127E` was identified by dat export + user confirmation. We pin the visually-verified literal (cf. AP-55). Retire when the runtime resolve is reproduced + confirmed. | If the pinned sprite diverges from what retail resolves at runtime, the main-pack icon goes stale. | `IconData::RenderIcons` IsThePlayer branch 0x0058d1ee (decomp 407546-407549); sprite `0x0600127E` dat-exported + visually confirmed | | 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 (main pack) or 24 (side bag) when the open container's `ItemsCapacity` is absent/0. Container-switching is now wired (D.2b): clicking a side bag sends `Use 0x0036` and the grid repopulates from `ViewContents 0x0196`. This row covers only the capacity *default* when the server omits `ItemsCapacity`. | `src/AcDream.App/UI/Layout/InventoryController.cs` (`Populate`) | `ItemsCapacity` is not reliably on every ClientObject yet; 102/24 are the retail standard capacities. Retire when `ItemsCapacity` is confirmed delivered for all containers. | A non-102/24 pack shows the wrong empty-slot count — cosmetic only. | retail main-pack capacity 102; side-pack 24; ACE `ItemsCapacity`; `ViewContents 0x0196` | | 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 | diff --git a/src/AcDream.App/UI/Layout/InventoryController.cs b/src/AcDream.App/UI/Layout/InventoryController.cs index 1d86e1f1..6429dabd 100644 --- a/src/AcDream.App/UI/Layout/InventoryController.cs +++ b/src/AcDream.App/UI/Layout/InventoryController.cs @@ -225,13 +225,15 @@ public sealed class InventoryController // Main-pack cell: the player's own container — clicking it opens/selects the main pack. // Retail draws a CONSTANT backpack icon here, NOT the player's body icon: IconData::RenderIcons - // (0x0058d1ee) has an IsThePlayer() branch that overrides m_idIcon with - // GetDIDByEnum(0x10000004, 7) = 0x060011F4 and m_itemType = TYPE_CONTAINER. Compose that - // backpack base over the Container type-underlay (the player object's own IconId is the - // character body, which would render wrong here). Retires AP-51. + // (0x0058d1ee) has an IsThePlayer() branch that draws a fixed backpack with m_itemType = + // TYPE_CONTAINER. Compose that backpack base over the Container type-underlay (the player + // object's own IconId is the character body, which would render wrong here). The backpack + // RenderSurface is 0x0600127E, VISUALLY CONFIRMED at the live gate 2026-06-22 (the earlier + // 0x060011F4 from a research dat-dump of GetDIDByEnum(0x10000004,7) was a green tile, not the + // pack — the index value was misreported). Retires AP-51. if (_topContainer is not null) { - const uint PlayerPackBaseIcon = 0x060011F4u; // GetDIDByEnum(0x10000004, 7) + const uint PlayerPackBaseIcon = 0x0600127Eu; // constant main-pack backpack (visual gate) _topContainer.Flush(); var main = new UiItemSlot { SpriteResolve = _topContainer.SpriteResolve }; main.SetItem(p, _iconIds(ItemType.Container, PlayerPackBaseIcon, 0u, 0u, 0u)); diff --git a/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs b/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs index 77f4271b..366d1093 100644 --- a/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs +++ b/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs @@ -370,13 +370,14 @@ public class InventoryControllerTests objects.AddOrUpdate(new ClientObject { ObjectId = Player, IconId = 0x06001234u }); (ItemType type, uint icon)? mainPackCall = null; InventoryController.Bind(layout, objects, () => Player, - iconIds: (t, icon, _, _, _) => { if (icon == 0x060011F4u) mainPackCall = (t, icon); return 0u; }, + iconIds: (t, icon, _, _, _) => { if (icon == 0x0600127Eu) mainPackCall = (t, icon); return 0u; }, strength: () => 100, datFont: null); - // Retail IconData::RenderIcons IsThePlayer branch: GetDIDByEnum(0x10000004,7)=0x060011F4 + TYPE_CONTAINER. + // Retail draws a constant backpack over the Container type-underlay (IconData::RenderIcons + // IsThePlayer branch). The backpack RenderSurface 0x0600127E is visually confirmed (2026-06-22). Assert.NotNull(mainPackCall); Assert.Equal(ItemType.Container, mainPackCall!.Value.type); - Assert.Equal(0x060011F4u, mainPackCall.Value.icon); + Assert.Equal(0x0600127Eu, mainPackCall.Value.icon); } // Reads the text of the UiText caption child attached by the controller.