Commit Graph
2 Commits
Author SHA1 Message Date
Brues 3c0bf3abed OnTooltipSetItem: fire via the inner invoker + restore the Lua stack
The initial fire used FUN_00702690, whose exec-context mutation (DAT_00ceeac0)
is only valid from the engine's top-level frame-script dispatch; firing it from
inside the item builder (mid-Script_Set*, and in the wild under a stack of addon
Set* wrappers) raised through the outer wrappers. Two fixes make the fire safe
for normal handlers:

- Call the inner invoker FUN_00704d50(handler, tooltip) directly. It binds the
  global `this` = tooltip and runs the handler under its own protected pcall
  (0 args), self-contained and context-independent.
- Snapshot lua_gettop before the fire and lua_settop back after, so the still-
  running outer C code sees exactly the stack it left. Without this the invoker
  left the stack shifted and the outer code / other DLLs' hooks indexed a stale
  slot -> ACCESS_VIOLATION.

Verified in-game: SetScript / HookScript / GetScript all work, `this` is the
tooltip (a1 is nil -- vanilla convention, not modern `self`), and no-op /
print / AddLine handlers fire cleanly on bag / equipped / merchant / auction.

Docs: correct the handler example to use `this` (not a `self` arg), and add the
caveat that re-entrant tooltip rebuilds from the handler (ShowCompareItem, Set*
on another tooltip) can crash in a hook-heavy client and must be deferred
(C_Timer.After(0, ...)) -- the event fires mid-build.
2026-07-02 23:18:17 -05:00
Brues 3179d1741e Add OnTooltipSetItem frame script
Backports the modern OnTooltipSetItem tooltip script to 1.12 as a real
frame script, settable via the standard SetScript / GetScript / HookScript
and firing whenever a tooltip's item is set (with the tooltip as self). Lets
addons annotate item tooltips by hooking one script instead of wrapping every
Set* method -- the destructive wrap-and-rebuild pattern (AtlasLoot's
AddSourceLine) that clobbers the tooltip's item linkage.

Fully native, two Game::HookAutoRegister co-hooks in Tooltip::SetItemEvent:

- Resolver (FUN_005295d0, the CGGameTooltip vtable script-name -> slot
  vmethod): for "OnTooltipSetItem" (a name the engine doesn't know) return a
  pointer to a C-side per-tooltip 8-byte {handler, context} cell. The tooltip
  object (alloc size 0x460) has no free slot, and the resolver's only contract
  is "return where the handler lives", so external storage behaves exactly
  like the engine's own slots -- proven by OnTooltipCleared (+0x44c).
- Item builder (FUN_0052B650, the shared funnel for SetBagItem /
  SetInventoryItem / SetHyperlink / SetMerchantItem / SetAuctionItem / ...):
  after the build, fire the handler via FUN_00702690 (the no-arg runner the
  clear uses for OnTooltipCleared). Recursion-guarded, and suppressed via
  Tooltip::SetItemEvent::Suppressor during Tooltip::Compare's internal
  equipped-item build so a handler doesn't run mid line-shift.

Item only for now; OnTooltipSetSpell / OnTooltipSetUnit follow the same
pattern with their own builders.
2026-07-02 19:41:29 -05:00