feat(D.2b): main-pack cell draws the constant backpack icon (AP-51)
The m_topContainer cell (0x100001C9) rendered blank (tex=0). Retail's IconData::RenderIcons (0x0058d1ee) has an IsThePlayer() branch that draws a CONSTANT backpack — m_idIcon = GetDIDByEnum(0x10000004, 7) = 0x060011F4, m_itemType = TYPE_CONTAINER — NOT the player's body icon (the original AP-51 "equipped-pack weenie icon" premise was wrong). Compose that base over the Container type-underlay via the existing _iconIds delegate. Verified vs decomp (407546-407549) + IconComposer.GetIcon (base=arg2, type drives underlay) + a live dat dump (map 0x25000008 index 7 = 0x060011F4). Test locks type+literal. AP-51 reworded to the residual hardcoded-vs-runtime-resolve nuance (cf. AP-55). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9dc44c3a3d
commit
c71b32f73d
3 changed files with 26 additions and 2 deletions
|
|
@ -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-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-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-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`) uses a placeholder/empty icon (`tex=0u`), not a weenie-driven backpack icon. | `src/AcDream.App/UI/Layout/InventoryController.cs` (`Populate`) | The player's own container entity has no item `IconId` on the client (it's a character, not an item); retail uses the equipped-pack's icon via the character's equipped-pack `CreateObject`. The equipped-pack DID path is deferred to Sub-phase C. | Main-pack cell renders blank (no icon) where retail shows the equipped backpack art — cosmetic gap visible when F12 is open. | `gmBackpackUI::PostInit` @0x4a8520 (m_topContainer bind); `CreateObject` weenie icon path |
|
| 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-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-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-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 |
|
| 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 |
|
||||||
|
|
|
||||||
|
|
@ -224,11 +224,17 @@ public sealed class InventoryController
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main-pack cell: the player's own container — clicking it opens/selects the main pack.
|
// 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.
|
||||||
if (_topContainer is not null)
|
if (_topContainer is not null)
|
||||||
{
|
{
|
||||||
|
const uint PlayerPackBaseIcon = 0x060011F4u; // GetDIDByEnum(0x10000004, 7)
|
||||||
_topContainer.Flush();
|
_topContainer.Flush();
|
||||||
var main = new UiItemSlot { SpriteResolve = _topContainer.SpriteResolve };
|
var main = new UiItemSlot { SpriteResolve = _topContainer.SpriteResolve };
|
||||||
main.SetItem(p, 0u);
|
main.SetItem(p, _iconIds(ItemType.Container, PlayerPackBaseIcon, 0u, 0u, 0u));
|
||||||
main.Clicked = () => OpenContainer(p);
|
main.Clicked = () => OpenContainer(p);
|
||||||
_topContainer.AddItem(main);
|
_topContainer.AddItem(main);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -361,6 +361,24 @@ public class InventoryControllerTests
|
||||||
Assert.True(top.GetItem(0)!.IsOpenContainer); // default open container = main pack
|
Assert.True(top.GetItem(0)!.IsOpenContainer); // default open container = main pack
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MainPackCell_requestsConstantBackpackIcon_notPlayerBodyIcon() // AP-51 retire
|
||||||
|
{
|
||||||
|
var (layout, _, _, _, _, _, _, _) = BuildLayout();
|
||||||
|
var objects = new ClientObjectTable();
|
||||||
|
// Give the player a (wrong) body icon to prove the main-pack cell does NOT use it.
|
||||||
|
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; },
|
||||||
|
strength: () => 100, datFont: null);
|
||||||
|
|
||||||
|
// Retail IconData::RenderIcons IsThePlayer branch: GetDIDByEnum(0x10000004,7)=0x060011F4 + TYPE_CONTAINER.
|
||||||
|
Assert.NotNull(mainPackCall);
|
||||||
|
Assert.Equal(ItemType.Container, mainPackCall!.Value.type);
|
||||||
|
Assert.Equal(0x060011F4u, mainPackCall.Value.icon);
|
||||||
|
}
|
||||||
|
|
||||||
// Reads the text of the UiText caption child attached by the controller.
|
// Reads the text of the UiText caption child attached by the controller.
|
||||||
private static string CaptionText(UiElement host)
|
private static string CaptionText(UiElement host)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue