Keep retail's underlay-free m_pDragIcon separate from the full cell icon and reveal the authored ghost mesh on physical source cells for the complete drag lifecycle. This removes the backpack backing from the cursor and retires AP-47. Co-Authored-By: Codex <codex@openai.com>
3.4 KiB
3.4 KiB
Retail item drag visuals — pseudocode
Date: 2026-07-13
Scope: the cursor graphic and source-cell visual while dragging an item from an
item list. This is a focused continuation of
2026-06-16-ui-item-slot-icon-dragdrop-spine-deep-dive.md.
Retail anchors
IconData::RenderIcons@0x0058d180(named-retail/acclient_2013_pseudo_c.txt:407524)UIElement_ItemList::PrepareDragIcon@0x004e2a50(named-retail/acclient_2013_pseudo_c.txt:230765)UIElement_ItemList::ItemList_BeginDrag@0x004e32d0(named-retail/acclient_2013_pseudo_c.txt:231350)UIElement_UIItem::SetWaitingState@0x004e11b0(named-retail/acclient_2013_pseudo_c.txt:229190)ACCWeenieObject::SetWaitingState@0x0058c0d0(named-retail/acclient_2013_pseudo_c.txt:406260)UIElement_UIItemlayout childm_elem_Icon_Ghosted = 0x10000349; the shared UIItem catalog0x21000037gives it DirectState surface0x0600109A.
The live DAT catalog and the named retail code agree. The in-tree WorldBuilder reference has no retained UI/item-list implementation to compare for this path.
Literal pseudocode
IconData.RenderIcons(item):
destroy old m_pIcon and m_pDragIcon
dragSurface = transparent 32 x 32
blit item.baseIcon onto dragSurface using normal blit
blit item.customOverlay onto dragSurface using alpha blit
replace pure-white dragSurface pixels from the effect-color surface
m_pDragIcon = Graphic(dragSurface)
cellSurface = transparent 32 x 32
blit typeDefaultUnderlay onto cellSurface using normal blit
blit item.customUnderlay onto cellSurface using alpha blit
blit dragSurface onto cellSurface using alpha blit
m_pIcon = Graphic(cellSurface)
ItemList.PrepareDragIcon(cell):
clear cell.m_dragIcon image
object = GetWeenieObject(cell.itemID)
set cell.m_dragIcon image to object.GetDragIcon() # m_pDragIcon, not m_pIcon
ItemList.ItemList_BeginDrag(cell):
if PrepareDragIcon(cell) failed:
return
if list is not vendor, salvage, or shortcut list:
cell.SetWaitingState(true)
StartDragAndDrop(cell.m_dragIcon, hotspot = 16,16)
UIItem.SetWaitingState(waiting):
object.SetWaitingState(waiting)
if cell is not unghostable:
set m_elem_Icon_Ghosted visible = waiting
on drag release/cancel:
clear source waiting state
hide m_elem_Icon_Ghosted
Port mapping
IconComposercaches the underlay-free composite separately and exposesGetDragIcon; the ordinaryGetIconstill layers the two underlays beneath the same cached drag pixels.UiItemSlotstoresIconTextureandDragIconTextureseparately.GetDragGhostreturns the latter, whileOnDrawkeeps the former in the source cell.UiRootowns the generic source-active lifecycle, including release outside UI and source-subtree removal.UiItemSlotmaps that lifecycle to the retail waiting visual for physical lists; shortcut aliases remain unghosted.UiItemSlot.WaitingSpriteis the authored0x0600109Amesh, not a procedural tint.
Conformance coverage
DragDropSpineTests.GetDragGhost_prefersDedicatedUnderlayFreeTextureDragDropSpineTests.InventoryDrag_ghostsSourceUntilReleaseDragDropSpineTests.ShortcutDrag_doesNotGhostSourceUiItemSlotTests.DefaultWaitingSprite_isRetailGhostMesh- Existing
IconComposerTests.TwoStageWithEffect_copiesTilePixelBeforeUnderlaylocks the two-stage composition order.