Record the connected 2026-07-13 confirmation that ACE Aetheria states 0/1/3/7 progressively reveal none, blue, blue+yellow, and all slots with the authored backgrounds and no relog. Co-Authored-By: Codex <codex@openai.com>
5.8 KiB
Retail paperdoll slot backgrounds — pseudocode
Date: 2026-07-13
Scope: the empty-cell art for all 24 equipment lists in gmPaperDollUI, including
the three quest-unlocked Aetheria sigil locations. 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 toINVENTORY_LOC/EquipMaskmapping.gmPaperDollUI::PostInit@0x004A5360(named-retail/acclient_2013_pseudo_c.txt:175232) — binds each element as a single-cellUIElement_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-map0x10000038, category5, key0x23, then clones the selectedUIElement_UIItemprototype.UIElement_ItemList::ItemList_AddItem@0x004E3F50andgmPaperDollUI::SetUIItemIntoLocation@0x004A5F90— even an empty location is represented by a real UIItem cell in its list.- Live DAT
LayoutDesc 0x21000037— each prototype's child0x1000033B, stateItemSlot_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
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 |
0x10000595 |
SigilOne / blue | 0x10000592 |
0x06006BEF |
0x10000596 |
SigilTwo / yellow | 0x10000593 |
0x06006BF0 |
0x10000597 |
SigilThree / red | 0x10000594 |
0x06006BF1 |
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 |
Aetheria unlock visibility
gmPaperDollUI::UpdateAetheria @ 0x004A3E50 reads player int quality 0x142
(PropertyInt.AetheriaBitfield, 322). RecvNotice_PlayerDescReceived @ 0x004A43E0
applies the login value, and OnQualityChanged @ 0x004A4490 reapplies it after a
live quest-completion update:
bits = player.intQuality(0x142, default=0)
blue.visible = (bits & 1) != 0
yellow.visible = (bits & 2) != 0
red.visible = (bits & 4) != 0
ACE independently confirms the property is [SendOnLogin] AetheriaBitfield = 322
and defines the same Blue=1, Yellow=2, Red=4 flags. The existing generic private
PropertyInt wire path (0x02CD) already stores the live value on the player object.
Live gate passed 2026-07-13 (edc9be30): ACE /enable-aetheria 0/1/3/7
progressively revealed none/blue/blue+yellow/all three immediately, with the correct
authored background for each color and no relog.
Port mapping
PaperdollSlotBackgroundsis the single table for element-id,EquipMask, and UIItem prototype.PaperdollControllerconsumes the same definitions for behavior, preventing the visual and equip-location maps from drifting apart.ItemListCellTemplate.ResolvePrototypeEmptySpritereuses the already-shipped inheritance-awareItemSlot_Emptyresolver instead of hardcoding surface IDs in the controller.RetailUiRuntimeand UI Studio resolve the 24 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.