From a50a5c7e7c96d80b5f146c34428b912aab4d819c Mon Sep 17 00:00:00 2001 From: Jrc13245 Date: Fri, 27 Feb 2026 07:59:05 -0500 Subject: [PATCH] use pfui swingtimer if SP not available, fix vanish regeant count --- Conditionals.lua | 147 +++++++++++++++---------------- Core.lua | 25 ++++-- Extensions/OverflowBuffFrame.lua | 13 ++- Extensions/Tooltip/Generic.lua | 13 ++- 4 files changed, 111 insertions(+), 87 deletions(-) diff --git a/Conditionals.lua b/Conditionals.lua index 910a9cd..aa4d508 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -2111,33 +2111,66 @@ function CleveRoids.ValidateComboPoints(operator, amount) return false end --- Validates swing timer percentage for SP_SwingTimer addon integration +-- Returns percent of swing elapsed (0-100) from best available source, or nil +-- Priority: 1) SP_SwingTimer, 2) pfUI swing timer module +function CleveRoids.GetSwingPercentElapsed() + -- Priority 1: SP_SwingTimer + if st_timer ~= nil then + local attackSpeed = st_timerMax or UnitAttackSpeed("player") + if attackSpeed and attackSpeed > 0 then + return ((attackSpeed - st_timer) / attackSpeed) * 100 + end + end + + -- Priority 2: pfUI swing timer + if pfUI and pfUI.swingtimer and pfUI.swingtimer.mainhand + and pfUI.swingtimer.mainhand:IsShown() then + return pfUI.swingtimer.mainhand:GetValue() * 100 + end + + return nil +end + +-- Returns timeRemaining, swingSpeed from best available source, or nil, nil +-- Priority: 1) SP_SwingTimer, 2) pfUI swing timer + GetUnitField +function CleveRoids.GetSwingTimerRaw() + -- Priority 1: SP_SwingTimer + if st_timer ~= nil and st_timerMax ~= nil then + return st_timer, st_timerMax + end + + -- Priority 2: pfUI swing timer + GetUnitField + if pfUI and pfUI.swingtimer and pfUI.swingtimer.mainhand + and pfUI.swingtimer.mainhand:IsShown() and GetUnitField then + local mhSpeed = GetUnitField("player", "baseAttackTime") + if mhSpeed and mhSpeed > 0 then + local swingSpeed = mhSpeed / 1000 + local progress = pfUI.swingtimer.mainhand:GetValue() + local timeRemaining = (1 - progress) * swingSpeed + return timeRemaining, swingSpeed + end + end + + return nil, nil +end + +-- Validates swing timer percentage for SP_SwingTimer / pfUI integration -- operator: Comparison operator (>, <, =, >=, <=, ~=) -- amount: Percentage of swing time elapsed (e.g., 20 means 20% of swing has elapsed) -- returns: True if percentElapsed [operator] amount function CleveRoids.ValidateSwingTimer(operator, amount) if not operator or not amount then return false end - -- Check if SP_SwingTimer is loaded by checking for st_timer global - if st_timer == nil then + local percentElapsed = CleveRoids.GetSwingPercentElapsed() + if percentElapsed == nil then -- Only show error once per session if not CleveRoids._swingTimerErrorShown then - DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [swingtimer] conditional requires the SP_SwingTimer addon. Get it at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [swingtimer] conditional requires SP_SwingTimer or pfUI (swing timer module). Get SP_SwingTimer at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) CleveRoids._swingTimerErrorShown = true end return false end - -- Get player's attack speed (main hand) - local attackSpeed = UnitAttackSpeed("player") - if not attackSpeed or attackSpeed <= 0 then return false end - - -- Calculate percentage of swing elapsed - -- st_timer counts down from attackSpeed to 0 (time remaining) - -- So: timeElapsed = attackSpeed - st_timer - local timeElapsed = attackSpeed - st_timer - local percentElapsed = (timeElapsed / attackSpeed) * 100 - -- Compare percent elapsed against threshold if CleveRoids.operators[operator] then return CleveRoids.comparators[operator](percentElapsed, amount) @@ -2261,9 +2294,10 @@ end -- IMPORTANT: Uses st_timerMax (SP_SwingTimer's adjusted swing timer) not UnitAttackSpeed -- because Flurry and other buffs modify st_timerMax but not UnitAttackSpeed function CleveRoids.GetSlamWindowPercent() - -- Use st_timerMax from SP_SwingTimer (accounts for Flurry) - -- Fall back to UnitAttackSpeed if st_timerMax not available - local attackSpeed = st_timerMax or UnitAttackSpeed("player") + -- Use swing speed from best available source (SP_SwingTimer or pfUI) + -- Fall back to UnitAttackSpeed if neither available + local _, swingSpeed = CleveRoids.GetSwingTimerRaw() + local attackSpeed = swingSpeed or UnitAttackSpeed("player") if not attackSpeed or attackSpeed <= 0 then return 0 end local slamCastTime = CleveRoids.GetSlamCastTime() @@ -2278,8 +2312,9 @@ end -- Scenario: No Slam this swing, cast instant, then Slam next swing without clipping -- Formula: MaxInstantPercent = (2 * SwingTimer - SlamCastTime - GCD) / SwingTimer * 100 function CleveRoids.GetInstantWindowPercent() - -- Use st_timerMax from SP_SwingTimer (accounts for Flurry) - local attackSpeed = st_timerMax or UnitAttackSpeed("player") + -- Use swing speed from best available source (SP_SwingTimer or pfUI) + local _, swingSpeed = CleveRoids.GetSwingTimerRaw() + local attackSpeed = swingSpeed or UnitAttackSpeed("player") if not attackSpeed or attackSpeed <= 0 then return 0 end local slamCastTime = CleveRoids.GetSlamCastTime() @@ -2293,46 +2328,32 @@ end -- Validate if current swing timer is within the Slam window (no clip) -- Returns true if casting Slam NOW will NOT clip the auto-attack function CleveRoids.ValidateNoSlamClip() - -- Check if SP_SwingTimer is loaded (st_timer and st_timerMax are global) - if st_timer == nil or st_timerMax == nil then + local percentElapsed = CleveRoids.GetSwingPercentElapsed() + if percentElapsed == nil then if not CleveRoids._slamClipErrorShown then - DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [noslamclip] conditional requires the SP_SwingTimer addon. Get it at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [noslamclip] conditional requires SP_SwingTimer or pfUI (swing timer module). Get SP_SwingTimer at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) CleveRoids._slamClipErrorShown = true end return false end - -- Use st_timerMax (Flurry-adjusted) not UnitAttackSpeed (base speed) - local attackSpeed = st_timerMax - if not attackSpeed or attackSpeed <= 0 then return false end - - local timeElapsed = attackSpeed - st_timer - local percentElapsed = (timeElapsed / attackSpeed) * 100 local maxPercent = CleveRoids.GetSlamWindowPercent() - return percentElapsed <= maxPercent end -- Validate if current swing timer is within the instant window for next Slam -- Returns true if casting an instant NOW will NOT cause the NEXT Slam to clip function CleveRoids.ValidateNoNextSlamClip() - -- Check if SP_SwingTimer is loaded (st_timer and st_timerMax are global) - if st_timer == nil or st_timerMax == nil then + local percentElapsed = CleveRoids.GetSwingPercentElapsed() + if percentElapsed == nil then if not CleveRoids._slamClipErrorShown then - DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [nonextslamclip] conditional requires the SP_SwingTimer addon. Get it at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [nonextslamclip] conditional requires SP_SwingTimer or pfUI (swing timer module). Get SP_SwingTimer at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) CleveRoids._slamClipErrorShown = true end return false end - -- Use st_timerMax (Flurry-adjusted) not UnitAttackSpeed (base speed) - local attackSpeed = st_timerMax - if not attackSpeed or attackSpeed <= 0 then return false end - - local timeElapsed = attackSpeed - st_timer - local percentElapsed = (timeElapsed / attackSpeed) * 100 local maxPercent = CleveRoids.GetInstantWindowPercent() - return percentElapsed <= maxPercent end @@ -6950,21 +6971,15 @@ CleveRoids.Keywords = { -- Handle multi-comparison (e.g., >50&<80) if args.comparisons and type(args.comparisons) == "table" then - -- Check if SP_SwingTimer is loaded - if st_timer == nil then + local percentElapsed = CleveRoids.GetSwingPercentElapsed() + if percentElapsed == nil then if not CleveRoids._swingTimerErrorShown then - DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [swingtimer] conditional requires the SP_SwingTimer addon. Get it at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [swingtimer] conditional requires SP_SwingTimer or pfUI (swing timer module). Get SP_SwingTimer at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) CleveRoids._swingTimerErrorShown = true end return false end - local attackSpeed = UnitAttackSpeed("player") - if not attackSpeed or attackSpeed <= 0 then return false end - - local timeElapsed = attackSpeed - st_timer - local percentElapsed = (timeElapsed / attackSpeed) * 100 - -- ALL comparisons must pass (AND logic) for _, comp in ipairs(args.comparisons) do if not CleveRoids.operators[comp.operator] then @@ -6988,21 +7003,15 @@ CleveRoids.Keywords = { -- Handle multi-comparison (e.g., >50&<80) if args.comparisons and type(args.comparisons) == "table" then - -- Check if SP_SwingTimer is loaded - if st_timer == nil then + local percentElapsed = CleveRoids.GetSwingPercentElapsed() + if percentElapsed == nil then if not CleveRoids._swingTimerErrorShown then - DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [swingtimer] conditional requires the SP_SwingTimer addon. Get it at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [swingtimer] conditional requires SP_SwingTimer or pfUI (swing timer module). Get SP_SwingTimer at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) CleveRoids._swingTimerErrorShown = true end return false end - local attackSpeed = UnitAttackSpeed("player") - if not attackSpeed or attackSpeed <= 0 then return false end - - local timeElapsed = attackSpeed - st_timer - local percentElapsed = (timeElapsed / attackSpeed) * 100 - -- ALL comparisons must pass (AND logic) for _, comp in ipairs(args.comparisons) do if not CleveRoids.operators[comp.operator] then @@ -7026,21 +7035,15 @@ CleveRoids.Keywords = { -- Handle multi-comparison by checking positive and negating if args.comparisons and type(args.comparisons) == "table" then - -- Check if SP_SwingTimer is loaded - if st_timer == nil then + local percentElapsed = CleveRoids.GetSwingPercentElapsed() + if percentElapsed == nil then if not CleveRoids._swingTimerErrorShown then - DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [swingtimer] conditional requires the SP_SwingTimer addon. Get it at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [swingtimer] conditional requires SP_SwingTimer or pfUI (swing timer module). Get SP_SwingTimer at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) CleveRoids._swingTimerErrorShown = true end return true end - local attackSpeed = UnitAttackSpeed("player") - if not attackSpeed or attackSpeed <= 0 then return true end - - local timeElapsed = attackSpeed - st_timer - local percentElapsed = (timeElapsed / attackSpeed) * 100 - -- Check if ALL comparisons pass for _, comp in ipairs(args.comparisons) do if not CleveRoids.operators[comp.operator] then @@ -7064,21 +7067,15 @@ CleveRoids.Keywords = { -- Handle multi-comparison by checking positive and negating if args.comparisons and type(args.comparisons) == "table" then - -- Check if SP_SwingTimer is loaded - if st_timer == nil then + local percentElapsed = CleveRoids.GetSwingPercentElapsed() + if percentElapsed == nil then if not CleveRoids._swingTimerErrorShown then - DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [swingtimer] conditional requires the SP_SwingTimer addon. Get it at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [swingtimer] conditional requires SP_SwingTimer or pfUI (swing timer module). Get SP_SwingTimer at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) CleveRoids._swingTimerErrorShown = true end return true end - local attackSpeed = UnitAttackSpeed("player") - if not attackSpeed or attackSpeed <= 0 then return true end - - local timeElapsed = attackSpeed - st_timer - local percentElapsed = (timeElapsed / attackSpeed) * 100 - -- Check if ALL comparisons pass for _, comp in ipairs(args.comparisons) do if not CleveRoids.operators[comp.operator] then diff --git a/Core.lua b/Core.lua index 6e32f8b..f789844 100644 --- a/Core.lua +++ b/Core.lua @@ -645,6 +645,7 @@ function CleveRoids.GetSpellCost(spellSlot, bookType) _, _, reagent = string.find(reagentText, "^Reagents?%s*:%s*(.*)") end reagent = _StripColor(reagent) + if reagent == "" then reagent = nil end -- Fallback: scan all lines on a named tooltip (handles Vanish layout) if not reagent or not cost then @@ -685,6 +686,7 @@ function CleveRoids.GetSpellCost(spellSlot, bookType) elseif string.find(rt, "^[Rr]eagents?%s*:") then reagent = _StripColor((lt ~= "" and lt) or (string.gsub(rt, "^[Rr]eagents?%s*:%s*", ""))) end + if reagent == "" then reagent = nil end end if not cost and rt ~= "" then @@ -696,7 +698,8 @@ function CleveRoids.GetSpellCost(spellSlot, bookType) end end - if not reagent then + if not reagent or reagent == "" then + reagent = nil local name = GetSpellName(spellSlot, bookType) if name then name = string.gsub(name, "%s*%(%s*Rank%s+%d+%s*%)%s*$", "") -- strip "(Rank X)" only @@ -704,7 +707,7 @@ function CleveRoids.GetSpellCost(spellSlot, bookType) end end - return (cost and tonumber(cost) or 0), (reagent and tostring(reagent) or nil) + return (cost and tonumber(cost) or 0), (reagent and reagent ~= "" and tostring(reagent) or nil) end function CleveRoids.GetProxyActionSlot(slot) @@ -4741,7 +4744,8 @@ function GetActionCount(slot) elseif actionToCheck.spell then local reagent = actionToCheck.spell.reagent - if not reagent then + if not reagent or reagent == "" then + reagent = nil local ss, bt = actionToCheck.spell.spellSlot, actionToCheck.spell.bookType if ss and bt then local _, r = CleveRoids.GetSpellCost(ss, bt) @@ -4752,7 +4756,7 @@ function GetActionCount(slot) end actionToCheck.spell.reagent = reagent -- cache it so we don't re-scan every frame end - if reagent then + if reagent and reagent ~= "" then count = CleveRoids.GetReagentCount(reagent) -- id-first bag scan, falls back to name/tooltip end end @@ -4791,8 +4795,17 @@ function IsConsumableAction(slot) end - if actionToCheck.spell and actionToCheck.spell.reagent then - return 1 + if actionToCheck.spell then + local reagent = actionToCheck.spell.reagent + if (not reagent or reagent == "") and actionToCheck.spell.name then + reagent = _ReagentBySpell[actionToCheck.spell.name] + if reagent then + actionToCheck.spell.reagent = reagent + end + end + if reagent and reagent ~= "" then + return 1 + end end end diff --git a/Extensions/OverflowBuffFrame.lua b/Extensions/OverflowBuffFrame.lua index 40fbbe2..2ec1878 100644 --- a/Extensions/OverflowBuffFrame.lua +++ b/Extensions/OverflowBuffFrame.lua @@ -250,12 +250,17 @@ local function GetTargetOverflowBuffs() local trackingData = CleveRoids.AllCasterAuraTracking if not trackingData or not trackingData[targetGuid] then return results end - -- Build set of visible buff textures on target - local visibleBuffs = {} + -- Build set of visible aura textures on target (buffs + debuffs) + local visibleAuras = {} for i = 1, 32 do local texture = UnitBuff("target", i) if not texture then break end - visibleBuffs[texture] = true + visibleAuras[texture] = true + end + for i = 1, 16 do + local texture = UnitDebuff("target", i) + if not texture then break end + visibleAuras[texture] = true end local now = GetTime() @@ -268,7 +273,7 @@ local function GetTargetOverflowBuffs() local isVisible = false if not auraData._testEntry then local spellIcon = lib and lib:GetCachedIcon(spellId) - isVisible = spellIcon and visibleBuffs[spellIcon] + isVisible = spellIcon and visibleAuras[spellIcon] end if not isVisible then table.insert(results, { diff --git a/Extensions/Tooltip/Generic.lua b/Extensions/Tooltip/Generic.lua index 224fc05..3281e36 100644 --- a/Extensions/Tooltip/Generic.lua +++ b/Extensions/Tooltip/Generic.lua @@ -58,7 +58,7 @@ function CleveRoids.IndexSpells() else local cost, reagent = CleveRoids.GetSpellCost(i, bookType) -- Fallback for known reagent spells if tooltip scan failed - if not reagent and CleveRoids.ReagentBySpell then + if (not reagent or reagent == "") and CleveRoids.ReagentBySpell then reagent = CleveRoids.ReagentBySpell[spellName] end local texture = GetSpellTexture(i, bookType) @@ -84,6 +84,10 @@ function CleveRoids.IndexSpells() } spells[bookType][spellName].highest = spells[bookType][spellName][spellRank] end + -- For spells with no rank string, the base entry is the highest + if not spellRank and not spells[bookType][spellName].highest then + spells[bookType][spellName].highest = spells[bookType][spellName] + end if reagent then CleveRoids.countedItemTypes[reagent] = true @@ -458,7 +462,12 @@ function CleveRoids.GetSpell(text) for book, spells in CleveRoids.Spells do if spells and spells[name] then - return spells[name][rank or "highest"] + local entry = spells[name][rank or "highest"] + if entry then return entry end + -- Fallback: for spells with no rank, return base entry + if not rank and spells[name].spellSlot then + return spells[name] + end end end end