feat(ui): the spell-bar drop ring — retail's authored drag-accept state, and the ring exposed a real drop off-by-one
Some checks failed
Headless portability / portable-headless (ubuntu-latest) (push) Has been cancelled
Headless portability / portable-headless (windows-latest) (push) Has been cancelled
Headless portability / linux-graphical (push) Has been cancelled
Headless portability / linux-vulkan (push) Has been cancelled

The green ring is retail's own art: every UIItem cell carries an
authored DragAccept child (catalog 0x21000037, child 0x1000045A), and
the spell bar's drag-over handler (SpellCastSubMenu::OnItemListDragOver
@0x004C5990) flips it to the Accept state (0x10000040 -> surface
0x060011F9) for any spell payload. Ported through a per-slot
SetDragAcceptVisual seam + a catalog DragOverAcceptance hook; other
lists are untouched (null acceptance = neutral). A polarity error in
our older docs (Accept/Reject state ids swapped) was corrected against
three independent sources; the shipped art was always right, only the
labels lied.

The ring shares ONE landing computation with the drop
(FavoriteDropIndex) — and that requirement exposed a genuine #354
off-by-one: the empty-tail path double-applied the -1 adjustment
(retail gates it on the lift's removal @0x004C7157), landing a
reordered spell second-to-last instead of last. Fixed;
discriminator-verified both ways. AP-172 narrowed + its false
empty-tail claim corrected.

Clean-room complete solution: 11,545 passed / 4 skipped / 0 failed.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-08 20:18:51 +02:00
parent 81a9d85a1d
commit 6bb4cfa795
9 changed files with 434 additions and 34 deletions

View file

@ -141,6 +141,19 @@ internal element states `SetDragAcceptState` writes — both are real; the Layou
states and the `0x1000003x/4x` UIStateIds are the same overlay seen from the dat side vs.
the C++ side. CONFIRMED.
> **Correction 2026-08-08 (spell-bar drop-ring research):** the parenthetical
> above has the accept/reject ids SWAPPED. The true mapping is
> `ItemSlot_DragOver_Accept = 0x10000040 → 0x060011F9` and
> `ItemSlot_DragOver_Reject = 0x10000041 → 0x060011F8`, confirmed by
> DatReaderWriter's retail-derived `UIStateId` enum, by the legal/illegal
> branches of `gmPaperDollUI::HandlePaperDollDragOver` @ 0x004A3AC9/0x004A3AEB
> and `VendorSellUI::OnItemListDragOver` @ 0x004C2327/0x004C2336, and by the
> machine layout dump (`2026-06-25-retail-ui-layout-dump.json`, states
> 268435520/268435521 on elements 0x1000046D/0x1000046C). The table row's
> name→art column above was always right; only this paragraph's numeric
> pairing was inverted (and propagated into
> `2026-07-13-retail-item-drag-visuals-pseudocode.md`, corrected the same day).
### 2.3 Key methods + the update pass (`UIItem_Update`, decomp 230226)
`UIItem_Update` is the per-change refresh; the controller calls it whenever the bound

View file

@ -79,12 +79,28 @@ if target list is a container selector
target.SetDragAcceptState(0x10000046) # ItemSlot_DragOver_DropIn
# 0x060011F7 green arrow
else if target accepts an ordinary item-list placement:
target.SetDragAcceptState(0x10000041) # ItemSlot_DragOver_Accept
target.SetDragAcceptState(0x10000040) # ItemSlot_DragOver_Accept
# 0x060011F9 green circle
else:
target.SetDragAcceptState(0x10000040) # 0x060011F8 reject
target.SetDragAcceptState(0x10000041) # ItemSlot_DragOver_Reject
# 0x060011F8 reject
```
> **Correction 2026-08-08 (spell-bar drop-ring research):** the block above
> originally had the Accept/Reject numeric ids swapped (`0x10000041` labeled
> Accept, `0x10000040` labeled reject). Three primary sources agree the true
> mapping is `ItemSlot_DragOver_Accept = 0x10000040 → 0x060011F9` and
> `ItemSlot_DragOver_Reject = 0x10000041 → 0x060011F8`: DatReaderWriter's
> retail-derived `UIStateId` enum; the legal/illegal branches in
> `gmPaperDollUI::HandlePaperDollDragOver` (`AutoWearIsLegal` → 0x10000040
> @ 0x004A3AC9, else 0x10000041 @ 0x004A3AEB) and
> `VendorSellUI::OnItemListDragOver` (`DragItemAcceptable` → 0x10000040
> @ 0x004C2327, else 0x10000041 @ 0x004C2336); and the machine layout dump
> (`2026-06-25-retail-ui-layout-dump.json`: state 268435520 = 0x10000040 →
> image 0x060011F9, state 268435521 = 0x10000041 → 0x060011F8). The
> art-per-semantic mapping in the shipped code was always correct; only the
> numeric labels here were swapped.
Therefore the backpack contents grid uses the green circle; the side-bag column
and main-pack container cell use the green drop-in arrow. The selected/open
indicators remain visible while `m_elem_Icon_Ghosted` is active, so the