diff --git a/Compatibility/pfUI.lua b/Compatibility/pfUI.lua index f570013..f71dacd 100644 --- a/Compatibility/pfUI.lua +++ b/Compatibility/pfUI.lua @@ -789,6 +789,25 @@ function Extension.PLAYER_LOGIN() end end + -- Register libdebuff downrank blocked hook now that pfUI is fully initialized. + -- Done here (not in Core.lua PLAYER_LOGIN) because pfUI.libdebuff_downrank_blocked_hooks + -- is only guaranteed to exist after InitPfUIIntegration has run. + if pfUI and pfUI.libdebuff_downrank_blocked_hooks then + table.insert(pfUI.libdebuff_downrank_blocked_hooks, function(spellName, castRank, activeRank, targetGuid, casterGuid) + local playerGuid = CleveRoids.GetGUID("player") + if casterGuid ~= playerGuid then return end + CleveRoids.DownrankBlocked[targetGuid] = CleveRoids.DownrankBlocked[targetGuid] or {} + CleveRoids.DownrankBlocked[targetGuid][spellName] = { + castRank = castRank, + activeRank = activeRank, + time = GetTime() + } + CleveRoids.DebugChanged("downrank_hook_" .. spellName .. "_" .. tostring(targetGuid), + string.format("|cffff0000[DownrankBlocked]|r %s Rank %d blocked by active Rank %d", + spellName, castRank, activeRank)) + end) + end + -- Final check after everything is loaded Extension.SetupCompatibility() diff --git a/Conditionals.lua b/Conditionals.lua index c42dc18..432707b 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -907,6 +907,11 @@ CleveRoids.OverflowBuffs = {} -- from pfUI.libdebuff_all_auras instead (which has full downrank protection). CleveRoids.AllCasterAuraTracking = {} +-- Tracks spells that libdebuff has identified as downrank-blocked for the current target. +-- Populated via pfUI.libdebuff_downrank_blocked_hooks, cleared implicitly by time. +-- Structure: [targetGuid][spellName] = { castRank, activeRank, time } +CleveRoids.DownrankBlocked = CleveRoids.DownrankBlocked or {} + -- Unified accessor: returns per-spell caster table for a target GUID. -- When pfUI 7.6+ is active, reads from pfUI.libdebuff_all_auras (field: .startTime) -- and translates to our field names (.start). Falls back to AllCasterAuraTracking. @@ -1856,18 +1861,22 @@ local function IsPendingDebuffCast(spellName, targetUnit) if castingName then local normalizedCasting = NormalizeSpellNameForComparison(castingName) if normalizedCasting == normalizedCheck then - -- Verify cast is still in progress (not finished) - if CleveRoids.castStartTime and CleveRoids.castDuration then - local remaining = CleveRoids.castDuration - (GetTime() - CleveRoids.castStartTime) - if remaining > 0.1 then + -- If libdebuff flagged this as downrank-blocked, don't treat as pending + local blocked = targetGuid and CleveRoids.DownrankBlocked[targetGuid] and + CleveRoids.DownrankBlocked[targetGuid][castingName] + if not blocked then + if CleveRoids.castStartTime and CleveRoids.castDuration then + local remaining = CleveRoids.castDuration - (GetTime() - CleveRoids.castStartTime) + if remaining > 0.1 then + CleveRoids.DebugChanged("pending_" .. normalizedCheck, + string.format("|cffff00ff[PendingDebuff]|r %s pending via CurrentSpell", spellName)) + return true + end + else CleveRoids.DebugChanged("pending_" .. normalizedCheck, string.format("|cffff00ff[PendingDebuff]|r %s pending via CurrentSpell", spellName)) return true end - else - CleveRoids.DebugChanged("pending_" .. normalizedCheck, - string.format("|cffff00ff[PendingDebuff]|r %s pending via CurrentSpell", spellName)) - return true end end end @@ -1879,10 +1888,13 @@ local function IsPendingDebuffCast(spellName, targetUnit) local queuedName = CleveRoids.queuedSpell.spellName local normalizedQueued = NormalizeSpellNameForComparison(queuedName) if normalizedQueued == normalizedCheck then - -- Spell is queued, debuff is pending - CleveRoids.DebugChanged("PendingDebuff_" .. spellName, - string.format("|cffff00ff[PendingDebuff]|r %s pending via queuedSpell", spellName)) - return true + local blocked = targetGuid and CleveRoids.DownrankBlocked[targetGuid] and + CleveRoids.DownrankBlocked[targetGuid][queuedName] + if not blocked then + CleveRoids.DebugChanged("PendingDebuff_" .. spellName, + string.format("|cffff00ff[PendingDebuff]|r %s pending via queuedSpell", spellName)) + return true + end end end @@ -1891,8 +1903,9 @@ local function IsPendingDebuffCast(spellName, targetUnit) local lib = CleveRoids.libdebuff if lib and targetGuid then if pfUI and pfUI.libdebuff_pending then - -- pfUI.libdebuff_pending[guid][spellName] = {casterGuid, rank, time} - -- Tracks all casters; filter to player's own casts only + -- pfUI.libdebuff_pending[guid][spellName] = {casterGuid, rank, time, downrankBlocked} + -- libdebuff sets downrankBlocked=true when the cast was identified as a downrank — + -- read that field directly instead of re-implementing the check here. local pendingForTarget = pfUI.libdebuff_pending[targetGuid] if pendingForTarget then local playerGuid = CleveRoids.GetGUID("player") @@ -1901,9 +1914,14 @@ local function IsPendingDebuffCast(spellName, targetUnit) if normalizedPending == normalizedCheck then local casterGuid = type(pendingData) == "table" and pendingData.casterGuid or nil if casterGuid and casterGuid == playerGuid then - CleveRoids.DebugChanged("PendingDebuff_" .. spellName, - string.format("|cffff00ff[PendingDebuff]|r %s pending via pfUI.libdebuff_pending (ours)", spellName)) - return true + if type(pendingData) == "table" and pendingData.downrankBlocked then + CleveRoids.DebugChanged("PendingDebuff_downrank_" .. spellName, + string.format("|cffff00ff[PendingDebuff]|r %s skipped - downrank blocked by libdebuff", spellName)) + else + CleveRoids.DebugChanged("PendingDebuff_" .. spellName, + string.format("|cffff00ff[PendingDebuff]|r %s pending via pfUI.libdebuff_pending (ours)", spellName)) + return true + end elseif casterGuid then CleveRoids.DebugChanged("PendingDebuff_other_" .. spellName, string.format("|cffff00ff[PendingDebuff]|r %s in pfUI pending but caster=%s (not ours) - skipping", diff --git a/Extensions/OverflowBuffFrame.lua b/Extensions/OverflowBuffFrame.lua index 8727d7b..709a71b 100644 --- a/Extensions/OverflowBuffFrame.lua +++ b/Extensions/OverflowBuffFrame.lua @@ -118,9 +118,14 @@ local function CreateIconButton(parent, index, iconTable) -- Tooltip btn:SetScript("OnEnter", function() local data = iconTable[index] - if not data or not data.spellId then return end + if not data or (not data.spellId and not data.displayName) then return end GameTooltip:SetOwner(btn, "ANCHOR_BOTTOMLEFT") - local spellName = GetSpellRecField and GetSpellRecField(data.spellId, "name") or ("Spell " .. data.spellId) + local spellName + if data.spellId then + spellName = GetSpellRecField and GetSpellRecField(data.spellId, "name") or ("Spell " .. data.spellId) + else + spellName = data.displayName + end if spellName then local baseName = CleveRoids.StripRank(spellName) GameTooltip:AddLine(baseName, 1, 1, 1) @@ -235,6 +240,12 @@ local function GetPlayerOverflowBuffs() for k in pairs(overflowBuffs) do overflowBuffs[k] = nil end + -- Also wipe combat log fallback entries + if CleveRoids.OverflowBuffsByName then + for k in pairs(CleveRoids.OverflowBuffsByName) do + CleveRoids.OverflowBuffsByName[k] = nil + end + end return results end end @@ -282,6 +293,29 @@ local function GetPlayerOverflowBuffs() end end + -- Include combat log fallback entries (name-based, no spell ID) + if CleveRoids.OverflowBuffsByName then + for lowerName, entry in pairs(CleveRoids.OverflowBuffsByName) do + if entry.timestamp then + local elapsed = now - entry.timestamp + if entry.durationSec and entry.durationSec > 0 and elapsed > entry.durationSec then + CleveRoids.OverflowBuffsByName[lowerName] = nil + else + local rem = entry.durationSec and entry.durationSec > 0 + and (entry.durationSec - elapsed) or 0 + if rem > 0 or not entry.durationSec or entry.durationSec <= 0 then + table.insert(results, { + spellId = nil, + remaining = rem > 0 and rem or 0, + source = "player", + displayName = entry.displayName or lowerName, + }) + end + end + end + end + end + table.sort(results, function(a, b) return a.remaining > b.remaining end) return results end @@ -402,6 +436,7 @@ local function PopulateFrame(frame, label, labelText, iconTable, buffs, countRef data.icon:Show() data.spellId = buff.spellId + data.displayName = buff.displayName data.remaining = buff.remaining data.source = buff.source data.duration:SetText(FormatDuration(buff.remaining)) @@ -416,6 +451,7 @@ local function PopulateFrame(frame, label, labelText, iconTable, buffs, countRef data.icon:Hide() data.duration:SetText("") data.spellId = nil + data.displayName = nil data.remaining = nil data.source = nil end diff --git a/Utility.lua b/Utility.lua index 525ccdb..430b878 100644 --- a/Utility.lua +++ b/Utility.lua @@ -5919,6 +5919,14 @@ ev:SetScript("OnEvent", function() CleveRoids.OverflowBuffs[spellId] = nil end + -- Also prune from combat log fallback overflow table (name-based) + if spellName and CleveRoids.OverflowBuffsByName then + local lowerName = string.lower(spellName) + if CleveRoids.OverflowBuffsByName[lowerName] then + CleveRoids.OverflowBuffsByName[lowerName] = nil + end + end + -- NAMPOWER v2.30+ BUFF_REMOVED_OTHER - Buffs removed from other units elseif event == "BUFF_REMOVED_OTHER" then if lib.hasPfUIEnhanced then return end