diff --git a/Core.lua b/Core.lua index 2bb068c..0bff412 100644 --- a/Core.lua +++ b/Core.lua @@ -4286,104 +4286,8 @@ function CleveRoids.OnUpdate(self) end end --- Initialize the nested table for the GameTooltip hooks if it doesn't exist -if not CleveRoids.Hooks.GameTooltip then CleveRoids.Hooks.GameTooltip = {} end - --- Save the original GameTooltip.SetAction function before we override it -CleveRoids.Hooks.GameTooltip.SetAction = GameTooltip.SetAction - --- Now, define our custom version of the function -function GameTooltip.SetAction(self, slot) - if not slot then return end - local actions = CleveRoids.GetAction(slot) - - -- If this is our macro but has no active action, show just the macro name - if actions and not actions.active then - local macroName = GetActionText(slot) - if macroName then - GameTooltip:SetText(macroName) - GameTooltip:Show() - return - end - end - - local action_to_display_info = nil - if actions then - -- Only show spell/item tooltip when there's an active action - if actions.active then - action_to_display_info = actions.active - end - end - - if action_to_display_info and action_to_display_info.action then - local action_name = action_to_display_info.action - - -- NEW: Check if action is a slot ID for tooltip - local slotId = tonumber(action_name) - if slotId and slotId >= 1 and slotId <= 19 then - -- Use the more specific SetInventoryItem function to prevent conflicts with other addons. - GameTooltip:SetInventoryItem("player", slotId) - GameTooltip:Show() - return - end - -- End new logic - - local current_spell_data = CleveRoids.GetSpell(action_name) - if current_spell_data and current_spell_data.id then - GameTooltip:SetSpellByID(current_spell_data.id) - GameTooltip:Show() - return - end - - local current_item_data = CleveRoids.GetItem(action_name) - if current_item_data then - -- Use specific functions based on where the item is located. - if current_item_data.inventoryID then - GameTooltip:SetInventoryItem("player", current_item_data.inventoryID) - elseif current_item_data.bagID and current_item_data.slot then - GameTooltip:SetBagItem(current_item_data.bagID, current_item_data.slot) - else - -- Fallback to the original method if location is unknown. - GameTooltip:SetHyperlink(current_item_data.link) - end - GameTooltip:Show() - return - end - - if action_to_display_info.macro and type(action_to_display_info.macro) == "table" then - local nested_action_info = action_to_display_info.macro - local nested_action_name = nested_action_info.action - - current_spell_data = CleveRoids.GetSpell(nested_action_name) - if current_spell_data and current_spell_data.id then - if current_spell_data.spellSlot and current_spell_data.bookType then - GameTooltip:SetSpell(current_spell_data.spellSlot, current_spell_data.bookType) - else - GameTooltip:SetSpellByID(current_spell_data.id) - end - GameTooltip:Show() - return - end - - current_item_data = CleveRoids.GetItem(nested_action_name) - if current_item_data then - if current_item_data.inventoryID then - GameTooltip:SetInventoryItem("player", current_item_data.inventoryID) - elseif current_item_data.bagID and current_item_data.slot then - GameTooltip:SetBagItem(current_item_data.bagID, current_item_data.slot) - else - GameTooltip:SetHyperlink(current_item_data.link) - end - GameTooltip:Show() - return - end - end - end - - -- If none of our custom logic handled it, call the original function we saved earlier. - CleveRoids.Hooks.GameTooltip.SetAction(self, slot) -end - +-- Not a display override: this invalidates our per-slot caches when an action is +-- dragged off a slot, so it stays regardless of who draws the button. CleveRoids.Hooks.PickupAction = PickupAction function PickupAction(slot) if not slot then return end @@ -4393,224 +4297,14 @@ function PickupAction(slot) return CleveRoids.Hooks.PickupAction(slot) end -CleveRoids.Hooks.ActionHasRange = ActionHasRange -function ActionHasRange(slot) - if not slot then return nil end - local actions = CleveRoids.GetAction(slot) - -- Only override for our macros with #showtooltip - if actions and actions.tooltip then - -- If we have an active action with valid range data, use it - if actions.active then - if actions.active.inRange ~= -1 then - return 1 -- Has range check with valid data - else - -- For channeled spells (inRange == -1), try proxy slot lookup - local spellName = actions.active.spell and actions.active.spell.name - local proxySlot = spellName and CleveRoids.GetProxyActionSlot(spellName) - if proxySlot then - return CleveRoids.Hooks.ActionHasRange(proxySlot) - end - end - end - -- FIX: Even without actions.active, if the macro has potential spell actions, - -- return 1 to ensure pfUI calls IsActionInRange (which will do proper checks) - -- This fixes Super macros where actions.active might not be populated yet - if actions.tooltip.spell or actions.tooltip.type == "spell" then - return 1 -- Tooltip is a spell - has range - end - -- Check if any action in the list is a spell - if actions.list then - for i = 1, table.getn(actions.list) do - local action = actions.list[i] - if action and (action.spell or action.type == "spell") then - return 1 -- Has at least one spell action - end - end - end - end - -- Not a macro we're tracking - pass through to original - return CleveRoids.Hooks.ActionHasRange(slot) -end - -CleveRoids.Hooks.IsActionInRange = IsActionInRange -function IsActionInRange(slot, unit) - if not slot then return nil end - local actions = CleveRoids.GetAction(slot) - -- Only override for our macros with #showtooltip - if actions and actions.tooltip then - -- If we have an active spell action, use its range data - if actions.active and actions.active.type == "spell" then - if actions.active.inRange ~= -1 then - return actions.active.inRange - else - -- For channeled spells (inRange == -1), try proxy slot lookup - local spellName = actions.active.spell and actions.active.spell.name - local proxySlot = spellName and CleveRoids.GetProxyActionSlot(spellName) - if proxySlot then - return CleveRoids.Hooks.IsActionInRange(proxySlot, unit) - end - end - end - -- FIX: If no active action but macro has tooltip spell, check its range directly - -- This ensures range coloring works even when actions.active hasn't been populated - if not actions.active and actions.tooltip.spell then - local spell = actions.tooltip.spell - local spellName = spell.name - if spellName and IsSpellInRange then - local targetUnit = unit or "target" - if UnitExists(targetUnit) then - -- FIX: Wrap in pcall - spell might not be in spellbook (items, other class spells, etc.) - local ok, result = pcall(IsSpellInRange, spellName, targetUnit) - if ok then - if result == 0 then - return 0 -- Out of range - elseif result == 1 then - return 1 -- In range - end - end - end - end - -- Try proxy slot as fallback - local proxySlot = spellName and CleveRoids.GetProxyActionSlot(spellName) - if proxySlot then - return CleveRoids.Hooks.IsActionInRange(proxySlot, unit) - end - end - end - -- Not a macro we're tracking - pass through to original - return CleveRoids.Hooks.IsActionInRange(slot, unit) -end - +-- These two aliases outlived the overrides they were saved for: ClassicAPI now +-- draws the buttons, but callers still need the unmodified globals, which is all +-- these names are now. IsReactiveUsable (Conditionals.lua) reads the raw usable +-- state of a proxy slot; DeferStopAttack and the auto-attack drift check read the +-- true current-action state, which the old override could not report because it +-- only echoed our cached flag. CleveRoids.Hooks.OriginalIsUsableAction = IsUsableAction -CleveRoids.Hooks.IsUsableAction = IsUsableAction -function IsUsableAction(slot, unit) - if not slot then return nil, nil end - local actions = CleveRoids.GetAction(slot) - - -- If this is one of our macros AND it uses #showtooltip - if actions and actions.tooltip then - -- IMPORTANT: Only override usability when #showtooltip is present - -- Macros without #showtooltip should use default game behavior - if actions.active then - -- We have an active action - return its usable state - -- FIX: Convert oom boolean to number (1/nil) for Blizzard's button code - local oomValue = actions.active.oom and 1 or nil - return actions.active.usable, oomValue - else - -- FIX: If no active action but tooltip has a spell, check its usability directly - -- This ensures usability coloring works even when actions.active hasn't been populated - if actions.tooltip.spell then - local spell = actions.tooltip.spell - local spellName = spell.name - if spellName and IsSpellUsable then - -- FIX: Wrap in pcall - spell might not be in spellbook (items, other class spells, etc.) - local ok, usable, notEnoughMana = pcall(IsSpellUsable, spellName) - if ok then - if usable == 1 then - return 1, nil -- Usable - elseif notEnoughMana == 1 then - return nil, 1 -- Out of mana - else - return nil, nil -- Not usable (wrong stance, etc.) - end - end - end - -- Fallback: check if spell has a cost and compare to current mana - if spell.cost and spell.cost > 0 then - local currentMana = UnitMana("player") - if currentMana < spell.cost then - return nil, 1 -- Out of mana - end - end - -- Default to usable if we can't determine otherwise - return 1, nil - elseif actions.tooltip.item then - -- FIX: Handle item tooltips - items are always usable unless on cooldown - -- The cooldown is handled separately by GetActionCooldown hook - return 1, nil - end - -- No tooltip spell or item - this macro's conditionals all failed - -- Return nil to make the icon dark - return nil, nil - end - else - -- Not our macro OR no #showtooltip - use game's default behavior - return CleveRoids.Hooks.IsUsableAction(slot, unit) - end -end - CleveRoids.Hooks.IsCurrentAction = IsCurrentAction -function IsCurrentAction(slot) - if not slot then return nil end - local actions = CleveRoids.GetAction(slot) - - -- When no action is active (all conditionals failed), don't check tooltip's "current" status - if actions and not actions.active and not actions.explicitTooltip and actions.list and table.getn(actions.list) > 0 then - return CleveRoids.Hooks.IsCurrentAction(slot) - end - - -- Use the same priority as GetActionTexture: active first, then tooltip - local actionToCheck = (actions and actions.active) or (actions and actions.tooltip) - - if not actionToCheck then - return CleveRoids.Hooks.IsCurrentAction(slot) - else - local name - if actionToCheck.spell then - if CleveRoids.IsAutoAttackSpell(actionToCheck.spell) then - return CleveRoids.CurrentSpell.autoAttack and 1 or nil - end - - local rank = actionToCheck.spell.rank or actionToCheck.spell.highest.rank - name = actionToCheck.spell.name..(rank and ("("..rank..")")) - - -- Check if this spell is currently queued or being cast via Nampower - -- Get spell ID for comparison - local spellId = actionToCheck.spell.id - if not spellId and GetSpellIdForName then - spellId = GetSpellIdForName(name) - end - - if spellId then - -- Prefer GetCastInfo (Nampower 2.18+) for cleaner API - if GetCastInfo then - local ok, info = pcall(GetCastInfo) - if ok and info and info.spellId == spellId then - -- Spell is actively being cast/channeled - return true - end - end - - -- Also check GetCurrentCastingInfo for queued spell detection - if GetCurrentCastingInfo then - local castId, visId, autoId, casting, channeling = GetCurrentCastingInfo() - - -- Show glow if actively casting/channeling this spell - if (casting == 1 and castId == spellId) or (channeling == 1 and visId == spellId) then - return true - end - -- Show glow if this spell is queued (castId set but not yet casting) - if casting == 0 and channeling == 0 and castId == spellId then - return true - end - end - end - elseif actionToCheck.item then - name = actionToCheck.item.name - end - - return CleveRoids.Hooks.IsCurrentAction(CleveRoids.GetProxyActionSlot(name) or slot) - end -end - -local function GetSlotMacroTexture(slot) - local kind, macroId = CleveRoids.ClassicAPI.GetActionInfo(slot) - if kind == "macro" and macroId then - local _, texture = GetMacroInfo(macroId) - return texture - end - return nil -end local function IsAutoAttackSpell(spell) if not spell then return false end @@ -4624,258 +4318,6 @@ local function IsAutoAttackSpell(spell) end CleveRoids.IsAutoAttackSpell = IsAutoAttackSpell -CleveRoids.Hooks.GetActionTexture = GetActionTexture -function GetActionTexture(slot) - if not slot then return nil end - local actions = CleveRoids.GetAction(slot) - - -- Check if this is one of our macros - if actions and (actions.active or actions.tooltip) then - - -- This block handles the case where all conditionals fail and no explicit - -- #showtooltip was set. It defaults to the macro's chosen icon. - if not actions.active and not actions.explicitTooltip and actions.list and table.getn(actions.list) > 0 then - -- Get the macro's own icon as fallback - local macroTexture = GetSlotMacroTexture(slot) - - -- When no conditionals pass, use macro icon (not first action's icon) - -- The actions.tooltip is just the first action which didn't pass conditionals - if macroTexture then - return macroTexture - end - - -- Should never reach here, but return unknown as last resort - return CleveRoids.unknownTexture - end - - -- Prioritize active action, fall back to tooltip - local a = actions.active or actions.tooltip - - -- Handle numeric slot actions (e.g., /use 13) - local slotId = tonumber(a.action) - if slotId and slotId >= 1 and slotId <= 19 then - local currentTexture = GetInventoryItemTexture("player", slotId) - if currentTexture then - return currentTexture - end - - -- Slot is empty, fall back to macro icon - local macroTexture = GetSlotMacroTexture(slot) - if macroTexture then - return macroTexture - end - return CleveRoids.unknownTexture - end - - -- *** THIS IS THE FIX *** - -- If an action is active, return its texture directly. - -- If no action is active, return the tooltip's texture. - -- If neither has a texture, fall back to the macro's icon. - local texture = (actions.active and actions.active.texture) or (actions.tooltip and actions.tooltip.texture) - - -- Check if this is a shapeshift form spell and use active texture if toggled on - if a and a.spell and a.action then - -- Strip rank info and underscores from spell name for comparison - local spellName = string.gsub(a.action, "%s*%(.-%)%s*$", "") - spellName = string.gsub(spellName, "_", " ") - -- Check all shapeshift forms to see if this spell matches and is active - for i = 1, GetNumShapeshiftForms() do - local icon, name, isActive, isCastable = GetShapeshiftFormInfo(i) - if name and string.lower(name) == string.lower(spellName) and isActive and icon then - texture = icon - break - end - end - end - - -- Check if this is a toggled buff ability (Prowl, Shadowmeld) and swap icon based on buff state - -- Note: Stealth is handled above by shapeshift form logic - if a and a.spell and a.action then - local spellName = string.gsub(a.action, "%s*%(.-%)%s*$", "") - spellName = string.gsub(spellName, "_", " ") - - -- Check if this is one of our toggled buff abilities (not shapeshift forms) - local toggledAbilities = { - [CleveRoids.Localized.Spells["Prowl"]] = true, - [CleveRoids.Localized.Spells["Shadowmeld"]] = true, - } - - if toggledAbilities[spellName] then - -- Check if the buff is active - if CleveRoids.ValidatePlayerBuff(spellName) then - -- Buff is active, use the active texture from auraTextures - local activeTexture = CleveRoids.auraTextures[spellName] - if activeTexture then - texture = activeTexture - end - end - end - end - - if a and a.spell and CleveRoids.IsAutoAttackSpell(a.spell) then - local mainHandTexture = GetInventoryItemTexture("player", 16) - if mainHandTexture then - texture = mainHandTexture - end - end - - if texture then - return texture - end - - -- Final fallback: get the macro's icon - local macroTexture = GetSlotMacroTexture(slot) - if macroTexture then - return macroTexture - end - - -- Should never reach here - return CleveRoids.unknownTexture - - end - - -- Not one of our macros, use the original function - return CleveRoids.Hooks.GetActionTexture(slot) -end - --- TODO: Look into https://github.com/Stanzilla/WoWUIBugs/issues/47 if needed -CleveRoids.Hooks.GetActionCooldown = GetActionCooldown -function GetActionCooldown(slot) - -- Guard against nil/invalid slot - if not slot then return 0, 0, 0 end - - local actions = CleveRoids.GetAction(slot) - -- Check for actions.active OR actions.tooltip - if actions and (actions.active or actions.tooltip) then - - -- When no action is active (all conditionals failed) but tooltip exists from #showtooltip, - -- don't show the tooltip's cooldown - fall back to the original function (no cooldown) - -- This matches the icon behavior in GetActionTexture (lines 3958-3977) - if not actions.active and not actions.explicitTooltip and actions.list and table.getn(actions.list) > 0 then - return CleveRoids.Hooks.GetActionCooldown(slot) - end - - -- Prioritize the active action, but fall back to the tooltip action - local a = actions.active or actions.tooltip - - local slotId = tonumber(a.action) - if slotId and slotId >= 1 and slotId <= 19 then - return GetInventoryItemCooldown("player", slotId) - end - - if a.spell and a.spell.spellSlot then - return GetSpellCooldown(a.spell.spellSlot, a.spell.bookType) - elseif a.item then - if a.item.bagID and a.item.slot then - return GetContainerItemCooldown(a.item.bagID, a.item.slot) - elseif a.item.inventoryID then - return GetInventoryItemCooldown("player", a.item.inventoryID) - end - end - - -- DEBUG: Log when we fall through without finding spell/item data (once per slot) - if CleveRoids.cooldownDebug and not CleveRoids.cooldownDebugLogged[slot] then - CleveRoids.cooldownDebugLogged[slot] = true - DEFAULT_CHAT_FRAME:AddMessage(string.format( - "|cffff0000[CD Issue]|r Slot %d: No cooldown data! action='%s' type=%s spell=%s item=%s", - slot, - tostring(a.action), - tostring(a.type), - tostring(a.spell ~= nil), - tostring(a.item ~= nil) - )) - DEFAULT_CHAT_FRAME:AddMessage("|cffff8800[CD Issue]|r This slot will show incorrect cooldown. Report macro text to developer.") - end - - -- Fallback to original function (preserves SuperWoW's #showtooltip handling) - return CleveRoids.Hooks.GetActionCooldown(slot) - else - return CleveRoids.Hooks.GetActionCooldown(slot) - end -end - -CleveRoids.Hooks.GetActionCount = GetActionCount -function GetActionCount(slot) - -- Guard against nil/invalid slot - if not slot then return 0 end - - local action = CleveRoids.GetAction(slot) - local count - - -- When no action is active (all conditionals failed), don't show tooltip's count - if action and not action.active and not action.explicitTooltip and action.list and table.getn(action.list) > 0 then - return CleveRoids.Hooks.GetActionCount(slot) - end - - -- Use the same priority as GetActionTexture: active first, then tooltip - local actionToCheck = (action and action.active) or (action and action.tooltip) - if actionToCheck then - - local slotId = tonumber(actionToCheck.action) - if slotId and slotId >= 1 and slotId <= 19 then - return GetInventoryItemCount("player", slotId) - end - - if actionToCheck.item then - count = CleveRoids.GetLiveItemCount(actionToCheck.item.name or actionToCheck.action) - - elseif actionToCheck.spell then - local reagentId = actionToCheck.spell.reagentId - if not reagentId then - local ss, bt = actionToCheck.spell.spellSlot, actionToCheck.spell.bookType - if ss and bt then - local _, _, rid = CleveRoids.GetSpellCost(ss, bt, actionToCheck.spell.id) - actionToCheck.spell.reagentId = rid -- cache itemID so we don't re-derive every frame - reagentId = rid - end - end - if reagentId then - count = CleveRoids.GetReagentCount(reagentId) -- id-based bag scan - end - end - end - - return count or CleveRoids.Hooks.GetActionCount(slot) -end - -CleveRoids.Hooks.IsConsumableAction = IsConsumableAction -function IsConsumableAction(slot) - -- Guard against nil/invalid slot - if not slot then return nil end - - local action = CleveRoids.GetAction(slot) - - -- When no action is active (all conditionals failed), don't show tooltip's consumable status - if action and not action.active and not action.explicitTooltip and action.list and table.getn(action.list) > 0 then - return CleveRoids.Hooks.IsConsumableAction(slot) - end - - -- Use the same priority as GetActionTexture: active first, then tooltip - local actionToCheck = (action and action.active) or (action and action.tooltip) - if actionToCheck then - - local slotId = tonumber(actionToCheck.action) - if slotId and slotId >= 1 and slotId <= 19 then - local _, count = GetInventoryItemCount("player", slotId) - if count and count > 0 then return 1 end - end - - if actionToCheck.item and - (CleveRoids.countedItemTypes[actionToCheck.item.type] - or CleveRoids.countedItemTypes[actionToCheck.item.name]) - then - return 1 - end - - - if actionToCheck.spell and actionToCheck.spell.reagentId then - return 1 - end - end - - return CleveRoids.Hooks.IsConsumableAction(slot) -end - -- ============================================================================ -- RunMacro Hook - Use our own macro execution system -- ============================================================================