acdream/docs/research/2026-07-13-retail-paperdoll-slot-backgrounds-pseudocode.md
Erik 1be18cc4db fix(paperdoll): restore authored slot backgrounds
Port the retail UIItem catalog path so every supported equipment location resolves its exact empty-state silhouette from live DAT. Share one definition table between equip behavior and presentation, wire runtime plus UI Studio, pin all 21 prototypes and surfaces, and retire AP-66.

Co-Authored-By: Codex <codex@openai.com>
2026-07-13 09:43:21 +02:00

100 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Retail paperdoll slot backgrounds — pseudocode
Date: 2026-07-13
Scope: the empty-cell art for the 21 currently supported equipment lists in
`gmPaperDollUI`. This corrects the earlier visual inference that the slots had no
per-location art. The paperdoll layout elements themselves inherit the same ItemList
base, but retail creates their cells from distinct `UIElement_UIItem` prototypes in
the shared catalog `0x21000037`.
## Retail anchors
- `gmPaperDollUI::GetLocationInfoFromElementID` @ `0x004A37F0`
(`named-retail/acclient_2013_pseudo_c.txt:173620`) — exact paperdoll element-id to
`INVENTORY_LOC`/`EquipMask` mapping.
- `gmPaperDollUI::PostInit` @ `0x004A5360`
(`named-retail/acclient_2013_pseudo_c.txt:175232`) — binds each element as a
single-cell `UIElement_ItemList`; hides only the nine armor lists in doll view.
- `UIElement_ItemList::InternalCreateItem` @ `0x004E3570`
(`named-retail/acclient_2013_pseudo_c.txt:231486`) — resolves the UIItem catalog
through enum-map `0x10000038`, category `5`, key `0x23`, then clones the selected
`UIElement_UIItem` prototype.
- `UIElement_ItemList::ItemList_AddItem` @ `0x004E3F50` and
`gmPaperDollUI::SetUIItemIntoLocation` @ `0x004A5F90` — even an empty location is
represented by a real UIItem cell in its list.
- Live DAT `LayoutDesc 0x21000037` — each prototype's child `0x1000033B`, state
`ItemSlot_Empty`, supplies the exact 32×32 RenderSurface below.
The retained `references/WorldBuilder` tree has no UI implementation. ACE/ACViewer
are interpretation aids for `EquipMask` and surface decoding; neither owns the retail
paperdoll widget behavior. The named client plus live DAT are therefore the oracle.
## Literal pseudocode
```text
PaperDoll.PostInit:
for every authored equipment-list element:
list = GetChildRecursive(elementID).DynamicCast(UIElement_ItemList)
RegisterItemListDragHandler(list, paperDoll)
UpdateItemSlotTooltip(list, empty)
hide the nine armor-location lists in doll view
ItemList.InternalCreateItem:
prototypeID = GetAttribute_Enum(ItemListCellTemplate)
catalog = GetByEnum(UIElementCatalog, LayoutDesc, UIItemCatalog)
return CreateChildElement(list, catalog, prototypeID).DynamicCast(UIElement_UIItem)
PaperDoll.SetUIItemIntoLocation(itemID, locationMask):
for each paperdoll slot whose location bit intersects locationMask:
flush that slot's one-cell item list
add itemID to that list
update its tooltip
UIItem empty draw:
draw prototype.child(0x1000033B).state(ItemSlot_Empty).image
```
## Authored slot-template table
| Paperdoll element | Equip location | UIItem prototype | Empty RenderSurface |
|---|---|---|---|
| `0x100001DA` | NeckWear | `0x10000446` | `0x06000F68` |
| `0x100001DB` | WristWearLeft | `0x10000447` | `0x06000F5D` |
| `0x100001DC` | FingerWearLeft | `0x10000448` | `0x06000F5A` |
| `0x100001DD` | WristWearRight | `0x10000449` | `0x06000F6A` |
| `0x100001DE` | FingerWearRight | `0x1000044A` | `0x06000F6B` |
| `0x100001DF` | weapon composite | `0x1000044B` | `0x06000F66` |
| `0x100001E0` | MissileAmmo | `0x1000044C` | `0x06000F5E` |
| `0x100001E1` | Shield | `0x1000044D` | `0x06000F6C` |
| `0x100001E2` | ChestWear | `0x1000044E` | `0x060032C5` |
| `0x100001E3` | UpperLegWear | `0x1000044F` | `0x060032C4` |
| `0x1000058E` | TrinketOne | `0x1000058F` | `0x06006A6C` |
| `0x100005E9` | Cloak | `0x100005EA` | `0x0600708F` |
| `0x100005AB` | HeadWear | `0x100005B4` | `0x06006D7F` |
| `0x100005AC` | ChestArmor | `0x100005B5` | `0x06006D7B` |
| `0x100005AD` | AbdomenArmor | `0x100005B6` | `0x06006D79` |
| `0x100005AE` | UpperArmArmor | `0x100005B7` | `0x06006D87` |
| `0x100005AF` | LowerArmArmor | `0x100005B8` | `0x06006D81` |
| `0x100005B0` | HandWear | `0x100005B9` | `0x06006D7D` |
| `0x100005B1` | UpperLegArmor | `0x100005BA` | `0x06006D89` |
| `0x100005B2` | LowerLegArmor | `0x100005BB` | `0x06006D83` |
| `0x100005B3` | FootWear | `0x100005BD` | `0x06006D85` |
The three Aetheria lists remain outside the current controller scope (AP-108). Their
authored backgrounds are blue/yellow/red prototypes `0x10000592..94`, but this fix
does not make those otherwise unimplemented slots reachable.
## Port mapping
- `PaperdollSlotBackgrounds` is the single table for element-id, `EquipMask`, and
UIItem prototype. `PaperdollController` consumes the same definitions for behavior,
preventing the visual and equip-location maps from drifting apart.
- `ItemListCellTemplate.ResolvePrototypeEmptySprite` reuses the already-shipped
inheritance-aware `ItemSlot_Empty` resolver instead of hardcoding surface IDs in
the controller.
- `RetailUiRuntime` and UI Studio resolve the 21 surfaces from the live DAT once at
bind time and inject them into the controller. A missing authored surface is a startup
error rather than a silently generic slot; the old inventory-grid sprite remains only
for isolated controller bindings that do not supply the paperdoll catalog map.