diff --git a/Compatibility/Bongos.lua b/Compatibility/Bongos.lua index 1d2774e..371d237 100644 --- a/Compatibility/Bongos.lua +++ b/Compatibility/Bongos.lua @@ -3,6 +3,45 @@ CleveRoids.RegisterActionEventHandler(function(slot, event) local button = getglobal("BActionButton" .. slot) if button then - BActionButton.Update(button) + -- For slot change events, do a full button update + if event == "ACTIONBAR_SLOT_CHANGED" then + BActionButton.Update(button) + end + + -- COOLDOWN FIX: Always update the cooldown using CleveRoids' active spell + -- Bongos uses button.cooldown for the cooldown frame + local cooldownFrame = button.cooldown or getglobal(button:GetName() .. "Cooldown") + if cooldownFrame then + local start, duration, enable + local spellSlot, bookType = CleveRoids.GetActionSpellSlot(slot) + + if spellSlot and bookType then + start, duration, enable = GetSpellCooldown(spellSlot, bookType) + else + local actions = CleveRoids.GetAction(slot) + local actionToCheck = actions and (actions.active or actions.tooltip) + if actionToCheck and actionToCheck.item then + local item = actionToCheck.item + if item.bagID and item.slot then + start, duration, enable = GetContainerItemCooldown(item.bagID, item.slot) + elseif item.inventoryID then + start, duration, enable = GetInventoryItemCooldown("player", item.inventoryID) + end + elseif actionToCheck then + local slotId = tonumber(actionToCheck.action) + if slotId and slotId >= 1 and slotId <= 19 then + start, duration, enable = GetInventoryItemCooldown("player", slotId) + end + end + + if not start then + start, duration, enable = GetActionCooldown(slot) + end + end + + if start and duration then + CooldownFrame_SetTimer(cooldownFrame, start, duration, (enable and enable > 0) and enable or 1) + end + end end end) diff --git a/Compatibility/pfUI.lua b/Compatibility/pfUI.lua index 2cfbd16..82ea2b7 100644 --- a/Compatibility/pfUI.lua +++ b/Compatibility/pfUI.lua @@ -367,17 +367,75 @@ function Extension.RegisterPfUIActionEventHandler() -- Register a handler that will be called whenever CleveRoids updates macro states if CleveRoids.RegisterActionEventHandler then + Extension.DLOG("Registering pfUI action event handler") CleveRoids.RegisterActionEventHandler(function(slot, event, ...) - -- Trigger pfUI's button update for this slot - -- Mark the slot for update in pfUI's cache, which will be processed on next OnUpdate - if pfUI.bars and pfUI.bars.update then - pfUI.bars.update[slot] = true + local button = pfUI.bars and pfUI.bars.buttons and pfUI.bars.buttons[slot] + + if Extension.Debug then + DEFAULT_CHAT_FRAME:AddMessage(string.format( + "|cff00ff00[pfUI CD]|r slot=%s event=%s button=%s cd=%s", + tostring(slot), tostring(event), + button and "yes" or "no", + (button and button.cd) and "yes" or "no" + )) end - -- Also directly call ButtonFullUpdate if the button exists - local button = pfUI.bars and pfUI.bars.buttons and pfUI.bars.buttons[slot] - if button and pfUI.bars.ButtonFullUpdate then - pfUI.bars.ButtonFullUpdate(button) + -- For slot change events, do a full button update + if event == "ACTIONBAR_SLOT_CHANGED" then + -- Trigger pfUI's button update for this slot + -- Mark the slot for update in pfUI's cache, which will be processed on next OnUpdate + if pfUI.bars and pfUI.bars.update then + pfUI.bars.update[slot] = true + end + + -- Also directly call ButtonFullUpdate if the button exists + if button and pfUI.bars.ButtonFullUpdate then + pfUI.bars.ButtonFullUpdate(button) + end + end + + -- COOLDOWN FIX: Always update cooldowns explicitly + -- pfUI's macro scanner caches spell info and doesn't know about + -- CleveRoids' conditional spell changes. Manually update the cooldown + -- using the active spell from CleveRoids. + -- Note: pfUI uses button.cd for cooldown frame (not button.cooldown) + if button and button.cd then + local start, duration, enable + local spellSlot, bookType = CleveRoids.GetActionSpellSlot(slot) + + if spellSlot and bookType then + -- Active spell found - get its cooldown + start, duration, enable = GetSpellCooldown(spellSlot, bookType) + else + -- No active spell - check for item cooldown + local actions = CleveRoids.GetAction(slot) + local actionToCheck = actions and (actions.active or actions.tooltip) + if actionToCheck and actionToCheck.item then + local item = actionToCheck.item + if item.bagID and item.slot then + start, duration, enable = GetContainerItemCooldown(item.bagID, item.slot) + elseif item.inventoryID then + start, duration, enable = GetInventoryItemCooldown("player", item.inventoryID) + end + elseif actionToCheck then + -- Check for equipment slot (e.g., trinket) + local slotId = tonumber(actionToCheck.action) + if slotId and slotId >= 1 and slotId <= 19 then + start, duration, enable = GetInventoryItemCooldown("player", slotId) + end + end + + -- Fallback: use the hooked GetActionCooldown for non-CleveRoids actions + if not start then + start, duration, enable = GetActionCooldown(slot) + end + end + + -- Apply cooldown if we have valid data + -- Ensure enable is at least 1 (0 can hide the cooldown) + if start and duration then + CooldownFrame_SetTimer(button.cd, start, duration, (enable and enable > 0) and enable or 1) + end end end) @@ -518,6 +576,24 @@ function Extension.OnLoad() -- Initial compatibility check Extension.SetupCompatibility() + + -- Add slash command to toggle pfUI cooldown debug + -- Usage: /pfuicd to toggle debug mode + SlashCmdList["PFUICD"] = function() + Extension.Debug = not Extension.Debug + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[pfUI Compat]|r Debug mode: " .. (Extension.Debug and "ON" or "OFF")) + if Extension.Debug then + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[pfUI Compat]|r Handler registered: " .. (Extension.actionHandlerRegistered and "YES" or "NO")) + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[pfUI Compat]|r pfUI detected: " .. (Extension.pfUILoaded and "YES" or "NO")) + if pfUI and pfUI.bars then + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[pfUI Compat]|r pfUI.bars exists: YES") + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[pfUI Compat]|r pfUI.bars.buttons: " .. (pfUI.bars.buttons and "YES" or "NO")) + else + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[pfUI Compat]|r pfUI.bars exists: NO") + end + end + end + SLASH_PFUICD1 = "/pfuicd" end function Extension.ADDON_LOADED() @@ -536,6 +612,14 @@ end function Extension.PLAYER_LOGIN() -- Final check after everything is loaded Extension.SetupCompatibility() + + -- Print startup status + if Extension.pfUILoaded then + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[SCRM]|r pfUI compatibility loaded. Use /pfuicd for debug.") + if not Extension.actionHandlerRegistered then + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SCRM]|r WARNING: pfUI action handler NOT registered!") + end + end end -- Utility: Schedule a delayed function call (if not already defined) diff --git a/Conditionals.lua b/Conditionals.lua index e8cfde7..9fb58d9 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -2592,6 +2592,12 @@ function CleveRoids.ValidateUnitDebuff(unit, args) return false -- Debuff doesn't exist, so all comparisons fail end + -- For non-player units, get time remaining once (used for all time comparisons) + local nonPlayerTimeRemaining = nil + if unit ~= "player" then + nonPlayerTimeRemaining = _get_debuff_timeleft(unit, args.name) or 0 + end + -- ALL comparisons must pass (AND logic) for _, comp in ipairs(args.comparisons) do if not ops[comp.operator] then @@ -2604,8 +2610,8 @@ function CleveRoids.ValidateUnitDebuff(unit, args) elseif unit == "player" then value_to_check = remaining or 0 else - -- Non-player units don't have remaining time, only check existence - return found + -- Non-player units: use time remaining from libdebuff + value_to_check = nonPlayerTimeRemaining end if not cmp[comp.operator](value_to_check, comp.amount) then @@ -4164,7 +4170,7 @@ CleveRoids.Keywords = { return true end - return CleveRoids.ValidatePower(conditionals.target, args.operator, args.amount) + return CleveRoids.ValidatePower(conditionals.target or "target", args.operator, args.amount) end, conditionals, "power") end, @@ -4215,7 +4221,7 @@ CleveRoids.Keywords = { return true end - return CleveRoids.ValidateRawPower(conditionals.target, args.operator, args.amount) + return CleveRoids.ValidateRawPower(conditionals.target or "target", args.operator, args.amount) end, conditionals, "rawpower") end, @@ -4295,7 +4301,7 @@ CleveRoids.Keywords = { return true end - return CleveRoids.ValidateHp(conditionals.target, args.operator, args.amount) + return CleveRoids.ValidateHp(conditionals.target or "target", args.operator, args.amount) end, conditionals, "hp") end, @@ -4326,7 +4332,7 @@ CleveRoids.Keywords = { return true end - return CleveRoids.ValidateLevel(conditionals.target, args.operator, args.amount) + return CleveRoids.ValidateLevel(conditionals.target or "target", args.operator, args.amount) end, conditionals, "level") end, @@ -4382,7 +4388,7 @@ CleveRoids.Keywords = { rawhp = function(conditionals) return Multi(conditionals.rawhp, function(args) if type(args) ~= "table" then return false end - return CleveRoids.ValidateRawHp(conditionals.target, args.operator, args.amount) + return CleveRoids.ValidateRawHp(conditionals.target or "target", args.operator, args.amount) end, conditionals, "rawhp") end, diff --git a/Console.lua b/Console.lua index b5c0ff9..8f7a84d 100644 --- a/Console.lua +++ b/Console.lua @@ -134,6 +134,7 @@ end -- /startattack hook CleveRoids.Hooks.STARTATTACK_SlashCmd = SlashCmdList.STARTATTACK SlashCmdList.STARTATTACK = function(msg) + if CleveRoids.stopMacroFlag then return end msg = msg or "" if string.find(msg, "%[") then CleveRoids.DoConditionalStartAttack(msg) @@ -145,6 +146,7 @@ end -- /stopattack hook CleveRoids.Hooks.STOPATTACK_SlashCmd = SlashCmdList.STOPATTACK SlashCmdList.STOPATTACK = function(msg) + if CleveRoids.stopMacroFlag then return end msg = msg or "" if string.find(msg, "%[") then -- If conditionals are present, let the function handle it. @@ -159,6 +161,7 @@ end -- /stopcasting hook CleveRoids.Hooks.STOPCASTING_SlashCmd = SlashCmdList.STOPCASTING SlashCmdList.STOPCASTING = function(msg) + if CleveRoids.stopMacroFlag then return end msg = msg or "" if string.find(msg, "%[") then -- If conditionals are present, let the function handle it. @@ -170,8 +173,10 @@ SlashCmdList.STOPCASTING = function(msg) end end +-- /unqueue hook CleveRoids.Hooks.UNQUEUE_SlashCmd = SlashCmdList.UNQUEUE SlashCmdList.UNQUEUE = function(msg) + if CleveRoids.stopMacroFlag then return end msg = msg or "" if string.find(msg, "%[") then -- If conditionals are present, let the function handle it. @@ -185,6 +190,7 @@ end -- /cast hook CleveRoids.Hooks.CAST_SlashCmd = SlashCmdList.CAST SlashCmdList.CAST = function(msg) + if CleveRoids.stopMacroFlag then return end if msg and string.find(msg, "[%[%?!~{]") then CleveRoids.DoCast(msg) else diff --git a/Core.lua b/Core.lua index ba63a08..8396269 100644 --- a/Core.lua +++ b/Core.lua @@ -2075,6 +2075,12 @@ function CleveRoids.TestAction(cmd, args) end function CleveRoids.DoWithConditionals(msg, hook, fixEmptyTargetFunc, targetBeforeAction, action) + -- Check stopmacro flag (skip non-STOPMACRO commands when flag is set) + -- This enables /stopmacro to work without SuperMacro for vanilla macros + if CleveRoids.stopMacroFlag and action ~= "STOPMACRO" then + return false + end + local msg, conditionals = CleveRoids.GetParsedMsg(msg) -- Debug: Log parsed msg and action type @@ -2315,6 +2321,11 @@ function CleveRoids.DoCastPet(msg) end function CleveRoids.DoTarget(msg) + -- Check stopmacro flag + if CleveRoids.stopMacroFlag then + return false + end + local action, conditionals = CleveRoids.GetParsedMsg(msg) if action ~= "" or type(conditionals) ~= "table" or not next(conditionals) then @@ -2611,6 +2622,11 @@ end -- Also checks if a condition is a spell so that you can mix item and spell use -- msg: The raw message intercepted from a /use or /equip command function CleveRoids.DoUse(msg) + -- Check stopmacro flag + if CleveRoids.stopMacroFlag then + return false + end + local handled = false local action = function(msg) @@ -3279,6 +3295,9 @@ function CleveRoids.DoCastSequence(sequence) end CleveRoids.DoConditionalCancelAura = function(msg) + -- Check stopmacro flag + if CleveRoids.stopMacroFlag then return false end + local s = CleveRoids.Trim(msg or "") if s == "" then return false end @@ -3301,6 +3320,13 @@ local UnitAffectingCombat = UnitAffectingCombat local pairs = pairs function CleveRoids.OnUpdate(self) + -- Clear stopmacro flag at the start of each frame + -- This ensures /stopmacro only affects commands in the same frame (same macro execution) + -- Without SuperMacro, this is necessary because we can't hook into macro line execution + if CleveRoids.stopMacroFlag then + CleveRoids.stopMacroFlag = false + end + -- PERFORMANCE: Single GetTime() call per frame local time = GetTime() @@ -4678,6 +4704,59 @@ function CleveRoids.Frame:SPELL_UPDATE_COOLDOWN() if CleveRoidMacros.realtime == 0 then CleveRoids.QueueActionUpdate() end + + -- COOLDOWN FIX: Explicitly update cooldowns on all managed action buttons + -- This ensures cooldowns display correctly even when the active action hasn't changed + CleveRoids.UpdateAllManagedCooldowns() +end + +-- Helper function to update cooldowns on all CleveRoids-managed action buttons +function CleveRoids.UpdateAllManagedCooldowns() + local Actions = CleveRoids.Actions + if not Actions then return end + + local handlerCount = CleveRoids.actionEventHandlers and table.getn(CleveRoids.actionEventHandlers) or 0 + if CleveRoids.cooldownDebug then + DEFAULT_CHAT_FRAME:AddMessage("|cffff8800[CD Update]|r Updating cooldowns, " .. handlerCount .. " handlers registered") + end + + for slot, actions in pairs(Actions) do + if actions then + -- Get the cooldown info for this slot + local start, duration, enable = GetActionCooldown(slot) + + -- Update Blizzard action buttons + local page = floor((slot - 1) / NUM_ACTIONBAR_BUTTONS) + 1 + local pageSlot = slot - (page - 1) * NUM_ACTIONBAR_BUTTONS + + local button + if slot >= 73 then + button = _G["BonusActionButton" .. pageSlot] + elseif slot >= 61 then + button = _G["MultiBarBottomLeftButton" .. pageSlot] + elseif slot >= 49 then + button = _G["MultiBarBottomRightButton" .. pageSlot] + elseif slot >= 37 then + button = _G["MultiBarLeftButton" .. pageSlot] + elseif slot >= 25 then + button = _G["MultiBarRightButton" .. pageSlot] + elseif page == CURRENT_ACTIONBAR_PAGE then + button = _G["ActionButton" .. pageSlot] + end + + if button then + local cooldown = _G[button:GetName() .. "Cooldown"] + if cooldown and start and duration then + CooldownFrame_SetTimer(cooldown, start, duration, (enable and enable > 0) and enable or 1) + end + end + + -- Notify action event handlers (for pfUI/Bongos) about the cooldown update + for _, fn_h in ipairs(CleveRoids.actionEventHandlers) do + fn_h(slot, "ACTIONBAR_UPDATE_COOLDOWN") + end + end + end end -- PERFORMANCE OPTIMIZATION: Throttled event handlers to reduce CPU spam -- UNIT_AURA can fire dozens of times per second during combat @@ -5050,6 +5129,18 @@ SlashCmdList["CLEVEROID"] = function(msg) return end + -- cooldowndebug (toggle cooldown update debug messages) + if cmd == "cooldowndebug" or cmd == "cddebug" then + CleveRoids.cooldownDebug = not CleveRoids.cooldownDebug + CleveRoids.Print("Cooldown debug " .. (CleveRoids.cooldownDebug and "enabled" or "disabled")) + if CleveRoids.cooldownDebug then + CleveRoids.Print("Use /pfuicd for pfUI-specific cooldown debug") + local handlerCount = CleveRoids.actionEventHandlers and table.getn(CleveRoids.actionEventHandlers) or 0 + CleveRoids.Print("Action event handlers registered: " .. handlerCount) + end + return + end + -- slotdebug - Debug action slot state for tooltip/range/mana issues if cmd == "slotdebug" then local slot = tonumber(val) diff --git a/Extensions/Mouseover/GameTooltip.lua b/Extensions/Mouseover/GameTooltip.lua index 039e900..b2e9862 100644 --- a/Extensions/Mouseover/GameTooltip.lua +++ b/Extensions/Mouseover/GameTooltip.lua @@ -19,7 +19,17 @@ end -- Native UPDATE_MOUSEOVER_UNIT handler (priority 2) -- This works even when tooltips are hidden by other addons +-- NOTE: We skip when selfTriggered is set to prevent TRP tooltip conflicts. +-- When we call SetMouseoverUnit ourselves, it triggers UPDATE_MOUSEOVER_UNIT, +-- which would add a redundant "native" source that persists after the real +-- source (pfUI, etc.) clears - causing TRP to show stale profile info. function Extension.UPDATE_MOUSEOVER_UNIT() + -- Skip if we ourselves triggered this event by calling SetMouseoverUnit + if CleveRoids.__mo and CleveRoids.__mo.selfTriggered then + CleveRoids.__mo.selfTriggered = false + return + end + if UnitExists("mouseover") then CleveRoids.SetMouseoverFrom("native", "mouseover") else diff --git a/Utility.lua b/Utility.lua index 8e1d0a3..7d6dbda 100644 --- a/Utility.lua +++ b/Utility.lua @@ -243,7 +243,7 @@ do local CleveRoids = _G.CleveRoids or {} _G.CleveRoids = CleveRoids - CleveRoids.__mo = CleveRoids.__mo or { sources = {}, current = nil } + CleveRoids.__mo = CleveRoids.__mo or { sources = {}, current = nil, selfTriggered = false } -- Priority levels for mouseover sources (higher = takes precedence) -- All unit frame addons get priority 3, native event gets 2, tooltip fallback gets 1 @@ -302,6 +302,8 @@ do local function apply(unit) if CleveRoids.hasSuperwow and _G.SetMouseoverUnit then + -- Set flag so UPDATE_MOUSEOVER_UNIT handler knows we triggered this + CleveRoids.__mo.selfTriggered = true _G.SetMouseoverUnit(unit) else CleveRoids.mouseoverUnit = unit