diff --git a/.gitignore b/.gitignore index 06727ec..3610e1c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ .vscode .idea NUL -temp \ No newline at end of file +temp +Tooltip_OG.lua +ItemButton_OG.lua \ No newline at end of file diff --git a/Core/ClamOpener.lua b/Core/ClamOpener.lua index 9af5449..bc9335c 100644 --- a/Core/ClamOpener.lua +++ b/Core/ClamOpener.lua @@ -22,6 +22,8 @@ local QUIET_DELAY = 0.5 -- seconds of silence after a *_CLOSED event before aut local running = false local silentRun = false local pendingToken = 0 -- bumped on every auto-trigger and on LOOT_OPENED to cancel stale timers +local waitingForLoot = false +local OpenNext -- forward declaration; defined after OnClamLootClosed -- Returns true if any blocking window (loot/mail/trade/merchant/bank/auction) -- is currently open. We don't want to UseContainerItem while these are active -- @@ -65,22 +67,33 @@ end local function StopRun(reason) if not running then return end running = false - -- Only drop the per-run UI_ERROR_MESSAGE listener; the persistent auto- - -- trigger events stay registered. addon.Modules.Events:UnregisterOwner("ClamOpener_Run") + addon.Modules.Events:UnregisterOwner("ClamOpener_LootWait") + waitingForLoot = false if reason and not silentRun then addon:Print(reason) end silentRun = false end -local function OpenNext() +-- waitingForLoot: set between UseContainerItem and the LOOT_CLOSED that confirms +-- the clam loot window has fully closed, preventing any concurrent UseContainerItem. +local function OnClamLootClosed() + -- Unregister this one-shot listener immediately. + addon.Modules.Events:UnregisterOwner("ClamOpener_LootWait") + waitingForLoot = false if not running then return end + -- Small delay to let the client fully settle before the next open. + Guda_ScheduleTimer(OPEN_DELAY, OpenNext) +end + +OpenNext = function() + if not running then return end + if waitingForLoot then return end -- Never use a clam while something else is in-flight: cursor busy, -- a blocking window open, or a server-side loot still active. Any of - -- these + UseContainerItem races the client loot state machine and - -- can soft-lock the loot UI ("too far away" greyed-out items). + -- these + UseContainerItem races the open window or get queued and lost. if CursorHasItem() or IsBlockingWindowOpen() or (GetNumLootItems and GetNumLootItems() > 0) then @@ -94,8 +107,11 @@ local function OpenNext() return end + -- Arm the one-shot LOOT_CLOSED listener before calling UseContainerItem + -- so we cannot miss the event even if it fires in the same frame. + waitingForLoot = true + addon.Modules.Events:Register("LOOT_CLOSED", OnClamLootClosed, "ClamOpener_LootWait") UseContainerItem(bagID, slotID) - Guda_ScheduleTimer(OPEN_DELAY, OpenNext) end -- Stop on UI_ERROR_MESSAGE (e.g. inventory full). UseContainerItem fires this diff --git a/Core/Tooltip.lua b/Core/Tooltip.lua index add1d2c..027b7e2 100644 --- a/Core/Tooltip.lua +++ b/Core/Tooltip.lua @@ -483,15 +483,14 @@ function Tooltip:Initialize() local oldSetHyperlink = GameTooltip.SetHyperlink function GameTooltip:SetHyperlink(link) return WithDeferredMoney(self, function() + -- The native Blizzard SetHyperlink only accepts a bare "item:ID:e:e:e" string. + -- Any addon that captured the native before Guda loaded (e.g. AtlasLoot) will + -- call it directly with whatever we pass here, so we MUST strip the color codes + -- and |H...|h wrapper before forwarding. Passing the full colored link causes + -- "unknown link type" in those addons. local _, _, inner = string.find(link or "", "|H(.+)|h") - local forwarded = link - local itemLinkForCounts = link - if inner then - forwarded = inner - if strfind(inner, "^item:") then - itemLinkForCounts = inner - end - end + local forwarded = inner or link + local itemLinkForCounts = inner or link local ret = oldSetHyperlink(self, forwarded) if itemLinkForCounts and strfind(itemLinkForCounts, "item:") then Tooltip:AddInventoryInfo(self, itemLinkForCounts) @@ -653,4 +652,4 @@ function Tooltip:Initialize() end) addon:Print("Tooltip integration enabled - Inventory displays above vendor price") -end \ No newline at end of file +end diff --git a/UI/ItemButton.lua b/UI/ItemButton.lua index 5755b18..c0c2058 100644 --- a/UI/ItemButton.lua +++ b/UI/ItemButton.lua @@ -263,6 +263,19 @@ local function IsInCategoryView(isBank) return (addon.Modules.DB:GetSetting(key) or "single") == "category" end +-- Set a tooltip from an item link, passing the BARE "item:ID:e:e:e" form rather +-- than the full colored link. The 1.12 native GameTooltip:SetHyperlink only +-- accepts the bare form; passing a full |cff..|Hitem:..|h[Name]|h|r link can +-- reach a bare-only native further down a re-hooked SetHyperlink chain (e.g. when +-- AtlasLoot / WoWTranslate / SuperCleverRoidMacros have re-hooked in an order that +-- bypasses Guda's own stripping hook) and throw "unknown link type" in that addon. +-- Always route Guda-originated SetHyperlink calls through this helper. +local function GudaSetTooltipHyperlink(tooltip, link) + if not link then return end + local _, _, bare = string.find(link, "|H(item:[^|]+)|h") + tooltip:SetHyperlink(bare or link) +end + -- Auto-clear cursor tracking when cursor is truly empty, and notify BagFrame -- of drag-state transitions. In 1.12 CURSOR_UPDATE is unreliable for item -- pickups, so we also poll CursorHasItem() on a throttled OnUpdate — @@ -2449,7 +2462,7 @@ function Guda_ItemButton_OnEnter(self) -- Read-only / other character mailbox OR current character mailbox when closed GameTooltip.GudaViewedCharacter = self.otherChar or currentPlayerName if self.itemData.link then - GameTooltip:SetHyperlink(self.itemData.link) + GudaSetTooltipHyperlink(GameTooltip, self.itemData.link) else GameTooltip:SetHyperlink("item:" .. self.itemData.itemID .. ":0:0:0") end @@ -2504,7 +2517,7 @@ function Guda_ItemButton_OnEnter(self) elseif self.otherChar or self.isReadOnly then GameTooltip.GudaViewedCharacter = self.otherChar if self.itemData and self.itemData.link then - local ok = pcall(GameTooltip.SetHyperlink, GameTooltip, self.itemData.link) + local ok = pcall(GudaSetTooltipHyperlink, GameTooltip, self.itemData.link) if not ok then GameTooltip:Hide() return @@ -2521,13 +2534,20 @@ function Guda_ItemButton_OnEnter(self) GameTooltip:SetBagItem(self.bagID, self.slotID) elseif self.itemData and self.itemData.link then -- Bank is closed - use cached link - GameTooltip:SetHyperlink(self.itemData.link) + GudaSetTooltipHyperlink(GameTooltip, self.itemData.link) end elseif self.bagID == -2 then - -- Keyring: SetBagItem might be unreliable for -2 in some 1.12.1 environments, fallback to hyperlink if needed + -- Keyring (bag -2): in 1.12 the native SetBagItem builds the keyring tooltip + -- by internally calling SetHyperlink with the FULL colored link + -- (|cff..|Hitem:..|h[Name]|h|r). When other addons (AtlasLoot, WoWTranslate) + -- have re-hooked SetHyperlink in an order that bypasses Guda's stripping, that + -- full link reaches a hook whose captured original is the raw Blizzard + -- SetHyperlink — which only accepts the BARE "item:ID:0:0:0" form and otherwise + -- throws "unknown link type". We avoid that internal path entirely by calling + -- SetHyperlink ourselves with the bare form (GudaSetTooltipHyperlink extracts it). local link = GetContainerItemLink(self.bagID, self.slotID) if link then - GameTooltip:SetHyperlink(link) + GudaSetTooltipHyperlink(GameTooltip, link) else GameTooltip:SetBagItem(self.bagID, self.slotID) end @@ -2636,4 +2656,4 @@ function Guda_ItemButton_OnLeave(self) Guda_BagFrame_ClearBagButtonHighlight() end end -end \ No newline at end of file +end